https://github.com/akkartik/mu/blob/main/linux/130emit.subx

      1 == code
      2 #   instruction                     effective address                                                   register    displacement    immediate
      3 # . op          subop               mod             rm32          base        index         scale       r32
      4 # . 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
      5
      6 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
      7 # it in 'width' bytes of hex, least significant first.
      8 # Otherwise just print the entire word including metadata.
      9 # Always print a trailing space.
     10 emit:  # out: (addr buffered-file), word: (addr slice), width: int
     11     # . prologue
     12     55/push-ebp
     13     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     14     # . save registers
     15     50/push-eax
     16     56/push-esi
     17     57/push-edi
     18     # esi = word
     19     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
     20     # var datum/edi: slice
     21     68/push  0/imm32/end
     22     68/push  0/imm32/start
     23     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
     24     # datum = next-token-from-slice(word->start, word->end, '/')
     25     # . . push args
     26     57/push-edi
     27     68/push  0x2f/imm32/slash
     28     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     29     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     30     # . . call
     31     e8/call  next-token-from-slice/disp32
     32     # . . discard args
     33     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     34     # if (valid-name?(datum)) write-slice-buffered(out, word) and return
     35     # . eax = valid-name?(name)
     36     # . . push args
     37     57/push-edi
     38     # . . call
     39     e8/call  valid-name?/disp32
     40     # . . discard args
     41     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     42     # . if (eax != false)
     43     3d/compare-eax-and  0/imm32/false
     44     74/jump-if-=  $emit:hex-int/disp8
     45 $emit:name:
     46     # . write-slice-buffered(out, word)
     47     # . . push args
     48     56/push-esi
     49     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     50     # . . call
     51     e8/call  write-slice-buffered/disp32
     52     # . . discard args
     53     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     54     # . write-buffered(out, " ")
     55     # . . push args
     56     68/push  Space/imm32
     57     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     58     # . . call
     59     e8/call  write-buffered/disp32
     60     # . . discard args
     61     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     62     # . return
     63     eb/jump  $emit:end/disp8
     64     # otherwise emit-hex(out, parse-hex-int-from-slice(datum), width)
     65     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
     66     #   name or a hex number. `emit` is mostly used by translate_subx, which
     67     #   is currently designed to only receive legal SubX programs. We just
     68     #   want to make sure that valid names aren't treated as (valid) hex
     69     #   numbers.)
     70 $emit:hex-int:
     71     # . var value/eax: int = parse-hex-int-from-slice(datum)
     72     # . . push args
     73     57/push-edi
     74     # . . call
     75     e8/call  parse-hex-int-from-slice/disp32
     76     # . . discard args
     77     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     78     # . emit-hex(out, value, width)
     79     # . . push args
     80     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     81     50/push-eax
     82     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     83     # . . call
     84     e8/call  emit-hex/disp32
     85     # . . discard args
     86     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     87 $emit:end:
     88     # . reclaim locals
     89     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     90     # . restore registers
     91     5f/pop-to-edi
     92     5e/pop-to-esi
     93     58/pop-to-eax
     94     # . epilogue
     95     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     96     5d/pop-to-ebp
     97     c3/return
     98
     99 test-emit-number:
    100     # . prologue
    101     55/push-ebp
    102     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    103     # setup
    104     # . clear-stream(_test-output-stream)
    105     # . . push args
    106     68/push  _test-output-stream/imm32
    107     # . . call
    108     e8/call  clear-stream/disp32
    109     # . . discard args
    110     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    111     # . clear-stream($_test-output-buffered-file->buffer)
    112     # . . push args
    113     68/push  $_test-output-buffered-file->buffer/imm32
    114     # . . call
    115     e8/call  clear-stream/disp32
    116     # . . discard args
    117     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    118     # (eax..ecx) = "30"
    119     b8/copy-to-eax  "30"/imm32
    120     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    121     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
    122     05/add-to-eax  4/imm32
    123     # var slice/ecx: slice = {eax, ecx}
    124     51/push-ecx
    125     50/push-eax
    126     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    127     # emit(_test-output-buffered-file, slice, 1)
    128     # . . push args
    129     68/push  1/imm32
    130     51/push-ecx
    131     68/push  _test-output-buffered-file/imm32
    132     # . . call
    133     e8/call  emit/disp32
    134     # . . discard args
    135     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    136     # flush(_test-output-buffered-file)
    137     # . . push args
    138     68/push  _test-output-buffered-file/imm32
    139     # . . call
    140     e8/call  flush/disp32
    141     # . . discard args
    142     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    143     # check-stream-equal(_test-output-stream, "30 ", msg)
    144     # . . push args
    145     68/push  "F - test-emit-number/1"/imm32
    146     68/push  "30 "/imm32
    147     68/push  _test-output-stream/imm32
    148     # . . call
    149     e8/call  check-stream-equal/disp32
    150     # . . discard args
    151     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    152     # . epilogue
    153     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    154     5d/pop-to-ebp
    155     c3/return
    156
    157 test-emit-negative-number:
    158     # test support for sign-extending negative numbers
    159     # . prologue
    160     55/push-ebp
    161     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    162     # setup
    163     # . clear-stream(_test-output-stream)
    164     # . . push args
    165     68/push  _test-output-stream/imm32
    166     # . . call
    167     e8/call  clear-stream/disp32
    168     # . . discard args
    169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    170     # . clear-stream($_test-output-buffered-file->buffer)
    171     # . . push args
    172     68/push  $_test-output-buffered-file->buffer/imm32
    173     # . . call
    174     e8/call  clear-stream/disp32
    175     # . . discard args
    176     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    177     # (eax..ecx) = "-2"
    178     b8/copy-to-eax  "-2"/imm32
    179     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    180     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
    181     05/add-to-eax  4/imm32
    182     # var slice/ecx: slice = {eax, ecx}
    183     51/push-ecx
    184     50/push-eax
    185     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    186     # emit(_test-output-buffered-file, slice, 2)
    187     # . . push args
    188     68/push  2/imm32
    189     51/push-ecx
    190     68/push  _test-output-buffered-file/imm32
    191     # . . call
    192     e8/call  emit/disp32
    193     # . . discard args
    194     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    195     # flush(_test-output-buffered-file)
    196     # . . push args
    197     68/push  _test-output-buffered-file/imm32
    198     # . . call
    199     e8/call  flush/disp32
    200     # . . discard args
    201     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    202     # check-stream-equal(_test-output-stream, "fe ff ", msg)
    203     # . . push args
    204     68/push  "F - test-emit-number/1"/imm32
    205     68/push  "fe ff "/imm32
    206     68/push  _test-output-stream/imm32
    207     # . . call
    208     e8/call  check-stream-equal/disp32
    209     # . . discard args
    210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    211     # . epilogue
    212     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    213     5d/pop-to-ebp
    214     c3/return
    215
    216 test-emit-number-with-metadata:
    217     # . prologue
    218     55/push-ebp
    219     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    220     # setup
    221     # . clear-stream(_test-output-stream)
    222     # . . push args
    223     68/push  _test-output-stream/imm32
    224     # . . call
    225     e8/call  clear-stream/disp32
    226     # . . discard args
    227     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    228     # . clear-stream($_test-output-buffered-file->buffer)
    229     # . . push args
    230     68/push  $_test-output-buffered-file->buffer/imm32
    231     # . . call
    232     e8/call  clear-stream/disp32
    233     # . . discard args
    234     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    235     # (eax..ecx) = "-2/foo"
    236     b8/copy-to-eax  "-2/foo"/imm32
    237     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    238     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
    239     05/add-to-eax  4/imm32
    240     # var slice/ecx: slice = {eax, ecx}
    241     51/push-ecx
    242     50/push-eax
    243     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    244     # emit(_test-output-buffered-file, slice, 2)
    245     # . . push args
    246     68/push  2/imm32
    247     51/push-ecx
    248     68/push  _test-output-buffered-file/imm32
    249     # . . call
    250     e8/call  emit/disp32
    251     # . . discard args
    252     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    253     # flush(_test-output-buffered-file)
    254     # . . push args
    255     68/push  _test-output-buffered-file/imm32
    256     # . . call
    257     e8/call  flush/disp32
    258     # . . discard args
    259     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    260     # the '/foo' will have no impact on the output
    261     # check-stream-equal(_test-output-stream, "fe ff ", msg)
    262     # . . push args
    263     68/push  "F - test-emit-number-with-metadata"/imm32
    264     68/push  "fe ff "/imm32
    265     68/push  _test-output-stream/imm32
    266     # . . call
    267     e8/call  check-stream-equal/disp32
    268     # . . discard args
    269     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    270     # . epilogue
    271     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    272     5d/pop-to-ebp
    273     c3/return
    274
    275 test-emit-non-number:
    276     # . prologue
    277     55/push-ebp
    278     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    279     # setup
    280     # . clear-stream(_test-output-stream)
    281     # . . push args
    282     68/push  _test-output-stream/imm32
    283     # . . call
    284     e8/call  clear-stream/disp32
    285     # . . discard args
    286     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    287     # . clear-stream($_test-output-buffered-file->buffer)
    288     # . . push args
    289     68/push  $_test-output-buffered-file->buffer/imm32
    290     # . . call
    291     e8/call  clear-stream/disp32
    292     # . . discard args
    293     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    294     # (eax..ecx) = "xyz"
    295     b8/copy-to-eax  "xyz"/imm32
    296     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    297     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
    298     05/add-to-eax  4/imm32
    299     # var slice/ecx: slice = {eax, ecx}
    300     51/push-ecx
    301     50/push-eax
    302     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    303     # emit(_test-output-buffered-file, slice, 2)
    304     # . . push args
    305     68/push  2/imm32
    306     51/push-ecx
    307     68/push  _test-output-buffered-file/imm32
    308     # . . call
    309     e8/call  emit/disp32
    310     # . . discard args
    311     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    312     # flush(_test-output-buffered-file)
    313     # . . push args
    314     68/push  _test-output-buffered-file/imm32
    315     # . . call
    316     e8/call  flush/disp32
    317     # . . discard args
    318     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    319     # check-stream-equal(_test-output-stream, "xyz", msg)
    320     # . . push args
    321     68/push  "F - test-emit-non-number"/imm32
    322     68/push  "xyz "/imm32
    323     68/push  _test-output-stream/imm32
    324     # . . call
    325     e8/call  check-stream-equal/disp32
    326     # . . discard args
    327     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    328     # . epilogue
    329     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    330     5d/pop-to-ebp
    331     c3/return
    332
    333 test-emit-non-number-with-metadata:
    334     # . prologue
    335     55/push-ebp
    336     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    337     # setup
    338     # . clear-stream(_test-output-stream)
    339     # . . push args
    340     68/push  _test-output-stream/imm32
    341     # . . call
    342     e8/call  clear-stream/disp32
    343     # . . discard args
    344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    345     # . clear-stream($_test-output-buffered-file->buffer)
    346     # . . push args
    347     68/push  $_test-output-buffered-file->buffer/imm32
    348     # . . call
    349     e8/call  clear-stream/disp32
    350     # . . discard args
    351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    352     # (eax..ecx) = "xyz/"
    353     b8/copy-to-eax  "xyz/"/imm32
    354     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    355     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
    356     05/add-to-eax  4/imm32
    357     # var slice/ecx: slice = {eax, ecx}
    358     51/push-ecx
    359     50/push-eax
    360     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    361     # emit(_test-output-buffered-file, slice, 2)
    362     # . . push args
    363     68/push  2/imm32
    364     51/push-ecx
    365     68/push  _test-output-buffered-file/imm32
    366     # . . call
    367     e8/call  emit/disp32
    368     # . . discard args
    369     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    370     # flush(_test-output-buffered-file)
    371     # . . push args
    372     68/push  _test-output-buffered-file/imm32
    373     # . . call
    374     e8/call  flush/disp32
    375     # . . discard args
    376     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    377     # check-stream-equal(_test-output-stream, "xyz/", msg)
    378     # . . push args
    379     68/push  "F - test-emit-non-number-with-metadata"/imm32
    380     68/push  "xyz/ "/imm32
    381     68/push  _test-output-stream/imm32
    382     # . . call
    383     e8/call  check-stream-equal/disp32
    384     # . . discard args
    385     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    386     # . epilogue
    387     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    388     5d/pop-to-ebp
    389     c3/return
    390
    391 test-emit-non-number-with-all-hex-digits-and-metadata:
    392     # . prologue
    393     55/push-ebp
    394     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    395     # setup
    396     # . clear-stream(_test-output-stream)
    397     # . . push args
    398     68/push  _test-output-stream/imm32
    399     # . . call
    400     e8/call  clear-stream/disp32
    401     # . . discard args
    402     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    403     # . clear-stream($_test-output-buffered-file->buffer)
    404     # . . push args
    405     68/push  $_test-output-buffered-file->buffer/imm32
    406     # . . call
    407     e8/call  clear-stream/disp32
    408     # . . discard args
    409     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    410     # (eax..ecx) = "abcd/xyz"
    411     b8/copy-to-eax  "abcd/xyz"/imm32
    412     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    413     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
    414     05/add-to-eax  4/imm32
    415     # var slice/ecx: slice = {eax, ecx}
    416     51/push-ecx
    417     50/push-eax
    418     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    419     # emit(_test-output-buffered-file, slice, 2)
    420     # . . push args
    421     68/push  2/imm32
    422     51/push-ecx
    423     68/push  _test-output-buffered-file/imm32
    424     # . . call
    425     e8/call  emit/disp32
    426     # . . discard args
    427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    428     # flush(_test-output-buffered-file)
    429     # . . push args
    430     68/push  _test-output-buffered-file/imm32
    431     # . . call
    432     e8/call  flush/disp32
    433     # . . discard args
    434     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    435 +-- 26 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    461     # check-stream-equal(_test-output-stream, "abcd/xyz")
    462     # . . push args
    463     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
    464     68/push  "abcd/xyz "/imm32
    465     68/push  _test-output-stream/imm32
    466     # . . call
    467     e8/call  check-stream-equal/disp32
    468     # . . discard args
    469     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    470     # . epilogue
    471     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    472     5d/pop-to-ebp
    473     c3/return