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

       1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
       2 # (landscape).
       3 # Read a list of addresses and labels from a file called 'labels',
       4 # then replace labels in stdin with their addresses.
       5 #
       6 # To build:
       7 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx labels_baremetal.subx  -o labels_baremetal
       8 #
       9 # Stdin should be a stream of bytes and some interspersed labels. Comments and
      10 # '==' segment headers are allowed, but names are ignored. The emitted code
      11 # will all lie in a single contiguous address range starting at address 0x7c00.
      12 # Addresses in segment headers are optional. If provided, this program will
      13 # insert padding in the output until the desired address is 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 labels
      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-labels-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-labels-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-labels-main:end/disp8
      86 $subx-labels-main:interactive:
      87     # - otherwise convert stdin
      88     # var labels-file/esi: (addr buffered-file) from syscall_open("labels", READ)
      89     # . data
      90     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x1000/imm32      # subtract from esp
      91     # . size
      92     68/push  0x1000/imm32
      93     # . read
      94     68/push  0/imm32/read
      95     # . write
      96     68/push  0/imm32/write
      97     # . fd = syscall_open("labels", READ)
      98     bb/copy-to-ebx  Label-file/imm32
      99     b9/copy-to-ecx  0/imm32/read-mode
     100     ba/copy-to-edx  0x180/imm32/permissions
     101     e8/call syscall_open/disp32
     102     50/push-eax
     103     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
     104     # subx-labels(Stdin, label-fd, Stdout)
     105     # . . push args
     106     68/push  Stdout/imm32
     107     56/push-esi
     108     68/push  Stdin/imm32
     109     # . . call
     110     e8/call  subx-labels/disp32
     111     # . . discard args
     112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     113 #?     # . write-stream(2/stderr, Trace-stream)
     114 #?     # . . push args
     115 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
     116 #?     68/push  2/imm32/stderr
     117 #?     # . . call
     118 #?     e8/call  write-stream/disp32
     119 #?     # . . discard args
     120 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     121     # syscall(exit, 0)
     122     bb/copy-to-ebx  0/imm32
     123 $subx-labels-main:end:
     124     e8/call  syscall_exit/disp32
     125
     126 == data
     127
     128 Label-file:
     129   6c/l 61/a 62/b 65/e 6c/l 73/s 00/nul
     130
     131 == code
     132
     133 subx-labels:  # infile: (addr buffered-file), labels-file: (addr buffered-file), out: (addr buffered-file)
     134     # pseudocode
     135     #   var labels: (stream {label-name, address} Max-labels)
     136     #   load-labels(labels-file, labels)
     137     #   var in: (stream byte Input-size)
     138     #   slurp(infile, in)
     139     #   emit-output(in, out, labels)
     140     #
     141     # . prologue
     142     55/push-ebp
     143     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     144     # . save registers
     145     52/push-edx
     146     56/push-esi
     147     # var labels/edx: (stream {label-name, address} Max-labels)
     148     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
     149     # . data
     150     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
     151     # . size
     152     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
     153     # . read
     154     68/push  0/imm32/read
     155     # . write
     156     68/push  0/imm32/write
     157     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     158     # load-labels(labels-file, labels)
     159     # . . push args
     160     52/push-edx
     161     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     162     # . . call
     163     e8/call  load-labels/disp32
     164     # . . discard args
     165     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     166     # var in/esi: (stream byte Input-size)
     167     # . data
     168     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
     169     # . size
     170     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
     171     # . read
     172     68/push  0/imm32/read
     173     # . write
     174     68/push  0/imm32/write
     175     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
     176     # slurp(infile, in)
     177     # . . push args
     178     56/push-esi
     179     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     180     # . . call
     181     e8/call  slurp/disp32
     182     # . . discard args
     183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     184     # emit-output(in, out, labels)
     185     # . . push args
     186     52/push-edx
     187     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     188     56/push-esi
     189     # . . call
     190     e8/call  emit-output/disp32
     191     # . . discard args
     192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     193     # flush(out)
     194     # . . push args
     195     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     196     # . . call
     197     e8/call  flush/disp32
     198     # . . discard args
     199     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     200 $subx-labels:end:
     201     # . reclaim locals
     202     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
     203     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
     204     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
     205     # . restore registers
     206     5e/pop-to-esi
     207     5a/pop-to-edx
     208     # . epilogue
     209     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     210     5d/pop-to-ebp
     211     c3/return
     212
     213 load-labels:  # in: (addr buffered-file), labels: (stream {label-name, address} Max-labels)
     214     # pseudocode
     215     #   var line: (stream byte 512)
     216     #   while true
     217     #     clear-stream(line)
     218     #     read-line-buffered(in, line)
     219     #     if (line->write == 0) break               # end of file
     220     #     var word-slice/ecx: (addr slice) = next-word(line)
     221     #     var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row)
     222     #     word-slice = next-word(line)
     223     #     var address/eax: int = parse-hex-int-from-slice(word-slice)
     224     #     *dest = address
     225     #
     226     # . prologue
     227     55/push-ebp
     228     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     229     # . save registers
     230     50/push-eax
     231     51/push-ecx
     232     52/push-edx
     233     56/push-esi
     234     57/push-edi
     235     # var line/ecx: (stream byte 512)
     236     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     237     68/push  0x200/imm32/size
     238     68/push  0/imm32/read
     239     68/push  0/imm32/write
     240     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     241     # var word-slice/edx: slice
     242     68/push  0/imm32/end
     243     68/push  0/imm32/start
     244     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     245 $load-labels:loop:
     246     # clear-stream(line)
     247     # . . push args
     248     51/push-ecx
     249     # . . call
     250     e8/call  clear-stream/disp32
     251     # . . discard args
     252     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     253     # read-line-buffered(in, line)
     254     # . . push args
     255     51/push-ecx
     256     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     257     # . . call
     258     e8/call  read-line-buffered/disp32
     259     # . . discard args
     260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     261 +-- 33 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
     294     # if (line->write == 0) break
     295     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
     296     0f 84/jump-if-=  $load-labels:end/disp32
     297     # next-word(line, word-slice)
     298     # . . push args
     299     52/push-edx
     300     51/push-ecx
     301     # . . call
     302     e8/call  next-word/disp32
     303     # . . discard args
     304     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     305 +-- 33 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
     338     # var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap)
     339     # . eax = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap)
     340     # . . push args
     341     68/push  Heap/imm32
     342     68/push  0xc/imm32/row-size
     343     52/push-edx
     344     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     345     # . . call
     346     e8/call  get-or-insert-slice/disp32
     347     # . . discard args
     348     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     349     # . edi = eax
     350     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
     351     # next-word(line, word-slice)
     352     # . . push args
     353     52/push-edx
     354     51/push-ecx
     355     # . . call
     356     e8/call  next-word/disp32
     357     # . . discard args
     358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     359     # var address/esi: int = parse-hex-int-from-slice(word-slice)
     360     # . . push args
     361     52/push-edx
     362     # . . call
     363     e8/call  parse-hex-int-from-slice/disp32
     364     # . . discard args
     365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     366     # . esi = eax
     367     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
     368     # *dest = address
     369     89/copy                         0/mod/indirect  7/rm32/edi    .           .                         6/r32/esi   .               .                 # copy esi to *edi
     370     #
     371     e9/jump  $load-labels:loop/disp32
     372 $load-labels:end:
     373     # . reclaim locals
     374     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
     375     # . restore registers
     376     5f/pop-to-edi
     377     5e/pop-to-esi
     378     5a/pop-to-edx
     379     59/pop-to-ecx
     380     58/pop-to-eax
     381     # . epilogue
     382     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     383     5d/pop-to-ebp
     384     c3/return
     385
     386 # global scratch space for emit-output
     387 == data
     388
     389 emit-output:datum:  # slice
     390   0/imm32/start
     391   0/imm32/end
     392
     393 == code
     394
     395 emit-output:  # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), address})
     396     # pseudocode:
     397     #   var address-of-next-instruction = 0x7c00
     398     #   var line: (stream byte 512)
     399     #   line-loop:
     400     #   while true
     401     #     clear-stream(line)
     402     #     read-line(in, line)
     403     #     if (line->write == 0) break               # end of file
     404     #     address-of-next-instruction += num-bytes(line)
     405     #     var far-jump-or-call? = far-jump-or-call?(line)
     406     #     rewind-stream(line)
     407     #     while true
     408     #       var word-slice = next-word(line)
     409     #       if slice-empty?(word-slice)             # end of line
     410     #         break
     411     #       if slice-starts-with?(word-slice, "#")  # comment
     412     #         break
     413     #       if label?(word-slice)                # no need for label declarations anymore
     414     #         goto line-loop                        # don't insert empty lines
     415     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
     416     #         word-slice = next-word(line)          # skip segment name
     417     #         word-slice = next-word(line)
     418     #         if !slice-empty?(word-slice)
     419     #           new-address = parse-hex-int-from-slice(word-slice)
     420     #           write-buffered(out, "# " address-of-next-instruction "\n")
     421     #           while address-of-next-instruction < new-address
     422     #             write-buffered("00")
     423     #             ++address-of-next-instruction
     424     #           write-buffered(out, "# " address-of-next-instruction "\n")
     425     #           goto line-loop                      # don't insert empty lines
     426     #       if length(word-slice) == 2
     427     #         write-slice-buffered(out, word-slice)
     428     #         write-buffered(out, " ")
     429     #         continue
     430     #       var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/")
     431     #       var address: (addr int) = get-slice(labels, datum)
     432     #       if has-metadata?(word-slice, "imm8")
     433     #         emit(out, *address, 1)
     434     #       else if has-metadata?(word-slice, "imm16")
     435     #         emit(out, *address, 2)
     436     #       else if has-metadata?(word-slice, "imm32")
     437     #         emit(out, *address, 4)
     438     #       else if has-metadata?(word-slice, "disp8")
     439     #         value = *address - address-of-next-instruction
     440     #         emit(out, value, 1)
     441     #       else if has-metadata?(word-slice, "disp32")
     442     #         if far-jump-or-call?
     443     #           value = *address - address-of-next-instruction
     444     #         else
     445     #           value = *address
     446     #         emit(out, value, 4)
     447     #       else
     448     #         abort
     449     #     write-buffered(out, "\n")
     450     #
     451     # registers:
     452     #   line: ecx
     453     #   word-slice: edx
     454     #   address-of-next-instruction: ebx
     455     #   far-jump-or-call?: edi
     456     #   address: esi (inner loop only)
     457     #   temporaries: eax, esi (outer loop)
     458     #
     459     # . prologue
     460     55/push-ebp
     461     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     462     # . save registers
     463     50/push-eax
     464     51/push-ecx
     465     52/push-edx
     466     53/push-ebx
     467     56/push-esi
     468     57/push-edi
     469     # var line/ecx: (stream byte 512)
     470     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     471     68/push  0x200/imm32/size
     472     68/push  0/imm32/read
     473     68/push  0/imm32/write
     474     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     475     # var word-slice/edx: slice
     476     68/push  0/imm32/end
     477     68/push  0/imm32/start
     478     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     479     # var address-of-next-instruction/ebx = 0x7c00
     480     bb/copy-to-ebx  0x7c00/imm32
     481 $emit-output:line-loop:
     482     # clear-stream(line)
     483     # . . push args
     484     51/push-ecx
     485     # . . call
     486     e8/call  clear-stream/disp32
     487     # . . discard args
     488     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     489     # read-line(in, line)
     490     # . . push args
     491     51/push-ecx
     492     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     493     # . . call
     494     e8/call  read-line/disp32
     495     # . . discard args
     496     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     497 +-- 33 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
     530 $emit-output:check-for-end-of-input:
     531     # if (line->write == 0) break
     532     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
     533     0f 84/jump-if-=  $emit-output:end/disp32
     534     # address-of-next-instruction += num-bytes(line)
     535     # . eax = num-bytes(line)
     536     # . . push args
     537     51/push-ecx
     538     # . . call
     539     e8/call  num-bytes/disp32
     540     # . . discard args
     541     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     542     # . ebx += eax
     543     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
     544     # var far-jump-or-call?/edi: boolean = far-jump-or-call?(line)
     545     # . . push args
     546     51/push-ecx
     547     # . . call
     548     e8/call  far-jump-or-call?/disp32
     549     # . . discard args
     550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     551     # rewind-stream(line)
     552     # . . push args
     553     51/push-ecx
     554     # . . call
     555     e8/call  rewind-stream/disp32
     556     # . . discard args
     557     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     558 $emit-output:word-loop:
     559     # next-word(line, word-slice)
     560     # . . push args
     561     52/push-edx
     562     51/push-ecx
     563     # . . call
     564     e8/call  next-word/disp32
     565     # . . discard args
     566     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     567 +-- 33 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
     600 $emit-output:check-for-end-of-line:
     601     # if (slice-empty?(word-slice)) break
     602     # . eax = slice-empty?(word-slice)
     603     # . . push args
     604     52/push-edx
     605     # . . call
     606     e8/call  slice-empty?/disp32
     607     # . . discard args
     608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     609     # . if (eax != 0) break
     610     3d/compare-eax-and  0/imm32/false
     611     0f 85/jump-if-!=  $emit-output:next-line/disp32
     612 $emit-output:check-for-comment:
     613     # if (slice-starts-with?(word-slice, "#")) break
     614     # . start/esi = word-slice->start
     615     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
     616     # . c/eax = *start
     617     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
     618     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
     619     # . if (eax == '#') break
     620     3d/compare-eax-and  0x23/imm32/hash
     621     0f 84/jump-if-=  $emit-output:next-line/disp32
     622 $emit-output:check-for-label:
     623     # if label?(word-slice) break
     624     # . eax = label?(word-slice)
     625     # . . push args
     626     52/push-edx
     627     # . . call
     628     e8/call  label?/disp32
     629     # . . discard args
     630     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     631     # . if (eax != false) break
     632     3d/compare-eax-and  0/imm32/false
     633     0f 85/jump-if-!=  $emit-output:line-loop/disp32
     634 $emit-output:check-for-segment-header:
     635     # if !slice-equal?(word-slice, "==") goto next check
     636     # . eax = slice-equal?(word-slice, "==")
     637     # . . push args
     638     68/push  "=="/imm32
     639     52/push-edx
     640     # . . call
     641     e8/call  slice-equal?/disp32
     642     # . . discard args
     643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     644     # . if (eax == false) goto next check
     645     3d/compare-eax-and  0/imm32/false
     646     0f 84/jump-if-=  $emit-output:2-character/disp32
     647     # skip segment name
     648     # . next-word(line, word-slice)
     649     # . . push args
     650     52/push-edx
     651     51/push-ecx
     652     # . . call
     653     e8/call  next-word/disp32
     654     # . . discard args
     655     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     656     # compute segment address if it exists
     657     # . next-word(line, word-slice)
     658     # . . push args
     659     52/push-edx
     660     51/push-ecx
     661     # . . call
     662     e8/call  next-word/disp32
     663     # . . discard args
     664     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     665     # . if slice-empty?(word-slice) goto padding-done
     666     # . . push args
     667     52/push-edx
     668     # . . call
     669     e8/call  slice-empty?/disp32
     670     # . . discard args
     671     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     672     # . .
     673     3d/compare-eax-and  0/imm32/false
     674     0f 85/jump-if-!=  $emit-output:padding-done/disp32
     675     # . var new-address/eax: int = parse-hex-int-from-slice(word-slice)
     676     # . . push args
     677     52/push-edx
     678     # . . call
     679     e8/call  parse-hex-int-from-slice/disp32
     680     # . . discard args
     681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     682     # write-buffered(out, "# " address-of-next-instruction "\n")
     683     # . write-buffered(out, "# ")
     684     # . . push args
     685     68/push  "# "/imm32
     686     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     687     # . . call
     688     e8/call  write-buffered/disp32
     689     # . . discard args
     690     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     691     # . write-int32-hex-buffered(out, address-of-next-instruction)
     692     # . . push args
     693     53/push-ebx
     694     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     695     # . . call
     696     e8/call  write-int32-hex-buffered/disp32
     697     # . . discard args
     698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     699     # . write-buffered(out, "\n")
     700     # . . push args
     701     68/push  Newline/imm32
     702     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     703     # . . call
     704     e8/call  write-buffered/disp32
     705     # . . discard args
     706     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     707 $emit-output:padding-loop:
     708     # if (address-of-next-instruction >= new-address) goto padding-loop-done
     709     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # compare ebx with eax
     710     73/jump-if-addr>=  $emit-output:padding-loop-done/disp8
     711     # if (address-of-next-instruction % 8 == 0) write-buffered("\n")
     712     53/push-ebx
     713     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               7/imm32           # bitwise and of ebx
     714     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
     715     5b/pop-to-ebx
     716     75/jump-if-!=  $emit-output:padding-core/disp8
     717     # . write-buffered(out, "\n")
     718     # . . push args
     719     68/push  Newline/imm32
     720     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     721     # . . call
     722     e8/call  write-buffered/disp32
     723     # . . discard args
     724     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     725 $emit-output:padding-core:
     726     # write-buffered("00")
     727     # . . push args
     728     68/push  "00 "/imm32
     729     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     730     # . . call
     731     e8/call  write-buffered/disp32
     732     # . . discard args
     733     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     734     # ++address-of-next-instruction
     735     43/increment-ebx
     736     # loop
     737     eb/jump $emit-output:padding-loop/disp8
     738 $emit-output:padding-loop-done:
     739     # . write-buffered(out, "\n")
     740     # . . push args
     741     68/push  Newline/imm32
     742     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     743     # . . call
     744     e8/call  write-buffered/disp32
     745     # . . discard args
     746     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     747 $emit-output:padding-done:
     748     # write-buffered(out, "# " address-of-next-instruction "\n")
     749     # . write-buffered(out, "# ")
     750     # . . push args
     751     68/push  "# "/imm32
     752     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     753     # . . call
     754     e8/call  write-buffered/disp32
     755     # . . discard args
     756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     757     # . write-int32-hex-buffered(out, address-of-next-instruction)
     758     # . . push args
     759     53/push-ebx
     760     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     761     # . . call
     762     e8/call  write-int32-hex-buffered/disp32
     763     # . . discard args
     764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     765     # . write-buffered(out, "\n")
     766     # . . push args
     767     68/push  Newline/imm32
     768     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     769     # . . call
     770     e8/call  write-buffered/disp32
     771     # . . discard args
     772     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     773     #
     774     e9/jump  $emit-output:line-loop/disp32
     775 $emit-output:2-character:
     776     # if (size(word-slice) != 2) goto next check
     777     # . eax = size(word-slice)
     778     8b/copy                         1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(edx+4) to eax
     779     2b/subtract                     0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # subtract *edx from eax
     780     # . if (eax != 2) goto next check
     781     3d/compare-eax-and  2/imm32
     782     75/jump-if-!=  $emit-output:check-metadata/disp8
     783     # write-slice-buffered(out, word-slice)
     784     # . . push args
     785     52/push-edx
     786     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     787     # . . call
     788     e8/call  write-slice-buffered/disp32
     789     # . . discard args
     790     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     791     # write-buffered(out, " ")
     792     # . . push args
     793     68/push  Space/imm32
     794     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     795     # . . call
     796     e8/call  write-buffered/disp32
     797     # . . discard args
     798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     799     # continue
     800     e9/jump  $emit-output:word-loop/disp32
     801 $emit-output:check-metadata:
     802     # - if we get here, 'word-slice' must be a label to be looked up
     803     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
     804     # . . push args
     805     68/push  emit-output:datum/imm32
     806     68/push  0x2f/imm32/slash
     807     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
     808     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
     809     # . . call
     810     e8/call  next-token-from-slice/disp32
     811     # . . discard args
     812     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     813 +-- 33 lines: #?     # dump datum ------------------------------------------------------------------------------------------------------------------------------------------
     846     # address/esi: (addr int) = get-slice(labels, datum, row-size=12, "label table")
     847     # . eax = get-slice(labels, datum, row-size=24, "label table")
     848     # . . push args
     849     68/push  "label table"/imm32
     850     68/push  0xc/imm32/row-size
     851     68/push  emit-output:datum/imm32
     852     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     853     # . . call
     854     e8/call  get-slice/disp32
     855     # . . discard args
     856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     857     # . esi = eax
     858     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
     859 $emit-output:check-imm8:
     860     # if (!has-metadata?(word-slice, "imm8")) goto next check
     861     # . eax = has-metadata?(edx, "imm8")
     862     # . . push args
     863     68/push  "imm8"/imm32
     864     52/push-edx
     865     # . . call
     866     e8/call  has-metadata?/disp32
     867     # . . discard args
     868     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     869     # . if (eax != false) abort
     870     3d/compare-eax-and  0/imm32/false
     871     74/jump-if-=  $emit-output:check-imm16/disp8
     872 $emit-output:emit-imm8:
     873     # emit-hex(out, *address, 1)
     874     # . . push args
     875     68/push  1/imm32
     876     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     877     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     878     # . . call
     879     e8/call  emit-hex/disp32
     880     # . . discard args
     881     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     882     # continue
     883     e9/jump  $emit-output:word-loop/disp32
     884 $emit-output:check-imm16:
     885     # if (!has-metadata?(word-slice, "imm16")) goto next check
     886     # . eax = has-metadata?(edx, "imm16")
     887     # . . push args
     888     68/push  "imm16"/imm32
     889     52/push-edx
     890     # . . call
     891     e8/call  has-metadata?/disp32
     892     # . . discard args
     893     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     894     # . if (eax == false) goto next check
     895     3d/compare-eax-and  0/imm32/false
     896     74/jump-if-=  $emit-output:check-imm32/disp8
     897 +-- 33 lines: #?     # dump *address ---------------------------------------------------------------------------------------------------------------------------------------
     930 $emit-output:emit-imm16:
     931     # emit-hex(out, *address, 2)
     932     # . . push args
     933     68/push  2/imm32
     934     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     935     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     936     # . . call
     937     e8/call  emit-hex/disp32
     938     # . . discard args
     939     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     940     # TODO: ensure that the higher 2 bytes are zero
     941     # continue
     942     e9/jump  $emit-output:word-loop/disp32
     943 $emit-output:check-imm32:
     944     # if (!has-metadata?(word-slice, "imm32")) goto next check
     945     # . eax = has-metadata?(edx, "imm32")
     946     # . . push args
     947     68/push  "imm32"/imm32
     948     52/push-edx
     949     # . . call
     950     e8/call  has-metadata?/disp32
     951     # . . discard args
     952     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     953     # . if (eax == false) goto next check
     954     3d/compare-eax-and  0/imm32/false
     955     74/jump-if-=  $emit-output:check-disp8/disp8
     956 +-- 33 lines: #?     # dump *address ---------------------------------------------------------------------------------------------------------------------------------------
     989 $emit-output:emit-imm32:
     990     # emit-hex(out, *address, 4)
     991     # . . push args
     992     68/push  4/imm32
     993     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     994     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     995     # . . call
     996     e8/call  emit-hex/disp32
     997     # . . discard args
     998     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     999     # continue
    1000     e9/jump  $emit-output:word-loop/disp32
    1001 $emit-output:check-disp8:
    1002     # if (!has-metadata?(word-slice, "disp8")) goto next check
    1003     # . eax = has-metadata?(edx, "disp8")
    1004     # . . push args
    1005     68/push  "disp8"/imm32
    1006     52/push-edx
    1007     # . . call
    1008     e8/call  has-metadata?/disp32
    1009     # . . discard args
    1010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1011     # . if (eax == false) goto next check
    1012     3d/compare-eax-and  0/imm32/false
    1013     74/jump-if-=  $emit-output:check-disp16/disp8
    1014 $emit-output:emit-disp8:
    1015     # emit-hex(out, *address - address-of-next-instruction, 1)
    1016     # . . push args
    1017     68/push  1/imm32
    1018     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    1019     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
    1020     50/push-eax
    1021     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1022     # . . call
    1023     e8/call  emit-hex/disp32
    1024     # . . discard args
    1025     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1026     # continue
    1027     e9/jump  $emit-output:word-loop/disp32
    1028 $emit-output:check-disp16:
    1029     # if (!has-metadata?(word-slice, "disp16")) goto next check
    1030     # . eax = has-metadata?(edx, "disp16")
    1031     # . . push args
    1032     68/push  "disp16"/imm32
    1033     52/push-edx
    1034     # . . call
    1035     e8/call  has-metadata?/disp32
    1036     # . . discard args
    1037     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1038     # . if (eax == false) goto next check
    1039     3d/compare-eax-and  0/imm32/false
    1040     74/jump-if-=  $emit-output:check-disp32/disp8
    1041 $emit-output:emit-disp16:
    1042     # emit-hex(out, *address - address-of-next-instruction, 2)
    1043     # . . push args
    1044     68/push  2/imm32
    1045     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    1046     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
    1047     50/push-eax
    1048     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1049     # . . call
    1050     e8/call  emit-hex/disp32
    1051     # . . discard args
    1052     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1053     # continue
    1054     e9/jump  $emit-output:word-loop/disp32
    1055 $emit-output:check-disp32:
    1056     # if (!has-metadata?(word-slice, "disp32")) abort
    1057     # . eax = has-metadata?(edx, "disp32")
    1058     # . . push args
    1059     68/push  "disp32"/imm32
    1060     52/push-edx
    1061     # . . call
    1062     e8/call  has-metadata?/disp32
    1063     # . . discard args
    1064     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1065     # . if (eax == false) abort
    1066     3d/compare-eax-and  0/imm32/false
    1067     0f 84/jump-if-=  $emit-output:abort/disp32
    1068 $emit-output:emit-disp32:
    1069     # var value/eax = *address
    1070     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    1071     # if (far-jump-or-call?) value -= address-of-next-instruction
    1072     81          7/subop/compare     3/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32/false     # compare edi
    1073     74/jump-if-=  $emit-output:really-emit-disp32/disp8
    1074     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
    1075 $emit-output:really-emit-disp32:
    1076     # emit-hex(out, value, 4)
    1077     # . . push args
    1078     68/push  4/imm32
    1079     50/push-eax
    1080     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1081     # . . call
    1082     e8/call  emit-hex/disp32
    1083     # . . discard args
    1084     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1085     # continue
    1086     e9/jump  $emit-output:word-loop/disp32
    1087 $emit-output:next-line:
    1088     # write-buffered(out, "\n")
    1089     # . . push args
    1090     68/push  Newline/imm32
    1091     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1092     # . . call
    1093     e8/call  write-buffered/disp32
    1094     # . . discard args
    1095     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1096     # loop
    1097     e9/jump  $emit-output:line-loop/disp32
    1098 $emit-output:end:
    1099     # . reclaim locals
    1100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
    1101     # . restore registers
    1102     5f/pop-to-edi
    1103     5e/pop-to-esi
    1104     5b/pop-to-ebx
    1105     5a/pop-to-edx
    1106     59/pop-to-ecx
    1107     58/pop-to-eax
    1108     # . epilogue
    1109     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1110     5d/pop-to-ebp
    1111     c3/return
    1112
    1113 $emit-output:abort:
    1114     # print(stderr, "missing metadata in " word-slice)
    1115     # . _write(2/stderr, "missing metadata in word ")
    1116     # . . push args
    1117     68/push  "emit-output: missing metadata in "/imm32
    1118     68/push  2/imm32/stderr
    1119     # . . call
    1120     e8/call  _write/disp32
    1121     # . . discard args
    1122     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1123     # . write-slice-buffered(Stderr, word-slice)
    1124     # . . push args
    1125     52/push-edx
    1126     68/push  Stderr/imm32
    1127     # . . call
    1128     e8/call  write-slice-buffered/disp32
    1129     # . . discard args
    1130     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1131     # . flush(Stderr)
    1132     # . . push args
    1133     68/push  Stderr/imm32
    1134     # . . call
    1135     e8/call  flush/disp32
    1136     # . . discard args
    1137     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1138     # . syscall(exit, 1)
    1139     bb/copy-to-ebx  1/imm32
    1140     e8/call  syscall_exit/disp32
    1141     # never gets here
    1142
    1143 test-emit-output-non-far-control-flow:
    1144     # labels turn into absolute addresses if opcodes are not far jumps or calls
    1145     #
    1146     # input:
    1147     #   in:
    1148     #     == code
    1149     #     ab cd ef gh
    1150     #     ij x/disp32
    1151     #     == data
    1152     #     00
    1153     #     34
    1154     #   labels:
    1155     #     - 'x': 0x11223344
    1156     #
    1157     # output:
    1158     #   ab cd ef gh
    1159     #   ij 44 33 22 11
    1160     #   00
    1161     #   34
    1162     #
    1163     # . prologue
    1164     55/push-ebp
    1165     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1166     # setup
    1167     # . clear-stream(_test-input-stream)
    1168     # . . push args
    1169     68/push  _test-input-stream/imm32
    1170     # . . call
    1171     e8/call  clear-stream/disp32
    1172     # . . discard args
    1173     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1174     # . clear-stream(_test-output-stream)
    1175     # . . push args
    1176     68/push  _test-output-stream/imm32
    1177     # . . call
    1178     e8/call  clear-stream/disp32
    1179     # . . discard args
    1180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1181     # . clear-stream($_test-output-buffered-file->buffer)
    1182     # . . push args
    1183     68/push  $_test-output-buffered-file->buffer/imm32
    1184     # . . call
    1185     e8/call  clear-stream/disp32
    1186     # . . discard args
    1187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1188     # . var labels/edx: (stream byte 8*24)
    1189     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    1190     68/push  0xc0/imm32/size
    1191     68/push  0/imm32/read
    1192     68/push  0/imm32/write
    1193     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1194     # . var h/ebx: (handle array byte)
    1195     68/push  0/imm32
    1196     68/push  0/imm32
    1197     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1198     # initialize input
    1199     # . write(_test-input-stream, "== code\n")
    1200     # . . push args
    1201     68/push  "== code\n"/imm32
    1202     68/push  _test-input-stream/imm32
    1203     # . . call
    1204     e8/call  write/disp32
    1205     # . . discard args
    1206     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1207     # . write(_test-input-stream, "ab cd ef gh\n")
    1208     # . . push args
    1209     68/push  "ab cd ef gh\n"/imm32
    1210     68/push  _test-input-stream/imm32
    1211     # . . call
    1212     e8/call  write/disp32
    1213     # . . discard args
    1214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1215     # . write(_test-input-stream, "ij x/disp32\n")
    1216     # . . push args
    1217     68/push  "ij x/disp32\n"/imm32
    1218     68/push  _test-input-stream/imm32
    1219     # . . call
    1220     e8/call  write/disp32
    1221     # . . discard args
    1222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1223     # . write(_test-input-stream, "== data\n")
    1224     # . . push args
    1225     68/push  "== data\n"/imm32
    1226     68/push  _test-input-stream/imm32
    1227     # . . call
    1228     e8/call  write/disp32
    1229     # . . discard args
    1230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1231     # . write(_test-input-stream, "00\n")
    1232     # . . push args
    1233     68/push  "00\n"/imm32
    1234     68/push  _test-input-stream/imm32
    1235     # . . call
    1236     e8/call  write/disp32
    1237     # . . discard args
    1238     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1239     # . write(_test-input-stream, "34\n")
    1240     # . . push args
    1241     68/push  "34\n"/imm32
    1242     68/push  _test-input-stream/imm32
    1243     # . . call
    1244     e8/call  write/disp32
    1245     # . . discard args
    1246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1247     # . stream-add2(labels, "x", 0x11223344)
    1248     68/push  0x11223344/imm32/label-address
    1249     # . . push handle for "x"
    1250     53/push-ebx
    1251     68/push  "x"/imm32
    1252     68/push  Heap/imm32
    1253     e8/call  copy-array/disp32
    1254     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1255     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1256     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1257     # . . push labels
    1258     52/push-edx
    1259     # . . call
    1260     e8/call  stream-add2/disp32
    1261     # . . discard args
    1262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1263     # component under test
    1264     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
    1265     # . . push args
    1266     52/push-edx
    1267     68/push  _test-output-buffered-file/imm32
    1268     68/push  _test-input-stream/imm32
    1269     # . . call
    1270     e8/call  emit-output/disp32
    1271     # . . discard args
    1272     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1273     # checks
    1274     # . flush(_test-output-buffered-file)
    1275     # . . push args
    1276     68/push  _test-output-buffered-file/imm32
    1277     # . . call
    1278     e8/call  flush/disp32
    1279     # . . discard args
    1280     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1281 +-- 33 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    1314     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
    1315     # . . push args
    1316     68/push  "F - test-emit-output-non-far-control-flow/0"/imm32
    1317     68/push  "# 0x00007c00"/imm32
    1318     68/push  _test-output-stream/imm32
    1319     # . . call
    1320     e8/call  check-next-stream-line-equal/disp32
    1321     # . . discard args
    1322     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1323     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
    1324     # . . push args
    1325     68/push  "F - test-emit-output-non-far-control-flow/1"/imm32
    1326     68/push  "ab cd ef gh "/imm32
    1327     68/push  _test-output-stream/imm32
    1328     # . . call
    1329     e8/call  check-next-stream-line-equal/disp32
    1330     # . . discard args
    1331     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1332     # . check-next-stream-line-equal(_test-output-stream, "ij 44 33 22 11 ", msg)
    1333     # . . push args
    1334     68/push  "F - test-emit-output-non-far-control-flow/2"/imm32
    1335     68/push  "ij 44 33 22 11 "/imm32
    1336     68/push  _test-output-stream/imm32
    1337     # . . call
    1338     e8/call  check-next-stream-line-equal/disp32
    1339     # . . discard args
    1340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1341     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c09", msg)
    1342     # . . push args
    1343     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
    1344     68/push  "# 0x00007c09"/imm32
    1345     68/push  _test-output-stream/imm32
    1346     # . . call
    1347     e8/call  check-next-stream-line-equal/disp32
    1348     # . . discard args
    1349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1350     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
    1351     # . . push args
    1352     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
    1353     68/push  "00 "/imm32
    1354     68/push  _test-output-stream/imm32
    1355     # . . call
    1356     e8/call  check-next-stream-line-equal/disp32
    1357     # . . discard args
    1358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1359     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
    1360     # . . push args
    1361     68/push  "F - test-emit-output-non-far-control-flow/4"/imm32
    1362     68/push  "34 "/imm32
    1363     68/push  _test-output-stream/imm32
    1364     # . . call
    1365     e8/call  check-next-stream-line-equal/disp32
    1366     # . . discard args
    1367     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1368     # . epilogue
    1369     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1370     5d/pop-to-ebp
    1371     c3/return
    1372
    1373 test-emit-output-with-padding:
    1374     # labels turn into absolute addresses if opcodes are not far jumps or calls
    1375     #
    1376     # input:
    1377     #   in:
    1378     #     == code
    1379     #     ab cd ef gh
    1380     #     == data 0x7c10
    1381     #     34
    1382     #
    1383     # output:
    1384     #   ab cd ef gh
    1385     #   # 0x7c04
    1386     #   00 00 00 00
    1387     #   00 00 00 00 00 00 00 00
    1388     #   # 0x7c10
    1389     #   34
    1390     #
    1391     # . prologue
    1392     55/push-ebp
    1393     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1394     # setup
    1395     # . clear-stream(_test-input-stream)
    1396     # . . push args
    1397     68/push  _test-input-stream/imm32
    1398     # . . call
    1399     e8/call  clear-stream/disp32
    1400     # . . discard args
    1401     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1402     # . clear-stream(_test-output-stream)
    1403     # . . push args
    1404     68/push  _test-output-stream/imm32
    1405     # . . call
    1406     e8/call  clear-stream/disp32
    1407     # . . discard args
    1408     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1409     # . clear-stream($_test-output-buffered-file->buffer)
    1410     # . . push args
    1411     68/push  $_test-output-buffered-file->buffer/imm32
    1412     # . . call
    1413     e8/call  clear-stream/disp32
    1414     # . . discard args
    1415     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1416     # . var labels/edx: (stream byte 8*24)
    1417     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    1418     68/push  0xc0/imm32/size
    1419     68/push  0/imm32/read
    1420     68/push  0/imm32/write
    1421     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1422     # . var h/ebx: (handle array byte)
    1423     68/push  0/imm32
    1424     68/push  0/imm32
    1425     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1426     # initialize input
    1427     # . write(_test-input-stream, "== code\n")
    1428     # . . push args
    1429     68/push  "== code\n"/imm32
    1430     68/push  _test-input-stream/imm32
    1431     # . . call
    1432     e8/call  write/disp32
    1433     # . . discard args
    1434     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1435     # . write(_test-input-stream, "ab cd ef gh\n")
    1436     # . . push args
    1437     68/push  "ab cd ef gh\n"/imm32
    1438     68/push  _test-input-stream/imm32
    1439     # . . call
    1440     e8/call  write/disp32
    1441     # . . discard args
    1442     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1443     # . write(_test-input-stream, "== data 0x7c10\n")
    1444     # . . push args
    1445     68/push  "== data 0x7c10\n"/imm32
    1446     68/push  _test-input-stream/imm32
    1447     # . . call
    1448     e8/call  write/disp32
    1449     # . . discard args
    1450     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1451     # . write(_test-input-stream, "34\n")
    1452     # . . push args
    1453     68/push  "34\n"/imm32
    1454     68/push  _test-input-stream/imm32
    1455     # . . call
    1456     e8/call  write/disp32
    1457     # . . discard args
    1458     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1459     # component under test
    1460     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
    1461     # . . push args
    1462     52/push-edx
    1463     68/push  _test-output-buffered-file/imm32
    1464     68/push  _test-input-stream/imm32
    1465     # . . call
    1466     e8/call  emit-output/disp32
    1467     # . . discard args
    1468     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1469     # checks
    1470     # . flush(_test-output-buffered-file)
    1471     # . . push args
    1472     68/push  _test-output-buffered-file/imm32
    1473     # . . call
    1474     e8/call  flush/disp32
    1475     # . . discard args
    1476     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1477 +-- 33 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    1510     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
    1511     # . . push args
    1512     68/push  "F - test-emit-output-with-padding/0"/imm32
    1513     68/push  "# 0x00007c00"/imm32
    1514     68/push  _test-output-stream/imm32
    1515     # . . call
    1516     e8/call  check-next-stream-line-equal/disp32
    1517     # . . discard args
    1518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1519     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
    1520     # . . push args
    1521     68/push  "F - test-emit-output-with-padding/1"/imm32
    1522     68/push  "ab cd ef gh "/imm32
    1523     68/push  _test-output-stream/imm32
    1524     # . . call
    1525     e8/call  check-next-stream-line-equal/disp32
    1526     # . . discard args
    1527     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1528     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c04", msg)
    1529     # . . push args
    1530     68/push  "F - test-emit-output-with-padding/0"/imm32
    1531     68/push  "# 0x00007c04"/imm32
    1532     68/push  _test-output-stream/imm32
    1533     # . . call
    1534     e8/call  check-next-stream-line-equal/disp32
    1535     # . . discard args
    1536     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1537     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
    1538     # . . push args
    1539     68/push  "F - test-emit-output-with-padding/2"/imm32
    1540     68/push  "00 00 00 00 "/imm32
    1541     68/push  _test-output-stream/imm32
    1542     # . . call
    1543     e8/call  check-next-stream-line-equal/disp32
    1544     # . . discard args
    1545     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1546     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
    1547     # . . push args
    1548     68/push  "F - test-emit-output-with-padding/3"/imm32
    1549     68/push  "00 00 00 00 00 00 00 00 "/imm32
    1550     68/push  _test-output-stream/imm32
    1551     # . . call
    1552     e8/call  check-next-stream-line-equal/disp32
    1553     # . . discard args
    1554     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1555     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c10", msg)
    1556     # . . push args
    1557     68/push  "F - test-emit-output-with-padding/0"/imm32
    1558     68/push  "# 0x00007c10"/imm32
    1559     68/push  _test-output-stream/imm32
    1560     # . . call
    1561     e8/call  check-next-stream-line-equal/disp32
    1562     # . . discard args
    1563     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1564     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
    1565     # . . push args
    1566     68/push  "F - test-emit-output-with-padding/4"/imm32
    1567     68/push  "34 "/imm32
    1568     68/push  _test-output-stream/imm32
    1569     # . . call
    1570     e8/call  check-next-stream-line-equal/disp32
    1571     # . . discard args
    1572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1573     # . epilogue
    1574     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1575     5d/pop-to-ebp
    1576     c3/return
    1577
    1578 test-emit-output-code-label:
    1579     # labels turn into PC-relative addresses if opcodes are far jumps or calls
    1580     #
    1581     # input:
    1582     #   in:
    1583     #     == code
    1584     #     ab cd
    1585     #     ef gh
    1586     #     e8 l1/disp32
    1587     #   labels:
    1588     #     - 'l1': 0x7c10
    1589     #
    1590     # output:
    1591     #   ab cd
    1592     #   ef gh
    1593     #   e8 07 00 00 00  # 0x7c10 - 0x7c09 = 7
    1594     #
    1595     # . prologue
    1596     55/push-ebp
    1597     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1598     # setup
    1599     # . clear-stream(_test-input-stream)
    1600     # . . push args
    1601     68/push  _test-input-stream/imm32
    1602     # . . call
    1603     e8/call  clear-stream/disp32
    1604     # . . discard args
    1605     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1606     # . clear-stream(_test-output-stream)
    1607     # . . push args
    1608     68/push  _test-output-stream/imm32
    1609     # . . call
    1610     e8/call  clear-stream/disp32
    1611     # . . discard args
    1612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1613     # . clear-stream($_test-output-buffered-file->buffer)
    1614     # . . push args
    1615     68/push  $_test-output-buffered-file->buffer/imm32
    1616     # . . call
    1617     e8/call  clear-stream/disp32
    1618     # . . discard args
    1619     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1620     # . var labels/edx: (stream byte 8*24)
    1621     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    1622     68/push  0xc0/imm32/size
    1623     68/push  0/imm32/read
    1624     68/push  0/imm32/write
    1625     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1626     # . var h/ebx: (handle array byte)
    1627     68/push  0/imm32
    1628     68/push  0/imm32
    1629     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1630     # initialize input
    1631     # . write(_test-input-stream, "== code\n")
    1632     # . . push args
    1633     68/push  "== code\n"/imm32
    1634     68/push  _test-input-stream/imm32
    1635     # . . call
    1636     e8/call  write/disp32
    1637     # . . discard args
    1638     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1639     # . write(_test-input-stream, "ab cd\n")
    1640     # . . push args
    1641     68/push  "ab cd\n"/imm32
    1642     68/push  _test-input-stream/imm32
    1643     # . . call
    1644     e8/call  write/disp32
    1645     # . . discard args
    1646     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1647     # . write(_test-input-stream, "ef gh\n")
    1648     # . . push args
    1649     68/push  "ef gh\n"/imm32
    1650     68/push  _test-input-stream/imm32
    1651     # . . call
    1652     e8/call  write/disp32
    1653     # . . discard args
    1654     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1655     # . write(_test-input-stream, "e8 l1/disp32\n")
    1656     # . . push args
    1657     68/push  "e8 l1/disp32\n"/imm32
    1658     68/push  _test-input-stream/imm32
    1659     # . . call
    1660     e8/call  write/disp32
    1661     # . . discard args
    1662     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1663     # . stream-add2(labels, "l1", 0x7c10)
    1664     68/push  0x7c10/imm32/label-address
    1665     # . . push handle for "l1"
    1666     53/push-ebx
    1667     68/push  "l1"/imm32
    1668     68/push  Heap/imm32
    1669     e8/call  copy-array/disp32
    1670     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1671     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1672     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1673     # . . push labels
    1674     52/push-edx
    1675     # . . call
    1676     e8/call  stream-add2/disp32
    1677     # . . discard args
    1678     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1679     # component under test
    1680     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
    1681     # . . push args
    1682     52/push-edx
    1683     68/push  _test-output-buffered-file/imm32
    1684     68/push  _test-input-stream/imm32
    1685     # . . call
    1686     e8/call  emit-output/disp32
    1687     # . . discard args
    1688     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1689     # checks
    1690     # . flush(_test-output-buffered-file)
    1691     # . . push args
    1692     68/push  _test-output-buffered-file/imm32
    1693     # . . call
    1694     e8/call  flush/disp32
    1695     # . . discard args
    1696     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1697 +-- 33 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    1730     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
    1731     # . . push args
    1732     68/push  "F - test-emit-output-code-label/0"/imm32
    1733     68/push  "# 0x00007c00"/imm32
    1734     68/push  _test-output-stream/imm32
    1735     # . . call
    1736     e8/call  check-next-stream-line-equal/disp32
    1737     # . . discard args
    1738     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1739     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
    1740     # . . push args
    1741     68/push  "F - test-emit-output-code-label/1"/imm32
    1742     68/push  "ab cd "/imm32
    1743     68/push  _test-output-stream/imm32
    1744     # . . call
    1745     e8/call  check-next-stream-line-equal/disp32
    1746     # . . discard args
    1747     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1748     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
    1749     # . . push args
    1750     68/push  "F - test-emit-output-code-label/2"/imm32
    1751     68/push  "ef gh "/imm32
    1752     68/push  _test-output-stream/imm32
    1753     # . . call
    1754     e8/call  check-next-stream-line-equal/disp32
    1755     # . . discard args
    1756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1757     # . check-next-stream-line-equal(_test-output-stream, "e8 07 00 00 00 ", msg)
    1758     # . . push args
    1759     68/push  "F - test-emit-output-code-label/3"/imm32
    1760     68/push  "e8 07 00 00 00 "/imm32
    1761     68/push  _test-output-stream/imm32
    1762     # . . call
    1763     e8/call  check-next-stream-line-equal/disp32
    1764     # . . discard args
    1765     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1766     # . epilogue
    1767     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1768     5d/pop-to-ebp
    1769     c3/return
    1770
    1771 test-emit-output-code-label-absolute:
    1772     # labels can also convert to absolute addresses
    1773     #
    1774     # input:
    1775     #   in:
    1776     #     == code
    1777     #     ab cd
    1778     #     ef gh
    1779     #     ij l1/imm32
    1780     #   labels:
    1781     #     - 'l1': 0x1056
    1782     #
    1783     # output:
    1784     #   ab cd
    1785     #   ef gh
    1786     #   ij 56 10 00 00
    1787     #
    1788     # . prologue
    1789     55/push-ebp
    1790     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1791     # setup
    1792     # . clear-stream(_test-input-stream)
    1793     # . . push args
    1794     68/push  _test-input-stream/imm32
    1795     # . . call
    1796     e8/call  clear-stream/disp32
    1797     # . . discard args
    1798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1799     # . clear-stream(_test-output-stream)
    1800     # . . push args
    1801     68/push  _test-output-stream/imm32
    1802     # . . call
    1803     e8/call  clear-stream/disp32
    1804     # . . discard args
    1805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1806     # . clear-stream($_test-output-buffered-file->buffer)
    1807     # . . push args
    1808     68/push  $_test-output-buffered-file->buffer/imm32
    1809     # . . call
    1810     e8/call  clear-stream/disp32
    1811     # . . discard args
    1812     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1813     # . var labels/edx: (stream byte 8*24)
    1814     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    1815     68/push  0xc0/imm32/size
    1816     68/push  0/imm32/read
    1817     68/push  0/imm32/write
    1818     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1819     # . var h/ebx: (handle array byte)
    1820     68/push  0/imm32
    1821     68/push  0/imm32
    1822     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1823     # initialize input
    1824     # . write(_test-input-stream, "== code 0x1000\n")
    1825     # . . push args
    1826     68/push  "== code\n"/imm32
    1827     68/push  _test-input-stream/imm32
    1828     # . . call
    1829     e8/call  write/disp32
    1830     # . . discard args
    1831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1832     # . write(_test-input-stream, "ab cd\n")
    1833     # . . push args
    1834     68/push  "ab cd\n"/imm32
    1835     68/push  _test-input-stream/imm32
    1836     # . . call
    1837     e8/call  write/disp32
    1838     # . . discard args
    1839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1840     # . write(_test-input-stream, "ef gh\n")
    1841     # . . push args
    1842     68/push  "ef gh\n"/imm32
    1843     68/push  _test-input-stream/imm32
    1844     # . . call
    1845     e8/call  write/disp32
    1846     # . . discard args
    1847     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1848     # . write(_test-input-stream, "ij l1/imm32\n")
    1849     # . . push args
    1850     68/push  "ij l1/imm32\n"/imm32
    1851     68/push  _test-input-stream/imm32
    1852     # . . call
    1853     e8/call  write/disp32
    1854     # . . discard args
    1855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1856     # . stream-add2(labels, "l1", 0x1056)
    1857     68/push  0x1056/imm32/label-address
    1858     # . . push handle for "l1"
    1859     53/push-ebx
    1860     68/push  "l1"/imm32
    1861     68/push  Heap/imm32
    1862     e8/call  copy-array/disp32
    1863     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1864     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1865     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1866     # . . push labels
    1867     52/push-edx
    1868     # . . call
    1869     e8/call  stream-add2/disp32
    1870     # . . discard args
    1871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1872     # component under test
    1873     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
    1874     # . . push args
    1875     52/push-edx
    1876     68/push  _test-output-buffered-file/imm32
    1877     68/push  _test-input-stream/imm32
    1878     # . . call
    1879     e8/call  emit-output/disp32
    1880     # . . discard args
    1881     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1882     # checks
    1883     # . flush(_test-output-buffered-file)
    1884     # . . push args
    1885     68/push  _test-output-buffered-file/imm32
    1886     # . . call
    1887     e8/call  flush/disp32
    1888     # . . discard args
    1889     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1890 +-- 33 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
    1923     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
    1924     # . . push args
    1925     68/push  "F - test-emit-output-code-label-absolute/0"/imm32
    1926     68/push  "# 0x00007c00"/imm32
    1927     68/push  _test-output-stream/imm32
    1928     # . . call
    1929     e8/call  check-next-stream-line-equal/disp32
    1930     # . . discard args
    1931     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1932     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
    1933     # . . push args
    1934     68/push  "F - test-emit-output-code-label-absolute/1"/imm32
    1935     68/push  "ab cd "/imm32
    1936     68/push  _test-output-stream/imm32
    1937     # . . call
    1938     e8/call  check-next-stream-line-equal/disp32
    1939     # . . discard args
    1940     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1941     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
    1942     # . . push args
    1943     68/push  "F - test-emit-output-code-label-absolute/2"/imm32
    1944     68/push  "ef gh "/imm32
    1945     68/push  _test-output-stream/imm32
    1946     # . . call
    1947     e8/call  check-next-stream-line-equal/disp32
    1948     # . . discard args
    1949     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1950     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
    1951     # . . push args
    1952     68/push  "F - test-emit-output-code-label-absolute/3"/imm32
    1953     68/push  "ij 56 10 00 00 "/imm32
    1954     68/push  _test-output-stream/imm32
    1955     # . . call
    1956     e8/call  check-next-stream-line-equal/disp32
    1957     # . . discard args
    1958     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1959     # . epilogue
    1960     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1961     5d/pop-to-ebp
    1962     c3/return
    1963
    1964 # reads line to make some checks
    1965 # don't assume the read state of line after calling this function
    1966 far-jump-or-call?:  # line: (addr stream byte) -> result/edi: boolean
    1967     # . prologue
    1968     55/push-ebp
    1969     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1970     # . save registers
    1971     50/push-eax
    1972     51/push-ecx
    1973     52/push-edx
    1974     53/push-ebx
    1975     # ecx = line
    1976     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
    1977     # var word-slice/edx: slice
    1978     68/push  0/imm32/end
    1979     68/push  0/imm32/start
    1980     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1981     # var datum-slice/ebx: slice
    1982     68/push  0/imm32/end
    1983     68/push  0/imm32/start
    1984     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1985     # result = false
    1986     bf/copy-to-edi  0/imm32/false
    1987 $far-jump-or-call?:check-first-word:
    1988     # next-word(line, word-slice)
    1989     # . . push args
    1990     52/push-edx
    1991     51/push-ecx
    1992     # . . call
    1993     e8/call  next-word/disp32
    1994     # . . discard args
    1995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1996     # if (slice-empty?(word-slice)) return false
    1997     # . eax = slice-empty?(word-slice)
    1998     # . . push args
    1999     52/push-edx
    2000     # . . call
    2001     e8/call  slice-empty?/disp32
    2002     # . . discard args
    2003     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2004     # . if (eax != 0) return
    2005     3d/compare-eax-and  0/imm32/false
    2006     0f 85/jump-if-!=  $far-jump-or-call?:end/disp32
    2007     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
    2008     # . . push args
    2009     53/push-ebx
    2010     68/push  0x2f/imm32/slash
    2011     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    2012     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    2013     # . . call
    2014     e8/call  next-token-from-slice/disp32
    2015     # . . discard args
    2016     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2017     # if (datum-slice == "e8") return true
    2018     # . eax = slice-equal?(datum-slice, "e8")
    2019     # . . push args
    2020     68/push  "e8"/imm32
    2021     53/push-ebx
    2022     # . . call
    2023     e8/call  slice-equal?/disp32
    2024     # . . discard args
    2025     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2026     # . if (eax != false) return true
    2027     3d/compare-eax-and  0/imm32/false
    2028     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
    2029     # if (datum-slice == "e9") return true
    2030     # . eax = slice-equal?(datum-slice, "e9")
    2031     # . . push args
    2032     68/push  "e9"/imm32
    2033     53/push-ebx
    2034     # . . call
    2035     e8/call  slice-equal?/disp32
    2036     # . . discard args
    2037     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2038     # . if (eax != false) return true
    2039     3d/compare-eax-and  0/imm32/false
    2040     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
    2041     # if (datum-slice != "0f") return false
    2042     # . eax = slice-equal?(datum-slice, "0f")
    2043     # . . push args
    2044     68/push  "0f"/imm32
    2045     53/push-ebx
    2046     # . . call
    2047     e8/call  slice-equal?/disp32
    2048     # . . discard args
    2049     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2050     # . if (eax == false) return
    2051     3d/compare-eax-and  0/imm32/false
    2052     74/jump-if-=  $far-jump-or-call?:end/disp8
    2053 $far-jump-or-call?:check-second-word:
    2054     # next-word(line, word-slice)
    2055     # . . push args
    2056     52/push-edx
    2057     51/push-ecx
    2058     # . . call
    2059     e8/call  next-word/disp32
    2060     # . . discard args
    2061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2062     # if (slice-empty?(word-slice)) return false
    2063     # . eax = slice-empty?(word-slice)
    2064     # . . push args
    2065     52/push-edx
    2066     # . . call
    2067     e8/call  slice-empty?/disp32
    2068     # . . discard args
    2069     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2070     # . if (eax != 0) return
    2071     3d/compare-eax-and  0/imm32/false
    2072     75/jump-if-!=  $far-jump-or-call?:end/disp8
    2073     # if datum of word-slice does not start with "8", return false
    2074     # . start/eax = word-slice->start
    2075     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy *edx to eax
    2076     # . c/eax = *start
    2077     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
    2078     25/and-eax-with  0xff/imm32
    2079     # . if (eax != '8') return
    2080     3d/compare-eax-and  0x38/imm32/8
    2081     75/jump-if-!=  $far-jump-or-call?:end/disp8
    2082     # otherwise return true
    2083 $far-jump-or-call?:return-true:
    2084     bf/copy-to-edi  1/imm32/true
    2085 $far-jump-or-call?:end:
    2086     # . reclaim locals
    2087     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2088     # . restore registers
    2089     5b/pop-to-ebx
    2090     5a/pop-to-edx
    2091     59/pop-to-ecx
    2092     58/pop-to-eax
    2093     # . epilogue
    2094     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2095     5d/pop-to-ebp
    2096     c3/return
    2097
    2098 # - some helpers for tests
    2099
    2100 stream-add2:  # in: (addr stream byte), key: handle, val: int
    2101     # . prologue
    2102     55/push-ebp
    2103     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2104     # . save registers
    2105     50/push-eax
    2106     51/push-ecx
    2107     52/push-edx
    2108     56/push-esi
    2109     # esi = in
    2110     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    2111     # curr/eax = &in->data[in->write]
    2112     # . eax = in->write
    2113     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    2114     # . eax = esi+eax+12
    2115     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
    2116     # max/edx = &in->data[in->size]
    2117     # . edx = in->size
    2118     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
    2119     # . edx = esi+edx+12
    2120     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
    2121     # if (curr >= max) abort
    2122     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    2123     73/jump-if-addr>=  $stream-add2:abort/disp8
    2124     # *curr = key->alloc-id
    2125     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
    2126     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    2127     # curr += 4
    2128     05/add-to-eax  4/imm32
    2129     # if (curr >= max) abort
    2130     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    2131     73/jump-if-addr>=  $stream-add2:abort/disp8
    2132     # *curr = key->payload
    2133     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
    2134     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    2135     # curr += 4
    2136     05/add-to-eax  4/imm32
    2137     # if (curr >= max) abort
    2138     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    2139     73/jump-if-addr>=  $stream-add2:abort/disp8
    2140     # *curr = val
    2141     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
    2142     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    2143     # in->write += 0xc
    2144     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to *esi
    2145 $stream-add2:end:
    2146     # . restore registers
    2147     5e/pop-to-esi
    2148     5a/pop-to-edx
    2149     59/pop-to-ecx
    2150     58/pop-to-eax
    2151     # . epilogue
    2152     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2153     5d/pop-to-ebp
    2154     c3/return
    2155
    2156 $stream-add2:abort:
    2157     # . _write(2/stderr, error)
    2158     # . . push args
    2159     68/push  "overflow in stream-add2\n"/imm32
    2160     68/push  2/imm32/stderr
    2161     # . . call
    2162     e8/call  _write/disp32
    2163     # . . discard args
    2164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2165     # . syscall(exit, 1)
    2166     bb/copy-to-ebx  1/imm32
    2167     e8/call  syscall_exit/disp32
    2168     # never gets here
    2169
    2170 # some variants of 'trace' that take multiple arguments in different combinations of types:
    2171 #   n: int
    2172 #   c: character [4-bytes, will eventually be UTF-8]
    2173 #   s: (addr array byte)
    2174 #   l: (addr slice)
    2175 # one gotcha: 's5' must not be empty
    2176
    2177 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
    2178     # . prologue
    2179     55/push-ebp
    2180     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2181     # write(*Trace-stream, s1)
    2182     # . . push args
    2183     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2184     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    2185     # . . call
    2186     e8/call  write/disp32
    2187     # . . discard args
    2188     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2189     # write-slice(*Trace-stream, l2)
    2190     # . . push args
    2191     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2192     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    2193     # . . call
    2194     e8/call  write-slice/disp32
    2195     # . . discard args
    2196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2197     # write(*Trace-stream, s3)
    2198     # . . push args
    2199     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    2200     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    2201     # . . call
    2202     e8/call  write/disp32
    2203     # . . discard args
    2204     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2205     # write-int32-hex(*Trace-stream, n4)
    2206     # . . push args
    2207     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    2208     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    2209     # . . call
    2210     e8/call  write-int32-hex/disp32
    2211     # . . discard args
    2212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2213     # trace(s5)  # implicitly adds a newline and finalizes the trace line
    2214     # . . push args
    2215     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    2216     # . . call
    2217     e8/call  trace/disp32
    2218     # . . discard args
    2219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2220 $trace-slsns:end:
    2221     # . epilogue
    2222     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2223     5d/pop-to-ebp
    2224     c3/return
    2225
    2226 test-trace-slsns:
    2227     # . prologue
    2228     55/push-ebp
    2229     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2230     # setup
    2231     # . *Trace-stream->write = 0
    2232     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
    2233     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
    2234     # (eax..ecx) = "b"
    2235     b8/copy-to-eax  "b"/imm32
    2236     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    2237     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
    2238     05/add-to-eax  4/imm32
    2239     # var b/ebx: slice = {eax, ecx}
    2240     51/push-ecx
    2241     50/push-eax
    2242     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    2243     # trace-slsls("A" b "c " 3 " e")
    2244     # . . push args
    2245     68/push  " e"/imm32
    2246     68/push  3/imm32
    2247     68/push  "c "/imm32
    2248     53/push-ebx
    2249     68/push  "A"/imm32
    2250     # . . call
    2251     e8/call  trace-slsns/disp32
    2252     # . . discard args
    2253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    2254 +-- 26 lines: #?     # dump *Trace-stream ----------------------------------------------------------------------------------------------------------------------------------
    2280     # check-trace-contains("Abc 0x00000003 e")
    2281     # . . push args
    2282     68/push  "F - test-trace-slsls"/imm32
    2283     68/push  "Abc 0x00000003 e"/imm32
    2284     # . . call
    2285     e8/call  check-trace-contains/disp32
    2286     # . . discard args
    2287     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2288     # . epilogue
    2289     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2290     5d/pop-to-ebp
    2291     c3/return
    2292
    2293 num-bytes:  # line: (addr stream byte) -> eax: int
    2294     # pseudocode:
    2295     #   result = 0
    2296     #   while true
    2297     #     var word-slice = next-word(line)
    2298     #     if slice-empty?(word-slice)             # end of line
    2299     #       break
    2300     #     if slice-starts-with?(word-slice, "#")  # comment
    2301     #       break
    2302     #     if label?(word-slice)                # no need for label declarations anymore
    2303     #       break
    2304     #     if slice-equal?(word-slice, "==")
    2305     #       break                                 # no need for segment header lines
    2306     #     result += compute-width(word-slice)
    2307     #   return result
    2308     #
    2309     # . prologue
    2310     55/push-ebp
    2311     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2312     # . save registers
    2313     51/push-ecx
    2314     52/push-edx
    2315     53/push-ebx
    2316     # var result/eax = 0
    2317     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    2318     # var word-slice/ecx: slice
    2319     68/push  0/imm32/end
    2320     68/push  0/imm32/start
    2321     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    2322 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
    2348     # . rewind-stream(line)
    2349     # . . push args
    2350     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2351     # . . call
    2352     e8/call  rewind-stream/disp32
    2353     # . . discard args
    2354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2355 $num-bytes:loop:
    2356     # next-word(line, word-slice)
    2357     # . . push args
    2358     51/push-ecx
    2359     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2360     # . . call
    2361     e8/call  next-word/disp32
    2362     # . . discard args
    2363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2364 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
    2404 $num-bytes:check0:
    2405     # if (slice-empty?(word-slice)) break
    2406     # . save result
    2407     50/push-eax
    2408     # . eax = slice-empty?(word-slice)
    2409     # . . push args
    2410     51/push-ecx
    2411     # . . call
    2412     e8/call  slice-empty?/disp32
    2413     # . . discard args
    2414     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2415     # . if (eax != false) break
    2416     3d/compare-eax-and  0/imm32/false
    2417     # . restore result now that ZF is set
    2418     58/pop-to-eax
    2419     75/jump-if-!=  $num-bytes:end/disp8
    2420 $num-bytes:check-for-comment:
    2421     # if (slice-starts-with?(word-slice, "#")) break
    2422     # . start/edx = word-slice->start
    2423     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    2424     # . c/ebx = *start
    2425     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    2426     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           3/r32/BL    .               .                 # copy byte at *edx to BL
    2427     # . if (ebx == '#') break
    2428     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x23/imm32/hash   # compare ebx
    2429     74/jump-if-=  $num-bytes:end/disp8
    2430 $num-bytes:check-for-label:
    2431     # if (slice-ends-with?(word-slice, ":")) break
    2432     # . end/edx = word-slice->end
    2433     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
    2434     # . c/ebx = *(end-1)
    2435     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    2436     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ecx to BL
    2437     # . if (ebx == ':') break
    2438     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x3a/imm32/colon  # compare ebx
    2439     74/jump-if-=  $num-bytes:end/disp8
    2440 $num-bytes:check-for-segment-header:
    2441     # if (slice-equal?(word-slice, "==")) break
    2442     # . push result
    2443     50/push-eax
    2444     # . eax = slice-equal?(word-slice, "==")
    2445     # . . push args
    2446     68/push  "=="/imm32
    2447     51/push-ecx
    2448     # . . call
    2449     e8/call  slice-equal?/disp32
    2450     # . . discard args
    2451     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2452     # . if (eax != false) break
    2453     3d/compare-eax-and  0/imm32/false
    2454     # . restore result now that ZF is set
    2455     58/pop-to-eax
    2456     75/jump-if-!=  $num-bytes:end/disp8
    2457 $num-bytes:loop-body:
    2458     # result += compute-width-of-slice(word-slice)
    2459     # . copy result to edx
    2460     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
    2461     # . eax = compute-width-of-slice(word-slice)
    2462     # . . push args
    2463     51/push-ecx
    2464     # . . call
    2465     e8/call compute-width-of-slice/disp32
    2466     # . . discard args
    2467     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2468     # . eax += result
    2469     01/add                          3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # add edx to eax
    2470     e9/jump  $num-bytes:loop/disp32
    2471 $num-bytes:end:
    2472     # . rewind-stream(line)
    2473     # . . push args
    2474     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    2475     # . . call
    2476     e8/call  rewind-stream/disp32
    2477     # . . discard args
    2478     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2479     # . reclaim locals
    2480     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2481     # . restore registers
    2482     5b/pop-to-ebx
    2483     5a/pop-to-edx
    2484     59/pop-to-ecx
    2485     # . epilogue
    2486     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2487     5d/pop-to-ebp
    2488     c3/return
    2489
    2490 test-num-bytes-handles-empty-string:
    2491     # if a line starts with '#', return 0
    2492     # . prologue
    2493     55/push-ebp
    2494     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2495     # setup
    2496     # . clear-stream(_test-input-stream)
    2497     # . . push args
    2498     68/push  _test-input-stream/imm32
    2499     # . . call
    2500     e8/call  clear-stream/disp32
    2501     # . . discard args
    2502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2503     # . clear-stream(_test-output-stream)
    2504     # . . push args
    2505     68/push  _test-output-stream/imm32
    2506     # . . call
    2507     e8/call  clear-stream/disp32
    2508     # . . discard args
    2509     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2510     # no contents in input
    2511     # eax = num-bytes(_test-input-stream)
    2512     # . . push args
    2513     68/push  _test-input-stream/imm32
    2514     # . . call
    2515     e8/call  num-bytes/disp32
    2516     # . . discard args
    2517     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2518     # check-ints-equal(eax, 0, msg)
    2519     # . . push args
    2520     68/push  "F - test-num-bytes-handles-empty-string"/imm32
    2521     68/push  0/imm32/true
    2522     50/push-eax
    2523     # . . call
    2524     e8/call  check-ints-equal/disp32
    2525     # . . discard args
    2526     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2527     # . epilogue
    2528     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2529     5d/pop-to-ebp
    2530     c3/return
    2531
    2532 test-num-bytes-ignores-comments:
    2533     # if a line starts with '#', return 0
    2534     # . prologue
    2535     55/push-ebp
    2536     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2537     # setup
    2538     # . clear-stream(_test-input-stream)
    2539     # . . push args
    2540     68/push  _test-input-stream/imm32
    2541     # . . call
    2542     e8/call  clear-stream/disp32
    2543     # . . discard args
    2544     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2545     # . clear-stream(_test-output-stream)
    2546     # . . push args
    2547     68/push  _test-output-stream/imm32
    2548     # . . call
    2549     e8/call  clear-stream/disp32
    2550     # . . discard args
    2551     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2552     # initialize input
    2553     # . write(_test-input-stream, "# abcd")
    2554     # . . push args
    2555     68/push  "# abcd"/imm32
    2556     68/push  _test-input-stream/imm32
    2557     # . . call
    2558     e8/call  write/disp32
    2559     # . . discard args
    2560     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2561     # eax = num-bytes(_test-input-stream)
    2562     # . . push args
    2563     68/push  _test-input-stream/imm32
    2564     # . . call
    2565     e8/call  num-bytes/disp32
    2566     # . . discard args
    2567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2568     # check-ints-equal(eax, 0, msg)
    2569     # . . push args
    2570     68/push  "F - test-num-bytes-ignores-comments"/imm32
    2571     68/push  0/imm32/true
    2572     50/push-eax
    2573     # . . call
    2574     e8/call  check-ints-equal/disp32
    2575     # . . discard args
    2576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2577     # . epilogue
    2578     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2579     5d/pop-to-ebp
    2580     c3/return
    2581
    2582 test-num-bytes-ignores-labels:
    2583     # if the first word ends with ':', return 0
    2584     # . prologue
    2585     55/push-ebp
    2586     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2587     # setup
    2588     # . clear-stream(_test-input-stream)
    2589     # . . push args
    2590     68/push  _test-input-stream/imm32
    2591     # . . call
    2592     e8/call  clear-stream/disp32
    2593     # . . discard args
    2594     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2595     # . clear-stream(_test-output-stream)
    2596     # . . push args
    2597     68/push  _test-output-stream/imm32
    2598     # . . call
    2599     e8/call  clear-stream/disp32
    2600     # . . discard args
    2601     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2602     # initialize input
    2603     # . write(_test-input-stream, "ab: # cd")
    2604     # . . push args
    2605     68/push  "ab: # cd"/imm32
    2606     68/push  _test-input-stream/imm32
    2607     # . . call
    2608     e8/call  write/disp32
    2609     # . . discard args
    2610     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2611     # eax = num-bytes(_test-input-stream)
    2612     # . . push args
    2613     68/push  _test-input-stream/imm32
    2614     # . . call
    2615     e8/call  num-bytes/disp32
    2616     # . . discard args
    2617     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2618     # check-ints-equal(eax, 0, msg)
    2619     # . . push args
    2620     68/push  "F - test-num-bytes-ignores-labels"/imm32
    2621     68/push  0/imm32/true
    2622     50/push-eax
    2623     # . . call
    2624     e8/call  check-ints-equal/disp32
    2625     # . . discard args
    2626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2627     # . epilogue
    2628     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2629     5d/pop-to-ebp
    2630     c3/return
    2631
    2632 test-num-bytes-ignores-segment-headers:
    2633     # if the first word is '==', return 0
    2634     # . prologue
    2635     55/push-ebp
    2636     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2637     # setup
    2638     # . clear-stream(_test-input-stream)
    2639     # . . push args
    2640     68/push  _test-input-stream/imm32
    2641     # . . call
    2642     e8/call  clear-stream/disp32
    2643     # . . discard args
    2644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2645     # . clear-stream(_test-output-stream)
    2646     # . . push args
    2647     68/push  _test-output-stream/imm32
    2648     # . . call
    2649     e8/call  clear-stream/disp32
    2650     # . . discard args
    2651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2652     # initialize input
    2653     # . write(_test-input-stream, "== ab cd")
    2654     # . . push args
    2655     68/push  "== ab cd"/imm32
    2656     68/push  _test-input-stream/imm32
    2657     # . . call
    2658     e8/call  write/disp32
    2659     # . . discard args
    2660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2661     # eax = num-bytes(_test-input-stream)
    2662     # . . push args
    2663     68/push  _test-input-stream/imm32
    2664     # . . call
    2665     e8/call  num-bytes/disp32
    2666     # . . discard args
    2667     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2668     # check-ints-equal(eax, 0, msg)
    2669     # . . push args
    2670     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
    2671     68/push  0/imm32/true
    2672     50/push-eax
    2673     # . . call
    2674     e8/call  check-ints-equal/disp32
    2675     # . . discard args
    2676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2677     # . epilogue
    2678     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2679     5d/pop-to-ebp
    2680     c3/return
    2681
    2682 test-num-bytes-counts-words-by-default:
    2683     # without metadata, count words
    2684     # . prologue
    2685     55/push-ebp
    2686     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2687     # setup
    2688     # . clear-stream(_test-input-stream)
    2689     # . . push args
    2690     68/push  _test-input-stream/imm32
    2691     # . . call
    2692     e8/call  clear-stream/disp32
    2693     # . . discard args
    2694     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2695     # . clear-stream(_test-output-stream)
    2696     # . . push args
    2697     68/push  _test-output-stream/imm32
    2698     # . . call
    2699     e8/call  clear-stream/disp32
    2700     # . . discard args
    2701     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2702     # initialize input
    2703     # . write(_test-input-stream, "ab cd ef")
    2704     # . . push args
    2705     68/push  "ab cd ef"/imm32
    2706     68/push  _test-input-stream/imm32
    2707     # . . call
    2708     e8/call  write/disp32
    2709     # . . discard args
    2710     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2711     # eax = num-bytes(_test-input-stream)
    2712     # . . push args
    2713     68/push  _test-input-stream/imm32
    2714     # . . call
    2715     e8/call  num-bytes/disp32
    2716     # . . discard args
    2717     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2718     # check-ints-equal(eax, 3, msg)
    2719     # . . push args
    2720     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
    2721     68/push  3/imm32/true
    2722     50/push-eax
    2723     # . . call
    2724     e8/call  check-ints-equal/disp32
    2725     # . . discard args
    2726     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2727     # . epilogue
    2728     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2729     5d/pop-to-ebp
    2730     c3/return
    2731
    2732 test-num-bytes-ignores-trailing-comment:
    2733     # trailing comments appropriately ignored
    2734     # . prologue
    2735     55/push-ebp
    2736     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2737     # setup
    2738     # . clear-stream(_test-input-stream)
    2739     # . . push args
    2740     68/push  _test-input-stream/imm32
    2741     # . . call
    2742     e8/call  clear-stream/disp32
    2743     # . . discard args
    2744     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2745     # . clear-stream(_test-output-stream)
    2746     # . . push args
    2747     68/push  _test-output-stream/imm32
    2748     # . . call
    2749     e8/call  clear-stream/disp32
    2750     # . . discard args
    2751     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2752     # initialize input
    2753     # . write(_test-input-stream, "ab cd # ef")
    2754     # . . push args
    2755     68/push  "ab cd # ef"/imm32
    2756     68/push  _test-input-stream/imm32
    2757     # . . call
    2758     e8/call  write/disp32
    2759     # . . discard args
    2760     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2761     # eax = num-bytes(_test-input-stream)
    2762     # . . push args
    2763     68/push  _test-input-stream/imm32
    2764     # . . call
    2765     e8/call  num-bytes/disp32
    2766     # . . discard args
    2767     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2768     # check-ints-equal(eax, 2, msg)
    2769     # . . push args
    2770     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
    2771     68/push  2/imm32/true
    2772     50/push-eax
    2773     # . . call
    2774     e8/call  check-ints-equal/disp32
    2775     # . . discard args
    2776     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2777     # . epilogue
    2778     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2779     5d/pop-to-ebp
    2780     c3/return
    2781
    2782 test-num-bytes-handles-imm32:
    2783     # if a word has the /imm32 metadata, count it as 4 bytes
    2784     # . prologue
    2785     55/push-ebp
    2786     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2787     # setup
    2788     # . clear-stream(_test-input-stream)
    2789     # . . push args
    2790     68/push  _test-input-stream/imm32
    2791     # . . call
    2792     e8/call  clear-stream/disp32
    2793     # . . discard args
    2794     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2795     # . clear-stream(_test-output-stream)
    2796     # . . push args
    2797     68/push  _test-output-stream/imm32
    2798     # . . call
    2799     e8/call  clear-stream/disp32
    2800     # . . discard args
    2801     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2802     # initialize input
    2803     # . write(_test-input-stream, "ab cd/imm32 ef")
    2804     # . . push args
    2805     68/push  "ab cd/imm32 ef"/imm32
    2806     68/push  _test-input-stream/imm32
    2807     # . . call
    2808     e8/call  write/disp32
    2809     # . . discard args
    2810     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2811     # eax = num-bytes(_test-input-stream)
    2812     # . . push args
    2813     68/push  _test-input-stream/imm32
    2814     # . . call
    2815     e8/call  num-bytes/disp32
    2816     # . . discard args
    2817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2818     # check-ints-equal(eax, 6, msg)
    2819     # . . push args
    2820     68/push  "F - test-num-bytes-handles-imm32"/imm32
    2821     68/push  6/imm32/true
    2822     50/push-eax
    2823     # . . call
    2824     e8/call  check-ints-equal/disp32
    2825     # . . discard args
    2826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2827     # . epilogue
    2828     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2829     5d/pop-to-ebp
    2830     c3/return
    2831
    2832 # . . vim:nowrap:textwidth=0