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

       1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
       2 # (landscape).
       3 # Use the addresses assigned to replace labels.
       4 #
       5 # To build:
       6 #   $ bootstrap/bootstrap translate [01]*.subx apps/subx-params.subx apps/survey_baremetal.subx  -o apps/survey_baremetal
       7 #
       8 # The expected input is a stream of bytes and some interspersed labels.
       9 # Comments and '==' segment headers are allowed, but names are ignored. The
      10 # emitted code will all lie in a single contiguous address range starting at
      11 # address 0x7c00. Addresses in segment headers are optional. If provided, this
      12 # program will insert padding in the output until the desired address is
      13 # reached.
      14 #
      15 #   $ cat x
      16 #   == code
      17 #   l1:
      18 #   aa bb l1/imm8
      19 #   cc dd l2/disp32
      20 #   l2:
      21 #   ee foo/imm32
      22 #   == data 0x7c10
      23 #   foo:
      24 #     34
      25 #
      26 # The output is the stream of bytes without segment headers or label definitions,
      27 # and with label references replaced with numeric values/displacements.
      28 #
      29 #   $ cat x  |bootstrap/bootstrap run survey_baremetal
      30 #   # 0x7c00
      31 #   aa bb nn  # some computed address
      32 #   cc dd nn nn nn nn  # some computed displacement
      33 #   ee nn nn nn nn  # address right after this instruction
      34 #   # 0x7c0e
      35 #   00 00  # padding
      36 #   # 0x7c10
      37 #   34  # data segment interleaved with code
      38
      39 == code
      40 #   instruction                     effective address                                                   register    displacement    immediate
      41 # . op          subop               mod             rm32          base        index         scale       r32
      42 # . 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
      43
      44 Entry:  # run tests if necessary, convert stdin if not
      45     # . prologue
      46     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
      47
      48     # Heap = new-segment(Heap-size)
      49     # . . push args
      50     68/push  Heap/imm32
      51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
      52     # . . call
      53     e8/call  new-segment/disp32
      54     # . . discard args
      55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      56     # initialize-trace-stream(Trace-size)
      57     # . . push args
      58     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
      59     # . . call
      60     e8/call  initialize-trace-stream/disp32
      61     # . . discard args
      62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
      63
      64     # - if argc > 1 and argv[1] == "test", then return run_tests()
      65     # if (argc <= 1) goto interactive
      66     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
      67     7e/jump-if-<=  $subx-survey-main:interactive/disp8
      68     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
      69     # . eax = kernel-string-equal?(argv[1], "test")
      70     # . . push args
      71     68/push  "test"/imm32
      72     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
      73     # . . call
      74     e8/call  kernel-string-equal?/disp32
      75     # . . discard args
      76     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      77     # . if (eax == false) goto interactive
      78     3d/compare-eax-and  0/imm32/false
      79     74/jump-if-=  $subx-survey-main:interactive/disp8
      80     # run-tests()
      81 #?     e8/call  test-emit-output-with-padding/disp32
      82     e8/call  run-tests/disp32
      83     # syscall(exit, *Num-test-failures)
      84     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
      85     eb/jump  $subx-survey-main:end/disp8
      86 $subx-survey-main:interactive:
      87     # - otherwise convert stdin
      88     # subx-survey(Stdin, Stdout)
      89     # . . push args
      90     68/push  Stdout/imm32
      91     68/push  Stdin/imm32
      92     # . . call
      93     e8/call  subx-survey/disp32
      94     # . . discard args
      95     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      96 #?     # . write-stream(2/stderr, Trace-stream)
      97 #?     # . . push args
      98 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
      99 #?     68/push  2/imm32/stderr
     100 #?     # . . call
     101 #?     e8/call  write-stream/disp32
     102 #?     # . . discard args
     103 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     104     # syscall(exit, 0)
     105     bb/copy-to-ebx  0/imm32
     106 $subx-survey-main:end:
     107     e8/call  syscall_exit/disp32
     108
     109 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
     110     # pseudocode
     111     #   var in: (stream byte Input-size)
     112     #   slurp(infile, in)
     113     #   var labels: (stream {label-name, address} Max-labels)
     114     #   compute-offsets(in, labels)
     115     #   rewind-stream(in)
     116     #   emit-output(in, out, labels)
     117     #
     118     # . prologue
     119     55/push-ebp
     120     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     121     # . save registers
     122     51/push-ecx
     123     52/push-edx
     124     56/push-esi
     125     # var labels/edx: (stream {label-name, address} Max-labels)
     126     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
     127     # . data
     128     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
     129     # . size
     130     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
     131     # . read
     132     68/push  0/imm32/read
     133     # . write
     134     68/push  0/imm32/write
     135     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     136     # var in/esi: (stream byte Input-size)
     137     # . data
     138     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
     139     # . size
     140     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
     141     # . read
     142     68/push  0/imm32/read
     143     # . write
     144     68/push  0/imm32/write
     145     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
     146     # slurp(infile, in)
     147     # . . push args
     148     56/push-esi
     149     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     150     # . . call
     151     e8/call  slurp/disp32
     152     # . . discard args
     153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     154     # compute-offsets(in, labels)
     155     # . . push args
     156     52/push-edx
     157     56/push-esi
     158     # . . call
     159     e8/call  compute-offsets/disp32
     160     # . . discard args
     161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     162     # rewind-stream(in)
     163     # . . push args
     164     56/push-esi
     165     # . . call
     166     e8/call  rewind-stream/disp32
     167     # . . discard args
     168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     169     # emit-output(in, out, labels)
     170     # . . push args
     171     52/push-edx
     172     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     173     56/push-esi
     174     # . . call
     175     e8/call  emit-output/disp32
     176     # . . discard args
     177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     178     # flush(out)
     179     # . . push args
     180     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     181     # . . call
     182     e8/call  flush/disp32
     183     # . . discard args
     184     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     185 $subx-survey:end:
     186     # . reclaim locals
     187     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
     188     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
     189     # . restore registers
     190     5e/pop-to-esi
     191     5a/pop-to-edx
     192     59/pop-to-ecx
     193     # . epilogue
     194     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     195     5d/pop-to-ebp
     196     c3/return
     197
     198 test-subx-survey-computes-addresses:
     199     # input:
     200     #   == code
     201     #   ab x/imm32
     202     #   == data
     203     #   x:
     204     #     01
     205     #
     206     # trace contains (in any order):
     207     #   label x is at address 0x7c05
     208     #
     209     # . prologue
     210     55/push-ebp
     211     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     212     # setup
     213     # . clear-stream(_test-input-stream)
     214     # . . push args
     215     68/push  _test-input-stream/imm32
     216     # . . call
     217     e8/call  clear-stream/disp32
     218     # . . discard args
     219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     220     # . clear-stream($_test-input-buffered-file->buffer)
     221     # . . push args
     222     68/push  $_test-input-buffered-file->buffer/imm32
     223     # . . call
     224     e8/call  clear-stream/disp32
     225     # . . discard args
     226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     227     # . clear-stream(_test-output-stream)
     228     # . . push args
     229     68/push  _test-output-stream/imm32
     230     # . . call
     231     e8/call  clear-stream/disp32
     232     # . . discard args
     233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     234     # . clear-stream($_test-output-buffered-file->buffer)
     235     # . . push args
     236     68/push  $_test-output-buffered-file->buffer/imm32
     237     # . . call
     238     e8/call  clear-stream/disp32
     239     # . . discard args
     240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     241     # initialize input
     242     # . write(_test-input-stream, "== code\n")
     243     # . . push args
     244     68/push  "== code\n"/imm32
     245     68/push  _test-input-stream/imm32
     246     # . . call
     247     e8/call  write/disp32
     248     # . . discard args
     249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     250     # . write(_test-input-stream, "ab x/imm32\n")
     251     # . . push args
     252     68/push  "ab x/imm32\n"/imm32
     253     68/push  _test-input-stream/imm32
     254     # . . call
     255     e8/call  write/disp32
     256     # . . discard args
     257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     258     # . write(_test-input-stream, "== data\n")
     259     # . . push args
     260     68/push  "== data\n"/imm32
     261     68/push  _test-input-stream/imm32
     262     # . . call
     263     e8/call  write/disp32
     264     # . . discard args
     265     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     266     # . write(_test-input-stream, "x:\n")
     267     # . . push args
     268     68/push  "x:\n"/imm32
     269     68/push  _test-input-stream/imm32
     270     # . . call
     271     e8/call  write/disp32
     272     # . . discard args
     273     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     274     # . write(_test-input-stream, "01\n")
     275     # . . push args
     276     68/push  "01\n"/imm32
     277     68/push  _test-input-stream/imm32
     278     # . . call
     279     e8/call  write/disp32
     280     # . . discard args
     281     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     282     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
     283     # . . push args
     284     68/push  _test-output-buffered-file/imm32
     285     68/push  _test-input-buffered-file/imm32
     286     # . . call
     287     e8/call  subx-survey/disp32
     288     # . . discard args
     289     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     290     # check trace
     291 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
     317     # . check-trace-contains("label 'x' is at address 0x00007c05.", msg)
     318     # . . push args
     319     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
     320     68/push  "label 'x' is at address 0x00007c05."/imm32
     321     # . . call
     322     e8/call  check-trace-contains/disp32
     323     # . . discard args
     324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     325     # . epilogue
     326     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     327     5d/pop-to-ebp
     328     c3/return
     329
     330 test-subx-survey-computes-addresses-with-padding:
     331     # input:
     332     #   == code
     333     #   ab x/imm32
     334     #   == data 0x7c10
     335     #   x:
     336     #     01
     337     #
     338     # trace contains (in any order):
     339     #   label x is at address 0x7c10
     340     #
     341     # . prologue
     342     55/push-ebp
     343     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     344     # setup
     345     # . clear-stream(_test-input-stream)
     346     # . . push args
     347     68/push  _test-input-stream/imm32
     348     # . . call
     349     e8/call  clear-stream/disp32
     350     # . . discard args
     351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     352     # . clear-stream($_test-input-buffered-file->buffer)
     353     # . . push args
     354     68/push  $_test-input-buffered-file->buffer/imm32
     355     # . . call
     356     e8/call  clear-stream/disp32
     357     # . . discard args
     358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     359     # . clear-stream(_test-output-stream)
     360     # . . push args
     361     68/push  _test-output-stream/imm32
     362     # . . call
     363     e8/call  clear-stream/disp32
     364     # . . discard args
     365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     366     # . clear-stream($_test-output-buffered-file->buffer)
     367     # . . push args
     368     68/push  $_test-output-buffered-file->buffer/imm32
     369     # . . call
     370     e8/call  clear-stream/disp32
     371     # . . discard args
     372     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     373     # initialize input
     374     # . write(_test-input-stream, "== code\n")
     375     # . . push args
     376     68/push  "== code\n"/imm32
     377     68/push  _test-input-stream/imm32
     378     # . . call
     379     e8/call  write/disp32
     380     # . . discard args
     381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     382     # . write(_test-input-stream, "ab x/imm32\n")
     383     # . . push args
     384     68/push  "ab x/imm32\n"/imm32
     385     68/push  _test-input-stream/imm32
     386     # . . call
     387     e8/call  write/disp32
     388     # . . discard args
     389     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     390     # . write(_test-input-stream, "== data\n")
     391     # . . push args
     392     68/push  "== data 0x7c10\n"/imm32
     393     68/push  _test-input-stream/imm32
     394     # . . call
     395     e8/call  write/disp32
     396     # . . discard args
     397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     398     # . write(_test-input-stream, "x:\n")
     399     # . . push args
     400     68/push  "x:\n"/imm32
     401     68/push  _test-input-stream/imm32
     402     # . . call
     403     e8/call  write/disp32
     404     # . . discard args
     405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     406     # . write(_test-input-stream, "01\n")
     407     # . . push args
     408     68/push  "01\n"/imm32
     409     68/push  _test-input-stream/imm32
     410     # . . call
     411     e8/call  write/disp32
     412     # . . discard args
     413     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     414     # subx-survey(_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-survey/disp32
     420     # . . discard args
     421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     422     # check trace
     423 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
     449     # . check-trace-contains("label 'x' is at address 0x00007c10.", msg)
     450     # . . push args
     451     68/push  "F - test-subx-survey-computes-addresses-with-padding/0"/imm32
     452     68/push  "label 'x' is at address 0x00007c10."/imm32
     453     # . . call
     454     e8/call  check-trace-contains/disp32
     455     # . . discard args
     456     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     457     # . epilogue
     458     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     459     5d/pop-to-ebp
     460     c3/return
     461
     462 compute-offsets:  # in: (addr stream byte), labels: (addr stream {(handle array byte), address})
     463     # pseudocode:
     464     #   var current-address = 0x7c00
     465     #   var line: (stream byte 512)
     466     #   while true                                  # line loop
     467     #     clear-stream(line)
     468     #     read-line(in, line)
     469     #     if (line->write == 0) break               # end of file
     470     #     while true                                # word loop
     471     #       word-slice = next-word(line)
     472     #       if slice-empty?(word-slice)             # end of line
     473     #         break
     474     #       else if slice-starts-with?(word-slice, "#")  # comment
     475     #         break                                 # end of line
     476     #       else if slice-equal?(word-slice, "==")  # segment header
     477     #         word-slice = next-word(line)
     478     #         if slice-empty?(word-slice)
     479     #           abort
     480     #         word-slice = next-word(line)  # segment address
     481     #         if slice-empty?(word-slice)
     482     #           goto line-loop              # segment address is optional
     483     #         new-address = parse-hex-int-from-slice(word-slice)
     484     #         if new-address < current-address
     485     #           abort
     486     #         current-address = new-address
     487     #       else if label?(word-slice)
     488     #         strip trailing ':' from word-slice
     489     #         trace("label '" word-slice "' is at address " current-address ".")
     490     #         # labels occupy no space, so no need to increment offsets
     491     #       else
     492     #         width = compute-width-of-slice(word-slice)
     493     #         current-address += width
     494     #
     495     # . prologue
     496     55/push-ebp
     497     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     498     # . save registers
     499     50/push-eax
     500     51/push-ecx
     501     52/push-edx
     502     53/push-ebx
     503     56/push-esi
     504     57/push-edi
     505     # var current-address/esi: int = 0x7c00
     506     be/copy-to-esi  0x7c00/imm32
     507     # var line/ecx: (stream byte 512)
     508     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     509     68/push  0x200/imm32/size
     510     68/push  0/imm32/read
     511     68/push  0/imm32/write
     512     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     513     # var word-slice/edx: (addr slice)
     514     68/push  0/imm32
     515     68/push  0/imm32
     516     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     517 $compute-offsets:line-loop:
     518     # clear-stream(line)
     519     # . . push args
     520     51/push-ecx
     521     # . . call
     522     e8/call  clear-stream/disp32
     523     # . . discard args
     524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     525     # read-line(in, line)
     526     # . . push args
     527     51/push-ecx
     528     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     529     # . . call
     530     e8/call  read-line/disp32
     531     # . . discard args
     532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     533     # if (line->write == 0) break
     534     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
     535     3d/compare-eax-and  0/imm32
     536     0f 84/jump-if-=  $compute-offsets:end/disp32
     537 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
     570 $compute-offsets:word-loop:
     571     # next-word(line, word-slice)
     572     # . . push args
     573     52/push-edx
     574     51/push-ecx
     575     # . . call
     576     e8/call  next-word/disp32
     577     # . . discard args
     578     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     579 $compute-offsets:case-empty:
     580     # if slice-empty?(word-slice) break
     581     # . eax = slice-empty?(word-slice)
     582     # . . push args
     583     52/push-edx
     584     # . . call
     585     e8/call  slice-empty?/disp32
     586     # . . discard args
     587     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     588     # . if (eax != false) break
     589     3d/compare-eax-and  0/imm32/false
     590     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
     591 $compute-offsets:case-comment:
     592     # if slice-starts-with?(word-slice, "#") break
     593     # . . push args
     594     68/push  "#"/imm32
     595     52/push-edx
     596     # . . call
     597     e8/call  slice-starts-with?/disp32
     598     # . . discard args
     599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     600     # . if (eax != false) break
     601     3d/compare-eax-and  0/imm32/false
     602     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
     603 $compute-offsets:case-segment-header:
     604     # if !slice-equal?(word-slice, "==") goto next case
     605     # . eax = slice-equal?(word-slice, "==")
     606     # . . push args
     607     68/push  "=="/imm32
     608     52/push-edx
     609     # . . call
     610     e8/call  slice-equal?/disp32
     611     # . . discard args
     612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     613     # . if (eax != false) break
     614     3d/compare-eax-and  0/imm32/false
     615     0f 84/jump-if-=  $compute-offsets:case-label/disp32
     616     # next-word(line, word-slice)
     617     # . . push args
     618     52/push-edx
     619     51/push-ecx
     620     # . . call
     621     e8/call  next-word/disp32
     622     # . . discard args
     623     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     624     # if slice-empty?(word-slice) abort
     625     # . eax = slice-empty?(word-slice)
     626     # . . push args
     627     52/push-edx
     628     # . . call
     629     e8/call  slice-empty?/disp32
     630     # . . discard args
     631     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     632     # . if (eax != false) abort
     633     3d/compare-eax-and  0/imm32/false
     634     0f 85/jump-if-!=  $compute-offsets:abort/disp32
     635     # next-word(line, word-slice)
     636     # . . push args
     637     52/push-edx
     638     51/push-ecx
     639     # . . call
     640     e8/call  next-word/disp32
     641     # . . discard args
     642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     643     # if slice-empty?(word-slice) break
     644     # . eax = slice-empty?(word-slice)
     645     # . . push args
     646     52/push-edx
     647     # . . call
     648     e8/call  slice-empty?/disp32
     649     # . . discard args
     650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     651     # . if (eax != false) break
     652     3d/compare-eax-and  0/imm32/false
     653     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
     654     # var new-address/eax: int = parse-hex-int-from-slice(word-slice)
     655     # . . push args
     656     52/push-edx
     657     # . . call
     658     e8/call  parse-hex-int-from-slice/disp32
     659     # . . discard args
     660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     661     # if (new-address < current-address) abort
     662     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           6/r32/esi   .               .                 # compare eax with esi
     663     0f 82/jump-if-addr<  $compute-offsets:error-bad-segment-address/disp32
     664     # current-address = new-address
     665     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
     666     # break
     667     e9/jump  $compute-offsets:line-loop/disp32
     668 $compute-offsets:case-label:
     669     # if (!label?(word-slice)) goto next case
     670     # . eax = label?(word-slice)
     671     # . . push args
     672     52/push-edx
     673     # . . call
     674     e8/call  label?/disp32
     675     # . . discard args
     676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     677     # . if (eax == false) goto next case
     678     3d/compare-eax-and  0/imm32/false
     679     0f 84/jump-if-=  $compute-offsets:case-default/disp32
     680     # strip trailing ':' from word-slice
     681     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
     682     # var tmp/eax: (addr int) = get-or-insert-slice(labels, word-slice, row-size=12)
     683     # . . push args
     684     68/push  Heap/imm32
     685     68/push  0xc/imm32/row-size
     686     52/push-edx
     687     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     688     # . . call
     689     e8/call  get-or-insert-slice/disp32
     690     # . . discard args
     691     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     692     # *tmp = current-address
     693     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to *eax
     694     # trace-slsns("label '" word-slice "' is at address " current-address ".")
     695     # . . push args
     696     68/push  "."/imm32
     697     56/push-esi
     698     68/push  "' is at address "/imm32
     699     52/push-edx
     700     68/push  "label '"/imm32
     701     # . . call
     702     e8/call  trace-slsns/disp32
     703     # . . discard args
     704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
     705     # continue
     706     e9/jump  $compute-offsets:word-loop/disp32
     707 $compute-offsets:case-default:
     708     # width/eax = compute-width-of-slice(word-slice)
     709     # . . push args
     710     52/push-edx
     711     # . . call
     712     e8/call compute-width-of-slice/disp32
     713     # . . discard args
     714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     715     # current-address += width
     716     01/add                          3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
     717 +-- 41 lines: #?     # dump segment-offset ----------------------------------------------------------------------------------------------------------------------------------------------
     758     e9/jump $compute-offsets:word-loop/disp32
     759 $compute-offsets:end:
     760     # . reclaim locals
     761     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
     762     # . restore registers
     763     5f/pop-to-edi
     764     5e/pop-to-esi
     765     5b/pop-to-ebx
     766     5a/pop-to-edx
     767     59/pop-to-ecx
     768     58/pop-to-eax
     769     # . epilogue
     770     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     771     5d/pop-to-ebp
     772     c3/return
     773
     774 $compute-offsets:abort:
     775     # . _write(2/stderr, error)
     776     # . . push args
     777     68/push  "'==' must be followed by segment name and optionally an address\n"/imm32
     778     68/push  2/imm32/stderr
     779     # . . call
     780     e8/call  _write/disp32
     781     # . . discard args
     782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     783     # . syscall(exit, 1)
     784     bb/copy-to-ebx  1/imm32
     785     e8/call  syscall_exit/disp32
     786     # never gets here
     787
     788 $compute-offsets:error-bad-segment-address:
     789     # . _write(2/stderr, error)
     790     # . . push args
     791     68/push  "'==' specifies an address that implies negative padding\n"/imm32
     792     68/push  2/imm32/stderr
     793     # . . call
     794     e8/call  _write/disp32
     795     # . . discard args
     796     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     797     # . syscall(exit, 1)
     798     bb/copy-to-ebx  1/imm32
     799     e8/call  syscall_exit/disp32
     800     # never gets here
     801
     802 test-compute-offsets:
     803     # input:
     804     #   == code
     805     #   ab x/imm32  # skip comment
     806     #   == data
     807     #   00
     808     #   x:
     809     #     34
     810     #
     811     # trace contains (in any order):
     812     #   label 'x' is at address 0x7c06.
     813     #
     814     # . prologue
     815     55/push-ebp
     816     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     817     # setup
     818     # . clear-stream(_test-input-stream)
     819     # . . push args
     820     68/push  _test-input-stream/imm32
     821     # . . call
     822     e8/call  clear-stream/disp32
     823     # . . discard args
     824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     825     # var labels/edx: (stream byte 2*12)
     826     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
     827     68/push  0x18/imm32/size
     828     68/push  0/imm32/read
     829     68/push  0/imm32/write
     830     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     831     # initialize input
     832     # . write(_test-input-stream, "== code\n")
     833     # . . push args
     834     68/push  "== code\n"/imm32
     835     68/push  _test-input-stream/imm32
     836     # . . call
     837     e8/call  write/disp32
     838     # . . discard args
     839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     840     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
     841     # . . push args
     842     68/push  "ab x/imm32  # skip comment\n"/imm32
     843     68/push  _test-input-stream/imm32
     844     # . . call
     845     e8/call  write/disp32
     846     # . . discard args
     847     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     848     # . write(_test-input-stream, "== data\n")
     849     # . . push args
     850     68/push  "== data\n"/imm32
     851     68/push  _test-input-stream/imm32
     852     # . . call
     853     e8/call  write/disp32
     854     # . . discard args
     855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     856     # . write(_test-input-stream, "00\n")
     857     # . . push args
     858     68/push  "00\n"/imm32
     859     68/push  _test-input-stream/imm32
     860     # . . call
     861     e8/call  write/disp32
     862     # . . discard args
     863     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     864     # . write(_test-input-stream, "x:\n")
     865     # . . push args
     866     68/push  "x:\n"/imm32
     867     68/push  _test-input-stream/imm32
     868     # . . call
     869     e8/call  write/disp32
     870     # . . discard args
     871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     872     # . write(_test-input-stream, "34\n")
     873     # . . push args
     874     68/push  "34\n"/imm32
     875     68/push  _test-input-stream/imm32
     876     # . . call
     877     e8/call  write/disp32
     878     # . . discard args
     879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     880     # compute-offsets(_test-input-stream, labels)
     881     # . . push args
     882     52/push-edx
     883     68/push  _test-input-stream/imm32
     884     # . . call
     885     e8/call  compute-offsets/disp32
     886     # . . discard args
     887     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32          # add to esp
     888 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
     914     # . check-trace-contains("label 'x' is at address 0x00007c06.", msg)
     915     # . . push args
     916     68/push  "F - test-compute-offsets"/imm32
     917     68/push  "label 'x' is at address 0x00007c06."/imm32
     918     # . . call
     919     e8/call  check-trace-contains/disp32
     920     # . . discard args
     921     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     922     # . check-ints-equal(labels->write, 0xc, msg)
     923     # . . push args
     924     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
     925     68/push  0xc/imm32/1-entry
     926     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
     927     # . . call
     928     e8/call  check-ints-equal/disp32
     929     # . . discard args
     930     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     931     # . reclaim locals
     932     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x24/imm32        # add to esp
     933     # . epilogue
     934     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     935     5d/pop-to-ebp
     936     c3/return
     937
     938 # global scratch space for emit-output
     939 == data
     940
     941 emit-output:datum:  # slice
     942   0/imm32/start
     943   0/imm32/end
     944
     945 == code
     946
     947 emit-output:  # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), address})
     948     # pseudocode:
     949     #   var address-of-next-instruction = 0x7c00
     950     #   var line: (stream byte 512)
     951     #   line-loop:
     952     #   while true
     953     #     clear-stream(line)
     954     #     read-line(in, line)
     955     #     if (line->write == 0) break               # end of file
     956     #     address-of-next-instruction += num-bytes(line)
     957     #     var far-jump-or-call? = far-jump-or-call?(line)
     958     #     rewind-stream(line)
     959     #     while true
     960     #       var word-slice = next-word(line)
     961     #       if slice-empty?(word-slice)             # end of line
     962     #         break
     963     #       if slice-starts-with?(word-slice, "#")  # comment
     964     #         break
     965     #       if label?(word-slice)                # no need for label declarations anymore
     966     #         goto line-loop                        # don't insert empty lines
     967     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
     968     #         word-slice = next-word(line)          # skip segment name
     969     #         word-slice = next-word(line)
     970     #         if !slice-empty?(word-slice)
     971     #           new-address = parse-hex-int-from-slice(word-slice)
     972     #           write-buffered(out, "# " address-of-next-instruction "\n")
     973     #           while address-of-next-instruction < new-address
     974     #             write-buffered("00")
     975     #             ++address-of-next-instruction
     976     #           write-buffered(out, "# " address-of-next-instruction "\n")
     977     #           goto line-loop                      # don't insert empty lines
     978     #       if length(word-slice) == 2
     979     #         write-slice-buffered(out, word-slice)
     980     #         write-buffered(out, " ")
     981     #         continue
     982     #       var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/")
     983     #       var address: (addr int) = get-slice(labels, datum)
     984     #       if has-metadata?(word-slice, "imm8")
     985     #         emit(out, *address, 1)
     986     #       else if has-metadata?(word-slice, "imm16")
     987     #         emit(out, *address, 2)
     988     #       else if has-metadata?(word-slice, "imm32")
     989     #         emit(out, *address, 4)
     990     #       else if has-metadata?(word-slice, "disp8")
     991     #         value = *address - address-of-next-instruction
     992     #         emit(out, value, 1)
     993     #       else if has-metadata?(word-slice, "disp32")
     994     #         if far-jump-or-call?
     995     #           value = *address - address-of-next-instruction
     996     #         else
     997     #           value = *address
     998     #         emit(out, value, 4)
     999     #       else
    1000     #         abort
    1001     #     write-buffered(out, "\n")
    1002     #
    1003     # registers:
    1004     #   line: ecx
    1005     #   word-slice: edx
    1006     #   address-of-next-instruction: ebx
    1007     #   far-jump-or-call?: edi
    1008     #   address: esi (inner loop only)
    1009     #   temporaries: eax, esi (outer loop)
    1010     #
    1011     # . prologue
    1012     55/push-ebp
    1013     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1014     # . save registers
    1015     50/push-eax
    1016     51/push-ecx
    1017     52/push-edx
    1018     53/push-ebx
    1019     56/push-esi
    1020     57/push-edi
    1021     # var line/ecx: (stream byte 512)
    1022     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
    1023     68/push  0x200/imm32/size
    1024     68/push  0/imm32/read
    1025     68/push  0/imm32/write
    1026     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1027     # var word-slice/edx: slice
    1028     68/push  0/imm32/end
    1029     68/push  0/imm32/start
    1030     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1031     # var address-of-next-instruction/ebx = 0x7c00
    1032     bb/copy-to-ebx  0x7c00/imm32
    1033 $emit-output:line-loop:
    1034     # clear-stream(line)
    1035     # . . push args
    1036     51/push-ecx
    1037     # . . call
    1038     e8/call  clear-stream/disp32
    1039     # . . discard args
    1040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1041     # read-line(in, line)
    1042     # . . push args
    1043     51/push-ecx
    1044     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1045     # . . call
    1046     e8/call  read-line/disp32
    1047     # . . discard args
    1048     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1049 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
    1082 $emit-output:check-for-end-of-input:
    1083     # if (line->write == 0) break
    1084     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
    1085     0f 84/jump-if-=  $emit-output:end/disp32
    1086     # address-of-next-instruction += num-bytes(line)
    1087     # . eax = num-bytes(line)
    1088     # . . push args
    1089     51/push-ecx
    1090     # . . call
    1091     e8/call  num-bytes/disp32
    1092     # . . discard args
    1093     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1094     # . ebx += eax
    1095     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
    1096     # var far-jump-or-call?/edi: boolean = far-jump-or-call?(line)
    1097     # . . push args
    1098     51/push-ecx
    1099     # . . call
    1100     e8/call  far-jump-or-call?/disp32
    1101     # . . discard args
    1102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1103     # rewind-stream(line)
    1104     # . . push args
    1105     51/push-ecx
    1106     # . . call
    1107     e8/call  rewind-stream/disp32
    1108     # . . discard args
    1109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1110 $emit-output:word-loop:
    1111     # next-word(line, word-slice)
    1112     # . . push args
    1113     52/push-edx
    1114     51/push-ecx
    1115     # . . call
    1116     e8/call  next-word/disp32
    1117     # . . discard args
    1118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1119 +-- 33 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
    1152 $emit-output:check-for-end-of-line:
    1153     # if (slice-empty?(word-slice)) break
    1154     # . eax = slice-empty?(word-slice)
    1155     # . . push args
    1156     52/push-edx
    1157     # . . call
    1158     e8/call  slice-empty?/disp32
    1159     # . . discard args
    1160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1161     # . if (eax != 0) break
    1162     3d/compare-eax-and  0/imm32/false
    1163     0f 85/jump-if-!=  $emit-output:next-line/disp32
    1164 $emit-output:check-for-comment:
    1165     # if (slice-starts-with?(word-slice, "#")) break
    1166     # . start/esi = word-slice->start
    1167     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
    1168     # . c/eax = *start
    1169     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    1170     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
    1171     # . if (eax == '#') break
    1172     3d/compare-eax-and  0x23/imm32/hash
    1173     0f 84/jump-if-=  $emit-output:next-line/disp32
    1174 $emit-output:check-for-label:
    1175     # if label?(word-slice) break
    1176     # . eax = label?(word-slice)
    1177     # . . push args
    1178     52/push-edx
    1179     # . . call
    1180     e8/call  label?/disp32
    1181     # . . discard args
    1182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1183     # . if (eax != false) break
    1184     3d/compare-eax-and  0/imm32/false
    1185     0f 85/jump-if-!=  $emit-output:line-loop/disp32
    1186 $emit-output:check-for-segment-header:
    1187     # if !slice-equal?(word-slice, "==") goto next check
    1188     # . eax = slice-equal?(word-slice, "==")
    1189     # . . push args
    1190     68/push  "=="/imm32
    1191     52/push-edx
    1192     # . . call
    1193     e8/call  slice-equal?/disp32
    1194     # . . discard args
    1195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1196     # . if (eax == false) goto next check
    1197     3d/compare-eax-and  0/imm32/false
    1198     0f 84/jump-if-=  $emit-output:2-character/disp32
    1199     # skip segment name
    1200     # . next-word(line, word-slice)
    1201     # . . push args
    1202     52/push-edx
    1203     51/push-ecx
    1204     # . . call
    1205     e8/call  next-word/disp32
    1206     # . . discard args
    1207     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1208     # compute segment address if it exists
    1209     # . next-word(line, word-slice)
    1210     # . . push args
    1211     52/push-edx
    1212     51/push-ecx
    1213     # . . call
    1214     e8/call  next-word/disp32
    1215     # . . discard args
    1216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1217     # . if slice-empty?(word-slice) goto padding-done
    1218     # . . push args
    1219     52/push-edx
    1220     # . . call
    1221     e8/call  slice-empty?/disp32
    1222     # . . discard args
    1223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1224     # . .
    1225     3d/compare-eax-and  0/imm32/false
    1226     0f 85/jump-if-!=  $emit-output:padding-done/disp32
    1227     # . var new-address/eax: int = parse-hex-int-from-slice(word-slice)
    1228     # . . push args
    1229     52/push-edx
    1230     # . . call
    1231     e8/call  parse-hex-int-from-slice/disp32
    1232     # . . discard args
    1233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1234     # write-buffered(out, "# " address-of-next-instruction "\n")
    1235     # . write-buffered(out, "# ")
    1236     # . . push args
    1237     68/push  "# "/imm32
    1238     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1239     # . . call
    1240     e8/call  write-buffered/disp32
    1241     # . . discard args
    1242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1243     # . write-int32-hex-buffered(out, address-of-next-instruction)
    1244     # . . push args
    1245     53/push-ebx
    1246     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1247     # . . call
    1248     e8/call  write-int32-hex-buffered/disp32
    1249     # . . discard args
    1250     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1251     # . write-buffered(out, "\n")
    1252     # . . push args
    1253     68/push  Newline/imm32
    1254     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1255     # . . call
    1256     e8/call  write-buffered/disp32
    1257     # . . discard args
    1258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1259 $emit-output:padding-loop:
    1260     # if (address-of-next-instruction >= new-address) goto padding-loop-done
    1261     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # compare ebx with eax
    1262     73/jump-if-addr>=  $emit-output:padding-loop-done/disp8
    1263     # if (address-of-next-instruction % 8 == 0) write-buffered("\n")
    1264     53/push-ebx
    1265     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               7/imm32           # bitwise and of ebx
    1266     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
    1267     5b/pop-to-ebx
    1268     75/jump-if-!=  $emit-output:padding-core/disp8
    1269     # . write-buffered(out, "\n")
    1270     # . . push args
    1271     68/push  Newline/imm32
    1272     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1273     # . . call
    1274     e8/call  write-buffered/disp32
    1275     # . . discard args
    1276     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1277 $emit-output:padding-core:
    1278     # write-buffered("00")
    1279     # . . push args
    1280     68/push  "00 "/imm32
    1281     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1282     # . . call
    1283     e8/call  write-buffered/disp32
    1284     # . . discard args
    1285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1286     # ++address-of-next-instruction
    1287     43/increment-ebx
    1288     # loop
    1289     eb/jump $emit-output:padding-loop/disp8
    1290 $emit-output:padding-loop-done:
    1291     # . write-buffered(out, "\n")
    1292     # . . push args
    1293     68/push  Newline/imm32
    1294     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1295     # . . call
    1296     e8/call  write-buffered/disp32
    1297     # . . discard args
    1298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1299 $emit-output:padding-done:
    1300     # write-buffered(out, "# " address-of-next-instruction "\n")
    1301     # . write-buffered(out, "# ")
    1302     # . . push args
    1303     68/push  "# "/imm32
    1304     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1305     # . . call
    1306     e8/call  write-buffered/disp32
    1307     # . . discard args
    1308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1309     # . write-int32-hex-buffered(out, address-of-next-instruction)
    1310     # . . push args
    1311     53/push-ebx
    1312     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1313     # . . call
    1314     e8/call  write-int32-hex-buffered/disp32
    1315     # . . discard args
    1316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1317     # . write-buffered(out, "\n")
    1318     # . . push args
    1319     68/push  Newline/imm32
    1320     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1321     # . . call
    1322     e8/call  write-buffered/disp32
    1323     # . . discard args
    1324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1325     #
    1326     e9/jump  $emit-output:line-loop/disp32
    1327 $emit-output:2-character:
    1328     # if (size(word-slice) != 2) goto next check
    1329     # . eax = size(word-slice)
    1330     8b/copy                         1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(edx+4) to eax
    1331     2b/subtract                     0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # subtract *edx from eax
    1332     # . if (eax != 2) goto next check
    1333     3d/compare-eax-and  2/imm32
    1334     75/jump-if-!=  $emit-output:check-metadata/disp8
    1335     # write-slice-buffered(out, word-slice)
    1336     # . . push args
    1337     52/push-edx
    1338     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1339     # . . call
    1340     e8/call  write-slice-buffered/disp32
    1341     # . . discard args
    1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1343     # write-buffered(out, " ")
    1344     # . . push args
    1345     68/push  Space/imm32
    1346     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1347     # . . call
    1348     e8/call  write-buffered/disp32
    1349     # . . discard args
    1350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1351     # continue
    1352     e9/jump  $emit-output:word-loop/disp32
    1353 $emit-output:check-metadata:
    1354     # - if we get here, 'word-slice' must be a label to be looked up
    1355     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
    1356     # . . push args
    1357     68/push  emit-output:datum/imm32
    1358     68/push  0x2f/imm32/slash
    1359     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    1360     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    1361     # . . call
    1362     e8/call  next-token-from-slice/disp32
    1363     # . . discard args
    1364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1365 +-- 33 lines: #?     # dump datum -------------------------------------------------------------------------------------------------------------------------------------------------------
    1398     # address/esi: (addr int) = get-slice(labels, datum, row-size=12, "label table")
    1399     # . eax = get-slice(labels, datum, row-size=24, "label table")
    1400     # . . push args
    1401     68/push  "label table"/imm32
    1402     68/push  0xc/imm32/row-size
    1403     68/push  emit-output:datum/imm32
    1404     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    1405     # . . call
    1406     e8/call  get-slice/disp32
    1407     # . . discard args
    1408     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1409     # . esi = eax
    1410     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
    1411 $emit-output:check-imm8:
    1412     # if (!has-metadata?(word-slice, "imm8")) goto next check
    1413     # . eax = has-metadata?(edx, "imm8")
    1414     # . . push args
    1415     68/push  "imm8"/imm32
    1416     52/push-edx
    1417     # . . call
    1418     e8/call  has-metadata?/disp32
    1419     # . . discard args
    1420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1421     # . if (eax != false) abort
    1422     3d/compare-eax-and  0/imm32/false
    1423     74/jump-if-=  $emit-output:check-imm16/disp8
    1424 $emit-output:emit-imm8:
    1425     # emit-hex(out, *address, 1)
    1426     # . . push args
    1427     68/push  1/imm32
    1428     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
    1429     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1430     # . . call
    1431     e8/call  emit-hex/disp32
    1432     # . . discard args
    1433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1434     # continue
    1435     e9/jump  $emit-output:word-loop/disp32
    1436 $emit-output:check-imm16:
    1437     # if (!has-metadata?(word-slice, "imm16")) goto next check
    1438     # . eax = has-metadata?(edx, "imm16")
    1439     # . . push args
    1440     68/push  "imm16"/imm32
    1441     52/push-edx
    1442     # . . call
    1443     e8/call  has-metadata?/disp32
    1444     # . . discard args
    1445     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1446     # . if (eax == false) goto next check
    1447     3d/compare-eax-and  0/imm32/false
    1448     74/jump-if-=  $emit-output:check-imm32/disp8
    1449 +-- 33 lines: #?     # dump *address ----------------------------------------------------------------------------------------------------------------------------------------------------
    1482 $emit-output:emit-imm16:
    1483     # emit-hex(out, *address, 2)
    1484     # . . push args
    1485     68/push  2/imm32
    1486     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
    1487     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1488     # . . call
    1489     e8/call  emit-hex/disp32
    1490     # . . discard args
    1491     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1492     # TODO: ensure that the higher 2 bytes are zero
    1493     # continue
    1494     e9/jump  $emit-output:word-loop/disp32
    1495 $emit-output:check-imm32:
    1496     # if (!has-metadata?(word-slice, "imm32")) goto next check
    1497     # . eax = has-metadata?(edx, "imm32")
    1498     # . . push args
    1499     68/push  "imm32"/imm32
    1500     52/push-edx
    1501     # . . call
    1502     e8/call  has-metadata?/disp32
    1503     # . . discard args
    1504     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1505     # . if (eax == false) goto next check
    1506     3d/compare-eax-and  0/imm32/false
    1507     74/jump-if-=  $emit-output:check-disp8/disp8
    1508 +-- 33 lines: #?     # dump *address ----------------------------------------------------------------------------------------------------------------------------------------------------
    1541 $emit-output:emit-imm32:
    1542     # emit-hex(out, *address, 4)
    1543     # . . push args
    1544     68/push  4/imm32
    1545     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
    1546     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1547     # . . call
    1548     e8/call  emit-hex/disp32
    1549     # . . discard args
    1550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1551     # continue
    1552     e9/jump  $emit-output:word-loop/disp32
    1553 $emit-output:check-disp8:
    1554     # if (!has-metadata?(word-slice, "disp8")) goto next check
    1555     # . eax = has-metadata?(edx, "disp8")
    1556     # . . push args
    1557     68/push  "disp8"/imm32
    1558     52/push-edx
    1559     # . . call
    1560     e8/call  has-metadata?/disp32
    1561     # . . discard args
    1562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1563     # . if (eax == false) goto next check
    1564     3d/compare-eax-and  0/imm32/false
    1565     74/jump-if-=  $emit-output:check-disp16/disp8
    1566 $emit-output:emit-disp8:
    1567     # emit-hex(out, *address - address-of-next-instruction, 1)
    1568     # . . push args
    1569     68/push  1/imm32
    1570     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    1571     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
    1572     50/push-eax
    1573     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1574     # . . call
    1575     e8/call  emit-hex/disp32
    1576     # . . discard args
    1577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1578     # continue
    1579     e9/jump  $emit-output:word-loop/disp32
    1580 $emit-output:check-disp16:
    1581     # if (!has-metadata?(word-slice, "disp16")) goto next check
    1582     # . eax = has-metadata?(edx, "disp16")
    1583     # . . push args
    1584     68/push  "disp16"/imm32
    1585     52/push-edx
    1586     # . . call
    1587     e8/call  has-metadata?/disp32
    1588     # . . discard args
    1589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1590     # . if (eax == false) goto next check
    1591     3d/compare-eax-and  0/imm32/false
    1592     74/jump-if-=  $emit-output:check-disp32/disp8
    1593 $emit-output:emit-disp16:
    1594     # emit-hex(out, *address - address-of-next-instruction, 2)
    1595     # . . push args
    1596     68/push  2/imm32
    1597     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    1598     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
    1599     50/push-eax
    1600     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1601     # . . call
    1602     e8/call  emit-hex/disp32
    1603     # . . discard args
    1604     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1605     # continue
    1606     e9/jump  $emit-output:word-loop/disp32
    1607 $emit-output:check-disp32:
    1608     # if (!has-metadata?(word-slice, "disp32")) abort
    1609     # . eax = has-metadata?(edx, "disp32")
    1610     # . . push args
    1611     68/push  "disp32"/imm32
    1612     52/push-edx
    1613     # . . call
    1614     e8/call  has-metadata?/disp32
    1615     # . . discard args
    1616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1617     # . if (eax == false) abort
    1618     3d/compare-eax-and  0/imm32/false
    1619     0f 84/jump-if-=  $emit-output:abort/disp32
    1620 $emit-output:emit-disp32:
    1621     # var value/eax = *address
    1622     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    1623     # if (far-jump-or-call?) value -= address-of-next-instruction
    1624     81          7/subop/compare     3/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32/false     # compare edi
    1625     74/jump-if-=  $emit-output:really-emit-disp32/disp8
    1626     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
    1627 $emit-output:really-emit-disp32:
    1628     # emit-hex(out, value, 4)
    1629     # . . push args
    1630     68/push  4/imm32
    1631     50/push-eax
    1632     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1633     # . . call
    1634     e8/call  emit-hex/disp32
    1635     # . . discard args
    1636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1637     # continue
    1638     e9/jump  $emit-output:word-loop/disp32
    1639 $emit-output:next-line:
    1640     # write-buffered(out, "\n")
    1641     # . . push args
    1642     68/push  Newline/imm32
    1643     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1644     # . . call
    1645     e8/call  write-buffered/disp32
    1646     # . . discard args
    1647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1648     # loop
    1649     e9/jump  $emit-output:line-loop/disp32
    1650 $emit-output:end:
    1651     # . reclaim locals
    1652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
    1653     # . restore registers
    1654     5f/pop-to-edi
    1655     5e/pop-to-esi
    1656     5b/pop-to-ebx
    1657     5a/pop-to-edx
    1658     59/pop-to-ecx
    1659     58/pop-to-eax
    1660     # . epilogue
    1661     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1662     5d/pop-to-ebp
    1663     c3/return
    1664
    1665 $emit-output:abort:
    1666     # print(stderr, "missing metadata in " word-slice)
    1667     # . _write(2/stderr, "missing metadata in word ")
    1668     # . . push args
    1669     68/push  "emit-output: missing metadata in "/imm32
    1670     68/push  2/imm32/stderr
    1671     # . . call
    1672     e8/call  _write/disp32
    1673     # . . discard args
    1674     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1675     # . write-slice-buffered(Stderr, word-slice)
    1676     # . . push args
    1677     52/push-edx
    1678     68/push  Stderr/imm32
    1679     # . . call
    1680     e8/call  write-slice-buffered/disp32
    1681     # . . discard args
    1682     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1683     # . flush(Stderr)
    1684     # . . push args
    1685     68/push  Stderr/imm32
    1686     # . . call
    1687     e8/call  flush/disp32
    1688     # . . discard args
    1689     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1690     # . syscall(exit, 1)
    1691     bb/copy-to-ebx  1/imm32
    1692     e8/call  syscall_exit/disp32
    1693     # never gets here
    1694
    1695 test-emit-output-non-far-control-flow:
    1696     # labels turn into absolute addresses if opcodes are not far jumps or calls
    1697     #
    1698     # input:
    1699     #   in:
    1700     #     == code
    1701     #     ab cd ef gh
    1702     #     ij x/disp32
    1703     #     == data
    1704     #     00
    1705     #     34
    1706     #   labels:
    1707     #     - 'x': 0x11223344
    1708     #
    1709     # output:
    1710     #   ab cd ef gh
    1711     #   ij 44 33 22 11
    1712     #   00
    1713     #   34
    1714     #
    1715     # . prologue
    1716     55/push-ebp
    1717     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1718     # setup
    1719     # . clear-stream(_test-input-stream)
    1720     # . . push args
    1721     68/push  _test-input-stream/imm32
    1722     # . . call
    1723     e8/call  clear-stream/disp32
    1724     # . . discard args
    1725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1726     # . clear-stream(_test-output-stream)
    1727     # . . push args
    1728     68/push  _test-output-stream/imm32
    1729     # . . call
    1730     e8/call  clear-stream/disp32
    1731     # . . discard args
    1732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1733     # . clear-stream($_test-output-buffered-file->buffer)
    1734     # . . push args
    1735     68/push  $_test-output-buffered-file->buffer/imm32
    1736     # . . call
    1737     e8/call  clear-stream/disp32
    1738     # . . discard args
    1739     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1740     # . var labels/edx: (stream byte 8*24)
    1741     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    1742     68/push  0xc0/imm32/size
    1743     68/push  0/imm32/read
    1744     68/push  0/imm32/write
    1745     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1746     # . var h/ebx: (handle array byte)
    1747     68/push  0/imm32
    1748     68/push  0/imm32
    1749     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1750     # initialize input
    1751     # . write(_test-input-stream, "== code\n")
    1752     # . . push args
    1753     68/push  "== code\n"/imm32
    1754     68/push  _test-input-stream/imm32
    1755     # . . call
    1756     e8/call  write/disp32
    1757     # . . discard args
    1758     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1759     # . write(_test-input-stream, "ab cd ef gh\n")
    1760     # . . push args
    1761     68/push  "ab cd ef gh\n"/imm32
    1762     68/push  _test-input-stream/imm32
    1763     # . . call
    1764     e8/call  write/disp32
    1765     # . . discard args
    1766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1767     # . write(_test-input-stream, "ij x/disp32\n")
    1768     # . . push args
    1769     68/push  "ij x/disp32\n"/imm32
    1770     68/push  _test-input-stream/imm32
    1771     # . . call
    1772     e8/call  write/disp32
    1773     # . . discard args
    1774     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1775     # . write(_test-input-stream, "== data\n")
    1776     # . . push args
    1777     68/push  "== data\n"/imm32
    1778     68/push  _test-input-stream/imm32
    1779     # . . call
    1780     e8/call  write/disp32
    1781     # . . discard args
    1782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1783     # . write(_test-input-stream, "00\n")
    1784     # . . push args
    1785     68/push  "00\n"/imm32
    1786     68/push  _test-input-stream/imm32
    1787     # . . call
    1788     e8/call  write/disp32
    1789     # . . discard args
    1790     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1791     # . write(_test-input-stream, "34\n")
    1792     # . . push args
    1793     68/push  "34\n"/imm32
    1794     68/push  _test-input-stream/imm32
    1795     # . . call
    1796     e8/call  write/disp32
    1797     # . . discard args
    1798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1799     # . stream-add2(labels, "x", 0x11223344)
    1800     68/push  0x11223344/imm32/label-address
    1801     # . . push handle for "x"
    1802     53/push-ebx
    1803     68/push  "x"/imm32
    1804     68/push  Heap/imm32
    1805     e8/call  copy-array/disp32
    1806     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1807     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1808     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1809     # . . push labels
    1810     52/push-edx
    1811     # . . call
    1812     e8/call  stream-add2/disp32
    1813     # . . discard args
    1814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1815     # component under test
    1816     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
    1817     # . . push args
    1818     52/push-edx
    1819     68/push  _test-output-buffered-file/imm32
    1820     68/push  _test-input-stream/imm32
    1821     # . . call
    1822     e8/call  emit-output/disp32
    1823     # . . discard args
    1824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1825     # checks
    1826     # . flush(_test-output-buffered-file)
    1827     # . . push args
    1828     68/push  _test-output-buffered-file/imm32
    1829     # . . call
    1830     e8/call  flush/disp32
    1831     # . . discard args
    1832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1833 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    1866     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
    1867     # . . push args
    1868     68/push  "F - test-emit-output-non-far-control-flow/0"/imm32
    1869     68/push  "# 0x00007c00"/imm32
    1870     68/push  _test-output-stream/imm32
    1871     # . . call
    1872     e8/call  check-next-stream-line-equal/disp32
    1873     # . . discard args
    1874     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1875     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
    1876     # . . push args
    1877     68/push  "F - test-emit-output-non-far-control-flow/1"/imm32
    1878     68/push  "ab cd ef gh "/imm32
    1879     68/push  _test-output-stream/imm32
    1880     # . . call
    1881     e8/call  check-next-stream-line-equal/disp32
    1882     # . . discard args
    1883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1884     # . check-next-stream-line-equal(_test-output-stream, "ij 44 33 22 11 ", msg)
    1885     # . . push args
    1886     68/push  "F - test-emit-output-non-far-control-flow/2"/imm32
    1887     68/push  "ij 44 33 22 11 "/imm32
    1888     68/push  _test-output-stream/imm32
    1889     # . . call
    1890     e8/call  check-next-stream-line-equal/disp32
    1891     # . . discard args
    1892     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1893     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c09", msg)
    1894     # . . push args
    1895     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
    1896     68/push  "# 0x00007c09"/imm32
    1897     68/push  _test-output-stream/imm32
    1898     # . . call
    1899     e8/call  check-next-stream-line-equal/disp32
    1900     # . . discard args
    1901     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1902     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
    1903     # . . push args
    1904     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
    1905     68/push  "00 "/imm32
    1906     68/push  _test-output-stream/imm32
    1907     # . . call
    1908     e8/call  check-next-stream-line-equal/disp32
    1909     # . . discard args
    1910     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1911     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
    1912     # . . push args
    1913     68/push  "F - test-emit-output-non-far-control-flow/4"/imm32
    1914     68/push  "34 "/imm32
    1915     68/push  _test-output-stream/imm32
    1916     # . . call
    1917     e8/call  check-next-stream-line-equal/disp32
    1918     # . . discard args
    1919     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1920     # . epilogue
    1921     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1922     5d/pop-to-ebp
    1923     c3/return
    1924
    1925 test-emit-output-with-padding:
    1926     # labels turn into absolute addresses if opcodes are not far jumps or calls
    1927     #
    1928     # input:
    1929     #   in:
    1930     #     == code
    1931     #     ab cd ef gh
    1932     #     == data 0x7c10
    1933     #     34
    1934     #
    1935     # output:
    1936     #   ab cd ef gh
    1937     #   # 0x7c04
    1938     #   00 00 00 00
    1939     #   00 00 00 00 00 00 00 00
    1940     #   # 0x7c10
    1941     #   34
    1942     #
    1943     # . prologue
    1944     55/push-ebp
    1945     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1946     # setup
    1947     # . clear-stream(_test-input-stream)
    1948     # . . push args
    1949     68/push  _test-input-stream/imm32
    1950     # . . call
    1951     e8/call  clear-stream/disp32
    1952     # . . discard args
    1953     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1954     # . clear-stream(_test-output-stream)
    1955     # . . push args
    1956     68/push  _test-output-stream/imm32
    1957     # . . call
    1958     e8/call  clear-stream/disp32
    1959     # . . discard args
    1960     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1961     # . clear-stream($_test-output-buffered-file->buffer)
    1962     # . . push args
    1963     68/push  $_test-output-buffered-file->buffer/imm32
    1964     # . . call
    1965     e8/call  clear-stream/disp32
    1966     # . . discard args
    1967     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1968     # . var labels/edx: (stream byte 8*24)
    1969     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    1970     68/push  0xc0/imm32/size
    1971     68/push  0/imm32/read
    1972     68/push  0/imm32/write
    1973     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1974     # . var h/ebx: (handle array byte)
    1975     68/push  0/imm32
    1976     68/push  0/imm32
    1977     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1978     # initialize input
    1979     # . write(_test-input-stream, "== code\n")
    1980     # . . push args
    1981     68/push  "== code\n"/imm32
    1982     68/push  _test-input-stream/imm32
    1983     # . . call
    1984     e8/call  write/disp32
    1985     # . . discard args
    1986     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1987     # . write(_test-input-stream, "ab cd ef gh\n")
    1988     # . . push args
    1989     68/push  "ab cd ef gh\n"/imm32
    1990     68/push  _test-input-stream/imm32
    1991     # . . call
    1992     e8/call  write/disp32
    1993     # . . discard args
    1994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1995     # . write(_test-input-stream, "== data 0x7c10\n")
    1996     # . . push args
    1997     68/push  "== data 0x7c10\n"/imm32
    1998     68/push  _test-input-stream/imm32
    1999     # . . call
    2000     e8/call  write/disp32
    2001     # . . discard args
    2002     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2003     # . write(_test-input-stream, "34\n")
    2004     # . . push args
    2005     68/push  "34\n"/imm32
    2006     68/push  _test-input-stream/imm32
    2007     # . . call
    2008     e8/call  write/disp32
    2009     # . . discard args
    2010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2011     # component under test
    2012     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
    2013     # . . push args
    2014     52/push-edx
    2015     68/push  _test-output-buffered-file/imm32
    2016     68/push  _test-input-stream/imm32
    2017     # . . call
    2018     e8/call  emit-output/disp32
    2019     # . . discard args
    2020     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2021     # checks
    2022     # . flush(_test-output-buffered-file)
    2023     # . . push args
    2024     68/push  _test-output-buffered-file/imm32
    2025     # . . call
    2026     e8/call  flush/disp32
    2027     # . . discard args
    2028     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2029 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    2062     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
    2063     # . . push args
    2064     68/push  "F - test-emit-output-with-padding/0"/imm32
    2065     68/push  "# 0x00007c00"/imm32
    2066     68/push  _test-output-stream/imm32
    2067     # . . call
    2068     e8/call  check-next-stream-line-equal/disp32
    2069     # . . discard args
    2070     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2071     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
    2072     # . . push args
    2073     68/push  "F - test-emit-output-with-padding/1"/imm32
    2074     68/push  "ab cd ef gh "/imm32
    2075     68/push  _test-output-stream/imm32
    2076     # . . call
    2077     e8/call  check-next-stream-line-equal/disp32
    2078     # . . discard args
    2079     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2080     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c04", msg)
    2081     # . . push args
    2082     68/push  "F - test-emit-output-with-padding/0"/imm32
    2083     68/push  "# 0x00007c04"/imm32
    2084     68/push  _test-output-stream/imm32
    2085     # . . call
    2086     e8/call  check-next-stream-line-equal/disp32
    2087     # . . discard args
    2088     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2089     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
    2090     # . . push args
    2091     68/push  "F - test-emit-output-with-padding/2"/imm32
    2092     68/push  "00 00 00 00 "/imm32
    2093     68/push  _test-output-stream/imm32
    2094     # . . call
    2095     e8/call  check-next-stream-line-equal/disp32
    2096     # . . discard args
    2097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2098     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
    2099     # . . push args
    2100     68/push  "F - test-emit-output-with-padding/3"/imm32
    2101     68/push  "00 00 00 00 00 00 00 00 "/imm32
    2102     68/push  _test-output-stream/imm32
    2103     # . . call
    2104     e8/call  check-next-stream-line-equal/disp32
    2105     # . . discard args
    2106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2107     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c10", msg)
    2108     # . . push args
    2109     68/push  "F - test-emit-output-with-padding/0"/imm32
    2110     68/push  "# 0x00007c10"/imm32
    2111     68/push  _test-output-stream/imm32
    2112     # . . call
    2113     e8/call  check-next-stream-line-equal/disp32
    2114     # . . discard args
    2115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2116     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
    2117     # . . push args
    2118     68/push  "F - test-emit-output-with-padding/4"/imm32
    2119     68/push  "34 "/imm32
    2120     68/push  _test-output-stream/imm32
    2121     # . . call
    2122     e8/call  check-next-stream-line-equal/disp32
    2123     # . . discard args
    2124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2125     # . epilogue
    2126     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2127     5d/pop-to-ebp
    2128     c3/return
    2129
    2130 test-emit-output-code-label:
    2131     # labels turn into PC-relative addresses if opcodes are far jumps or calls
    2132     #
    2133     # input:
    2134     #   in:
    2135     #     == code
    2136     #     ab cd
    2137     #     ef gh
    2138     #     e8 l1/disp32
    2139     #   labels:
    2140     #     - 'l1': 0x7c10
    2141     #
    2142     # output:
    2143     #   ab cd
    2144     #   ef gh
    2145     #   e8 07 00 00 00  # 0x7c10 - 0x7c09 = 7
    2146     #
    2147     # . prologue
    2148     55/push-ebp
    2149     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2150     # setup
    2151     # . clear-stream(_test-input-stream)
    2152     # . . push args
    2153     68/push  _test-input-stream/imm32
    2154     # . . call
    2155     e8/call  clear-stream/disp32
    2156     # . . discard args
    2157     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2158     # . clear-stream(_test-output-stream)
    2159     # . . push args
    2160     68/push  _test-output-stream/imm32
    2161     # . . call
    2162     e8/call  clear-stream/disp32
    2163     # . . discard args
    2164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2165     # . clear-stream($_test-output-buffered-file->buffer)
    2166     # . . push args
    2167     68/push  $_test-output-buffered-file->buffer/imm32
    2168     # . . call
    2169     e8/call  clear-stream/disp32
    2170     # . . discard args
    2171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2172     # . var labels/edx: (stream byte 8*24)
    2173     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    2174     68/push  0xc0/imm32/size
    2175     68/push  0/imm32/read
    2176     68/push  0/imm32/write
    2177     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2178     # . var h/ebx: (handle array byte)
    2179     68/push  0/imm32
    2180     68/push  0/imm32
    2181     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    2182     # initialize input
    2183     # . write(_test-input-stream, "== code\n")
    2184     # . . push args
    2185     68/push  "== code\n"/imm32
    2186     68/push  _test-input-stream/imm32
    2187     # . . call
    2188     e8/call  write/disp32
    2189     # . . discard args
    2190     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2191     # . write(_test-input-stream, "ab cd\n")
    2192     # . . push args
    2193     68/push  "ab cd\n"/imm32
    2194     68/push  _test-input-stream/imm32
    2195     # . . call
    2196     e8/call  write/disp32
    2197     # . . discard args
    2198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2199     # . write(_test-input-stream, "ef gh\n")
    2200     # . . push args
    2201     68/push  "ef gh\n"/imm32
    2202     68/push  _test-input-stream/imm32
    2203     # . . call
    2204     e8/call  write/disp32
    2205     # . . discard args
    2206     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2207     # . write(_test-input-stream, "e8 l1/disp32\n")
    2208     # . . push args
    2209     68/push  "e8 l1/disp32\n"/imm32
    2210     68/push  _test-input-stream/imm32
    2211     # . . call
    2212     e8/call  write/disp32
    2213     # . . discard args
    2214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2215     # . stream-add2(labels, "l1", 0x7c10)
    2216     68/push  0x7c10/imm32/label-address
    2217     # . . push handle for "l1"
    2218     53/push-ebx
    2219     68/push  "l1"/imm32
    2220     68/push  Heap/imm32
    2221     e8/call  copy-array/disp32
    2222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2223     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    2224     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    2225     # . . push labels
    2226     52/push-edx
    2227     # . . call
    2228     e8/call  stream-add2/disp32
    2229     # . . discard args
    2230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2231     # component under test
    2232     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
    2233     # . . push args
    2234     52/push-edx
    2235     68/push  _test-output-buffered-file/imm32
    2236     68/push  _test-input-stream/imm32
    2237     # . . call
    2238     e8/call  emit-output/disp32
    2239     # . . discard args
    2240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2241     # checks
    2242     # . flush(_test-output-buffered-file)
    2243     # . . push args
    2244     68/push  _test-output-buffered-file/imm32
    2245     # . . call
    2246     e8/call  flush/disp32
    2247     # . . discard args
    2248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2249 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    2282     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
    2283     # . . push args
    2284     68/push  "F - test-emit-output-code-label/0"/imm32
    2285     68/push  "# 0x00007c00"/imm32
    2286     68/push  _test-output-stream/imm32
    2287     # . . call
    2288     e8/call  check-next-stream-line-equal/disp32
    2289     # . . discard args
    2290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2291     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
    2292     # . . push args
    2293     68/push  "F - test-emit-output-code-label/1"/imm32
    2294     68/push  "ab cd "/imm32
    2295     68/push  _test-output-stream/imm32
    2296     # . . call
    2297     e8/call  check-next-stream-line-equal/disp32
    2298     # . . discard args
    2299     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2300     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
    2301     # . . push args
    2302     68/push  "F - test-emit-output-code-label/2"/imm32
    2303     68/push  "ef gh "/imm32
    2304     68/push  _test-output-stream/imm32
    2305     # . . call
    2306     e8/call  check-next-stream-line-equal/disp32
    2307     # . . discard args
    2308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2309     # . check-next-stream-line-equal(_test-output-stream, "e8 07 00 00 00 ", msg)
    2310     # . . push args
    2311     68/push  "F - test-emit-output-code-label/3"/imm32
    2312     68/push  "e8 07 00 00 00 "/imm32
    2313     68/push  _test-output-stream/imm32
    2314     # . . call
    2315     e8/call  check-next-stream-line-equal/disp32
    2316     # . . discard args
    2317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2318     # . epilogue
    2319     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2320     5d/pop-to-ebp
    2321     c3/return
    2322
    2323 test-emit-output-code-label-absolute:
    2324     # labels can also convert to absolute addresses
    2325     #
    2326     # input:
    2327     #   in:
    2328     #     == code
    2329     #     ab cd
    2330     #     ef gh
    2331     #     ij l1/imm32
    2332     #   labels:
    2333     #     - 'l1': 0x1056
    2334     #
    2335     # output:
    2336     #   ab cd
    2337     #   ef gh
    2338     #   ij 56 10 00 00
    2339     #
    2340     # . prologue
    2341     55/push-ebp
    2342     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2343     # setup
    2344     # . clear-stream(_test-input-stream)
    2345     # . . push args
    2346     68/push  _test-input-stream/imm32
    2347     # . . call
    2348     e8/call  clear-stream/disp32
    2349     # . . discard args
    2350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2351     # . clear-stream(_test-output-stream)
    2352     # . . push args
    2353     68/push  _test-output-stream/imm32
    2354     # . . call
    2355     e8/call  clear-stream/disp32
    2356     # . . discard args
    2357     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2358     # . clear-stream($_test-output-buffered-file->buffer)
    2359     # . . push args
    2360     68/push  $_test-output-buffered-file->buffer/imm32
    2361     # . . call
    2362     e8/call  clear-stream/disp32
    2363     # . . discard args
    2364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2365     # . var labels/edx: (stream byte 8*24)
    2366     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    2367     68/push  0xc0/imm32/size
    2368     68/push  0/imm32/read
    2369     68/push  0/imm32/write
    2370     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2371     # . var h/ebx: (handle array byte)
    2372     68/push  0/imm32
    2373     68/push  0/imm32
    2374     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    2375     # initialize input
    2376     # . write(_test-input-stream, "== code 0x1000\n")
    2377     # . . push args
    2378     68/push  "== code\n"/imm32
    2379     68/push  _test-input-stream/imm32
    2380     # . . call
    2381     e8/call  write/disp32
    2382     # . . discard args
    2383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2384     # . write(_test-input-stream, "ab cd\n")
    2385     # . . push args
    2386     68/push  "ab cd\n"/imm32
    2387     68/push  _test-input-stream/imm32
    2388     # . . call
    2389     e8/call  write/disp32
    2390     # . . discard args
    2391     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2392     # . write(_test-input-stream, "ef gh\n")
    2393     # . . push args
    2394     68/push  "ef gh\n"/imm32
    2395     68/push  _test-input-stream/imm32
    2396     # . . call
    2397     e8/call  write/disp32
    2398     # . . discard args
    2399     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2400     # . write(_test-input-stream, "ij l1/imm32\n")
    2401     # . . push args
    2402     68/push  "ij l1/imm32\n"/imm32
    2403     68/push  _test-input-stream/imm32
    2404     # . . call
    2405     e8/call  write/disp32
    2406     # . . discard args
    2407     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2408     # . stream-add2(labels, "l1", 0x1056)
    2409     68/push  0x1056/imm32/label-address
    2410     # . . push handle for "l1"
    2411     53/push-ebx
    2412     68/push  "l1"/imm32
    2413     68/push  Heap/imm32
    2414     e8/call  copy-array/disp32
    2415     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2416     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    2417     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    2418     # . . push labels
    2419     52/push-edx
    2420     # . . call
    2421     e8/call  stream-add2/disp32
    2422     # . . discard args
    2423     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2424     # component under test
    2425     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
    2426     # . . push args
    2427     52/push-edx
    2428     68/push  _test-output-buffered-file/imm32
    2429     68/push  _test-input-stream/imm32
    2430     # . . call
    2431     e8/call  emit-output/disp32
    2432     # . . discard args
    2433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2434     # checks
    2435     # . flush(_test-output-buffered-file)
    2436     # . . push args
    2437     68/push  _test-output-buffered-file/imm32
    2438     # . . call
    2439     e8/call  flush/disp32
    2440     # . . discard args
    2441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2442 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    2475     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
    2476     # . . push args
    2477     68/push  "F - test-emit-output-code-label-absolute/0"/imm32
    2478     68/push  "# 0x00007c00"/imm32
    2479     68/push  _test-output-stream/imm32
    2480     # . . call
    2481     e8/call  check-next-stream-line-equal/disp32
    2482     # . . discard args
    2483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2484     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
    2485     # . . push args
    2486     68/push  "F - test-emit-output-code-label-absolute/1"/imm32
    2487     68/push  "ab cd "/imm32
    2488     68/push  _test-output-stream/imm32
    2489     # . . call
    2490     e8/call  check-next-stream-line-equal/disp32
    2491     # . . discard args
    2492     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2493     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
    2494     # . . push args
    2495     68/push  "F - test-emit-output-code-label-absolute/2"/imm32
    2496     68/push  "ef gh "/imm32
    2497     68/push  _test-output-stream/imm32
    2498     # . . call
    2499     e8/call  check-next-stream-line-equal/disp32
    2500     # . . discard args
    2501     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2502     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
    2503     # . . push args
    2504     68/push  "F - test-emit-output-code-label-absolute/3"/imm32
    2505     68/push  "ij 56 10 00 00 "/imm32
    2506     68/push  _test-output-stream/imm32
    2507     # . . call
    2508     e8/call  check-next-stream-line-equal/disp32
    2509     # . . discard args
    2510     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2511     # . epilogue
    2512     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2513     5d/pop-to-ebp
    2514     c3/return
    2515
    2516 # reads line to make some checks
    2517 # don't assume the read state of line after calling this function
    2518 far-jump-or-call?:  # line: (addr stream byte) -> result/edi: boolean
    2519     # . prologue
    2520     55/push-ebp
    2521     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2522     # . save registers
    2523     50/push-eax
    2524     51/push-ecx
    2525     52/push-edx
    2526     53/push-ebx
    2527     # ecx = line
    2528     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
    2529     # var word-slice/edx: slice
    2530     68/push  0/imm32/end
    2531     68/push  0/imm32/start
    2532     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2533     # var datum-slice/ebx: slice
    2534     68/push  0/imm32/end
    2535     68/push  0/imm32/start
    2536     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    2537     # result = false
    2538     bf/copy-to-edi  0/imm32/false
    2539 $far-jump-or-call?:check-first-word:
    2540     # next-word(line, word-slice)
    2541     # . . push args
    2542     52/push-edx
    2543     51/push-ecx
    2544     # . . call
    2545     e8/call  next-word/disp32
    2546     # . . discard args
    2547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2548     # if (slice-empty?(word-slice)) return false
    2549     # . eax = slice-empty?(word-slice)
    2550     # . . push args
    2551     52/push-edx
    2552     # . . call
    2553     e8/call  slice-empty?/disp32
    2554     # . . discard args
    2555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2556     # . if (eax != 0) return
    2557     3d/compare-eax-and  0/imm32/false
    2558     0f 85/jump-if-!=  $far-jump-or-call?:end/disp32
    2559     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
    2560     # . . push args
    2561     53/push-ebx
    2562     68/push  0x2f/imm32/slash
    2563     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    2564     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    2565     # . . call
    2566     e8/call  next-token-from-slice/disp32
    2567     # . . discard args
    2568     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2569     # if (datum-slice == "e8") return true
    2570     # . eax = slice-equal?(datum-slice, "e8")
    2571     # . . push args
    2572     68/push  "e8"/imm32
    2573     53/push-ebx
    2574     # . . call
    2575     e8/call  slice-equal?/disp32
    2576     # . . discard args
    2577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2578     # . if (eax != false) return true
    2579     3d/compare-eax-and  0/imm32/false
    2580     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
    2581     # if (datum-slice == "e9") return true
    2582     # . eax = slice-equal?(datum-slice, "e9")
    2583     # . . push args
    2584     68/push  "e9"/imm32
    2585     53/push-ebx
    2586     # . . call
    2587     e8/call  slice-equal?/disp32
    2588     # . . discard args
    2589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2590     # . if (eax != false) return true
    2591     3d/compare-eax-and  0/imm32/false
    2592     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
    2593     # if (datum-slice != "0f") return false
    2594     # . eax = slice-equal?(datum-slice, "0f")
    2595     # . . push args
    2596     68/push  "0f"/imm32
    2597     53/push-ebx
    2598     # . . call
    2599     e8/call  slice-equal?/disp32
    2600     # . . discard args
    2601     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2602     # . if (eax == false) return
    2603     3d/compare-eax-and  0/imm32/false
    2604     74/jump-if-=  $far-jump-or-call?:end/disp8
    2605 $far-jump-or-call?:check-second-word:
    2606     # next-word(line, word-slice)
    2607     # . . push args
    2608     52/push-edx
    2609     51/push-ecx
    2610     # . . call
    2611     e8/call  next-word/disp32
    2612     # . . discard args
    2613     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2614     # if (slice-empty?(word-slice)) return false
    2615     # . eax = slice-empty?(word-slice)
    2616     # . . push args
    2617     52/push-edx
    2618     # . . call
    2619     e8/call  slice-empty?/disp32
    2620     # . . discard args
    2621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2622     # . if (eax != 0) return
    2623     3d/compare-eax-and  0/imm32/false
    2624     75/jump-if-!=  $far-jump-or-call?:end/disp8
    2625     # if datum of word-slice does not start with "8", return false
    2626     # . start/eax = word-slice->start
    2627     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy *edx to eax
    2628     # . c/eax = *start
    2629     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
    2630     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
    2631     # . if (eax != '8') return
    2632     3d/compare-eax-and  0x38/imm32/8
    2633     75/jump-if-!=  $far-jump-or-call?:end/disp8
    2634     # otherwise return true
    2635 $far-jump-or-call?:return-true:
    2636     bf/copy-to-edi  1/imm32/true
    2637 $far-jump-or-call?:end:
    2638     # . reclaim locals
    2639     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2640     # . restore registers
    2641     5b/pop-to-ebx
    2642     5a/pop-to-edx
    2643     59/pop-to-ecx
    2644     58/pop-to-eax
    2645     # . epilogue
    2646     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2647     5d/pop-to-ebp
    2648     c3/return
    2649
    2650 # - some helpers for tests
    2651
    2652 stream-add2:  # in: (addr stream byte), key: handle, val: int
    2653     # . prologue
    2654     55/push-ebp
    2655     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2656     # . save registers
    2657     50/push-eax
    2658     51/push-ecx
    2659     52/push-edx
    2660     56/push-esi
    2661     # esi = in
    2662     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    2663     # curr/eax = &in->data[in->write]
    2664     # . eax = in->write
    2665     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    2666     # . eax = esi+eax+12
    2667     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           0/r32/eax   0xc/disp8       .                 # copy esi+eax+12 to eax
    2668     # max/edx = &in->data[in->size]
    2669     # . edx = in->size
    2670     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
    2671     # . edx = esi+edx+12
    2672     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy esi+edx+12 to edx
    2673     # if (curr >= max) abort
    2674     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    2675     73/jump-if-addr>=  $stream-add2:abort/disp8
    2676     # *curr = key->alloc-id
    2677     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
    2678     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    2679     # curr += 4
    2680     05/add-to-eax  4/imm32
    2681     # if (curr >= max) abort
    2682     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    2683     73/jump-if-addr>=  $stream-add2:abort/disp8
    2684     # *curr = key->payload
    2685     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
    2686     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    2687     # curr += 4
    2688     05/add-to-eax  4/imm32
    2689     # if (curr >= max) abort
    2690     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    2691     73/jump-if-addr>=  $stream-add2:abort/disp8
    2692     # *curr = val
    2693     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
    2694     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    2695     # in->write += 0xc
    2696     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to *esi
    2697 $stream-add2:end:
    2698     # . restore registers
    2699     5e/pop-to-esi
    2700     5a/pop-to-edx
    2701     59/pop-to-ecx
    2702     58/pop-to-eax
    2703     # . epilogue
    2704     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2705     5d/pop-to-ebp
    2706     c3/return
    2707
    2708 $stream-add2:abort:
    2709     # . _write(2/stderr, error)
    2710     # . . push args
    2711     68/push  "overflow in stream-add2\n"/imm32
    2712     68/push  2/imm32/stderr
    2713     # . . call
    2714     e8/call  _write/disp32
    2715     # . . discard args
    2716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2717     # . syscall(exit, 1)
    2718     bb/copy-to-ebx  1/imm32
    2719     e8/call  syscall_exit/disp32
    2720     # never gets here
    2721
    2722 # some variants of 'trace' that take multiple arguments in different combinations of types:
    2723 #   n: int
    2724 #   c: character [4-bytes, will eventually be UTF-8]
    2725 #   s: (addr array byte)
    2726 #   l: (addr slice)
    2727 # one gotcha: 's5' must not be empty
    2728
    2729 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
    2730     # . prologue
    2731     55/push-ebp
    2732     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2733     # write(*Trace-stream, s1)
    2734     # . . push args
    2735     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2736     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    2737     # . . call
    2738     e8/call  write/disp32
    2739     # . . discard args
    2740     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2741     # write-slice(*Trace-stream, l2)
    2742     # . . push args
    2743     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2744     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    2745     # . . call
    2746     e8/call  write-slice/disp32
    2747     # . . discard args
    2748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2749     # write(*Trace-stream, s3)
    2750     # . . push args
    2751     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    2752     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    2753     # . . call
    2754     e8/call  write/disp32
    2755     # . . discard args
    2756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2757     # write-int32-hex(*Trace-stream, n4)
    2758     # . . push args
    2759     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    2760     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    2761     # . . call
    2762     e8/call  write-int32-hex/disp32
    2763     # . . discard args
    2764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2765     # trace(s5)  # implicitly adds a newline and finalizes the trace line
    2766     # . . push args
    2767     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    2768     # . . call
    2769     e8/call  trace/disp32
    2770     # . . discard args
    2771     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2772 $trace-slsns:end:
    2773     # . epilogue
    2774     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2775     5d/pop-to-ebp
    2776     c3/return
    2777
    2778 test-trace-slsns:
    2779     # . prologue
    2780     55/push-ebp
    2781     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2782     # setup
    2783     # . *Trace-stream->write = 0
    2784     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
    2785     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
    2786     # (eax..ecx) = "b"
    2787     b8/copy-to-eax  "b"/imm32
    2788     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    2789     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
    2790     05/add-to-eax  4/imm32
    2791     # var b/ebx: slice = {eax, ecx}
    2792     51/push-ecx
    2793     50/push-eax
    2794     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    2795     # trace-slsls("A" b "c " 3 " e")
    2796     # . . push args
    2797     68/push  " e"/imm32
    2798     68/push  3/imm32
    2799     68/push  "c "/imm32
    2800     53/push-ebx
    2801     68/push  "A"/imm32
    2802     # . . call
    2803     e8/call  trace-slsns/disp32
    2804     # . . discard args
    2805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    2806 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    2832     # check-trace-contains("Abc 0x00000003 e")
    2833     # . . push args
    2834     68/push  "F - test-trace-slsls"/imm32
    2835     68/push  "Abc 0x00000003 e"/imm32
    2836     # . . call
    2837     e8/call  check-trace-contains/disp32
    2838     # . . discard args
    2839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2840     # . epilogue
    2841     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2842     5d/pop-to-ebp
    2843     c3/return
    2844
    2845 num-bytes:  # line: (addr stream byte) -> eax: int
    2846     # pseudocode:
    2847     #   result = 0
    2848     #   while true
    2849     #     var word-slice = next-word(line)
    2850     #     if slice-empty?(word-slice)             # end of line
    2851     #       break
    2852     #     if slice-starts-with?(word-slice, "#")  # comment
    2853     #       break
    2854     #     if label?(word-slice)                # no need for label declarations anymore
    2855     #       break
    2856     #     if slice-equal?(word-slice, "==")
    2857     #       break                                 # no need for segment header lines
    2858     #     result += compute-width(word-slice)
    2859     #   return result
    2860     #
    2861     # . prologue
    2862     55/push-ebp
    2863     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2864     # . save registers
    2865     51/push-ecx
    2866     52/push-edx
    2867     53/push-ebx
    2868     # var result/eax = 0
    2869     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    2870     # var word-slice/ecx: slice
    2871     68/push  0/imm32/end
    2872     68/push  0/imm32/start
    2873     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    2874 +-- 26 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
    2900     # . rewind-stream(line)
    2901     # . . push args
    2902     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2903     # . . call
    2904     e8/call  rewind-stream/disp32
    2905     # . . discard args
    2906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2907 $num-bytes:loop:
    2908     # next-word(line, word-slice)
    2909     # . . push args
    2910     51/push-ecx
    2911     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2912     # . . call
    2913     e8/call  next-word/disp32
    2914     # . . discard args
    2915     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2916 +-- 40 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
    2956 $num-bytes:check0:
    2957     # if (slice-empty?(word-slice)) break
    2958     # . save result
    2959     50/push-eax
    2960     # . eax = slice-empty?(word-slice)
    2961     # . . push args
    2962     51/push-ecx
    2963     # . . call
    2964     e8/call  slice-empty?/disp32
    2965     # . . discard args
    2966     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2967     # . if (eax != false) break
    2968     3d/compare-eax-and  0/imm32/false
    2969     # . restore result now that ZF is set
    2970     58/pop-to-eax
    2971     75/jump-if-!=  $num-bytes:end/disp8
    2972 $num-bytes:check-for-comment:
    2973     # if (slice-starts-with?(word-slice, "#")) break
    2974     # . start/edx = word-slice->start
    2975     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    2976     # . c/ebx = *start
    2977     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    2978     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           3/r32/BL    .               .                 # copy byte at *edx to BL
    2979     # . if (ebx == '#') break
    2980     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x23/imm32/hash   # compare ebx
    2981     74/jump-if-=  $num-bytes:end/disp8
    2982 $num-bytes:check-for-label:
    2983     # if (slice-ends-with?(word-slice, ":")) break
    2984     # . end/edx = word-slice->end
    2985     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
    2986     # . c/ebx = *(end-1)
    2987     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    2988     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ecx to BL
    2989     # . if (ebx == ':') break
    2990     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x3a/imm32/colon  # compare ebx
    2991     74/jump-if-=  $num-bytes:end/disp8
    2992 $num-bytes:check-for-segment-header:
    2993     # if (slice-equal?(word-slice, "==")) break
    2994     # . push result
    2995     50/push-eax
    2996     # . eax = slice-equal?(word-slice, "==")
    2997     # . . push args
    2998     68/push  "=="/imm32
    2999     51/push-ecx
    3000     # . . call
    3001     e8/call  slice-equal?/disp32
    3002     # . . discard args
    3003     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3004     # . if (eax != false) break
    3005     3d/compare-eax-and  0/imm32/false
    3006     # . restore result now that ZF is set
    3007     58/pop-to-eax
    3008     75/jump-if-!=  $num-bytes:end/disp8
    3009 $num-bytes:loop-body:
    3010     # result += compute-width-of-slice(word-slice)
    3011     # . copy result to edx
    3012     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
    3013     # . eax = compute-width-of-slice(word-slice)
    3014     # . . push args
    3015     51/push-ecx
    3016     # . . call
    3017     e8/call compute-width-of-slice/disp32
    3018     # . . discard args
    3019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3020     # . eax += result
    3021     01/add                          3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # add edx to eax
    3022     e9/jump  $num-bytes:loop/disp32
    3023 $num-bytes:end:
    3024     # . rewind-stream(line)
    3025     # . . push args
    3026     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3027     # . . call
    3028     e8/call  rewind-stream/disp32
    3029     # . . discard args
    3030     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3031     # . reclaim locals
    3032     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3033     # . restore registers
    3034     5b/pop-to-ebx
    3035     5a/pop-to-edx
    3036     59/pop-to-ecx
    3037     # . epilogue
    3038     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3039     5d/pop-to-ebp
    3040     c3/return
    3041
    3042 test-num-bytes-handles-empty-string:
    3043     # if a line starts with '#', return 0
    3044     # . prologue
    3045     55/push-ebp
    3046     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3047     # setup
    3048     # . clear-stream(_test-input-stream)
    3049     # . . push args
    3050     68/push  _test-input-stream/imm32
    3051     # . . call
    3052     e8/call  clear-stream/disp32
    3053     # . . discard args
    3054     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3055     # . clear-stream(_test-output-stream)
    3056     # . . push args
    3057     68/push  _test-output-stream/imm32
    3058     # . . call
    3059     e8/call  clear-stream/disp32
    3060     # . . discard args
    3061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3062     # no contents in input
    3063     # eax = num-bytes(_test-input-stream)
    3064     # . . push args
    3065     68/push  _test-input-stream/imm32
    3066     # . . call
    3067     e8/call  num-bytes/disp32
    3068     # . . discard args
    3069     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3070     # check-ints-equal(eax, 0, msg)
    3071     # . . push args
    3072     68/push  "F - test-num-bytes-handles-empty-string"/imm32
    3073     68/push  0/imm32/true
    3074     50/push-eax
    3075     # . . call
    3076     e8/call  check-ints-equal/disp32
    3077     # . . discard args
    3078     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3079     # . epilogue
    3080     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3081     5d/pop-to-ebp
    3082     c3/return
    3083
    3084 test-num-bytes-ignores-comments:
    3085     # if a line starts with '#', return 0
    3086     # . prologue
    3087     55/push-ebp
    3088     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3089     # setup
    3090     # . clear-stream(_test-input-stream)
    3091     # . . push args
    3092     68/push  _test-input-stream/imm32
    3093     # . . call
    3094     e8/call  clear-stream/disp32
    3095     # . . discard args
    3096     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3097     # . clear-stream(_test-output-stream)
    3098     # . . push args
    3099     68/push  _test-output-stream/imm32
    3100     # . . call
    3101     e8/call  clear-stream/disp32
    3102     # . . discard args
    3103     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3104     # initialize input
    3105     # . write(_test-input-stream, "# abcd")
    3106     # . . push args
    3107     68/push  "# abcd"/imm32
    3108     68/push  _test-input-stream/imm32
    3109     # . . call
    3110     e8/call  write/disp32
    3111     # . . discard args
    3112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3113     # eax = num-bytes(_test-input-stream)
    3114     # . . push args
    3115     68/push  _test-input-stream/imm32
    3116     # . . call
    3117     e8/call  num-bytes/disp32
    3118     # . . discard args
    3119     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3120     # check-ints-equal(eax, 0, msg)
    3121     # . . push args
    3122     68/push  "F - test-num-bytes-ignores-comments"/imm32
    3123     68/push  0/imm32/true
    3124     50/push-eax
    3125     # . . call
    3126     e8/call  check-ints-equal/disp32
    3127     # . . discard args
    3128     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3129     # . epilogue
    3130     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3131     5d/pop-to-ebp
    3132     c3/return
    3133
    3134 test-num-bytes-ignores-labels:
    3135     # if the first word ends with ':', return 0
    3136     # . prologue
    3137     55/push-ebp
    3138     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3139     # setup
    3140     # . clear-stream(_test-input-stream)
    3141     # . . push args
    3142     68/push  _test-input-stream/imm32
    3143     # . . call
    3144     e8/call  clear-stream/disp32
    3145     # . . discard args
    3146     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3147     # . clear-stream(_test-output-stream)
    3148     # . . push args
    3149     68/push  _test-output-stream/imm32
    3150     # . . call
    3151     e8/call  clear-stream/disp32
    3152     # . . discard args
    3153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3154     # initialize input
    3155     # . write(_test-input-stream, "ab: # cd")
    3156     # . . push args
    3157     68/push  "ab: # cd"/imm32
    3158     68/push  _test-input-stream/imm32
    3159     # . . call
    3160     e8/call  write/disp32
    3161     # . . discard args
    3162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3163     # eax = num-bytes(_test-input-stream)
    3164     # . . push args
    3165     68/push  _test-input-stream/imm32
    3166     # . . call
    3167     e8/call  num-bytes/disp32
    3168     # . . discard args
    3169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3170     # check-ints-equal(eax, 0, msg)
    3171     # . . push args
    3172     68/push  "F - test-num-bytes-ignores-labels"/imm32
    3173     68/push  0/imm32/true
    3174     50/push-eax
    3175     # . . call
    3176     e8/call  check-ints-equal/disp32
    3177     # . . discard args
    3178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3179     # . epilogue
    3180     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3181     5d/pop-to-ebp
    3182     c3/return
    3183
    3184 test-num-bytes-ignores-segment-headers:
    3185     # if the first word is '==', return 0
    3186     # . prologue
    3187     55/push-ebp
    3188     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3189     # setup
    3190     # . clear-stream(_test-input-stream)
    3191     # . . push args
    3192     68/push  _test-input-stream/imm32
    3193     # . . call
    3194     e8/call  clear-stream/disp32
    3195     # . . discard args
    3196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3197     # . clear-stream(_test-output-stream)
    3198     # . . push args
    3199     68/push  _test-output-stream/imm32
    3200     # . . call
    3201     e8/call  clear-stream/disp32
    3202     # . . discard args
    3203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3204     # initialize input
    3205     # . write(_test-input-stream, "== ab cd")
    3206     # . . push args
    3207     68/push  "== ab cd"/imm32
    3208     68/push  _test-input-stream/imm32
    3209     # . . call
    3210     e8/call  write/disp32
    3211     # . . discard args
    3212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3213     # eax = num-bytes(_test-input-stream)
    3214     # . . push args
    3215     68/push  _test-input-stream/imm32
    3216     # . . call
    3217     e8/call  num-bytes/disp32
    3218     # . . discard args
    3219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3220     # check-ints-equal(eax, 0, msg)
    3221     # . . push args
    3222     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
    3223     68/push  0/imm32/true
    3224     50/push-eax
    3225     # . . call
    3226     e8/call  check-ints-equal/disp32
    3227     # . . discard args
    3228     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3229     # . epilogue
    3230     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3231     5d/pop-to-ebp
    3232     c3/return
    3233
    3234 test-num-bytes-counts-words-by-default:
    3235     # without metadata, count words
    3236     # . prologue
    3237     55/push-ebp
    3238     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3239     # setup
    3240     # . clear-stream(_test-input-stream)
    3241     # . . push args
    3242     68/push  _test-input-stream/imm32
    3243     # . . call
    3244     e8/call  clear-stream/disp32
    3245     # . . discard args
    3246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3247     # . clear-stream(_test-output-stream)
    3248     # . . push args
    3249     68/push  _test-output-stream/imm32
    3250     # . . call
    3251     e8/call  clear-stream/disp32
    3252     # . . discard args
    3253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3254     # initialize input
    3255     # . write(_test-input-stream, "ab cd ef")
    3256     # . . push args
    3257     68/push  "ab cd ef"/imm32
    3258     68/push  _test-input-stream/imm32
    3259     # . . call
    3260     e8/call  write/disp32
    3261     # . . discard args
    3262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3263     # eax = num-bytes(_test-input-stream)
    3264     # . . push args
    3265     68/push  _test-input-stream/imm32
    3266     # . . call
    3267     e8/call  num-bytes/disp32
    3268     # . . discard args
    3269     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3270     # check-ints-equal(eax, 3, msg)
    3271     # . . push args
    3272     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
    3273     68/push  3/imm32/true
    3274     50/push-eax
    3275     # . . call
    3276     e8/call  check-ints-equal/disp32
    3277     # . . discard args
    3278     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3279     # . epilogue
    3280     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3281     5d/pop-to-ebp
    3282     c3/return
    3283
    3284 test-num-bytes-ignores-trailing-comment:
    3285     # trailing comments appropriately ignored
    3286     # . prologue
    3287     55/push-ebp
    3288     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3289     # setup
    3290     # . clear-stream(_test-input-stream)
    3291     # . . push args
    3292     68/push  _test-input-stream/imm32
    3293     # . . call
    3294     e8/call  clear-stream/disp32
    3295     # . . discard args
    3296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3297     # . clear-stream(_test-output-stream)
    3298     # . . push args
    3299     68/push  _test-output-stream/imm32
    3300     # . . call
    3301     e8/call  clear-stream/disp32
    3302     # . . discard args
    3303     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3304     # initialize input
    3305     # . write(_test-input-stream, "ab cd # ef")
    3306     # . . push args
    3307     68/push  "ab cd # ef"/imm32
    3308     68/push  _test-input-stream/imm32
    3309     # . . call
    3310     e8/call  write/disp32
    3311     # . . discard args
    3312     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3313     # eax = num-bytes(_test-input-stream)
    3314     # . . push args
    3315     68/push  _test-input-stream/imm32
    3316     # . . call
    3317     e8/call  num-bytes/disp32
    3318     # . . discard args
    3319     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3320     # check-ints-equal(eax, 2, msg)
    3321     # . . push args
    3322     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
    3323     68/push  2/imm32/true
    3324     50/push-eax
    3325     # . . call
    3326     e8/call  check-ints-equal/disp32
    3327     # . . discard args
    3328     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3329     # . epilogue
    3330     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3331     5d/pop-to-ebp
    3332     c3/return
    3333
    3334 test-num-bytes-handles-imm32:
    3335     # if a word has the /imm32 metadata, count it as 4 bytes
    3336     # . prologue
    3337     55/push-ebp
    3338     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3339     # setup
    3340     # . clear-stream(_test-input-stream)
    3341     # . . push args
    3342     68/push  _test-input-stream/imm32
    3343     # . . call
    3344     e8/call  clear-stream/disp32
    3345     # . . discard args
    3346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3347     # . clear-stream(_test-output-stream)
    3348     # . . push args
    3349     68/push  _test-output-stream/imm32
    3350     # . . call
    3351     e8/call  clear-stream/disp32
    3352     # . . discard args
    3353     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3354     # initialize input
    3355     # . write(_test-input-stream, "ab cd/imm32 ef")
    3356     # . . push args
    3357     68/push  "ab cd/imm32 ef"/imm32
    3358     68/push  _test-input-stream/imm32
    3359     # . . call
    3360     e8/call  write/disp32
    3361     # . . discard args
    3362     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3363     # eax = num-bytes(_test-input-stream)
    3364     # . . push args
    3365     68/push  _test-input-stream/imm32
    3366     # . . call
    3367     e8/call  num-bytes/disp32
    3368     # . . discard args
    3369     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3370     # check-ints-equal(eax, 6, msg)
    3371     # . . push args
    3372     68/push  "F - test-num-bytes-handles-imm32"/imm32
    3373     68/push  6/imm32/true
    3374     50/push-eax
    3375     # . . call
    3376     e8/call  check-ints-equal/disp32
    3377     # . . discard args
    3378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3379     # . epilogue
    3380     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3381     5d/pop-to-ebp
    3382     c3/return
    3383
    3384 # . . vim:nowrap:textwidth=0