https://github.com/akkartik/mu/blob/master/subx/apps/pack.subx

       1 # Read a text file of SubX instructions from stdin, and convert it into a list
       2 # of whitespace-separated ascii hex bytes on stdout. Label definitions and
       3 # uses are left untouched.
       4 #
       5 # To run (from the subx/ directory):
       6 #   $ ./subx translate *.subx apps/pack.subx -o apps/pack
       7 #   $ echo '05/add-to-EAX 0x20/imm32'  |./subx run apps/pack
       8 # Expected output:
       9 #   05 20 00 00 00  # 05/add-to-EAX 0x20/imm32
      10 # The original instruction gets included as a comment at the end of each
      11 # converted line.
      12 #
      13 # There's zero error-checking. For now we assume the input program is valid.
      14 # We'll continue to rely on the C++ version for error messages.
      15
      16 == code
      17 #   instruction                     effective address                                                   register    displacement    immediate
      18 # . op          subop               mod             rm32          base        index         scale       r32
      19 # . 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
      20
      21 Entry:  # run tests if necessary, convert stdin if not
      22
      23     # for debugging: run a single test
      24 #?     e8/call test-emit-non-number-with-all-hex-digits-and-metadata/disp32
      25 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
      26 #?     eb/jump  $main:end/disp8
      27
      28     # . prolog
      29     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
      30     # - if argc > 1 and argv[1] == "test", then return run_tests()
      31     # . argc > 1
      32     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
      33     7e/jump-if-lesser-or-equal  $run-main/disp8
      34     # . argv[1] == "test"
      35     # . . push args
      36     68/push  "test"/imm32
      37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
      38     # . . call
      39     e8/call  kernel-string-equal?/disp32
      40     # . . discard args
      41     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
      42     # . check result
      43     3d/compare-EAX-and  1/imm32
      44     75/jump-if-not-equal  $run-main/disp8
      45     # . run-tests()
      46     e8/call  run-tests/disp32
      47     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
      48     eb/jump  $main:end/disp8
      49 $run-main:
      50     # - otherwise convert stdin
      51     # var ed/EAX : exit-descriptor
      52     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
      53     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
      54     # configure ed to really exit()
      55     # . ed->target = 0
      56     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
      57     # return convert(Stdin, 1/stdout, 2/stderr, ed)
      58     # . . push args
      59     50/push-EAX/ed
      60     68/push  Stderr/imm32
      61     68/push  Stdout/imm32
      62     68/push  Stdin/imm32
      63     # . . call
      64     e8/call  convert/disp32
      65     # . . discard args
      66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
      67     # . syscall(exit, 0)
      68     bb/copy-to-EBX  0/imm32
      69 $main:end:
      70     b8/copy-to-EAX  1/imm32/exit
      71     cd/syscall  0x80/imm8
      72
      73 # - big picture
      74 # We'll operate on each line/instruction in isolation. That way we only need to
      75 # allocate memory for converting a single instruction.
      76 #
      77 # To pack an entire file, convert every segment in it
      78 # To convert a code segment, convert every instruction (line) until the next segment header
      79 # To convert a non-data segment, convert every word until the next segment header
      80 #
      81 # primary state: line
      82 #   stream of 512 bytes; abort if it ever overflows
      83
      84 # conceptual hierarchy within a line:
      85 #   line = words separated by ' ', maybe followed by comment starting with '#'
      86 #   word = datum until '/', then 0 or more metadata separated by '/'
      87 #
      88 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
      89 #   next-token(stream, delim char) -> slice (start, end pointers)
      90 #   next-token-from-slice(start, end, delim char) -> slice
      91 #   slice-equal?(slice, string)
      92
      93 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
      94     # pseudocode:
      95     #   var line = new-stream(512, 1)
      96     #   var in-code? = false
      97     #   while true
      98     #     clear-stream(line)
      99     #     read-line(in, line)
     100     #     if (line->write == 0) break             # end of file
     101     #     var word-slice = next-word(line)
     102     #     if slice-empty?(word-slice)             # whitespace
     103     #       write-stream-data(out, line)
     104     #     else if (slice-equal?(word-slice, "=="))
     105     #       word-slice = next-word(line)
     106     #       in-code? = slice-equal?(word-slice, "code")
     107     #       write-stream-data(out, line)
     108     #     else if (in-code?)
     109     #       rewind-stream(line)
     110     #       convert-instruction(line, out)
     111     #     else
     112     #       rewind-stream(line)
     113     #       convert-data(line, out)
     114     #   flush(out)
     115     #
     116     # . prolog
     117     55/push-EBP
     118     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     119     # . save registers
     120     50/push-EAX
     121     51/push-ECX
     122     52/push-EDX
     123     53/push-EBX
     124     # var line/ECX : (address stream byte) = stream(512)
     125     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
     126     68/push  0x200/imm32/length
     127     68/push  0/imm32/read
     128     68/push  0/imm32/write
     129     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     130     # var word-slice/EDX = {0, 0}
     131     68/push  0/imm32/end
     132     68/push  0/imm32/curr
     133     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
     134     # var in-code?/EBX = false
     135     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
     136 $convert:loop:
     137     # clear-stream(line)
     138     # . . push args
     139     51/push-ECX
     140     # . . call
     141     e8/call  clear-stream/disp32
     142     # . . discard args
     143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     144     # read-line(in, line)
     145     # . . push args
     146     51/push-ECX
     147     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     148     # . . call
     149     e8/call  read-line/disp32
     150     # . . discard args
     151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     152 $convert:check0:
     153     # if (line->write == 0) break
     154     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
     155     0f 84/jump-if-equal  $convert:break/disp32
     156 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
     182     # next-word(line, word-slice)
     183     # . . push args
     184     52/push-EDX
     185     51/push-ECX
     186     # . . call
     187     e8/call  next-word/disp32
     188     # . . discard args
     189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     190 $convert:check1:
     191     # if (slice-empty?(word-slice)) write-stream-data(out, line)
     192     # . EAX = slice-empty?(word-slice)
     193     # . . push args
     194     52/push-EDX
     195     # . . call
     196     e8/call  slice-empty?/disp32
     197     # . . discard args
     198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     199     # . if (EAX != 0) write-stream-data(out, line)
     200     3d/compare-EAX-and  0/imm32
     201     0f 85/jump-if-not-equal  $convert:pass-through/disp32
     202 $convert:check2:
     203 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
     245     # if (slice-equal?(word-slice, "=="))
     246     #   word-slice = next-word(line)
     247     #   in-code? = slice-equal?(word-slice, "code")
     248     #   write-stream-data(out, line)
     249     # . EAX = slice-equal?(word-slice, "==")
     250     # . . push args
     251     68/push  "=="/imm32
     252     52/push-EDX
     253     # . . call
     254     e8/call  slice-equal?/disp32
     255     # . . discard args
     256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     257     # . if (EAX == 0) goto check3
     258     3d/compare-EAX-and  0/imm32
     259     0f 84/jump-if-equal  $convert:check3/disp32
     260     # . next-word(line, word-slice)
     261     # . . push args
     262     52/push-EDX
     263     51/push-ECX
     264     # . . call
     265     e8/call  next-word/disp32
     266     # . . discard args
     267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     268 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
     310     # . in-code? = slice-equal?(word-slice, "code")
     311     # . . push args
     312     68/push  "code"/imm32
     313     52/push-EDX
     314     # . . call
     315     e8/call  slice-equal?/disp32
     316     # . . discard args
     317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     318     # . . in-code? = EAX
     319     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
     320     # . goto pass-through
     321     eb/jump  $convert:pass-through/disp8
     322 $convert:check3:
     323     # else rewind-stream(line)
     324     # . rewind-stream(line)
     325     # . . push args
     326     51/push-ECX
     327     # . . call
     328     e8/call  rewind-stream/disp32
     329     # . . discard args
     330     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     331     # if (in-code? != 0) convert-instruction(line, out)
     332     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
     333     74/jump-if-equal  $convert:data/disp8
     334 $convert:code:
     335     # . convert-instruction(line, out)
     336     # . . push args
     337     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     338     51/push-ECX
     339     # . . call
     340     e8/call  convert-instruction/disp32
     341     # . . discard args
     342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     343     # . loop
     344     e9/jump  $convert:loop/disp32
     345 $convert:data:
     346     # else convert-data(line, out)
     347     # . . push args
     348     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     349     51/push-ECX
     350     # . . call
     351     e8/call  convert-data/disp32
     352     # . . discard args
     353     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     354     # . loop
     355     e9/jump  $convert:loop/disp32
     356 $convert:pass-through:
     357     # write-stream-data(out, line)
     358     # . . push args
     359     51/push-ECX
     360     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     361     # . . call
     362     e8/call  write-stream-data/disp32
     363     # . . discard args
     364     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     365     # . loop
     366     e9/jump  $convert:loop/disp32
     367 $convert:break:
     368     # flush(out)
     369     # . . push args
     370     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
     371     # . . call
     372     e8/call  flush/disp32
     373     # . . discard args
     374     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     375 $convert:end:
     376     # . reclaim locals
     377     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
     378     # . restore registers
     379     5b/pop-to-EBX
     380     5a/pop-to-EDX
     381     59/pop-to-ECX
     382     58/pop-to-EAX
     383     # . epilog
     384     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     385     5d/pop-to-EBP
     386     c3/return
     387
     388 test-convert-passes-empty-lines-through:
     389     # if a line is empty, pass it along unchanged
     390     # . prolog
     391     55/push-EBP
     392     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     393     # setup
     394     # . clear-stream(_test-input-stream)
     395     # . . push args
     396     68/push  _test-input-stream/imm32
     397     # . . call
     398     e8/call  clear-stream/disp32
     399     # . . discard args
     400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     401     # . clear-stream(_test-input-buffered-file+4)
     402     # . . push args
     403     b8/copy-to-EAX  _test-input-buffered-file/imm32
     404     05/add-to-EAX  4/imm32
     405     50/push-EAX
     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     # . clear-stream(_test-output-stream)
     411     # . . push args
     412     68/push  _test-output-stream/imm32
     413     # . . call
     414     e8/call  clear-stream/disp32
     415     # . . discard args
     416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     417     # . clear-stream(_test-output-buffered-file+4)
     418     # . . push args
     419     b8/copy-to-EAX  _test-output-buffered-file/imm32
     420     05/add-to-EAX  4/imm32
     421     50/push-EAX
     422     # . . call
     423     e8/call  clear-stream/disp32
     424     # . . discard args
     425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     426     # write nothing to input
     427     # convert(_test-input-buffered-file, _test-output-buffered-file)
     428     # . . push args
     429     68/push  _test-output-buffered-file/imm32
     430     68/push  _test-input-buffered-file/imm32
     431     # . . call
     432     e8/call  convert/disp32
     433     # . . discard args
     434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     435     # check that the line just passed through
     436     # . flush(_test-output-buffered-file)
     437     # . . push args
     438     68/push  _test-output-buffered-file/imm32
     439     # . . call
     440     e8/call  flush/disp32
     441     # . . discard args
     442     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     443     # . check-stream-equal(_test-output-stream, "", msg)
     444     # . . push args
     445     68/push  "F - test-convert-passes-empty-lines-through"/imm32
     446     68/push  ""/imm32
     447     68/push  _test-output-stream/imm32
     448     # . . call
     449     e8/call  check-stream-equal/disp32
     450     # . . discard args
     451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     452     # . epilog
     453     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     454     5d/pop-to-EBP
     455     c3/return
     456
     457 test-convert-passes-lines-with-just-whitespace-through:
     458     # if a line is empty, pass it along unchanged
     459     # . prolog
     460     55/push-EBP
     461     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     462     # setup
     463     # . clear-stream(_test-input-stream)
     464     # . . push args
     465     68/push  _test-input-stream/imm32
     466     # . . call
     467     e8/call  clear-stream/disp32
     468     # . . discard args
     469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     470     # . clear-stream(_test-input-buffered-file+4)
     471     # . . push args
     472     b8/copy-to-EAX  _test-input-buffered-file/imm32
     473     05/add-to-EAX  4/imm32
     474     50/push-EAX
     475     # . . call
     476     e8/call  clear-stream/disp32
     477     # . . discard args
     478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     479     # . clear-stream(_test-output-stream)
     480     # . . push args
     481     68/push  _test-output-stream/imm32
     482     # . . call
     483     e8/call  clear-stream/disp32
     484     # . . discard args
     485     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     486     # . clear-stream(_test-output-buffered-file+4)
     487     # . . push args
     488     b8/copy-to-EAX  _test-output-buffered-file/imm32
     489     05/add-to-EAX  4/imm32
     490     50/push-EAX
     491     # . . call
     492     e8/call  clear-stream/disp32
     493     # . . discard args
     494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     495     # initialize input
     496     # . write(_test-input-stream, "    ")
     497     # . . push args
     498     68/push  "    "/imm32
     499     68/push  _test-input-stream/imm32
     500     # . . call
     501     e8/call  write/disp32
     502     # . . discard args
     503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     504     # convert(_test-input-buffered-file, _test-output-buffered-file)
     505     # . . push args
     506     68/push  _test-output-buffered-file/imm32
     507     68/push  _test-input-buffered-file/imm32
     508     # . . call
     509     e8/call  convert/disp32
     510     # . . discard args
     511     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     512     # check that the line just passed through
     513     # . flush(_test-output-buffered-file)
     514     # . . push args
     515     68/push  _test-output-buffered-file/imm32
     516     # . . call
     517     e8/call  flush/disp32
     518     # . . discard args
     519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     520     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
     521     # . . push args
     522     68/push  "F - test-convert-passes-with-just-whitespace-through"/imm32
     523     68/push  "    "/imm32
     524     68/push  _test-output-stream/imm32
     525     # . . call
     526     e8/call  check-next-stream-line-equal/disp32
     527     # . . discard args
     528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     529     # . epilog
     530     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     531     5d/pop-to-EBP
     532     c3/return
     533
     534 test-convert-passes-segment-headers-through:
     535     # if a line starts with '==', pass it along unchanged
     536     # . prolog
     537     55/push-EBP
     538     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     539     # setup
     540     # . clear-stream(_test-input-stream)
     541     # . . push args
     542     68/push  _test-input-stream/imm32
     543     # . . call
     544     e8/call  clear-stream/disp32
     545     # . . discard args
     546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     547     # . clear-stream(_test-input-buffered-file+4)
     548     # . . push args
     549     b8/copy-to-EAX  _test-input-buffered-file/imm32
     550     05/add-to-EAX  4/imm32
     551     50/push-EAX
     552     # . . call
     553     e8/call  clear-stream/disp32
     554     # . . discard args
     555     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     556     # . clear-stream(_test-output-stream)
     557     # . . push args
     558     68/push  _test-output-stream/imm32
     559     # . . call
     560     e8/call  clear-stream/disp32
     561     # . . discard args
     562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     563     # . clear-stream(_test-output-buffered-file+4)
     564     # . . push args
     565     b8/copy-to-EAX  _test-output-buffered-file/imm32
     566     05/add-to-EAX  4/imm32
     567     50/push-EAX
     568     # . . call
     569     e8/call  clear-stream/disp32
     570     # . . discard args
     571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     572     # initialize input
     573     # . write(_test-input-stream, "== abcd")
     574     # . . push args
     575     68/push  "== abcd"/imm32
     576     68/push  _test-input-stream/imm32
     577     # . . call
     578     e8/call  write/disp32
     579     # . . discard args
     580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     581     # convert(_test-input-buffered-file, _test-output-buffered-file)
     582     # . . push args
     583     68/push  _test-output-buffered-file/imm32
     584     68/push  _test-input-buffered-file/imm32
     585     # . . call
     586     e8/call  convert/disp32
     587     # . . discard args
     588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     589     # check that the line just passed through
     590     # . flush(_test-output-buffered-file)
     591     # . . push args
     592     68/push  _test-output-buffered-file/imm32
     593     # . . call
     594     e8/call  flush/disp32
     595     # . . discard args
     596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     597     # . check-stream-equal(_test-output-stream, "== abcd", msg)
     598     # . . push args
     599     68/push  "F - test-convert-passes-segment-headers-through"/imm32
     600     68/push  "== abcd"/imm32
     601     68/push  _test-output-stream/imm32
     602     # . . call
     603     e8/call  check-stream-equal/disp32
     604     # . . discard args
     605     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     606     # . epilog
     607     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     608     5d/pop-to-EBP
     609     c3/return
     610
     611 test-convert-in-data-segment:
     612     # correctly process lines in the data segment
     613     # . prolog
     614     55/push-EBP
     615     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     616     # setup
     617     # . clear-stream(_test-input-stream)
     618     # . . push args
     619     68/push  _test-input-stream/imm32
     620     # . . call
     621     e8/call  clear-stream/disp32
     622     # . . discard args
     623     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     624     # . clear-stream(_test-input-buffered-file+4)
     625     # . . push args
     626     b8/copy-to-EAX  _test-input-buffered-file/imm32
     627     05/add-to-EAX  4/imm32
     628     50/push-EAX
     629     # . . call
     630     e8/call  clear-stream/disp32
     631     # . . discard args
     632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     633     # . clear-stream(_test-output-stream)
     634     # . . push args
     635     68/push  _test-output-stream/imm32
     636     # . . call
     637     e8/call  clear-stream/disp32
     638     # . . discard args
     639     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     640     # . clear-stream(_test-output-buffered-file+4)
     641     # . . push args
     642     b8/copy-to-EAX  _test-output-buffered-file/imm32
     643     05/add-to-EAX  4/imm32
     644     50/push-EAX
     645     # . . call
     646     e8/call  clear-stream/disp32
     647     # . . discard args
     648     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     649     # initialize input
     650     #   == code
     651     #   == data
     652     #   3 4/imm32
     653     # . write(_test-input-stream, "== code")
     654     # . . push args
     655     68/push  "== code\n"/imm32
     656     68/push  _test-input-stream/imm32
     657     # . . call
     658     e8/call  write/disp32
     659     # . . discard args
     660     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     661     # . write(_test-input-stream, "== data\n")
     662     # . . push args
     663     68/push  "== data\n"/imm32
     664     68/push  _test-input-stream/imm32
     665     # . . call
     666     e8/call  write/disp32
     667     # . . discard args
     668     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     669     # . write(_test-input-stream, "3 4/imm32\n")
     670     # . . push args
     671     68/push  "3 4/imm32\n"/imm32
     672     68/push  _test-input-stream/imm32
     673     # . . call
     674     e8/call  write/disp32
     675     # . . discard args
     676     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     677     # convert(_test-input-buffered-file, _test-output-buffered-file)
     678     # . . push args
     679     68/push  _test-output-buffered-file/imm32
     680     68/push  _test-input-buffered-file/imm32
     681     # . . call
     682     e8/call  convert/disp32
     683     # . . discard args
     684     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     685     # check output
     686 +-- 26 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
     712     # . flush(_test-output-buffered-file)
     713     # . . push args
     714     68/push  _test-output-buffered-file/imm32
     715     # . . call
     716     e8/call  flush/disp32
     717     # . . discard args
     718     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     719     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
     720     # . . push args
     721     68/push  "F - test-convert-in-data-segment/0"/imm32
     722     68/push  "== code"/imm32
     723     68/push  _test-output-stream/imm32
     724     # . . call
     725     e8/call  check-next-stream-line-equal/disp32
     726     # . . discard args
     727     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     728     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
     729     # . . push args
     730     68/push  "F - test-convert-in-data-segment/1"/imm32
     731     68/push  "== data"/imm32
     732     68/push  _test-output-stream/imm32
     733     # . . call
     734     e8/call  check-next-stream-line-equal/disp32
     735     # . . discard args
     736     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     737     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
     738     # . . push args
     739     68/push  "F - test-convert-in-data-segment/2"/imm32
     740     68/push  "03 04 00 00 00 "/imm32
     741     68/push  _test-output-stream/imm32
     742     # . . call
     743     e8/call  check-next-stream-line-equal/disp32
     744     # . . discard args
     745     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     746     # . epilog
     747     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     748     5d/pop-to-EBP
     749     c3/return
     750
     751 test-convert-code-and-data-segments:
     752     # correctly process lines in both code and data segments
     753     # . prolog
     754     55/push-EBP
     755     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     756     # setup
     757     # . clear-stream(_test-input-stream)
     758     # . . push args
     759     68/push  _test-input-stream/imm32
     760     # . . call
     761     e8/call  clear-stream/disp32
     762     # . . discard args
     763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     764     # . clear-stream(_test-input-buffered-file+4)
     765     # . . push args
     766     b8/copy-to-EAX  _test-input-buffered-file/imm32
     767     05/add-to-EAX  4/imm32
     768     50/push-EAX
     769     # . . call
     770     e8/call  clear-stream/disp32
     771     # . . discard args
     772     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     773     # . clear-stream(_test-output-stream)
     774     # . . push args
     775     68/push  _test-output-stream/imm32
     776     # . . call
     777     e8/call  clear-stream/disp32
     778     # . . discard args
     779     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     780     # . clear-stream(_test-output-buffered-file+4)
     781     # . . push args
     782     b8/copy-to-EAX  _test-output-buffered-file/imm32
     783     05/add-to-EAX  4/imm32
     784     50/push-EAX
     785     # . . call
     786     e8/call  clear-stream/disp32
     787     # . . discard args
     788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     789     # initialize input
     790     #   == code
     791     #   e8/call 20/disp32
     792     #   68/push 0x20/imm8
     793     #   == data
     794     #   3 4/imm32
     795     # . write(_test-input-stream, "== code\n")
     796     # . . push args
     797     68/push  "== code\n"/imm32
     798     68/push  _test-input-stream/imm32
     799     # . . call
     800     e8/call  write/disp32
     801     # . . discard args
     802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     803     # . write(_test-input-stream, "e8/call 20/disp32\n")
     804     # . . push args
     805     68/push  "e8/call 20/disp32\n"/imm32
     806     68/push  _test-input-stream/imm32
     807     # . . call
     808     e8/call  write/disp32
     809     # . . discard args
     810     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     811     # . write(_test-input-stream, "68/push 0x20/imm8\n")
     812     # . . push args
     813     68/push  "68/push 0x20/imm8\n"/imm32
     814     68/push  _test-input-stream/imm32
     815     # . . call
     816     e8/call  write/disp32
     817     # . . discard args
     818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     819     # . write(_test-input-stream, "== data\n")
     820     # . . push args
     821     68/push  "== data\n"/imm32
     822     68/push  _test-input-stream/imm32
     823     # . . call
     824     e8/call  write/disp32
     825     # . . discard args
     826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     827     # . write(_test-input-stream, "3 4/imm32\n")
     828     # . . push args
     829     68/push  "3 4/imm32\n"/imm32
     830     68/push  _test-input-stream/imm32
     831     # . . call
     832     e8/call  write/disp32
     833     # . . discard args
     834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     835     # convert(_test-input-buffered-file, _test-output-buffered-file)
     836     # . . push args
     837     68/push  _test-output-buffered-file/imm32
     838     68/push  _test-input-buffered-file/imm32
     839     # . . call
     840     e8/call  convert/disp32
     841     # . . discard args
     842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     843     # check output
     844     #   == code
     845     #   e8 20 00 00 00  # e8/call 20/disp32
     846     #   68 20  # 68/push 0x20/imm8
     847     #   == data
     848     #   03 04 00 00 00
     849 +-- 26 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
     875     # . flush(_test-output-buffered-file)
     876     # . . push args
     877     68/push  _test-output-buffered-file/imm32
     878     # . . call
     879     e8/call  flush/disp32
     880     # . . discard args
     881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
     882     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
     883     # . . push args
     884     68/push  "F - test-convert-code-and-data-segments/0"/imm32
     885     68/push  "== code"/imm32
     886     68/push  _test-output-stream/imm32
     887     # . . call
     888     e8/call  check-next-stream-line-equal/disp32
     889     # . . discard args
     890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     891     # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
     892     # . . push args
     893     68/push  "F - test-convert-code-and-data-segments/1"/imm32
     894     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
     895     68/push  _test-output-stream/imm32
     896     # . . call
     897     e8/call  check-next-stream-line-equal/disp32
     898     # . . discard args
     899     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     900     # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
     901     # . . push args
     902     68/push  "F - test-convert-code-and-data-segments/2"/imm32
     903     68/push  "68 20  # 68/push 0x20/imm8"/imm32
     904     68/push  _test-output-stream/imm32
     905     # . . call
     906     e8/call  check-next-stream-line-equal/disp32
     907     # . . discard args
     908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     909     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
     910     # . . push args
     911     68/push  "F - test-convert-code-and-data-segments/3"/imm32
     912     68/push  "== data"/imm32
     913     68/push  _test-output-stream/imm32
     914     # . . call
     915     e8/call  check-next-stream-line-equal/disp32
     916     # . . discard args
     917     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     918     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
     919     # . . push args
     920     68/push  "F - test-convert-code-and-data-segments/4"/imm32
     921     68/push  "03 04 00 00 00 "/imm32
     922     68/push  _test-output-stream/imm32
     923     # . . call
     924     e8/call  check-next-stream-line-equal/disp32
     925     # . . discard args
     926     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
     927     # . epilog
     928     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
     929     5d/pop-to-EBP
     930     c3/return
     931
     932 convert-data:  # line : (address stream byte), out : (address buffered-file) -> <void>
     933     # pseudocode:
     934     #   var word-slice = {0, 0}
     935     #   while true
     936     #     word-slice = next-word(line)
     937     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
     938     #       break  # skip emitting some whitespace
     939     #     if slice-starts-with?(word-slice, "#")      # comment
     940     #       write-slice(out, word-slice)
     941     #       break
     942     #     if slice-ends-with?(word-slice, ":")        # label
     943     #       write-stream-data(out, line)
     944     #       break
     945     #     if has-metadata?(word-slice, "imm32")
     946     #       emit(out, word-slice, 4)
     947     #     # disp32 is not permitted in data segments, and anything else is only a byte long
     948     #     else
     949     #       emit(out, word-slice, 1)
     950     #
     951     # . prolog
     952     55/push-EBP
     953     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
     954     # . save registers
     955     50/push-EAX
     956     51/push-ECX
     957     52/push-EDX
     958     # var word-slice/ECX = {0, 0}
     959     68/push  0/imm32/end
     960     68/push  0/imm32/start
     961     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
     962 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
     988 $convert-data:loop:
     989     # next-word(line, word-slice)
     990     # . . push args
     991     51/push-ECX
     992     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
     993     # . . call
     994     e8/call  next-word/disp32
     995     # . . discard args
     996     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
     997 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
    1039 $convert-data:check0:
    1040     # if (slice-empty?(word-slice)) break
    1041     # . EAX = slice-empty?(word-slice)
    1042     # . . push args
    1043     51/push-ECX
    1044     # . . call
    1045     e8/call  slice-empty?/disp32
    1046     # . . discard args
    1047     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1048     # . if (EAX != 0) break
    1049     3d/compare-EAX-and  0/imm32
    1050     0f 85/jump-if-not-equal  $convert-data:break/disp32
    1051 $convert-data:check-for-comment:
    1052     # if (slice-starts-with?(word-slice, "#"))
    1053     # . start/EDX = word-slice->start
    1054     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    1055     # . c/EAX = *start
    1056     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    1057     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    1058     # . if (EAX != '#') goto next check
    1059     3d/compare-EAX-and  0x23/imm32/hash
    1060     75/jump-if-not-equal  $convert-data:check-for-label/disp8
    1061 $convert-data:comment:
    1062     # write-slice(out, word-slice)
    1063     # . . push args
    1064     51/push-ECX
    1065     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1066     # . . call
    1067     e8/call  write-slice/disp32
    1068     # . . discard args
    1069     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1070     # break
    1071     75/jump-if-not-equal  $convert-data:break/disp8
    1072 $convert-data:check-for-label:
    1073     # if (slice-ends-with?(word-slice, ":"))
    1074     # . end/EDX = word-slice->end
    1075     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
    1076     # . c/EAX = *(end-1)
    1077     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    1078     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
    1079     # . if (EAX != ':') goto next check
    1080     3d/compare-EAX-and  0x3a/imm32/colon
    1081     75/jump-if-not-equal  $convert-data:check-for-imm32/disp8
    1082 $convert-data:label:
    1083     # write-stream-data(out, line)
    1084     # . . push args
    1085     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1086     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1087     # . . call
    1088     e8/call  write-stream-data/disp32
    1089     # . . discard args
    1090     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1091     # break
    1092     75/jump-if-not-equal  $convert-data:break/disp8
    1093 $convert-data:check-for-imm32:
    1094     # if (has-metadata?(word-slice, "imm32"))
    1095     # . EAX = has-metadata?(ECX, "imm32")
    1096     # . . push args
    1097     68/push  "imm32"/imm32
    1098     51/push-ECX
    1099     # . . call
    1100     e8/call  has-metadata?/disp32
    1101     # . . discard args
    1102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1103     # . if (EAX == 0) process as a single byte
    1104     3d/compare-EAX-and  0/imm32
    1105     74/jump-if-equal  $convert-data:single-byte/disp8
    1106 $convert-data:imm32:
    1107     # emit(out, word-slice, 4)
    1108     # . . push args
    1109     68/push  4/imm32
    1110     51/push-ECX
    1111     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1112     # . . call
    1113     e8/call  emit/disp32
    1114     # . . discard args
    1115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1116     e9/jump  $convert-data:loop/disp32
    1117 $convert-data:single-byte:
    1118     # emit(out, word-slice, 1)
    1119     # . . push args
    1120     68/push  1/imm32
    1121     51/push-ECX
    1122     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1123     # . . call
    1124     e8/call  emit/disp32
    1125     # . . discard args
    1126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1127     e9/jump  $convert-data:loop/disp32
    1128 $convert-data:break:
    1129 $convert-data:end:
    1130     # . reclaim locals
    1131     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1132     # . restore registers
    1133     5a/pop-to-EDX
    1134     59/pop-to-ECX
    1135     58/pop-to-EAX
    1136     # . epilog
    1137     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1138     5d/pop-to-EBP
    1139     c3/return
    1140
    1141 test-convert-data-passes-comments-through:
    1142     # if a line starts with '#', pass it along unchanged
    1143     # . prolog
    1144     55/push-EBP
    1145     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1146     # setup
    1147     # . clear-stream(_test-input-stream)
    1148     # . . push args
    1149     68/push  _test-input-stream/imm32
    1150     # . . call
    1151     e8/call  clear-stream/disp32
    1152     # . . discard args
    1153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1154     # . clear-stream(_test-output-stream)
    1155     # . . push args
    1156     68/push  _test-output-stream/imm32
    1157     # . . call
    1158     e8/call  clear-stream/disp32
    1159     # . . discard args
    1160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1161     # . clear-stream(_test-output-buffered-file+4)
    1162     # . . push args
    1163     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1164     05/add-to-EAX  4/imm32
    1165     50/push-EAX
    1166     # . . call
    1167     e8/call  clear-stream/disp32
    1168     # . . discard args
    1169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1170     # initialize input
    1171     # . write(_test-input-stream, "# abcd")
    1172     # . . push args
    1173     68/push  "# abcd"/imm32
    1174     68/push  _test-input-stream/imm32
    1175     # . . call
    1176     e8/call  write/disp32
    1177     # . . discard args
    1178     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1179     # convert-data(_test-input-stream, _test-output-buffered-file)
    1180     # . . push args
    1181     68/push  _test-output-buffered-file/imm32
    1182     68/push  _test-input-stream/imm32
    1183     # . . call
    1184     e8/call  convert-data/disp32
    1185     # . . discard args
    1186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1187     # check that the line just passed through
    1188     # . flush(_test-output-buffered-file)
    1189     # . . push args
    1190     68/push  _test-output-buffered-file/imm32
    1191     # . . call
    1192     e8/call  flush/disp32
    1193     # . . discard args
    1194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1195 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    1221     # . check-stream-equal(_test-output-stream, "# abcd", msg)
    1222     # . . push args
    1223     68/push  "F - test-convert-data-passes-comments-through"/imm32
    1224     68/push  "# abcd"/imm32
    1225     68/push  _test-output-stream/imm32
    1226     # . . call
    1227     e8/call  check-stream-equal/disp32
    1228     # . . discard args
    1229     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1230     # . epilog
    1231     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1232     5d/pop-to-EBP
    1233     c3/return
    1234
    1235 test-convert-data-passes-labels-through:
    1236     # if the first word ends with ':', pass along the entire line unchanged
    1237     # . prolog
    1238     55/push-EBP
    1239     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1240     # setup
    1241     # . clear-stream(_test-input-stream)
    1242     # . . push args
    1243     68/push  _test-input-stream/imm32
    1244     # . . call
    1245     e8/call  clear-stream/disp32
    1246     # . . discard args
    1247     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1248     # . clear-stream(_test-output-stream)
    1249     # . . push args
    1250     68/push  _test-output-stream/imm32
    1251     # . . call
    1252     e8/call  clear-stream/disp32
    1253     # . . discard args
    1254     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1255     # . clear-stream(_test-output-buffered-file+4)
    1256     # . . push args
    1257     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1258     05/add-to-EAX  4/imm32
    1259     50/push-EAX
    1260     # . . call
    1261     e8/call  clear-stream/disp32
    1262     # . . discard args
    1263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1264     # initialize input
    1265     # . write(_test-input-stream, "ab: # cd")
    1266     # . . push args
    1267     68/push  "ab: # cd"/imm32
    1268     68/push  _test-input-stream/imm32
    1269     # . . call
    1270     e8/call  write/disp32
    1271     # . . discard args
    1272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1273     # convert-data(_test-input-stream, _test-output-buffered-file)
    1274     # . . push args
    1275     68/push  _test-output-buffered-file/imm32
    1276     68/push  _test-input-stream/imm32
    1277     # . . call
    1278     e8/call  convert-data/disp32
    1279     # . . discard args
    1280     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1281     # check that the line just passed through
    1282     # . flush(_test-output-buffered-file)
    1283     # . . push args
    1284     68/push  _test-output-buffered-file/imm32
    1285     # . . call
    1286     e8/call  flush/disp32
    1287     # . . discard args
    1288     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1289     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
    1290     # . . push args
    1291     68/push  "F - test-convert-data-passes-labels-through"/imm32
    1292     68/push  "ab: # cd"/imm32
    1293     68/push  _test-output-stream/imm32
    1294     # . . call
    1295     e8/call  check-stream-equal/disp32
    1296     # . . discard args
    1297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1298     # . epilog
    1299     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1300     5d/pop-to-EBP
    1301     c3/return
    1302
    1303 test-convert-data-passes-names-through:
    1304     # If a word is a valid name, just emit it unchanged.
    1305     # Later phases will deal with it.
    1306     # . prolog
    1307     55/push-EBP
    1308     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1309     # setup
    1310     # . clear-stream(_test-input-stream)
    1311     # . . push args
    1312     68/push  _test-input-stream/imm32
    1313     # . . call
    1314     e8/call  clear-stream/disp32
    1315     # . . discard args
    1316     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1317     # . clear-stream(_test-output-stream)
    1318     # . . push args
    1319     68/push  _test-output-stream/imm32
    1320     # . . call
    1321     e8/call  clear-stream/disp32
    1322     # . . discard args
    1323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1324     # . clear-stream(_test-output-buffered-file+4)
    1325     # . . push args
    1326     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1327     05/add-to-EAX  4/imm32
    1328     50/push-EAX
    1329     # . . call
    1330     e8/call  clear-stream/disp32
    1331     # . . discard args
    1332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1333     # initialize input
    1334     # . write(_test-input-stream, "abcd/imm32")
    1335     # . . push args
    1336     68/push  "abcd/imm32"/imm32
    1337     68/push  _test-input-stream/imm32
    1338     # . . call
    1339     e8/call  write/disp32
    1340     # . . discard args
    1341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1342     # convert-data(_test-input-stream, _test-output-buffered-file)
    1343     # . . push args
    1344     68/push  _test-output-buffered-file/imm32
    1345     68/push  _test-input-stream/imm32
    1346     # . . call
    1347     e8/call  convert-data/disp32
    1348     # . . discard args
    1349     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1350     # check that the line just passed through
    1351     # . flush(_test-output-buffered-file)
    1352     # . . push args
    1353     68/push  _test-output-buffered-file/imm32
    1354     # . . call
    1355     e8/call  flush/disp32
    1356     # . . discard args
    1357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1358     # . check-stream-equal(_test-output-stream, "abcd/imm32", msg)
    1359     # . . push args
    1360     68/push  "F - test-convert-data-passes-names-through"/imm32
    1361     68/push  "abcd/imm32 "/imm32
    1362     68/push  _test-output-stream/imm32
    1363     # . . call
    1364     e8/call  check-stream-equal/disp32
    1365     # . . discard args
    1366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1367     # . epilog
    1368     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1369     5d/pop-to-EBP
    1370     c3/return
    1371
    1372 test-convert-data-handles-imm32:
    1373     # If a word has the /imm32 metadata, emit it in 4 bytes.
    1374     # . prolog
    1375     55/push-EBP
    1376     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1377     # setup
    1378     # . clear-stream(_test-input-stream)
    1379     # . . push args
    1380     68/push  _test-input-stream/imm32
    1381     # . . call
    1382     e8/call  clear-stream/disp32
    1383     # . . discard args
    1384     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1385     # . clear-stream(_test-output-stream)
    1386     # . . push args
    1387     68/push  _test-output-stream/imm32
    1388     # . . call
    1389     e8/call  clear-stream/disp32
    1390     # . . discard args
    1391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1392     # . clear-stream(_test-output-buffered-file+4)
    1393     # . . push args
    1394     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1395     05/add-to-EAX  4/imm32
    1396     50/push-EAX
    1397     # . . call
    1398     e8/call  clear-stream/disp32
    1399     # . . discard args
    1400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1401     # initialize input
    1402     # . write(_test-input-stream, "30/imm32")
    1403     # . . push args
    1404     68/push  "30/imm32"/imm32
    1405     68/push  _test-input-stream/imm32
    1406     # . . call
    1407     e8/call  write/disp32
    1408     # . . discard args
    1409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1410     # convert-data(_test-input-stream, _test-output-buffered-file)
    1411     # . . push args
    1412     68/push  _test-output-buffered-file/imm32
    1413     68/push  _test-input-stream/imm32
    1414     # . . call
    1415     e8/call  convert-data/disp32
    1416     # . . discard args
    1417     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1418     # check that 4 bytes were written
    1419     # . flush(_test-output-buffered-file)
    1420     # . . push args
    1421     68/push  _test-output-buffered-file/imm32
    1422     # . . call
    1423     e8/call  flush/disp32
    1424     # . . discard args
    1425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1426     # . check-stream-equal(_test-output-stream, "30 00 00 00 ", msg)
    1427     # . . push args
    1428     68/push  "F - test-convert-data-handles-imm32"/imm32
    1429     68/push  "30 00 00 00 "/imm32
    1430     68/push  _test-output-stream/imm32
    1431     # . . call
    1432     e8/call  check-stream-equal/disp32
    1433     # . . discard args
    1434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1435     # . epilog
    1436     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1437     5d/pop-to-EBP
    1438     c3/return
    1439
    1440 test-convert-data-handles-single-byte:
    1441     # Any metadata but /imm32 will emit a single byte.
    1442     # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
    1443     # . prolog
    1444     55/push-EBP
    1445     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1446     # setup
    1447     # . clear-stream(_test-input-stream)
    1448     # . . push args
    1449     68/push  _test-input-stream/imm32
    1450     # . . call
    1451     e8/call  clear-stream/disp32
    1452     # . . discard args
    1453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1454     # . clear-stream(_test-output-stream)
    1455     # . . push args
    1456     68/push  _test-output-stream/imm32
    1457     # . . call
    1458     e8/call  clear-stream/disp32
    1459     # . . discard args
    1460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1461     # . clear-stream(_test-output-buffered-file+4)
    1462     # . . push args
    1463     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1464     05/add-to-EAX  4/imm32
    1465     50/push-EAX
    1466     # . . call
    1467     e8/call  clear-stream/disp32
    1468     # . . discard args
    1469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1470     # initialize input
    1471     # . write(_test-input-stream, "30/imm16")
    1472     # . . push args
    1473     68/push  "30/imm16"/imm32
    1474     68/push  _test-input-stream/imm32
    1475     # . . call
    1476     e8/call  write/disp32
    1477     # . . discard args
    1478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1479     # convert-data(_test-input-stream, _test-output-buffered-file)
    1480     # . . push args
    1481     68/push  _test-output-buffered-file/imm32
    1482     68/push  _test-input-stream/imm32
    1483     # . . call
    1484     e8/call  convert-data/disp32
    1485     # . . discard args
    1486     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1487     # check that a single byte was written (imm16 is not a valid operand type)
    1488     # . flush(_test-output-buffered-file)
    1489     # . . push args
    1490     68/push  _test-output-buffered-file/imm32
    1491     # . . call
    1492     e8/call  flush/disp32
    1493     # . . discard args
    1494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1495     # . check-stream-equal(_test-output-stream, "30 ", msg)
    1496     # . . push args
    1497     68/push  "F - test-convert-data-handles-single-byte"/imm32
    1498     68/push  "30 "/imm32
    1499     68/push  _test-output-stream/imm32
    1500     # . . call
    1501     e8/call  check-stream-equal/disp32
    1502     # . . discard args
    1503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1504     # . epilog
    1505     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1506     5d/pop-to-EBP
    1507     c3/return
    1508
    1509 test-convert-data-multiple-bytes:
    1510     # Multiple single-byte words in input stream get processed one by one.
    1511     # . prolog
    1512     55/push-EBP
    1513     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1514     # setup
    1515     # . clear-stream(_test-input-stream)
    1516     # . . push args
    1517     68/push  _test-input-stream/imm32
    1518     # . . call
    1519     e8/call  clear-stream/disp32
    1520     # . . discard args
    1521     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1522     # . clear-stream(_test-output-stream)
    1523     # . . push args
    1524     68/push  _test-output-stream/imm32
    1525     # . . call
    1526     e8/call  clear-stream/disp32
    1527     # . . discard args
    1528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1529     # . clear-stream(_test-output-buffered-file+4)
    1530     # . . push args
    1531     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1532     05/add-to-EAX  4/imm32
    1533     50/push-EAX
    1534     # . . call
    1535     e8/call  clear-stream/disp32
    1536     # . . discard args
    1537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1538     # initialize input
    1539     # . write(_test-input-stream, "1 2")
    1540     # . . push args
    1541     68/push  "1 2"/imm32
    1542     68/push  _test-input-stream/imm32
    1543     # . . call
    1544     e8/call  write/disp32
    1545     # . . discard args
    1546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1547     # convert-data(_test-input-stream, _test-output-buffered-file)
    1548     # . . push args
    1549     68/push  _test-output-buffered-file/imm32
    1550     68/push  _test-input-stream/imm32
    1551     # . . call
    1552     e8/call  convert-data/disp32
    1553     # . . discard args
    1554     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1555     # check output
    1556     # . flush(_test-output-buffered-file)
    1557     # . . push args
    1558     68/push  _test-output-buffered-file/imm32
    1559     # . . call
    1560     e8/call  flush/disp32
    1561     # . . discard args
    1562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1563     # . check-stream-equal(_test-output-stream, "01 02 ", msg)
    1564     # . . push args
    1565     68/push  "F - test-convert-data-multiple-bytes"/imm32
    1566     68/push  "01 02 "/imm32
    1567     68/push  _test-output-stream/imm32
    1568     # . . call
    1569     e8/call  check-stream-equal/disp32
    1570     # . . discard args
    1571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1572     # . epilog
    1573     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1574     5d/pop-to-EBP
    1575     c3/return
    1576
    1577 test-convert-data-byte-then-name:
    1578     # Single-byte word followed by valid name get processed one by one.
    1579     # . prolog
    1580     55/push-EBP
    1581     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1582     # setup
    1583     # . clear-stream(_test-input-stream)
    1584     # . . push args
    1585     68/push  _test-input-stream/imm32
    1586     # . . call
    1587     e8/call  clear-stream/disp32
    1588     # . . discard args
    1589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1590     # . clear-stream(_test-output-stream)
    1591     # . . push args
    1592     68/push  _test-output-stream/imm32
    1593     # . . call
    1594     e8/call  clear-stream/disp32
    1595     # . . discard args
    1596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1597     # . clear-stream(_test-output-buffered-file+4)
    1598     # . . push args
    1599     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1600     05/add-to-EAX  4/imm32
    1601     50/push-EAX
    1602     # . . call
    1603     e8/call  clear-stream/disp32
    1604     # . . discard args
    1605     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1606     # initialize input
    1607     # . write(_test-input-stream, "30 abcd/o")
    1608     # . . push args
    1609     68/push  "30 abcd/o"/imm32
    1610     68/push  _test-input-stream/imm32
    1611     # . . call
    1612     e8/call  write/disp32
    1613     # . . discard args
    1614     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1615     # convert-data(_test-input-stream, _test-output-buffered-file)
    1616     # . . push args
    1617     68/push  _test-output-buffered-file/imm32
    1618     68/push  _test-input-stream/imm32
    1619     # . . call
    1620     e8/call  convert-data/disp32
    1621     # . . discard args
    1622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1623     # check output
    1624     # . flush(_test-output-buffered-file)
    1625     # . . push args
    1626     68/push  _test-output-buffered-file/imm32
    1627     # . . call
    1628     e8/call  flush/disp32
    1629     # . . discard args
    1630     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1631     # . check-stream-equal(_test-output-stream, "30 abcd/o ", msg)
    1632     # . . push args
    1633     68/push  "F - test-convert-data-byte-then-name"/imm32
    1634     68/push  "30 abcd/o "/imm32
    1635     68/push  _test-output-stream/imm32
    1636     # . . call
    1637     e8/call  check-stream-equal/disp32
    1638     # . . discard args
    1639     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1640     # . epilog
    1641     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1642     5d/pop-to-EBP
    1643     c3/return
    1644
    1645 test-convert-data-multiple-words:
    1646     # Multiple words in input stream get processed one by one.
    1647     # . prolog
    1648     55/push-EBP
    1649     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1650     # setup
    1651     # . clear-stream(_test-input-stream)
    1652     # . . push args
    1653     68/push  _test-input-stream/imm32
    1654     # . . call
    1655     e8/call  clear-stream/disp32
    1656     # . . discard args
    1657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1658     # . clear-stream(_test-output-stream)
    1659     # . . push args
    1660     68/push  _test-output-stream/imm32
    1661     # . . call
    1662     e8/call  clear-stream/disp32
    1663     # . . discard args
    1664     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1665     # . clear-stream(_test-output-buffered-file+4)
    1666     # . . push args
    1667     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1668     05/add-to-EAX  4/imm32
    1669     50/push-EAX
    1670     # . . call
    1671     e8/call  clear-stream/disp32
    1672     # . . discard args
    1673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1674     # initialize input
    1675     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
    1676     # . . push args
    1677     68/push  "30 abcd/o 42e1/imm32"/imm32
    1678     68/push  _test-input-stream/imm32
    1679     # . . call
    1680     e8/call  write/disp32
    1681     # . . discard args
    1682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1683     # convert-data(_test-input-stream, _test-output-buffered-file)
    1684     # . . push args
    1685     68/push  _test-output-buffered-file/imm32
    1686     68/push  _test-input-stream/imm32
    1687     # . . call
    1688     e8/call  convert-data/disp32
    1689     # . . discard args
    1690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1691     # check output
    1692     # . flush(_test-output-buffered-file)
    1693     # . . push args
    1694     68/push  _test-output-buffered-file/imm32
    1695     # . . call
    1696     e8/call  flush/disp32
    1697     # . . discard args
    1698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1699 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    1725     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 ", msg)
    1726     # . . push args
    1727     68/push  "F - test-convert-data-multiple-words"/imm32
    1728     68/push  "30 abcd/o e1 42 00 00 "/imm32
    1729     68/push  _test-output-stream/imm32
    1730     # . . call
    1731     e8/call  check-stream-equal/disp32
    1732     # . . discard args
    1733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1734     # . epilog
    1735     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1736     5d/pop-to-EBP
    1737     c3/return
    1738
    1739 test-convert-data-trailing-comment:
    1740     # Trailing comments in data segment get appropriately ignored.
    1741     # . prolog
    1742     55/push-EBP
    1743     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1744     # setup
    1745     # . clear-stream(_test-input-stream)
    1746     # . . push args
    1747     68/push  _test-input-stream/imm32
    1748     # . . call
    1749     e8/call  clear-stream/disp32
    1750     # . . discard args
    1751     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1752     # . clear-stream(_test-output-stream)
    1753     # . . push args
    1754     68/push  _test-output-stream/imm32
    1755     # . . call
    1756     e8/call  clear-stream/disp32
    1757     # . . discard args
    1758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1759     # . clear-stream(_test-output-buffered-file+4)
    1760     # . . push args
    1761     b8/copy-to-EAX  _test-output-buffered-file/imm32
    1762     05/add-to-EAX  4/imm32
    1763     50/push-EAX
    1764     # . . call
    1765     e8/call  clear-stream/disp32
    1766     # . . discard args
    1767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1768     # initialize input
    1769     # . write(_test-input-stream, "30/imm32 # comment")
    1770     # . . push args
    1771     68/push  "30/imm32 # comment"/imm32
    1772     68/push  _test-input-stream/imm32
    1773     # . . call
    1774     e8/call  write/disp32
    1775     # . . discard args
    1776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1777     # convert-data(_test-input-stream, _test-output-buffered-file)
    1778     # . . push args
    1779     68/push  _test-output-buffered-file/imm32
    1780     68/push  _test-input-stream/imm32
    1781     # . . call
    1782     e8/call  convert-data/disp32
    1783     # . . discard args
    1784     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1785     # check output
    1786     # . flush(_test-output-buffered-file)
    1787     # . . push args
    1788     68/push  _test-output-buffered-file/imm32
    1789     # . . call
    1790     e8/call  flush/disp32
    1791     # . . discard args
    1792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1793 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    1819     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
    1820     # . . push args
    1821     68/push  "F - test-convert-data-trailing-comment"/imm32
    1822     68/push  "30 00 00 00 # comment"/imm32
    1823     68/push  _test-output-stream/imm32
    1824     # . . call
    1825     e8/call  check-stream-equal/disp32
    1826     # . . discard args
    1827     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    1828     # . epilog
    1829     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1830     5d/pop-to-EBP
    1831     c3/return
    1832
    1833 # pack an instruction, following the C++ version
    1834 #
    1835 # zero error handling at the moment (continuing to rely on the C++ version for that):
    1836 #   missing fields are always 0-filled
    1837 #   bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte.
    1838 #   may pick up any of duplicate operands in an instruction
    1839 #   silently drop extraneous operands
    1840 #   unceremoniously abort on non-numeric operands except disp or imm
    1841 #   opcodes must be lowercase and zero padded
    1842 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
    1843 convert-instruction:  # line : (address stream byte), out : (address buffered-file) -> <void>
    1844     # pseudocode:
    1845     #   # some early exits
    1846     #   var word-slice = next-word(line)
    1847     #   if slice-empty?(word-slice)
    1848     #     write-stream-data(out, line)
    1849     #     return
    1850     #   if slice-starts-with?(word-slice, "#")
    1851     #     write-stream-data(out, line)
    1852     #     return
    1853     #   if slice-ends-with?(word-slice, ":")
    1854     #     write-stream-data(out, line)
    1855     #     return
    1856     #   # really convert
    1857     #   emit-opcodes(line, out)
    1858     #   emit-modrm(line, out)
    1859     #   emit-sib(line, out)
    1860     #   emit-disp(line, out)
    1861     #   emit-imm(line, out)
    1862     #   emit-line-in-comment(line, out)
    1863     #
    1864     # . prolog
    1865     55/push-EBP
    1866     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    1867     # . save registers
    1868     50/push-EAX
    1869     51/push-ECX
    1870     52/push-EDX
    1871     # var word-slice/ECX = {0, 0}
    1872     68/push  0/imm32/end
    1873     68/push  0/imm32/start
    1874     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    1875     # next-word(line, word-slice)
    1876     # . . push args
    1877     51/push-ECX
    1878     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1879     # . . call
    1880     e8/call  next-word/disp32
    1881     # . . discard args
    1882     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1883 $convert-instruction:check0:
    1884     # if (slice-empty?(word-slice)) break
    1885     # . EAX = slice-empty?(word-slice)
    1886     # . . push args
    1887     51/push-ECX
    1888     # . . call
    1889     e8/call  slice-empty?/disp32
    1890     # . . discard args
    1891     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    1892     # . if (EAX != 0) pass through
    1893     3d/compare-EAX-and  0/imm32
    1894     75/jump-if-not-equal  $convert-instruction:pass-through/disp8
    1895 $convert-instruction:check1:
    1896     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
    1897     # . start/EDX = word-slice->start
    1898     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    1899     # . c/EAX = *start
    1900     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    1901     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    1902     # . if (EAX == '#') pass through
    1903     3d/compare-EAX-and  0x23/imm32/hash
    1904     74/jump-if-equal  $convert-instruction:pass-through/disp8
    1905 $convert-instruction:check2:
    1906     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
    1907     # . end/EDX = word-slice->end
    1908     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
    1909     # . c/EAX = *(end-1)
    1910     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    1911     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
    1912     # . if (EAX == ':') pass through
    1913     3d/compare-EAX-and  0x3a/imm32/colon
    1914     75/jump-if-not-equal  $convert-instruction:really-convert/disp8
    1915 $convert-instruction:pass-through:
    1916     # write-stream-data(out, line)
    1917     # . . push args
    1918     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1919     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1920     # . . call
    1921     e8/call  write-stream-data/disp32
    1922     # . . discard args
    1923     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1924     # return
    1925     eb/jump  $convert-instruction:end/disp8
    1926 $convert-instruction:really-convert:
    1927     # emit-opcodes(line, out)
    1928     # . . push args
    1929     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1930     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1931     # . . call
    1932     e8/call  emit-opcodes/disp32
    1933     # . . discard args
    1934     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1935     # emit-modrm(line, out)
    1936     # . . push args
    1937     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1938     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1939     # . . call
    1940     e8/call  emit-modrm/disp32
    1941     # . . discard args
    1942     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1943     # emit-sib(line, out)
    1944     # . . push args
    1945     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1946     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1947     # . . call
    1948     e8/call  emit-sib/disp32
    1949     # . . discard args
    1950     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1951     # emit-disp(line, out)
    1952     # . . push args
    1953     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1954     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1955     # . . call
    1956     e8/call  emit-disp/disp32
    1957     # . . discard args
    1958     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1959     # emit-imm(line, out)
    1960     # . . push args
    1961     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1962     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1963     # . . call
    1964     e8/call  emit-imm/disp32
    1965     # . . discard args
    1966     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1967     # emit-line-in-comment(line, out)
    1968     # . . push args
    1969     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    1970     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    1971     # . . call
    1972     e8/call  emit-line-in-comment/disp32
    1973     # . . discard args
    1974     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1975 $convert-instruction:end:
    1976     # . restore locals
    1977     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    1978     # . restore registers
    1979     5a/pop-to-EDX
    1980     59/pop-to-ECX
    1981     58/pop-to-EAX
    1982     # . epilog
    1983     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    1984     5d/pop-to-EBP
    1985     c3/return
    1986
    1987 emit-opcodes:  # line : (address stream byte), out : (address buffered-file) -> <void>
    1988     # opcodes occupy 1-3 bytes:
    1989     #   xx
    1990     #   0f xx
    1991     #   f2 xx
    1992     #   f3 xx
    1993     #   f2 0f xx
    1994     #   f3 0f xx
    1995     #
    1996     # pseudocode:
    1997     #   rewind-stream(line)
    1998     #
    1999     #   var op1 = next-word(line)
    2000     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
    2001     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
    2002     #   write-slice(out, op1)
    2003     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3")
    2004     #     return
    2005     #
    2006     #   var op2 = next-word(line)
    2007     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
    2008     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
    2009     #   write-slice(out, op2)
    2010     #   if slice-equal?(op1, "0f")
    2011     #     return
    2012     #   if !slice-equal?(op2, "0f")
    2013     #     return
    2014     #
    2015     #   var op3 = next-word(line)
    2016     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
    2017     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
    2018     #   write-slice(out, op3)
    2019     #
    2020     # . prolog
    2021     55/push-EBP
    2022     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    2023     # . save registers
    2024     50/push-EAX
    2025     51/push-ECX
    2026     52/push-EDX
    2027     53/push-EBX
    2028     # var op1/ECX = {0, 0}
    2029     68/push  0/imm32/end
    2030     68/push  0/imm32/start
    2031     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    2032     # var op2/EDX = {0, 0}
    2033     68/push  0/imm32/end
    2034     68/push  0/imm32/start
    2035     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
    2036     # rewind-stream(line)
    2037     # . . push args
    2038     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2039     # . . call
    2040     e8/call  rewind-stream/disp32
    2041     # . . discard args
    2042     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2043 $emit-opcodes:op1:
    2044     # next-word(line, op1)
    2045     # . . push args
    2046     51/push-ECX
    2047     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2048     # . . call
    2049     e8/call  next-word/disp32
    2050     # . . discard args
    2051     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2052     # if (slice-empty?(op1)) return
    2053     # . EAX = slice-empty?(op1)
    2054     # . . push args
    2055     51/push-ECX
    2056     # . . call
    2057     e8/call  slice-empty?/disp32
    2058     # . . discard args
    2059     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2060     # . if (EAX != 0) return
    2061     3d/compare-EAX-and  0/imm32
    2062     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
    2063     # if (slice-starts-with?(op1, "#")) return
    2064     # . start/EBX = op1->start
    2065     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # copy *ECX to EBX
    2066     # . c/EAX = *start
    2067     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    2068     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
    2069     # . if (EAX == '#') return
    2070     3d/compare-EAX-and  0x23/imm32/hash
    2071     0f 84/jump-if-equal  $emit-opcodes:end/disp32
    2072     # op1 = next-token-from-slice(op1->start, op1->end, '/')
    2073     # . . push args
    2074     51/push-ECX
    2075     68/push  0x2f/imm32/slash
    2076     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
    2077     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
    2078     # . . call
    2079     e8/call  next-token-from-slice/disp32
    2080     # . . discard args
    2081     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    2082     # write-slice(out, op1)
    2083     # . . push args
    2084     51/push-ECX
    2085     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    2086     # . . call
    2087     e8/call  write-slice/disp32
    2088     # . . discard args
    2089     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2090     # write-buffered(out, " ")
    2091     # . . push args
    2092     68/push  " "/imm32
    2093     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    2094     # . . call
    2095     e8/call  write-buffered/disp32
    2096     # . . discard args
    2097     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2098     # if (slice-equal?(op1, "0f")) goto op2
    2099     # . EAX = slice-equal?(op1, "0f")
    2100     # . . push args
    2101     68/push  "0f"/imm32
    2102     51/push-ECX
    2103     # . . call
    2104     e8/call  slice-equal?/disp32
    2105     # . . discard args
    2106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2107     # . if (EAX != 0) goto op2
    2108     3d/compare-EAX-and  0/imm32
    2109     75/jump-if-not-equal  $emit-opcodes:op2/disp8
    2110     # if (slice-equal?(op1, "f2")) goto op2
    2111     # . EAX = slice-equal?(op1, "f2")
    2112     # . . push args
    2113     68/push  "f2"/imm32
    2114     51/push-ECX
    2115     # . . call
    2116     e8/call  slice-equal?/disp32
    2117     # . . discard args
    2118     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2119     # . if (EAX != 0) goto op2
    2120     3d/compare-EAX-and  0/imm32
    2121     75/jump-if-not-equal  $emit-opcodes:op2/disp8
    2122     # if (slice-equal?(op1, "f3")) goto op2
    2123     # . EAX = slice-equal?(op1, "f3")
    2124     # . . push args
    2125     68/push  "f3"/imm32
    2126     51/push-ECX
    2127     # . . call
    2128     e8/call  slice-equal?/disp32
    2129     # . . discard args
    2130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2131     # . if (EAX != 0) goto op2
    2132     3d/compare-EAX-and  0/imm32
    2133     75/jump-if-not-equal  $emit-opcodes:op2/disp8
    2134     # otherwise return
    2135     e9/jump  $emit-opcodes:end/disp32
    2136 $emit-opcodes:op2:
    2137     # next-word(line, op2)
    2138     # . . push args
    2139     52/push-EDX
    2140     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2141     # . . call
    2142     e8/call  next-word/disp32
    2143     # . . discard args
    2144     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2145     # if (slice-empty?(op2)) return
    2146     # . EAX = slice-empty?(op2)
    2147     # . . push args
    2148     52/push-EDX
    2149     # . . call
    2150     e8/call  slice-empty?/disp32
    2151     # . . discard args
    2152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2153     # . if (EAX != 0) return
    2154     3d/compare-EAX-and  0/imm32
    2155     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
    2156     # if (slice-starts-with?(op2, "#")) return
    2157     # . start/EBX = op2->start
    2158     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
    2159     # . c/EAX = *start
    2160     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    2161     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
    2162     # . if (EAX == '#') return
    2163     3d/compare-EAX-and  0x23/imm32/hash
    2164     0f 84/jump-if-equal  $emit-opcodes:end/disp32
    2165     # op2 = next-token-from-slice(op2->start, op2->end, '/')
    2166     # . . push args
    2167     52/push-EDX
    2168     68/push  0x2f/imm32/slash
    2169     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
    2170     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
    2171     # . . call
    2172     e8/call  next-token-from-slice/disp32
    2173     # . . discard args
    2174     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    2175     # write-slice(out, op2)
    2176     # . . push args
    2177     52/push-EDX
    2178     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    2179     # . . call
    2180     e8/call  write-slice/disp32
    2181     # . . discard args
    2182     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2183     # write-buffered(out, " ")
    2184     # . . push args
    2185     68/push  " "/imm32
    2186     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    2187     # . . call
    2188     e8/call  write-buffered/disp32
    2189     # . . discard args
    2190     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2191     # if (slice-equal?(op1, "0f")) return
    2192     # . EAX = slice-equal?(op1, "0f")
    2193     # . . push args
    2194     68/push  "0f"/imm32
    2195     51/push-ECX
    2196     # . . call
    2197     e8/call  slice-equal?/disp32
    2198     # . . discard args
    2199     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2200     # . if (EAX != 0) return
    2201     3d/compare-EAX-and  0/imm32
    2202     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
    2203     # if (!slice-equal?(op2, "0f")) return
    2204     # . EAX = slice-equal?(op2, "0f")
    2205     # . . push args
    2206     68/push  "0f"/imm32
    2207     52/push-EDX
    2208     # . . call
    2209     e8/call  slice-equal?/disp32
    2210     # . . discard args
    2211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2212     # . if (EAX == 0) return
    2213     3d/compare-EAX-and  0/imm32
    2214     0f 84/jump-if-equal  $emit-opcodes:end/disp32
    2215 $emit-opcodes:op3:
    2216     # next-word(line, op3)  # reuse op2/EDX
    2217     # . . push args
    2218     52/push-EDX
    2219     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2220     # . . call
    2221     e8/call  next-word/disp32
    2222     # . . discard args
    2223     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2224     # if (slice-empty?(op3)) return
    2225     # . EAX = slice-empty?(op3)
    2226     # . . push args
    2227     52/push-EDX
    2228     # . . call
    2229     e8/call  slice-empty?/disp32
    2230     # . . discard args
    2231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2232     # . if (EAX != 0) return
    2233     3d/compare-EAX-and  0/imm32
    2234     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
    2235     # if (slice-starts-with?(op3, "#")) return
    2236     # . start/EBX = op2->start
    2237     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
    2238     # . c/EAX = *start
    2239     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    2240     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
    2241     # . if (EAX == '#') return
    2242     3d/compare-EAX-and  0x23/imm32/hash
    2243     0f 84/jump-if-equal  $emit-opcodes:end/disp32
    2244     # op3 = next-token-from-slice(op3->start, op3->end, '/')
    2245     # . . push args
    2246     52/push-EDX
    2247     68/push  0x2f/imm32/slash
    2248     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
    2249     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
    2250     # . . call
    2251     e8/call  next-token-from-slice/disp32
    2252     # . . discard args
    2253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    2254     # write-slice(out, op3)
    2255     # . . push args
    2256     52/push-EDX
    2257     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    2258     # . . call
    2259     e8/call  write-slice/disp32
    2260     # . . discard args
    2261     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2262     # write-buffered(out, " ")
    2263     # . . push args
    2264     68/push  " "/imm32
    2265     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    2266     # . . call
    2267     e8/call  write-buffered/disp32
    2268     # . . discard args
    2269     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2270 $emit-opcodes:end:
    2271     # . restore locals
    2272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    2273     # . restore registers
    2274     5b/pop-to-EBX
    2275     5a/pop-to-EDX
    2276     59/pop-to-ECX
    2277     58/pop-to-EAX
    2278     # . epilog
    2279     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    2280     5d/pop-to-EBP
    2281     c3/return
    2282
    2283 emit-modrm:  # line : (address stream byte), out : (address buffered-file) -> <void>
    2284     # pseudocode:
    2285     #   rewind-stream(line)
    2286     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
    2287     #   var word-slice = {0, 0}
    2288     #   while true
    2289     #     word-slice = next-word(line)
    2290     #     if (slice-empty?(word-slice)) break
    2291     #     if (slice-starts-with?(word-slice, "#")) break
    2292     #     if (has-metadata?(word-slice, "mod"))
    2293     #       mod = parse-hex-int(next-token-from-slice(word-slice, "/"))
    2294     #       has-modrm? = true
    2295     #     else if (has-metadata?(word-slice, "rm32"))
    2296     #       rm32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
    2297     #       has-modrm? = true
    2298     #     else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop"))
    2299     #       r32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
    2300     #       has-modrm? = true
    2301     #   if has-modrm?
    2302     #     var modrm = mod & 0b11
    2303     #     modrm <<= 2
    2304     #     modrm |= r32 & 0b111
    2305     #     modrm <<= 3
    2306     #     modrm |= rm32 & 0b111
    2307     #     emit-hex(out, modrm, 1)
    2308     #
    2309     # . prolog
    2310     55/push-EBP
    2311     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    2312     # . save registers
    2313     50/push-EAX
    2314     51/push-ECX
    2315     52/push-EDX
    2316     53/push-EBX
    2317     56/push-ESI
    2318     57/push-EDI
    2319     # var word-slice/ECX = {0, 0}
    2320     68/push  0/imm32/end
    2321     68/push  0/imm32/start
    2322     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    2323     # var has-modrm?/EDX = false
    2324     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
    2325     # var mod/EBX = 0
    2326     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
    2327     # var rm32/ESI = 0
    2328     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
    2329     # var r32/EDI = 0
    2330     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
    2331     # rewind-stream(line)
    2332     # . . push args
    2333     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2334     # . . call
    2335     e8/call  rewind-stream/disp32
    2336     # . . discard args
    2337     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2338 $emit-modrm:loop:
    2339 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
    2365     # next-word(line, word-slice)
    2366     # . . push args
    2367     51/push-ECX
    2368     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2369     # . . call
    2370     e8/call  next-word/disp32
    2371     # . . discard args
    2372     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2373 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
    2415 $emit-modrm:check0:
    2416     # if (slice-empty?(word-slice)) break
    2417     # . EAX = slice-empty?(word-slice)
    2418     # . . push args
    2419     51/push-ECX
    2420     # . . call
    2421     e8/call  slice-empty?/disp32
    2422     # . . discard args
    2423     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2424     # . if (EAX != 0) pass through
    2425     3d/compare-EAX-and  0/imm32
    2426     0f 85/jump-if-not-equal  $emit-modrm:break/disp32
    2427 $emit-modrm:check1:
    2428     # if (slice-starts-with?(word-slice, "#")) break
    2429     # . spill EDX
    2430     52/push-EDX
    2431     # . start/EDX = word-slice->start
    2432     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    2433     # . c/EAX = *start
    2434     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    2435     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    2436     # . restore EDX
    2437     5a/pop-to-EDX
    2438     # . if (EAX == '#') pass through
    2439     3d/compare-EAX-and  0x23/imm32/hash
    2440     0f 84/jump-if-equal  $emit-modrm:break/disp32
    2441 $emit-modrm:check-for-mod:
    2442     # if (has-metadata?(word-slice, "mod"))
    2443     # . EAX = has-metadata?(ECX, "mod")
    2444     # . . push args
    2445     68/push  "mod"/imm32
    2446     51/push-ECX
    2447     # . . call
    2448     e8/call  has-metadata?/disp32
    2449     # . . discard args
    2450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2451     # . if (EAX == 0) goto next check
    2452     3d/compare-EAX-and  0/imm32
    2453     74/jump-if-equal  $emit-modrm:check-for-rm32/disp8
    2454 $emit-modrm:mod:
    2455     # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2456     # . EAX = parse-datum-of-word(word-slice)
    2457     # . . push args
    2458     51/push-ECX
    2459     # . . call
    2460     e8/call  parse-datum-of-word/disp32
    2461     # . . discard args
    2462     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2463     # . mod = EAX
    2464     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
    2465     # has-modrm? = true
    2466     ba/copy-to-EDX  1/imm32/true
    2467     # continue
    2468     e9/jump  $emit-modrm:loop/disp32
    2469 $emit-modrm:check-for-rm32:
    2470     # if (has-metadata?(word-slice, "rm32"))
    2471     # . EAX = has-metadata?(ECX, "rm32")
    2472     # . . push args
    2473     68/push  "rm32"/imm32
    2474     51/push-ECX
    2475     # . . call
    2476     e8/call  has-metadata?/disp32
    2477     # . . discard args
    2478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2479     # . if (EAX == 0) goto next check
    2480     3d/compare-EAX-and  0/imm32
    2481     74/jump-if-equal  $emit-modrm:check-for-r32/disp8
    2482 $emit-modrm:rm32:
    2483     # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2484     # . EAX = parse-datum-of-word(word-slice)
    2485     # . . push args
    2486     51/push-ECX
    2487     # . . call
    2488     e8/call  parse-datum-of-word/disp32
    2489     # . . discard args
    2490     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2491     # . rm32 = EAX
    2492     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
    2493     # has-modrm? = true
    2494     ba/copy-to-EDX  1/imm32/true
    2495     # continue
    2496     e9/jump  $emit-modrm:loop/disp32
    2497 $emit-modrm:check-for-r32:
    2498     # if (has-metadata?(word-slice, "r32"))
    2499     # . EAX = has-metadata?(ECX, "r32")
    2500     # . . push args
    2501     68/push  "r32"/imm32
    2502     51/push-ECX
    2503     # . . call
    2504     e8/call  has-metadata?/disp32
    2505     # . . discard args
    2506     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2507     # . if (EAX == 0) goto next check
    2508     3d/compare-EAX-and  0/imm32
    2509     74/jump-if-equal  $emit-modrm:check-for-subop/disp8
    2510 $emit-modrm:r32:
    2511     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2512     # . EAX = parse-datum-of-word(word-slice)
    2513     # . . push args
    2514     51/push-ECX
    2515     # . . call
    2516     e8/call  parse-datum-of-word/disp32
    2517     # . . discard args
    2518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2519     # . r32 = EAX
    2520     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
    2521     # has-modrm? = true
    2522     ba/copy-to-EDX  1/imm32/true
    2523     # continue
    2524     e9/jump  $emit-modrm:loop/disp32
    2525 $emit-modrm:check-for-subop:
    2526     # if (has-metadata?(word-slice, "subop"))
    2527     # . EAX = has-metadata?(ECX, "subop")
    2528     # . . push args
    2529     68/push  "subop"/imm32
    2530     51/push-ECX
    2531     # . . call
    2532     e8/call  has-metadata?/disp32
    2533     # . . discard args
    2534     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2535     # . if (EAX == 0) loop
    2536     3d/compare-EAX-and  0/imm32
    2537     0f 84/jump-if-equal  $emit-modrm:loop/disp32
    2538 $emit-modrm:subop:
    2539     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2540     # . EAX = parse-datum-of-word(word-slice)
    2541     # . . push args
    2542     51/push-ECX
    2543     # . . call
    2544     e8/call  parse-datum-of-word/disp32
    2545     # . . discard args
    2546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2547     # . r32 = EAX
    2548     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
    2549     # has-modrm? = true
    2550     ba/copy-to-EDX  1/imm32/true
    2551     # continue
    2552     e9/jump  $emit-modrm:loop/disp32
    2553 $emit-modrm:break:
    2554     # if (!has-modrm?) return
    2555     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
    2556     74/jump-if-equal  $emit-modrm:end/disp8
    2557 $emit-modrm:calculate:
    2558     # modrm/EBX = mod & 0b11
    2559     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
    2560     # modrm <<= 2
    2561     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
    2562     # modrm |= r32 & 0b111
    2563     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
    2564     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
    2565     # modrm <<= 3
    2566     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
    2567     # modrm |= rm32 & 0b111
    2568     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
    2569     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
    2570 $emit-modrm:emit:
    2571     # emit-hex(out, modrm, 1)
    2572     # . . push args
    2573     68/push  1/imm32
    2574     53/push-EBX
    2575     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    2576     # . . call
    2577     e8/call  emit-hex/disp32
    2578     # . . discard args
    2579     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    2580 $emit-modrm:end:
    2581     # . restore locals
    2582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2583     # . restore registers
    2584     5f/pop-to-EDI
    2585     5e/pop-to-ESI
    2586     5b/pop-to-EBX
    2587     5a/pop-to-EDX
    2588     59/pop-to-ECX
    2589     58/pop-to-EAX
    2590     # . epilog
    2591     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    2592     5d/pop-to-EBP
    2593     c3/return
    2594
    2595 emit-sib:  # line : (address stream byte), out : (address buffered-file) -> <void>
    2596     # pseudocode:
    2597     #   var has-sib? = false, base = 0, index = 0, scale = 0
    2598     #   var word-slice = {0, 0}
    2599     #   while true
    2600     #     word-slice = next-word(line)
    2601     #     if (slice-empty?(word-slice)) break
    2602     #     if (slice-starts-with?(word-slice, "#")) break
    2603     #     if (has-metadata?(word-slice, "base")
    2604     #       base = parse-hex-int(next-token-from-slice(word-slice, "/"))
    2605     #       has-sib? = true
    2606     #     else if (has-metadata?(word-slice, "index")
    2607     #       index = parse-hex-int(next-token-from-slice(word-slice, "/"))
    2608     #       has-sib? = true
    2609     #     else if (has-metadata?(word-slice, "scale")
    2610     #       scale = parse-hex-int(next-token-from-slice(word-slice, "/"))
    2611     #       has-sib? = true
    2612     #   if has-sib?
    2613     #     var sib = scale & 0b11
    2614     #     sib <<= 2
    2615     #     sib |= index & 0b111
    2616     #     sib <<= 3
    2617     #     sib |= base & 0b111
    2618     #     emit-hex(out, sib, 1)
    2619     #
    2620     # . prolog
    2621     55/push-EBP
    2622     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    2623     # . save registers
    2624     50/push-EAX
    2625     51/push-ECX
    2626     52/push-EDX
    2627     53/push-EBX
    2628     56/push-ESI
    2629     57/push-EDI
    2630     # var word-slice/ECX = {0, 0}
    2631     68/push  0/imm32/end
    2632     68/push  0/imm32/start
    2633     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    2634     # var has-sib?/EDX = false
    2635     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
    2636     # var scale/EBX = 0
    2637     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
    2638     # var base/ESI = 0
    2639     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
    2640     # var index/EDI = 0
    2641     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
    2642     # rewind-stream(line)
    2643     # . . push args
    2644     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2645     # . . call
    2646     e8/call  rewind-stream/disp32
    2647     # . . discard args
    2648     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2649 $emit-sib:loop:
    2650 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
    2676     # next-word(line, word-slice)
    2677     # . . push args
    2678     51/push-ECX
    2679     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2680     # . . call
    2681     e8/call  next-word/disp32
    2682     # . . discard args
    2683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2684 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
    2726 $emit-sib:check0:
    2727     # if (slice-empty?(word-slice)) break
    2728     # . EAX = slice-empty?(word-slice)
    2729     # . . push args
    2730     51/push-ECX
    2731     # . . call
    2732     e8/call  slice-empty?/disp32
    2733     # . . discard args
    2734     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2735     # . if (EAX != 0) pass through
    2736     3d/compare-EAX-and  0/imm32
    2737     0f 85/jump-if-not-equal  $emit-sib:break/disp32
    2738 $emit-sib:check1:
    2739     # if (slice-starts-with?(word-slice, "#")) break
    2740     # . spill EDX
    2741     52/push-EDX
    2742     # . start/EDX = word-slice->start
    2743     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    2744     # . c/EAX = *start
    2745     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    2746     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    2747     # . restore EDX
    2748     5a/pop-to-EDX
    2749     # . if (EAX == '#') pass through
    2750     3d/compare-EAX-and  0x23/imm32/hash
    2751     0f 84/jump-if-equal  $emit-sib:break/disp32
    2752 $emit-sib:check-for-scale:
    2753     # if (has-metadata?(word-slice, "scale"))
    2754     # . EAX = has-metadata?(ECX, "scale")
    2755     # . . push args
    2756     68/push  "scale"/imm32
    2757     51/push-ECX
    2758     # . . call
    2759     e8/call  has-metadata?/disp32
    2760     # . . discard args
    2761     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2762     # . if (EAX == 0) goto next check
    2763     3d/compare-EAX-and  0/imm32
    2764     74/jump-if-equal  $emit-sib:check-for-base/disp8
    2765 $emit-sib:scale:
    2766     # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2767     # . EAX = parse-datum-of-word(word-slice)
    2768     # . . push args
    2769     51/push-ECX
    2770     # . . call
    2771     e8/call  parse-datum-of-word/disp32
    2772     # . . discard args
    2773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2774     # . scale = EAX
    2775     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
    2776     # has-sib? = true
    2777     ba/copy-to-EDX  1/imm32/true
    2778     # continue
    2779     e9/jump  $emit-sib:loop/disp32
    2780 $emit-sib:check-for-base:
    2781     # if (has-metadata?(word-slice, "base"))
    2782     # . EAX = has-metadata?(ECX, "base")
    2783     # . . push args
    2784     68/push  "base"/imm32
    2785     51/push-ECX
    2786     # . . call
    2787     e8/call  has-metadata?/disp32
    2788     # . . discard args
    2789     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2790     # . if (EAX == 0) goto next check
    2791     3d/compare-EAX-and  0/imm32
    2792     74/jump-if-equal  $emit-sib:check-for-index/disp8
    2793 $emit-sib:base:
    2794     # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2795     # . EAX = parse-datum-of-word(word-slice)
    2796     # . . push args
    2797     51/push-ECX
    2798     # . . call
    2799     e8/call  parse-datum-of-word/disp32
    2800     # . . discard args
    2801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2802     # . base = EAX
    2803     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
    2804     # has-sib? = true
    2805     ba/copy-to-EDX  1/imm32/true
    2806     # continue
    2807     e9/jump  $emit-sib:loop/disp32
    2808 $emit-sib:check-for-index:
    2809     # if (has-metadata?(word-slice, "index"))
    2810     # . EAX = has-metadata?(ECX, "index")
    2811     # . . push args
    2812     68/push  "index"/imm32
    2813     51/push-ECX
    2814     # . . call
    2815     e8/call  has-metadata?/disp32
    2816     # . . discard args
    2817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2818     # . if (EAX == 0) loop
    2819     3d/compare-EAX-and  0/imm32
    2820     0f 84/jump-if-equal  $emit-sib:loop/disp32
    2821 $emit-sib:index:
    2822     # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2823     # . EAX = parse-datum-of-word(word-slice)
    2824     # . . push args
    2825     51/push-ECX
    2826     # . . call
    2827     e8/call  parse-datum-of-word/disp32
    2828     # . . discard args
    2829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2830     # . index = EAX
    2831     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
    2832     # has-sib? = true
    2833     ba/copy-to-EDX  1/imm32/true
    2834     # continue
    2835     e9/jump  $emit-sib:loop/disp32
    2836 $emit-sib:break:
    2837     # if (!has-sib?) return
    2838     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
    2839     74/jump-if-equal  $emit-sib:end/disp8
    2840 $emit-sib:calculate:
    2841     # sib/EBX = scale & 0b11
    2842     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
    2843     # sib <<= 2
    2844     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
    2845     # sib |= index & 0b111
    2846     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
    2847     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
    2848     # sib <<= 3
    2849     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
    2850     # sib |= base & 0b111
    2851     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
    2852     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
    2853 $emit-sib:emit:
    2854     # emit-hex(out, sib, 1)
    2855     # . . push args
    2856     68/push  1/imm32
    2857     53/push-EBX
    2858     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    2859     # . . call
    2860     e8/call  emit-hex/disp32
    2861     # . . discard args
    2862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    2863 $emit-sib:end:
    2864     # . restore locals
    2865     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2866     # . restore registers
    2867     5f/pop-to-EDI
    2868     5e/pop-to-ESI
    2869     5b/pop-to-EBX
    2870     5a/pop-to-EDX
    2871     59/pop-to-ECX
    2872     58/pop-to-EAX
    2873     # . epilog
    2874     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    2875     5d/pop-to-EBP
    2876     c3/return
    2877
    2878 emit-disp:  # line : (address stream byte), out : (address buffered-file) -> <void>
    2879     # pseudocode:
    2880     #   rewind-stream(line)
    2881     #   var word-slice = {0, 0}
    2882     #   while true
    2883     #     word-slice = next-word(line)
    2884     #     if (slice-empty?(word-slice)) break
    2885     #     if (slice-starts-with?(word-slice, "#")) break
    2886     #     if has-metadata?(word-slice, "disp32")
    2887     #       emit(out, word-slice, 4)
    2888     #       break
    2889     #     if has-metadata?(word-slice, "disp16")
    2890     #       emit(out, word-slice, 2)
    2891     #       break
    2892     #     if has-metadata?(word-slice, "disp8")
    2893     #       emit(out, word-slice, 1)
    2894     #       break
    2895     #
    2896     # . prolog
    2897     55/push-EBP
    2898     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    2899     # . save registers
    2900     50/push-EAX
    2901     51/push-ECX
    2902     52/push-EDX
    2903     # var word-slice/ECX = {0, 0}
    2904     68/push  0/imm32/end
    2905     68/push  0/imm32/start
    2906     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    2907     # rewind-stream(line)
    2908     # . . push args
    2909     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2910     # . . call
    2911     e8/call  rewind-stream/disp32
    2912     # . . discard args
    2913     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    2914 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
    2940 $emit-disp:loop:
    2941     # next-word(line, word-slice)
    2942     # . . push args
    2943     51/push-ECX
    2944     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    2945     # . . call
    2946     e8/call  next-word/disp32
    2947     # . . discard args
    2948     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    2949 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
    2991 $emit-disp:check0:
    2992     # if (slice-empty?(word-slice)) break
    2993     # . EAX = slice-empty?(word-slice)
    2994     # . . push args
    2995     51/push-ECX
    2996     # . . call
    2997     e8/call  slice-empty?/disp32
    2998     # . . discard args
    2999     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3000     # . if (EAX != 0) pass through
    3001     3d/compare-EAX-and  0/imm32
    3002     0f 85/jump-if-not-equal  $emit-disp:break/disp32
    3003 $emit-disp:check1:
    3004     # if (slice-starts-with?(word-slice, "#")) break
    3005     # . start/EDX = word-slice->start
    3006     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    3007     # . c/EAX = *start
    3008     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    3009     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    3010     # . if (EAX == '#') break
    3011     3d/compare-EAX-and  0x23/imm32/hash
    3012     0f 84/jump-if-equal  $emit-disp:break/disp32
    3013 $emit-disp:check-for-disp32:
    3014     # if (has-metadata?(word-slice, "disp32"))
    3015     # . EAX = has-metadata?(ECX, "disp32")
    3016     # . . push args
    3017     68/push  "disp32"/imm32
    3018     51/push-ECX
    3019     # . . call
    3020     e8/call  has-metadata?/disp32
    3021     # . . discard args
    3022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3023     # . if (EAX == 0) goto next check
    3024     3d/compare-EAX-and  0/imm32
    3025     74/jump-if-equal  $emit-disp:check-for-disp16/disp8
    3026 $emit-disp:disp32:
    3027     # emit(out, word-slice, 4)
    3028     # . . push args
    3029     68/push  4/imm32
    3030     51/push-ECX
    3031     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    3032     # . . call
    3033     e8/call  emit/disp32
    3034     # . . discard args
    3035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3036     # break
    3037     e9/jump  $emit-disp:break/disp32
    3038 $emit-disp:check-for-disp16:
    3039     # else if (has-metadata?(word-slice, "disp16"))
    3040     # . EAX = has-metadata?(ECX, "disp16")
    3041     # . . push args
    3042     68/push  "disp16"/imm32
    3043     51/push-ECX
    3044     # . . call
    3045     e8/call  has-metadata?/disp32
    3046     # . . discard args
    3047     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3048     # . if (EAX == 0) goto next check
    3049     3d/compare-EAX-and  0/imm32
    3050     74/jump-if-equal  $emit-disp:check-for-disp8/disp8
    3051 $emit-disp:disp16:
    3052     # emit(out, word-slice, 2)
    3053     # . . push args
    3054     68/push  2/imm32
    3055     51/push-ECX
    3056     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    3057     # . . call
    3058     e8/call  emit/disp32
    3059     # . . discard args
    3060     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3061     # break
    3062     e9/jump  $emit-disp:break/disp32
    3063 $emit-disp:check-for-disp8:
    3064     # if (has-metadata?(word-slice, "disp8"))
    3065     # . EAX = has-metadata?(ECX, "disp8")
    3066     # . . push args
    3067     68/push  "disp8"/imm32
    3068     51/push-ECX
    3069     # . . call
    3070     e8/call  has-metadata?/disp32
    3071     # . . discard args
    3072     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3073     # . if (EAX == 0) loop
    3074     3d/compare-EAX-and  0/imm32
    3075     0f 84/jump-if-equal  $emit-disp:loop/disp32
    3076 $emit-disp:disp8:
    3077     # emit(out, word-slice, 1)
    3078     # . . push args
    3079     68/push  1/imm32
    3080     51/push-ECX
    3081     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    3082     # . . call
    3083     e8/call  emit/disp32
    3084     # . . discard args
    3085     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3086     # break
    3087 $emit-disp:break:
    3088     # . restore locals
    3089     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3090     # . restore registers
    3091     5a/pop-to-EDX
    3092     59/pop-to-ECX
    3093     58/pop-to-EAX
    3094     # . epilog
    3095     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3096     5d/pop-to-EBP
    3097     c3/return
    3098
    3099 emit-imm:  # line : (address stream byte), out : (address buffered-file) -> <void>
    3100     # pseudocode:
    3101     #   rewind-stream(line)
    3102     #   var word-slice = {0, 0}
    3103     #   while true
    3104     #     word-slice = next-word(line)
    3105     #     if (slice-empty?(word-slice)) break
    3106     #     if (slice-starts-with?(word-slice, "#")) break
    3107     #     if has-metadata?(word-slice, "imm32")
    3108     #       emit(out, word-slice, 4)
    3109     #       break
    3110     #     if has-metadata?(word-slice, "imm16")
    3111     #       emit(out, word-slice, 2)
    3112     #       break
    3113     #     if has-metadata?(word-slice, "imm8")
    3114     #       emit(out, word-slice, 1)
    3115     #       break
    3116     #
    3117     # . prolog
    3118     55/push-EBP
    3119     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3120     # . save registers
    3121     50/push-EAX
    3122     51/push-ECX
    3123     52/push-EDX
    3124     # var word-slice/ECX = {0, 0}
    3125     68/push  0/imm32/end
    3126     68/push  0/imm32/start
    3127     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    3128     # rewind-stream(line)
    3129     # . . push args
    3130     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    3131     # . . call
    3132     e8/call  rewind-stream/disp32
    3133     # . . discard args
    3134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3135 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
    3161 $emit-imm:loop:
    3162     # next-word(line, word-slice)
    3163     # . . push args
    3164     51/push-ECX
    3165     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    3166     # . . call
    3167     e8/call  next-word/disp32
    3168     # . . discard args
    3169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3170 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
    3212 $emit-imm:check0:
    3213     # if (slice-empty?(word-slice)) break
    3214     # . EAX = slice-empty?(word-slice)
    3215     # . . push args
    3216     51/push-ECX
    3217     # . . call
    3218     e8/call  slice-empty?/disp32
    3219     # . . discard args
    3220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3221     # . if (EAX != 0) pass through
    3222     3d/compare-EAX-and  0/imm32
    3223     0f 85/jump-if-not-equal  $emit-imm:break/disp32
    3224 $emit-imm:check1:
    3225     # if (slice-starts-with?(word-slice, "#")) break
    3226     # . start/EDX = slice->start
    3227     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
    3228     # . c/EAX = *start
    3229     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    3230     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
    3231     # . if (EAX == '#') break
    3232     3d/compare-EAX-and  0x23/imm32/hash
    3233     0f 84/jump-if-equal  $emit-imm:break/disp32
    3234 $emit-imm:check-for-imm32:
    3235     # if (has-metadata?(word-slice, "imm32"))
    3236     # . EAX = has-metadata?(ECX, "imm32")
    3237     # . . push args
    3238     68/push  "imm32"/imm32
    3239     51/push-ECX
    3240     # . . call
    3241     e8/call  has-metadata?/disp32
    3242     # . . discard args
    3243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3244     # . if (EAX == 0) goto next check
    3245     3d/compare-EAX-and  0/imm32
    3246     74/jump-if-equal  $emit-imm:check-for-imm16/disp8
    3247 $emit-imm:imm32:
    3248     # emit(out, word-slice, 4)
    3249     # . . push args
    3250     68/push  4/imm32
    3251     51/push-ECX
    3252     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    3253     # . . call
    3254     e8/call  emit/disp32
    3255     # . . discard args
    3256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3257     # break
    3258     e9/jump  $emit-imm:break/disp32
    3259 $emit-imm:check-for-imm16:
    3260     # if (has-metadata?(word-slice, "imm16"))
    3261     # . EAX = has-metadata?(ECX, "imm16")
    3262     # . . push args
    3263     68/push  "imm16"/imm32
    3264     51/push-ECX
    3265     # . . call
    3266     e8/call  has-metadata?/disp32
    3267     # . . discard args
    3268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3269     # . if (EAX == 0) goto next check
    3270     3d/compare-EAX-and  0/imm32
    3271     74/jump-if-equal  $emit-imm:check-for-imm8/disp8
    3272 $emit-imm:imm16:
    3273     # emit(out, word-slice, 2)
    3274     # . . push args
    3275     68/push  2/imm32
    3276     51/push-ECX
    3277     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    3278     # . . call
    3279     e8/call  emit/disp32
    3280     # . . discard args
    3281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3282     # break
    3283     e9/jump  $emit-imm:break/disp32
    3284 $emit-imm:check-for-imm8:
    3285     # if (has-metadata?(word-slice, "imm8"))
    3286     # . EAX = has-metadata?(ECX, "imm8")
    3287     # . . push args
    3288     68/push  "imm8"/imm32
    3289     51/push-ECX
    3290     # . . call
    3291     e8/call  has-metadata?/disp32
    3292     # . . discard args
    3293     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3294     # . if (EAX == 0) loop
    3295     3d/compare-EAX-and  0/imm32
    3296     0f 84/jump-if-equal  $emit-imm:loop/disp32
    3297 $emit-imm:imm8:
    3298     # emit(out, word-slice, 1)
    3299     # . . push args
    3300     68/push  1/imm32
    3301     51/push-ECX
    3302     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    3303     # . . call
    3304     e8/call  emit/disp32
    3305     # . . discard args
    3306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3307     # break
    3308 $emit-imm:break:
    3309     # . restore locals
    3310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3311     # . restore registers
    3312     5a/pop-to-EDX
    3313     59/pop-to-ECX
    3314     58/pop-to-EAX
    3315     # . epilog
    3316     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3317     5d/pop-to-EBP
    3318     c3/return
    3319
    3320 emit-line-in-comment:  # line : (address stream byte), out : (address buffered-file) -> <void>
    3321     # . prolog
    3322     55/push-EBP
    3323     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3324     # write-buffered(out, " # ")
    3325     # . . push args
    3326     68/push  " # "/imm32
    3327     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    3328     # . . call
    3329     e8/call  write-buffered/disp32
    3330     # . . discard args
    3331     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3332     # write-stream-data(out, line)
    3333     # . . push args
    3334     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    3335     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    3336     # . . call
    3337     e8/call  write-stream-data/disp32
    3338     # . . discard args
    3339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3340 $emit-line-in-comment:end:
    3341     # . epilog
    3342     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3343     5d/pop-to-EBP
    3344     c3/return
    3345
    3346 test-convert-instruction-passes-comments-through:
    3347     # if a line starts with '#', pass it along unchanged
    3348     # . prolog
    3349     55/push-EBP
    3350     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3351     # setup
    3352     # . clear-stream(_test-input-stream)
    3353     # . . push args
    3354     68/push  _test-input-stream/imm32
    3355     # . . call
    3356     e8/call  clear-stream/disp32
    3357     # . . discard args
    3358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3359     # . clear-stream(_test-output-stream)
    3360     # . . push args
    3361     68/push  _test-output-stream/imm32
    3362     # . . call
    3363     e8/call  clear-stream/disp32
    3364     # . . discard args
    3365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3366     # . clear-stream(_test-output-buffered-file+4)
    3367     # . . push args
    3368     b8/copy-to-EAX  _test-output-buffered-file/imm32
    3369     05/add-to-EAX  4/imm32
    3370     50/push-EAX
    3371     # . . call
    3372     e8/call  clear-stream/disp32
    3373     # . . discard args
    3374     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3375     # initialize input
    3376     # . write(_test-input-stream, "# abcd")
    3377     # . . push args
    3378     68/push  "# abcd"/imm32
    3379     68/push  _test-input-stream/imm32
    3380     # . . call
    3381     e8/call  write/disp32
    3382     # . . discard args
    3383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3384     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3385     # . . push args
    3386     68/push  _test-output-buffered-file/imm32
    3387     68/push  _test-input-stream/imm32
    3388     # . . call
    3389     e8/call  convert-instruction/disp32
    3390     # . . discard args
    3391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3392     # check that the line just passed through
    3393     # . flush(_test-output-buffered-file)
    3394     # . . push args
    3395     68/push  _test-output-buffered-file/imm32
    3396     # . . call
    3397     e8/call  flush/disp32
    3398     # . . discard args
    3399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3400     # . check-stream-equal(_test-output-stream, "# abcd", msg)
    3401     # . . push args
    3402     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
    3403     68/push  "# abcd"/imm32
    3404     68/push  _test-output-stream/imm32
    3405     # . . call
    3406     e8/call  check-stream-equal/disp32
    3407     # . . discard args
    3408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3409     # . epilog
    3410     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3411     5d/pop-to-EBP
    3412     c3/return
    3413
    3414 test-convert-instruction-passes-labels-through:
    3415     # if the first word ends with ':', pass along the entire line unchanged
    3416     # . prolog
    3417     55/push-EBP
    3418     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3419     # setup
    3420     # . clear-stream(_test-input-stream)
    3421     # . . push args
    3422     68/push  _test-input-stream/imm32
    3423     # . . call
    3424     e8/call  clear-stream/disp32
    3425     # . . discard args
    3426     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3427     # . clear-stream(_test-output-stream)
    3428     # . . push args
    3429     68/push  _test-output-stream/imm32
    3430     # . . call
    3431     e8/call  clear-stream/disp32
    3432     # . . discard args
    3433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3434     # . clear-stream(_test-output-buffered-file+4)
    3435     # . . push args
    3436     b8/copy-to-EAX  _test-output-buffered-file/imm32
    3437     05/add-to-EAX  4/imm32
    3438     50/push-EAX
    3439     # . . call
    3440     e8/call  clear-stream/disp32
    3441     # . . discard args
    3442     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3443     # initialize input
    3444     # . write(_test-input-stream, "ab: # cd")
    3445     # . . push args
    3446     68/push  "ab: # cd"/imm32
    3447     68/push  _test-input-stream/imm32
    3448     # . . call
    3449     e8/call  write/disp32
    3450     # . . discard args
    3451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3452     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3453     # . . push args
    3454     68/push  _test-output-buffered-file/imm32
    3455     68/push  _test-input-stream/imm32
    3456     # . . call
    3457     e8/call  convert-instruction/disp32
    3458     # . . discard args
    3459     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3460     # check that the line just passed through
    3461     # . flush(_test-output-buffered-file)
    3462     # . . push args
    3463     68/push  _test-output-buffered-file/imm32
    3464     # . . call
    3465     e8/call  flush/disp32
    3466     # . . discard args
    3467     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3468     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
    3469     # . . push args
    3470     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
    3471     68/push  "ab: # cd"/imm32
    3472     68/push  _test-output-stream/imm32
    3473     # . . call
    3474     e8/call  check-stream-equal/disp32
    3475     # . . discard args
    3476     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3477     # . epilog
    3478     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3479     5d/pop-to-EBP
    3480     c3/return
    3481
    3482 test-convert-instruction-handles-single-opcode:
    3483     # if the instruction consists of a single opcode, strip its metadata and pass it along
    3484     # . prolog
    3485     55/push-EBP
    3486     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3487     # setup
    3488     # . clear-stream(_test-input-stream)
    3489     # . . push args
    3490     68/push  _test-input-stream/imm32
    3491     # . . call
    3492     e8/call  clear-stream/disp32
    3493     # . . discard args
    3494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3495     # . clear-stream(_test-output-stream)
    3496     # . . push args
    3497     68/push  _test-output-stream/imm32
    3498     # . . call
    3499     e8/call  clear-stream/disp32
    3500     # . . discard args
    3501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3502     # . clear-stream(_test-output-buffered-file+4)
    3503     # . . push args
    3504     b8/copy-to-EAX  _test-output-buffered-file/imm32
    3505     05/add-to-EAX  4/imm32
    3506     50/push-EAX
    3507     # . . call
    3508     e8/call  clear-stream/disp32
    3509     # . . discard args
    3510     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3511     # initialize input
    3512     # . write(_test-input-stream, "ab/cd # comment")
    3513     # . . push args
    3514     68/push  "ab/cd # comment"/imm32
    3515     68/push  _test-input-stream/imm32
    3516     # . . call
    3517     e8/call  write/disp32
    3518     # . . discard args
    3519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3520     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3521     # . . push args
    3522     68/push  _test-output-buffered-file/imm32
    3523     68/push  _test-input-stream/imm32
    3524     # . . call
    3525     e8/call  convert-instruction/disp32
    3526     # . . discard args
    3527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3528     # check output
    3529     # . flush(_test-output-buffered-file)
    3530     # . . push args
    3531     68/push  _test-output-buffered-file/imm32
    3532     # . . call
    3533     e8/call  flush/disp32
    3534     # . . discard args
    3535     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3536 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    3562     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
    3563     # . . push args
    3564     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
    3565     68/push  "ab  # ab/cd # comment"/imm32
    3566     68/push  _test-output-stream/imm32
    3567     # . . call
    3568     e8/call  check-stream-equal/disp32
    3569     # . . discard args
    3570     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3571     # . epilog
    3572     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3573     5d/pop-to-EBP
    3574     c3/return
    3575
    3576 test-convert-instruction-handles-0f-opcode:
    3577     # if the instruction starts with 0f opcode, include a second opcode
    3578     # . prolog
    3579     55/push-EBP
    3580     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3581     # setup
    3582     # . clear-stream(_test-input-stream)
    3583     # . . push args
    3584     68/push  _test-input-stream/imm32
    3585     # . . call
    3586     e8/call  clear-stream/disp32
    3587     # . . discard args
    3588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3589     # . clear-stream(_test-output-stream)
    3590     # . . push args
    3591     68/push  _test-output-stream/imm32
    3592     # . . call
    3593     e8/call  clear-stream/disp32
    3594     # . . discard args
    3595     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3596     # . clear-stream(_test-output-buffered-file+4)
    3597     # . . push args
    3598     b8/copy-to-EAX  _test-output-buffered-file/imm32
    3599     05/add-to-EAX  4/imm32
    3600     50/push-EAX
    3601     # . . call
    3602     e8/call  clear-stream/disp32
    3603     # . . discard args
    3604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3605     # initialize input
    3606     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
    3607     # . . push args
    3608     68/push  "0f/m1 ab/m2 # comment"/imm32
    3609     68/push  _test-input-stream/imm32
    3610     # . . call
    3611     e8/call  write/disp32
    3612     # . . discard args
    3613     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3614     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3615     # . . push args
    3616     68/push  _test-output-buffered-file/imm32
    3617     68/push  _test-input-stream/imm32
    3618     # . . call
    3619     e8/call  convert-instruction/disp32
    3620     # . . discard args
    3621     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3622     # check output
    3623     # . flush(_test-output-buffered-file)
    3624     # . . push args
    3625     68/push  _test-output-buffered-file/imm32
    3626     # . . call
    3627     e8/call  flush/disp32
    3628     # . . discard args
    3629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3630 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    3656     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
    3657     # . . push args
    3658     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
    3659     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
    3660     68/push  _test-output-stream/imm32
    3661     # . . call
    3662     e8/call  check-stream-equal/disp32
    3663     # . . discard args
    3664     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3665     # . epilog
    3666     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3667     5d/pop-to-EBP
    3668     c3/return
    3669
    3670 test-convert-instruction-handles-f2-opcode:
    3671     # if the instruction starts with f2 opcode, include a second opcode
    3672     # . prolog
    3673     55/push-EBP
    3674     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3675     # setup
    3676     # . clear-stream(_test-input-stream)
    3677     # . . push args
    3678     68/push  _test-input-stream/imm32
    3679     # . . call
    3680     e8/call  clear-stream/disp32
    3681     # . . discard args
    3682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3683     # . clear-stream(_test-output-stream)
    3684     # . . push args
    3685     68/push  _test-output-stream/imm32
    3686     # . . call
    3687     e8/call  clear-stream/disp32
    3688     # . . discard args
    3689     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3690     # . clear-stream(_test-output-buffered-file+4)
    3691     # . . push args
    3692     b8/copy-to-EAX  _test-output-buffered-file/imm32
    3693     05/add-to-EAX  4/imm32
    3694     50/push-EAX
    3695     # . . call
    3696     e8/call  clear-stream/disp32
    3697     # . . discard args
    3698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3699     # initialize input
    3700     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
    3701     # . . push args
    3702     68/push  "f2/m1 ab/m2 # comment"/imm32
    3703     68/push  _test-input-stream/imm32
    3704     # . . call
    3705     e8/call  write/disp32
    3706     # . . discard args
    3707     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3708     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3709     # . . push args
    3710     68/push  _test-output-buffered-file/imm32
    3711     68/push  _test-input-stream/imm32
    3712     # . . call
    3713     e8/call  convert-instruction/disp32
    3714     # . . discard args
    3715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3716     # check output
    3717     # . flush(_test-output-buffered-file)
    3718     # . . push args
    3719     68/push  _test-output-buffered-file/imm32
    3720     # . . call
    3721     e8/call  flush/disp32
    3722     # . . discard args
    3723     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3724 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    3750     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
    3751     # . . push args
    3752     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
    3753     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
    3754     68/push  _test-output-stream/imm32
    3755     # . . call
    3756     e8/call  check-stream-equal/disp32
    3757     # . . discard args
    3758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3759     # . epilog
    3760     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3761     5d/pop-to-EBP
    3762     c3/return
    3763
    3764 test-convert-instruction-handles-f3-opcode:
    3765     # if the instruction starts with f3 opcode, include a second opcode
    3766     # . prolog
    3767     55/push-EBP
    3768     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3769     # setup
    3770     # . clear-stream(_test-input-stream)
    3771     # . . push args
    3772     68/push  _test-input-stream/imm32
    3773     # . . call
    3774     e8/call  clear-stream/disp32
    3775     # . . discard args
    3776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3777     # . clear-stream(_test-output-stream)
    3778     # . . push args
    3779     68/push  _test-output-stream/imm32
    3780     # . . call
    3781     e8/call  clear-stream/disp32
    3782     # . . discard args
    3783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3784     # . clear-stream(_test-output-buffered-file+4)
    3785     # . . push args
    3786     b8/copy-to-EAX  _test-output-buffered-file/imm32
    3787     05/add-to-EAX  4/imm32
    3788     50/push-EAX
    3789     # . . call
    3790     e8/call  clear-stream/disp32
    3791     # . . discard args
    3792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3793     # initialize input
    3794     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
    3795     # . . push args
    3796     68/push  "f3/m1 ab/m2 # comment"/imm32
    3797     68/push  _test-input-stream/imm32
    3798     # . . call
    3799     e8/call  write/disp32
    3800     # . . discard args
    3801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3802     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3803     # . . push args
    3804     68/push  _test-output-buffered-file/imm32
    3805     68/push  _test-input-stream/imm32
    3806     # . . call
    3807     e8/call  convert-instruction/disp32
    3808     # . . discard args
    3809     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3810     # check output
    3811     # . flush(_test-output-buffered-file)
    3812     # . . push args
    3813     68/push  _test-output-buffered-file/imm32
    3814     # . . call
    3815     e8/call  flush/disp32
    3816     # . . discard args
    3817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3818 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    3844     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
    3845     # . . push args
    3846     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
    3847     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
    3848     68/push  _test-output-stream/imm32
    3849     # . . call
    3850     e8/call  check-stream-equal/disp32
    3851     # . . discard args
    3852     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3853     # . epilog
    3854     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3855     5d/pop-to-EBP
    3856     c3/return
    3857
    3858 test-convert-instruction-handles-f2-0f-opcode:
    3859     # if the instruction starts with f2 0f opcode, include a second opcode
    3860     # . prolog
    3861     55/push-EBP
    3862     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3863     # setup
    3864     # . clear-stream(_test-input-stream)
    3865     # . . push args
    3866     68/push  _test-input-stream/imm32
    3867     # . . call
    3868     e8/call  clear-stream/disp32
    3869     # . . discard args
    3870     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3871     # . clear-stream(_test-output-stream)
    3872     # . . push args
    3873     68/push  _test-output-stream/imm32
    3874     # . . call
    3875     e8/call  clear-stream/disp32
    3876     # . . discard args
    3877     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3878     # . clear-stream(_test-output-buffered-file+4)
    3879     # . . push args
    3880     b8/copy-to-EAX  _test-output-buffered-file/imm32
    3881     05/add-to-EAX  4/imm32
    3882     50/push-EAX
    3883     # . . call
    3884     e8/call  clear-stream/disp32
    3885     # . . discard args
    3886     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3887     # initialize input
    3888     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
    3889     # . . push args
    3890     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
    3891     68/push  _test-input-stream/imm32
    3892     # . . call
    3893     e8/call  write/disp32
    3894     # . . discard args
    3895     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3896     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3897     # . . push args
    3898     68/push  _test-output-buffered-file/imm32
    3899     68/push  _test-input-stream/imm32
    3900     # . . call
    3901     e8/call  convert-instruction/disp32
    3902     # . . discard args
    3903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3904     # check output
    3905     # . flush(_test-output-buffered-file)
    3906     # . . push args
    3907     68/push  _test-output-buffered-file/imm32
    3908     # . . call
    3909     e8/call  flush/disp32
    3910     # . . discard args
    3911     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3912 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    3938     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
    3939     # . . push args
    3940     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
    3941     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
    3942     68/push  _test-output-stream/imm32
    3943     # . . call
    3944     e8/call  check-stream-equal/disp32
    3945     # . . discard args
    3946     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    3947     # . epilog
    3948     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    3949     5d/pop-to-EBP
    3950     c3/return
    3951
    3952 test-convert-instruction-handles-f3-0f-opcode:
    3953     # if the instruction starts with f3 0f opcode, include a second opcode
    3954     # . prolog
    3955     55/push-EBP
    3956     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    3957     # setup
    3958     # . clear-stream(_test-input-stream)
    3959     # . . push args
    3960     68/push  _test-input-stream/imm32
    3961     # . . call
    3962     e8/call  clear-stream/disp32
    3963     # . . discard args
    3964     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3965     # . clear-stream(_test-output-stream)
    3966     # . . push args
    3967     68/push  _test-output-stream/imm32
    3968     # . . call
    3969     e8/call  clear-stream/disp32
    3970     # . . discard args
    3971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3972     # . clear-stream(_test-output-buffered-file+4)
    3973     # . . push args
    3974     b8/copy-to-EAX  _test-output-buffered-file/imm32
    3975     05/add-to-EAX  4/imm32
    3976     50/push-EAX
    3977     # . . call
    3978     e8/call  clear-stream/disp32
    3979     # . . discard args
    3980     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    3981     # initialize input
    3982     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
    3983     # . . push args
    3984     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
    3985     68/push  _test-input-stream/imm32
    3986     # . . call
    3987     e8/call  write/disp32
    3988     # . . discard args
    3989     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3990     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3991     # . . push args
    3992     68/push  _test-output-buffered-file/imm32
    3993     68/push  _test-input-stream/imm32
    3994     # . . call
    3995     e8/call  convert-instruction/disp32
    3996     # . . discard args
    3997     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    3998     # check output
    3999     # . flush(_test-output-buffered-file)
    4000     # . . push args
    4001     68/push  _test-output-buffered-file/imm32
    4002     # . . call
    4003     e8/call  flush/disp32
    4004     # . . discard args
    4005     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4006 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4032     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
    4033     # . . push args
    4034     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
    4035     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
    4036     68/push  _test-output-stream/imm32
    4037     # . . call
    4038     e8/call  check-stream-equal/disp32
    4039     # . . discard args
    4040     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4041     # . epilog
    4042     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4043     5d/pop-to-EBP
    4044     c3/return
    4045
    4046 test-convert-instruction-handles-unused-opcodes:
    4047     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
    4048     # . prolog
    4049     55/push-EBP
    4050     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4051     # setup
    4052     # . clear-stream(_test-input-stream)
    4053     # . . push args
    4054     68/push  _test-input-stream/imm32
    4055     # . . call
    4056     e8/call  clear-stream/disp32
    4057     # . . discard args
    4058     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4059     # . clear-stream(_test-output-stream)
    4060     # . . push args
    4061     68/push  _test-output-stream/imm32
    4062     # . . call
    4063     e8/call  clear-stream/disp32
    4064     # . . discard args
    4065     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4066     # . clear-stream(_test-output-buffered-file+4)
    4067     # . . push args
    4068     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4069     05/add-to-EAX  4/imm32
    4070     50/push-EAX
    4071     # . . call
    4072     e8/call  clear-stream/disp32
    4073     # . . discard args
    4074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4075     # initialize input
    4076     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
    4077     # . . push args
    4078     68/push  "ab/m1 cd/m2 # comment"/imm32
    4079     68/push  _test-input-stream/imm32
    4080     # . . call
    4081     e8/call  write/disp32
    4082     # . . discard args
    4083     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4084     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4085     # . . push args
    4086     68/push  _test-output-buffered-file/imm32
    4087     68/push  _test-input-stream/imm32
    4088     # . . call
    4089     e8/call  convert-instruction/disp32
    4090     # . . discard args
    4091     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4092     # check output
    4093     # . flush(_test-output-buffered-file)
    4094     # . . push args
    4095     68/push  _test-output-buffered-file/imm32
    4096     # . . call
    4097     e8/call  flush/disp32
    4098     # . . discard args
    4099     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4100 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4126     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
    4127     # . . push args
    4128     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
    4129     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
    4130     68/push  _test-output-stream/imm32
    4131     # . . call
    4132     e8/call  check-stream-equal/disp32
    4133     # . . discard args
    4134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4135     # . epilog
    4136     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4137     5d/pop-to-EBP
    4138     c3/return
    4139
    4140 test-convert-instruction-handles-unused-second-opcodes:
    4141     # if the second opcode isn't 0f, don't include further opcodes
    4142     # . prolog
    4143     55/push-EBP
    4144     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4145     # setup
    4146     # . clear-stream(_test-input-stream)
    4147     # . . push args
    4148     68/push  _test-input-stream/imm32
    4149     # . . call
    4150     e8/call  clear-stream/disp32
    4151     # . . discard args
    4152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4153     # . clear-stream(_test-output-stream)
    4154     # . . push args
    4155     68/push  _test-output-stream/imm32
    4156     # . . call
    4157     e8/call  clear-stream/disp32
    4158     # . . discard args
    4159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4160     # . clear-stream(_test-output-buffered-file+4)
    4161     # . . push args
    4162     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4163     05/add-to-EAX  4/imm32
    4164     50/push-EAX
    4165     # . . call
    4166     e8/call  clear-stream/disp32
    4167     # . . discard args
    4168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4169     # initialize input
    4170     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
    4171     # . . push args
    4172     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
    4173     68/push  _test-input-stream/imm32
    4174     # . . call
    4175     e8/call  write/disp32
    4176     # . . discard args
    4177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4178     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4179     # . . push args
    4180     68/push  _test-output-buffered-file/imm32
    4181     68/push  _test-input-stream/imm32
    4182     # . . call
    4183     e8/call  convert-instruction/disp32
    4184     # . . discard args
    4185     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4186     # check output
    4187     # . flush(_test-output-buffered-file)
    4188     # . . push args
    4189     68/push  _test-output-buffered-file/imm32
    4190     # . . call
    4191     e8/call  flush/disp32
    4192     # . . discard args
    4193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4194 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4220     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
    4221     # . . push args
    4222     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
    4223     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
    4224     68/push  _test-output-stream/imm32
    4225     # . . call
    4226     e8/call  check-stream-equal/disp32
    4227     # . . discard args
    4228     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4229     # . epilog
    4230     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4231     5d/pop-to-EBP
    4232     c3/return
    4233
    4234 test-convert-instruction-handles-unused-second-opcodes-2:
    4235     # if the second opcode isn't 0f, don't include further opcodes
    4236     # . prolog
    4237     55/push-EBP
    4238     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4239     # setup
    4240     # . clear-stream(_test-input-stream)
    4241     # . . push args
    4242     68/push  _test-input-stream/imm32
    4243     # . . call
    4244     e8/call  clear-stream/disp32
    4245     # . . discard args
    4246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4247     # . clear-stream(_test-output-stream)
    4248     # . . push args
    4249     68/push  _test-output-stream/imm32
    4250     # . . call
    4251     e8/call  clear-stream/disp32
    4252     # . . discard args
    4253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4254     # . clear-stream(_test-output-buffered-file+4)
    4255     # . . push args
    4256     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4257     05/add-to-EAX  4/imm32
    4258     50/push-EAX
    4259     # . . call
    4260     e8/call  clear-stream/disp32
    4261     # . . discard args
    4262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4263     # initialize input
    4264     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
    4265     # . . push args
    4266     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
    4267     68/push  _test-input-stream/imm32
    4268     # . . call
    4269     e8/call  write/disp32
    4270     # . . discard args
    4271     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4272     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4273     # . . push args
    4274     68/push  _test-output-buffered-file/imm32
    4275     68/push  _test-input-stream/imm32
    4276     # . . call
    4277     e8/call  convert-instruction/disp32
    4278     # . . discard args
    4279     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4280     # check output
    4281     # . flush(_test-output-buffered-file)
    4282     # . . push args
    4283     68/push  _test-output-buffered-file/imm32
    4284     # . . call
    4285     e8/call  flush/disp32
    4286     # . . discard args
    4287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4288 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4314     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
    4315     # . . push args
    4316     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
    4317     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
    4318     68/push  _test-output-stream/imm32
    4319     # . . call
    4320     e8/call  check-stream-equal/disp32
    4321     # . . discard args
    4322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4323     # . epilog
    4324     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4325     5d/pop-to-EBP
    4326     c3/return
    4327
    4328 test-convert-instruction-emits-modrm-byte:
    4329     # pack mod, rm32 and r32 operands into ModR/M byte
    4330     # . prolog
    4331     55/push-EBP
    4332     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4333     # setup
    4334     # . clear-stream(_test-input-stream)
    4335     # . . push args
    4336     68/push  _test-input-stream/imm32
    4337     # . . call
    4338     e8/call  clear-stream/disp32
    4339     # . . discard args
    4340     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4341     # . clear-stream(_test-output-stream)
    4342     # . . push args
    4343     68/push  _test-output-stream/imm32
    4344     # . . call
    4345     e8/call  clear-stream/disp32
    4346     # . . discard args
    4347     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4348     # . clear-stream(_test-output-buffered-file+4)
    4349     # . . push args
    4350     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4351     05/add-to-EAX  4/imm32
    4352     50/push-EAX
    4353     # . . call
    4354     e8/call  clear-stream/disp32
    4355     # . . discard args
    4356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4357     # initialize input
    4358     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
    4359     # . . push args
    4360     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
    4361     68/push  _test-input-stream/imm32
    4362     # . . call
    4363     e8/call  write/disp32
    4364     # . . discard args
    4365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4366     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4367     # . . push args
    4368     68/push  _test-output-buffered-file/imm32
    4369     68/push  _test-input-stream/imm32
    4370     # . . call
    4371     e8/call  convert-instruction/disp32
    4372     # . . discard args
    4373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4374     # check output
    4375     # . flush(_test-output-buffered-file)
    4376     # . . push args
    4377     68/push  _test-output-buffered-file/imm32
    4378     # . . call
    4379     e8/call  flush/disp32
    4380     # . . discard args
    4381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4382 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4408     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
    4409     # . . push args
    4410     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
    4411     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
    4412     68/push  _test-output-stream/imm32
    4413     # . . call
    4414     e8/call  check-stream-equal/disp32
    4415     # . . discard args
    4416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4417     # . epilog
    4418     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4419     5d/pop-to-EBP
    4420     c3/return
    4421
    4422 test-convert-instruction-emits-modrm-byte-from-subop:
    4423     # pack mod, rm32 and subop operands into ModR/M byte
    4424     # . prolog
    4425     55/push-EBP
    4426     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4427     # setup
    4428     # . clear-stream(_test-input-stream)
    4429     # . . push args
    4430     68/push  _test-input-stream/imm32
    4431     # . . call
    4432     e8/call  clear-stream/disp32
    4433     # . . discard args
    4434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4435     # . clear-stream(_test-output-stream)
    4436     # . . push args
    4437     68/push  _test-output-stream/imm32
    4438     # . . call
    4439     e8/call  clear-stream/disp32
    4440     # . . discard args
    4441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4442     # . clear-stream(_test-output-buffered-file+4)
    4443     # . . push args
    4444     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4445     05/add-to-EAX  4/imm32
    4446     50/push-EAX
    4447     # . . call
    4448     e8/call  clear-stream/disp32
    4449     # . . discard args
    4450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4451     # initialize input
    4452     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
    4453     # . . push args
    4454     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
    4455     68/push  _test-input-stream/imm32
    4456     # . . call
    4457     e8/call  write/disp32
    4458     # . . discard args
    4459     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4460     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4461     # . . push args
    4462     68/push  _test-output-buffered-file/imm32
    4463     68/push  _test-input-stream/imm32
    4464     # . . call
    4465     e8/call  convert-instruction/disp32
    4466     # . . discard args
    4467     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4468     # check output
    4469     # . flush(_test-output-buffered-file)
    4470     # . . push args
    4471     68/push  _test-output-buffered-file/imm32
    4472     # . . call
    4473     e8/call  flush/disp32
    4474     # . . discard args
    4475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4476 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4502     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
    4503     # . . push args
    4504     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
    4505     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
    4506     68/push  _test-output-stream/imm32
    4507     # . . call
    4508     e8/call  check-stream-equal/disp32
    4509     # . . discard args
    4510     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4511     # . epilog
    4512     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4513     5d/pop-to-EBP
    4514     c3/return
    4515
    4516 test-convert-instruction-emits-modrm-byte-with-missing-mod:
    4517     # pack rm32 and r32 operands into ModR/M byte
    4518     # . prolog
    4519     55/push-EBP
    4520     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4521     # setup
    4522     # . clear-stream(_test-input-stream)
    4523     # . . push args
    4524     68/push  _test-input-stream/imm32
    4525     # . . call
    4526     e8/call  clear-stream/disp32
    4527     # . . discard args
    4528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4529     # . clear-stream(_test-output-stream)
    4530     # . . push args
    4531     68/push  _test-output-stream/imm32
    4532     # . . call
    4533     e8/call  clear-stream/disp32
    4534     # . . discard args
    4535     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4536     # . clear-stream(_test-output-buffered-file+4)
    4537     # . . push args
    4538     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4539     05/add-to-EAX  4/imm32
    4540     50/push-EAX
    4541     # . . call
    4542     e8/call  clear-stream/disp32
    4543     # . . discard args
    4544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4545     # initialize input
    4546     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
    4547     # . . push args
    4548     68/push  "8b/copy 0/rm32 1/r32"/imm32
    4549     68/push  _test-input-stream/imm32
    4550     # . . call
    4551     e8/call  write/disp32
    4552     # . . discard args
    4553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4554     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4555     # . . push args
    4556     68/push  _test-output-buffered-file/imm32
    4557     68/push  _test-input-stream/imm32
    4558     # . . call
    4559     e8/call  convert-instruction/disp32
    4560     # . . discard args
    4561     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4562     # check output
    4563     # . flush(_test-output-buffered-file)
    4564     # . . push args
    4565     68/push  _test-output-buffered-file/imm32
    4566     # . . call
    4567     e8/call  flush/disp32
    4568     # . . discard args
    4569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4570 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4596     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
    4597     # . . push args
    4598     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
    4599     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
    4600     68/push  _test-output-stream/imm32
    4601     # . . call
    4602     e8/call  check-stream-equal/disp32
    4603     # . . discard args
    4604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4605     # . epilog
    4606     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4607     5d/pop-to-EBP
    4608     c3/return
    4609
    4610 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
    4611     # pack mod and r32 operands into ModR/M byte
    4612     # . prolog
    4613     55/push-EBP
    4614     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4615     # setup
    4616     # . clear-stream(_test-input-stream)
    4617     # . . push args
    4618     68/push  _test-input-stream/imm32
    4619     # . . call
    4620     e8/call  clear-stream/disp32
    4621     # . . discard args
    4622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4623     # . clear-stream(_test-output-stream)
    4624     # . . push args
    4625     68/push  _test-output-stream/imm32
    4626     # . . call
    4627     e8/call  clear-stream/disp32
    4628     # . . discard args
    4629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4630     # . clear-stream(_test-output-buffered-file+4)
    4631     # . . push args
    4632     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4633     05/add-to-EAX  4/imm32
    4634     50/push-EAX
    4635     # . . call
    4636     e8/call  clear-stream/disp32
    4637     # . . discard args
    4638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4639     # initialize input
    4640     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
    4641     # . . push args
    4642     68/push  "8b/copy 0/mod 1/r32"/imm32
    4643     68/push  _test-input-stream/imm32
    4644     # . . call
    4645     e8/call  write/disp32
    4646     # . . discard args
    4647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4648     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4649     # . . push args
    4650     68/push  _test-output-buffered-file/imm32
    4651     68/push  _test-input-stream/imm32
    4652     # . . call
    4653     e8/call  convert-instruction/disp32
    4654     # . . discard args
    4655     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4656     # check output
    4657     # . flush(_test-output-buffered-file)
    4658     # . . push args
    4659     68/push  _test-output-buffered-file/imm32
    4660     # . . call
    4661     e8/call  flush/disp32
    4662     # . . discard args
    4663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4664 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4690     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
    4691     # . . push args
    4692     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
    4693     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
    4694     68/push  _test-output-stream/imm32
    4695     # . . call
    4696     e8/call  check-stream-equal/disp32
    4697     # . . discard args
    4698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4699     # . epilog
    4700     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4701     5d/pop-to-EBP
    4702     c3/return
    4703
    4704 test-convert-instruction-emits-modrm-byte-with-missing-r32:
    4705     # pack mod and rm32 operands into ModR/M byte
    4706     # . prolog
    4707     55/push-EBP
    4708     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4709     # setup
    4710     # . clear-stream(_test-input-stream)
    4711     # . . push args
    4712     68/push  _test-input-stream/imm32
    4713     # . . call
    4714     e8/call  clear-stream/disp32
    4715     # . . discard args
    4716     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4717     # . clear-stream(_test-output-stream)
    4718     # . . push args
    4719     68/push  _test-output-stream/imm32
    4720     # . . call
    4721     e8/call  clear-stream/disp32
    4722     # . . discard args
    4723     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4724     # . clear-stream(_test-output-buffered-file+4)
    4725     # . . push args
    4726     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4727     05/add-to-EAX  4/imm32
    4728     50/push-EAX
    4729     # . . call
    4730     e8/call  clear-stream/disp32
    4731     # . . discard args
    4732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4733     # initialize input
    4734     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
    4735     # . . push args
    4736     68/push  "8b/copy 0/mod 0/rm32"/imm32
    4737     68/push  _test-input-stream/imm32
    4738     # . . call
    4739     e8/call  write/disp32
    4740     # . . discard args
    4741     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4742     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4743     # . . push args
    4744     68/push  _test-output-buffered-file/imm32
    4745     68/push  _test-input-stream/imm32
    4746     # . . call
    4747     e8/call  convert-instruction/disp32
    4748     # . . discard args
    4749     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4750     # check output
    4751     # . flush(_test-output-buffered-file)
    4752     # . . push args
    4753     68/push  _test-output-buffered-file/imm32
    4754     # . . call
    4755     e8/call  flush/disp32
    4756     # . . discard args
    4757     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4758 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4784     # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
    4785     # . . push args
    4786     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
    4787     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
    4788     68/push  _test-output-stream/imm32
    4789     # . . call
    4790     e8/call  check-stream-equal/disp32
    4791     # . . discard args
    4792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4793     # . epilog
    4794     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4795     5d/pop-to-EBP
    4796     c3/return
    4797
    4798 test-convert-instruction-emits-sib-byte:
    4799     # pack base, index and scale operands into SIB byte
    4800     # . prolog
    4801     55/push-EBP
    4802     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4803     # setup
    4804     # . clear-stream(_test-input-stream)
    4805     # . . push args
    4806     68/push  _test-input-stream/imm32
    4807     # . . call
    4808     e8/call  clear-stream/disp32
    4809     # . . discard args
    4810     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4811     # . clear-stream(_test-output-stream)
    4812     # . . push args
    4813     68/push  _test-output-stream/imm32
    4814     # . . call
    4815     e8/call  clear-stream/disp32
    4816     # . . discard args
    4817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4818     # . clear-stream(_test-output-buffered-file+4)
    4819     # . . push args
    4820     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4821     05/add-to-EAX  4/imm32
    4822     50/push-EAX
    4823     # . . call
    4824     e8/call  clear-stream/disp32
    4825     # . . discard args
    4826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4827     # initialize input
    4828     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
    4829     # . . push args
    4830     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
    4831     68/push  _test-input-stream/imm32
    4832     # . . call
    4833     e8/call  write/disp32
    4834     # . . discard args
    4835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4836     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4837     # . . push args
    4838     68/push  _test-output-buffered-file/imm32
    4839     68/push  _test-input-stream/imm32
    4840     # . . call
    4841     e8/call  convert-instruction/disp32
    4842     # . . discard args
    4843     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4844     # check output
    4845     # . flush(_test-output-buffered-file)
    4846     # . . push args
    4847     68/push  _test-output-buffered-file/imm32
    4848     # . . call
    4849     e8/call  flush/disp32
    4850     # . . discard args
    4851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4852 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4878     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
    4879     # . . push args
    4880     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
    4881     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
    4882     68/push  _test-output-stream/imm32
    4883     # . . call
    4884     e8/call  check-stream-equal/disp32
    4885     # . . discard args
    4886     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4887     # . epilog
    4888     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4889     5d/pop-to-EBP
    4890     c3/return
    4891
    4892 test-convert-instruction-emits-sib-byte-with-missing-base:
    4893     # pack index and scale operands into SIB byte
    4894     # . prolog
    4895     55/push-EBP
    4896     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4897     # setup
    4898     # . clear-stream(_test-input-stream)
    4899     # . . push args
    4900     68/push  _test-input-stream/imm32
    4901     # . . call
    4902     e8/call  clear-stream/disp32
    4903     # . . discard args
    4904     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4905     # . clear-stream(_test-output-stream)
    4906     # . . push args
    4907     68/push  _test-output-stream/imm32
    4908     # . . call
    4909     e8/call  clear-stream/disp32
    4910     # . . discard args
    4911     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4912     # . clear-stream(_test-output-buffered-file+4)
    4913     # . . push args
    4914     b8/copy-to-EAX  _test-output-buffered-file/imm32
    4915     05/add-to-EAX  4/imm32
    4916     50/push-EAX
    4917     # . . call
    4918     e8/call  clear-stream/disp32
    4919     # . . discard args
    4920     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4921     # initialize input
    4922     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
    4923     # . . push args
    4924     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
    4925     68/push  _test-input-stream/imm32
    4926     # . . call
    4927     e8/call  write/disp32
    4928     # . . discard args
    4929     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4930     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4931     # . . push args
    4932     68/push  _test-output-buffered-file/imm32
    4933     68/push  _test-input-stream/imm32
    4934     # . . call
    4935     e8/call  convert-instruction/disp32
    4936     # . . discard args
    4937     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    4938     # check output
    4939     # . flush(_test-output-buffered-file)
    4940     # . . push args
    4941     68/push  _test-output-buffered-file/imm32
    4942     # . . call
    4943     e8/call  flush/disp32
    4944     # . . discard args
    4945     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4946 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    4972     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
    4973     # . . push args
    4974     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
    4975     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
    4976     68/push  _test-output-stream/imm32
    4977     # . . call
    4978     e8/call  check-stream-equal/disp32
    4979     # . . discard args
    4980     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    4981     # . epilog
    4982     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    4983     5d/pop-to-EBP
    4984     c3/return
    4985
    4986 test-convert-instruction-emits-sib-byte-with-missing-index:
    4987     # pack base and scale operands into SIB byte
    4988     # . prolog
    4989     55/push-EBP
    4990     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    4991     # setup
    4992     # . clear-stream(_test-input-stream)
    4993     # . . push args
    4994     68/push  _test-input-stream/imm32
    4995     # . . call
    4996     e8/call  clear-stream/disp32
    4997     # . . discard args
    4998     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    4999     # . clear-stream(_test-output-stream)
    5000     # . . push args
    5001     68/push  _test-output-stream/imm32
    5002     # . . call
    5003     e8/call  clear-stream/disp32
    5004     # . . discard args
    5005     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5006     # . clear-stream(_test-output-buffered-file+4)
    5007     # . . push args
    5008     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5009     05/add-to-EAX  4/imm32
    5010     50/push-EAX
    5011     # . . call
    5012     e8/call  clear-stream/disp32
    5013     # . . discard args
    5014     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5015     # initialize input
    5016     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
    5017     # . . push args
    5018     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
    5019     68/push  _test-input-stream/imm32
    5020     # . . call
    5021     e8/call  write/disp32
    5022     # . . discard args
    5023     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5024     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5025     # . . push args
    5026     68/push  _test-output-buffered-file/imm32
    5027     68/push  _test-input-stream/imm32
    5028     # . . call
    5029     e8/call  convert-instruction/disp32
    5030     # . . discard args
    5031     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5032     # check output
    5033     # . flush(_test-output-buffered-file)
    5034     # . . push args
    5035     68/push  _test-output-buffered-file/imm32
    5036     # . . call
    5037     e8/call  flush/disp32
    5038     # . . discard args
    5039     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5040 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5066     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
    5067     # . . push args
    5068     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
    5069     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
    5070     68/push  _test-output-stream/imm32
    5071     # . . call
    5072     e8/call  check-stream-equal/disp32
    5073     # . . discard args
    5074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5075     # . epilog
    5076     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5077     5d/pop-to-EBP
    5078     c3/return
    5079
    5080 test-convert-instruction-emits-sib-byte-with-missing-scale:
    5081     # pack base and index operands into SIB byte
    5082     # . prolog
    5083     55/push-EBP
    5084     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5085     # setup
    5086     # . clear-stream(_test-input-stream)
    5087     # . . push args
    5088     68/push  _test-input-stream/imm32
    5089     # . . call
    5090     e8/call  clear-stream/disp32
    5091     # . . discard args
    5092     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5093     # . clear-stream(_test-output-stream)
    5094     # . . push args
    5095     68/push  _test-output-stream/imm32
    5096     # . . call
    5097     e8/call  clear-stream/disp32
    5098     # . . discard args
    5099     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5100     # . clear-stream(_test-output-buffered-file+4)
    5101     # . . push args
    5102     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5103     05/add-to-EAX  4/imm32
    5104     50/push-EAX
    5105     # . . call
    5106     e8/call  clear-stream/disp32
    5107     # . . discard args
    5108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5109     # initialize input
    5110     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
    5111     # . . push args
    5112     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
    5113     68/push  _test-input-stream/imm32
    5114     # . . call
    5115     e8/call  write/disp32
    5116     # . . discard args
    5117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5118     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5119     # . . push args
    5120     68/push  _test-output-buffered-file/imm32
    5121     68/push  _test-input-stream/imm32
    5122     # . . call
    5123     e8/call  convert-instruction/disp32
    5124     # . . discard args
    5125     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5126     # check output
    5127     # . flush(_test-output-buffered-file)
    5128     # . . push args
    5129     68/push  _test-output-buffered-file/imm32
    5130     # . . call
    5131     e8/call  flush/disp32
    5132     # . . discard args
    5133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5134 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5160     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
    5161     # . . push args
    5162     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
    5163     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
    5164     68/push  _test-output-stream/imm32
    5165     # . . call
    5166     e8/call  check-stream-equal/disp32
    5167     # . . discard args
    5168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5169     # . epilog
    5170     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5171     5d/pop-to-EBP
    5172     c3/return
    5173
    5174 test-convert-instruction-handles-disp32-operand:
    5175     # expand /disp32 operand into 4 bytes
    5176     # . prolog
    5177     55/push-EBP
    5178     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5179     # setup
    5180     # . clear-stream(_test-input-stream)
    5181     # . . push args
    5182     68/push  _test-input-stream/imm32
    5183     # . . call
    5184     e8/call  clear-stream/disp32
    5185     # . . discard args
    5186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5187     # . clear-stream(_test-output-stream)
    5188     # . . push args
    5189     68/push  _test-output-stream/imm32
    5190     # . . call
    5191     e8/call  clear-stream/disp32
    5192     # . . discard args
    5193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5194     # . clear-stream(_test-output-buffered-file+4)
    5195     # . . push args
    5196     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5197     05/add-to-EAX  4/imm32
    5198     50/push-EAX
    5199     # . . call
    5200     e8/call  clear-stream/disp32
    5201     # . . discard args
    5202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5203     # initialize input
    5204     # . write(_test-input-stream, "e8/call 20/disp32")
    5205     # . . push args
    5206     68/push  "e8/call 20/disp32"/imm32
    5207     68/push  _test-input-stream/imm32
    5208     # . . call
    5209     e8/call  write/disp32
    5210     # . . discard args
    5211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5212     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5213     # . . push args
    5214     68/push  _test-output-buffered-file/imm32
    5215     68/push  _test-input-stream/imm32
    5216     # . . call
    5217     e8/call  convert-instruction/disp32
    5218     # . . discard args
    5219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5220     # check output
    5221     # . flush(_test-output-buffered-file)
    5222     # . . push args
    5223     68/push  _test-output-buffered-file/imm32
    5224     # . . call
    5225     e8/call  flush/disp32
    5226     # . . discard args
    5227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5228 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5254     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
    5255     # . . push args
    5256     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
    5257     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
    5258     68/push  _test-output-stream/imm32
    5259     # . . call
    5260     e8/call  check-stream-equal/disp32
    5261     # . . discard args
    5262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5263     # . epilog
    5264     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5265     5d/pop-to-EBP
    5266     c3/return
    5267
    5268 test-convert-instruction-handles-disp16-operand:
    5269     # expand /disp16 operand into 2 bytes
    5270     # . prolog
    5271     55/push-EBP
    5272     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5273     # setup
    5274     # . clear-stream(_test-input-stream)
    5275     # . . push args
    5276     68/push  _test-input-stream/imm32
    5277     # . . call
    5278     e8/call  clear-stream/disp32
    5279     # . . discard args
    5280     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5281     # . clear-stream(_test-output-stream)
    5282     # . . push args
    5283     68/push  _test-output-stream/imm32
    5284     # . . call
    5285     e8/call  clear-stream/disp32
    5286     # . . discard args
    5287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5288     # . clear-stream(_test-output-buffered-file+4)
    5289     # . . push args
    5290     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5291     05/add-to-EAX  4/imm32
    5292     50/push-EAX
    5293     # . . call
    5294     e8/call  clear-stream/disp32
    5295     # . . discard args
    5296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5297     # initialize input
    5298     # . write(_test-input-stream, "e8/call 20/disp16")
    5299     # . . push args
    5300     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
    5301     68/push  _test-input-stream/imm32
    5302     # . . call
    5303     e8/call  write/disp32
    5304     # . . discard args
    5305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5306     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5307     # . . push args
    5308     68/push  _test-output-buffered-file/imm32
    5309     68/push  _test-input-stream/imm32
    5310     # . . call
    5311     e8/call  convert-instruction/disp32
    5312     # . . discard args
    5313     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5314     # check output
    5315     # . flush(_test-output-buffered-file)
    5316     # . . push args
    5317     68/push  _test-output-buffered-file/imm32
    5318     # . . call
    5319     e8/call  flush/disp32
    5320     # . . discard args
    5321     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5322 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5348     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
    5349     # . . push args
    5350     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
    5351     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
    5352     68/push  _test-output-stream/imm32
    5353     # . . call
    5354     e8/call  check-stream-equal/disp32
    5355     # . . discard args
    5356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5357     # . epilog
    5358     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5359     5d/pop-to-EBP
    5360     c3/return
    5361
    5362 test-convert-instruction-handles-disp8-operand:
    5363     # expand /disp8 operand into 1 byte
    5364     # . prolog
    5365     55/push-EBP
    5366     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5367     # setup
    5368     # . clear-stream(_test-input-stream)
    5369     # . . push args
    5370     68/push  _test-input-stream/imm32
    5371     # . . call
    5372     e8/call  clear-stream/disp32
    5373     # . . discard args
    5374     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5375     # . clear-stream(_test-output-stream)
    5376     # . . push args
    5377     68/push  _test-output-stream/imm32
    5378     # . . call
    5379     e8/call  clear-stream/disp32
    5380     # . . discard args
    5381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5382     # . clear-stream(_test-output-buffered-file+4)
    5383     # . . push args
    5384     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5385     05/add-to-EAX  4/imm32
    5386     50/push-EAX
    5387     # . . call
    5388     e8/call  clear-stream/disp32
    5389     # . . discard args
    5390     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5391     # initialize input
    5392     # . write(_test-input-stream, "eb/jump 20/disp8")
    5393     # . . push args
    5394     68/push  "eb/jump 20/disp8"/imm32
    5395     68/push  _test-input-stream/imm32
    5396     # . . call
    5397     e8/call  write/disp32
    5398     # . . discard args
    5399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5400     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5401     # . . push args
    5402     68/push  _test-output-buffered-file/imm32
    5403     68/push  _test-input-stream/imm32
    5404     # . . call
    5405     e8/call  convert-instruction/disp32
    5406     # . . discard args
    5407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5408     # check output
    5409     # . flush(_test-output-buffered-file)
    5410     # . . push args
    5411     68/push  _test-output-buffered-file/imm32
    5412     # . . call
    5413     e8/call  flush/disp32
    5414     # . . discard args
    5415     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5416 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5442     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
    5443     # . . push args
    5444     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
    5445     68/push  "eb 20  # eb/jump 20/disp8"/imm32
    5446     68/push  _test-output-stream/imm32
    5447     # . . call
    5448     e8/call  check-stream-equal/disp32
    5449     # . . discard args
    5450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5451     # . epilog
    5452     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5453     5d/pop-to-EBP
    5454     c3/return
    5455
    5456 test-convert-instruction-handles-disp8-name:
    5457     # pass /disp8 name directly through
    5458     # . prolog
    5459     55/push-EBP
    5460     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5461     # setup
    5462     # . clear-stream(_test-input-stream)
    5463     # . . push args
    5464     68/push  _test-input-stream/imm32
    5465     # . . call
    5466     e8/call  clear-stream/disp32
    5467     # . . discard args
    5468     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5469     # . clear-stream(_test-output-stream)
    5470     # . . push args
    5471     68/push  _test-output-stream/imm32
    5472     # . . call
    5473     e8/call  clear-stream/disp32
    5474     # . . discard args
    5475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5476     # . clear-stream(_test-output-buffered-file+4)
    5477     # . . push args
    5478     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5479     05/add-to-EAX  4/imm32
    5480     50/push-EAX
    5481     # . . call
    5482     e8/call  clear-stream/disp32
    5483     # . . discard args
    5484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5485     # initialize input
    5486     # . write(_test-input-stream, "eb/jump xyz/disp8")
    5487     # . . push args
    5488     68/push  "eb/jump xyz/disp8"/imm32
    5489     68/push  _test-input-stream/imm32
    5490     # . . call
    5491     e8/call  write/disp32
    5492     # . . discard args
    5493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5494     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5495     # . . push args
    5496     68/push  _test-output-buffered-file/imm32
    5497     68/push  _test-input-stream/imm32
    5498     # . . call
    5499     e8/call  convert-instruction/disp32
    5500     # . . discard args
    5501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5502     # check output
    5503     # . flush(_test-output-buffered-file)
    5504     # . . push args
    5505     68/push  _test-output-buffered-file/imm32
    5506     # . . call
    5507     e8/call  flush/disp32
    5508     # . . discard args
    5509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5510 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5536     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
    5537     # . . push args
    5538     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
    5539     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
    5540     68/push  _test-output-stream/imm32
    5541     # . . call
    5542     e8/call  check-stream-equal/disp32
    5543     # . . discard args
    5544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5545     # . epilog
    5546     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5547     5d/pop-to-EBP
    5548     c3/return
    5549
    5550 test-convert-instruction-handles-imm32-operand:
    5551     # expand /imm32 operand into 4 bytes
    5552     # . prolog
    5553     55/push-EBP
    5554     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5555     # setup
    5556     # . clear-stream(_test-input-stream)
    5557     # . . push args
    5558     68/push  _test-input-stream/imm32
    5559     # . . call
    5560     e8/call  clear-stream/disp32
    5561     # . . discard args
    5562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5563     # . clear-stream(_test-output-stream)
    5564     # . . push args
    5565     68/push  _test-output-stream/imm32
    5566     # . . call
    5567     e8/call  clear-stream/disp32
    5568     # . . discard args
    5569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5570     # . clear-stream(_test-output-buffered-file+4)
    5571     # . . push args
    5572     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5573     05/add-to-EAX  4/imm32
    5574     50/push-EAX
    5575     # . . call
    5576     e8/call  clear-stream/disp32
    5577     # . . discard args
    5578     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5579     # initialize input
    5580     # . write(_test-input-stream, "68/push 0x20/imm32")
    5581     # . . push args
    5582     68/push  "68/push 0x20/imm32"/imm32
    5583     68/push  _test-input-stream/imm32
    5584     # . . call
    5585     e8/call  write/disp32
    5586     # . . discard args
    5587     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5588     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5589     # . . push args
    5590     68/push  _test-output-buffered-file/imm32
    5591     68/push  _test-input-stream/imm32
    5592     # . . call
    5593     e8/call  convert-instruction/disp32
    5594     # . . discard args
    5595     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5596     # check output
    5597     # . flush(_test-output-buffered-file)
    5598     # . . push args
    5599     68/push  _test-output-buffered-file/imm32
    5600     # . . call
    5601     e8/call  flush/disp32
    5602     # . . discard args
    5603     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5604 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5630     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
    5631     # . . push args
    5632     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
    5633     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
    5634     68/push  _test-output-stream/imm32
    5635     # . . call
    5636     e8/call  check-stream-equal/disp32
    5637     # . . discard args
    5638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5639     # . epilog
    5640     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5641     5d/pop-to-EBP
    5642     c3/return
    5643
    5644 test-convert-instruction-handles-imm16-operand:
    5645     # expand /imm16 operand into 2 bytes
    5646     # we don't have one of these at the moment, so this expands to an invalid instruction
    5647     # . prolog
    5648     55/push-EBP
    5649     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5650     # setup
    5651     # . clear-stream(_test-input-stream)
    5652     # . . push args
    5653     68/push  _test-input-stream/imm32
    5654     # . . call
    5655     e8/call  clear-stream/disp32
    5656     # . . discard args
    5657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5658     # . clear-stream(_test-output-stream)
    5659     # . . push args
    5660     68/push  _test-output-stream/imm32
    5661     # . . call
    5662     e8/call  clear-stream/disp32
    5663     # . . discard args
    5664     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5665     # . clear-stream(_test-output-buffered-file+4)
    5666     # . . push args
    5667     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5668     05/add-to-EAX  4/imm32
    5669     50/push-EAX
    5670     # . . call
    5671     e8/call  clear-stream/disp32
    5672     # . . discard args
    5673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5674     # initialize input
    5675     # . write(_test-input-stream, "68/push 0x20/imm16")
    5676     # . . push args
    5677     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
    5678     68/push  _test-input-stream/imm32
    5679     # . . call
    5680     e8/call  write/disp32
    5681     # . . discard args
    5682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5683     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5684     # . . push args
    5685     68/push  _test-output-buffered-file/imm32
    5686     68/push  _test-input-stream/imm32
    5687     # . . call
    5688     e8/call  convert-instruction/disp32
    5689     # . . discard args
    5690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5691     # check output
    5692     # . flush(_test-output-buffered-file)
    5693     # . . push args
    5694     68/push  _test-output-buffered-file/imm32
    5695     # . . call
    5696     e8/call  flush/disp32
    5697     # . . discard args
    5698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5699 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5725     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
    5726     # . . push args
    5727     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
    5728     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
    5729     68/push  _test-output-stream/imm32
    5730     # . . call
    5731     e8/call  check-stream-equal/disp32
    5732     # . . discard args
    5733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5734     # . epilog
    5735     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5736     5d/pop-to-EBP
    5737     c3/return
    5738
    5739 test-convert-instruction-handles-imm8-operand:
    5740     # expand /imm8 operand into 1 byte
    5741     # we don't have one of these at the moment, so this expands to an invalid instruction
    5742     # . prolog
    5743     55/push-EBP
    5744     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5745     # setup
    5746     # . clear-stream(_test-input-stream)
    5747     # . . push args
    5748     68/push  _test-input-stream/imm32
    5749     # . . call
    5750     e8/call  clear-stream/disp32
    5751     # . . discard args
    5752     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5753     # . clear-stream(_test-output-stream)
    5754     # . . push args
    5755     68/push  _test-output-stream/imm32
    5756     # . . call
    5757     e8/call  clear-stream/disp32
    5758     # . . discard args
    5759     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5760     # . clear-stream(_test-output-buffered-file+4)
    5761     # . . push args
    5762     b8/copy-to-EAX  _test-output-buffered-file/imm32
    5763     05/add-to-EAX  4/imm32
    5764     50/push-EAX
    5765     # . . call
    5766     e8/call  clear-stream/disp32
    5767     # . . discard args
    5768     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5769     # initialize input
    5770     # . write(_test-input-stream, "68/push 0x20/imm8")
    5771     # . . push args
    5772     68/push  "68/push 0x20/imm8"/imm32
    5773     68/push  _test-input-stream/imm32
    5774     # . . call
    5775     e8/call  write/disp32
    5776     # . . discard args
    5777     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5778     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5779     # . . push args
    5780     68/push  _test-output-buffered-file/imm32
    5781     68/push  _test-input-stream/imm32
    5782     # . . call
    5783     e8/call  convert-instruction/disp32
    5784     # . . discard args
    5785     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5786     # check output
    5787     # . flush(_test-output-buffered-file)
    5788     # . . push args
    5789     68/push  _test-output-buffered-file/imm32
    5790     # . . call
    5791     e8/call  flush/disp32
    5792     # . . discard args
    5793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5794 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    5820     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
    5821     # . . push args
    5822     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
    5823     68/push  "68 20  # 68/push 0x20/imm8"/imm32
    5824     68/push  _test-output-stream/imm32
    5825     # . . call
    5826     e8/call  check-stream-equal/disp32
    5827     # . . discard args
    5828     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5829     # . epilog
    5830     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5831     5d/pop-to-EBP
    5832     c3/return
    5833
    5834 # (re)compute the bounds of the next word in the line
    5835 # return empty string on reaching end of file
    5836 next-word:  # line : (address stream byte), out : (address slice)
    5837     # . prolog
    5838     55/push-EBP
    5839     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5840     # . save registers
    5841     50/push-EAX
    5842     51/push-ECX
    5843     56/push-ESI
    5844     57/push-EDI
    5845     # ESI = line
    5846     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    5847     # EDI = out
    5848     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
    5849     # skip-chars-matching(line, ' ')
    5850     # . . push args
    5851     68/push  0x20/imm32/space
    5852     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    5853     # . . call
    5854     e8/call  skip-chars-matching/disp32
    5855     # . . discard args
    5856     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5857 $next-word:check0:
    5858     # if (line->read >= line->write) clear out and return
    5859     # . EAX = line->read
    5860     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
    5861     # . if (EAX < line->write) goto next check
    5862     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
    5863     7c/jump-if-lesser  $next-word:check-for-comment/disp8
    5864     # . return out = {0, 0}
    5865     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
    5866     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
    5867     eb/jump  $next-word:end/disp8
    5868 $next-word:check-for-comment:
    5869     # out->start = &line->data[line->read]
    5870     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
    5871     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
    5872     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
    5873     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
    5874     # . EAX = line->data[line->read]
    5875     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    5876     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
    5877     # . compare
    5878     3d/compare-EAX-and  0x23/imm32/pound
    5879     75/jump-if-not-equal  $next-word:regular-word/disp8
    5880 $next-word:comment:
    5881     # . out->end = &line->data[line->write]
    5882     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
    5883     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  0/index/EAX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+EAX+12 to EAX
    5884     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
    5885     # . line->read = line->write
    5886     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
    5887     # . return
    5888     eb/jump  $next-word:end/disp8
    5889 $next-word:regular-word:
    5890     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
    5891     # . . push args
    5892     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    5893     # . . call
    5894     e8/call  skip-chars-not-matching-whitespace/disp32
    5895     # . . discard args
    5896     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5897     # out->end = &line->data[line->read]
    5898     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
    5899     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
    5900     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
    5901 $next-word:end:
    5902     # . restore registers
    5903     5f/pop-to-EDI
    5904     5e/pop-to-ESI
    5905     59/pop-to-ECX
    5906     58/pop-to-EAX
    5907     # . epilog
    5908     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5909     5d/pop-to-EBP
    5910     c3/return
    5911
    5912 test-next-word:
    5913     # . prolog
    5914     55/push-EBP
    5915     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5916     # setup
    5917     # . clear-stream(_test-stream)
    5918     # . . push args
    5919     68/push  _test-stream/imm32
    5920     # . . call
    5921     e8/call  clear-stream/disp32
    5922     # . . discard args
    5923     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5924     # var slice/ECX = {0, 0}
    5925     68/push  0/imm32/end
    5926     68/push  0/imm32/start
    5927     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    5928     # write(_test-stream, "  ab")
    5929     # . . push args
    5930     68/push  "  ab"/imm32
    5931     68/push  _test-stream/imm32
    5932     # . . call
    5933     e8/call  write/disp32
    5934     # . . discard args
    5935     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5936     # next-word(_test-stream, slice)
    5937     # . . push args
    5938     51/push-ECX
    5939     68/push  _test-stream/imm32
    5940     # . . call
    5941     e8/call  next-word/disp32
    5942     # . . discard args
    5943     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5944     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
    5945     # . check-ints-equal(slice->start - _test-stream, 14, msg)
    5946     # . . push args
    5947     68/push  "F - test-next-word: start"/imm32
    5948     68/push  0xe/imm32
    5949     # . . push slice->start - _test-stream
    5950     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    5951     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    5952     50/push-EAX
    5953     # . . call
    5954     e8/call  check-ints-equal/disp32
    5955     # . . discard args
    5956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5957     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
    5958     # . check-ints-equal(slice->end - _test-stream, 16, msg)
    5959     # . . push args
    5960     68/push  "F - test-next-word: end"/imm32
    5961     68/push  0x10/imm32
    5962     # . . push slice->end - _test-stream
    5963     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    5964     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    5965     50/push-EAX
    5966     # . . call
    5967     e8/call  check-ints-equal/disp32
    5968     # . . discard args
    5969     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    5970     # . epilog
    5971     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    5972     5d/pop-to-EBP
    5973     c3/return
    5974
    5975 test-next-word-returns-whole-comment:
    5976     # . prolog
    5977     55/push-EBP
    5978     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    5979     # setup
    5980     # . clear-stream(_test-stream)
    5981     # . . push args
    5982     68/push  _test-stream/imm32
    5983     # . . call
    5984     e8/call  clear-stream/disp32
    5985     # . . discard args
    5986     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    5987     # var slice/ECX = {0, 0}
    5988     68/push  0/imm32/end
    5989     68/push  0/imm32/start
    5990     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    5991     # write(_test-stream, "  # a")
    5992     # . . push args
    5993     68/push  "  # a"/imm32
    5994     68/push  _test-stream/imm32
    5995     # . . call
    5996     e8/call  write/disp32
    5997     # . . discard args
    5998     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    5999     # next-word(_test-stream, slice)
    6000     # . . push args
    6001     51/push-ECX
    6002     68/push  _test-stream/imm32
    6003     # . . call
    6004     e8/call  next-word/disp32
    6005     # . . discard args
    6006     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6007     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
    6008     # . check-ints-equal(slice->start - _test-stream, 14, msg)
    6009     # . . push args
    6010     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
    6011     68/push  0xe/imm32
    6012     # . . push slice->start - _test-stream
    6013     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
    6014     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    6015     50/push-EAX
    6016     # . . call
    6017     e8/call  check-ints-equal/disp32
    6018     # . . discard args
    6019     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6020     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
    6021     # . check-ints-equal(slice->end - _test-stream, 17, msg)
    6022     # . . push args
    6023     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
    6024     68/push  0x11/imm32
    6025     # . . push slice->end - _test-stream
    6026     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    6027     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
    6028     50/push-EAX
    6029     # . . call
    6030     e8/call  check-ints-equal/disp32
    6031     # . . discard args
    6032     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6033     # . epilog
    6034     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6035     5d/pop-to-EBP
    6036     c3/return
    6037
    6038 test-next-word-returns-empty-string-on-eof:
    6039     # . prolog
    6040     55/push-EBP
    6041     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6042     # setup
    6043     # . clear-stream(_test-stream)
    6044     # . . push args
    6045     68/push  _test-stream/imm32
    6046     # . . call
    6047     e8/call  clear-stream/disp32
    6048     # . . discard args
    6049     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6050     # var slice/ECX = {0, 0}
    6051     68/push  0/imm32/end
    6052     68/push  0/imm32/start
    6053     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6054     # write nothing to _test-stream
    6055     # next-word(_test-stream, slice)
    6056     # . . push args
    6057     51/push-ECX
    6058     68/push  _test-stream/imm32
    6059     # . . call
    6060     e8/call  next-word/disp32
    6061     # . . discard args
    6062     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6063     # check-ints-equal(slice->end - slice->start, 0, msg)
    6064     # . . push args
    6065     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
    6066     68/push  0/imm32
    6067     # . . push slice->end - slice->start
    6068     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
    6069     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
    6070     50/push-EAX
    6071     # . . call
    6072     e8/call  check-ints-equal/disp32
    6073     # . . discard args
    6074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6075     # . epilog
    6076     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6077     5d/pop-to-EBP
    6078     c3/return
    6079
    6080 has-metadata?:  # word : (address slice), s : (address string) -> EAX : boolean
    6081     # pseudocode:
    6082     #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
    6083     #   curr = twig->end
    6084     #   while true
    6085     #     twig = next-token-from-slice(curr, word->end, '/')
    6086     #     if (twig.empty()) break
    6087     #     if (slice-equal?(twig, s)) return true
    6088     #     curr = twig->end
    6089     #   return false
    6090     # . prolog
    6091     55/push-EBP
    6092     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6093     # . save registers
    6094     51/push-ECX
    6095     52/push-EDX
    6096     56/push-ESI
    6097     57/push-EDI
    6098     # ESI = word
    6099     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    6100     # EDX = word->end
    6101     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
    6102     # var twig/EDI : (address slice) = {0, 0}
    6103     68/push  0/imm32/end
    6104     68/push  0/imm32/start
    6105     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
    6106     # next-token-from-slice(word->start, word->end, '/', twig)
    6107     # . . push args
    6108     57/push-EDI
    6109     68/push  0x2f/imm32/slash
    6110     52/push-EDX
    6111     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
    6112     # . . call
    6113     e8/call  next-token-from-slice/disp32
    6114     # . . discard args
    6115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    6116     # curr/ECX = twig->end
    6117     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
    6118 $has-metadata?:loop:
    6119     # next-token-from-slice(curr, word->end, '/', twig)
    6120     # . . push args
    6121     57/push-EDI
    6122     68/push  0x2f/imm32/slash
    6123     52/push-EDX
    6124     51/push-ECX
    6125     # . . call
    6126     e8/call  next-token-from-slice/disp32
    6127     # . . discard args
    6128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    6129     # if (slice-empty?(twig)) return false
    6130     # . EAX = slice-empty?(twig)
    6131     # . . push args
    6132     57/push-EDI
    6133     # . . call
    6134     e8/call  slice-empty?/disp32
    6135     # . . discard args
    6136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6137     # . if (EAX != 0) return false
    6138     3d/compare-EAX-and  0/imm32
    6139     75/jump-if-not-equal  $has-metadata?:false/disp8
    6140     # if (slice-equal?(twig, s)) return true
    6141     # . EAX = slice-equal?(twig, s)
    6142     # . . push args
    6143     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
    6144     57/push-EDI
    6145     # . . call
    6146     e8/call  slice-equal?/disp32
    6147     # . . discard args
    6148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6149     # . if (EAX != 0) return true
    6150     3d/compare-EAX-and  0/imm32
    6151     75/jump-if-not-equal  $has-metadata?:true/disp8
    6152     # curr = twig->end
    6153     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
    6154     eb/jump  $has-metadata?:loop/disp8
    6155 $has-metadata?:true:
    6156     b8/copy-to-EAX  1/imm32/true
    6157     eb/jump  $has-metadata?:end/disp8
    6158 $has-metadata?:false:
    6159     b8/copy-to-EAX  0/imm32/false
    6160 $has-metadata?:end:
    6161     # . reclaim locals
    6162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6163     # . restore registers
    6164     5f/pop-to-EDI
    6165     5e/pop-to-ESI
    6166     5a/pop-to-EDX
    6167     59/pop-to-ECX
    6168     # . epilog
    6169     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6170     5d/pop-to-EBP
    6171     c3/return
    6172
    6173 test-has-metadata-true:
    6174     # . prolog
    6175     55/push-EBP
    6176     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6177     # (EAX..ECX) = "ab/c"
    6178     b8/copy-to-EAX  "ab/c"/imm32
    6179     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    6180     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
    6181     05/add-to-EAX  4/imm32
    6182     # var in/ESI : (address slice) = {EAX, ECX}
    6183     51/push-ECX
    6184     50/push-EAX
    6185     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    6186     # EAX = has-metadata?(ESI, "c")
    6187     # . . push args
    6188     68/push  "c"/imm32
    6189     56/push-ESI
    6190     # . . call
    6191     e8/call  has-metadata?/disp32
    6192     # . . discard args
    6193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6194     # check-ints-equal(EAX, 1, msg)
    6195     # . . push args
    6196     68/push  "F - test-has-metadata-true"/imm32
    6197     68/push  1/imm32/true
    6198     50/push-EAX
    6199     # . . call
    6200     e8/call  check-ints-equal/disp32
    6201     # . . discard args
    6202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6203     # . epilog
    6204     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6205     5d/pop-to-EBP
    6206     c3/return
    6207
    6208 test-has-metadata-false:
    6209     # . prolog
    6210     55/push-EBP
    6211     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6212     # (EAX..ECX) = "ab/c"
    6213     b8/copy-to-EAX  "ab/c"/imm32
    6214     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    6215     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
    6216     05/add-to-EAX  4/imm32
    6217     # var in/ESI : (address slice) = {EAX, ECX}
    6218     51/push-ECX
    6219     50/push-EAX
    6220     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    6221     # EAX = has-metadata?(ESI, "c")
    6222     # . . push args
    6223     68/push  "d"/imm32
    6224     56/push-ESI
    6225     # . . call
    6226     e8/call  has-metadata?/disp32
    6227     # . . discard args
    6228     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6229     # check-ints-equal(EAX, 0, msg)
    6230     # . . push args
    6231     68/push  "F - test-has-metadata-false"/imm32
    6232     68/push  0/imm32/false
    6233     50/push-EAX
    6234     # . . call
    6235     e8/call  check-ints-equal/disp32
    6236     # . . discard args
    6237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6238     # . epilog
    6239     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6240     5d/pop-to-EBP
    6241     c3/return
    6242
    6243 test-has-metadata-ignore-name:
    6244     # . prolog
    6245     55/push-EBP
    6246     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6247     # (EAX..ECX) = "a/b"
    6248     b8/copy-to-EAX  "a/b"/imm32
    6249     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    6250     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
    6251     05/add-to-EAX  4/imm32
    6252     # var in/ESI : (address slice) = {EAX, ECX}
    6253     51/push-ECX
    6254     50/push-EAX
    6255     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    6256     # EAX = has-metadata?(ESI, "a")
    6257     # . . push args
    6258     68/push  "a"/imm32
    6259     56/push-ESI
    6260     # . . call
    6261     e8/call  has-metadata?/disp32
    6262     # . . discard args
    6263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6264     # check-ints-equal(EAX, 0, msg)
    6265     # . . push args
    6266     68/push  "F - test-has-metadata-ignore-name"/imm32
    6267     68/push  0/imm32/false
    6268     50/push-EAX
    6269     # . . call
    6270     e8/call  check-ints-equal/disp32
    6271     # . . discard args
    6272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6273     # . epilog
    6274     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6275     5d/pop-to-EBP
    6276     c3/return
    6277
    6278 test-has-metadata-multiple-true:
    6279     # . prolog
    6280     55/push-EBP
    6281     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6282     # (EAX..ECX) = "a/b/c"
    6283     b8/copy-to-EAX  "a/b/c"/imm32
    6284     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    6285     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
    6286     05/add-to-EAX  4/imm32
    6287     # var in/ESI : (address slice) = {EAX, ECX}
    6288     51/push-ECX
    6289     50/push-EAX
    6290     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    6291     # EAX = has-metadata?(ESI, "c")
    6292     # . . push args
    6293     68/push  "c"/imm32
    6294     56/push-ESI
    6295     # . . call
    6296     e8/call  has-metadata?/disp32
    6297     # . . discard args
    6298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6299     # check-ints-equal(EAX, 1, msg)
    6300     # . . push args
    6301     68/push  "F - test-has-metadata-multiple-true"/imm32
    6302     68/push  1/imm32/true
    6303     50/push-EAX
    6304     # . . call
    6305     e8/call  check-ints-equal/disp32
    6306     # . . discard args
    6307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6308     # . epilog
    6309     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6310     5d/pop-to-EBP
    6311     c3/return
    6312
    6313 test-has-metadata-multiple-false:
    6314     # . prolog
    6315     55/push-EBP
    6316     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6317     # (EAX..ECX) = "a/b/c"
    6318     b8/copy-to-EAX  "a/b/c"/imm32
    6319     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
    6320     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
    6321     05/add-to-EAX  4/imm32
    6322     # var in/ESI : (address slice) = {EAX, ECX}
    6323     51/push-ECX
    6324     50/push-EAX
    6325     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
    6326     # EAX = has-metadata?(ESI, "d")
    6327     # . . push args
    6328     68/push  "d"/imm32
    6329     56/push-ESI
    6330     # . . call
    6331     e8/call  has-metadata?/disp32
    6332     # . . discard args
    6333     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6334     # check-ints-equal(EAX, 0, msg)
    6335     # . . push args
    6336     68/push  "F - test-has-metadata-multiple-false"/imm32
    6337     68/push  0/imm32/false
    6338     50/push-EAX
    6339     # . . call
    6340     e8/call  check-ints-equal/disp32
    6341     # . . discard args
    6342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6343     # . epilog
    6344     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6345     5d/pop-to-EBP
    6346     c3/return
    6347
    6348 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
    6349 # it in 'width' bytes of hex, least significant first.
    6350 # Otherwise just print the entire word including metadata.
    6351 # Always print a trailing space.
    6352 emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
    6353     # . prolog
    6354     55/push-EBP
    6355     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6356     # . save registers
    6357     50/push-EAX
    6358     56/push-ESI
    6359     57/push-EDI
    6360     # ESI = word
    6361     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
    6362     # var name/EDI : (address slice) = {0, 0}
    6363     68/push  0/imm32/end
    6364     68/push  0/imm32/start
    6365     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
    6366     # datum = next-token-from-slice(word->start, word->end, '/')
    6367     # . . push args
    6368     57/push-EDI
    6369     68/push  0x2f/imm32/slash
    6370     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
    6371     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
    6372     # . . call
    6373     e8/call  next-token-from-slice/disp32
    6374     # . . discard args
    6375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    6376     # if (is-valid-name?(datum)) write-slice(out, word) and return
    6377     # . EAX = is-valid-name?(name)
    6378     # . . push args
    6379     57/push-EDI
    6380     # . . call
    6381     e8/call  is-valid-name?/disp32
    6382     # . . discard args
    6383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6384     # . if (EAX != 0)
    6385     3d/compare-EAX-and  0/imm32
    6386     74/jump-if-equal  $emit:hex-int/disp8
    6387 $emit:name:
    6388     # . write-slice(out, word)
    6389     # . . push args
    6390     56/push-ESI
    6391     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    6392     # . . call
    6393     e8/call  write-slice/disp32
    6394     # . . discard args
    6395     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6396     # . write-buffered(out, " ")
    6397     # . . push args
    6398     68/push  " "/imm32
    6399     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    6400     # . . call
    6401     e8/call  write-buffered/disp32
    6402     # . . discard args
    6403     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6404     # . return
    6405     eb/jump  $emit:end/disp8
    6406     # otherwise emit-hex(out, parse-hex-int(datum), width)
    6407     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
    6408     #   name or a hex number, but we're only going to be passing in real legal
    6409     #   programs. We just want to make sure that valid names aren't treated as
    6410     #   (valid) hex numbers.)
    6411 $emit:hex-int:
    6412     # . value/EAX = parse-hex-int(datum)
    6413     # . . push args
    6414     57/push-EDI
    6415     # . . call
    6416     e8/call  parse-hex-int/disp32
    6417     # . . discard args
    6418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6419     # . emit-hex(out, value, width)
    6420     # . . push args
    6421     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
    6422     50/push-EAX
    6423     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
    6424     # . . call
    6425     e8/call  emit-hex/disp32
    6426     # . . discard args
    6427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6428 $emit:end:
    6429     # . reclaim locals
    6430     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    6431     # . restore registers
    6432     5f/pop-to-EDI
    6433     5e/pop-to-ESI
    6434     58/pop-to-EAX
    6435     # . epilog
    6436     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6437     5d/pop-to-EBP
    6438     c3/return
    6439
    6440 test-emit-number:
    6441     # . prolog
    6442     55/push-EBP
    6443     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6444     # setup
    6445     # . clear-stream(_test-output-stream)
    6446     # . . push args
    6447     68/push  _test-output-stream/imm32
    6448     # . . call
    6449     e8/call  clear-stream/disp32
    6450     # . . discard args
    6451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6452     # . clear-stream(_test-output-buffered-file+4)
    6453     # . . push args
    6454     b8/copy-to-EAX  _test-output-buffered-file/imm32
    6455     05/add-to-EAX  4/imm32
    6456     50/push-EAX
    6457     # . . call
    6458     e8/call  clear-stream/disp32
    6459     # . . discard args
    6460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6461     # var slice/ECX = "30"
    6462     68/push  _test-slice-three-zero-end/imm32/end
    6463     68/push  _test-slice-three-zero/imm32/start
    6464     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6465     # emit(_test-output-buffered-file, slice, 1)
    6466     # . . push args
    6467     68/push  1/imm32
    6468     51/push-ECX
    6469     68/push  _test-output-buffered-file/imm32
    6470     # . . call
    6471     e8/call  emit/disp32
    6472     # . . discard args
    6473     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6474     # flush(_test-output-buffered-file)
    6475     # . . push args
    6476     68/push  _test-output-buffered-file/imm32
    6477     # . . call
    6478     e8/call  flush/disp32
    6479     # . . discard args
    6480     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6481     # check-stream-equal(_test-output-stream, "30 ", msg)
    6482     # . . push args
    6483     68/push  "F - test-emit-number/1"/imm32
    6484     68/push  "30 "/imm32
    6485     68/push  _test-output-stream/imm32
    6486     # . . call
    6487     e8/call  check-stream-equal/disp32
    6488     # . . discard args
    6489     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6490     # . epilog
    6491     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6492     5d/pop-to-EBP
    6493     c3/return
    6494
    6495 test-emit-negative-number:
    6496     # test support for sign-extending negative numbers
    6497     # . prolog
    6498     55/push-EBP
    6499     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6500     # setup
    6501     # . clear-stream(_test-output-stream)
    6502     # . . push args
    6503     68/push  _test-output-stream/imm32
    6504     # . . call
    6505     e8/call  clear-stream/disp32
    6506     # . . discard args
    6507     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6508     # . clear-stream(_test-output-buffered-file+4)
    6509     # . . push args
    6510     b8/copy-to-EAX  _test-output-buffered-file/imm32
    6511     05/add-to-EAX  4/imm32
    6512     50/push-EAX
    6513     # . . call
    6514     e8/call  clear-stream/disp32
    6515     # . . discard args
    6516     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6517     # var slice/ECX = "-2"
    6518     68/push  _test-slice-negative-two-end/imm32/end
    6519     68/push  _test-slice-negative-two/imm32/start
    6520     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6521     # emit(_test-output-buffered-file, slice, 2)
    6522     # . . push args
    6523     68/push  2/imm32
    6524     51/push-ECX
    6525     68/push  _test-output-buffered-file/imm32
    6526     # . . call
    6527     e8/call  emit/disp32
    6528     # . . discard args
    6529     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6530     # flush(_test-output-buffered-file)
    6531     # . . push args
    6532     68/push  _test-output-buffered-file/imm32
    6533     # . . call
    6534     e8/call  flush/disp32
    6535     # . . discard args
    6536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6537     # check-stream-equal(_test-output-stream, "fe ff ", msg)
    6538     # . . push args
    6539     68/push  "F - test-emit-number/1"/imm32
    6540     68/push  "fe ff "/imm32
    6541     68/push  _test-output-stream/imm32
    6542     # . . call
    6543     e8/call  check-stream-equal/disp32
    6544     # . . discard args
    6545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6546     # . epilog
    6547     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6548     5d/pop-to-EBP
    6549     c3/return
    6550
    6551 test-emit-number-with-metadata:
    6552     # . prolog
    6553     55/push-EBP
    6554     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6555     # setup
    6556     # . clear-stream(_test-output-stream)
    6557     # . . push args
    6558     68/push  _test-output-stream/imm32
    6559     # . . call
    6560     e8/call  clear-stream/disp32
    6561     # . . discard args
    6562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6563     # . clear-stream(_test-output-buffered-file+4)
    6564     # . . push args
    6565     b8/copy-to-EAX  _test-output-buffered-file/imm32
    6566     05/add-to-EAX  4/imm32
    6567     50/push-EAX
    6568     # . . call
    6569     e8/call  clear-stream/disp32
    6570     # . . discard args
    6571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6572     # var slice/ECX = "-2/foo"
    6573     68/push  _test-slice-negative-two-metadata-end/imm32/end
    6574     68/push  _test-slice-negative-two/imm32/start
    6575     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6576     # emit(_test-output-buffered-file, slice, 2)
    6577     # . . push args
    6578     68/push  2/imm32
    6579     51/push-ECX
    6580     68/push  _test-output-buffered-file/imm32
    6581     # . . call
    6582     e8/call  emit/disp32
    6583     # . . discard args
    6584     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6585     # flush(_test-output-buffered-file)
    6586     # . . push args
    6587     68/push  _test-output-buffered-file/imm32
    6588     # . . call
    6589     e8/call  flush/disp32
    6590     # . . discard args
    6591     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6592     # the '/foo' will have no impact on the output
    6593     # check-stream-equal(_test-output-stream, "fe ff ", msg)
    6594     # . . push args
    6595     68/push  "F - test-emit-number-with-metadata"/imm32
    6596     68/push  "fe ff "/imm32
    6597     68/push  _test-output-stream/imm32
    6598     # . . call
    6599     e8/call  check-stream-equal/disp32
    6600     # . . discard args
    6601     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6602     # . epilog
    6603     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6604     5d/pop-to-EBP
    6605     c3/return
    6606
    6607 test-emit-non-number:
    6608     # . prolog
    6609     55/push-EBP
    6610     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6611     # setup
    6612     # . clear-stream(_test-output-stream)
    6613     # . . push args
    6614     68/push  _test-output-stream/imm32
    6615     # . . call
    6616     e8/call  clear-stream/disp32
    6617     # . . discard args
    6618     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6619     # . clear-stream(_test-output-buffered-file+4)
    6620     # . . push args
    6621     b8/copy-to-EAX  _test-output-buffered-file/imm32
    6622     05/add-to-EAX  4/imm32
    6623     50/push-EAX
    6624     # . . call
    6625     e8/call  clear-stream/disp32
    6626     # . . discard args
    6627     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6628     # var slice/ECX = "xyz"
    6629     68/push  _test-slice-non-number-word-end/imm32/end
    6630     68/push  _test-slice-non-number-word/imm32/start
    6631     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6632     # emit(_test-output-buffered-file, slice, 2)
    6633     # . . push args
    6634     68/push  2/imm32
    6635     51/push-ECX
    6636     68/push  _test-output-buffered-file/imm32
    6637     # . . call
    6638     e8/call  emit/disp32
    6639     # . . discard args
    6640     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6641     # flush(_test-output-buffered-file)
    6642     # . . push args
    6643     68/push  _test-output-buffered-file/imm32
    6644     # . . call
    6645     e8/call  flush/disp32
    6646     # . . discard args
    6647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6648     # check-stream-equal(_test-output-stream, "xyz", msg)
    6649     # . . push args
    6650     68/push  "F - test-emit-non-number"/imm32
    6651     68/push  "xyz "/imm32
    6652     68/push  _test-output-stream/imm32
    6653     # . . call
    6654     e8/call  check-stream-equal/disp32
    6655     # . . discard args
    6656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6657     # . epilog
    6658     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6659     5d/pop-to-EBP
    6660     c3/return
    6661
    6662 test-emit-non-number-with-metadata:
    6663     # . prolog
    6664     55/push-EBP
    6665     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6666     # setup
    6667     # . clear-stream(_test-output-stream)
    6668     # . . push args
    6669     68/push  _test-output-stream/imm32
    6670     # . . call
    6671     e8/call  clear-stream/disp32
    6672     # . . discard args
    6673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6674     # . clear-stream(_test-output-buffered-file+4)
    6675     # . . push args
    6676     b8/copy-to-EAX  _test-output-buffered-file/imm32
    6677     05/add-to-EAX  4/imm32
    6678     50/push-EAX
    6679     # . . call
    6680     e8/call  clear-stream/disp32
    6681     # . . discard args
    6682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6683     # var slice/ECX = "xyz/"
    6684     68/push  _test-slice-non-number-word-metadata-end/imm32/end
    6685     68/push  _test-slice-non-number-word/imm32/start
    6686     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6687     # emit(_test-output-buffered-file, slice, 2)
    6688     # . . push args
    6689     68/push  2/imm32
    6690     51/push-ECX
    6691     68/push  _test-output-buffered-file/imm32
    6692     # . . call
    6693     e8/call  emit/disp32
    6694     # . . discard args
    6695     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6696     # flush(_test-output-buffered-file)
    6697     # . . push args
    6698     68/push  _test-output-buffered-file/imm32
    6699     # . . call
    6700     e8/call  flush/disp32
    6701     # . . discard args
    6702     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6703     # check-stream-equal(_test-output-stream, "xyz/", msg)
    6704     # . . push args
    6705     68/push  "F - test-emit-non-number-with-metadata"/imm32
    6706     68/push  "xyz/ "/imm32
    6707     68/push  _test-output-stream/imm32
    6708     # . . call
    6709     e8/call  check-stream-equal/disp32
    6710     # . . discard args
    6711     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6712     # . epilog
    6713     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6714     5d/pop-to-EBP
    6715     c3/return
    6716
    6717 test-emit-non-number-with-all-hex-digits-and-metadata:
    6718     # . prolog
    6719     55/push-EBP
    6720     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6721     # setup
    6722     # . clear-stream(_test-output-stream)
    6723     # . . push args
    6724     68/push  _test-output-stream/imm32
    6725     # . . call
    6726     e8/call  clear-stream/disp32
    6727     # . . discard args
    6728     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6729     # . clear-stream(_test-output-buffered-file+4)
    6730     # . . push args
    6731     b8/copy-to-EAX  _test-output-buffered-file/imm32
    6732     05/add-to-EAX  4/imm32
    6733     50/push-EAX
    6734     # . . call
    6735     e8/call  clear-stream/disp32
    6736     # . . discard args
    6737     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6738     # var slice/ECX = "abcd/xyz"
    6739     68/push  _test-slice-hexlike-non-number-word-metadata-end/imm32/end
    6740     68/push  _test-slice-hexlike-non-number-word/imm32/start
    6741     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6742     # emit(_test-output-buffered-file, slice, 2)
    6743     # . . push args
    6744     68/push  2/imm32
    6745     51/push-ECX
    6746     68/push  _test-output-buffered-file/imm32
    6747     # . . call
    6748     e8/call  emit/disp32
    6749     # . . discard args
    6750     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6751     # flush(_test-output-buffered-file)
    6752     # . . push args
    6753     68/push  _test-output-buffered-file/imm32
    6754     # . . call
    6755     e8/call  flush/disp32
    6756     # . . discard args
    6757     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6758 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
    6784     # check-stream-equal(_test-output-stream, "abcd/xyz")
    6785     # . . push args
    6786     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
    6787     68/push  "abcd/xyz "/imm32
    6788     68/push  _test-output-stream/imm32
    6789     # . . call
    6790     e8/call  check-stream-equal/disp32
    6791     # . . discard args
    6792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6793     # . epilog
    6794     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6795     5d/pop-to-EBP
    6796     c3/return
    6797
    6798 # conditions for 'valid' names that are not at risk of looking like hex numbers
    6799 # keep in sync with the rules in labels.cc
    6800 #: - if it starts with a digit, it's treated as a number. If it can't be
    6801 #:   parsed as hex it will raise an error.
    6802 #: - if it starts with '-' it's treated as a number.
    6803 #: - if it starts with '0x' it's treated as a number. (redundant)
    6804 #: - if it's two characters long, it can't be a name. Either it's a hex
    6805 #:   byte, or it raises an error.
    6806 is-valid-name?:  # in : (address slice) -> EAX : boolean
    6807     # . prolog
    6808     55/push-EBP
    6809     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6810     # . save registers
    6811     51/push-ECX
    6812     56/push-ESI
    6813     # ESI = in
    6814     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    6815     # start/ECX = in->start
    6816     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
    6817     # end/EAX = in->end
    6818     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
    6819 $is-valid-name?:check0:
    6820     # if (start >= end) return false
    6821     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # compare ECX with EAX
    6822     7d/jump-if-greater-or-equal  $is-valid-name?:false/disp8
    6823 $is-valid-name?:check1:
    6824     # EAX -= ECX
    6825     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
    6826     # if (EAX == 2) return false
    6827     3d/compare-EAX-and  2/imm32
    6828     74/jump-if-equal  $is-valid-name?:false/disp8
    6829 $is-valid-name?:check2:
    6830     # c/EAX = *ECX
    6831     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
    6832     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
    6833     # if (c == "-") return false
    6834     3d/compare-EAX-and  2d/imm32/-
    6835     74/jump-if-equal  $is-valid-name?:false/disp8
    6836 $is-valid-name?:check3a:
    6837     # if (c < "0") return true
    6838     3d/compare-EAX-with  30/imm32/0
    6839     7c/jump-if-lesser  $is-valid-name?:true/disp8
    6840 $is-valid-name?:check3b:
    6841     # if (c > "9") return true
    6842     3d/compare-EAX-with  39/imm32/9
    6843     7f/jump-if-greater  $is-valid-name?:true/disp8
    6844 $is-valid-name?:false:
    6845     # return false
    6846     b8/copy-to-EAX  0/imm32/false
    6847     eb/jump  $is-valid-name?:end/disp8
    6848 $is-valid-name?:true:
    6849     # return true
    6850     b8/copy-to-EAX  1/imm32/true
    6851 $is-valid-name?:end:
    6852     # . restore registers
    6853     5e/pop-to-ESI
    6854     59/pop-to-ECX
    6855     # . epilog
    6856     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6857     5d/pop-to-EBP
    6858     c3/return
    6859
    6860 test-is-valid-name-digit-prefix:
    6861     # . prolog
    6862     55/push-EBP
    6863     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6864     # var slice/ECX = "34"
    6865     68/push  _test-slice-hex-int-end/imm32
    6866     68/push  _test-slice-hex-int/imm32
    6867     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6868     # EAX = is-valid-name?(slice)
    6869     # . . push args
    6870     51/push-ECX
    6871     # . . call
    6872     e8/call  is-valid-name?/disp32
    6873     # . . discard args
    6874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6875     # check-ints-equal(EAX, 0, msg)
    6876     # . . push args
    6877     68/push  "F - test-is-valid-name-digit-prefix"/imm32
    6878     68/push  0/imm32/false
    6879     50/push-EAX
    6880     # . . call
    6881     e8/call  check-ints-equal/disp32
    6882     # . . discard args
    6883     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6884     # . epilog
    6885     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6886     5d/pop-to-EBP
    6887     c3/return
    6888
    6889 test-is-valid-name-negative-prefix:
    6890     # . prolog
    6891     55/push-EBP
    6892     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6893     # var slice/ECX = "-0x34"
    6894     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
    6895     68/push  _test-slice-hex-int-with-0x-prefix-negative/imm32
    6896     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6897     # EAX = is-valid-name?(slice)
    6898     # . . push args
    6899     51/push-ECX
    6900     # . . call
    6901     e8/call  is-valid-name?/disp32
    6902     # . . discard args
    6903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6904     # check-ints-equal(EAX, 0, msg)
    6905     # . . push args
    6906     68/push  "F - test-is-valid-name-negative-prefix"/imm32
    6907     68/push  0/imm32/false
    6908     50/push-EAX
    6909     # . . call
    6910     e8/call  check-ints-equal/disp32
    6911     # . . discard args
    6912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6913     # . epilog
    6914     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6915     5d/pop-to-EBP
    6916     c3/return
    6917
    6918 test-is-valid-name-0x-prefix:
    6919     # . prolog
    6920     55/push-EBP
    6921     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6922     # var slice/ECX = "0x34"
    6923     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
    6924     68/push  _test-slice-hex-int-with-0x-prefix/imm32
    6925     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6926     # EAX = is-valid-name?(slice)
    6927     # . . push args
    6928     51/push-ECX
    6929     # . . call
    6930     e8/call  is-valid-name?/disp32
    6931     # . . discard args
    6932     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6933     # check-ints-equal(EAX, 0, msg)
    6934     # . . push args
    6935     68/push  "F - test-is-valid-name-0x-prefix"/imm32
    6936     68/push  0/imm32/false
    6937     50/push-EAX
    6938     # . . call
    6939     e8/call  check-ints-equal/disp32
    6940     # . . discard args
    6941     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6942     # . epilog
    6943     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6944     5d/pop-to-EBP
    6945     c3/return
    6946
    6947 test-is-valid-name-starts-with-pre-digit:
    6948     # . prolog
    6949     55/push-EBP
    6950     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6951     # var slice/ECX = "/03"
    6952     68/push  _test-slice-with-slash-prefix-end/imm32
    6953     68/push  _test-slice-with-slash-prefix/imm32
    6954     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6955     # EAX = is-valid-name?(slice)
    6956     # . . push args
    6957     51/push-ECX
    6958     # . . call
    6959     e8/call  is-valid-name?/disp32
    6960     # . . discard args
    6961     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6962     # check-ints-equal(EAX, 1, msg)
    6963     # . . push args
    6964     68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
    6965     68/push  1/imm32/true
    6966     50/push-EAX
    6967     # . . call
    6968     e8/call  check-ints-equal/disp32
    6969     # . . discard args
    6970     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    6971     # . epilog
    6972     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    6973     5d/pop-to-EBP
    6974     c3/return
    6975
    6976 test-is-valid-name-starts-with-post-digit:
    6977     # . prolog
    6978     55/push-EBP
    6979     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    6980     # var slice/ECX = "q34"
    6981     68/push  _test-slice-char-and-digits-end/imm32
    6982     68/push  _test-slice-char-and-digits/imm32
    6983     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    6984     # EAX = is-valid-name?(slice)
    6985     # . . push args
    6986     51/push-ECX
    6987     # . . call
    6988     e8/call  is-valid-name?/disp32
    6989     # . . discard args
    6990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    6991     # check-ints-equal(EAX, 1, msg)
    6992     # . . push args
    6993     68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
    6994     68/push  1/imm32/true
    6995     50/push-EAX
    6996     # . . call
    6997     e8/call  check-ints-equal/disp32
    6998     # . . discard args
    6999     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7000     # . epilog
    7001     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    7002     5d/pop-to-EBP
    7003     c3/return
    7004
    7005 test-is-valid-name-starts-with-digit:
    7006     # . prolog
    7007     55/push-EBP
    7008     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    7009     # var slice/ECX = "0x34"
    7010     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
    7011     68/push  _test-slice-hex-int-with-0x-prefix/imm32
    7012     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    7013     # EAX = is-valid-name?(slice)
    7014     # . . push args
    7015     51/push-ECX
    7016     # . . call
    7017     e8/call  is-valid-name?/disp32
    7018     # . . discard args
    7019     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7020     # check-ints-equal(EAX, 0, msg)
    7021     # . . push args
    7022     68/push  "F - test-is-valid-name-starts-with-digit"/imm32
    7023     68/push  0/imm32/false
    7024     50/push-EAX
    7025     # . . call
    7026     e8/call  check-ints-equal/disp32
    7027     # . . discard args
    7028     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7029     # . epilog
    7030     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    7031     5d/pop-to-EBP
    7032     c3/return
    7033
    7034 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
    7035 emit-hex:  # out : (address buffered-file), n : int, width : int -> <void>
    7036     # . prolog
    7037     55/push-EBP
    7038     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    7039     # . save registers
    7040     50/push-EAX
    7041     51/push-ECX
    7042     52/push-EDX
    7043     53/push-EBX
    7044     57/push-EDI
    7045     # EDI = out
    7046     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
    7047     # EBX = n
    7048     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
    7049     # EDX = width
    7050     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
    7051     # var curr/ECX = 0
    7052     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
    7053 $emit-hex:loop:
    7054     # if (curr >= width) break
    7055     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
    7056     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
    7057     # print-byte(out, EBX)
    7058     # . . push args
    7059     53/push-EBX
    7060     57/push-EDI
    7061     # . . call
    7062     e8/call  print-byte/disp32
    7063     # . . discard args
    7064     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    7065     # write-byte(out, ' ')
    7066     # . . push args
    7067     68/push  0x20/imm32/space
    7068     57/push-EDI
    7069     # . . call
    7070     e8/call  write-byte/disp32
    7071     # . . discard args
    7072     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    7073     # EBX = EBX >> 8
    7074     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
    7075 $emit-hex:continue:
    7076     # ++curr
    7077     41/increment-ECX
    7078     eb/jump  $emit-hex:loop/disp8
    7079 $emit-hex:end:
    7080     # . restore registers
    7081     5f/pop-to-EDI
    7082     5b/pop-to-EBX
    7083     5a/pop-to-EDX
    7084     59/pop-to-ECX
    7085     58/pop-to-EAX
    7086     # . epilog
    7087     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    7088     5d/pop-to-EBP
    7089     c3/return
    7090
    7091 test-emit-hex-single-byte:
    7092     # setup
    7093     # . clear-stream(_test-output-stream)
    7094     # . . push args
    7095     68/push  _test-output-stream/imm32
    7096     # . . call
    7097     e8/call  clear-stream/disp32
    7098     # . . discard args
    7099     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7100     # . clear-stream(_test-output-buffered-file+4)
    7101     # . . push args
    7102     b8/copy-to-EAX  _test-output-buffered-file/imm32
    7103     05/add-to-EAX  4/imm32
    7104     50/push-EAX
    7105     # . . call
    7106     e8/call  clear-stream/disp32
    7107     # . . discard args
    7108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7109     # emit-hex(_test-output-buffered-file, 0xab, 1)
    7110     # . . push args
    7111     68/push  1/imm32
    7112     68/push  0xab/imm32
    7113     68/push  _test-output-buffered-file/imm32
    7114     # . . call
    7115     e8/call  emit-hex/disp32
    7116     # . . discard args
    7117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7118     # flush(_test-output-buffered-file)
    7119     # . . push args
    7120     68/push  _test-output-buffered-file/imm32
    7121     # . . call
    7122     e8/call  flush/disp32
    7123     # . . discard args
    7124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7125     # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
    7126     # . . push args
    7127     68/push  "F - test-emit-hex-single-byte"/imm32
    7128     68/push  0x206261/imm32
    7129     # . . push *_test-output-stream->data
    7130     b8/copy-to-EAX  _test-output-stream/imm32
    7131     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
    7132     # . . call
    7133     e8/call  check-ints-equal/disp32
    7134     # . . discard args
    7135     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7136     # . end
    7137     c3/return
    7138
    7139 test-emit-hex-multiple-byte:
    7140     # setup
    7141     # . clear-stream(_test-output-stream)
    7142     # . . push args
    7143     68/push  _test-output-stream/imm32
    7144     # . . call
    7145     e8/call  clear-stream/disp32
    7146     # . . discard args
    7147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7148     # . clear-stream(_test-output-buffered-file+4)
    7149     # . . push args
    7150     b8/copy-to-EAX  _test-output-buffered-file/imm32
    7151     05/add-to-EAX  4/imm32
    7152     50/push-EAX
    7153     # . . call
    7154     e8/call  clear-stream/disp32
    7155     # . . discard args
    7156     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7157     # emit-hex(_test-output-buffered-file, 0x1234, 2)
    7158     # . . push args
    7159     68/push  2/imm32
    7160     68/push  0x1234/imm32
    7161     68/push  _test-output-buffered-file/imm32
    7162     # . . call
    7163     e8/call  emit-hex/disp32
    7164     # . . discard args
    7165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7166     # flush(_test-output-buffered-file)
    7167     # . . push args
    7168     68/push  _test-output-buffered-file/imm32
    7169     # . . call
    7170     e8/call  flush/disp32
    7171     # . . discard args
    7172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7173     # check-stream-equal(_test-output-stream, "34 12 ", msg)
    7174     # . . push args
    7175     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
    7176     68/push  "34 12 "/imm32
    7177     68/push  _test-output-stream/imm32
    7178     # . . call
    7179     e8/call  check-stream-equal/disp32
    7180     # . . discard args
    7181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7182     # . end
    7183     c3/return
    7184
    7185 test-emit-hex-zero-pad:
    7186     # setup
    7187     # . clear-stream(_test-output-stream)
    7188     # . . push args
    7189     68/push  _test-output-stream/imm32
    7190     # . . call
    7191     e8/call  clear-stream/disp32
    7192     # . . discard args
    7193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7194     # . clear-stream(_test-output-buffered-file+4)
    7195     # . . push args
    7196     b8/copy-to-EAX  _test-output-buffered-file/imm32
    7197     05/add-to-EAX  4/imm32
    7198     50/push-EAX
    7199     # . . call
    7200     e8/call  clear-stream/disp32
    7201     # . . discard args
    7202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7203     # emit-hex(_test-output-buffered-file, 0xab, 2)
    7204     # . . push args
    7205     68/push  2/imm32
    7206     68/push  0xab/imm32
    7207     68/push  _test-output-buffered-file/imm32
    7208     # . . call
    7209     e8/call  emit-hex/disp32
    7210     # . . discard args
    7211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7212     # flush(_test-output-buffered-file)
    7213     # . . push args
    7214     68/push  _test-output-buffered-file/imm32
    7215     # . . call
    7216     e8/call  flush/disp32
    7217     # . . discard args
    7218     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7219     # check(_test-output-stream->data == 'ab 00 ')
    7220     # . . push args
    7221     68/push  "F - test-emit-hex-zero-pad/1"/imm32
    7222     68/push  "ab 00 "/imm32
    7223     68/push  _test-output-stream/imm32
    7224     # . . call
    7225     e8/call  check-stream-equal/disp32
    7226     # . . discard args
    7227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7228     # . end
    7229     c3/return
    7230
    7231 test-emit-hex-negative:
    7232     # setup
    7233     # . clear-stream(_test-output-stream)
    7234     # . . push args
    7235     68/push  _test-output-stream/imm32
    7236     # . . call
    7237     e8/call  clear-stream/disp32
    7238     # . . discard args
    7239     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7240     # . clear-stream(_test-output-buffered-file+4)
    7241     # . . push args
    7242     b8/copy-to-EAX  _test-output-buffered-file/imm32
    7243     05/add-to-EAX  4/imm32
    7244     50/push-EAX
    7245     # . . call
    7246     e8/call  clear-stream/disp32
    7247     # . . discard args
    7248     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7249     # emit-hex(_test-output-buffered-file, -1, 2)
    7250     # . . push args
    7251     68/push  2/imm32
    7252     68/push  -1/imm32
    7253     68/push  _test-output-buffered-file/imm32
    7254     # . . call
    7255     e8/call  emit-hex/disp32
    7256     # . . discard args
    7257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7258     # flush(_test-output-buffered-file)
    7259     # . . push args
    7260     68/push  _test-output-buffered-file/imm32
    7261     # . . call
    7262     e8/call  flush/disp32
    7263     # . . discard args
    7264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7265     # check-stream-equal(_test-output-stream == "ff ff ")
    7266     # . . push args
    7267     68/push  "F - test-emit-hex-negative/1"/imm32
    7268     68/push  "ff ff "/imm32
    7269     68/push  _test-output-stream/imm32
    7270     # . . call
    7271     e8/call  check-stream-equal/disp32
    7272     # . . discard args
    7273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
    7274     # . end
    7275     c3/return
    7276
    7277 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/'))
    7278 parse-datum-of-word:  # word : (address slice) -> value/EAX
    7279     # . prolog
    7280     55/push-EBP
    7281     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
    7282     # . save registers
    7283     51/push-ECX
    7284     56/push-ESI
    7285     # ESI = word
    7286     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
    7287     # var slice/ECX = {0, 0}
    7288     68/push  0/imm32/end
    7289     68/push  0/imm32/start
    7290     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
    7291     # slice = next-token-from-slice(word->start, word->end, '/')
    7292     # . . push args
    7293     51/push-ECX
    7294     68/push  0x2f/imm32/slash
    7295     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
    7296     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
    7297     # . . call
    7298     e8/call  next-token-from-slice/disp32
    7299     # . . discard args
    7300     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
    7301     # value/EAX = parse-hex-int(slice)
    7302     # . . push args
    7303     51/push-ECX
    7304     # . . call
    7305     e8/call  parse-hex-int/disp32
    7306     # . . discard args
    7307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
    7308 $parse-datum-of-word:end:
    7309     # . reclaim locals
    7310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
    7311     # . restore registers
    7312     5e/pop-to-ESI
    7313     59/pop-to-ECX
    7314     # . epilog
    7315     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
    7316     5d/pop-to-EBP
    7317     c3/return
    7318
    7319 == data
    7320
    7321 _test-slice-negative-two:
    7322     2d/- 32/2
    7323 _test-slice-negative-two-end:
    7324     2f/slash 66/f 6f/o 6f/o
    7325 _test-slice-negative-two-metadata-end:
    7326
    7327 _test-slice-three-zero:
    7328     33/3 30/0
    7329 _test-slice-three-zero-end:
    7330
    7331 _test-slice-non-number-word:
    7332     78/x 79/y 7a/z
    7333 _test-slice-non-number-word-end:
    7334     2f/slash
    7335 _test-slice-non-number-word-metadata-end:
    7336
    7337 _test-slice-hexlike-non-number-word:
    7338     61/a 62/b 63/c 64/d
    7339 _test-slice-hexlike-non-number-word-end:
    7340     2f/slash
    7341     78/x 79/y 7a/z
    7342 _test-slice-hexlike-non-number-word-metadata-end:
    7343
    7344 _test-slice-with-slash-prefix:
    7345   2f/slash 30/0 33/3
    7346 _test-slice-with-slash-prefix-end:
    7347
    7348 # . . vim:nowrap:textwidth=0