https://github.com/akkartik/mu/blob/master/subx/074print-int-decimal.subx

      1 # Helper to print an int32 in decimal.
      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 #? Entry:  # run a single test, while debugging
      9 #?     e8/call test-print-int32-decimal/disp32
     10 #?     # syscall(exit, Num-test-failures)
     11 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
     12 #?     b8/copy-to-EAX  1/imm32/exit
     13 #?     cd/syscall  0x80/imm8
     14
     15 print-int32-decimal:  # out : (address stream), n : int32
     16     # works by generating characters from lowest to highest and pushing them
     17     # to the stack, before popping them one by one into the stream
     18     #
     19     # pseudocode:
     20     #   copy ESP to EBX
     21     #   EAX = |n|
     22     #   while true
     23     #     if (EAX == 0) break
     24     #     sign-extend EAX into EDX
     25     #     EAX, EDX = EAX/10, EAX%10
     26     #     push EDX
     27     #   if n < 0
     28     #     push '-' - '0' = -3
     29     #   max/ECX = &out->data[out->length]
     30     #   w/EAX = out->write
     31     #   curr/EDI = &out->data[out->write]
     32     #   while true
     33     #     if (ESP == EBX) break
     34     #     if (curr >= max) abort
     35     #     pop into EDX
     36     #     EDX += '0'  # convert decimal digit to ascii
     37     #     *curr = DL
     38     #     ++curr
     39     #     ++w
     40     #   out->write = w
     41     #
     42     # . prolog
     43     55/push-EBP
     44     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     45     # . save registers
     46     50/push-EAX
     47     51/push-ECX
     48     52/push-EDX
     49     53/push-EBX
     50     57/push-EDI
     51     # copy ESP to EBX
     52     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
     53     # ten/ECX = 10
     54     b9/copy-to-ECX  0xa/imm32
     55     # EAX = |n|
     56     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
     57     3d/compare-EAX-with  0/imm32
     58     7d/jump-if-greater-or-equal $print-int32-decimal:read-loop/disp8
     59 $print-int32-decimal:negative:
     60     f7          3/subop/negate      3/mod/direct    0/rm32/EAX    .           .             .           .           .               .                 # negate EAX
     61 $print-int32-decimal:read-loop:
     62     # if (EAX == 0) break
     63     3d/compare-EAX-and  0/imm32
     64     74/jump-if-equal $print-int32-decimal:read-break/disp8
     65     # sign-extend
     66     99/sign-extend-EAX-into-EDX
     67     # EAX, EDX = divide-with-remainder EAX/ten, EAX%ten
     68     f7          7/subop/divide-by   3/mod/direct    1/rm32/ECX    .           .             .           .           .               .                 # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX
     69     # push EDX
     70     52/push-EDX
     71     eb/jump  $print-int32-decimal:read-loop/disp8
     72 $print-int32-decimal:read-break:
     73     # if (n < 0) push('-')
     74     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       0/imm32           # compare *(EBP+12)
     75     7d/jump-if-greater-or-equal $print-int32-decimal:write/disp8
     76 $print-int32-decimal:push-negative:
     77     68/push  -3/imm32/dash-minus-zero
     78     # fall through
     79 $print-int32-decimal:write:
     80     # EDI = out
     81     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
     82     # max/ECX = &out->data[out->length]
     83     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EDI+8) to ECX
     84     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy EDI+ECX+12 to ECX
     85     # w/EAX = out->write
     86     8b/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy *EDI to EAX
     87     # curr/EDI = &out->data[out->write]
     88     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    7/base/EDI  0/index/EAX   .           7/r32/EDI   0xc/disp8       .                 # copy EDI+EAX+12 to EDI
     89 $print-int32-decimal:write-loop:
     90     # if (ESP == EBX) break
     91     39/compare                      3/mod/direct    4/rm32/ESP    .           .             .           3/r32/EBX   .               .                 # compare ESP and EBX
     92     74/jump-if-equal  $print-int32-decimal:write-break/disp8
     93     # if (curr >= max) abort
     94     39/compare                      3/mod/direct    7/rm32/EDI    .           .             .           1/r32/ECX   .               .                 # compare EDI and ECX
     95     7d/jump-if-greater-or-equal  $print-int32-decimal:abort/disp8
     96     # pop into EDX
     97     5a/pop-into-EDX
     98     # EDX += '0'
     99     81          0/subop/add         3/mod/direct    2/rm32/EDX    .           .             .           .           .               0x30/imm32/zero   # add to EDX
    100 $print-int32-decimal:write-char:
    101     # *curr = DL
    102     88/copy-byte                    0/mod/indirect  7/rm32/EDI    .           .             .           2/r32/DL    .               .                 # copy DL to byte at *ECX
    103     # ++curr
    104     47/increment-EDI
    105     # ++w
    106     40/increment-EAX
    107     eb/jump  $print-int32-decimal:write-loop/disp8
    108 $print-int32-decimal:write-break:
    109     # out->write = w
    110     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
    111     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
    112 $print-int32-decimal:end:
    113     # . restore registers
    114     5f/pop-to-EDI
    115     5b/pop-to-EBX
    116     5a/pop-to-EDX
    117     59/pop-to-ECX
    118     58/pop-to-EAX
    119     # . epilog
    120     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    121     5d/pop-to-EBP
    122     c3/return
    123
    124 $print-int32-decimal:abort:
    125     # . _write(2/stderr, error)
    126     # . . push args
    127     68/push  "print-int32-decimal: out of space"/imm32
    128     68/push  2/imm32/stderr
    129     # . . call
    130     e8/call  _write/disp32
    131     # . . discard args
    132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    133     # . syscall(exit, 1)
    134     bb/copy-to-EBX  1/imm32
    135     b8/copy-to-EAX  1/imm32/exit
    136     cd/syscall  0x80/imm8
    137     # never gets here
    138
    139 test-print-int32-decimal:
    140     # - check that a single-digit number converts correctly
    141     # setup
    142     # . clear-stream(_test-stream)
    143     # . . push args
    144     68/push  _test-stream/imm32
    145     # . . call
    146     e8/call  clear-stream/disp32
    147     # . . discard args
    148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    149     # print-int32-decimal(_test-stream, 9)
    150     # . . push args
    151     68/push  9/imm32
    152     68/push  _test-stream/imm32
    153     # . . call
    154     e8/call  print-int32-decimal/disp32
    155     # . . discard args
    156     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    157     # check-stream-equal(_test-stream, "9", msg)
    158     # . . push args
    159     68/push  "F - test-print-int32-decimal"/imm32
    160     68/push  "9"/imm32
    161     68/push  _test-stream/imm32
    162     # . . call
    163     e8/call  check-stream-equal/disp32
    164     # . . discard args
    165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    166     # . end
    167     c3/return
    168
    169 test-print-int32-decimal-multiple-digits:
    170     # - check that a multi-digit number converts correctly
    171     # setup
    172     # . clear-stream(_test-stream)
    173     # . . push args
    174     68/push  _test-stream/imm32
    175     # . . call
    176     e8/call  clear-stream/disp32
    177     # . . discard args
    178     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    179     # print-int32-decimal(_test-stream, 10)
    180     # . . push args
    181     68/push  0xa/imm32
    182     68/push  _test-stream/imm32
    183     # . . call
    184     e8/call  print-int32-decimal/disp32
    185     # . . discard args
    186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    187     # check-stream-equal(_test-stream, "10", msg)
    188     # . . push args
    189     68/push  "F - test-print-int32-decimal-multiple-digits"/imm32
    190     68/push  "10"/imm32
    191     68/push  _test-stream/imm32
    192     # . . call
    193     e8/call  check-stream-equal/disp32
    194     # . . discard args
    195     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    196     # . end
    197     c3/return
    198
    199 test-print-int32-decimal-negative:
    200     # - check that a negative single-digit number converts correctly
    201     # setup
    202     # . clear-stream(_test-stream)
    203     # . . push args
    204     68/push  _test-stream/imm32
    205     # . . call
    206     e8/call  clear-stream/disp32
    207     # . . discard args
    208     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    209     # print-int32-decimal(_test-stream, -9)
    210     # . . push args
    211     68/push  -9/imm32
    212     68/push  _test-stream/imm32
    213     # . . call
    214     e8/call  print-int32-decimal/disp32
    215     # . . discard args
    216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    217 +-- 26 lines: #?     # dump _test-stream ---------------------------------------------------------------------------------------------------------------------
    243     # check-stream-equal(_test-stream, "-9", msg)
    244     # . . push args
    245     68/push  "F - test-print-int32-decimal-negative"/imm32
    246     68/push  "-9"/imm32
    247     68/push  _test-stream/imm32
    248     # . . call
    249     e8/call  check-stream-equal/disp32
    250     # . . discard args
    251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    252     # . end
    253     c3/return
    254
    255 test-print-int32-decimal-negative-multiple-digits:
    256     # - check that a multi-digit number converts correctly
    257     # setup
    258     # . clear-stream(_test-stream)
    259     # . . push args
    260     68/push  _test-stream/imm32
    261     # . . call
    262     e8/call  clear-stream/disp32
    263     # . . discard args
    264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    265     # print-int32-decimal(_test-stream, -10)
    266     # . . push args
    267     68/push  -0xa/imm32
    268     68/push  _test-stream/imm32
    269     # . . call
    270     e8/call  print-int32-decimal/disp32
    271     # . . discard args
    272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    273     # check-stream-equal(_test-stream, "-10", msg)
    274     # . . push args
    275     68/push  "F - test-print-int32-decimal-negative-multiple-digits"/imm32
    276     68/push  "-10"/imm32
    277     68/push  _test-stream/imm32
    278     # . . call
    279     e8/call  check-stream-equal/disp32
    280     # . . discard args
    281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    282     # . end
    283     c3/return
    284
    285 # . . vim:nowrap:textwidth=0