https://github.com/akkartik/mu/blob/main/117write-int-hex.subx

      1 # Write out the (hex) textual representation of numbers.
      2
      3 == code
      4 #   instruction                     effective address                                                   register    displacement    immediate
      5 # . op          subop               mod             rm32          base        index         scale       r32
      6 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
      7
      8 # convert the lowest nibble of eax to ascii and return it in the lowest byte of eax
      9 to-hex-char:  # in/eax: int -> out/eax: int
     10     # no error checking; accepts argument in eax
     11     # if (eax <= 9) return eax + '0'
     12     3d/compare-eax-with  0x9/imm32/9
     13     7f/jump-if->  $to-hex-char:else/disp8
     14     05/add-to-eax  0x30/imm32/0
     15     c3/return
     16 $to-hex-char:else:
     17     # otherwise return eax + 'a' - 10
     18     05/add-to-eax  0x57/imm32/a-10
     19     c3/return
     20
     21 append-byte-hex:  # f: (addr stream byte), n: int
     22     # . prologue
     23     55/push-ebp
     24     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     25     # . save registers
     26     50/push-eax
     27     # AL = convert upper nibble to hex
     28     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
     29     c1/shift    5/subop/logic-right 3/mod/direct    0/rm32/eax    .           .             .           .           .               4/imm8            # shift eax right by 4 bits, while padding zeroes
     30     25/and-eax  0xf/imm32
     31     # . AL = to-hex-char(AL)
     32     e8/call  to-hex-char/disp32
     33     # append-byte(f, AL)
     34     # . . push args
     35     50/push-eax
     36     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     37     # . . call
     38     e8/call  append-byte/disp32
     39     # . . discard args
     40     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     41     # AL = convert lower nibble to hex
     42     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
     43     25/and-eax  0xf/imm32
     44     # . AL = to-hex-char(AL)
     45     e8/call  to-hex-char/disp32
     46     # append-byte(f, AL)
     47     # . . push args
     48     50/push-eax
     49     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     50     # . . call
     51     e8/call  append-byte/disp32
     52     # . . discard args
     53     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     54 $append-byte-hex:end:
     55     # . restore registers
     56     58/pop-to-eax
     57     # . epilogue
     58     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     59     5d/pop-to-ebp
     60     c3/return
     61
     62 test-append-byte-hex:
     63     # - check that append-byte-hex adds the hex textual representation
     64     # setup
     65     # . clear-stream(_test-stream)
     66     # . . push args
     67     68/push  _test-stream/imm32
     68     # . . call
     69     e8/call  clear-stream/disp32
     70     # . . discard args
     71     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     72     # append-byte-hex(_test-stream, 0xa)  # exercises digit, non-digit as well as leading zero
     73     # . . push args
     74     68/push  0xa/imm32
     75     68/push  _test-stream/imm32
     76     # . . call
     77     e8/call  append-byte-hex/disp32
     78     # . . discard args
     79     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     80     # check-stream-equal(_test-stream, "0a", msg)
     81     # . . push args
     82     68/push  "F - test-append-byte-hex"/imm32
     83     68/push  "0a"/imm32
     84     68/push  _test-stream/imm32
     85     # . . call
     86     e8/call  check-stream-equal/disp32
     87     # . . discard args
     88     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     89     # . end
     90     c3/return
     91
     92 write-int32-hex:  # f: (addr stream byte), n: int
     93     # . prologue
     94     55/push-ebp
     95     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     96 $write-int32-hex:hex-prefix:
     97     # write(f, "0x")
     98     # . . push args
     99     68/push  "0x"/imm32
    100     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    101     # . . call
    102     e8/call  write/disp32
    103     # . . discard args
    104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    105 $write-int32-hex:rest:
    106     # write-int32-hex-bits(f, n, 32)
    107     # . . push args
    108     68/push  0x20/imm32
    109     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    110     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    111     # . . call
    112     e8/call  write-int32-hex-bits/disp32
    113     # . . discard args
    114     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    115 $write-int32-hex:end:
    116     # . epilogue
    117     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    118     5d/pop-to-ebp
    119     c3/return
    120
    121 # print rightmost 'bits' of 'n'
    122 # bits must be multiple of 4
    123 write-int32-hex-bits:  # f: (addr stream byte), n: int, bits: int
    124     # pseudocode:
    125     #  bits -= 4
    126     #  while true
    127     #    if (bits < 0) break
    128     #    eax = n >> bits
    129     #    eax = eax & 0xf
    130     #    append-byte(f, AL)
    131     #    bits -= 4
    132     #
    133     # . prologue
    134     55/push-ebp
    135     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    136     # . save registers
    137     50/push-eax
    138     51/push-ecx
    139     # ecx = bits-4
    140     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
    141     81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # subtract from ecx
    142 $write-int32-hex-bits:loop:
    143     # if (bits < 0) break
    144     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0/imm32           # compare ecx
    145     7c/jump-if-<  $write-int32-hex-bits:end/disp8
    146     # eax = n >> bits
    147     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    148     d3/>>ecx    5/subop/pad-zeroes  3/mod/direct    0/rm32/eax    .           .             .           .           .               .                 # shift eax right by ecx bits, padding zeroes
    149     # eax = to-hex-char(AL)
    150     25/and-eax  0xf/imm32
    151     e8/call  to-hex-char/disp32
    152     # append-byte(f, AL)
    153     # . . push args
    154     50/push-eax
    155     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    156     # . . call
    157     e8/call  append-byte/disp32
    158     # . . discard args
    159     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    160     # bits -= 4
    161     81          5/subop/subtract    3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # subtract from ecx
    162     eb/jump  $write-int32-hex-bits:loop/disp8
    163 $write-int32-hex-bits:end:
    164     # . restore registers
    165     59/pop-to-ecx
    166     58/pop-to-eax
    167     # . epilogue
    168     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    169     5d/pop-to-ebp
    170     c3/return
    171
    172 test-write-int32-hex:
    173     # - check that write-int32-hex prints the hex textual representation
    174     # setup
    175     # . clear-stream(_test-stream)
    176     # . . push args
    177     68/push  _test-stream/imm32
    178     # . . call
    179     e8/call  clear-stream/disp32
    180     # . . discard args
    181     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    182     # write-int32-hex(_test-stream, 0x8899aa)
    183     # . . push args
    184     68/push  0x8899aa/imm32
    185     68/push  _test-stream/imm32
    186     # . . call
    187     e8/call  write-int32-hex/disp32
    188     # . . discard args
    189     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    190     # check-stream-equal(_test-stream, "0x008899aa", msg)
    191     # . . push args
    192     68/push  "F - test-write-int32-hex"/imm32
    193     68/push  "0x008899aa"/imm32
    194     68/push  _test-stream/imm32
    195     # . . call
    196     e8/call  check-stream-equal/disp32
    197     # . . discard args
    198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    199     # . end
    200     c3/return
    201
    202 # . . vim:nowrap:textwidth=0