https://github.com/akkartik/mu/blob/main/linux/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:
       6 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx pack.subx  -o pack
       7 #   $ echo '05/add-to-eax 0x20/imm32'  |bootstrap/bootstrap run 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     # . prologue
      23     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
      24
      25     # initialize heap
      26     # . Heap = new-segment(Heap-size)
      27     # . . push args
      28     68/push  Heap/imm32
      29     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
      30     # . . call
      31     e8/call  new-segment/disp32
      32     # . . discard args
      33     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      34
      35     # - if argc > 1 and argv[1] == "test", then return run_tests()
      36     # if (argc <= 1) goto interactive
      37     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
      38     7e/jump-if-<=  $subx-pack-main:interactive/disp8
      39     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
      40     # . eax = kernel-string-equal?(argv[1], "test")
      41     # . . push args
      42     68/push  "test"/imm32
      43     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
      44     # . . call
      45     e8/call  kernel-string-equal?/disp32
      46     # . . discard args
      47     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      48     # . if (eax == false) goto interactive
      49     3d/compare-eax-and  0/imm32/false
      50     74/jump-if-=  $subx-pack-main:interactive/disp8
      51     # run-tests()
      52     e8/call  run-tests/disp32
      53     # syscall(exit, *Num-test-failures)
      54     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
      55     eb/jump  $subx-pack-main:end/disp8
      56 $subx-pack-main:interactive:
      57     # - otherwise convert stdin
      58     # subx-pack(Stdin, Stdout)
      59     # . . push args
      60     68/push  Stdout/imm32
      61     68/push  Stdin/imm32
      62     # . . call
      63     e8/call  subx-pack/disp32
      64     # . . discard args
      65     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x8/imm32         # add to esp
      66     # syscall(exit, 0)
      67     bb/copy-to-ebx  0/imm32
      68 $subx-pack-main:end:
      69     e8/call  syscall_exit/disp32
      70
      71 # - big picture
      72 # We'll operate on each line/instruction in isolation. That way we only need to
      73 # allocate memory for converting a single instruction.
      74 #
      75 # To pack an entire file, convert every segment in it
      76 # To convert a code segment, convert every instruction (line) until the next segment header
      77 # To convert a non-data segment, convert every word until the next segment header
      78 #
      79 # primary state: line
      80 #   stream of 512 bytes; abort if it ever overflows
      81
      82 # conceptual hierarchy within a line:
      83 #   line = words separated by ' ', maybe followed by comment starting with '#'
      84 #   word = datum until '/', then 0 or more metadata separated by '/'
      85 #
      86 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
      87 #   next-token(stream, delim char) -> slice (start, end pointers)
      88 #   next-token-from-slice(start, end, delim char) -> slice
      89 #   slice-equal?(slice, string)
      90
      91 subx-pack:  # in: (addr buffered-file), out: (addr buffered-file)
      92     # pseudocode:
      93     #   var line: (stream byte 512)
      94     #   var in-code? = false
      95     #   while true
      96     #     clear-stream(line)
      97     #     read-line-buffered(in, line)
      98     #     if (line->write == 0) break             # end of file
      99     #     var word-slice = next-word(line)
     100     #     if slice-empty?(word-slice)             # whitespace
     101     #       write-stream-data(out, line)
     102     #     else if (slice-equal?(word-slice, "=="))
     103     #       word-slice = next-word(line)
     104     #       in-code? = slice-equal?(word-slice, "code")
     105     #       write-stream-data(out, line)
     106     #     else if (in-code?)
     107     #       rewind-stream(line)
     108     #       convert-instruction(line, out)
     109     #     else
     110     #       rewind-stream(line)
     111     #       convert-data(line, out)
     112     #   flush(out)
     113     #
     114     # . prologue
     115     55/push-ebp
     116     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     117     # . save registers
     118     50/push-eax
     119     51/push-ecx
     120     52/push-edx
     121     53/push-ebx
     122     # var line/ecx: (stream byte 512)
     123     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     124     68/push  0x200/imm32/length
     125     68/push  0/imm32/read
     126     68/push  0/imm32/write
     127     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     128     # var word-slice/edx: slice
     129     68/push  0/imm32/end
     130     68/push  0/imm32/start
     131     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     132     # var in-code?/ebx: boolean = false
     133     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
     134 $subx-pack:loop:
     135     # clear-stream(line)
     136     # . . push args
     137     51/push-ecx
     138     # . . call
     139     e8/call  clear-stream/disp32
     140     # . . discard args
     141     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     142     # read-line-buffered(in, line)
     143     # . . push args
     144     51/push-ecx
     145     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     146     # . . call
     147     e8/call  read-line-buffered/disp32
     148     # . . discard args
     149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     150 $subx-pack:check0:
     151     # if (line->write == 0) break
     152     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
     153     0f 84/jump-if-=  $subx-pack:break/disp32
     154 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
     180     # next-word(line, word-slice)
     181     # . . push args
     182     52/push-edx
     183     51/push-ecx
     184     # . . call
     185     e8/call  next-word/disp32
     186     # . . discard args
     187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     188 $subx-pack:check1:
     189     # if (slice-empty?(word-slice)) write-stream-data(out, line)
     190     # . eax = slice-empty?(word-slice)
     191     # . . push args
     192     52/push-edx
     193     # . . call
     194     e8/call  slice-empty?/disp32
     195     # . . discard args
     196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     197     # . if (eax != false) write-stream-data(out, line)
     198     3d/compare-eax-and  0/imm32/false
     199     0f 85/jump-if-!=  $subx-pack:pass-through/disp32
     200 $subx-pack:check2:
     201 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
     241     # if (!slice-equal?(word-slice, "==")) goto next check
     242     # . eax = slice-equal?(word-slice, "==")
     243     # . . push args
     244     68/push  "=="/imm32
     245     52/push-edx
     246     # . . call
     247     e8/call  slice-equal?/disp32
     248     # . . discard args
     249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     250     # . if (eax == false) goto check3
     251     3d/compare-eax-and  0/imm32/false
     252     0f 84/jump-if-=  $subx-pack:check3/disp32
     253     # word-slice = next-word(line)
     254     # . . push args
     255     52/push-edx
     256     51/push-ecx
     257     # . . call
     258     e8/call  next-word/disp32
     259     # . . discard args
     260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     261 +-- 40 lines: #?     # dump segment name -----------------------------------------------------------------------------------------------------------------------------------
     301     # in-code? = slice-equal?(word-slice, "code")
     302     # . . push args
     303     68/push  "code"/imm32
     304     52/push-edx
     305     # . . call
     306     e8/call  slice-equal?/disp32
     307     # . . discard args
     308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     309     # . . in-code? = eax
     310     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
     311     # write-stream-data(out, line)
     312     eb/jump  $subx-pack:pass-through/disp8
     313 $subx-pack:check3:
     314     # else rewind-stream(line)
     315     # . rewind-stream(line)
     316     # . . push args
     317     51/push-ecx
     318     # . . call
     319     e8/call  rewind-stream/disp32
     320     # . . discard args
     321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     322     # if (in-code? != false) convert-instruction(line, out)
     323     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32/false     # compare ebx
     324     74/jump-if-=  $subx-pack:data/disp8
     325 $subx-pack:code:
     326     # . convert-instruction(line, out)
     327     # . . push args
     328     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     329     51/push-ecx
     330     # . . call
     331     e8/call  convert-instruction/disp32
     332     # . . discard args
     333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     334     # . loop
     335     e9/jump  $subx-pack:loop/disp32
     336 $subx-pack:data:
     337     # else convert-data(line, out)
     338     # . . push args
     339     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     340     51/push-ecx
     341     # . . call
     342     e8/call  convert-data/disp32
     343     # . . discard args
     344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     345     # . loop
     346     e9/jump  $subx-pack:loop/disp32
     347 $subx-pack:pass-through:
     348     # write-stream-data(out, line)
     349     # . . push args
     350     51/push-ecx
     351     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     352     # . . call
     353     e8/call  write-stream-data/disp32
     354     # . . discard args
     355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     356     # . loop
     357     e9/jump  $subx-pack:loop/disp32
     358 $subx-pack:break:
     359     # flush(out)
     360     # . . push args
     361     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     362     # . . call
     363     e8/call  flush/disp32
     364     # . . discard args
     365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     366 $subx-pack:end:
     367     # . reclaim locals
     368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
     369     # . restore registers
     370     5b/pop-to-ebx
     371     5a/pop-to-edx
     372     59/pop-to-ecx
     373     58/pop-to-eax
     374     # . epilogue
     375     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     376     5d/pop-to-ebp
     377     c3/return
     378
     379 test-subx-pack-passes-empty-lines-through:
     380     # if a line is empty, pass it along unchanged
     381     # . prologue
     382     55/push-ebp
     383     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     384     # setup
     385     # . clear-stream(_test-input-stream)
     386     # . . push args
     387     68/push  _test-input-stream/imm32
     388     # . . call
     389     e8/call  clear-stream/disp32
     390     # . . discard args
     391     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     392     # . clear-stream($_test-input-buffered-file->buffer)
     393     # . . push args
     394     68/push  $_test-input-buffered-file->buffer/imm32
     395     # . . call
     396     e8/call  clear-stream/disp32
     397     # . . discard args
     398     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     399     # . clear-stream(_test-output-stream)
     400     # . . push args
     401     68/push  _test-output-stream/imm32
     402     # . . call
     403     e8/call  clear-stream/disp32
     404     # . . discard args
     405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     406     # . clear-stream($_test-output-buffered-file->buffer)
     407     # . . push args
     408     68/push  $_test-output-buffered-file->buffer/imm32
     409     # . . call
     410     e8/call  clear-stream/disp32
     411     # . . discard args
     412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     413     # write nothing to input
     414     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
     415     # . . push args
     416     68/push  _test-output-buffered-file/imm32
     417     68/push  _test-input-buffered-file/imm32
     418     # . . call
     419     e8/call  subx-pack/disp32
     420     # . . discard args
     421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     422     # check that the line just passed through
     423     # . flush(_test-output-buffered-file)
     424     # . . push args
     425     68/push  _test-output-buffered-file/imm32
     426     # . . call
     427     e8/call  flush/disp32
     428     # . . discard args
     429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     430     # . check-stream-equal(_test-output-stream, "", msg)
     431     # . . push args
     432     68/push  "F - test-subx-pack-passes-empty-lines-through"/imm32
     433     68/push  ""/imm32
     434     68/push  _test-output-stream/imm32
     435     # . . call
     436     e8/call  check-stream-equal/disp32
     437     # . . discard args
     438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     439     # . epilogue
     440     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     441     5d/pop-to-ebp
     442     c3/return
     443
     444 test-subx-pack-passes-lines-with-just-whitespace-through:
     445     # if a line is empty, pass it along unchanged
     446     # . prologue
     447     55/push-ebp
     448     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     449     # setup
     450     # . clear-stream(_test-input-stream)
     451     # . . push args
     452     68/push  _test-input-stream/imm32
     453     # . . call
     454     e8/call  clear-stream/disp32
     455     # . . discard args
     456     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     457     # . clear-stream($_test-input-buffered-file->buffer)
     458     # . . push args
     459     68/push  $_test-input-buffered-file->buffer/imm32
     460     # . . call
     461     e8/call  clear-stream/disp32
     462     # . . discard args
     463     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     464     # . clear-stream(_test-output-stream)
     465     # . . push args
     466     68/push  _test-output-stream/imm32
     467     # . . call
     468     e8/call  clear-stream/disp32
     469     # . . discard args
     470     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     471     # . clear-stream($_test-output-buffered-file->buffer)
     472     # . . push args
     473     68/push  $_test-output-buffered-file->buffer/imm32
     474     # . . call
     475     e8/call  clear-stream/disp32
     476     # . . discard args
     477     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     478     # initialize input
     479     # . write(_test-input-stream, "    ")
     480     # . . push args
     481     68/push  "    "/imm32
     482     68/push  _test-input-stream/imm32
     483     # . . call
     484     e8/call  write/disp32
     485     # . . discard args
     486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     487     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
     488     # . . push args
     489     68/push  _test-output-buffered-file/imm32
     490     68/push  _test-input-buffered-file/imm32
     491     # . . call
     492     e8/call  subx-pack/disp32
     493     # . . discard args
     494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     495     # check that the line just passed through
     496     # . flush(_test-output-buffered-file)
     497     # . . push args
     498     68/push  _test-output-buffered-file/imm32
     499     # . . call
     500     e8/call  flush/disp32
     501     # . . discard args
     502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     503     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
     504     # . . push args
     505     68/push  "F - test-subx-pack-passes-with-just-whitespace-through"/imm32
     506     68/push  "    "/imm32
     507     68/push  _test-output-stream/imm32
     508     # . . call
     509     e8/call  check-next-stream-line-equal/disp32
     510     # . . discard args
     511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     512     # . epilogue
     513     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     514     5d/pop-to-ebp
     515     c3/return
     516
     517 test-subx-pack-passes-segment-headers-through:
     518     # if a line starts with '==', pass it along unchanged
     519     # . prologue
     520     55/push-ebp
     521     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     522     # setup
     523     # . clear-stream(_test-input-stream)
     524     # . . push args
     525     68/push  _test-input-stream/imm32
     526     # . . call
     527     e8/call  clear-stream/disp32
     528     # . . discard args
     529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     530     # . clear-stream($_test-input-buffered-file->buffer)
     531     # . . push args
     532     68/push  $_test-input-buffered-file->buffer/imm32
     533     # . . call
     534     e8/call  clear-stream/disp32
     535     # . . discard args
     536     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     537     # . clear-stream(_test-output-stream)
     538     # . . push args
     539     68/push  _test-output-stream/imm32
     540     # . . call
     541     e8/call  clear-stream/disp32
     542     # . . discard args
     543     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     544     # . clear-stream($_test-output-buffered-file->buffer)
     545     # . . push args
     546     68/push  $_test-output-buffered-file->buffer/imm32
     547     # . . call
     548     e8/call  clear-stream/disp32
     549     # . . discard args
     550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     551     # initialize input
     552     # . write(_test-input-stream, "== abcd 0x1")
     553     # . . push args
     554     68/push  "== abcd 0x1"/imm32
     555     68/push  _test-input-stream/imm32
     556     # . . call
     557     e8/call  write/disp32
     558     # . . discard args
     559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     560     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
     561     # . . push args
     562     68/push  _test-output-buffered-file/imm32
     563     68/push  _test-input-buffered-file/imm32
     564     # . . call
     565     e8/call  subx-pack/disp32
     566     # . . discard args
     567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     568     # check that the line just passed through
     569     # . flush(_test-output-buffered-file)
     570     # . . push args
     571     68/push  _test-output-buffered-file/imm32
     572     # . . call
     573     e8/call  flush/disp32
     574     # . . discard args
     575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     576     # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg)
     577     # . . push args
     578     68/push  "F - test-subx-pack-passes-segment-headers-through"/imm32
     579     68/push  "== abcd 0x1"/imm32
     580     68/push  _test-output-stream/imm32
     581     # . . call
     582     e8/call  check-stream-equal/disp32
     583     # . . discard args
     584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     585     # . epilogue
     586     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     587     5d/pop-to-ebp
     588     c3/return
     589
     590 test-subx-pack-in-data-segment:
     591     # correctly process lines in the data segment
     592     # . prologue
     593     55/push-ebp
     594     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     595     # setup
     596     # . clear-stream(_test-input-stream)
     597     # . . push args
     598     68/push  _test-input-stream/imm32
     599     # . . call
     600     e8/call  clear-stream/disp32
     601     # . . discard args
     602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     603     # . clear-stream($_test-input-buffered-file->buffer)
     604     # . . push args
     605     68/push  $_test-input-buffered-file->buffer/imm32
     606     # . . call
     607     e8/call  clear-stream/disp32
     608     # . . discard args
     609     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     610     # . clear-stream(_test-output-stream)
     611     # . . push args
     612     68/push  _test-output-stream/imm32
     613     # . . call
     614     e8/call  clear-stream/disp32
     615     # . . discard args
     616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     617     # . clear-stream($_test-output-buffered-file->buffer)
     618     # . . push args
     619     68/push  $_test-output-buffered-file->buffer/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     # initialize input
     625     #   == code 0x1
     626     #   == data 0x2
     627     #   3 4/imm32
     628     # . write(_test-input-stream, "== code 0x1")
     629     # . . push args
     630     68/push  "== code 0x1\n"/imm32
     631     68/push  _test-input-stream/imm32
     632     # . . call
     633     e8/call  write/disp32
     634     # . . discard args
     635     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     636     # . write(_test-input-stream, "== data 0x2\n")
     637     # . . push args
     638     68/push  "== data 0x2\n"/imm32
     639     68/push  _test-input-stream/imm32
     640     # . . call
     641     e8/call  write/disp32
     642     # . . discard args
     643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     644     # . write(_test-input-stream, "3 4/imm32\n")
     645     # . . push args
     646     68/push  "3 4/imm32\n"/imm32
     647     68/push  _test-input-stream/imm32
     648     # . . call
     649     e8/call  write/disp32
     650     # . . discard args
     651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     652     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
     653     # . . push args
     654     68/push  _test-output-buffered-file/imm32
     655     68/push  _test-input-buffered-file/imm32
     656     # . . call
     657     e8/call  subx-pack/disp32
     658     # . . discard args
     659     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     660     # check output
     661 +-- 26 lines: #?     # debug print -----------------------------------------------------------------------------------------------------------------------------------------
     687     # . flush(_test-output-buffered-file)
     688     # . . push args
     689     68/push  _test-output-buffered-file/imm32
     690     # . . call
     691     e8/call  flush/disp32
     692     # . . discard args
     693     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     694     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
     695     # . . push args
     696     68/push  "F - test-subx-pack-in-data-segment/0"/imm32
     697     68/push  "== code 0x1"/imm32
     698     68/push  _test-output-stream/imm32
     699     # . . call
     700     e8/call  check-next-stream-line-equal/disp32
     701     # . . discard args
     702     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     703     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
     704     # . . push args
     705     68/push  "F - test-subx-pack-in-data-segment/1"/imm32
     706     68/push  "== data 0x2"/imm32
     707     68/push  _test-output-stream/imm32
     708     # . . call
     709     e8/call  check-next-stream-line-equal/disp32
     710     # . . discard args
     711     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     712     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
     713     # . . push args
     714     68/push  "F - test-subx-pack-in-data-segment/2"/imm32
     715     68/push  "03 04 00 00 00 "/imm32
     716     68/push  _test-output-stream/imm32
     717     # . . call
     718     e8/call  check-next-stream-line-equal/disp32
     719     # . . discard args
     720     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     721     # . epilogue
     722     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     723     5d/pop-to-ebp
     724     c3/return
     725
     726 test-subx-pack-code-and-data-segments:
     727     # correctly process lines in both code and data segments
     728     # . prologue
     729     55/push-ebp
     730     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     731     # setup
     732     # . clear-stream(_test-input-stream)
     733     # . . push args
     734     68/push  _test-input-stream/imm32
     735     # . . call
     736     e8/call  clear-stream/disp32
     737     # . . discard args
     738     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     739     # . clear-stream($_test-input-buffered-file->buffer)
     740     # . . push args
     741     68/push  $_test-input-buffered-file->buffer/imm32
     742     # . . call
     743     e8/call  clear-stream/disp32
     744     # . . discard args
     745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     746     # . clear-stream(_test-output-stream)
     747     # . . push args
     748     68/push  _test-output-stream/imm32
     749     # . . call
     750     e8/call  clear-stream/disp32
     751     # . . discard args
     752     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     753     # . clear-stream($_test-output-buffered-file->buffer)
     754     # . . push args
     755     68/push  $_test-output-buffered-file->buffer/imm32
     756     # . . call
     757     e8/call  clear-stream/disp32
     758     # . . discard args
     759     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     760     # initialize input
     761     #   == code 0x1
     762     #   e8/call 20/disp32
     763     #   68/push 0x20/imm8
     764     #   == data 0x2
     765     #   3 4/imm32
     766     # . write(_test-input-stream, "== code 0x1\n")
     767     # . . push args
     768     68/push  "== code 0x1\n"/imm32
     769     68/push  _test-input-stream/imm32
     770     # . . call
     771     e8/call  write/disp32
     772     # . . discard args
     773     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     774     # . write(_test-input-stream, "e8/call 20/disp32\n")
     775     # . . push args
     776     68/push  "e8/call 20/disp32\n"/imm32
     777     68/push  _test-input-stream/imm32
     778     # . . call
     779     e8/call  write/disp32
     780     # . . discard args
     781     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     782     # . write(_test-input-stream, "68/push 0x20/imm8\n")
     783     # . . push args
     784     68/push  "68/push 0x20/imm8\n"/imm32
     785     68/push  _test-input-stream/imm32
     786     # . . call
     787     e8/call  write/disp32
     788     # . . discard args
     789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     790     # . write(_test-input-stream, "== data 0x2\n")
     791     # . . push args
     792     68/push  "== data 0x2\n"/imm32
     793     68/push  _test-input-stream/imm32
     794     # . . call
     795     e8/call  write/disp32
     796     # . . discard args
     797     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     798     # . write(_test-input-stream, "3 4/imm32\n")
     799     # . . push args
     800     68/push  "3 4/imm32\n"/imm32
     801     68/push  _test-input-stream/imm32
     802     # . . call
     803     e8/call  write/disp32
     804     # . . discard args
     805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     806     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
     807     # . . push args
     808     68/push  _test-output-buffered-file/imm32
     809     68/push  _test-input-buffered-file/imm32
     810     # . . call
     811     e8/call  subx-pack/disp32
     812     # . . discard args
     813     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     814     # check output
     815     #   == code 0x1
     816     #   e8 20 00 00 00  # e8/call 20/disp32
     817     #   68 20  # 68/push 0x20/imm8
     818     #   == data 0x2
     819     #   03 04 00 00 00
     820 +-- 26 lines: #?     # debug print -----------------------------------------------------------------------------------------------------------------------------------------
     846     # . flush(_test-output-buffered-file)
     847     # . . push args
     848     68/push  _test-output-buffered-file/imm32
     849     # . . call
     850     e8/call  flush/disp32
     851     # . . discard args
     852     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     853     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
     854     # . . push args
     855     68/push  "F - test-subx-pack-code-and-data-segments/0"/imm32
     856     68/push  "== code 0x1"/imm32
     857     68/push  _test-output-stream/imm32
     858     # . . call
     859     e8/call  check-next-stream-line-equal/disp32
     860     # . . discard args
     861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     862     # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
     863     # . . push args
     864     68/push  "F - test-subx-pack-code-and-data-segments/1"/imm32
     865     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
     866     68/push  _test-output-stream/imm32
     867     # . . call
     868     e8/call  check-next-stream-line-equal/disp32
     869     # . . discard args
     870     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     871     # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
     872     # . . push args
     873     68/push  "F - test-subx-pack-code-and-data-segments/2"/imm32
     874     68/push  "68 20  # 68/push 0x20/imm8"/imm32
     875     68/push  _test-output-stream/imm32
     876     # . . call
     877     e8/call  check-next-stream-line-equal/disp32
     878     # . . discard args
     879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     880     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
     881     # . . push args
     882     68/push  "F - test-subx-pack-code-and-data-segments/3"/imm32
     883     68/push  "== data 0x2"/imm32
     884     68/push  _test-output-stream/imm32
     885     # . . call
     886     e8/call  check-next-stream-line-equal/disp32
     887     # . . discard args
     888     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     889     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
     890     # . . push args
     891     68/push  "F - test-subx-pack-code-and-data-segments/4"/imm32
     892     68/push  "03 04 00 00 00 "/imm32
     893     68/push  _test-output-stream/imm32
     894     # . . call
     895     e8/call  check-next-stream-line-equal/disp32
     896     # . . discard args
     897     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     898     # . epilogue
     899     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     900     5d/pop-to-ebp
     901     c3/return
     902
     903 convert-data:  # line: (addr stream byte), out: (addr buffered-file)
     904     # pseudocode:
     905     #   var word-slice: slice
     906     #   while true
     907     #     word-slice = next-word(line)
     908     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
     909     #       break  # skip emitting some whitespace
     910     #     if slice-starts-with?(word-slice, "#")      # comment
     911     #       write-slice-buffered(out, word-slice)
     912     #       return
     913     #     if slice-ends-with?(word-slice, ":")        # label
     914     #       write-stream-data(out, line)
     915     #       return
     916     #     if has-metadata?(word-slice, "imm32")
     917     #       emit(out, word-slice, 4)
     918     #     else if has-metadata?(word-slice, "imm16")
     919     #       emit(out, word-slice, 2)
     920     #     else
     921     #       emit(out, word-slice, 1)
     922     #   write-buffered(out, "\n")
     923     #
     924     # . prologue
     925     55/push-ebp
     926     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     927     # . save registers
     928     50/push-eax
     929     51/push-ecx
     930     52/push-edx
     931     # var word-slice/ecx: slice
     932     68/push  0/imm32/end
     933     68/push  0/imm32/start
     934     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     935 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
     961 $convert-data:loop:
     962     # next-word(line, word-slice)
     963     # . . push args
     964     51/push-ecx
     965     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     966     # . . call
     967     e8/call  next-word/disp32
     968     # . . discard args
     969     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     970 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
    1010 $convert-data:check0:
    1011     # if (slice-empty?(word-slice)) break
    1012     # . eax = slice-empty?(word-slice)
    1013     # . . push args
    1014     51/push-ecx
    1015     # . . call
    1016     e8/call  slice-empty?/disp32
    1017     # . . discard args
    1018     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1019     # . if (eax != false) break
    1020     3d/compare-eax-and  0/imm32/false
    1021     0f 85/jump-if-!=  $convert-data:break/disp32
    1022 $convert-data:check-for-comment:
    1023     # if (slice-starts-with?(word-slice, "#"))
    1024     # . var start/edx: (addr byte) = word-slice->start
    1025     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    1026     # . var c/eax: byte = *start
    1027     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    1028     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
    1029     # . if (c != '#') goto next check
    1030     3d/compare-eax-and  0x23/imm32/hash
    1031     75/jump-if-!=  $convert-data:check-for-label/disp8
    1032 $convert-data:comment:
    1033     # write-slice-buffered(out, word-slice)
    1034     # . . push args
    1035     51/push-ecx
    1036     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1037     # . . call
    1038     e8/call  write-slice-buffered/disp32
    1039     # . . discard args
    1040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1041     # return
    1042     0f 85/jump-if-!=  $convert-data:end/disp32
    1043 $convert-data:check-for-label:
    1044     # if (slice-ends-with?(word-slice, ":"))
    1045     # . var end/edx: (addr byte) = word-slice->end
    1046     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
    1047     # . var c/eax: byte = *(end-1)
    1048     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    1049     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
    1050     # . if (c != ':') goto next check
    1051     3d/compare-eax-and  0x3a/imm32/colon
    1052     75/jump-if-!=  $convert-data:check-for-imm32/disp8
    1053 $convert-data:label:
    1054     # write-stream-data(out, line)
    1055     # . . push args
    1056     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1057     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1058     # . . call
    1059     e8/call  write-stream-data/disp32
    1060     # . . discard args
    1061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1062     # return
    1063     0f 85/jump-if-!=  $convert-data:end/disp32
    1064 $convert-data:check-for-imm32:
    1065     # if (has-metadata?(word-slice, "imm32"))
    1066     # . eax = has-metadata?(ecx, "imm32")
    1067     # . . push args
    1068     68/push  "imm32"/imm32
    1069     51/push-ecx
    1070     # . . call
    1071     e8/call  has-metadata?/disp32
    1072     # . . discard args
    1073     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1074     # . if (eax == false) process as a single byte
    1075     3d/compare-eax-and  0/imm32/false
    1076     74/jump-if-=  $convert-data:check-for-imm16/disp8
    1077 $convert-data:imm32:
    1078     # emit(out, word-slice, 4)
    1079     # . . push args
    1080     68/push  4/imm32
    1081     51/push-ecx
    1082     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1083     # . . call
    1084     e8/call  emit/disp32
    1085     # . . discard args
    1086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1087     e9/jump  $convert-data:loop/disp32
    1088 $convert-data:check-for-imm16:
    1089     # if (has-metadata?(word-slice, "imm16"))
    1090     # . eax = has-metadata?(ecx, "imm16")
    1091     # . . push args
    1092     68/push  "imm16"/imm32
    1093     51/push-ecx
    1094     # . . call
    1095     e8/call  has-metadata?/disp32
    1096     # . . discard args
    1097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1098     # . if (eax == false) process as a single byte
    1099     3d/compare-eax-and  0/imm32/false
    1100     74/jump-if-=  $convert-data:single-byte/disp8
    1101 $convert-data:imm16:
    1102     # emit(out, word-slice, 2)
    1103     # . . push args
    1104     68/push  2/imm32
    1105     51/push-ecx
    1106     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1107     # . . call
    1108     e8/call  emit/disp32
    1109     # . . discard args
    1110     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1111     e9/jump  $convert-data:loop/disp32
    1112 $convert-data:single-byte:
    1113     # emit(out, word-slice, 1)
    1114     # . . push args
    1115     68/push  1/imm32
    1116     51/push-ecx
    1117     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1118     # . . call
    1119     e8/call  emit/disp32
    1120     # . . discard args
    1121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1122     e9/jump  $convert-data:loop/disp32
    1123 $convert-data:break:
    1124     # write-buffered(out, "\n")
    1125     # . . push args
    1126     68/push  Newline/imm32
    1127     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1128     # . . call
    1129     e8/call  write-buffered/disp32
    1130     # . . discard args
    1131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1132 $convert-data:end:
    1133     # . reclaim locals
    1134     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1135     # . restore registers
    1136     5a/pop-to-edx
    1137     59/pop-to-ecx
    1138     58/pop-to-eax
    1139     # . epilogue
    1140     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1141     5d/pop-to-ebp
    1142     c3/return
    1143
    1144 test-convert-data-passes-comments-through:
    1145     # if a line starts with '#', pass it along unchanged
    1146     # . prologue
    1147     55/push-ebp
    1148     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1149     # setup
    1150     # . clear-stream(_test-input-stream)
    1151     # . . push args
    1152     68/push  _test-input-stream/imm32
    1153     # . . call
    1154     e8/call  clear-stream/disp32
    1155     # . . discard args
    1156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1157     # . clear-stream(_test-output-stream)
    1158     # . . push args
    1159     68/push  _test-output-stream/imm32
    1160     # . . call
    1161     e8/call  clear-stream/disp32
    1162     # . . discard args
    1163     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1164     # . clear-stream($_test-output-buffered-file->buffer)
    1165     # . . push args
    1166     68/push  $_test-output-buffered-file->buffer/imm32
    1167     # . . call
    1168     e8/call  clear-stream/disp32
    1169     # . . discard args
    1170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1171     # initialize input
    1172     # . write(_test-input-stream, "# abcd")
    1173     # . . push args
    1174     68/push  "# abcd"/imm32
    1175     68/push  _test-input-stream/imm32
    1176     # . . call
    1177     e8/call  write/disp32
    1178     # . . discard args
    1179     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1180     # convert-data(_test-input-stream, _test-output-buffered-file)
    1181     # . . push args
    1182     68/push  _test-output-buffered-file/imm32
    1183     68/push  _test-input-stream/imm32
    1184     # . . call
    1185     e8/call  convert-data/disp32
    1186     # . . discard args
    1187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1188     # check that the line just passed through
    1189     # . flush(_test-output-buffered-file)
    1190     # . . push args
    1191     68/push  _test-output-buffered-file/imm32
    1192     # . . call
    1193     e8/call  flush/disp32
    1194     # . . discard args
    1195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1196 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    1222     # . check-stream-equal(_test-output-stream, "# abcd", msg)
    1223     # . . push args
    1224     68/push  "F - test-convert-data-passes-comments-through"/imm32
    1225     68/push  "# abcd"/imm32
    1226     68/push  _test-output-stream/imm32
    1227     # . . call
    1228     e8/call  check-stream-equal/disp32
    1229     # . . discard args
    1230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1231     # . epilogue
    1232     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1233     5d/pop-to-ebp
    1234     c3/return
    1235
    1236 test-convert-data-passes-labels-through:
    1237     # if the first word ends with ':', pass along the entire line unchanged
    1238     # . prologue
    1239     55/push-ebp
    1240     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1241     # setup
    1242     # . clear-stream(_test-input-stream)
    1243     # . . push args
    1244     68/push  _test-input-stream/imm32
    1245     # . . call
    1246     e8/call  clear-stream/disp32
    1247     # . . discard args
    1248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1249     # . clear-stream(_test-output-stream)
    1250     # . . push args
    1251     68/push  _test-output-stream/imm32
    1252     # . . call
    1253     e8/call  clear-stream/disp32
    1254     # . . discard args
    1255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1256     # . clear-stream($_test-output-buffered-file->buffer)
    1257     # . . push args
    1258     68/push  $_test-output-buffered-file->buffer/imm32
    1259     # . . call
    1260     e8/call  clear-stream/disp32
    1261     # . . discard args
    1262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1263     # initialize input
    1264     # . write(_test-input-stream, "ab: # cd")
    1265     # . . push args
    1266     68/push  "ab: # cd"/imm32
    1267     68/push  _test-input-stream/imm32
    1268     # . . call
    1269     e8/call  write/disp32
    1270     # . . discard args
    1271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1272     # convert-data(_test-input-stream, _test-output-buffered-file)
    1273     # . . push args
    1274     68/push  _test-output-buffered-file/imm32
    1275     68/push  _test-input-stream/imm32
    1276     # . . call
    1277     e8/call  convert-data/disp32
    1278     # . . discard args
    1279     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1280     # check that the line just passed through
    1281     # . flush(_test-output-buffered-file)
    1282     # . . push args
    1283     68/push  _test-output-buffered-file/imm32
    1284     # . . call
    1285     e8/call  flush/disp32
    1286     # . . discard args
    1287     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1288     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
    1289     # . . push args
    1290     68/push  "F - test-convert-data-passes-labels-through"/imm32
    1291     68/push  "ab: # cd"/imm32
    1292     68/push  _test-output-stream/imm32
    1293     # . . call
    1294     e8/call  check-stream-equal/disp32
    1295     # . . discard args
    1296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1297     # . epilogue
    1298     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1299     5d/pop-to-ebp
    1300     c3/return
    1301
    1302 test-convert-data-passes-names-through:
    1303     # If a word is a valid name, just emit it unchanged.
    1304     # Later phases will deal with it.
    1305     # . prologue
    1306     55/push-ebp
    1307     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1308     # setup
    1309     # . clear-stream(_test-input-stream)
    1310     # . . push args
    1311     68/push  _test-input-stream/imm32
    1312     # . . call
    1313     e8/call  clear-stream/disp32
    1314     # . . discard args
    1315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1316     # . clear-stream(_test-output-stream)
    1317     # . . push args
    1318     68/push  _test-output-stream/imm32
    1319     # . . call
    1320     e8/call  clear-stream/disp32
    1321     # . . discard args
    1322     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1323     # . clear-stream($_test-output-buffered-file->buffer)
    1324     # . . push args
    1325     68/push  $_test-output-buffered-file->buffer/imm32
    1326     # . . call
    1327     e8/call  clear-stream/disp32
    1328     # . . discard args
    1329     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1330     # initialize input
    1331     # . write(_test-input-stream, "abcd/imm32")
    1332     # . . push args
    1333     68/push  "abcd/imm32"/imm32
    1334     68/push  _test-input-stream/imm32
    1335     # . . call
    1336     e8/call  write/disp32
    1337     # . . discard args
    1338     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1339     # convert-data(_test-input-stream, _test-output-buffered-file)
    1340     # . . push args
    1341     68/push  _test-output-buffered-file/imm32
    1342     68/push  _test-input-stream/imm32
    1343     # . . call
    1344     e8/call  convert-data/disp32
    1345     # . . discard args
    1346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1347     # check that the line just passed through
    1348     # . flush(_test-output-buffered-file)
    1349     # . . push args
    1350     68/push  _test-output-buffered-file/imm32
    1351     # . . call
    1352     e8/call  flush/disp32
    1353     # . . discard args
    1354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1355     # . check-stream-equal(_test-output-stream, "abcd/imm32 \n", msg)
    1356     # . . push args
    1357     68/push  "F - test-convert-data-passes-names-through"/imm32
    1358     68/push  "abcd/imm32 \n"/imm32
    1359     68/push  _test-output-stream/imm32
    1360     # . . call
    1361     e8/call  check-stream-equal/disp32
    1362     # . . discard args
    1363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1364     # . epilogue
    1365     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1366     5d/pop-to-ebp
    1367     c3/return
    1368
    1369 test-convert-data-handles-imm32:
    1370     # If a word has the /imm32 metadata, emit it in 4 bytes.
    1371     # . prologue
    1372     55/push-ebp
    1373     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1374     # setup
    1375     # . clear-stream(_test-input-stream)
    1376     # . . push args
    1377     68/push  _test-input-stream/imm32
    1378     # . . call
    1379     e8/call  clear-stream/disp32
    1380     # . . discard args
    1381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1382     # . clear-stream(_test-output-stream)
    1383     # . . push args
    1384     68/push  _test-output-stream/imm32
    1385     # . . call
    1386     e8/call  clear-stream/disp32
    1387     # . . discard args
    1388     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1389     # . clear-stream($_test-output-buffered-file->buffer)
    1390     # . . push args
    1391     68/push  $_test-output-buffered-file->buffer/imm32
    1392     # . . call
    1393     e8/call  clear-stream/disp32
    1394     # . . discard args
    1395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1396     # initialize input
    1397     # . write(_test-input-stream, "30/imm32")
    1398     # . . push args
    1399     68/push  "30/imm32"/imm32
    1400     68/push  _test-input-stream/imm32
    1401     # . . call
    1402     e8/call  write/disp32
    1403     # . . discard args
    1404     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1405     # convert-data(_test-input-stream, _test-output-buffered-file)
    1406     # . . push args
    1407     68/push  _test-output-buffered-file/imm32
    1408     68/push  _test-input-stream/imm32
    1409     # . . call
    1410     e8/call  convert-data/disp32
    1411     # . . discard args
    1412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1413     # check that 4 bytes were written
    1414     # . flush(_test-output-buffered-file)
    1415     # . . push args
    1416     68/push  _test-output-buffered-file/imm32
    1417     # . . call
    1418     e8/call  flush/disp32
    1419     # . . discard args
    1420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1421     # . check-stream-equal(_test-output-stream, "30 00 00 00 \n", msg)
    1422     # . . push args
    1423     68/push  "F - test-convert-data-handles-imm32"/imm32
    1424     68/push  "30 00 00 00 \n"/imm32
    1425     68/push  _test-output-stream/imm32
    1426     # . . call
    1427     e8/call  check-stream-equal/disp32
    1428     # . . discard args
    1429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1430     # . epilogue
    1431     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1432     5d/pop-to-ebp
    1433     c3/return
    1434
    1435 test-convert-data-handles-imm16:
    1436     # . prologue
    1437     55/push-ebp
    1438     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1439     # setup
    1440     # . clear-stream(_test-input-stream)
    1441     # . . push args
    1442     68/push  _test-input-stream/imm32
    1443     # . . call
    1444     e8/call  clear-stream/disp32
    1445     # . . discard args
    1446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1447     # . clear-stream(_test-output-stream)
    1448     # . . push args
    1449     68/push  _test-output-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-buffered-file->buffer)
    1455     # . . push args
    1456     68/push  $_test-output-buffered-file->buffer/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     # initialize input
    1462     # . write(_test-input-stream, "30/imm16")
    1463     # . . push args
    1464     68/push  "30/imm16"/imm32
    1465     68/push  _test-input-stream/imm32
    1466     # . . call
    1467     e8/call  write/disp32
    1468     # . . discard args
    1469     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1470     # convert-data(_test-input-stream, _test-output-buffered-file)
    1471     # . . push args
    1472     68/push  _test-output-buffered-file/imm32
    1473     68/push  _test-input-stream/imm32
    1474     # . . call
    1475     e8/call  convert-data/disp32
    1476     # . . discard args
    1477     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1478     # check that a single byte was written
    1479     # . flush(_test-output-buffered-file)
    1480     # . . push args
    1481     68/push  _test-output-buffered-file/imm32
    1482     # . . call
    1483     e8/call  flush/disp32
    1484     # . . discard args
    1485     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1486     # . check-stream-equal(_test-output-stream, "30 00 \n", msg)
    1487     # . . push args
    1488     68/push  "F - test-convert-data-handles-imm16"/imm32
    1489     68/push  "30 00 \n"/imm32
    1490     68/push  _test-output-stream/imm32
    1491     # . . call
    1492     e8/call  check-stream-equal/disp32
    1493     # . . discard args
    1494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1495     # . epilogue
    1496     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1497     5d/pop-to-ebp
    1498     c3/return
    1499
    1500 test-convert-data-handles-imm8:
    1501     # . prologue
    1502     55/push-ebp
    1503     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1504     # setup
    1505     # . clear-stream(_test-input-stream)
    1506     # . . push args
    1507     68/push  _test-input-stream/imm32
    1508     # . . call
    1509     e8/call  clear-stream/disp32
    1510     # . . discard args
    1511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1512     # . clear-stream(_test-output-stream)
    1513     # . . push args
    1514     68/push  _test-output-stream/imm32
    1515     # . . call
    1516     e8/call  clear-stream/disp32
    1517     # . . discard args
    1518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1519     # . clear-stream($_test-output-buffered-file->buffer)
    1520     # . . push args
    1521     68/push  $_test-output-buffered-file->buffer/imm32
    1522     # . . call
    1523     e8/call  clear-stream/disp32
    1524     # . . discard args
    1525     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1526     # initialize input
    1527     # . write(_test-input-stream, "30/imm8")
    1528     # . . push args
    1529     68/push  "30/imm8"/imm32
    1530     68/push  _test-input-stream/imm32
    1531     # . . call
    1532     e8/call  write/disp32
    1533     # . . discard args
    1534     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1535     # convert-data(_test-input-stream, _test-output-buffered-file)
    1536     # . . push args
    1537     68/push  _test-output-buffered-file/imm32
    1538     68/push  _test-input-stream/imm32
    1539     # . . call
    1540     e8/call  convert-data/disp32
    1541     # . . discard args
    1542     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1543     # check that a single byte was written
    1544     # . flush(_test-output-buffered-file)
    1545     # . . push args
    1546     68/push  _test-output-buffered-file/imm32
    1547     # . . call
    1548     e8/call  flush/disp32
    1549     # . . discard args
    1550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1551     # . check-stream-equal(_test-output-stream, "30 \n", msg)
    1552     # . . push args
    1553     68/push  "F - test-convert-data-handles-imm8"/imm32
    1554     68/push  "30 \n"/imm32
    1555     68/push  _test-output-stream/imm32
    1556     # . . call
    1557     e8/call  check-stream-equal/disp32
    1558     # . . discard args
    1559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1560     # . epilogue
    1561     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1562     5d/pop-to-ebp
    1563     c3/return
    1564
    1565 test-convert-data-multiple-bytes:
    1566     # Multiple single-byte words in input stream get processed one by one.
    1567     # . prologue
    1568     55/push-ebp
    1569     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1570     # setup
    1571     # . clear-stream(_test-input-stream)
    1572     # . . push args
    1573     68/push  _test-input-stream/imm32
    1574     # . . call
    1575     e8/call  clear-stream/disp32
    1576     # . . discard args
    1577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1578     # . clear-stream(_test-output-stream)
    1579     # . . push args
    1580     68/push  _test-output-stream/imm32
    1581     # . . call
    1582     e8/call  clear-stream/disp32
    1583     # . . discard args
    1584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1585     # . clear-stream($_test-output-buffered-file->buffer)
    1586     # . . push args
    1587     68/push  $_test-output-buffered-file->buffer/imm32
    1588     # . . call
    1589     e8/call  clear-stream/disp32
    1590     # . . discard args
    1591     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1592     # initialize input
    1593     # . write(_test-input-stream, "1 2")
    1594     # . . push args
    1595     68/push  "1 2"/imm32
    1596     68/push  _test-input-stream/imm32
    1597     # . . call
    1598     e8/call  write/disp32
    1599     # . . discard args
    1600     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1601     # convert-data(_test-input-stream, _test-output-buffered-file)
    1602     # . . push args
    1603     68/push  _test-output-buffered-file/imm32
    1604     68/push  _test-input-stream/imm32
    1605     # . . call
    1606     e8/call  convert-data/disp32
    1607     # . . discard args
    1608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1609     # check output
    1610     # . flush(_test-output-buffered-file)
    1611     # . . push args
    1612     68/push  _test-output-buffered-file/imm32
    1613     # . . call
    1614     e8/call  flush/disp32
    1615     # . . discard args
    1616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1617     # . check-stream-equal(_test-output-stream, "01 02 \n", msg)
    1618     # . . push args
    1619     68/push  "F - test-convert-data-multiple-bytes"/imm32
    1620     68/push  "01 02 \n"/imm32
    1621     68/push  _test-output-stream/imm32
    1622     # . . call
    1623     e8/call  check-stream-equal/disp32
    1624     # . . discard args
    1625     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1626     # . epilogue
    1627     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1628     5d/pop-to-ebp
    1629     c3/return
    1630
    1631 test-convert-data-byte-then-name:
    1632     # Single-byte word followed by valid name get processed one by one.
    1633     # . prologue
    1634     55/push-ebp
    1635     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1636     # setup
    1637     # . clear-stream(_test-input-stream)
    1638     # . . push args
    1639     68/push  _test-input-stream/imm32
    1640     # . . call
    1641     e8/call  clear-stream/disp32
    1642     # . . discard args
    1643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1644     # . clear-stream(_test-output-stream)
    1645     # . . push args
    1646     68/push  _test-output-stream/imm32
    1647     # . . call
    1648     e8/call  clear-stream/disp32
    1649     # . . discard args
    1650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1651     # . clear-stream($_test-output-buffered-file->buffer)
    1652     # . . push args
    1653     68/push  $_test-output-buffered-file->buffer/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     # initialize input
    1659     # . write(_test-input-stream, "30 abcd/o")
    1660     # . . push args
    1661     68/push  "30 abcd/o"/imm32
    1662     68/push  _test-input-stream/imm32
    1663     # . . call
    1664     e8/call  write/disp32
    1665     # . . discard args
    1666     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1667     # convert-data(_test-input-stream, _test-output-buffered-file)
    1668     # . . push args
    1669     68/push  _test-output-buffered-file/imm32
    1670     68/push  _test-input-stream/imm32
    1671     # . . call
    1672     e8/call  convert-data/disp32
    1673     # . . discard args
    1674     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1675     # check output
    1676     # . flush(_test-output-buffered-file)
    1677     # . . push args
    1678     68/push  _test-output-buffered-file/imm32
    1679     # . . call
    1680     e8/call  flush/disp32
    1681     # . . discard args
    1682     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1683     # . check-stream-equal(_test-output-stream, "30 abcd/o \n", msg)
    1684     # . . push args
    1685     68/push  "F - test-convert-data-byte-then-name"/imm32
    1686     68/push  "30 abcd/o \n"/imm32
    1687     68/push  _test-output-stream/imm32
    1688     # . . call
    1689     e8/call  check-stream-equal/disp32
    1690     # . . discard args
    1691     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1692     # . epilogue
    1693     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1694     5d/pop-to-ebp
    1695     c3/return
    1696
    1697 test-convert-data-multiple-words:
    1698     # Multiple words in input stream get processed one by one.
    1699     # . prologue
    1700     55/push-ebp
    1701     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1702     # setup
    1703     # . clear-stream(_test-input-stream)
    1704     # . . push args
    1705     68/push  _test-input-stream/imm32
    1706     # . . call
    1707     e8/call  clear-stream/disp32
    1708     # . . discard args
    1709     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1710     # . clear-stream(_test-output-stream)
    1711     # . . push args
    1712     68/push  _test-output-stream/imm32
    1713     # . . call
    1714     e8/call  clear-stream/disp32
    1715     # . . discard args
    1716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1717     # . clear-stream($_test-output-buffered-file->buffer)
    1718     # . . push args
    1719     68/push  $_test-output-buffered-file->buffer/imm32
    1720     # . . call
    1721     e8/call  clear-stream/disp32
    1722     # . . discard args
    1723     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1724     # initialize input
    1725     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
    1726     # . . push args
    1727     68/push  "30 abcd/o 42e1/imm32"/imm32
    1728     68/push  _test-input-stream/imm32
    1729     # . . call
    1730     e8/call  write/disp32
    1731     # . . discard args
    1732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1733     # convert-data(_test-input-stream, _test-output-buffered-file)
    1734     # . . push args
    1735     68/push  _test-output-buffered-file/imm32
    1736     68/push  _test-input-stream/imm32
    1737     # . . call
    1738     e8/call  convert-data/disp32
    1739     # . . discard args
    1740     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1741     # check output
    1742     # . flush(_test-output-buffered-file)
    1743     # . . push args
    1744     68/push  _test-output-buffered-file/imm32
    1745     # . . call
    1746     e8/call  flush/disp32
    1747     # . . discard args
    1748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1749 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    1775     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 \n", msg)
    1776     # . . push args
    1777     68/push  "F - test-convert-data-multiple-words"/imm32
    1778     68/push  "30 abcd/o e1 42 00 00 \n"/imm32
    1779     68/push  _test-output-stream/imm32
    1780     # . . call
    1781     e8/call  check-stream-equal/disp32
    1782     # . . discard args
    1783     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1784     # . epilogue
    1785     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1786     5d/pop-to-ebp
    1787     c3/return
    1788
    1789 test-convert-data-trailing-comment:
    1790     # Trailing comments in data segment get appropriately ignored.
    1791     # . prologue
    1792     55/push-ebp
    1793     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1794     # setup
    1795     # . clear-stream(_test-input-stream)
    1796     # . . push args
    1797     68/push  _test-input-stream/imm32
    1798     # . . call
    1799     e8/call  clear-stream/disp32
    1800     # . . discard args
    1801     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1802     # . clear-stream(_test-output-stream)
    1803     # . . push args
    1804     68/push  _test-output-stream/imm32
    1805     # . . call
    1806     e8/call  clear-stream/disp32
    1807     # . . discard args
    1808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1809     # . clear-stream($_test-output-buffered-file->buffer)
    1810     # . . push args
    1811     68/push  $_test-output-buffered-file->buffer/imm32
    1812     # . . call
    1813     e8/call  clear-stream/disp32
    1814     # . . discard args
    1815     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1816     # initialize input
    1817     # . write(_test-input-stream, "30/imm32 # comment")
    1818     # . . push args
    1819     68/push  "30/imm32 # comment"/imm32
    1820     68/push  _test-input-stream/imm32
    1821     # . . call
    1822     e8/call  write/disp32
    1823     # . . discard args
    1824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1825     # convert-data(_test-input-stream, _test-output-buffered-file)
    1826     # . . push args
    1827     68/push  _test-output-buffered-file/imm32
    1828     68/push  _test-input-stream/imm32
    1829     # . . call
    1830     e8/call  convert-data/disp32
    1831     # . . discard args
    1832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1833     # check output
    1834     # . flush(_test-output-buffered-file)
    1835     # . . push args
    1836     68/push  _test-output-buffered-file/imm32
    1837     # . . call
    1838     e8/call  flush/disp32
    1839     # . . discard args
    1840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1841 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    1867     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
    1868     # . . push args
    1869     68/push  "F - test-convert-data-trailing-comment"/imm32
    1870     68/push  "30 00 00 00 # comment"/imm32
    1871     68/push  _test-output-stream/imm32
    1872     # . . call
    1873     e8/call  check-stream-equal/disp32
    1874     # . . discard args
    1875     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1876     # . epilogue
    1877     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1878     5d/pop-to-ebp
    1879     c3/return
    1880
    1881 # pack an instruction, following the C++ version
    1882 #
    1883 # zero error handling at the moment (continuing to rely on the C++ version for that):
    1884 #   missing fields are always 0-filled
    1885 #   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.
    1886 #   may pick up any of duplicate operands in an instruction
    1887 #   silently drop extraneous operands
    1888 #   unceremoniously abort on non-numeric operands except disp or imm
    1889 #   opcodes must be lowercase and zero padded
    1890 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
    1891 convert-instruction:  # line: (addr stream byte), out: (addr buffered-file)
    1892     # pseudocode:
    1893     #   # some early exits
    1894     #   var word-slice = next-word(line)
    1895     #   if slice-empty?(word-slice)
    1896     #     write-stream-data(out, line)
    1897     #     return
    1898     #   if slice-starts-with?(word-slice, "#")
    1899     #     write-stream-data(out, line)
    1900     #     return
    1901     #   if slice-ends-with?(word-slice, ":")
    1902     #     write-stream-data(out, line)
    1903     #     return
    1904     #   # really convert
    1905     #   emit-opcodes(line, out)
    1906     #   emit-modrm(line, out)
    1907     #   emit-sib(line, out)
    1908     #   emit-disp(line, out)
    1909     #   emit-imm(line, out)
    1910     #   emit-line-in-comment(line, out)
    1911     #
    1912     # . prologue
    1913     55/push-ebp
    1914     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1915     # . save registers
    1916     50/push-eax
    1917     51/push-ecx
    1918     52/push-edx
    1919     # var word-slice/ecx: slice
    1920     68/push  0/imm32/end
    1921     68/push  0/imm32/start
    1922     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1923     # next-word(line, word-slice)
    1924     # . . push args
    1925     51/push-ecx
    1926     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1927     # . . call
    1928     e8/call  next-word/disp32
    1929     # . . discard args
    1930     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1931 $convert-instruction:check0:
    1932     # if (slice-empty?(word-slice)) break
    1933     # . eax = slice-empty?(word-slice)
    1934     # . . push args
    1935     51/push-ecx
    1936     # . . call
    1937     e8/call  slice-empty?/disp32
    1938     # . . discard args
    1939     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1940     # . if (eax != false) pass through
    1941     3d/compare-eax-and  0/imm32/false
    1942     75/jump-if-!=  $convert-instruction:pass-through/disp8
    1943 $convert-instruction:check1:
    1944     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
    1945     # . var start/edx: (addr byte) = word-slice->start
    1946     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    1947     # . var c/eax: byte = *start
    1948     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    1949     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
    1950     # . if (c == '#') pass through
    1951     3d/compare-eax-and  0x23/imm32/hash
    1952     74/jump-if-=  $convert-instruction:pass-through/disp8
    1953 $convert-instruction:check2:
    1954     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
    1955     # . var end/edx: (addr byte) = word-slice->end
    1956     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
    1957     # . var c/eax: byte = *(end-1)
    1958     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    1959     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
    1960     # . if (c == ':') pass through
    1961     3d/compare-eax-and  0x3a/imm32/colon
    1962     75/jump-if-!=  $convert-instruction:really-convert/disp8
    1963 $convert-instruction:pass-through:
    1964     # write-stream-data(out, line)
    1965     # . . push args
    1966     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1967     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1968     # . . call
    1969     e8/call  write-stream-data/disp32
    1970     # . . discard args
    1971     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1972     # return
    1973     eb/jump  $convert-instruction:end/disp8
    1974 $convert-instruction:really-convert:
    1975     # emit-opcodes(line, out)
    1976     # . . push args
    1977     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1978     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1979     # . . call
    1980     e8/call  emit-opcodes/disp32
    1981     # . . discard args
    1982     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1983     # emit-modrm(line, out)
    1984     # . . push args
    1985     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1986     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1987     # . . call
    1988     e8/call  emit-modrm/disp32
    1989     # . . discard args
    1990     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1991     # emit-sib(line, out)
    1992     # . . push args
    1993     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1994     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1995     # . . call
    1996     e8/call  emit-sib/disp32
    1997     # . . discard args
    1998     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1999     # emit-disp(line, out)
    2000     # . . push args
    2001     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2002     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2003     # . . call
    2004     e8/call  emit-disp/disp32
    2005     # . . discard args
    2006     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2007     # emit-imm(line, out)
    2008     # . . push args
    2009     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2010     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2011     # . . call
    2012     e8/call  emit-imm/disp32
    2013     # . . discard args
    2014     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2015     # emit-line-in-comment(line, out)
    2016     # . . push args
    2017     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2018     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2019     # . . call
    2020     e8/call  emit-line-in-comment/disp32
    2021     # . . discard args
    2022     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2023 $convert-instruction:end:
    2024     # . restore locals
    2025     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2026     # . restore registers
    2027     5a/pop-to-edx
    2028     59/pop-to-ecx
    2029     58/pop-to-eax
    2030     # . epilogue
    2031     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2032     5d/pop-to-ebp
    2033     c3/return
    2034
    2035 emit-opcodes:  # line: (addr stream byte), out: (addr buffered-file)
    2036     # opcodes occupy 1-3 bytes:
    2037     #   xx
    2038     #   0f xx
    2039     #   f2 xx
    2040     #   f3 xx
    2041     #   f2 0f xx
    2042     #   f3 0f xx
    2043     #   66 xx (hacky support for boot.subx)
    2044     #   db xx (hacky support for boot.subx)
    2045     #
    2046     # pseudocode:
    2047     #   rewind-stream(line)
    2048     #
    2049     #   var op1 = next-word(line)
    2050     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
    2051     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
    2052     #   write-slice-buffered(out, op1)
    2053     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3") && !slice-equal?(op1, "66") && !slice-equal?(op1, "db")
    2054     #     return
    2055     #
    2056     #   var op2 = next-word(line)
    2057     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
    2058     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
    2059     #   write-slice-buffered(out, op2)
    2060     #   if slice-equal?(op1, "0f")
    2061     #     return
    2062     #   if !slice-equal?(op2, "0f")
    2063     #     return
    2064     #
    2065     #   var op3 = next-word(line)
    2066     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
    2067     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
    2068     #   write-slice-buffered(out, op3)
    2069     #
    2070     # . prologue
    2071     55/push-ebp
    2072     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2073     # . save registers
    2074     50/push-eax
    2075     51/push-ecx
    2076     52/push-edx
    2077     53/push-ebx
    2078     # var op1/ecx: slice
    2079     68/push  0/imm32/end
    2080     68/push  0/imm32/start
    2081     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    2082     # var op2/edx: slice
    2083     68/push  0/imm32/end
    2084     68/push  0/imm32/start
    2085     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2086     # rewind-stream(line)
    2087     # . . push args
    2088     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2089     # . . call
    2090     e8/call  rewind-stream/disp32
    2091     # . . discard args
    2092     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2093 $emit-opcodes:op1:
    2094     # next-word(line, op1)
    2095     # . . push args
    2096     51/push-ecx
    2097     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2098     # . . call
    2099     e8/call  next-word/disp32
    2100     # . . discard args
    2101     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2102     # if (slice-empty?(op1)) return
    2103     # . eax = slice-empty?(op1)
    2104     # . . push args
    2105     51/push-ecx
    2106     # . . call
    2107     e8/call  slice-empty?/disp32
    2108     # . . discard args
    2109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2110     # . if (eax != false) return
    2111     3d/compare-eax-and  0/imm32/false
    2112     0f 85/jump-if-!=  $emit-opcodes:end/disp32
    2113     # if (slice-starts-with?(op1, "#")) return
    2114     # . var start/ebx: (addr byte) = op1->start
    2115     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # copy *ecx to ebx
    2116     # . var c/eax: byte = *start
    2117     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    2118     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
    2119     # . if (c == '#') return
    2120     3d/compare-eax-and  0x23/imm32/hash
    2121     0f 84/jump-if-=  $emit-opcodes:end/disp32
    2122     # op1 = next-token-from-slice(op1->start, op1->end, '/')
    2123     # . . push args
    2124     51/push-ecx
    2125     68/push  0x2f/imm32/slash
    2126     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
    2127     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
    2128     # . . call
    2129     e8/call  next-token-from-slice/disp32
    2130     # . . discard args
    2131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2132     # write-slice-buffered(out, op1)
    2133     # . . push args
    2134     51/push-ecx
    2135     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2136     # . . call
    2137     e8/call  write-slice-buffered/disp32
    2138     # . . discard args
    2139     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2140     # write-buffered(out, " ")
    2141     # . . push args
    2142     68/push  Space/imm32
    2143     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2144     # . . call
    2145     e8/call  write-buffered/disp32
    2146     # . . discard args
    2147     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2148     # if (slice-equal?(op1, "0f")) goto op2
    2149     # . eax = slice-equal?(op1, "0f")
    2150     # . . push args
    2151     68/push  "0f"/imm32
    2152     51/push-ecx
    2153     # . . call
    2154     e8/call  slice-equal?/disp32
    2155     # . . discard args
    2156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2157     # . if (eax != false) goto op2
    2158     3d/compare-eax-and  0/imm32/false
    2159     75/jump-if-!=  $emit-opcodes:op2/disp8
    2160     # if (slice-equal?(op1, "f2")) goto op2
    2161     # . eax = slice-equal?(op1, "f2")
    2162     # . . push args
    2163     68/push  "f2"/imm32
    2164     51/push-ecx
    2165     # . . call
    2166     e8/call  slice-equal?/disp32
    2167     # . . discard args
    2168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2169     # . if (eax != false) goto op2
    2170     3d/compare-eax-and  0/imm32/false
    2171     75/jump-if-!=  $emit-opcodes:op2/disp8
    2172     # if (slice-equal?(op1, "f3")) goto op2
    2173     # . eax = slice-equal?(op1, "f3")
    2174     # . . push args
    2175     68/push  "f3"/imm32
    2176     51/push-ecx
    2177     # . . call
    2178     e8/call  slice-equal?/disp32
    2179     # . . discard args
    2180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2181     # . if (eax != false) goto op2
    2182     3d/compare-eax-and  0/imm32/false
    2183     75/jump-if-!=  $emit-opcodes:op2/disp8
    2184     # if (slice-equal?(op1, "66")) goto op2
    2185     # . eax = slice-equal?(op1, "66")
    2186     # . . push args
    2187     68/push  "66"/imm32
    2188     51/push-ecx
    2189     # . . call
    2190     e8/call  slice-equal?/disp32
    2191     # . . discard args
    2192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2193     # . if (eax != false) goto op2
    2194     3d/compare-eax-and  0/imm32/false
    2195     75/jump-if-!=  $emit-opcodes:op2/disp8
    2196     # if (slice-equal?(op1, "db")) goto op2
    2197     # . eax = slice-equal?(op1, "db")
    2198     # . . push args
    2199     68/push  "db"/imm32
    2200     51/push-ecx
    2201     # . . call
    2202     e8/call  slice-equal?/disp32
    2203     # . . discard args
    2204     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2205     # . if (eax != false) goto op2
    2206     3d/compare-eax-and  0/imm32/false
    2207     75/jump-if-!=  $emit-opcodes:op2/disp8
    2208     # otherwise return
    2209     e9/jump  $emit-opcodes:end/disp32
    2210 $emit-opcodes:op2:
    2211     # next-word(line, op2)
    2212     # . . push args
    2213     52/push-edx
    2214     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2215     # . . call
    2216     e8/call  next-word/disp32
    2217     # . . discard args
    2218     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2219     # if (slice-empty?(op2)) return
    2220     # . eax = slice-empty?(op2)
    2221     # . . push args
    2222     52/push-edx
    2223     # . . call
    2224     e8/call  slice-empty?/disp32
    2225     # . . discard args
    2226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2227     # . if (eax != false) return
    2228     3d/compare-eax-and  0/imm32/false
    2229     0f 85/jump-if-!=  $emit-opcodes:end/disp32
    2230     # if (slice-starts-with?(op2, "#")) return
    2231     # . var start/ebx: (addr byte) = op2->start
    2232     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
    2233     # . var c/eax: byte = *start
    2234     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    2235     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
    2236     # . if (c == '#') return
    2237     3d/compare-eax-and  0x23/imm32/hash
    2238     0f 84/jump-if-=  $emit-opcodes:end/disp32
    2239     # op2 = next-token-from-slice(op2->start, op2->end, '/')
    2240     # . . push args
    2241     52/push-edx
    2242     68/push  0x2f/imm32/slash
    2243     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    2244     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    2245     # . . call
    2246     e8/call  next-token-from-slice/disp32
    2247     # . . discard args
    2248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2249     # write-slice-buffered(out, op2)
    2250     # . . push args
    2251     52/push-edx
    2252     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2253     # . . call
    2254     e8/call  write-slice-buffered/disp32
    2255     # . . discard args
    2256     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2257     # write-buffered(out, " ")
    2258     # . . push args
    2259     68/push  Space/imm32
    2260     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2261     # . . call
    2262     e8/call  write-buffered/disp32
    2263     # . . discard args
    2264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2265     # if (slice-equal?(op1, "0f")) return
    2266     # . eax = slice-equal?(op1, "0f")
    2267     # . . push args
    2268     68/push  "0f"/imm32
    2269     51/push-ecx
    2270     # . . call
    2271     e8/call  slice-equal?/disp32
    2272     # . . discard args
    2273     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2274     # . if (eax != false) return
    2275     3d/compare-eax-and  0/imm32/false
    2276     0f 85/jump-if-!=  $emit-opcodes:end/disp32
    2277     # if (!slice-equal?(op2, "0f")) return
    2278     # . eax = slice-equal?(op2, "0f")
    2279     # . . push args
    2280     68/push  "0f"/imm32
    2281     52/push-edx
    2282     # . . call
    2283     e8/call  slice-equal?/disp32
    2284     # . . discard args
    2285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2286     # . if (eax == false) return
    2287     3d/compare-eax-and  0/imm32/false
    2288     0f 84/jump-if-=  $emit-opcodes:end/disp32
    2289 $emit-opcodes:op3:
    2290     # next-word(line, op3)  # reuse op2/edx
    2291     # . . push args
    2292     52/push-edx
    2293     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2294     # . . call
    2295     e8/call  next-word/disp32
    2296     # . . discard args
    2297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2298     # if (slice-empty?(op3)) return
    2299     # . eax = slice-empty?(op3)
    2300     # . . push args
    2301     52/push-edx
    2302     # . . call
    2303     e8/call  slice-empty?/disp32
    2304     # . . discard args
    2305     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2306     # . if (eax != false) return
    2307     3d/compare-eax-and  0/imm32/false
    2308     0f 85/jump-if-!=  $emit-opcodes:end/disp32
    2309     # if (slice-starts-with?(op3, "#")) return
    2310     # . var start/ebx: (addr byte) = op2->start
    2311     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
    2312     # . var c/eax: byte = *start
    2313     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    2314     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
    2315     # . if (c == '#') return
    2316     3d/compare-eax-and  0x23/imm32/hash
    2317     0f 84/jump-if-=  $emit-opcodes:end/disp32
    2318     # op3 = next-token-from-slice(op3->start, op3->end, '/')
    2319     # . . push args
    2320     52/push-edx
    2321     68/push  0x2f/imm32/slash
    2322     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    2323     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    2324     # . . call
    2325     e8/call  next-token-from-slice/disp32
    2326     # . . discard args
    2327     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2328     # write-slice-buffered(out, op3)
    2329     # . . push args
    2330     52/push-edx
    2331     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2332     # . . call
    2333     e8/call  write-slice-buffered/disp32
    2334     # . . discard args
    2335     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2336     # write-buffered(out, " ")
    2337     # . . push args
    2338     68/push  Space/imm32
    2339     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2340     # . . call
    2341     e8/call  write-buffered/disp32
    2342     # . . discard args
    2343     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2344 $emit-opcodes:end:
    2345     # . restore locals
    2346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2347     # . restore registers
    2348     5b/pop-to-ebx
    2349     5a/pop-to-edx
    2350     59/pop-to-ecx
    2351     58/pop-to-eax
    2352     # . epilogue
    2353     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2354     5d/pop-to-ebp
    2355     c3/return
    2356
    2357 emit-modrm:  # line: (addr stream byte), out: (addr buffered-file)
    2358     # pseudocode:
    2359     #   rewind-stream(line)
    2360     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
    2361     #   var word-slice: slice
    2362     #   while true
    2363     #     word-slice = next-word(line)
    2364     #     if (slice-empty?(word-slice)) break
    2365     #     if (slice-starts-with?(word-slice, "#")) break
    2366     #     if (has-metadata?(word-slice, "mod"))
    2367     #       mod = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
    2368     #       has-modrm? = true
    2369     #     else if has-metadata?(word-slice, "rm32") or has-metadata?(word-slice, "xm32")
    2370     #       rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
    2371     #       has-modrm? = true
    2372     #     else if has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "x32") or has-metadata?(word-slice, "subop")
    2373     #       r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
    2374     #       has-modrm? = true
    2375     #   if has-modrm?
    2376     #     var modrm = mod & 0b11
    2377     #     modrm <<= 3
    2378     #     modrm |= r32 & 0b111
    2379     #     modrm <<= 3
    2380     #     modrm |= rm32 & 0b111
    2381     #     emit-hex(out, modrm, 1)
    2382     #
    2383     # . prologue
    2384     55/push-ebp
    2385     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2386     # . save registers
    2387     50/push-eax
    2388     51/push-ecx
    2389     52/push-edx
    2390     53/push-ebx
    2391     56/push-esi
    2392     57/push-edi
    2393     # var word-slice/ecx: slice
    2394     68/push  0/imm32/end
    2395     68/push  0/imm32/start
    2396     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    2397     # var has-modrm?/edx: boolean = false
    2398     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
    2399     # var mod/ebx: byte = 0
    2400     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    2401     # var rm32/esi: byte = 0
    2402     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
    2403     # var r32/edi: byte = 0
    2404     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
    2405     # rewind-stream(line)
    2406     # . . push args
    2407     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2408     # . . call
    2409     e8/call  rewind-stream/disp32
    2410     # . . discard args
    2411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2412 +-- 33 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
    2445 $emit-modrm:loop:
    2446     # next-word(line, word-slice)
    2447     # . . push args
    2448     51/push-ecx
    2449     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2450     # . . call
    2451     e8/call  next-word/disp32
    2452     # . . discard args
    2453     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2454 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
    2494 $emit-modrm:check0:
    2495     # if (slice-empty?(word-slice)) break
    2496     # . eax = slice-empty?(word-slice)
    2497     # . . push args
    2498     51/push-ecx
    2499     # . . call
    2500     e8/call  slice-empty?/disp32
    2501     # . . discard args
    2502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2503     # . if (eax != false) pass through
    2504     3d/compare-eax-and  0/imm32/false
    2505     0f 85/jump-if-!=  $emit-modrm:break/disp32
    2506 $emit-modrm:check1:
    2507     # if (slice-starts-with?(word-slice, "#")) break
    2508     # . spill edx
    2509     52/push-edx
    2510     # . var start/edx: (addr byte) = word-slice->start
    2511     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    2512     # . var c/eax: byte = *start
    2513     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    2514     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
    2515     # . restore edx
    2516     5a/pop-to-edx
    2517     # . if (c == '#') pass through
    2518     3d/compare-eax-and  0x23/imm32/hash
    2519     0f 84/jump-if-=  $emit-modrm:break/disp32
    2520 $emit-modrm:check-for-mod:
    2521     # if (has-metadata?(word-slice, "mod"))
    2522     # . eax = has-metadata?(ecx, "mod")
    2523     # . . push args
    2524     68/push  "mod"/imm32
    2525     51/push-ecx
    2526     # . . call
    2527     e8/call  has-metadata?/disp32
    2528     # . . discard args
    2529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2530     # . if (eax == false) goto next check
    2531     3d/compare-eax-and  0/imm32/false
    2532     74/jump-if-=  $emit-modrm:check-for-rm32/disp8
    2533 $emit-modrm:mod:
    2534     # mod = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2535     # . eax = parse-datum-of-word(word-slice)
    2536     # . . push args
    2537     51/push-ecx
    2538     # . . call
    2539     e8/call  parse-datum-of-word/disp32
    2540     # . . discard args
    2541     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2542     # . mod = eax
    2543     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
    2544     # has-modrm? = true
    2545     ba/copy-to-edx  1/imm32/true
    2546     # continue
    2547     e9/jump  $emit-modrm:loop/disp32
    2548 $emit-modrm:check-for-rm32:
    2549     # if (has-metadata?(word-slice, "rm32"))
    2550     # . eax = has-metadata?(ecx, "rm32")
    2551     # . . push args
    2552     68/push  "rm32"/imm32
    2553     51/push-ecx
    2554     # . . call
    2555     e8/call  has-metadata?/disp32
    2556     # . . discard args
    2557     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2558     # . if (eax == false) goto next check
    2559     3d/compare-eax-and  0/imm32/false
    2560     74/jump-if-=  $emit-modrm:check-for-xm32/disp8
    2561 $emit-modrm:rm32:
    2562     # rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2563     # . eax = parse-datum-of-word(word-slice)
    2564     # . . push args
    2565     51/push-ecx
    2566     # . . call
    2567     e8/call  parse-datum-of-word/disp32
    2568     # . . discard args
    2569     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2570     # . rm32 = eax
    2571     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
    2572     # has-modrm? = true
    2573     ba/copy-to-edx  1/imm32/true
    2574     # continue
    2575     e9/jump  $emit-modrm:loop/disp32
    2576 $emit-modrm:check-for-xm32:
    2577     # if (has-metadata?(word-slice, "xm32"))
    2578     # . eax = has-metadata?(ecx, "xm32")
    2579     # . . push args
    2580     68/push  "xm32"/imm32
    2581     51/push-ecx
    2582     # . . call
    2583     e8/call  has-metadata?/disp32
    2584     # . . discard args
    2585     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2586     # . if (eax == false) goto next check
    2587     3d/compare-eax-and  0/imm32/false
    2588     74/jump-if-=  $emit-modrm:check-for-r32/disp8
    2589 $emit-modrm:xm32:
    2590     # rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2591     # . eax = parse-datum-of-word(word-slice)
    2592     # . . push args
    2593     51/push-ecx
    2594     # . . call
    2595     e8/call  parse-datum-of-word/disp32
    2596     # . . discard args
    2597     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2598     # . rm32 = eax
    2599     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
    2600     # has-modrm? = true
    2601     ba/copy-to-edx  1/imm32/true
    2602     # continue
    2603     e9/jump  $emit-modrm:loop/disp32
    2604 $emit-modrm:check-for-r32:
    2605     # if (has-metadata?(word-slice, "r32"))
    2606     # . eax = has-metadata?(ecx, "r32")
    2607     # . . push args
    2608     68/push  "r32"/imm32
    2609     51/push-ecx
    2610     # . . call
    2611     e8/call  has-metadata?/disp32
    2612     # . . discard args
    2613     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2614     # . if (eax == false) goto next check
    2615     3d/compare-eax-and  0/imm32/false
    2616     74/jump-if-=  $emit-modrm:check-for-x32/disp8
    2617 $emit-modrm:r32:
    2618     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2619     # . eax = parse-datum-of-word(word-slice)
    2620     # . . push args
    2621     51/push-ecx
    2622     # . . call
    2623     e8/call  parse-datum-of-word/disp32
    2624     # . . discard args
    2625     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2626     # . r32 = eax
    2627     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
    2628     # has-modrm? = true
    2629     ba/copy-to-edx  1/imm32/true
    2630     # continue
    2631     e9/jump  $emit-modrm:loop/disp32
    2632 $emit-modrm:check-for-x32:
    2633     # if (has-metadata?(word-slice, "x32"))
    2634     # . eax = has-metadata?(ecx, "x32")
    2635     # . . push args
    2636     68/push  "x32"/imm32
    2637     51/push-ecx
    2638     # . . call
    2639     e8/call  has-metadata?/disp32
    2640     # . . discard args
    2641     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2642     # . if (eax == false) goto next check
    2643     3d/compare-eax-and  0/imm32/false
    2644     74/jump-if-=  $emit-modrm:check-for-subop/disp8
    2645 $emit-modrm:x32:
    2646     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2647     # . eax = parse-datum-of-word(word-slice)
    2648     # . . push args
    2649     51/push-ecx
    2650     # . . call
    2651     e8/call  parse-datum-of-word/disp32
    2652     # . . discard args
    2653     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2654     # . r32 = eax
    2655     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
    2656     # has-modrm? = true
    2657     ba/copy-to-edx  1/imm32/true
    2658     # continue
    2659     e9/jump  $emit-modrm:loop/disp32
    2660 $emit-modrm:check-for-subop:
    2661     # if (has-metadata?(word-slice, "subop"))
    2662     # . eax = has-metadata?(ecx, "subop")
    2663     # . . push args
    2664     68/push  "subop"/imm32
    2665     51/push-ecx
    2666     # . . call
    2667     e8/call  has-metadata?/disp32
    2668     # . . discard args
    2669     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2670     # . if (eax == false) loop
    2671     3d/compare-eax-and  0/imm32/false
    2672     0f 84/jump-if-=  $emit-modrm:loop/disp32
    2673 $emit-modrm:subop:
    2674     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2675     # . eax = parse-datum-of-word(word-slice)
    2676     # . . push args
    2677     51/push-ecx
    2678     # . . call
    2679     e8/call  parse-datum-of-word/disp32
    2680     # . . discard args
    2681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2682     # . r32 = eax
    2683     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
    2684     # has-modrm? = true
    2685     ba/copy-to-edx  1/imm32/true
    2686     # continue
    2687     e9/jump  $emit-modrm:loop/disp32
    2688 $emit-modrm:break:
    2689     # if (!has-modrm?) return
    2690     81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
    2691     74/jump-if-=  $emit-modrm:end/disp8
    2692 $emit-modrm:calculate:
    2693     # var modrm/ebx: byte = mod & 0b11
    2694     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
    2695     # modrm <<= 3
    2696     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
    2697     # modrm |= r32 & 0b111
    2698     81          4/subop/and         3/mod/direct    7/rm32/edi    .           .             .           .           .               7/imm32/0b111     # bitwise and of edi
    2699     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           7/r32/edi   .               .                 # ebx = bitwise OR with edi
    2700     # modrm <<= 3
    2701     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
    2702     # modrm |= rm32 & 0b111
    2703     81          4/subop/and         3/mod/direct    6/rm32/esi    .           .             .           .           .               7/imm32/0b111     # bitwise and of esi
    2704     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           6/r32/esi   .               .                 # ebx = bitwise OR with esi
    2705 $emit-modrm:emit:
    2706     # emit-hex(out, modrm, 1)
    2707     # . . push args
    2708     68/push  1/imm32
    2709     53/push-ebx
    2710     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2711     # . . call
    2712     e8/call  emit-hex/disp32
    2713     # . . discard args
    2714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2715 $emit-modrm:end:
    2716     # . restore locals
    2717     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2718     # . restore registers
    2719     5f/pop-to-edi
    2720     5e/pop-to-esi
    2721     5b/pop-to-ebx
    2722     5a/pop-to-edx
    2723     59/pop-to-ecx
    2724     58/pop-to-eax
    2725     # . epilogue
    2726     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2727     5d/pop-to-ebp
    2728     c3/return
    2729
    2730 emit-sib:  # line: (addr stream byte), out: (addr buffered-file)
    2731     # pseudocode:
    2732     #   var has-sib? = false, base = 0, index = 0, scale = 0
    2733     #   var word-slice: slice
    2734     #   while true
    2735     #     word-slice = next-word(line)
    2736     #     if (slice-empty?(word-slice)) break
    2737     #     if (slice-starts-with?(word-slice, "#")) break
    2738     #     if (has-metadata?(word-slice, "base")
    2739     #       base = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
    2740     #       has-sib? = true
    2741     #     else if (has-metadata?(word-slice, "index")
    2742     #       index = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
    2743     #       has-sib? = true
    2744     #     else if (has-metadata?(word-slice, "scale")
    2745     #       scale = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
    2746     #       has-sib? = true
    2747     #   if has-sib?
    2748     #     var sib = scale & 0b11
    2749     #     sib <<= 3
    2750     #     sib |= index & 0b111
    2751     #     sib <<= 3
    2752     #     sib |= base & 0b111
    2753     #     emit-hex(out, sib, 1)
    2754     #
    2755     # . prologue
    2756     55/push-ebp
    2757     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2758     # . save registers
    2759     50/push-eax
    2760     51/push-ecx
    2761     52/push-edx
    2762     53/push-ebx
    2763     56/push-esi
    2764     57/push-edi
    2765     # var word-slice/ecx: slice
    2766     68/push  0/imm32/end
    2767     68/push  0/imm32/start
    2768     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    2769     # var has-sib?/edx: boolean = false
    2770     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
    2771     # var scale/ebx: byte = 0
    2772     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    2773     # var base/esi: byte = 0
    2774     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
    2775     # var index/edi: byte = 0
    2776     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
    2777     # rewind-stream(line)
    2778     # . . push args
    2779     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2780     # . . call
    2781     e8/call  rewind-stream/disp32
    2782     # . . discard args
    2783     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2784 $emit-sib:loop:
    2785 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
    2811     # next-word(line, word-slice)
    2812     # . . push args
    2813     51/push-ecx
    2814     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2815     # . . call
    2816     e8/call  next-word/disp32
    2817     # . . discard args
    2818     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2819 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
    2859 $emit-sib:check0:
    2860     # if (slice-empty?(word-slice)) break
    2861     # . eax = slice-empty?(word-slice)
    2862     # . . push args
    2863     51/push-ecx
    2864     # . . call
    2865     e8/call  slice-empty?/disp32
    2866     # . . discard args
    2867     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2868     # . if (eax != false) pass through
    2869     3d/compare-eax-and  0/imm32/false
    2870     0f 85/jump-if-!=  $emit-sib:break/disp32
    2871 $emit-sib:check1:
    2872     # if (slice-starts-with?(word-slice, "#")) break
    2873     # . spill edx
    2874     52/push-edx
    2875     # . var start/edx: (addr byte) = word-slice->start
    2876     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    2877     # . var c/eax: byte = *start
    2878     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    2879     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
    2880     # . restore edx
    2881     5a/pop-to-edx
    2882     # . if (c == '#') pass through
    2883     3d/compare-eax-and  0x23/imm32/hash
    2884     0f 84/jump-if-=  $emit-sib:break/disp32
    2885 $emit-sib:check-for-scale:
    2886     # if (has-metadata?(word-slice, "scale"))
    2887     # . eax = has-metadata?(ecx, "scale")
    2888     # . . push args
    2889     68/push  "scale"/imm32
    2890     51/push-ecx
    2891     # . . call
    2892     e8/call  has-metadata?/disp32
    2893     # . . discard args
    2894     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2895     # . if (eax == false) goto next check
    2896     3d/compare-eax-and  0/imm32/false
    2897     74/jump-if-=  $emit-sib:check-for-base/disp8
    2898 $emit-sib:scale:
    2899     # scale = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2900     # . eax = parse-datum-of-word(word-slice)
    2901     # . . push args
    2902     51/push-ecx
    2903     # . . call
    2904     e8/call  parse-datum-of-word/disp32
    2905     # . . discard args
    2906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2907     # . scale = eax
    2908     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
    2909     # has-sib? = true
    2910     ba/copy-to-edx  1/imm32/true
    2911     # continue
    2912     e9/jump  $emit-sib:loop/disp32
    2913 $emit-sib:check-for-base:
    2914     # if (has-metadata?(word-slice, "base"))
    2915     # . eax = has-metadata?(ecx, "base")
    2916     # . . push args
    2917     68/push  "base"/imm32
    2918     51/push-ecx
    2919     # . . call
    2920     e8/call  has-metadata?/disp32
    2921     # . . discard args
    2922     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2923     # . if (eax == false) goto next check
    2924     3d/compare-eax-and  0/imm32/false
    2925     74/jump-if-=  $emit-sib:check-for-index/disp8
    2926 $emit-sib:base:
    2927     # base = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2928     # . eax = parse-datum-of-word(word-slice)
    2929     # . . push args
    2930     51/push-ecx
    2931     # . . call
    2932     e8/call  parse-datum-of-word/disp32
    2933     # . . discard args
    2934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2935     # . base = eax
    2936     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
    2937     # has-sib? = true
    2938     ba/copy-to-edx  1/imm32/true
    2939     # continue
    2940     e9/jump  $emit-sib:loop/disp32
    2941 $emit-sib:check-for-index:
    2942     # if (has-metadata?(word-slice, "index"))
    2943     # . eax = has-metadata?(ecx, "index")
    2944     # . . push args
    2945     68/push  "index"/imm32
    2946     51/push-ecx
    2947     # . . call
    2948     e8/call  has-metadata?/disp32
    2949     # . . discard args
    2950     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2951     # . if (eax == false) loop
    2952     3d/compare-eax-and  0/imm32/false
    2953     0f 84/jump-if-=  $emit-sib:loop/disp32
    2954 $emit-sib:index:
    2955     # index = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
    2956     # . eax = parse-datum-of-word(word-slice)
    2957     # . . push args
    2958     51/push-ecx
    2959     # . . call
    2960     e8/call  parse-datum-of-word/disp32
    2961     # . . discard args
    2962     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2963     # . index = eax
    2964     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
    2965     # has-sib? = true
    2966     ba/copy-to-edx  1/imm32/true
    2967     # continue
    2968     e9/jump  $emit-sib:loop/disp32
    2969 $emit-sib:break:
    2970     # if (!has-sib?) return
    2971     81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
    2972     74/jump-if-=  $emit-sib:end/disp8
    2973 $emit-sib:calculate:
    2974     # var sib/ebx: byte = scale & 0b11
    2975     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
    2976     # sib <<= 2
    2977     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
    2978     # sib |= index & 0b111
    2979     81          4/subop/and         3/mod/direct    7/rm32/edi    .           .             .           .           .               7/imm32/0b111     # bitwise and of edi
    2980     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           7/r32/edi   .               .                 # ebx = bitwise OR with edi
    2981     # sib <<= 3
    2982     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
    2983     # sib |= base & 0b111
    2984     81          4/subop/and         3/mod/direct    6/rm32/esi    .           .             .           .           .               7/imm32/0b111     # bitwise and of esi
    2985     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           6/r32/esi   .               .                 # ebx = bitwise OR with esi
    2986 $emit-sib:emit:
    2987     # emit-hex(out, sib, 1)
    2988     # . . push args
    2989     68/push  1/imm32
    2990     53/push-ebx
    2991     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2992     # . . call
    2993     e8/call  emit-hex/disp32
    2994     # . . discard args
    2995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2996 $emit-sib:end:
    2997     # . restore locals
    2998     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2999     # . restore registers
    3000     5f/pop-to-edi
    3001     5e/pop-to-esi
    3002     5b/pop-to-ebx
    3003     5a/pop-to-edx
    3004     59/pop-to-ecx
    3005     58/pop-to-eax
    3006     # . epilogue
    3007     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3008     5d/pop-to-ebp
    3009     c3/return
    3010
    3011 emit-disp:  # line: (addr stream byte), out: (addr buffered-file)
    3012     # pseudocode:
    3013     #   rewind-stream(line)
    3014     #   var word-slice: slice
    3015     #   while true
    3016     #     word-slice = next-word(line)
    3017     #     if (slice-empty?(word-slice)) break
    3018     #     if (slice-starts-with?(word-slice, "#")) break
    3019     #     if has-metadata?(word-slice, "disp32")
    3020     #       emit(out, word-slice, 4)
    3021     #       break
    3022     #     if has-metadata?(word-slice, "disp16")
    3023     #       emit(out, word-slice, 2)
    3024     #       break
    3025     #     if has-metadata?(word-slice, "disp8")
    3026     #       emit(out, word-slice, 1)
    3027     #       break
    3028     #
    3029     # . prologue
    3030     55/push-ebp
    3031     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3032     # . save registers
    3033     50/push-eax
    3034     51/push-ecx
    3035     52/push-edx
    3036     # var word-slice/ecx: slice
    3037     68/push  0/imm32/end
    3038     68/push  0/imm32/start
    3039     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    3040     # rewind-stream(line)
    3041     # . . push args
    3042     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3043     # . . call
    3044     e8/call  rewind-stream/disp32
    3045     # . . discard args
    3046     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3047 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
    3073 $emit-disp:loop:
    3074     # next-word(line, word-slice)
    3075     # . . push args
    3076     51/push-ecx
    3077     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3078     # . . call
    3079     e8/call  next-word/disp32
    3080     # . . discard args
    3081     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3082 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
    3122 $emit-disp:check0:
    3123     # if (slice-empty?(word-slice)) break
    3124     # . eax = slice-empty?(word-slice)
    3125     # . . push args
    3126     51/push-ecx
    3127     # . . call
    3128     e8/call  slice-empty?/disp32
    3129     # . . discard args
    3130     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3131     # . if (eax != false) pass through
    3132     3d/compare-eax-and  0/imm32/false
    3133     0f 85/jump-if-!=  $emit-disp:break/disp32
    3134 $emit-disp:check1:
    3135     # if (slice-starts-with?(word-slice, "#")) break
    3136     # . var start/edx: (addr byte) = word-slice->start
    3137     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    3138     # . var c/eax: byte = *start
    3139     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    3140     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
    3141     # . if (c == '#') break
    3142     3d/compare-eax-and  0x23/imm32/hash
    3143     0f 84/jump-if-=  $emit-disp:break/disp32
    3144 $emit-disp:check-for-disp32:
    3145     # if (has-metadata?(word-slice, "disp32"))
    3146     # . eax = has-metadata?(ecx, "disp32")
    3147     # . . push args
    3148     68/push  "disp32"/imm32
    3149     51/push-ecx
    3150     # . . call
    3151     e8/call  has-metadata?/disp32
    3152     # . . discard args
    3153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3154     # . if (eax == false) goto next check
    3155     3d/compare-eax-and  0/imm32/false
    3156     74/jump-if-=  $emit-disp:check-for-disp16/disp8
    3157 $emit-disp:disp32:
    3158     # emit(out, word-slice, 4)
    3159     # . . push args
    3160     68/push  4/imm32
    3161     51/push-ecx
    3162     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3163     # . . call
    3164     e8/call  emit/disp32
    3165     # . . discard args
    3166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3167     # break
    3168     e9/jump  $emit-disp:break/disp32
    3169 $emit-disp:check-for-disp16:
    3170     # else if (has-metadata?(word-slice, "disp16"))
    3171     # . eax = has-metadata?(ecx, "disp16")
    3172     # . . push args
    3173     68/push  "disp16"/imm32
    3174     51/push-ecx
    3175     # . . call
    3176     e8/call  has-metadata?/disp32
    3177     # . . discard args
    3178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3179     # . if (eax == false) goto next check
    3180     3d/compare-eax-and  0/imm32/false
    3181     74/jump-if-=  $emit-disp:check-for-disp8/disp8
    3182 $emit-disp:disp16:
    3183     # emit(out, word-slice, 2)
    3184     # . . push args
    3185     68/push  2/imm32
    3186     51/push-ecx
    3187     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3188     # . . call
    3189     e8/call  emit/disp32
    3190     # . . discard args
    3191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3192     # break
    3193     e9/jump  $emit-disp:break/disp32
    3194 $emit-disp:check-for-disp8:
    3195     # if (has-metadata?(word-slice, "disp8"))
    3196     # . eax = has-metadata?(ecx, "disp8")
    3197     # . . push args
    3198     68/push  "disp8"/imm32
    3199     51/push-ecx
    3200     # . . call
    3201     e8/call  has-metadata?/disp32
    3202     # . . discard args
    3203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3204     # . if (eax == false) loop
    3205     3d/compare-eax-and  0/imm32/false
    3206     0f 84/jump-if-=  $emit-disp:loop/disp32
    3207 $emit-disp:disp8:
    3208     # emit(out, word-slice, 1)
    3209     # . . push args
    3210     68/push  1/imm32
    3211     51/push-ecx
    3212     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3213     # . . call
    3214     e8/call  emit/disp32
    3215     # . . discard args
    3216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3217     # break
    3218 $emit-disp:break:
    3219     # . restore locals
    3220     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3221     # . restore registers
    3222     5a/pop-to-edx
    3223     59/pop-to-ecx
    3224     58/pop-to-eax
    3225     # . epilogue
    3226     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3227     5d/pop-to-ebp
    3228     c3/return
    3229
    3230 emit-imm:  # line: (addr stream byte), out: (addr buffered-file)
    3231     # pseudocode:
    3232     #   rewind-stream(line)
    3233     #   var word-slice: slice
    3234     #   while true
    3235     #     word-slice = next-word(line)
    3236     #     if (slice-empty?(word-slice)) break
    3237     #     if (slice-starts-with?(word-slice, "#")) break
    3238     #     if has-metadata?(word-slice, "imm32")
    3239     #       emit(out, word-slice, 4)
    3240     #       break
    3241     #     if has-metadata?(word-slice, "imm16")
    3242     #       emit(out, word-slice, 2)
    3243     #       break
    3244     #     if has-metadata?(word-slice, "imm8")
    3245     #       emit(out, word-slice, 1)
    3246     #       break
    3247     #
    3248     # . prologue
    3249     55/push-ebp
    3250     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3251     # . save registers
    3252     50/push-eax
    3253     51/push-ecx
    3254     52/push-edx
    3255     # var word-slice/ecx: slice
    3256     68/push  0/imm32/end
    3257     68/push  0/imm32/start
    3258     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    3259     # rewind-stream(line)
    3260     # . . push args
    3261     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3262     # . . call
    3263     e8/call  rewind-stream/disp32
    3264     # . . discard args
    3265     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3266 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
    3292 $emit-imm:loop:
    3293     # next-word(line, word-slice)
    3294     # . . push args
    3295     51/push-ecx
    3296     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3297     # . . call
    3298     e8/call  next-word/disp32
    3299     # . . discard args
    3300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3301 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
    3341 $emit-imm:check0:
    3342     # if (slice-empty?(word-slice)) break
    3343     # . eax = slice-empty?(word-slice)
    3344     # . . push args
    3345     51/push-ecx
    3346     # . . call
    3347     e8/call  slice-empty?/disp32
    3348     # . . discard args
    3349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3350     # . if (eax != false) pass through
    3351     3d/compare-eax-and  0/imm32/false
    3352     0f 85/jump-if-!=  $emit-imm:break/disp32
    3353 $emit-imm:check1:
    3354     # if (slice-starts-with?(word-slice, "#")) break
    3355     # . var start/edx: (addr byte) = slice->start
    3356     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    3357     # . var c/eax: byte = *start
    3358     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    3359     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
    3360     # . if (c == '#') break
    3361     3d/compare-eax-and  0x23/imm32/hash
    3362     0f 84/jump-if-=  $emit-imm:break/disp32
    3363 $emit-imm:check-for-imm32:
    3364     # if (has-metadata?(word-slice, "imm32"))
    3365     # . eax = has-metadata?(ecx, "imm32")
    3366     # . . push args
    3367     68/push  "imm32"/imm32
    3368     51/push-ecx
    3369     # . . call
    3370     e8/call  has-metadata?/disp32
    3371     # . . discard args
    3372     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3373     # . if (eax == false) goto next check
    3374     3d/compare-eax-and  0/imm32/false
    3375     74/jump-if-=  $emit-imm:check-for-imm16/disp8
    3376 $emit-imm:imm32:
    3377     # emit(out, word-slice, 4)
    3378     # . . push args
    3379     68/push  4/imm32
    3380     51/push-ecx
    3381     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3382     # . . call
    3383     e8/call  emit/disp32
    3384     # . . discard args
    3385     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3386     # break
    3387     e9/jump  $emit-imm:break/disp32
    3388 $emit-imm:check-for-imm16:
    3389     # if (has-metadata?(word-slice, "imm16"))
    3390     # . eax = has-metadata?(ecx, "imm16")
    3391     # . . push args
    3392     68/push  "imm16"/imm32
    3393     51/push-ecx
    3394     # . . call
    3395     e8/call  has-metadata?/disp32
    3396     # . . discard args
    3397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3398     # . if (eax == false) goto next check
    3399     3d/compare-eax-and  0/imm32/false
    3400     74/jump-if-=  $emit-imm:check-for-imm8/disp8
    3401 $emit-imm:imm16:
    3402     # emit(out, word-slice, 2)
    3403     # . . push args
    3404     68/push  2/imm32
    3405     51/push-ecx
    3406     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3407     # . . call
    3408     e8/call  emit/disp32
    3409     # . . discard args
    3410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3411     # break
    3412     e9/jump  $emit-imm:break/disp32
    3413 $emit-imm:check-for-imm8:
    3414     # if (has-metadata?(word-slice, "imm8"))
    3415     # . eax = has-metadata?(ecx, "imm8")
    3416     # . . push args
    3417     68/push  "imm8"/imm32
    3418     51/push-ecx
    3419     # . . call
    3420     e8/call  has-metadata?/disp32
    3421     # . . discard args
    3422     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3423     # . if (eax == false) loop
    3424     3d/compare-eax-and  0/imm32/false
    3425     0f 84/jump-if-=  $emit-imm:loop/disp32
    3426 $emit-imm:imm8:
    3427     # emit(out, word-slice, 1)
    3428     # . . push args
    3429     68/push  1/imm32
    3430     51/push-ecx
    3431     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3432     # . . call
    3433     e8/call  emit/disp32
    3434     # . . discard args
    3435     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3436     # break
    3437 $emit-imm:break:
    3438     # . restore locals
    3439     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3440     # . restore registers
    3441     5a/pop-to-edx
    3442     59/pop-to-ecx
    3443     58/pop-to-eax
    3444     # . epilogue
    3445     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3446     5d/pop-to-ebp
    3447     c3/return
    3448
    3449 emit-line-in-comment:  # line: (addr stream byte), out: (addr buffered-file)
    3450     # . prologue
    3451     55/push-ebp
    3452     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3453     # write-buffered(out, " # ")
    3454     # . . push args
    3455     68/push  " # "/imm32
    3456     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3457     # . . call
    3458     e8/call  write-buffered/disp32
    3459     # . . discard args
    3460     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3461     # write-stream-data(out, line)
    3462     # . . push args
    3463     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3464     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3465     # . . call
    3466     e8/call  write-stream-data/disp32
    3467     # . . discard args
    3468     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3469 $emit-line-in-comment:end:
    3470     # . epilogue
    3471     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3472     5d/pop-to-ebp
    3473     c3/return
    3474
    3475 test-convert-instruction-passes-comments-through:
    3476     # if a line starts with '#', pass it along unchanged
    3477     # . prologue
    3478     55/push-ebp
    3479     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3480     # setup
    3481     # . clear-stream(_test-input-stream)
    3482     # . . push args
    3483     68/push  _test-input-stream/imm32
    3484     # . . call
    3485     e8/call  clear-stream/disp32
    3486     # . . discard args
    3487     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3488     # . clear-stream(_test-output-stream)
    3489     # . . push args
    3490     68/push  _test-output-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-buffered-file->buffer)
    3496     # . . push args
    3497     68/push  $_test-output-buffered-file->buffer/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     # initialize input
    3503     # . write(_test-input-stream, "# abcd")
    3504     # . . push args
    3505     68/push  "# abcd"/imm32
    3506     68/push  _test-input-stream/imm32
    3507     # . . call
    3508     e8/call  write/disp32
    3509     # . . discard args
    3510     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3511     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3512     # . . push args
    3513     68/push  _test-output-buffered-file/imm32
    3514     68/push  _test-input-stream/imm32
    3515     # . . call
    3516     e8/call  convert-instruction/disp32
    3517     # . . discard args
    3518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3519     # check that the line just passed through
    3520     # . flush(_test-output-buffered-file)
    3521     # . . push args
    3522     68/push  _test-output-buffered-file/imm32
    3523     # . . call
    3524     e8/call  flush/disp32
    3525     # . . discard args
    3526     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3527     # . check-stream-equal(_test-output-stream, "# abcd", msg)
    3528     # . . push args
    3529     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
    3530     68/push  "# abcd"/imm32
    3531     68/push  _test-output-stream/imm32
    3532     # . . call
    3533     e8/call  check-stream-equal/disp32
    3534     # . . discard args
    3535     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3536     # . epilogue
    3537     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3538     5d/pop-to-ebp
    3539     c3/return
    3540
    3541 test-convert-instruction-passes-labels-through:
    3542     # if the first word ends with ':', pass along the entire line unchanged
    3543     # . prologue
    3544     55/push-ebp
    3545     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3546     # setup
    3547     # . clear-stream(_test-input-stream)
    3548     # . . push args
    3549     68/push  _test-input-stream/imm32
    3550     # . . call
    3551     e8/call  clear-stream/disp32
    3552     # . . discard args
    3553     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3554     # . clear-stream(_test-output-stream)
    3555     # . . push args
    3556     68/push  _test-output-stream/imm32
    3557     # . . call
    3558     e8/call  clear-stream/disp32
    3559     # . . discard args
    3560     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3561     # . clear-stream($_test-output-buffered-file->buffer)
    3562     # . . push args
    3563     68/push  $_test-output-buffered-file->buffer/imm32
    3564     # . . call
    3565     e8/call  clear-stream/disp32
    3566     # . . discard args
    3567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3568     # initialize input
    3569     # . write(_test-input-stream, "ab: # cd")
    3570     # . . push args
    3571     68/push  "ab: # cd"/imm32
    3572     68/push  _test-input-stream/imm32
    3573     # . . call
    3574     e8/call  write/disp32
    3575     # . . discard args
    3576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3577     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3578     # . . push args
    3579     68/push  _test-output-buffered-file/imm32
    3580     68/push  _test-input-stream/imm32
    3581     # . . call
    3582     e8/call  convert-instruction/disp32
    3583     # . . discard args
    3584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3585     # check that the line just passed through
    3586     # . flush(_test-output-buffered-file)
    3587     # . . push args
    3588     68/push  _test-output-buffered-file/imm32
    3589     # . . call
    3590     e8/call  flush/disp32
    3591     # . . discard args
    3592     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3593     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
    3594     # . . push args
    3595     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
    3596     68/push  "ab: # cd"/imm32
    3597     68/push  _test-output-stream/imm32
    3598     # . . call
    3599     e8/call  check-stream-equal/disp32
    3600     # . . discard args
    3601     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3602     # . epilogue
    3603     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3604     5d/pop-to-ebp
    3605     c3/return
    3606
    3607 test-convert-instruction-handles-single-opcode:
    3608     # if the instruction consists of a single opcode, strip its metadata and pass it along
    3609     # . prologue
    3610     55/push-ebp
    3611     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3612     # setup
    3613     # . clear-stream(_test-input-stream)
    3614     # . . push args
    3615     68/push  _test-input-stream/imm32
    3616     # . . call
    3617     e8/call  clear-stream/disp32
    3618     # . . discard args
    3619     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3620     # . clear-stream(_test-output-stream)
    3621     # . . push args
    3622     68/push  _test-output-stream/imm32
    3623     # . . call
    3624     e8/call  clear-stream/disp32
    3625     # . . discard args
    3626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3627     # . clear-stream($_test-output-buffered-file->buffer)
    3628     # . . push args
    3629     68/push  $_test-output-buffered-file->buffer/imm32
    3630     # . . call
    3631     e8/call  clear-stream/disp32
    3632     # . . discard args
    3633     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3634     # initialize input
    3635     # . write(_test-input-stream, "ab/cd # comment")
    3636     # . . push args
    3637     68/push  "ab/cd # comment"/imm32
    3638     68/push  _test-input-stream/imm32
    3639     # . . call
    3640     e8/call  write/disp32
    3641     # . . discard args
    3642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3643     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3644     # . . push args
    3645     68/push  _test-output-buffered-file/imm32
    3646     68/push  _test-input-stream/imm32
    3647     # . . call
    3648     e8/call  convert-instruction/disp32
    3649     # . . discard args
    3650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3651     # check output
    3652     # . flush(_test-output-buffered-file)
    3653     # . . push args
    3654     68/push  _test-output-buffered-file/imm32
    3655     # . . call
    3656     e8/call  flush/disp32
    3657     # . . discard args
    3658     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3659 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    3685     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
    3686     # . . push args
    3687     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
    3688     68/push  "ab  # ab/cd # comment"/imm32
    3689     68/push  _test-output-stream/imm32
    3690     # . . call
    3691     e8/call  check-stream-equal/disp32
    3692     # . . discard args
    3693     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3694     # . epilogue
    3695     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3696     5d/pop-to-ebp
    3697     c3/return
    3698
    3699 test-convert-instruction-handles-0f-opcode:
    3700     # if the instruction starts with 0f opcode, include a second opcode
    3701     # . prologue
    3702     55/push-ebp
    3703     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3704     # setup
    3705     # . clear-stream(_test-input-stream)
    3706     # . . push args
    3707     68/push  _test-input-stream/imm32
    3708     # . . call
    3709     e8/call  clear-stream/disp32
    3710     # . . discard args
    3711     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3712     # . clear-stream(_test-output-stream)
    3713     # . . push args
    3714     68/push  _test-output-stream/imm32
    3715     # . . call
    3716     e8/call  clear-stream/disp32
    3717     # . . discard args
    3718     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3719     # . clear-stream($_test-output-buffered-file->buffer)
    3720     # . . push args
    3721     68/push  $_test-output-buffered-file->buffer/imm32
    3722     # . . call
    3723     e8/call  clear-stream/disp32
    3724     # . . discard args
    3725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3726     # initialize input
    3727     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
    3728     # . . push args
    3729     68/push  "0f/m1 ab/m2 # comment"/imm32
    3730     68/push  _test-input-stream/imm32
    3731     # . . call
    3732     e8/call  write/disp32
    3733     # . . discard args
    3734     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3735     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3736     # . . push args
    3737     68/push  _test-output-buffered-file/imm32
    3738     68/push  _test-input-stream/imm32
    3739     # . . call
    3740     e8/call  convert-instruction/disp32
    3741     # . . discard args
    3742     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3743     # check output
    3744     # . flush(_test-output-buffered-file)
    3745     # . . push args
    3746     68/push  _test-output-buffered-file/imm32
    3747     # . . call
    3748     e8/call  flush/disp32
    3749     # . . discard args
    3750     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3751 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    3777     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
    3778     # . . push args
    3779     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
    3780     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
    3781     68/push  _test-output-stream/imm32
    3782     # . . call
    3783     e8/call  check-stream-equal/disp32
    3784     # . . discard args
    3785     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3786     # . epilogue
    3787     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3788     5d/pop-to-ebp
    3789     c3/return
    3790
    3791 test-convert-instruction-handles-f2-opcode:
    3792     # if the instruction starts with f2 opcode, include a second opcode
    3793     # . prologue
    3794     55/push-ebp
    3795     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3796     # setup
    3797     # . clear-stream(_test-input-stream)
    3798     # . . push args
    3799     68/push  _test-input-stream/imm32
    3800     # . . call
    3801     e8/call  clear-stream/disp32
    3802     # . . discard args
    3803     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3804     # . clear-stream(_test-output-stream)
    3805     # . . push args
    3806     68/push  _test-output-stream/imm32
    3807     # . . call
    3808     e8/call  clear-stream/disp32
    3809     # . . discard args
    3810     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3811     # . clear-stream($_test-output-buffered-file->buffer)
    3812     # . . push args
    3813     68/push  $_test-output-buffered-file->buffer/imm32
    3814     # . . call
    3815     e8/call  clear-stream/disp32
    3816     # . . discard args
    3817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3818     # initialize input
    3819     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
    3820     # . . push args
    3821     68/push  "f2/m1 ab/m2 # comment"/imm32
    3822     68/push  _test-input-stream/imm32
    3823     # . . call
    3824     e8/call  write/disp32
    3825     # . . discard args
    3826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3827     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3828     # . . push args
    3829     68/push  _test-output-buffered-file/imm32
    3830     68/push  _test-input-stream/imm32
    3831     # . . call
    3832     e8/call  convert-instruction/disp32
    3833     # . . discard args
    3834     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3835     # check output
    3836     # . flush(_test-output-buffered-file)
    3837     # . . push args
    3838     68/push  _test-output-buffered-file/imm32
    3839     # . . call
    3840     e8/call  flush/disp32
    3841     # . . discard args
    3842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3843 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    3869     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
    3870     # . . push args
    3871     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
    3872     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
    3873     68/push  _test-output-stream/imm32
    3874     # . . call
    3875     e8/call  check-stream-equal/disp32
    3876     # . . discard args
    3877     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3878     # . epilogue
    3879     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3880     5d/pop-to-ebp
    3881     c3/return
    3882
    3883 test-convert-instruction-handles-f3-opcode:
    3884     # if the instruction starts with f3 opcode, include a second opcode
    3885     # . prologue
    3886     55/push-ebp
    3887     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3888     # setup
    3889     # . clear-stream(_test-input-stream)
    3890     # . . push args
    3891     68/push  _test-input-stream/imm32
    3892     # . . call
    3893     e8/call  clear-stream/disp32
    3894     # . . discard args
    3895     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3896     # . clear-stream(_test-output-stream)
    3897     # . . push args
    3898     68/push  _test-output-stream/imm32
    3899     # . . call
    3900     e8/call  clear-stream/disp32
    3901     # . . discard args
    3902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3903     # . clear-stream($_test-output-buffered-file->buffer)
    3904     # . . push args
    3905     68/push  $_test-output-buffered-file->buffer/imm32
    3906     # . . call
    3907     e8/call  clear-stream/disp32
    3908     # . . discard args
    3909     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3910     # initialize input
    3911     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
    3912     # . . push args
    3913     68/push  "f3/m1 ab/m2 # comment"/imm32
    3914     68/push  _test-input-stream/imm32
    3915     # . . call
    3916     e8/call  write/disp32
    3917     # . . discard args
    3918     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3919     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    3920     # . . push args
    3921     68/push  _test-output-buffered-file/imm32
    3922     68/push  _test-input-stream/imm32
    3923     # . . call
    3924     e8/call  convert-instruction/disp32
    3925     # . . discard args
    3926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3927     # check output
    3928     # . flush(_test-output-buffered-file)
    3929     # . . push args
    3930     68/push  _test-output-buffered-file/imm32
    3931     # . . call
    3932     e8/call  flush/disp32
    3933     # . . discard args
    3934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3935 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    3961     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
    3962     # . . push args
    3963     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
    3964     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
    3965     68/push  _test-output-stream/imm32
    3966     # . . call
    3967     e8/call  check-stream-equal/disp32
    3968     # . . discard args
    3969     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3970     # . epilogue
    3971     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3972     5d/pop-to-ebp
    3973     c3/return
    3974
    3975 test-convert-instruction-handles-f2-0f-opcode:
    3976     # if the instruction starts with f2 0f opcode, include a second opcode
    3977     # . prologue
    3978     55/push-ebp
    3979     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3980     # setup
    3981     # . clear-stream(_test-input-stream)
    3982     # . . push args
    3983     68/push  _test-input-stream/imm32
    3984     # . . call
    3985     e8/call  clear-stream/disp32
    3986     # . . discard args
    3987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3988     # . clear-stream(_test-output-stream)
    3989     # . . push args
    3990     68/push  _test-output-stream/imm32
    3991     # . . call
    3992     e8/call  clear-stream/disp32
    3993     # . . discard args
    3994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3995     # . clear-stream($_test-output-buffered-file->buffer)
    3996     # . . push args
    3997     68/push  $_test-output-buffered-file->buffer/imm32
    3998     # . . call
    3999     e8/call  clear-stream/disp32
    4000     # . . discard args
    4001     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4002     # initialize input
    4003     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
    4004     # . . push args
    4005     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
    4006     68/push  _test-input-stream/imm32
    4007     # . . call
    4008     e8/call  write/disp32
    4009     # . . discard args
    4010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4011     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4012     # . . push args
    4013     68/push  _test-output-buffered-file/imm32
    4014     68/push  _test-input-stream/imm32
    4015     # . . call
    4016     e8/call  convert-instruction/disp32
    4017     # . . discard args
    4018     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4019     # check output
    4020     # . flush(_test-output-buffered-file)
    4021     # . . push args
    4022     68/push  _test-output-buffered-file/imm32
    4023     # . . call
    4024     e8/call  flush/disp32
    4025     # . . discard args
    4026     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4027 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4053     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
    4054     # . . push args
    4055     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
    4056     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
    4057     68/push  _test-output-stream/imm32
    4058     # . . call
    4059     e8/call  check-stream-equal/disp32
    4060     # . . discard args
    4061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4062     # . epilogue
    4063     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4064     5d/pop-to-ebp
    4065     c3/return
    4066
    4067 test-convert-instruction-handles-f3-0f-opcode:
    4068     # if the instruction starts with f3 0f opcode, include a second opcode
    4069     # . prologue
    4070     55/push-ebp
    4071     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4072     # setup
    4073     # . clear-stream(_test-input-stream)
    4074     # . . push args
    4075     68/push  _test-input-stream/imm32
    4076     # . . call
    4077     e8/call  clear-stream/disp32
    4078     # . . discard args
    4079     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4080     # . clear-stream(_test-output-stream)
    4081     # . . push args
    4082     68/push  _test-output-stream/imm32
    4083     # . . call
    4084     e8/call  clear-stream/disp32
    4085     # . . discard args
    4086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4087     # . clear-stream($_test-output-buffered-file->buffer)
    4088     # . . push args
    4089     68/push  $_test-output-buffered-file->buffer/imm32
    4090     # . . call
    4091     e8/call  clear-stream/disp32
    4092     # . . discard args
    4093     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4094     # initialize input
    4095     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
    4096     # . . push args
    4097     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
    4098     68/push  _test-input-stream/imm32
    4099     # . . call
    4100     e8/call  write/disp32
    4101     # . . discard args
    4102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4103     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4104     # . . push args
    4105     68/push  _test-output-buffered-file/imm32
    4106     68/push  _test-input-stream/imm32
    4107     # . . call
    4108     e8/call  convert-instruction/disp32
    4109     # . . discard args
    4110     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4111     # check output
    4112     # . flush(_test-output-buffered-file)
    4113     # . . push args
    4114     68/push  _test-output-buffered-file/imm32
    4115     # . . call
    4116     e8/call  flush/disp32
    4117     # . . discard args
    4118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4119 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4145     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
    4146     # . . push args
    4147     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
    4148     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
    4149     68/push  _test-output-stream/imm32
    4150     # . . call
    4151     e8/call  check-stream-equal/disp32
    4152     # . . discard args
    4153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4154     # . epilogue
    4155     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4156     5d/pop-to-ebp
    4157     c3/return
    4158
    4159 test-convert-instruction-handles-unused-opcodes:
    4160     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
    4161     # . prologue
    4162     55/push-ebp
    4163     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4164     # setup
    4165     # . clear-stream(_test-input-stream)
    4166     # . . push args
    4167     68/push  _test-input-stream/imm32
    4168     # . . call
    4169     e8/call  clear-stream/disp32
    4170     # . . discard args
    4171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4172     # . clear-stream(_test-output-stream)
    4173     # . . push args
    4174     68/push  _test-output-stream/imm32
    4175     # . . call
    4176     e8/call  clear-stream/disp32
    4177     # . . discard args
    4178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4179     # . clear-stream($_test-output-buffered-file->buffer)
    4180     # . . push args
    4181     68/push  $_test-output-buffered-file->buffer/imm32
    4182     # . . call
    4183     e8/call  clear-stream/disp32
    4184     # . . discard args
    4185     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4186     # initialize input
    4187     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
    4188     # . . push args
    4189     68/push  "ab/m1 cd/m2 # comment"/imm32
    4190     68/push  _test-input-stream/imm32
    4191     # . . call
    4192     e8/call  write/disp32
    4193     # . . discard args
    4194     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4195     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4196     # . . push args
    4197     68/push  _test-output-buffered-file/imm32
    4198     68/push  _test-input-stream/imm32
    4199     # . . call
    4200     e8/call  convert-instruction/disp32
    4201     # . . discard args
    4202     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4203     # check output
    4204     # . flush(_test-output-buffered-file)
    4205     # . . push args
    4206     68/push  _test-output-buffered-file/imm32
    4207     # . . call
    4208     e8/call  flush/disp32
    4209     # . . discard args
    4210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4211 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4237     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
    4238     # . . push args
    4239     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
    4240     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
    4241     68/push  _test-output-stream/imm32
    4242     # . . call
    4243     e8/call  check-stream-equal/disp32
    4244     # . . discard args
    4245     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4246     # . epilogue
    4247     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4248     5d/pop-to-ebp
    4249     c3/return
    4250
    4251 test-convert-instruction-handles-unused-second-opcodes:
    4252     # if the second opcode isn't 0f, don't include further opcodes
    4253     # . prologue
    4254     55/push-ebp
    4255     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4256     # setup
    4257     # . clear-stream(_test-input-stream)
    4258     # . . push args
    4259     68/push  _test-input-stream/imm32
    4260     # . . call
    4261     e8/call  clear-stream/disp32
    4262     # . . discard args
    4263     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4264     # . clear-stream(_test-output-stream)
    4265     # . . push args
    4266     68/push  _test-output-stream/imm32
    4267     # . . call
    4268     e8/call  clear-stream/disp32
    4269     # . . discard args
    4270     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4271     # . clear-stream($_test-output-buffered-file->buffer)
    4272     # . . push args
    4273     68/push  $_test-output-buffered-file->buffer/imm32
    4274     # . . call
    4275     e8/call  clear-stream/disp32
    4276     # . . discard args
    4277     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4278     # initialize input
    4279     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
    4280     # . . push args
    4281     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
    4282     68/push  _test-input-stream/imm32
    4283     # . . call
    4284     e8/call  write/disp32
    4285     # . . discard args
    4286     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4287     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4288     # . . push args
    4289     68/push  _test-output-buffered-file/imm32
    4290     68/push  _test-input-stream/imm32
    4291     # . . call
    4292     e8/call  convert-instruction/disp32
    4293     # . . discard args
    4294     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4295     # check output
    4296     # . flush(_test-output-buffered-file)
    4297     # . . push args
    4298     68/push  _test-output-buffered-file/imm32
    4299     # . . call
    4300     e8/call  flush/disp32
    4301     # . . discard args
    4302     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4303 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4329     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
    4330     # . . push args
    4331     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
    4332     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
    4333     68/push  _test-output-stream/imm32
    4334     # . . call
    4335     e8/call  check-stream-equal/disp32
    4336     # . . discard args
    4337     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4338     # . epilogue
    4339     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4340     5d/pop-to-ebp
    4341     c3/return
    4342
    4343 test-convert-instruction-handles-unused-second-opcodes-2:
    4344     # if the second opcode isn't 0f, don't include further opcodes
    4345     # . prologue
    4346     55/push-ebp
    4347     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4348     # setup
    4349     # . clear-stream(_test-input-stream)
    4350     # . . push args
    4351     68/push  _test-input-stream/imm32
    4352     # . . call
    4353     e8/call  clear-stream/disp32
    4354     # . . discard args
    4355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4356     # . clear-stream(_test-output-stream)
    4357     # . . push args
    4358     68/push  _test-output-stream/imm32
    4359     # . . call
    4360     e8/call  clear-stream/disp32
    4361     # . . discard args
    4362     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4363     # . clear-stream($_test-output-buffered-file->buffer)
    4364     # . . push args
    4365     68/push  $_test-output-buffered-file->buffer/imm32
    4366     # . . call
    4367     e8/call  clear-stream/disp32
    4368     # . . discard args
    4369     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4370     # initialize input
    4371     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
    4372     # . . push args
    4373     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
    4374     68/push  _test-input-stream/imm32
    4375     # . . call
    4376     e8/call  write/disp32
    4377     # . . discard args
    4378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4379     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4380     # . . push args
    4381     68/push  _test-output-buffered-file/imm32
    4382     68/push  _test-input-stream/imm32
    4383     # . . call
    4384     e8/call  convert-instruction/disp32
    4385     # . . discard args
    4386     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4387     # check output
    4388     # . flush(_test-output-buffered-file)
    4389     # . . push args
    4390     68/push  _test-output-buffered-file/imm32
    4391     # . . call
    4392     e8/call  flush/disp32
    4393     # . . discard args
    4394     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4395 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4421     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
    4422     # . . push args
    4423     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
    4424     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
    4425     68/push  _test-output-stream/imm32
    4426     # . . call
    4427     e8/call  check-stream-equal/disp32
    4428     # . . discard args
    4429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4430     # . epilogue
    4431     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4432     5d/pop-to-ebp
    4433     c3/return
    4434
    4435 test-convert-instruction-emits-modrm-byte:
    4436     # pack mod, rm32 and r32 operands into ModR/M byte
    4437     # . prologue
    4438     55/push-ebp
    4439     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4440     # setup
    4441     # . clear-stream(_test-input-stream)
    4442     # . . push args
    4443     68/push  _test-input-stream/imm32
    4444     # . . call
    4445     e8/call  clear-stream/disp32
    4446     # . . discard args
    4447     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4448     # . clear-stream(_test-output-stream)
    4449     # . . push args
    4450     68/push  _test-output-stream/imm32
    4451     # . . call
    4452     e8/call  clear-stream/disp32
    4453     # . . discard args
    4454     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4455     # . clear-stream($_test-output-buffered-file->buffer)
    4456     # . . push args
    4457     68/push  $_test-output-buffered-file->buffer/imm32
    4458     # . . call
    4459     e8/call  clear-stream/disp32
    4460     # . . discard args
    4461     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4462     # initialize input
    4463     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
    4464     # . . push args
    4465     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
    4466     68/push  _test-input-stream/imm32
    4467     # . . call
    4468     e8/call  write/disp32
    4469     # . . discard args
    4470     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4471     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4472     # . . push args
    4473     68/push  _test-output-buffered-file/imm32
    4474     68/push  _test-input-stream/imm32
    4475     # . . call
    4476     e8/call  convert-instruction/disp32
    4477     # . . discard args
    4478     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4479     # check output
    4480     # . flush(_test-output-buffered-file)
    4481     # . . push args
    4482     68/push  _test-output-buffered-file/imm32
    4483     # . . call
    4484     e8/call  flush/disp32
    4485     # . . discard args
    4486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4487 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4513     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
    4514     # . . push args
    4515     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
    4516     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
    4517     68/push  _test-output-stream/imm32
    4518     # . . call
    4519     e8/call  check-stream-equal/disp32
    4520     # . . discard args
    4521     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4522     # . epilogue
    4523     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4524     5d/pop-to-ebp
    4525     c3/return
    4526
    4527 test-convert-instruction-emits-modrm-byte-with-non-zero-mod:
    4528     # . prologue
    4529     55/push-ebp
    4530     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4531     # setup
    4532     # . clear-stream(_test-input-stream)
    4533     # . . push args
    4534     68/push  _test-input-stream/imm32
    4535     # . . call
    4536     e8/call  clear-stream/disp32
    4537     # . . discard args
    4538     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4539     # . clear-stream(_test-output-stream)
    4540     # . . push args
    4541     68/push  _test-output-stream/imm32
    4542     # . . call
    4543     e8/call  clear-stream/disp32
    4544     # . . discard args
    4545     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4546     # . clear-stream($_test-output-buffered-file->buffer)
    4547     # . . push args
    4548     68/push  $_test-output-buffered-file->buffer/imm32
    4549     # . . call
    4550     e8/call  clear-stream/disp32
    4551     # . . discard args
    4552     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4553     # initialize input
    4554     # . write(_test-input-stream, "01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx")
    4555     # . . push args
    4556     68/push  "01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx"/imm32
    4557     68/push  _test-input-stream/imm32
    4558     # . . call
    4559     e8/call  write/disp32
    4560     # . . discard args
    4561     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4562     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4563     # . . push args
    4564     68/push  _test-output-buffered-file/imm32
    4565     68/push  _test-input-stream/imm32
    4566     # . . call
    4567     e8/call  convert-instruction/disp32
    4568     # . . discard args
    4569     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4570     # . flush(_test-output-buffered-file)
    4571     # . . push args
    4572     68/push  _test-output-buffered-file/imm32
    4573     # . . call
    4574     e8/call  flush/disp32
    4575     # . . discard args
    4576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4577 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4603     # check output
    4604     # . check-stream-equal(_test-output-stream, "# abcd", msg)
    4605     # . . push args
    4606     68/push  "F - test-convert-instruction-foo"/imm32
    4607     68/push  "01 cb  # 01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx"/imm32
    4608     68/push  _test-output-stream/imm32
    4609     # . . call
    4610     e8/call  check-stream-equal/disp32
    4611     # . . discard args
    4612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4613     # . epilogue
    4614     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4615     5d/pop-to-ebp
    4616     c3/return
    4617
    4618 test-convert-instruction-emits-modrm-byte-from-subop:
    4619     # pack mod, rm32 and subop operands into ModR/M byte
    4620     # . prologue
    4621     55/push-ebp
    4622     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4623     # setup
    4624     # . clear-stream(_test-input-stream)
    4625     # . . push args
    4626     68/push  _test-input-stream/imm32
    4627     # . . call
    4628     e8/call  clear-stream/disp32
    4629     # . . discard args
    4630     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4631     # . clear-stream(_test-output-stream)
    4632     # . . push args
    4633     68/push  _test-output-stream/imm32
    4634     # . . call
    4635     e8/call  clear-stream/disp32
    4636     # . . discard args
    4637     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4638     # . clear-stream($_test-output-buffered-file->buffer)
    4639     # . . push args
    4640     68/push  $_test-output-buffered-file->buffer/imm32
    4641     # . . call
    4642     e8/call  clear-stream/disp32
    4643     # . . discard args
    4644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4645     # initialize input
    4646     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
    4647     # . . push args
    4648     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
    4649     68/push  _test-input-stream/imm32
    4650     # . . call
    4651     e8/call  write/disp32
    4652     # . . discard args
    4653     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4654     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4655     # . . push args
    4656     68/push  _test-output-buffered-file/imm32
    4657     68/push  _test-input-stream/imm32
    4658     # . . call
    4659     e8/call  convert-instruction/disp32
    4660     # . . discard args
    4661     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4662     # check output
    4663     # . flush(_test-output-buffered-file)
    4664     # . . push args
    4665     68/push  _test-output-buffered-file/imm32
    4666     # . . call
    4667     e8/call  flush/disp32
    4668     # . . discard args
    4669     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4670 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4696     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
    4697     # . . push args
    4698     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
    4699     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
    4700     68/push  _test-output-stream/imm32
    4701     # . . call
    4702     e8/call  check-stream-equal/disp32
    4703     # . . discard args
    4704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4705     # . epilogue
    4706     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4707     5d/pop-to-ebp
    4708     c3/return
    4709
    4710 test-convert-instruction-emits-modrm-byte-with-missing-mod:
    4711     # pack rm32 and r32 operands into ModR/M byte
    4712     # . prologue
    4713     55/push-ebp
    4714     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4715     # setup
    4716     # . clear-stream(_test-input-stream)
    4717     # . . push args
    4718     68/push  _test-input-stream/imm32
    4719     # . . call
    4720     e8/call  clear-stream/disp32
    4721     # . . discard args
    4722     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4723     # . clear-stream(_test-output-stream)
    4724     # . . push args
    4725     68/push  _test-output-stream/imm32
    4726     # . . call
    4727     e8/call  clear-stream/disp32
    4728     # . . discard args
    4729     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4730     # . clear-stream($_test-output-buffered-file->buffer)
    4731     # . . push args
    4732     68/push  $_test-output-buffered-file->buffer/imm32
    4733     # . . call
    4734     e8/call  clear-stream/disp32
    4735     # . . discard args
    4736     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4737     # initialize input
    4738     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
    4739     # . . push args
    4740     68/push  "8b/copy 0/rm32 1/r32"/imm32
    4741     68/push  _test-input-stream/imm32
    4742     # . . call
    4743     e8/call  write/disp32
    4744     # . . discard args
    4745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4746     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4747     # . . push args
    4748     68/push  _test-output-buffered-file/imm32
    4749     68/push  _test-input-stream/imm32
    4750     # . . call
    4751     e8/call  convert-instruction/disp32
    4752     # . . discard args
    4753     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4754     # check output
    4755     # . flush(_test-output-buffered-file)
    4756     # . . push args
    4757     68/push  _test-output-buffered-file/imm32
    4758     # . . call
    4759     e8/call  flush/disp32
    4760     # . . discard args
    4761     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4762 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4788     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
    4789     # . . push args
    4790     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
    4791     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
    4792     68/push  _test-output-stream/imm32
    4793     # . . call
    4794     e8/call  check-stream-equal/disp32
    4795     # . . discard args
    4796     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4797     # . epilogue
    4798     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4799     5d/pop-to-ebp
    4800     c3/return
    4801
    4802 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
    4803     # pack mod and r32 operands into ModR/M byte
    4804     # . prologue
    4805     55/push-ebp
    4806     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4807     # setup
    4808     # . clear-stream(_test-input-stream)
    4809     # . . push args
    4810     68/push  _test-input-stream/imm32
    4811     # . . call
    4812     e8/call  clear-stream/disp32
    4813     # . . discard args
    4814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4815     # . clear-stream(_test-output-stream)
    4816     # . . push args
    4817     68/push  _test-output-stream/imm32
    4818     # . . call
    4819     e8/call  clear-stream/disp32
    4820     # . . discard args
    4821     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4822     # . clear-stream($_test-output-buffered-file->buffer)
    4823     # . . push args
    4824     68/push  $_test-output-buffered-file->buffer/imm32
    4825     # . . call
    4826     e8/call  clear-stream/disp32
    4827     # . . discard args
    4828     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4829     # initialize input
    4830     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
    4831     # . . push args
    4832     68/push  "8b/copy 0/mod 1/r32"/imm32
    4833     68/push  _test-input-stream/imm32
    4834     # . . call
    4835     e8/call  write/disp32
    4836     # . . discard args
    4837     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4838     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    4839     # . . push args
    4840     68/push  _test-output-buffered-file/imm32
    4841     68/push  _test-input-stream/imm32
    4842     # . . call
    4843     e8/call  convert-instruction/disp32
    4844     # . . discard args
    4845     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4846     # check output
    4847     # . flush(_test-output-buffered-file)
    4848     # . . push args
    4849     68/push  _test-output-buffered-file/imm32
    4850     # . . call
    4851     e8/call  flush/disp32
    4852     # . . discard args
    4853     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4854 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    4880     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
    4881     # . . push args
    4882     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
    4883     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
    4884     68/push  _test-output-stream/imm32
    4885     # . . call
    4886     e8/call  check-stream-equal/disp32
    4887     # . . discard args
    4888     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4889     # . epilogue
    4890     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4891     5d/pop-to-ebp
    4892     c3/return
    4893
    4894 test-convert-instruction-emits-modrm-byte-with-missing-r32:
    4895     # pack mod and rm32 operands into ModR/M byte
    4896     # . prologue
    4897     55/push-ebp
    4898     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4899     # setup
    4900     # . clear-stream(_test-input-stream)
    4901     # . . push args
    4902     68/push  _test-input-stream/imm32
    4903     # . . call
    4904     e8/call  clear-stream/disp32
    4905     # . . discard args
    4906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4907     # . clear-stream(_test-output-stream)
    4908     # . . push args
    4909     68/push  _test-output-stream/imm32
    4910     # . . call
    4911     e8/call  clear-stream/disp32
    4912     # . . discard args
    4913     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4914     # . clear-stream($_test-output-buffered-file->buffer)
    4915     # . . push args
    4916     68/push  $_test-output-buffered-file->buffer/imm32
    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 0/rm32")
    4923     # . . push args
    4924     68/push  "8b/copy 0/mod 0/rm32"/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 00  # 8b/copy 0/mod 0/rm32", msg)
    4973     # . . push args
    4974     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
    4975     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/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     # . epilogue
    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:
    4987     # pack base, index and scale operands into SIB byte
    4988     # . prologue
    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->buffer)
    5007     # . . push args
    5008     68/push  $_test-output-buffered-file->buffer/imm32
    5009     # . . call
    5010     e8/call  clear-stream/disp32
    5011     # . . discard args
    5012     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5013     # initialize input
    5014     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
    5015     # . . push args
    5016     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
    5017     68/push  _test-input-stream/imm32
    5018     # . . call
    5019     e8/call  write/disp32
    5020     # . . discard args
    5021     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5022     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5023     # . . push args
    5024     68/push  _test-output-buffered-file/imm32
    5025     68/push  _test-input-stream/imm32
    5026     # . . call
    5027     e8/call  convert-instruction/disp32
    5028     # . . discard args
    5029     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5030     # check output
    5031     # . flush(_test-output-buffered-file)
    5032     # . . push args
    5033     68/push  _test-output-buffered-file/imm32
    5034     # . . call
    5035     e8/call  flush/disp32
    5036     # . . discard args
    5037     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5038 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5064     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
    5065     # . . push args
    5066     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
    5067     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
    5068     68/push  _test-output-stream/imm32
    5069     # . . call
    5070     e8/call  check-stream-equal/disp32
    5071     # . . discard args
    5072     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5073     # . epilogue
    5074     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5075     5d/pop-to-ebp
    5076     c3/return
    5077
    5078 test-convert-instruction-emits-scale:
    5079     # pack base, index and scale operands into SIB byte
    5080     # . prologue
    5081     55/push-ebp
    5082     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5083     # setup
    5084     # . clear-stream(_test-input-stream)
    5085     # . . push args
    5086     68/push  _test-input-stream/imm32
    5087     # . . call
    5088     e8/call  clear-stream/disp32
    5089     # . . discard args
    5090     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5091     # . clear-stream(_test-output-stream)
    5092     # . . push args
    5093     68/push  _test-output-stream/imm32
    5094     # . . call
    5095     e8/call  clear-stream/disp32
    5096     # . . discard args
    5097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5098     # . clear-stream($_test-output-buffered-file->buffer)
    5099     # . . push args
    5100     68/push  $_test-output-buffered-file->buffer/imm32
    5101     # . . call
    5102     e8/call  clear-stream/disp32
    5103     # . . discard args
    5104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5105     # initialize input
    5106     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/scale")
    5107     # . . push args
    5108     68/push  "8b/copy 0/mod 4/rm32 1/scale"/imm32
    5109     68/push  _test-input-stream/imm32
    5110     # . . call
    5111     e8/call  write/disp32
    5112     # . . discard args
    5113     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5114     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5115     # . . push args
    5116     68/push  _test-output-buffered-file/imm32
    5117     68/push  _test-input-stream/imm32
    5118     # . . call
    5119     e8/call  convert-instruction/disp32
    5120     # . . discard args
    5121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5122     # check output
    5123     # . flush(_test-output-buffered-file)
    5124     # . . push args
    5125     68/push  _test-output-buffered-file/imm32
    5126     # . . call
    5127     e8/call  flush/disp32
    5128     # . . discard args
    5129     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5130 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5156     # . check-stream-equal(_test-output-stream, "8b 04 40  # 8b/copy 0/mod 4/rm32 1/scale", msg)
    5157     # . . push args
    5158     68/push  "F - test-convert-instruction-emits-scale"/imm32
    5159     68/push  "8b 04 40  # 8b/copy 0/mod 4/rm32 1/scale"/imm32
    5160     68/push  _test-output-stream/imm32
    5161     # . . call
    5162     e8/call  check-stream-equal/disp32
    5163     # . . discard args
    5164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5165     # . epilogue
    5166     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5167     5d/pop-to-ebp
    5168     c3/return
    5169
    5170 test-convert-instruction-emits-sib-byte-with-missing-base:
    5171     # pack index and scale operands into SIB byte
    5172     # . prologue
    5173     55/push-ebp
    5174     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5175     # setup
    5176     # . clear-stream(_test-input-stream)
    5177     # . . push args
    5178     68/push  _test-input-stream/imm32
    5179     # . . call
    5180     e8/call  clear-stream/disp32
    5181     # . . discard args
    5182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5183     # . clear-stream(_test-output-stream)
    5184     # . . push args
    5185     68/push  _test-output-stream/imm32
    5186     # . . call
    5187     e8/call  clear-stream/disp32
    5188     # . . discard args
    5189     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5190     # . clear-stream($_test-output-buffered-file->buffer)
    5191     # . . push args
    5192     68/push  $_test-output-buffered-file->buffer/imm32
    5193     # . . call
    5194     e8/call  clear-stream/disp32
    5195     # . . discard args
    5196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5197     # initialize input
    5198     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
    5199     # . . push args
    5200     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
    5201     68/push  _test-input-stream/imm32
    5202     # . . call
    5203     e8/call  write/disp32
    5204     # . . discard args
    5205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5206     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5207     # . . push args
    5208     68/push  _test-output-buffered-file/imm32
    5209     68/push  _test-input-stream/imm32
    5210     # . . call
    5211     e8/call  convert-instruction/disp32
    5212     # . . discard args
    5213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5214     # check output
    5215     # . flush(_test-output-buffered-file)
    5216     # . . push args
    5217     68/push  _test-output-buffered-file/imm32
    5218     # . . call
    5219     e8/call  flush/disp32
    5220     # . . discard args
    5221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5222 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5248     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
    5249     # . . push args
    5250     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
    5251     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
    5252     68/push  _test-output-stream/imm32
    5253     # . . call
    5254     e8/call  check-stream-equal/disp32
    5255     # . . discard args
    5256     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5257     # . epilogue
    5258     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5259     5d/pop-to-ebp
    5260     c3/return
    5261
    5262 test-convert-instruction-emits-sib-byte-with-missing-index:
    5263     # pack base and scale operands into SIB byte
    5264     # . prologue
    5265     55/push-ebp
    5266     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5267     # setup
    5268     # . clear-stream(_test-input-stream)
    5269     # . . push args
    5270     68/push  _test-input-stream/imm32
    5271     # . . call
    5272     e8/call  clear-stream/disp32
    5273     # . . discard args
    5274     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5275     # . clear-stream(_test-output-stream)
    5276     # . . push args
    5277     68/push  _test-output-stream/imm32
    5278     # . . call
    5279     e8/call  clear-stream/disp32
    5280     # . . discard args
    5281     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5282     # . clear-stream($_test-output-buffered-file->buffer)
    5283     # . . push args
    5284     68/push  $_test-output-buffered-file->buffer/imm32
    5285     # . . call
    5286     e8/call  clear-stream/disp32
    5287     # . . discard args
    5288     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5289     # initialize input
    5290     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
    5291     # . . push args
    5292     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
    5293     68/push  _test-input-stream/imm32
    5294     # . . call
    5295     e8/call  write/disp32
    5296     # . . discard args
    5297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5298     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5299     # . . push args
    5300     68/push  _test-output-buffered-file/imm32
    5301     68/push  _test-input-stream/imm32
    5302     # . . call
    5303     e8/call  convert-instruction/disp32
    5304     # . . discard args
    5305     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5306     # check output
    5307     # . flush(_test-output-buffered-file)
    5308     # . . push args
    5309     68/push  _test-output-buffered-file/imm32
    5310     # . . call
    5311     e8/call  flush/disp32
    5312     # . . discard args
    5313     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5314 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5340     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
    5341     # . . push args
    5342     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
    5343     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
    5344     68/push  _test-output-stream/imm32
    5345     # . . call
    5346     e8/call  check-stream-equal/disp32
    5347     # . . discard args
    5348     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5349     # . epilogue
    5350     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5351     5d/pop-to-ebp
    5352     c3/return
    5353
    5354 test-convert-instruction-emits-sib-byte-with-missing-scale:
    5355     # pack base and index operands into SIB byte
    5356     # . prologue
    5357     55/push-ebp
    5358     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5359     # setup
    5360     # . clear-stream(_test-input-stream)
    5361     # . . push args
    5362     68/push  _test-input-stream/imm32
    5363     # . . call
    5364     e8/call  clear-stream/disp32
    5365     # . . discard args
    5366     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5367     # . clear-stream(_test-output-stream)
    5368     # . . push args
    5369     68/push  _test-output-stream/imm32
    5370     # . . call
    5371     e8/call  clear-stream/disp32
    5372     # . . discard args
    5373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5374     # . clear-stream($_test-output-buffered-file->buffer)
    5375     # . . push args
    5376     68/push  $_test-output-buffered-file->buffer/imm32
    5377     # . . call
    5378     e8/call  clear-stream/disp32
    5379     # . . discard args
    5380     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5381     # initialize input
    5382     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
    5383     # . . push args
    5384     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
    5385     68/push  _test-input-stream/imm32
    5386     # . . call
    5387     e8/call  write/disp32
    5388     # . . discard args
    5389     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5390     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5391     # . . push args
    5392     68/push  _test-output-buffered-file/imm32
    5393     68/push  _test-input-stream/imm32
    5394     # . . call
    5395     e8/call  convert-instruction/disp32
    5396     # . . discard args
    5397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5398     # check output
    5399     # . flush(_test-output-buffered-file)
    5400     # . . push args
    5401     68/push  _test-output-buffered-file/imm32
    5402     # . . call
    5403     e8/call  flush/disp32
    5404     # . . discard args
    5405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5406 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5432     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
    5433     # . . push args
    5434     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
    5435     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
    5436     68/push  _test-output-stream/imm32
    5437     # . . call
    5438     e8/call  check-stream-equal/disp32
    5439     # . . discard args
    5440     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5441     # . epilogue
    5442     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5443     5d/pop-to-ebp
    5444     c3/return
    5445
    5446 test-convert-instruction-handles-disp32-operand:
    5447     # expand /disp32 operand into 4 bytes
    5448     # . prologue
    5449     55/push-ebp
    5450     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5451     # setup
    5452     # . clear-stream(_test-input-stream)
    5453     # . . push args
    5454     68/push  _test-input-stream/imm32
    5455     # . . call
    5456     e8/call  clear-stream/disp32
    5457     # . . discard args
    5458     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5459     # . clear-stream(_test-output-stream)
    5460     # . . push args
    5461     68/push  _test-output-stream/imm32
    5462     # . . call
    5463     e8/call  clear-stream/disp32
    5464     # . . discard args
    5465     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5466     # . clear-stream($_test-output-buffered-file->buffer)
    5467     # . . push args
    5468     68/push  $_test-output-buffered-file->buffer/imm32
    5469     # . . call
    5470     e8/call  clear-stream/disp32
    5471     # . . discard args
    5472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5473     # initialize input
    5474     # . write(_test-input-stream, "e8/call 20/disp32")
    5475     # . . push args
    5476     68/push  "e8/call 20/disp32"/imm32
    5477     68/push  _test-input-stream/imm32
    5478     # . . call
    5479     e8/call  write/disp32
    5480     # . . discard args
    5481     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5482     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5483     # . . push args
    5484     68/push  _test-output-buffered-file/imm32
    5485     68/push  _test-input-stream/imm32
    5486     # . . call
    5487     e8/call  convert-instruction/disp32
    5488     # . . discard args
    5489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5490     # check output
    5491     # . flush(_test-output-buffered-file)
    5492     # . . push args
    5493     68/push  _test-output-buffered-file/imm32
    5494     # . . call
    5495     e8/call  flush/disp32
    5496     # . . discard args
    5497     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5498 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5524     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
    5525     # . . push args
    5526     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
    5527     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
    5528     68/push  _test-output-stream/imm32
    5529     # . . call
    5530     e8/call  check-stream-equal/disp32
    5531     # . . discard args
    5532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5533     # . epilogue
    5534     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5535     5d/pop-to-ebp
    5536     c3/return
    5537
    5538 test-convert-instruction-handles-disp16-operand:
    5539     # expand /disp16 operand into 2 bytes
    5540     # . prologue
    5541     55/push-ebp
    5542     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5543     # setup
    5544     # . clear-stream(_test-input-stream)
    5545     # . . push args
    5546     68/push  _test-input-stream/imm32
    5547     # . . call
    5548     e8/call  clear-stream/disp32
    5549     # . . discard args
    5550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5551     # . clear-stream(_test-output-stream)
    5552     # . . push args
    5553     68/push  _test-output-stream/imm32
    5554     # . . call
    5555     e8/call  clear-stream/disp32
    5556     # . . discard args
    5557     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5558     # . clear-stream($_test-output-buffered-file->buffer)
    5559     # . . push args
    5560     68/push  $_test-output-buffered-file->buffer/imm32
    5561     # . . call
    5562     e8/call  clear-stream/disp32
    5563     # . . discard args
    5564     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5565     # initialize input
    5566     # . write(_test-input-stream, "e8/call 20/disp16")
    5567     # . . push args
    5568     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
    5569     68/push  _test-input-stream/imm32
    5570     # . . call
    5571     e8/call  write/disp32
    5572     # . . discard args
    5573     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5574     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5575     # . . push args
    5576     68/push  _test-output-buffered-file/imm32
    5577     68/push  _test-input-stream/imm32
    5578     # . . call
    5579     e8/call  convert-instruction/disp32
    5580     # . . discard args
    5581     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5582     # check output
    5583     # . flush(_test-output-buffered-file)
    5584     # . . push args
    5585     68/push  _test-output-buffered-file/imm32
    5586     # . . call
    5587     e8/call  flush/disp32
    5588     # . . discard args
    5589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5590 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5616     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
    5617     # . . push args
    5618     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
    5619     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
    5620     68/push  _test-output-stream/imm32
    5621     # . . call
    5622     e8/call  check-stream-equal/disp32
    5623     # . . discard args
    5624     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5625     # . epilogue
    5626     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5627     5d/pop-to-ebp
    5628     c3/return
    5629
    5630 test-convert-instruction-handles-disp8-operand:
    5631     # expand /disp8 operand into 1 byte
    5632     # . prologue
    5633     55/push-ebp
    5634     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5635     # setup
    5636     # . clear-stream(_test-input-stream)
    5637     # . . push args
    5638     68/push  _test-input-stream/imm32
    5639     # . . call
    5640     e8/call  clear-stream/disp32
    5641     # . . discard args
    5642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5643     # . clear-stream(_test-output-stream)
    5644     # . . push args
    5645     68/push  _test-output-stream/imm32
    5646     # . . call
    5647     e8/call  clear-stream/disp32
    5648     # . . discard args
    5649     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5650     # . clear-stream($_test-output-buffered-file->buffer)
    5651     # . . push args
    5652     68/push  $_test-output-buffered-file->buffer/imm32
    5653     # . . call
    5654     e8/call  clear-stream/disp32
    5655     # . . discard args
    5656     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5657     # initialize input
    5658     # . write(_test-input-stream, "eb/jump 20/disp8")
    5659     # . . push args
    5660     68/push  "eb/jump 20/disp8"/imm32
    5661     68/push  _test-input-stream/imm32
    5662     # . . call
    5663     e8/call  write/disp32
    5664     # . . discard args
    5665     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5666     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5667     # . . push args
    5668     68/push  _test-output-buffered-file/imm32
    5669     68/push  _test-input-stream/imm32
    5670     # . . call
    5671     e8/call  convert-instruction/disp32
    5672     # . . discard args
    5673     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5674     # check output
    5675     # . flush(_test-output-buffered-file)
    5676     # . . push args
    5677     68/push  _test-output-buffered-file/imm32
    5678     # . . call
    5679     e8/call  flush/disp32
    5680     # . . discard args
    5681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5682 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5708     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
    5709     # . . push args
    5710     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
    5711     68/push  "eb 20  # eb/jump 20/disp8"/imm32
    5712     68/push  _test-output-stream/imm32
    5713     # . . call
    5714     e8/call  check-stream-equal/disp32
    5715     # . . discard args
    5716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5717     # . epilogue
    5718     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5719     5d/pop-to-ebp
    5720     c3/return
    5721
    5722 test-convert-instruction-handles-disp8-name:
    5723     # pass /disp8 name directly through
    5724     # . prologue
    5725     55/push-ebp
    5726     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5727     # setup
    5728     # . clear-stream(_test-input-stream)
    5729     # . . push args
    5730     68/push  _test-input-stream/imm32
    5731     # . . call
    5732     e8/call  clear-stream/disp32
    5733     # . . discard args
    5734     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5735     # . clear-stream(_test-output-stream)
    5736     # . . push args
    5737     68/push  _test-output-stream/imm32
    5738     # . . call
    5739     e8/call  clear-stream/disp32
    5740     # . . discard args
    5741     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5742     # . clear-stream($_test-output-buffered-file->buffer)
    5743     # . . push args
    5744     68/push  $_test-output-buffered-file->buffer/imm32
    5745     # . . call
    5746     e8/call  clear-stream/disp32
    5747     # . . discard args
    5748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5749     # initialize input
    5750     # . write(_test-input-stream, "eb/jump xyz/disp8")
    5751     # . . push args
    5752     68/push  "eb/jump xyz/disp8"/imm32
    5753     68/push  _test-input-stream/imm32
    5754     # . . call
    5755     e8/call  write/disp32
    5756     # . . discard args
    5757     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5758     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5759     # . . push args
    5760     68/push  _test-output-buffered-file/imm32
    5761     68/push  _test-input-stream/imm32
    5762     # . . call
    5763     e8/call  convert-instruction/disp32
    5764     # . . discard args
    5765     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5766     # check output
    5767     # . flush(_test-output-buffered-file)
    5768     # . . push args
    5769     68/push  _test-output-buffered-file/imm32
    5770     # . . call
    5771     e8/call  flush/disp32
    5772     # . . discard args
    5773     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5774 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5800     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
    5801     # . . push args
    5802     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
    5803     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
    5804     68/push  _test-output-stream/imm32
    5805     # . . call
    5806     e8/call  check-stream-equal/disp32
    5807     # . . discard args
    5808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5809     # . epilogue
    5810     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5811     5d/pop-to-ebp
    5812     c3/return
    5813
    5814 test-convert-instruction-handles-imm32-operand:
    5815     # expand /imm32 operand into 4 bytes
    5816     # . prologue
    5817     55/push-ebp
    5818     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5819     # setup
    5820     # . clear-stream(_test-input-stream)
    5821     # . . push args
    5822     68/push  _test-input-stream/imm32
    5823     # . . call
    5824     e8/call  clear-stream/disp32
    5825     # . . discard args
    5826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5827     # . clear-stream(_test-output-stream)
    5828     # . . push args
    5829     68/push  _test-output-stream/imm32
    5830     # . . call
    5831     e8/call  clear-stream/disp32
    5832     # . . discard args
    5833     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5834     # . clear-stream($_test-output-buffered-file->buffer)
    5835     # . . push args
    5836     68/push  $_test-output-buffered-file->buffer/imm32
    5837     # . . call
    5838     e8/call  clear-stream/disp32
    5839     # . . discard args
    5840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5841     # initialize input
    5842     # . write(_test-input-stream, "68/push 0x20/imm32")
    5843     # . . push args
    5844     68/push  "68/push 0x20/imm32"/imm32
    5845     68/push  _test-input-stream/imm32
    5846     # . . call
    5847     e8/call  write/disp32
    5848     # . . discard args
    5849     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5850     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5851     # . . push args
    5852     68/push  _test-output-buffered-file/imm32
    5853     68/push  _test-input-stream/imm32
    5854     # . . call
    5855     e8/call  convert-instruction/disp32
    5856     # . . discard args
    5857     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5858     # check output
    5859     # . flush(_test-output-buffered-file)
    5860     # . . push args
    5861     68/push  _test-output-buffered-file/imm32
    5862     # . . call
    5863     e8/call  flush/disp32
    5864     # . . discard args
    5865     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5866 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5892     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
    5893     # . . push args
    5894     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
    5895     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
    5896     68/push  _test-output-stream/imm32
    5897     # . . call
    5898     e8/call  check-stream-equal/disp32
    5899     # . . discard args
    5900     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5901     # . epilogue
    5902     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5903     5d/pop-to-ebp
    5904     c3/return
    5905
    5906 test-convert-instruction-handles-imm16-operand:
    5907     # expand /imm16 operand into 2 bytes
    5908     # we don't have one of these at the moment, so this expands to an invalid instruction
    5909     # . prologue
    5910     55/push-ebp
    5911     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    5912     # setup
    5913     # . clear-stream(_test-input-stream)
    5914     # . . push args
    5915     68/push  _test-input-stream/imm32
    5916     # . . call
    5917     e8/call  clear-stream/disp32
    5918     # . . discard args
    5919     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5920     # . clear-stream(_test-output-stream)
    5921     # . . push args
    5922     68/push  _test-output-stream/imm32
    5923     # . . call
    5924     e8/call  clear-stream/disp32
    5925     # . . discard args
    5926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5927     # . clear-stream($_test-output-buffered-file->buffer)
    5928     # . . push args
    5929     68/push  $_test-output-buffered-file->buffer/imm32
    5930     # . . call
    5931     e8/call  clear-stream/disp32
    5932     # . . discard args
    5933     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5934     # initialize input
    5935     # . write(_test-input-stream, "68/push 0x20/imm16")
    5936     # . . push args
    5937     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
    5938     68/push  _test-input-stream/imm32
    5939     # . . call
    5940     e8/call  write/disp32
    5941     # . . discard args
    5942     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5943     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    5944     # . . push args
    5945     68/push  _test-output-buffered-file/imm32
    5946     68/push  _test-input-stream/imm32
    5947     # . . call
    5948     e8/call  convert-instruction/disp32
    5949     # . . discard args
    5950     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    5951     # check output
    5952     # . flush(_test-output-buffered-file)
    5953     # . . push args
    5954     68/push  _test-output-buffered-file/imm32
    5955     # . . call
    5956     e8/call  flush/disp32
    5957     # . . discard args
    5958     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    5959 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    5985     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
    5986     # . . push args
    5987     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
    5988     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
    5989     68/push  _test-output-stream/imm32
    5990     # . . call
    5991     e8/call  check-stream-equal/disp32
    5992     # . . discard args
    5993     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    5994     # . epilogue
    5995     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5996     5d/pop-to-ebp
    5997     c3/return
    5998
    5999 test-convert-instruction-handles-imm8-operand:
    6000     # expand /imm8 operand into 1 byte
    6001     # we don't have one of these at the moment, so this expands to an invalid instruction
    6002     # . prologue
    6003     55/push-ebp
    6004     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    6005     # setup
    6006     # . clear-stream(_test-input-stream)
    6007     # . . push args
    6008     68/push  _test-input-stream/imm32
    6009     # . . call
    6010     e8/call  clear-stream/disp32
    6011     # . . discard args
    6012     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    6013     # . clear-stream(_test-output-stream)
    6014     # . . push args
    6015     68/push  _test-output-stream/imm32
    6016     # . . call
    6017     e8/call  clear-stream/disp32
    6018     # . . discard args
    6019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    6020     # . clear-stream($_test-output-buffered-file->buffer)
    6021     # . . push args
    6022     68/push  $_test-output-buffered-file->buffer/imm32
    6023     # . . call
    6024     e8/call  clear-stream/disp32
    6025     # . . discard args
    6026     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    6027     # initialize input
    6028     # . write(_test-input-stream, "68/push 0x20/imm8")
    6029     # . . push args
    6030     68/push  "68/push 0x20/imm8"/imm32
    6031     68/push  _test-input-stream/imm32
    6032     # . . call
    6033     e8/call  write/disp32
    6034     # . . discard args
    6035     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    6036     # convert-instruction(_test-input-stream, _test-output-buffered-file)
    6037     # . . push args
    6038     68/push  _test-output-buffered-file/imm32
    6039     68/push  _test-input-stream/imm32
    6040     # . . call
    6041     e8/call  convert-instruction/disp32
    6042     # . . discard args
    6043     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    6044     # check output
    6045     # . flush(_test-output-buffered-file)
    6046     # . . push args
    6047     68/push  _test-output-buffered-file/imm32
    6048     # . . call
    6049     e8/call  flush/disp32
    6050     # . . discard args
    6051     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    6052 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    6078     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
    6079     # . . push args
    6080     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
    6081     68/push  "68 20  # 68/push 0x20/imm8"/imm32
    6082     68/push  _test-output-stream/imm32
    6083     # . . call
    6084     e8/call  check-stream-equal/disp32
    6085     # . . discard args
    6086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    6087     # . epilogue
    6088     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    6089     5d/pop-to-ebp
    6090     c3/return
    6091
    6092 # shortcut for parse-hex-int-from-slice(next-token-from-slice(word->start, word->end, '/'))
    6093 parse-datum-of-word:  # word: (addr slice) -> value/eax: int
    6094     # . prologue
    6095     55/push-ebp
    6096     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    6097     # . save registers
    6098     51/push-ecx
    6099     56/push-esi
    6100     # esi = word
    6101     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    6102     # var slice/ecx: slice
    6103     68/push  0/imm32/end
    6104     68/push  0/imm32/start
    6105     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    6106     # slice = next-token-from-slice(word->start, word->end, '/')
    6107     # . . push args
    6108     51/push-ecx
    6109     68/push  0x2f/imm32/slash
    6110     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
    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     # return parse-hex-int-from-slice(slice)
    6117     # . . push args
    6118     51/push-ecx
    6119     # . . call
    6120     e8/call  parse-hex-int-from-slice/disp32
    6121     # . . discard args
    6122     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    6123 $parse-datum-of-word:end:
    6124     # . reclaim locals
    6125     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    6126     # . restore registers
    6127     5e/pop-to-esi
    6128     59/pop-to-ecx
    6129     # . epilogue
    6130     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    6131     5d/pop-to-ebp
    6132     c3/return
    6133
    6134 # . . vim:nowrap:textwidth=0