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

       1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
       2 # (landscape).
       3 # Use the addresses assigned to:
       4 #   a) replace labels
       5 #   b) add an ELF header and segment headers with addresses and offsets correctly filled in
       6 #
       7 # To build:
       8 #   $ bootstrap/bootstrap translate [01]*.subx apps/subx-params.subx apps/survey_elf.subx  -o apps/survey_elf
       9 #
      10 # The expected input is a stream of bytes with '==' segment headers, comments
      11 # and some interspersed labels.
      12 #   $ cat x
      13 #   == code 0x1
      14 #   l1:
      15 #   aa bb l1/imm8
      16 #   cc dd l2/disp32
      17 #   l2:
      18 #   ee foo/imm32
      19 #   == data 0x10
      20 #   foo:
      21 #     00
      22 #
      23 # The output is the stream of bytes without segment headers or label definitions,
      24 # and with label references replaced with numeric values/displacements.
      25 #
      26 #   $ cat x  |bootstrap/bootstrap run survey_elf
      27 #   ...ELF header bytes...
      28 #   # ELF header above will specify that code segment begins at this offset
      29 #   aa bb nn  # some computed address
      30 #   cc dd nn nn nn nn  # some computed displacement
      31 #   ee nn nn nn nn  # some computed address
      32 #   # ELF header above will specify that data segment begins at this offset
      33 #   00
      34 #
      35 # The ELF format has some persnickety constraints on the starting addresses of
      36 # segments, so input headers are treated as guidelines and adjusted in the
      37 # output.
      38
      39 == code
      40 #   instruction                     effective address                                                   register    displacement    immediate
      41 # . op          subop               mod             rm32          base        index         scale       r32
      42 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
      43
      44 Entry:  # run tests if necessary, convert stdin if not
      45     # . prologue
      46     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
      47
      48     # Heap = new-segment(Heap-size)
      49     # . . push args
      50     68/push  Heap/imm32
      51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
      52     # . . call
      53     e8/call  new-segment/disp32
      54     # . . discard args
      55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      56     # initialize-trace-stream(Trace-size)
      57     # . . push args
      58     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
      59     # . . call
      60     e8/call  initialize-trace-stream/disp32
      61     # . . discard args
      62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
      63
      64     # - if argc > 1 and argv[1] == "test", then return run_tests()
      65     # if (argc <= 1) goto interactive
      66     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
      67     7e/jump-if-<=  $subx-survey-main:interactive/disp8
      68     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
      69     # . eax = kernel-string-equal?(argv[1], "test")
      70     # . . push args
      71     68/push  "test"/imm32
      72     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
      73     # . . call
      74     e8/call  kernel-string-equal?/disp32
      75     # . . discard args
      76     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      77     # . if (eax == false) goto interactive
      78     3d/compare-eax-and  0/imm32/false
      79     74/jump-if-=  $subx-survey-main:interactive/disp8
      80     # run-tests()
      81     e8/call  run-tests/disp32
      82     # syscall(exit, *Num-test-failures)
      83     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
      84     eb/jump  $subx-survey-main:end/disp8
      85 $subx-survey-main:interactive:
      86     # - otherwise convert stdin
      87     # subx-survey(Stdin, Stdout)
      88     # . . push args
      89     68/push  Stdout/imm32
      90     68/push  Stdin/imm32
      91     # . . call
      92     e8/call  subx-survey/disp32
      93     # . . discard args
      94     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      95 #?     # . write-stream(2/stderr, Trace-stream)
      96 #?     # . . push args
      97 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
      98 #?     68/push  2/imm32/stderr
      99 #?     # . . call
     100 #?     e8/call  write-stream/disp32
     101 #?     # . . discard args
     102 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     103     # syscall(exit, 0)
     104     bb/copy-to-ebx  0/imm32
     105 $subx-survey-main:end:
     106     e8/call  syscall_exit/disp32
     107
     108 # data structures:
     109 #   segment-info: {address, file-offset, size}                                  (12 bytes)
     110 #   segments: (addr stream {(handle array byte), segment-info})                 (20 bytes per row)
     111 #   label-info: {segment-name: (handle array byte), segment-offset, address}    (16 bytes)
     112 #   labels: (addr stream {(handle array byte), label-info})                     (24 bytes per row)
     113 # these are all inefficient, using sequential scans for lookups
     114
     115 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
     116     # pseudocode
     117     #   var in: (stream byte Input-size)
     118     #   slurp(infile, in)
     119     #   var segments: (stream {segment-name, segment-info})
     120     #   var labels: (stream {label-name, label-info} Max-labels)
     121     #   compute-offsets(in, segments, labels)
     122     #   compute-addresses(segments, labels)
     123     #   rewind-stream(in)
     124     #   emit-output(in, out, segments, labels)
     125     #
     126     # . prologue
     127     55/push-ebp
     128     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     129     # . save registers
     130     51/push-ecx
     131     52/push-edx
     132     56/push-esi
     133     # var segments/ecx: (stream {string, segment-info} 200)   # 10 rows * 20 bytes/row
     134     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc8/imm32        # subtract from esp
     135     68/push  0xc8/imm32/size
     136     68/push  0/imm32/read
     137     68/push  0/imm32/write
     138     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     139     # var labels/edx: (stream label-info Max-labels)
     140     # . data
     141     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
     142     # . size
     143     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
     144     # . read
     145     68/push  0/imm32/read
     146     # . write
     147     68/push  0/imm32/write
     148     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     149     # var in/esi: (stream byte Input-size)
     150     # . data
     151     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
     152     # . size
     153     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
     154     # . read
     155     68/push  0/imm32/read
     156     # . write
     157     68/push  0/imm32/write
     158     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
     159     # slurp(infile, in)
     160     # . . push args
     161     56/push-esi
     162     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     163     # . . call
     164     e8/call  slurp/disp32
     165     # . . discard args
     166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     167     # compute-offsets(in, segments, labels)
     168     # . . push args
     169     52/push-edx
     170     51/push-ecx
     171     56/push-esi
     172     # . . call
     173     e8/call  compute-offsets/disp32
     174     # . . discard args
     175     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     176     # compute-addresses(segments, labels)
     177     # . . push args
     178     52/push-edx
     179     51/push-ecx
     180     # . . call
     181     e8/call  compute-addresses/disp32
     182     # . . discard args
     183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     184     # rewind-stream(in)
     185     # . . push args
     186     56/push-esi
     187     # . . call
     188     e8/call  rewind-stream/disp32
     189     # . . discard args
     190     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     191     # emit-output(in, out, segments, labels)
     192     # . . push args
     193     52/push-edx
     194     51/push-ecx
     195     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     196     56/push-esi
     197     # . . call
     198     e8/call  emit-output/disp32
     199     # . . discard args
     200     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     201     # flush(out)
     202     # . . push args
     203     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     204     # . . call
     205     e8/call  flush/disp32
     206     # . . discard args
     207     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     208 $subx-survey:end:
     209     # . reclaim locals
     210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xec/imm32        # add to esp
     211     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
     212     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
     213     # . restore registers
     214     5e/pop-to-esi
     215     5a/pop-to-edx
     216     59/pop-to-ecx
     217     # . epilogue
     218     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     219     5d/pop-to-ebp
     220     c3/return
     221
     222 test-subx-survey-computes-addresses:
     223     # input:
     224     #   == code 0x1
     225     #   Entry:
     226     #   ab x/imm32
     227     #   == data 0x1000
     228     #   x:
     229     #     01
     230     #
     231     # trace contains (in any order):
     232     #   label x is at address 0x1079
     233     #   segment code starts at address 0x74
     234     #   segment code has size 5
     235     #   segment data starts at address 0x1079
     236     #
     237     # . prologue
     238     55/push-ebp
     239     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     240     # setup
     241     # . clear-stream(_test-input-stream)
     242     # . . push args
     243     68/push  _test-input-stream/imm32
     244     # . . call
     245     e8/call  clear-stream/disp32
     246     # . . discard args
     247     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     248     # . clear-stream($_test-input-buffered-file->buffer)
     249     # . . push args
     250     68/push  $_test-input-buffered-file->buffer/imm32
     251     # . . call
     252     e8/call  clear-stream/disp32
     253     # . . discard args
     254     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     255     # . clear-stream(_test-output-stream)
     256     # . . push args
     257     68/push  _test-output-stream/imm32
     258     # . . call
     259     e8/call  clear-stream/disp32
     260     # . . discard args
     261     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     262     # . clear-stream($_test-output-buffered-file->buffer)
     263     # . . push args
     264     68/push  $_test-output-buffered-file->buffer/imm32
     265     # . . call
     266     e8/call  clear-stream/disp32
     267     # . . discard args
     268     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     269     # initialize input
     270     # . write(_test-input-stream, "== code 0x1\n")
     271     # . . push args
     272     68/push  "== code 0x1\n"/imm32
     273     68/push  _test-input-stream/imm32
     274     # . . call
     275     e8/call  write/disp32
     276     # . . discard args
     277     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     278     # . write(_test-input-stream, "Entry:\n")
     279     # . . push args
     280     68/push  "Entry:\n"/imm32
     281     68/push  _test-input-stream/imm32
     282     # . . call
     283     e8/call  write/disp32
     284     # . . discard args
     285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     286     # . write(_test-input-stream, "ab x/imm32\n")
     287     # . . push args
     288     68/push  "ab x/imm32\n"/imm32
     289     68/push  _test-input-stream/imm32
     290     # . . call
     291     e8/call  write/disp32
     292     # . . discard args
     293     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     294     # . write(_test-input-stream, "== data 0x1000\n")
     295     # . . push args
     296     68/push  "== data 0x1000\n"/imm32
     297     68/push  _test-input-stream/imm32
     298     # . . call
     299     e8/call  write/disp32
     300     # . . discard args
     301     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     302     # . write(_test-input-stream, "x:\n")
     303     # . . push args
     304     68/push  "x:\n"/imm32
     305     68/push  _test-input-stream/imm32
     306     # . . call
     307     e8/call  write/disp32
     308     # . . discard args
     309     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     310     # . write(_test-input-stream, "01\n")
     311     # . . push args
     312     68/push  "01\n"/imm32
     313     68/push  _test-input-stream/imm32
     314     # . . call
     315     e8/call  write/disp32
     316     # . . discard args
     317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     318     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
     319     # . . push args
     320     68/push  _test-output-buffered-file/imm32
     321     68/push  _test-input-buffered-file/imm32
     322     # . . call
     323     e8/call  subx-survey/disp32
     324     # . . discard args
     325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     326     # check trace
     327 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
     353     # . check-trace-contains("label 'x' is at address 0x00001079.", msg)
     354     # . . push args
     355     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
     356     68/push  "label 'x' is at address 0x00001079."/imm32
     357     # . . call
     358     e8/call  check-trace-contains/disp32
     359     # . . discard args
     360     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     361     # . check-trace-contains("segment 'code' starts at address 0x00000074.", msg)
     362     # . . push args
     363     68/push  "F - test-subx-survey-computes-addresses/1"/imm32
     364     68/push  "segment 'code' starts at address 0x00000074."/imm32
     365     # . . call
     366     e8/call  check-trace-contains/disp32
     367     # . . discard args
     368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     369     # . check-trace-contains("segment 'code' has size 0x00000005.", msg)
     370     # . . push args
     371     68/push  "F - test-subx-survey-computes-addresses/2"/imm32
     372     68/push  "segment 'code' has size 0x00000005."/imm32
     373     # . . call
     374     e8/call  check-trace-contains/disp32
     375     # . . discard args
     376     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     377     # . check-trace-contains("segment 'data' starts at address 0x00001079.", msg)
     378     # . . push args
     379     68/push  "F - test-subx-survey-computes-addresses/3"/imm32
     380     68/push  "segment 'data' starts at address 0x00001079."/imm32
     381     # . . call
     382     e8/call  check-trace-contains/disp32
     383     # . . discard args
     384     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     385     # . epilogue
     386     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     387     5d/pop-to-ebp
     388     c3/return
     389
     390 # global scratch space for compute-offsets
     391 == data
     392
     393 compute-offsets:file-offset:  # int
     394   0/imm32
     395 compute-offsets:segment-offset:  # int
     396   0/imm32
     397 compute-offsets:segment-tmp:  # slice
     398   0/imm32/start
     399   0/imm32/end
     400
     401 == code
     402
     403 # write segments->file-offset,
     404 #       segments->size,
     405 #       labels->segment-name, and
     406 #       labels->segment-offset
     407 compute-offsets:  # in: (addr stream byte), segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
     408     # skeleton:
     409     #   for lines in 'in'
     410     #     for words in line
     411     #       switch word
     412     #         case 1
     413     #         case 2
     414     #         ...
     415     #         default
     416     #
     417     # pseudocode:
     418     #   var curr-segment-name: (handle array byte)
     419     #   var file-offset = 0
     420     #   var segment-offset = 0
     421     #   var line: (stream byte 512)
     422     #   var sinfo: (addr segment-info)
     423     #   var linfo: (addr label-info)
     424     #   while true                                  # line loop
     425     #     clear-stream(line)
     426     #     read-line(in, line)
     427     #     if (line->write == 0) break               # end of file
     428     #     while true                                # word loop
     429     #       word-slice = next-word(line)
     430     #       if slice-empty?(word-slice)             # end of line
     431     #         break
     432     #       else if slice-starts-with?(word-slice, "#")  # comment
     433     #         break                                 # end of line
     434     #       else if slice-equal?(word-slice, "==")
     435     #         if *curr-segment-name != 0
     436     #           sinfo = get-or-insert-handle(segments, curr-segment-name)
     437     #           sinfo->size = file-offset - sinfo->file-offset
     438     #           trace("segment '", curr-segment-name, "' has size ", sinfo->size)
     439     #         segment-tmp = next-word(line)
     440     #         if slice-empty?(segment-tmp)
     441     #           abort
     442     #         curr-segment-name = slice-to-string(segment-tmp)
     443     #         segment-tmp = next-word(line)
     444     #         if slice-empty?(segment-tmp)
     445     #           abort
     446     #         sinfo = get-or-insert-handle(segments, curr-segment-name)
     447     #         sinfo->starting-address = parse-hex-int-from-slice(segment-tmp)
     448     #         sinfo->file-offset = file-offset
     449     #         trace("segment '", curr-segment-name, "' is at file offset ", sinfo->file-offset)
     450     #         segment-offset = 0
     451     #         break  (next line)
     452     #       else if label?(word-slice)
     453     #         strip trailing ':' from word-slice
     454     #         linfo: (addr label-info) = get-or-insert-slice(labels, word-slice)
     455     #         linfo->segment-name = curr-segment-name
     456     #         trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
     457     #         linfo->segment-offset = segment-offset
     458     #         trace("label '", word-slice, "' is at segment offset ", segment-offset, ".")
     459     #         # labels occupy no space, so no need to increment offsets
     460     #       else
     461     #         width = compute-width-of-slice(word-slice)
     462     #         segment-offset += width
     463     #         file-offset += width
     464     #   sinfo = get-or-insert-handle(segments, curr-segment-name)
     465     #   sinfo->size = file-offset - sinfo->file-offset
     466     #
     467     # . prologue
     468     55/push-ebp
     469     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     470     # . save registers
     471     50/push-eax
     472     51/push-ecx
     473     52/push-edx
     474     53/push-ebx
     475     56/push-esi
     476     57/push-edi
     477     # var curr-segment-name/esi: (handle array byte)
     478     68/push  0/imm32
     479     68/push  0/imm32
     480     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
     481     # file-offset = 0
     482     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:file-offset/disp32  0/imm32  # copy to *compute-offsets:file-offset
     483     # segment-offset = 0
     484     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:segment-offset/disp32  0/imm32  # copy to *compute-offsets:segment-offset
     485     # var line/ecx: (stream byte 512)
     486     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     487     68/push  0x200/imm32/size
     488     68/push  0/imm32/read
     489     68/push  0/imm32/write
     490     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     491     # var word-slice/edx: (addr slice)
     492     68/push  0/imm32
     493     68/push  0/imm32
     494     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     495 $compute-offsets:line-loop:
     496     # clear-stream(line)
     497     # . . push args
     498     51/push-ecx
     499     # . . call
     500     e8/call  clear-stream/disp32
     501     # . . discard args
     502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     503     # read-line(in, line)
     504     # . . push args
     505     51/push-ecx
     506     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     507     # . . call
     508     e8/call  read-line/disp32
     509     # . . discard args
     510     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     511     # if (line->write == 0) break
     512     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
     513     3d/compare-eax-and  0/imm32
     514     0f 84/jump-if-=  $compute-offsets:break-line-loop/disp32
     515 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
     548 $compute-offsets:word-loop:
     549     # next-word(line, word-slice)
     550     # . . push args
     551     52/push-edx
     552     51/push-ecx
     553     # . . call
     554     e8/call  next-word/disp32
     555     # . . discard args
     556     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     557 $compute-offsets:case-empty:
     558     # if slice-empty?(word-slice) break
     559     # . eax = slice-empty?(word-slice)
     560     # . . push args
     561     52/push-edx
     562     # . . call
     563     e8/call  slice-empty?/disp32
     564     # . . discard args
     565     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     566     # . if (eax != false) break
     567     3d/compare-eax-and  0/imm32/false
     568     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
     569 $compute-offsets:case-comment:
     570     # if slice-starts-with?(word-slice, "#") continue
     571     # . . push args
     572     68/push  "#"/imm32
     573     52/push-edx
     574     # . . call
     575     e8/call  slice-starts-with?/disp32
     576     # . . discard args
     577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     578     # . if (eax != false) break
     579     3d/compare-eax-and  0/imm32/false
     580     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
     581 $compute-offsets:case-segment-header:
     582     # if (!slice-equal?(word-slice, "==")) goto next case
     583     # . eax = slice-equal?(word-slice, "==")
     584     # . . push args
     585     68/push  "=="/imm32
     586     52/push-edx
     587     # . . call
     588     e8/call  slice-equal?/disp32
     589     # . . discard args
     590     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     591     # . if (eax == false) goto next case
     592     3d/compare-eax-and  0/imm32/false
     593     0f 84/jump-if-=  $compute-offsets:case-label/disp32
     594     # if (*curr-segment-name == 0) goto construct-next-segment
     595     81          7/subop/compare     0/mod/indirect  6/rm32/esi    .           .             .           .           .               0/imm32           # compare *esi
     596     74/jump-if-=  $compute-offsets:construct-next-segment/disp8
     597     # sinfo/edi = get-or-insert-handle(segments, curr-segment-name, row-size=16)
     598     # . eax = get-or-insert-handle(segments, curr-segment-name, row-size=16)
     599     # . . push args
     600     68/push  0x14/imm32/row-size
     601     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     602     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     603     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     604     # . . call
     605     e8/call  get-or-insert-handle/disp32
     606     # . . discard args
     607     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     608     # . edi = eax
     609     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
     610     # sinfo->size = file-offset - sinfo->file-offset
     611     # . save ecx
     612     51/push-ecx
     613     # . ebx = *file-offset
     614     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:file-offset/disp32  # copy *file-offset to ebx
     615     # . ecx = sinfo->file-offset
     616     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
     617     # . ebx -= ecx
     618     29/subtract                     3/mod/direct    3/rm32/ebx    .           .             .           1/r32/ecx   .               .                 # subtract ecx from ebx
     619     # . sinfo->size = ebx
     620     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   8/disp8         .                 # copy ebx to *(edi+8)
     621     # . restore ecx
     622     59/pop-to-ecx
     623     # trace-sssns("segment '", curr-segment-name, "' has size ", sinfo->size, ".")
     624     # . eax = lookup(curr-segment-name)
     625     # . . push args
     626     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     627     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     628     # . . call
     629     e8/call  lookup/disp32
     630     # . . discard args
     631     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     632     # . trace-sssns("segment '", eax, "' has size ", sinfo->size, ".")
     633     # . . push args
     634     68/push  "."/imm32
     635     53/push-ebx
     636     68/push  "' has size "/imm32
     637     50/push-eax
     638     68/push  "segment '"/imm32
     639     # . . call
     640     e8/call  trace-sssns/disp32
     641     # . . discard args
     642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
     643 $compute-offsets:construct-next-segment:
     644     # next-word(line, segment-tmp)
     645     # . . push args
     646     68/push  compute-offsets:segment-tmp/imm32
     647     51/push-ecx
     648     # . . call
     649     e8/call  next-word/disp32
     650     # . . discard args
     651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     652     # if slice-empty?(segment-tmp) abort
     653     # . eax = slice-empty?(segment-tmp)
     654     # . . push args
     655     68/push  compute-offsets:segment-tmp/imm32
     656     # . . call
     657     e8/call  slice-empty?/disp32
     658     # . . discard args
     659     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     660     # . if (eax != false) abort
     661     3d/compare-eax-and  0/imm32/false
     662     0f 85/jump-if-!=  $compute-offsets:abort/disp32
     663 $compute-offsets:update-curr-segment-name:
     664     # slice-to-string(Heap, segment-tmp, curr-segment-name)
     665     # . . push args
     666     56/push-esi
     667     68/push  compute-offsets:segment-tmp/imm32
     668     68/push  Heap/imm32
     669     # . . call
     670     e8/call  slice-to-string/disp32
     671     # . . discard args
     672     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     673     # next-word(line, segment-tmp)
     674     # . . push args
     675     68/push  compute-offsets:segment-tmp/imm32
     676     51/push-ecx
     677     # . . call
     678     e8/call  next-word/disp32
     679     # . . discard args
     680     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     681     # if slice-empty?(segment-tmp) abort
     682     # . eax = slice-empty?(segment-tmp)
     683     # . . push args
     684     68/push  compute-offsets:segment-tmp/imm32
     685     # . . call
     686     e8/call  slice-empty?/disp32
     687     # . . discard args
     688     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     689     # . if (eax != false) abort
     690     3d/compare-eax-and  0/imm32/false
     691     0f 85/jump-if-!=  $compute-offsets:abort/disp32
     692     # sinfo/edi = get-or-insert-handle(segments, curr-segment-name, row-size=16)
     693     # . eax = get-or-insert-handle(segments, curr-segment-name, row-size=16)
     694     # . . push args
     695     68/push  0x14/imm32/row-size
     696     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     697     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     698     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     699     # . . call
     700     e8/call  get-or-insert-handle/disp32
     701     # . . discard args
     702     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     703     # . edi = eax
     704     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
     705     # sinfo->address = parse-hex-int-from-slice(segment-tmp)
     706     # . eax = parse-hex-int-from-slice(segment-tmp)
     707     # . . push args
     708     68/push  compute-offsets:segment-tmp/imm32
     709     # . . call
     710     e8/call  parse-hex-int-from-slice/disp32
     711     # . . discard args
     712     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     713     # . sinfo->address = eax
     714     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
     715     # sinfo->file-offset = *file-offset
     716     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:file-offset/disp32  # copy *file-offset to eax
     717     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
     718     # trace-sssns("segment '", curr-segment-name, "' is at file offset ", sinfo->file-offset, "")
     719     # . eax = lookup(curr-segment-name)
     720     # . . push args
     721     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     722     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     723     # . . call
     724     e8/call  lookup/disp32
     725     # . . discard args
     726     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     727     # . trace-sssns("segment '", eax, "' is at file offset ", file-offset, ".")
     728     # . . push args
     729     68/push  "."/imm32
     730     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:file-offset/disp32  # push *file-offset
     731     68/push  "' is at file offset "/imm32
     732     50/push-eax
     733     68/push  "segment '"/imm32
     734     # . . call
     735     e8/call  trace-sssns/disp32
     736     # . . discard args
     737     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
     738     # segment-offset = 0
     739     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     compute-offsets:segment-offset/disp32  0/imm32  # copy to *segment-offset
     740     # break
     741     e9/jump $compute-offsets:line-loop/disp32
     742 $compute-offsets:case-label:
     743     # if (!label?(word-slice)) goto next case
     744     # . eax = label?(word-slice)
     745     # . . push args
     746     52/push-edx
     747     # . . call
     748     e8/call  label?/disp32
     749     # . . discard args
     750     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     751     # . if (eax == false) goto next case
     752     3d/compare-eax-and  0/imm32/false
     753     0f 84/jump-if-=  $compute-offsets:case-default/disp32
     754     # strip trailing ':' from word-slice
     755     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
     756     # linfo/edi = get-or-insert-slice(labels, word-slice, row-size=24)
     757     # . eax = get-or-insert-slice(labels, word-slice, row-size=24)
     758     # . . push args
     759     68/push  Heap/imm32
     760     68/push  0x18/imm32/row-size
     761     52/push-edx
     762     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
     763     # . . call
     764     e8/call  get-or-insert-slice/disp32
     765     # . . discard args
     766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     767     # . edi = eax
     768     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
     769 $compute-offsets:save-label-offset:
     770     # linfo->segment-name = curr-segment-name
     771     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
     772     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
     773     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
     774     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
     775     # trace-slsss("label '" word-slice "' is in segment '" current-segment-name "'.")
     776     # . eax = lookup(curr-segment-name)
     777     # . . push args
     778     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     779     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     780     # . . call
     781     e8/call  lookup/disp32
     782     # . . discard args
     783     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     784     # . trace-slsss("label '" word-slice "' is in segment '" eax "'.")
     785     # . . push args
     786     68/push  "'."/imm32
     787     50/push-eax
     788     68/push  "' is in segment '"/imm32
     789     52/push-edx
     790     68/push  "label '"/imm32
     791     # . . call
     792     e8/call  trace-slsss/disp32
     793     # . . discard args
     794     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
     795     # linfo->segment-offset = segment-offset
     796     # . ebx = segment-offset
     797     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:segment-offset/disp32  # copy *segment-offset to ebx
     798     # . linfo->segment-offset = ebx
     799     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   8/disp8         .                 # copy ebx to *(edi+8)
     800     # trace-slsns("label '" word-slice "' is at segment offset " *segment-offset/eax ".")
     801     # . . push args
     802     68/push  "."/imm32
     803     53/push-ebx
     804     68/push  "' is at segment offset "/imm32
     805     52/push-edx
     806     68/push  "label '"/imm32
     807     # . . call
     808     e8/call  trace-slsns/disp32
     809     # . . discard args
     810     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
     811     # continue
     812     e9/jump  $compute-offsets:word-loop/disp32
     813 $compute-offsets:case-default:
     814     # width/eax = compute-width-of-slice(word-slice)
     815     # . . push args
     816     52/push-edx
     817     # . . call
     818     e8/call compute-width-of-slice/disp32
     819     # . . discard args
     820     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     821     # segment-offset += width
     822     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:segment-offset/disp32  # add eax to *segment-offset
     823     # file-offset += width
     824     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:file-offset/disp32  # add eax to *file-offset
     825 +-- 41 lines: #?     # dump segment-offset ----------------------------------------------------------------------------------------------------------------------------------------------
     866     e9/jump $compute-offsets:word-loop/disp32
     867 $compute-offsets:break-line-loop:
     868     # sinfo/edi = get-or-insert-handle(segments, curr-segment-name, row-size=16)
     869     # . eax = get-or-insert-handle(segments, curr-segment-name, row-size=16)
     870     # . . push args
     871     68/push  0x14/imm32/row-size
     872     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     873     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     874     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     875     # . . call
     876     e8/call  get-or-insert-handle/disp32
     877     # . . discard args
     878     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     879     # . edi = eax
     880     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
     881     # sinfo->size = file-offset - sinfo->file-offset
     882     # . save ecx
     883     51/push-ecx
     884     # . ebx = *file-offset
     885     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:file-offset/disp32  # copy *file-offset to ebx
     886     # . ecx = sinfo->file-offset
     887     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
     888     # . ebx -= ecx
     889     29/subtract                     3/mod/direct    3/rm32/ebx    .           .             .           1/r32/ecx   .               .                 # subtract ecx from ebx
     890     # . sinfo->size = ebx
     891     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           3/r32/ebx   8/disp8         .                 # copy ebx to *(edi+8)
     892     # . restore ecx
     893     59/pop-to-ecx
     894     # trace-sssns("segment '", curr-segment-name, "' has size ", sinfo->size, ".")
     895     # . eax = lookup(curr-segment-name)
     896     # . . push args
     897     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
     898     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
     899     # . . call
     900     e8/call  lookup/disp32
     901     # . . discard args
     902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     903     # . trace-sssns("segment '", eax, "' has size ", ebx, ".")
     904     # . . push args
     905     68/push  "."/imm32
     906     53/push-ebx
     907     68/push  "' has size "/imm32
     908     50/push-eax
     909     68/push  "segment '"/imm32
     910     # . . call
     911     e8/call  trace-sssns/disp32
     912     # . . discard args
     913     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
     914 $compute-offsets:end:
     915     # . reclaim locals
     916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x21c/imm32       # add to esp
     917     # . restore registers
     918     5f/pop-to-edi
     919     5e/pop-to-esi
     920     5b/pop-to-ebx
     921     5a/pop-to-edx
     922     59/pop-to-ecx
     923     58/pop-to-eax
     924     # . epilogue
     925     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     926     5d/pop-to-ebp
     927     c3/return
     928
     929 $compute-offsets:abort:
     930     # . _write(2/stderr, error)
     931     # . . push args
     932     68/push  "'==' must be followed by segment name and optionally an address\n"/imm32
     933     68/push  2/imm32/stderr
     934     # . . call
     935     e8/call  _write/disp32
     936     # . . discard args
     937     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     938     # . syscall(exit, 1)
     939     bb/copy-to-ebx  1/imm32
     940     e8/call  syscall_exit/disp32
     941     # never gets here
     942
     943 test-compute-offsets:
     944     # input:
     945     #   == code 0x1
     946     #   ab x/imm32  # skip comment
     947     #   == data 0x1000
     948     #   00
     949     #   x:
     950     #     34
     951     #
     952     # trace contains (in any order):
     953     #   segment 'code' is at file offset 0x0.
     954     #   segment 'code' has size 0x5.
     955     #   segment 'data' is at file offset 0x5.
     956     #   segment 'data' has size 0x2.
     957     #   label 'x' is in segment 'data'.
     958     #   label 'x' is at segment offset 0x1.
     959     #
     960     # . prologue
     961     55/push-ebp
     962     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     963     # setup
     964     # . clear-stream(_test-input-stream)
     965     # . . push args
     966     68/push  _test-input-stream/imm32
     967     # . . call
     968     e8/call  clear-stream/disp32
     969     # . . discard args
     970     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     971     # var segments/ecx: (stream byte 2*20)
     972     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x28/imm32        # subtract from esp
     973     68/push  0x28/imm32/size
     974     68/push  0/imm32/read
     975     68/push  0/imm32/write
     976     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     977     # var labels/edx: (stream byte 2*24)
     978     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x30/imm32        # subtract from esp
     979     68/push  0x30/imm32/size
     980     68/push  0/imm32/read
     981     68/push  0/imm32/write
     982     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     983     # initialize input
     984     # . write(_test-input-stream, "== code 0x1\n")
     985     # . . push args
     986     68/push  "== code 0x1\n"/imm32
     987     68/push  _test-input-stream/imm32
     988     # . . call
     989     e8/call  write/disp32
     990     # . . discard args
     991     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     992     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
     993     # . . push args
     994     68/push  "ab x/imm32  # skip comment\n"/imm32
     995     68/push  _test-input-stream/imm32
     996     # . . call
     997     e8/call  write/disp32
     998     # . . discard args
     999     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1000     # . write(_test-input-stream, "== data 0x1000\n")
    1001     # . . push args
    1002     68/push  "== data 0x1000\n"/imm32
    1003     68/push  _test-input-stream/imm32
    1004     # . . call
    1005     e8/call  write/disp32
    1006     # . . discard args
    1007     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1008     # . write(_test-input-stream, "00\n")
    1009     # . . push args
    1010     68/push  "00\n"/imm32
    1011     68/push  _test-input-stream/imm32
    1012     # . . call
    1013     e8/call  write/disp32
    1014     # . . discard args
    1015     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1016     # . write(_test-input-stream, "x:\n")
    1017     # . . push args
    1018     68/push  "x:\n"/imm32
    1019     68/push  _test-input-stream/imm32
    1020     # . . call
    1021     e8/call  write/disp32
    1022     # . . discard args
    1023     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1024     # . write(_test-input-stream, "34\n")
    1025     # . . push args
    1026     68/push  "34\n"/imm32
    1027     68/push  _test-input-stream/imm32
    1028     # . . call
    1029     e8/call  write/disp32
    1030     # . . discard args
    1031     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1032     # compute-offsets(_test-input-stream, segments, labels)
    1033     # . . push args
    1034     52/push-edx
    1035     51/push-ecx
    1036     68/push  _test-input-stream/imm32
    1037     # . . call
    1038     e8/call  compute-offsets/disp32
    1039     # . . discard args
    1040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32        # add to esp
    1041 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    1067     # check trace
    1068     # . check-trace-contains("segment 'code' is at file offset 0x00000000.", msg)
    1069     # . . push args
    1070     68/push  "F - test-compute-offsets/0"/imm32
    1071     68/push  "segment 'code' is at file offset 0x00000000."/imm32
    1072     # . . call
    1073     e8/call  check-trace-contains/disp32
    1074     # . . discard args
    1075     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1076     # . check-trace-contains("segment 'code' has size 0x00000005", msg)
    1077     # . . push args
    1078     68/push  "F - test-compute-offsets/1"/imm32
    1079     68/push  "segment 'code' has size 0x00000005."/imm32
    1080     # . . call
    1081     e8/call  check-trace-contains/disp32
    1082     # . . discard args
    1083     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1084     # . check-trace-contains("segment 'data' is at file offset 0x00000005.", msg)
    1085     # . . push args
    1086     68/push  "F - test-compute-offsets/2"/imm32
    1087     68/push  "segment 'data' is at file offset 0x00000005."/imm32
    1088     # . . call
    1089     e8/call  check-trace-contains/disp32
    1090     # . . discard args
    1091     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1092     # . check-trace-contains("segment 'data' has size 0x00000002.", msg)
    1093     # . . push args
    1094     68/push  "F - test-compute-offsets/3"/imm32
    1095     68/push  "segment 'data' has size 0x00000002."/imm32
    1096     # . . call
    1097     e8/call  check-trace-contains/disp32
    1098     # . . discard args
    1099     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1100     # . check-trace-contains("label 'x' is in segment 'data'.", msg)
    1101     # . . push args
    1102     68/push  "F - test-compute-offsets/4"/imm32
    1103     68/push  "label 'x' is in segment 'data'."/imm32
    1104     # . . call
    1105     e8/call  check-trace-contains/disp32
    1106     # . . discard args
    1107     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1108     # . check-trace-contains("label 'x' is at segment offset 0x00000001.", msg)
    1109     # . . push args
    1110     68/push  "F - test-compute-offsets/5"/imm32
    1111     68/push  "label 'x' is at segment offset 0x00000001."/imm32
    1112     # . . call
    1113     e8/call  check-trace-contains/disp32
    1114     # . . discard args
    1115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1116     # . check-ints-equal(labels->write, 0x18, msg)
    1117     # . . push args
    1118     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
    1119     68/push  0x18/imm32/1-entry
    1120     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    1121     # . . call
    1122     e8/call  check-ints-equal/disp32
    1123     # . . discard args
    1124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1125     # . epilogue
    1126     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1127     5d/pop-to-ebp
    1128     c3/return
    1129
    1130 # write segments->file-offset,
    1131 #       segments->address, and
    1132 #       labels->address
    1133 compute-addresses:  # segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
    1134     # pseudocode:
    1135     #   var srow: (addr segment-row) = segments->data
    1136     #   var max: (addr byte) = &segments->data[segments->write]
    1137     #   var num-segments: int = segments->write / 20
    1138     #   var starting-offset: int = 0x34 + (num-segments * 0x20)
    1139     #   while true
    1140     #     if (srow >= max) break
    1141     #     srow->file-offset += starting-offset
    1142     #     srow->address &= 0xfffff000  # clear last 12 bits for p_align
    1143     #     srow->address += (srow->file-offset & 0x00000fff)
    1144     #     trace-sssns("segment " srow->key " starts at address " srow->address)
    1145     #     srow += 20  # row-size
    1146     #   var lrow: (addr label-row) = labels->data
    1147     #   max = &labels->data[labels->write]
    1148     #   while true
    1149     #     if (lrow >= max) break
    1150     #     var seg-name: (addr array byte) = lookup(lrow->segment-name)
    1151     #     var label-seg: (addr segment-info) = get(segments, seg-name)
    1152     #     lrow->address = label-seg->address + lrow->segment-offset
    1153     #     trace-sssns("label " lrow->key " is at address " lrow->address)
    1154     #     lrow += 24  # row-size
    1155     #
    1156     # . prologue
    1157     55/push-ebp
    1158     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1159     # . save registers
    1160     50/push-eax
    1161     51/push-ecx
    1162     52/push-edx
    1163     53/push-ebx
    1164     56/push-esi
    1165     57/push-edi
    1166     # esi = segments
    1167     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    1168     # var num-segments/edi: int = segments->write / 20 (row-size)
    1169     # . eax = segments->write
    1170     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    1171     # . edx = 0
    1172     ba/copy-to-edx  0/imm32
    1173     # . ecx = 20 (row-size)
    1174     b9/copy-to-ecx  0x14/imm32/row-size
    1175     # . eax /= ecx (clobbering edx)
    1176     f7          7/subop/divide      3/mod/direct    1/rm32/ecx    .           .             .           .           .               .                 # divide eax by ecx
    1177     # . edi = eax
    1178     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
    1179     # var starting-offset/edi: int = 0x34 + (num-segments * 0x20)  # make room for ELF headers
    1180     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               5/imm8            # shift edi left by 5 bits
    1181     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               0x34/imm32        # add to edi
    1182     # var max/ecx: (addr byte) = &segments->data[segments->write]
    1183     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
    1184     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           1/r32/ecx   0xc/disp8       .                 # copy esi+ecx+12 to ecx
    1185     # var srow/esi: (addr segment-row) = segments->data
    1186     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           6/r32/esi   0xc/disp8       .                 # copy esi+12 to esi
    1187 $compute-addresses:segment-loop:
    1188     # if (srow >= max) break
    1189     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi with ecx
    1190     73/jump-if-addr>=  $compute-addresses:segment-break/disp8
    1191     # srow->file-offset += starting-offset
    1192     01/add                          1/mod/*+disp8   6/rm32/esi    .           .             .           7/r32/edi   0xc/disp8       .                 # add edi to *(esi+12)
    1193     # clear last 12 bits of srow->address for p_align=0x1000
    1194     # . edx = srow->address
    1195     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
    1196     # . edx &= 0xfffff000
    1197     81          4/subop/and         3/mod/direct    2/rm32/edx    .           .             .           .           .               0xfffff000/imm32  # bitwise and of edx
    1198     # update last 12 bits from srow->file-offset
    1199     # . ebx = srow->file-offset
    1200     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy *(esi+12) to ebx
    1201     # . ebx &= 0xfff
    1202     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x00000fff/imm32  # bitwise and of ebx
    1203     # . srow->address = edx | ebx
    1204     09/or                           3/mod/direct    2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # edx = bitwise OR with ebx
    1205     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy edx to *(esi+8)
    1206     # trace-sssns("segment " srow " starts at address " srow->address ".")
    1207     # . eax = lookup(*srow)
    1208     # . . push args
    1209     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
    1210     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
    1211     # . . call
    1212     e8/call  lookup/disp32
    1213     # . . discard args
    1214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1215     # . trace-sssns("segment " eax " starts at address " srow->address ".")
    1216     # . . push args
    1217     68/push  "."/imm32
    1218     52/push-edx
    1219     68/push  "' starts at address "/imm32
    1220     50/push-eax
    1221     68/push  "segment '"/imm32
    1222     # . . call
    1223     e8/call  trace-sssns/disp32
    1224     # . . discard args
    1225     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    1226     # srow += 20  # size of row
    1227     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0x14/imm32        # add to esi
    1228     eb/jump  $compute-addresses:segment-loop/disp8
    1229 $compute-addresses:segment-break:
    1230 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    1256     # esi = labels
    1257     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
    1258     # var max/ecx: (addr byte) = &labels->data[labels->write]
    1259     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
    1260     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           1/r32/ecx   0xc/disp8       .                 # copy esi+ecx+12 to ecx
    1261     # var lrow/esi: (addr label-row) = labels->data
    1262     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           6/r32/esi   0xc/disp8       .                 # copy esi+12 to esi
    1263 $compute-addresses:label-loop:
    1264     # if (lrow >= max) break
    1265     39/compare                      3/mod/direct    6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare esi with ecx
    1266     0f 83/jump-if-addr>=  $compute-addresses:end/disp32
    1267 +-- 26 lines: #?     # dump lrow->key ---------------------------------------------------------------------------------------------------------------------------------------------------
    1293     # var seg-name/edx: (addr array byte) = lookup(lrow->segment-name)
    1294     # . eax = lookup(lrow->segment-name)
    1295     # . . push args
    1296     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           0xc/disp8       .                 # push *(esi+12)
    1297     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           8/disp8         .                 # push *(esi+8)
    1298     # . . call
    1299     e8/call  lookup/disp32
    1300     # . . discard args
    1301     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1302     # . edx = eax
    1303     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
    1304 +-- 26 lines: #?     # dump seg-name ----------------------------------------------------------------------------------------------------------------------------------------------------
    1330     # var label-seg/edx: (addr segment-info) = get(segments, seg-name, row-size=20, "segment table")
    1331     # . eax = get(segments, seg-name, row-size=20)
    1332     # . . push args
    1333     68/push  "segment table"/imm32
    1334     68/push  0x14/imm32/row-size
    1335     52/push-edx
    1336     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1337     # . . call
    1338     e8/call  get/disp32
    1339     # . . discard args
    1340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    1341     # . edx = eax
    1342     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
    1343     # ebx = label-seg->address
    1344     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
    1345     # ebx += lrow->segment-offset
    1346     03/add                          1/mod/*+disp8   6/rm32/esi    .           .             .           3/r32/ebx   0x10/disp8      .                 # add *(esi+16) to ebx
    1347     # lrow->address = ebx
    1348     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           3/r32/ebx   0x14/disp8      .                 # copy ebx to *(esi+20)
    1349     # trace-sssns("label " lrow->key " is at address " lrow->address ".")
    1350     # . eax = lookup(lrow->key)
    1351     # . . push args
    1352     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
    1353     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
    1354     # . . call
    1355     e8/call  lookup/disp32
    1356     # . . discard args
    1357     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1358     # . trace-sssns("label " eax " is at address " lrow->address ".")
    1359     # . . push args
    1360     68/push  "."/imm32
    1361     53/push-ebx
    1362     68/push  "' is at address "/imm32
    1363     50/push-eax
    1364     68/push  "label '"/imm32
    1365     # . . call
    1366     e8/call  trace-sssns/disp32
    1367     # . . discard args
    1368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    1369     # lrow += 24  # size of row
    1370     81          0/subop/add         3/mod/direct    6/rm32/esi    .           .             .           .           .               0x18/imm32        # add to esi
    1371     e9/jump  $compute-addresses:label-loop/disp32
    1372 $compute-addresses:end:
    1373     # . restore registers
    1374     5f/pop-to-edi
    1375     5e/pop-to-esi
    1376     5b/pop-to-ebx
    1377     5a/pop-to-edx
    1378     59/pop-to-ecx
    1379     58/pop-to-eax
    1380     # . epilogue
    1381     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1382     5d/pop-to-ebp
    1383     c3/return
    1384
    1385 test-compute-addresses:
    1386     # input:
    1387     #   segments:
    1388     #     - 'a': {0x1000, 0, 5}
    1389     #     - 'b': {0x2018, 5, 1}
    1390     #     - 'c': {0x5444, 6, 12}
    1391     #   labels:
    1392     #     - 'l1': {'a', 3, 0}
    1393     #     - 'l2': {'b', 0, 0}
    1394     #
    1395     # trace contains in any order (comments in parens):
    1396     #   segment 'a' starts at address 0x00001094.  (0x34 + 0x20 for each segment)
    1397     #   segment 'b' starts at address 0x00002099.  (0x018 discarded)
    1398     #   segment 'c' starts at address 0x0000509a.  (0x444 discarded)
    1399     #   label 'l1' is at address 0x00001097.       (0x1094 + segment-offset 3)
    1400     #   label 'l2' is at address 0x00002099.       (0x2099 + segment-offset 0)
    1401     #
    1402     # . prologue
    1403     55/push-ebp
    1404     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1405     # setup
    1406     # . var segments/ecx: (stream byte 10*20)
    1407     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc8/imm32        # subtract from esp
    1408     68/push  0xc8/imm32/size
    1409     68/push  0/imm32/read
    1410     68/push  0/imm32/write
    1411     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1412     # . var labels/edx: (stream byte 8*24)
    1413     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    1414     68/push  0xc0/imm32/size
    1415     68/push  0/imm32/read
    1416     68/push  0/imm32/write
    1417     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1418     # . var h/ebx: (handle array byte)
    1419     68/push  0/imm32
    1420     68/push  0/imm32
    1421     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1422     # . h = copy-array(Heap, "a")
    1423     # . . push args
    1424     53/push-ebx
    1425     68/push  "a"/imm32
    1426     68/push  Heap/imm32
    1427     # . . call
    1428     e8/call  copy-array/disp32
    1429     # . . discard args
    1430     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1431     # . stream-add5(segments, "a", 0x1000, 0, 5)
    1432     # . . push args
    1433     68/push  5/imm32/segment-size
    1434     68/push  0/imm32/file-offset
    1435     68/push  0x1000/imm32/start-address
    1436     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1437     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1438     51/push-ecx
    1439     # . . call
    1440     e8/call  stream-add5/disp32
    1441     # . . discard args
    1442     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
    1443     # . h = copy-array(Heap, "b")
    1444     # . . push args
    1445     53/push-ebx
    1446     68/push  "b"/imm32
    1447     68/push  Heap/imm32
    1448     # . . call
    1449     e8/call  copy-array/disp32
    1450     # . . discard args
    1451     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1452     # . stream-add5(segments, "b", 0x2018, 5, 1)
    1453     # . . push args
    1454     68/push  1/imm32/segment-size
    1455     68/push  5/imm32/file-offset
    1456     68/push  0x2018/imm32/start-address
    1457     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1458     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1459     51/push-ecx
    1460     # . . call
    1461     e8/call  stream-add5/disp32
    1462     # . . discard args
    1463     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
    1464     # . h = copy-array(Heap, "c")
    1465     # . . push args
    1466     53/push-ebx
    1467     68/push  "c"/imm32
    1468     68/push  Heap/imm32
    1469     # . . call
    1470     e8/call  copy-array/disp32
    1471     # . . discard args
    1472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1473     # . stream-add5(segments, "c", 0x5444, 6, 12)
    1474     68/push  0xc/imm32/segment-size
    1475     68/push  6/imm32/file-offset
    1476     68/push  0x5444/imm32/start-address
    1477     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1478     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1479     51/push-ecx
    1480     # . . call
    1481     e8/call  stream-add5/disp32
    1482     # . . discard args
    1483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
    1484     # . stream-add6(labels, "l1", "a", 3, 0)
    1485     # . . push args
    1486     68/push  0/imm32/label-address
    1487     68/push  3/imm32/segment-offset
    1488     # . . push "a"
    1489     53/push-ebx
    1490     68/push  "a"/imm32
    1491     68/push  Heap/imm32
    1492     e8/call  copy-array/disp32
    1493     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1494     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1495     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1496     # . . push "l1"
    1497     53/push-ebx
    1498     68/push  "l1"/imm32
    1499     68/push  Heap/imm32
    1500     e8/call  copy-array/disp32
    1501     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1502     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1503     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1504     # . . push labels
    1505     52/push-edx
    1506     # . . call
    1507     e8/call  stream-add6/disp32
    1508     # . . discard args
    1509     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x1c/imm32        # add to esp
    1510     # . stream-add6(labels, "l2", "b", 0, 0)
    1511     # . . push args
    1512     68/push  0/imm32/label-address
    1513     68/push  0/imm32/segment-offset
    1514     # . . push "b"
    1515     53/push-ebx
    1516     68/push  "b"/imm32
    1517     68/push  Heap/imm32
    1518     e8/call  copy-array/disp32
    1519     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1520     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1521     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1522     # . . push "l2"
    1523     53/push-ebx
    1524     68/push  "l2"/imm32
    1525     68/push  Heap/imm32
    1526     e8/call  copy-array/disp32
    1527     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1528     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1529     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1530     # . . push labels
    1531     52/push-edx
    1532     # . . call
    1533     e8/call  stream-add6/disp32
    1534     # . . discard args
    1535     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x1c/imm32        # add to esp
    1536     # component under test
    1537     # . compute-addresses(segments, labels)
    1538     # . . push args
    1539     52/push-edx
    1540     51/push-ecx
    1541     # . . call
    1542     e8/call  compute-addresses/disp32
    1543     # . . discard args
    1544     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1545     # checks
    1546 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    1572     # . check-trace-contains("segment 'a' starts at address 0x00001094.", msg)
    1573     # . . push args
    1574     68/push  "F - test-compute-addresses/0"/imm32
    1575     68/push  "segment 'a' starts at address 0x00001094."/imm32
    1576     # . . call
    1577     e8/call  check-trace-contains/disp32
    1578     # . . discard args
    1579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1580     # . check-trace-contains("segment 'b' starts at address 0x00002099.", msg)
    1581     # . . push args
    1582     68/push  "F - test-compute-addresses/1"/imm32
    1583     68/push  "segment 'b' starts at address 0x00002099."/imm32
    1584     # . . call
    1585     e8/call  check-trace-contains/disp32
    1586     # . . discard args
    1587     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1588     # . check-trace-contains("segment 'c' starts at address 0x0000509a.", msg)
    1589     # . . push args
    1590     68/push  "F - test-compute-addresses/2"/imm32
    1591     68/push  "segment 'c' starts at address 0x0000509a."/imm32
    1592     # . . call
    1593     e8/call  check-trace-contains/disp32
    1594     # . . discard args
    1595     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1596     # . check-trace-contains("label 'l1' is at address 0x00001097.", msg)
    1597     # . . push args
    1598     68/push  "F - test-compute-addresses/3"/imm32
    1599     68/push  "label 'l1' is at address 0x00001097."/imm32
    1600     # . . call
    1601     e8/call  check-trace-contains/disp32
    1602     # . . discard args
    1603     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1604     # . check-trace-contains("label 'l2' is at address 0x00002099.", msg)
    1605     # . . push args
    1606     68/push  "F - test-compute-addresses/4"/imm32
    1607     68/push  "label 'l2' is at address 0x00002099."/imm32
    1608     # . . call
    1609     e8/call  check-trace-contains/disp32
    1610     # . . discard args
    1611     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1612     # . check-ints-equal(labels->write, 0x30, msg)
    1613     # . . push args
    1614     68/push  "F - test-compute-addresses/maintains-labels-write-index"/imm32
    1615     68/push  0x30/imm32/2-entries
    1616     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    1617     # . . call
    1618     e8/call  check-ints-equal/disp32
    1619     # . . discard args
    1620     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1621     # . epilogue
    1622     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1623     5d/pop-to-ebp
    1624     c3/return
    1625
    1626 test-compute-addresses-large-segments:
    1627     # input:
    1628     #   segments:
    1629     #     - 'a': {0x1000, 0, 0x5604}
    1630     #     - 'b': {0x2018, 0x5604, 1}
    1631     #   labels:
    1632     #     - 'l1': {'a', 3, 0}
    1633     #
    1634     # trace contains in any order (comments in parens):
    1635     #   segment 'a' starts at address 0x00001074.  (0x34 + 0x20 for each segment)
    1636     #   segment 'b' starts at address 0x00002678.  (0x018 discarded; last 3 nibbles from 0x1074 + 0x5604)
    1637     #   label 'l1' is at address 0x00001077.       (0x1074 + segment-offset 3)
    1638     #
    1639     # . prologue
    1640     55/push-ebp
    1641     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1642     # setup
    1643     # . var segments/ecx: (stream byte 10*20)
    1644     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc8/imm32        # subtract from esp
    1645     68/push  0xc8/imm32/size
    1646     68/push  0/imm32/read
    1647     68/push  0/imm32/write
    1648     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1649     # . var labels/edx: (stream byte 8*24)
    1650     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    1651     68/push  0xc0/imm32/size
    1652     68/push  0/imm32/read
    1653     68/push  0/imm32/write
    1654     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1655     # . var h/ebx: (handle array byte)
    1656     68/push  0/imm32
    1657     68/push  0/imm32
    1658     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1659     # . h = copy-array(Heap, "a")
    1660     # . . push args
    1661     53/push-ebx
    1662     68/push  "a"/imm32
    1663     68/push  Heap/imm32
    1664     # . . call
    1665     e8/call  copy-array/disp32
    1666     # . . discard args
    1667     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1668     # . stream-add5(segments, "a", 0x1000, 0, 0x5604)
    1669     68/push  0x5604/imm32/segment-size
    1670     68/push  0/imm32/file-offset
    1671     68/push  0x1000/imm32/start-address
    1672     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1673     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1674     51/push-ecx
    1675     # . . call
    1676     e8/call  stream-add5/disp32
    1677     # . . discard args
    1678     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
    1679     # . h = copy-array(Heap, "b")
    1680     # . . push args
    1681     53/push-ebx
    1682     68/push  "b"/imm32
    1683     68/push  Heap/imm32
    1684     # . . call
    1685     e8/call  copy-array/disp32
    1686     # . . discard args
    1687     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1688     # . stream-add5(segments, "b", 0x2018, 0x5604, 1)
    1689     68/push  1/imm32/segment-size
    1690     68/push  0x5604/imm32/file-offset
    1691     68/push  0x2018/imm32/start-address
    1692     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1693     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1694     51/push-ecx
    1695     # . . call
    1696     e8/call  stream-add5/disp32
    1697     # . . discard args
    1698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
    1699     # . stream-add6(labels, "l1", "a", 3, 0)
    1700     68/push  0/imm32/label-address
    1701     68/push  3/imm32/segment-offset
    1702     # . . push "a"
    1703     53/push-ebx
    1704     68/push  "a"/imm32
    1705     68/push  Heap/imm32
    1706     e8/call  copy-array/disp32
    1707     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1708     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1709     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1710     # . . push "l1"
    1711     53/push-ebx
    1712     68/push  "l1"/imm32
    1713     68/push  Heap/imm32
    1714     e8/call  copy-array/disp32
    1715     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1716     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    1717     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    1718     # . . push labels
    1719     52/push-edx
    1720     # . . call
    1721     e8/call  stream-add6/disp32
    1722     # . . discard args
    1723     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x1c/imm32        # add to esp
    1724     # component under test
    1725     # . compute-addresses(segments, labels)
    1726     # . . push args
    1727     52/push-edx
    1728     51/push-ecx
    1729     # . . call
    1730     e8/call  compute-addresses/disp32
    1731     # . . discard args
    1732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1733     # checks
    1734 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    1760     # . check-trace-contains("segment 'a' starts at address 0x00001074.", msg)
    1761     # . . push args
    1762     68/push  "F - test-compute-addresses-large-segments/0"/imm32
    1763     68/push  "segment 'a' starts at address 0x00001074."/imm32
    1764     # . . call
    1765     e8/call  check-trace-contains/disp32
    1766     # . . discard args
    1767     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1768     # . check-trace-contains("segment 'b' starts at address 0x00002678.", msg)
    1769     # . . push args
    1770     68/push  "F - test-compute-addresses-large-segments/1"/imm32
    1771     68/push  "segment 'b' starts at address 0x00002678."/imm32
    1772     # . . call
    1773     e8/call  check-trace-contains/disp32
    1774     # . . discard args
    1775     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1776     # . check-trace-contains("label 'l1' is at address 0x00001077.", msg)
    1777     # . . push args
    1778     68/push  "F - test-compute-addresses-large-segments/3"/imm32
    1779     68/push  "label 'l1' is at address 0x00001077."/imm32
    1780     # . . call
    1781     e8/call  check-trace-contains/disp32
    1782     # . . discard args
    1783     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1784     # . epilogue
    1785     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1786     5d/pop-to-ebp
    1787     c3/return
    1788
    1789 emit-output:  # in: (addr stream byte), out: (addr buffered-file), segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
    1790     # pseudocode:
    1791     #   emit-headers(out, segments, labels)
    1792     #   emit-segments(in, out, labels)
    1793     #
    1794     # . prologue
    1795     55/push-ebp
    1796     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1797 +--  9 lines: #?     # write(2/stderr, "emit-headers\n") --------------------------------------------------------------------------------------------------------------------------------
    1806     # emit-headers(out, segments, labels)
    1807     # . . push args
    1808     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8       .                # push *(ebp+20)
    1809     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8       .                # push *(ebp+16)
    1810     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8        .                # push *(ebp+12)
    1811     # . . call
    1812     e8/call  emit-headers/disp32
    1813     # . . discard args
    1814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1815 +--  9 lines: #?     # write(2/stderr, "emit-segments\n") -------------------------------------------------------------------------------------------------------------------------------
    1824     # emit-segments(in, out, labels)
    1825     # . . push args
    1826     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    1827     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1828     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1829     # . . call
    1830     e8/call  emit-segments/disp32
    1831     # . . discard args
    1832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    1833 $emit-output:end:
    1834     # . epilogue
    1835     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1836     5d/pop-to-ebp
    1837     c3/return
    1838
    1839 # global scratch space for emit-segments
    1840 == data
    1841
    1842 emit-segments:datum:  # slice
    1843   0/imm32/start
    1844   0/imm32/end
    1845
    1846 == code
    1847
    1848 emit-segments:  # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), label-info})
    1849     # pseudocode:
    1850     #   var offset-of-next-instruction = 0
    1851     #   var line: (stream byte 512)
    1852     #   line-loop:
    1853     #   while true
    1854     #     clear-stream(line)
    1855     #     read-line(in, line)
    1856     #     if (line->write == 0) break               # end of file
    1857     #     offset-of-next-instruction += num-bytes(line)
    1858     #     var far-jump-or-call? = far-jump-or-call?(line)
    1859     #     rewind-stream(line)
    1860     #     while true
    1861     #       var word-slice = next-word(line)
    1862     #       if slice-empty?(word-slice)             # end of line
    1863     #         break
    1864     #       if slice-starts-with?(word-slice, "#")  # comment
    1865     #         break
    1866     #       if label?(word-slice)                # no need for label declarations anymore
    1867     #         goto line-loop                        # don't insert empty lines
    1868     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
    1869     #         goto line-loop                        # don't insert empty lines
    1870     #       if length(word-slice) == 2
    1871     #         write-slice-buffered(out, word-slice)
    1872     #         write-buffered(out, " ")
    1873     #         continue
    1874     #       var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/")
    1875     #       var info: (addr label-info) = get-slice(labels, datum)
    1876     #       if has-metadata?(word-slice, "imm8")
    1877     #         abort
    1878     #       else if has-metadata?(word-slice, "imm32")
    1879     #         emit(out, info->address, 4)
    1880     #       else if has-metadata?(word-slice, "disp8")
    1881     #         value = info->offset - offset-of-next-instruction
    1882     #         emit(out, value, 1)
    1883     #       else if has-metadata?(word-slice, "disp32")
    1884     #         if far-jump-or-call?
    1885     #           value = info->offset - offset-of-next-instruction
    1886     #         else
    1887     #           value = info->address
    1888     #         emit(out, value, 4)
    1889     #       else
    1890     #         abort
    1891     #     write-buffered(out, "\n")
    1892     #
    1893     # registers:
    1894     #   line: ecx
    1895     #   word-slice: edx
    1896     #   offset-of-next-instruction: ebx
    1897     #   far-jump-or-call?: edi
    1898     #   info: esi (inner loop only)
    1899     #   temporaries: eax, esi (outer loop)
    1900     #
    1901     # . prologue
    1902     55/push-ebp
    1903     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1904     # . save registers
    1905     50/push-eax
    1906     51/push-ecx
    1907     52/push-edx
    1908     53/push-ebx
    1909     56/push-esi
    1910     57/push-edi
    1911     # var line/ecx: (stream byte 512)
    1912     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
    1913     68/push  0x200/imm32/size
    1914     68/push  0/imm32/read
    1915     68/push  0/imm32/write
    1916     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    1917     # var word-slice/edx: slice
    1918     68/push  0/imm32/end
    1919     68/push  0/imm32/start
    1920     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    1921     # offset-of-next-instruction/ebx = 0
    1922     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    1923 $emit-segments:line-loop:
    1924     # clear-stream(line)
    1925     # . . push args
    1926     51/push-ecx
    1927     # . . call
    1928     e8/call  clear-stream/disp32
    1929     # . . discard args
    1930     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1931     # read-line(in, line)
    1932     # . . push args
    1933     51/push-ecx
    1934     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1935     # . . call
    1936     e8/call  read-line/disp32
    1937     # . . discard args
    1938     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1939 +-- 33 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
    1972 $emit-segments:check-for-end-of-input:
    1973     # if (line->write == 0) break
    1974     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
    1975     0f 84/jump-if-=  $emit-segments:end/disp32
    1976     # offset-of-next-instruction += num-bytes(line)
    1977     # . eax = num-bytes(line)
    1978     # . . push args
    1979     51/push-ecx
    1980     # . . call
    1981     e8/call  num-bytes/disp32
    1982     # . . discard args
    1983     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1984     # . ebx += eax
    1985     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
    1986     # var far-jump-or-call?/edi: boolean = far-jump-or-call?(line)
    1987     # . . push args
    1988     51/push-ecx
    1989     # . . call
    1990     e8/call  far-jump-or-call?/disp32
    1991     # . . discard args
    1992     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1993     # rewind-stream(line)
    1994     # . . push args
    1995     51/push-ecx
    1996     # . . call
    1997     e8/call  rewind-stream/disp32
    1998     # . . discard args
    1999     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2000 $emit-segments:word-loop:
    2001     # next-word(line, word-slice)
    2002     # . . push args
    2003     52/push-edx
    2004     51/push-ecx
    2005     # . . call
    2006     e8/call  next-word/disp32
    2007     # . . discard args
    2008     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2009 +-- 33 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
    2042 $emit-segments:check-for-end-of-line:
    2043     # if (slice-empty?(word-slice)) break
    2044     # . eax = slice-empty?(word-slice)
    2045     # . . push args
    2046     52/push-edx
    2047     # . . call
    2048     e8/call  slice-empty?/disp32
    2049     # . . discard args
    2050     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2051     # . if (eax != 0) break
    2052     3d/compare-eax-and  0/imm32/false
    2053     0f 85/jump-if-!=  $emit-segments:next-line/disp32
    2054 $emit-segments:check-for-comment:
    2055     # if (slice-starts-with?(word-slice, "#")) break
    2056     # . start/esi = word-slice->start
    2057     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
    2058     # . c/eax = *start
    2059     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    2060     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
    2061     # . if (eax == '#') break
    2062     3d/compare-eax-and  0x23/imm32/hash
    2063     0f 84/jump-if-=  $emit-segments:next-line/disp32
    2064 $emit-segments:check-for-label:
    2065     # if label?(word-slice) break
    2066     # . eax = label?(word-slice)
    2067     # . . push args
    2068     52/push-edx
    2069     # . . call
    2070     e8/call  label?/disp32
    2071     # . . discard args
    2072     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2073     # . if (eax != false) break
    2074     3d/compare-eax-and  0/imm32/false
    2075     0f 85/jump-if-!=  $emit-segments:line-loop/disp32
    2076 $emit-segments:check-for-segment-header:
    2077     # if (slice-equal?(word-slice, "==")) break
    2078     # . eax = slice-equal?(word-slice, "==")
    2079     # . . push args
    2080     68/push  "=="/imm32
    2081     52/push-edx
    2082     # . . call
    2083     e8/call  slice-equal?/disp32
    2084     # . . discard args
    2085     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2086     # . if (eax != false) break
    2087     3d/compare-eax-and  0/imm32/false
    2088     0f 85/jump-if-!=  $emit-segments:line-loop/disp32
    2089 $emit-segments:2-character:
    2090     # if (size(word-slice) != 2) goto next check
    2091     # . eax = size(word-slice)
    2092     8b/copy                         1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(edx+4) to eax
    2093     2b/subtract                     0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # subtract *edx from eax
    2094     # . if (eax != 2) goto next check
    2095     3d/compare-eax-and  2/imm32
    2096     75/jump-if-!=  $emit-segments:check-metadata/disp8
    2097     # write-slice-buffered(out, word-slice)
    2098     # . . push args
    2099     52/push-edx
    2100     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2101     # . . call
    2102     e8/call  write-slice-buffered/disp32
    2103     # . . discard args
    2104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2105     # write-buffered(out, " ")
    2106     # . . push args
    2107     68/push  Space/imm32
    2108     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2109     # . . call
    2110     e8/call  write-buffered/disp32
    2111     # . . discard args
    2112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2113     # continue
    2114     e9/jump  $emit-segments:word-loop/disp32
    2115 $emit-segments:check-metadata:
    2116     # - if we get here, 'word-slice' must be a label to be looked up
    2117     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
    2118     # . . push args
    2119     68/push  emit-segments:datum/imm32
    2120     68/push  0x2f/imm32/slash
    2121     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    2122     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    2123     # . . call
    2124     e8/call  next-token-from-slice/disp32
    2125     # . . discard args
    2126     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2127 +-- 33 lines: #?     # dump datum -------------------------------------------------------------------------------------------------------------------------------------------------------
    2160     # info/esi = get-slice(labels, datum, row-size=24, "label table")
    2161     # . eax = get-slice(labels, datum, row-size=24, "label table")
    2162     # . . push args
    2163     68/push  "label table"/imm32
    2164     68/push  0x18/imm32/row-size
    2165     68/push  emit-segments:datum/imm32
    2166     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    2167     # . . call
    2168     e8/call  get-slice/disp32
    2169     # . . discard args
    2170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    2171     # . esi = eax
    2172     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
    2173 $emit-segments:check-imm8:
    2174     # if (has-metadata?(word-slice, "imm8")) abort
    2175     # . eax = has-metadata?(edx, "imm8")
    2176     # . . push args
    2177     68/push  "imm8"/imm32
    2178     52/push-edx
    2179     # . . call
    2180     e8/call  has-metadata?/disp32
    2181     # . . discard args
    2182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2183     # . if (eax != false) abort
    2184     3d/compare-eax-and  0/imm32/false
    2185     0f 85/jump-if-!=  $emit-segments:imm8-abort/disp32
    2186 $emit-segments:check-imm32:
    2187     # if (!has-metadata?(word-slice, "imm32")) goto next check
    2188     # . eax = has-metadata?(edx, "imm32")
    2189     # . . push args
    2190     68/push  "imm32"/imm32
    2191     52/push-edx
    2192     # . . call
    2193     e8/call  has-metadata?/disp32
    2194     # . . discard args
    2195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2196     # . if (eax == false) goto next check
    2197     3d/compare-eax-and  0/imm32/false
    2198     74/jump-if-=  $emit-segments:check-disp8/disp8
    2199 +-- 33 lines: #?     # dump info->address -----------------------------------------------------------------------------------------------------------------------------------------------
    2232 $emit-segments:emit-imm32:
    2233     # emit-hex(out, info->address, 4)
    2234     # . . push args
    2235     68/push  4/imm32
    2236     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           0xc/disp8       .                 # push *(esi+12)
    2237     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2238     # . . call
    2239     e8/call  emit-hex/disp32
    2240     # . . discard args
    2241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2242     # continue
    2243     e9/jump  $emit-segments:word-loop/disp32
    2244 $emit-segments:check-disp8:
    2245     # if (!has-metadata?(word-slice, "disp8")) goto next check
    2246     # . eax = has-metadata?(edx, "disp8")
    2247     # . . push args
    2248     68/push  "disp8"/imm32
    2249     52/push-edx
    2250     # . . call
    2251     e8/call  has-metadata?/disp32
    2252     # . . discard args
    2253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2254     # . if (eax == false) goto next check
    2255     3d/compare-eax-and  0/imm32/false
    2256     74/jump-if-=  $emit-segments:check-disp32/disp8
    2257 $emit-segments:emit-disp8:
    2258     # emit-hex(out, info->offset - offset-of-next-instruction, 1)
    2259     # . . push args
    2260     68/push  1/imm32
    2261     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   8/disp8         .                 # copy *(esi+8) to eax
    2262     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
    2263     50/push-eax
    2264     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2265     # . . call
    2266     e8/call  emit-hex/disp32
    2267     # . . discard args
    2268     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2269     # continue
    2270     e9/jump  $emit-segments:word-loop/disp32
    2271 $emit-segments:check-disp32:
    2272     # if (!has-metadata?(word-slice, "disp32")) abort
    2273     # . eax = has-metadata?(edx, "disp32")
    2274     # . . push args
    2275     68/push  "disp32"/imm32
    2276     52/push-edx
    2277     # . . call
    2278     e8/call  has-metadata?/disp32
    2279     # . . discard args
    2280     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2281     # . if (eax == false) abort
    2282     3d/compare-eax-and  0/imm32/false
    2283     0f 84/jump-if-=  $emit-segments:abort/disp32
    2284 $emit-segments:emit-disp32:
    2285     # var value/eax = info->address
    2286     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(esi+12) to eax
    2287     # if (far-jump-or-call?) value = info->offset - offset-of-next-instruction
    2288     81          7/subop/compare     3/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32/false     # compare edi
    2289     74/jump-if-=  $emit-segments:really-emit-disp32/disp8
    2290     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   8/disp8         .                 # copy *(esi+8) to eax
    2291     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
    2292 $emit-segments:really-emit-disp32:
    2293     # emit-hex(out, value, 4)
    2294     # . . push args
    2295     68/push  4/imm32
    2296     50/push-eax
    2297     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2298     # . . call
    2299     e8/call  emit-hex/disp32
    2300     # . . discard args
    2301     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2302     # continue
    2303     e9/jump  $emit-segments:word-loop/disp32
    2304 $emit-segments:next-line:
    2305     # write-buffered(out, "\n")
    2306     # . . push args
    2307     68/push  Newline/imm32
    2308     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    2309     # . . call
    2310     e8/call  write-buffered/disp32
    2311     # . . discard args
    2312     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2313     # loop
    2314     e9/jump  $emit-segments:line-loop/disp32
    2315 $emit-segments:end:
    2316     # . reclaim locals
    2317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
    2318     # . restore registers
    2319     5f/pop-to-edi
    2320     5e/pop-to-esi
    2321     5b/pop-to-ebx
    2322     5a/pop-to-edx
    2323     59/pop-to-ecx
    2324     58/pop-to-eax
    2325     # . epilogue
    2326     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2327     5d/pop-to-ebp
    2328     c3/return
    2329
    2330 $emit-segments:imm8-abort:
    2331     # . _write(2/stderr, error)
    2332     # . . push args
    2333     68/push  "emit-segments: cannot refer to code labels with /imm8"/imm32
    2334     68/push  2/imm32/stderr
    2335     # . . call
    2336     e8/call  _write/disp32
    2337     # . . discard args
    2338     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2339     # . syscall(exit, 1)
    2340     bb/copy-to-ebx  1/imm32
    2341     e8/call  syscall_exit/disp32
    2342     # never gets here
    2343
    2344 $emit-segments:abort:
    2345     # print(stderr, "missing metadata in " word-slice)
    2346     # . _write(2/stderr, "missing metadata in word ")
    2347     # . . push args
    2348     68/push  "emit-segments: missing metadata in "/imm32
    2349     68/push  2/imm32/stderr
    2350     # . . call
    2351     e8/call  _write/disp32
    2352     # . . discard args
    2353     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2354     # . write-slice-buffered(Stderr, word-slice)
    2355     # . . push args
    2356     52/push-edx
    2357     68/push  Stderr/imm32
    2358     # . . call
    2359     e8/call  write-slice-buffered/disp32
    2360     # . . discard args
    2361     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2362     # . flush(Stderr)
    2363     # . . push args
    2364     68/push  Stderr/imm32
    2365     # . . call
    2366     e8/call  flush/disp32
    2367     # . . discard args
    2368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2369     # . syscall(exit, 1)
    2370     bb/copy-to-ebx  1/imm32
    2371     e8/call  syscall_exit/disp32
    2372     # never gets here
    2373
    2374 test-emit-segments-non-far-control-flow:
    2375     # labels turn into absolute addresses if opcodes are not far jumps or calls
    2376     #
    2377     # input:
    2378     #   in:
    2379     #     == code 0x1000
    2380     #     ab cd ef gh
    2381     #     ij x/disp32
    2382     #     == data 0x2000
    2383     #     00
    2384     #     x:
    2385     #       34
    2386     #   labels:
    2387     #     - 'x': {'data', 1, 0x207a}
    2388     #
    2389     # output:
    2390     #   ab cd ef gh
    2391     #   ij 7a 20 00 00
    2392     #   00
    2393     #   34
    2394     #
    2395     # . prologue
    2396     55/push-ebp
    2397     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2398     # setup
    2399     # . clear-stream(_test-input-stream)
    2400     # . . push args
    2401     68/push  _test-input-stream/imm32
    2402     # . . call
    2403     e8/call  clear-stream/disp32
    2404     # . . discard args
    2405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2406     # . clear-stream(_test-output-stream)
    2407     # . . push args
    2408     68/push  _test-output-stream/imm32
    2409     # . . call
    2410     e8/call  clear-stream/disp32
    2411     # . . discard args
    2412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2413     # . clear-stream($_test-output-buffered-file->buffer)
    2414     # . . push args
    2415     68/push  $_test-output-buffered-file->buffer/imm32
    2416     # . . call
    2417     e8/call  clear-stream/disp32
    2418     # . . discard args
    2419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2420     # . var labels/edx: (stream byte 8*24)
    2421     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    2422     68/push  0xc0/imm32/size
    2423     68/push  0/imm32/read
    2424     68/push  0/imm32/write
    2425     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2426     # . var h/ebx: (handle array byte)
    2427     68/push  0/imm32
    2428     68/push  0/imm32
    2429     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    2430     # initialize input
    2431     # . write(_test-input-stream, "== code 0x1000\n")
    2432     # . . push args
    2433     68/push  "== code 0x1000\n"/imm32
    2434     68/push  _test-input-stream/imm32
    2435     # . . call
    2436     e8/call  write/disp32
    2437     # . . discard args
    2438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2439     # . write(_test-input-stream, "ab cd ef gh\n")
    2440     # . . push args
    2441     68/push  "ab cd ef gh\n"/imm32
    2442     68/push  _test-input-stream/imm32
    2443     # . . call
    2444     e8/call  write/disp32
    2445     # . . discard args
    2446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2447     # . write(_test-input-stream, "ij x/disp32\n")
    2448     # . . push args
    2449     68/push  "ij x/disp32\n"/imm32
    2450     68/push  _test-input-stream/imm32
    2451     # . . call
    2452     e8/call  write/disp32
    2453     # . . discard args
    2454     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2455     # . write(_test-input-stream, "== data 0x2000\n")
    2456     # . . push args
    2457     68/push  "== data 0x2000\n"/imm32
    2458     68/push  _test-input-stream/imm32
    2459     # . . call
    2460     e8/call  write/disp32
    2461     # . . discard args
    2462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2463     # . write(_test-input-stream, "00\n")
    2464     # . . push args
    2465     68/push  "00\n"/imm32
    2466     68/push  _test-input-stream/imm32
    2467     # . . call
    2468     e8/call  write/disp32
    2469     # . . discard args
    2470     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2471     # . write(_test-input-stream, "x:\n")
    2472     # . . push args
    2473     68/push  "x:\n"/imm32
    2474     68/push  _test-input-stream/imm32
    2475     # . . call
    2476     e8/call  write/disp32
    2477     # . . discard args
    2478     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2479     # . write(_test-input-stream, "34\n")
    2480     # . . push args
    2481     68/push  "34\n"/imm32
    2482     68/push  _test-input-stream/imm32
    2483     # . . call
    2484     e8/call  write/disp32
    2485     # . . discard args
    2486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2487     # . stream-add6(labels, "x", "data", 1, 0x207a)
    2488     68/push  0x207a/imm32/label-address
    2489     68/push  1/imm32/segment-offset
    2490     # . . push "data"
    2491     53/push-ebx
    2492     68/push  "data"/imm32
    2493     68/push  Heap/imm32
    2494     e8/call  copy-array/disp32
    2495     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2496     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    2497     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    2498     # . . push "l1"
    2499     53/push-ebx
    2500     68/push  "x"/imm32
    2501     68/push  Heap/imm32
    2502     e8/call  copy-array/disp32
    2503     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2504     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    2505     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    2506     # . . push labels
    2507     52/push-edx
    2508     # . . call
    2509     e8/call  stream-add6/disp32
    2510     # . . discard args
    2511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x1c/imm32        # add to esp
    2512     # component under test
    2513     # . emit-segments(_test-input-stream, _test-output-buffered-file, labels)
    2514     # . . push args
    2515     52/push-edx
    2516     68/push  _test-output-buffered-file/imm32
    2517     68/push  _test-input-stream/imm32
    2518     # . . call
    2519     e8/call  emit-segments/disp32
    2520     # . . discard args
    2521     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2522     # checks
    2523     # . flush(_test-output-buffered-file)
    2524     # . . push args
    2525     68/push  _test-output-buffered-file/imm32
    2526     # . . call
    2527     e8/call  flush/disp32
    2528     # . . discard args
    2529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2530 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    2563     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
    2564     # . . push args
    2565     68/push  "F - test-emit-segments-global-variable/0"/imm32
    2566     68/push  "ab cd ef gh "/imm32
    2567     68/push  _test-output-stream/imm32
    2568     # . . call
    2569     e8/call  check-next-stream-line-equal/disp32
    2570     # . . discard args
    2571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2572     # . check-next-stream-line-equal(_test-output-stream, "ij 7a 20 00 00 ", msg)
    2573     # . . push args
    2574     68/push  "F - test-emit-segments-global-variable/1"/imm32
    2575     68/push  "ij 7a 20 00 00 "/imm32
    2576     68/push  _test-output-stream/imm32
    2577     # . . call
    2578     e8/call  check-next-stream-line-equal/disp32
    2579     # . . discard args
    2580     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2581     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
    2582     # . . push args
    2583     68/push  "F - test-emit-segments-global-variable/2"/imm32
    2584     68/push  "00 "/imm32
    2585     68/push  _test-output-stream/imm32
    2586     # . . call
    2587     e8/call  check-next-stream-line-equal/disp32
    2588     # . . discard args
    2589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2590     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
    2591     # . . push args
    2592     68/push  "F - test-emit-segments-global-variable/3"/imm32
    2593     68/push  "34 "/imm32
    2594     68/push  _test-output-stream/imm32
    2595     # . . call
    2596     e8/call  check-next-stream-line-equal/disp32
    2597     # . . discard args
    2598     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2599     # . epilogue
    2600     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2601     5d/pop-to-ebp
    2602     c3/return
    2603
    2604 test-emit-segments-code-label:
    2605     # labels turn into PC-relative addresses if opcodes are far jumps or calls
    2606     #
    2607     # input:
    2608     #   in:
    2609     #     == code 0x1000
    2610     #     ab cd
    2611     #     l1:
    2612     #       ef gh
    2613     #       e8 l1/disp32
    2614     #   labels:
    2615     #     - 'l1': {'code', 2, 0x1056}
    2616     #
    2617     # output:
    2618     #   ab cd
    2619     #   ef gh
    2620     #   e8 f9 ff ff ff  # -7
    2621     #
    2622     # . prologue
    2623     55/push-ebp
    2624     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2625     # setup
    2626     # . clear-stream(_test-input-stream)
    2627     # . . push args
    2628     68/push  _test-input-stream/imm32
    2629     # . . call
    2630     e8/call  clear-stream/disp32
    2631     # . . discard args
    2632     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2633     # . clear-stream(_test-output-stream)
    2634     # . . push args
    2635     68/push  _test-output-stream/imm32
    2636     # . . call
    2637     e8/call  clear-stream/disp32
    2638     # . . discard args
    2639     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2640     # . clear-stream($_test-output-buffered-file->buffer)
    2641     # . . push args
    2642     68/push  $_test-output-buffered-file->buffer/imm32
    2643     # . . call
    2644     e8/call  clear-stream/disp32
    2645     # . . discard args
    2646     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2647     # . var labels/edx: (stream byte 8*24)
    2648     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    2649     68/push  0xc0/imm32/size
    2650     68/push  0/imm32/read
    2651     68/push  0/imm32/write
    2652     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2653     # . var h/ebx: (handle array byte)
    2654     68/push  0/imm32
    2655     68/push  0/imm32
    2656     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    2657     # initialize input
    2658     # . write(_test-input-stream, "== code 0x1000\n")
    2659     # . . push args
    2660     68/push  "== code 0x1000\n"/imm32
    2661     68/push  _test-input-stream/imm32
    2662     # . . call
    2663     e8/call  write/disp32
    2664     # . . discard args
    2665     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2666     # . write(_test-input-stream, "ab cd\n")
    2667     # . . push args
    2668     68/push  "ab cd\n"/imm32
    2669     68/push  _test-input-stream/imm32
    2670     # . . call
    2671     e8/call  write/disp32
    2672     # . . discard args
    2673     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2674     # . write(_test-input-stream, "l1:\n")
    2675     # . . push args
    2676     68/push  "l1:\n"/imm32
    2677     68/push  _test-input-stream/imm32
    2678     # . . call
    2679     e8/call  write/disp32
    2680     # . . discard args
    2681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2682     # . write(_test-input-stream, "  ef gh\n")
    2683     # . . push args
    2684     68/push  "  ef gh\n"/imm32
    2685     68/push  _test-input-stream/imm32
    2686     # . . call
    2687     e8/call  write/disp32
    2688     # . . discard args
    2689     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2690     # . write(_test-input-stream, "  e8 l1/disp32\n")
    2691     # . . push args
    2692     68/push  "  e8 l1/disp32\n"/imm32
    2693     68/push  _test-input-stream/imm32
    2694     # . . call
    2695     e8/call  write/disp32
    2696     # . . discard args
    2697     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2698     # . stream-add6(labels, "l1", "code", 2, 0x1056)
    2699     68/push  0x1056/imm32/label-address
    2700     68/push  2/imm32/segment-offset
    2701     # . . push "data"
    2702     53/push-ebx
    2703     68/push  "code"/imm32
    2704     68/push  Heap/imm32
    2705     e8/call  copy-array/disp32
    2706     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2707     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    2708     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    2709     # . . push "l1"
    2710     53/push-ebx
    2711     68/push  "l1"/imm32
    2712     68/push  Heap/imm32
    2713     e8/call  copy-array/disp32
    2714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2715     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    2716     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    2717     # . . push labels
    2718     52/push-edx
    2719     # . . call
    2720     e8/call  stream-add6/disp32
    2721     # . . discard args
    2722     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x1c/imm32        # add to esp
    2723     # component under test
    2724     # . emit-segments(_test-input-stream, _test-output-buffered-file, labels)
    2725     # . . push args
    2726     52/push-edx
    2727     68/push  _test-output-buffered-file/imm32
    2728     68/push  _test-input-stream/imm32
    2729     # . . call
    2730     e8/call  emit-segments/disp32
    2731     # . . discard args
    2732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2733     # checks
    2734     # . flush(_test-output-buffered-file)
    2735     # . . push args
    2736     68/push  _test-output-buffered-file/imm32
    2737     # . . call
    2738     e8/call  flush/disp32
    2739     # . . discard args
    2740     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2741 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    2774     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
    2775     # . . push args
    2776     68/push  "F - test-emit-segments-code-label/0"/imm32
    2777     68/push  "ab cd "/imm32
    2778     68/push  _test-output-stream/imm32
    2779     # . . call
    2780     e8/call  check-next-stream-line-equal/disp32
    2781     # . . discard args
    2782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2783     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
    2784     # . . push args
    2785     68/push  "F - test-emit-segments-code-label/1"/imm32
    2786     68/push  "ef gh "/imm32
    2787     68/push  _test-output-stream/imm32
    2788     # . . call
    2789     e8/call  check-next-stream-line-equal/disp32
    2790     # . . discard args
    2791     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2792     # . check-next-stream-line-equal(_test-output-stream, "e8 f9 ff ff ff ", msg)
    2793     # . . push args
    2794     68/push  "F - test-emit-segments-code-label/2"/imm32
    2795     68/push  "e8 f9 ff ff ff "/imm32
    2796     68/push  _test-output-stream/imm32
    2797     # . . call
    2798     e8/call  check-next-stream-line-equal/disp32
    2799     # . . discard args
    2800     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2801     # . epilogue
    2802     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    2803     5d/pop-to-ebp
    2804     c3/return
    2805
    2806 test-emit-segments-code-label-absolute:
    2807     # labels can also convert to absolute addresses
    2808     #
    2809     # input:
    2810     #   in:
    2811     #     == code 0x1000
    2812     #     ab cd
    2813     #     l1:
    2814     #       ef gh
    2815     #       ij l1/imm32
    2816     #   labels:
    2817     #     - 'l1': {'code', 2, 0x1056}
    2818     #
    2819     # output:
    2820     #   ab cd
    2821     #   ef gh
    2822     #   ij 56 10 00 00
    2823     #
    2824     # . prologue
    2825     55/push-ebp
    2826     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    2827     # setup
    2828     # . clear-stream(_test-input-stream)
    2829     # . . push args
    2830     68/push  _test-input-stream/imm32
    2831     # . . call
    2832     e8/call  clear-stream/disp32
    2833     # . . discard args
    2834     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2835     # . clear-stream(_test-output-stream)
    2836     # . . push args
    2837     68/push  _test-output-stream/imm32
    2838     # . . call
    2839     e8/call  clear-stream/disp32
    2840     # . . discard args
    2841     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2842     # . clear-stream($_test-output-buffered-file->buffer)
    2843     # . . push args
    2844     68/push  $_test-output-buffered-file->buffer/imm32
    2845     # . . call
    2846     e8/call  clear-stream/disp32
    2847     # . . discard args
    2848     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2849     # . var labels/edx: (stream byte 8*24)
    2850     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
    2851     68/push  0xc0/imm32/size
    2852     68/push  0/imm32/read
    2853     68/push  0/imm32/write
    2854     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    2855     # . var h/ebx: (handle array byte)
    2856     68/push  0/imm32
    2857     68/push  0/imm32
    2858     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    2859     # initialize input
    2860     # . write(_test-input-stream, "== code 0x1000\n")
    2861     # . . push args
    2862     68/push  "== code 0x1000\n"/imm32
    2863     68/push  _test-input-stream/imm32
    2864     # . . call
    2865     e8/call  write/disp32
    2866     # . . discard args
    2867     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2868     # . write(_test-input-stream, "ab cd\n")
    2869     # . . push args
    2870     68/push  "ab cd\n"/imm32
    2871     68/push  _test-input-stream/imm32
    2872     # . . call
    2873     e8/call  write/disp32
    2874     # . . discard args
    2875     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2876     # . write(_test-input-stream, "l1:\n")
    2877     # . . push args
    2878     68/push  "l1:\n"/imm32
    2879     68/push  _test-input-stream/imm32
    2880     # . . call
    2881     e8/call  write/disp32
    2882     # . . discard args
    2883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2884     # . write(_test-input-stream, "  ef gh\n")
    2885     # . . push args
    2886     68/push  "  ef gh\n"/imm32
    2887     68/push  _test-input-stream/imm32
    2888     # . . call
    2889     e8/call  write/disp32
    2890     # . . discard args
    2891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2892     # . write(_test-input-stream, "  ij l1/imm32\n")
    2893     # . . push args
    2894     68/push  "  ij l1/imm32\n"/imm32
    2895     68/push  _test-input-stream/imm32
    2896     # . . call
    2897     e8/call  write/disp32
    2898     # . . discard args
    2899     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    2900     # . stream-add6(labels, "l1", "code", 2, 0x1056)
    2901     68/push  0x1056/imm32/label-address
    2902     68/push  2/imm32/segment-offset
    2903     # . . push "data"
    2904     53/push-ebx
    2905     68/push  "code"/imm32
    2906     68/push  Heap/imm32
    2907     e8/call  copy-array/disp32
    2908     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2909     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    2910     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    2911     # . . push "l1"
    2912     53/push-ebx
    2913     68/push  "l1"/imm32
    2914     68/push  Heap/imm32
    2915     e8/call  copy-array/disp32
    2916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2917     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
    2918     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
    2919     # . . push labels
    2920     52/push-edx
    2921     # . . call
    2922     e8/call  stream-add6/disp32
    2923     # . . discard args
    2924     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x1c/imm32        # add to esp
    2925     # component under test
    2926     # . emit-segments(_test-input-stream, _test-output-buffered-file, labels)
    2927     # . . push args
    2928     52/push-edx
    2929     68/push  _test-output-buffered-file/imm32
    2930     68/push  _test-input-stream/imm32
    2931     # . . call
    2932     e8/call  emit-segments/disp32
    2933     # . . discard args
    2934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2935     # checks
    2936     # . flush(_test-output-buffered-file)
    2937     # . . push args
    2938     68/push  _test-output-buffered-file/imm32
    2939     # . . call
    2940     e8/call  flush/disp32
    2941     # . . discard args
    2942     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    2943 +-- 33 lines: #?     # dump output ------------------------------------------------------------------------------------------------------------------------------------------------------
    2976     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
    2977     # . . push args
    2978     68/push  "F - test-emit-segments-code-label-absolute/0"/imm32
    2979     68/push  "ab cd "/imm32
    2980     68/push  _test-output-stream/imm32
    2981     # . . call
    2982     e8/call  check-next-stream-line-equal/disp32
    2983     # . . discard args
    2984     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2985     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
    2986     # . . push args
    2987     68/push  "F - test-emit-segments-code-label-absolute/1"/imm32
    2988     68/push  "ef gh "/imm32
    2989     68/push  _test-output-stream/imm32
    2990     # . . call
    2991     e8/call  check-next-stream-line-equal/disp32
    2992     # . . discard args
    2993     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    2994     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
    2995     # . . push args
    2996     68/push  "F - test-emit-segments-code-label-absolute/2"/imm32
    2997     68/push  "ij 56 10 00 00 "/imm32
    2998     68/push  _test-output-stream/imm32
    2999     # . . call
    3000     e8/call  check-next-stream-line-equal/disp32
    3001     # . . discard args
    3002     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3003     # . epilogue
    3004     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3005     5d/pop-to-ebp
    3006     c3/return
    3007
    3008 # reads line to make some checks
    3009 # don't assume the read state of line after calling this function
    3010 far-jump-or-call?:  # line: (addr stream byte) -> result/edi: boolean
    3011     # . prologue
    3012     55/push-ebp
    3013     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3014     # . save registers
    3015     50/push-eax
    3016     51/push-ecx
    3017     52/push-edx
    3018     53/push-ebx
    3019     # ecx = line
    3020     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
    3021     # var word-slice/edx: slice
    3022     68/push  0/imm32/end
    3023     68/push  0/imm32/start
    3024     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    3025     # var datum-slice/ebx: slice
    3026     68/push  0/imm32/end
    3027     68/push  0/imm32/start
    3028     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    3029     # result = false
    3030     bf/copy-to-edi  0/imm32/false
    3031 $far-jump-or-call?:check-first-word:
    3032     # next-word(line, word-slice)
    3033     # . . push args
    3034     52/push-edx
    3035     51/push-ecx
    3036     # . . call
    3037     e8/call  next-word/disp32
    3038     # . . discard args
    3039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3040     # if (slice-empty?(word-slice)) return false
    3041     # . eax = slice-empty?(word-slice)
    3042     # . . push args
    3043     52/push-edx
    3044     # . . call
    3045     e8/call  slice-empty?/disp32
    3046     # . . discard args
    3047     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3048     # . if (eax != 0) return
    3049     3d/compare-eax-and  0/imm32/false
    3050     0f 85/jump-if-!=  $far-jump-or-call?:end/disp32
    3051     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
    3052     # . . push args
    3053     53/push-ebx
    3054     68/push  0x2f/imm32/slash
    3055     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
    3056     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
    3057     # . . call
    3058     e8/call  next-token-from-slice/disp32
    3059     # . . discard args
    3060     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    3061     # if (datum-slice == "e8") return true
    3062     # . eax = slice-equal?(datum-slice, "e8")
    3063     # . . push args
    3064     68/push  "e8"/imm32
    3065     53/push-ebx
    3066     # . . call
    3067     e8/call  slice-equal?/disp32
    3068     # . . discard args
    3069     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3070     # . if (eax != false) return true
    3071     3d/compare-eax-and  0/imm32/false
    3072     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
    3073     # if (datum-slice == "e9") return true
    3074     # . eax = slice-equal?(datum-slice, "e9")
    3075     # . . push args
    3076     68/push  "e9"/imm32
    3077     53/push-ebx
    3078     # . . call
    3079     e8/call  slice-equal?/disp32
    3080     # . . discard args
    3081     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3082     # . if (eax != false) return true
    3083     3d/compare-eax-and  0/imm32/false
    3084     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
    3085     # if (datum-slice != "0f") return false
    3086     # . eax = slice-equal?(datum-slice, "0f")
    3087     # . . push args
    3088     68/push  "0f"/imm32
    3089     53/push-ebx
    3090     # . . call
    3091     e8/call  slice-equal?/disp32
    3092     # . . discard args
    3093     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3094     # . if (eax == false) return
    3095     3d/compare-eax-and  0/imm32/false
    3096     74/jump-if-=  $far-jump-or-call?:end/disp8
    3097 $far-jump-or-call?:check-second-word:
    3098     # next-word(line, word-slice)
    3099     # . . push args
    3100     52/push-edx
    3101     51/push-ecx
    3102     # . . call
    3103     e8/call  next-word/disp32
    3104     # . . discard args
    3105     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3106     # if (slice-empty?(word-slice)) return false
    3107     # . eax = slice-empty?(word-slice)
    3108     # . . push args
    3109     52/push-edx
    3110     # . . call
    3111     e8/call  slice-empty?/disp32
    3112     # . . discard args
    3113     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3114     # . if (eax != 0) return
    3115     3d/compare-eax-and  0/imm32/false
    3116     75/jump-if-!=  $far-jump-or-call?:end/disp8
    3117     # if datum of word-slice does not start with "8", return false
    3118     # . start/eax = word-slice->start
    3119     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy *edx to eax
    3120     # . c/eax = *start
    3121     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
    3122     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
    3123     # . if (eax != '8') return
    3124     3d/compare-eax-and  0x38/imm32/8
    3125     75/jump-if-!=  $far-jump-or-call?:end/disp8
    3126     # otherwise return true
    3127 $far-jump-or-call?:return-true:
    3128     bf/copy-to-edi  1/imm32/true
    3129 $far-jump-or-call?:end:
    3130     # . reclaim locals
    3131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    3132     # . restore registers
    3133     5b/pop-to-ebx
    3134     5a/pop-to-edx
    3135     59/pop-to-ecx
    3136     58/pop-to-eax
    3137     # . epilogue
    3138     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3139     5d/pop-to-ebp
    3140     c3/return
    3141
    3142 emit-headers:  # out: (addr buffered-file), segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
    3143     # pseudocode:
    3144     #   emit-elf-header(out, segments, labels)
    3145     #   var curr-segment-row: (addr handle array byte) = segments->data
    3146     #   max = &segments->data[segments->write]
    3147     #   while true
    3148     #     if (curr-segment >= max) break
    3149     #     emit-elf-program-header-entry(out, curr-segment-row)
    3150     #     curr-segment-row += 20                        # size of a row
    3151     #
    3152     # . prologue
    3153     55/push-ebp
    3154     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3155     # . save registers
    3156     50/push-eax
    3157     51/push-ecx
    3158     # emit-elf-header(out, segments, labels)
    3159     # . . push args
    3160     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    3161     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3162     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3163     # . . call
    3164     e8/call  emit-elf-header/disp32
    3165     # . . discard args
    3166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    3167     # eax = segments
    3168     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    3169     # ecx = segments->write
    3170     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    3171     # curr-segment/eax = segments->data
    3172     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           0/r32/eax   0xc/disp8       .                 # copy eax+12 to eax
    3173     # max/ecx = &segments->data[segments->write]
    3174     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # add eax to ecx
    3175 $emit-headers:loop:
    3176     # if (curr-segment >= max) break
    3177     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
    3178     0f 83/jump-if-addr>=  $emit-headers:end/disp32
    3179 +-- 63 lines: #?     # dump curr-segment->name ------------------------------------------------------------------------------------------------------------------------------------------
    3242 +--  9 lines: #?     # write(2/stderr, "emit-segment-header\n") -------------------------------------------------------------------------------------------------------------------------
    3251     # emit-elf-program-header-entry(out, curr-segment)
    3252     # . . push args
    3253     50/push-eax
    3254     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3255     # . . call
    3256     e8/call  emit-elf-program-header-entry/disp32
    3257     # . . discard args
    3258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3259     # curr-segment += 20                        # size of a row
    3260     81          0/subop/add         3/mod/direct    0/rm32/eax    .           .             .           .           .               0x14/imm32        # add to eax
    3261     e9/jump  $emit-headers:loop/disp32
    3262 $emit-headers:end:
    3263     # . restore registers
    3264     59/pop-to-ecx
    3265     58/pop-to-eax
    3266     # . epilogue
    3267     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3268     5d/pop-to-ebp
    3269     c3/return
    3270
    3271 emit-elf-header:  # out: (addr buffered-file), segments: (addr stream {(handle array byte), segment-info}), labels: (addr stream {(handle array byte), label-info})
    3272     # pseudocode
    3273     #   *$Elf_e_entry = get(labels, "Entry")->address
    3274     #   *$Elf_e_phnum = segments->write / 20         # size of a row
    3275     #   emit-hex-array(out, Elf_header)
    3276     #   write-buffered(out, "\n")
    3277     #
    3278     # . prologue
    3279     55/push-ebp
    3280     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3281     # . save registers
    3282     50/push-eax
    3283     51/push-ecx
    3284     52/push-edx  # just because we need to call idiv
    3285     # *$Elf_e_entry = get(labels, "Entry")->address
    3286     # . eax = labels
    3287     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
    3288     # . label-info/eax = get(labels, "Entry", row-size=24, "label table")
    3289     # . . push args
    3290     68/push  "label table"/imm32
    3291     68/push  0x18/imm32/row-size
    3292     68/push  "Entry"/imm32
    3293     50/push-eax
    3294     # . . call
    3295     e8/call  get/disp32
    3296     # . . discard args
    3297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
    3298     # . eax = label-info->address
    3299     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(eax+12) to eax
    3300     # . *$Elf_e_entry = eax
    3301     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_e_entry/disp32               # copy eax to *$Elf_e_entry
    3302     # *$Elf_e_phnum = segments->write / 20
    3303     # . eax = segments
    3304     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
    3305     # . len/eax = segments->write
    3306     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           0/r32/eax   .               .                 # copy *eax to eax
    3307     # . eax = len / 20  (clobbering ecx and edx)
    3308     b9/copy-to-ecx  0x14/imm32
    3309     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
    3310     f7          7/subop/idiv        3/mod/direct    1/rm32/ecx    .           .             .           .           .               .                 # divide edx:eax by ecx, storing quotient in eax and remainder in edx
    3311     # . *$Elf_e_phnum = eax
    3312     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_e_phnum/disp32               # copy eax to *$Elf_e_phnum
    3313     # emit-hex-array(out, Elf_header)
    3314     # . . push args
    3315     68/push  Elf_header/imm32
    3316     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3317     # . . call
    3318     e8/call  emit-hex-array/disp32
    3319     # . . discard args
    3320     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3321     # write-buffered(out, "\n")
    3322     # . . push args
    3323     68/push  Newline/imm32
    3324     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3325     # . . call
    3326     e8/call  write-buffered/disp32
    3327     # . . discard args
    3328     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3329 $emit-elf-header:end:
    3330     # . restore registers
    3331     5a/pop-to-edx
    3332     59/pop-to-ecx
    3333     58/pop-to-eax
    3334     # . epilogue
    3335     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3336     5d/pop-to-ebp
    3337     c3/return
    3338
    3339 # segment-info: {address, file-offset, size}                                  (12 bytes)
    3340 # segments: (addr stream {(handle array byte), segment-info})                 (20 bytes per row)
    3341 emit-elf-program-header-entry:  # out: (addr buffered-file), curr-segment: (addr {(handle array byte), segment-info})
    3342     # pseudocode:
    3343     #   *$Elf_p_offset = curr-segment->file-offset
    3344     #   *$Elf_p_vaddr = curr-segment->address
    3345     #   *$Elf_p_paddr = curr-segment->address
    3346     #   *$Elf_p_filesz = curr-segment->size
    3347     #   *$Elf_p_memsz = curr-segment->size
    3348     #   if curr-segment->name == "code"
    3349     #     *$Elf_p_flags = 5  # r-x
    3350     #   else
    3351     #     *$Elf_p_flags = 6  # rw-
    3352     #   emit-hex-array(out, Elf_program_header_entry)
    3353     #   write-buffered(out, "\n")
    3354     #
    3355     # . prologue
    3356     55/push-ebp
    3357     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3358     # . save registers
    3359     50/push-eax
    3360     56/push-esi
    3361     # esi = curr-segment
    3362     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
    3363     # *$Elf_p_offset = curr-segment->file-offset
    3364     # . eax = curr-segment->file-offset
    3365     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(esi+12) to eax
    3366     # . *$Elf_p_offset = eax
    3367     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_offset/disp32              # copy eax to *$Elf_p_offset
    3368     # *$Elf_p_vaddr = curr-segment->address
    3369     # . eax = curr-segment->address
    3370     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   8/disp8         .                 # copy *(esi+8) to eax
    3371     # . *$Elf_p_vaddr = eax
    3372     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_vaddr/disp32               # copy eax to *$Elf_p_vaddr
    3373     # *$Elf_p_paddr = curr-segment->address
    3374     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_paddr/disp32               # copy eax to *$Elf_p_paddr
    3375     # *$Elf_p_filesz = curr-segment->size
    3376     # . eax = curr-segment->size
    3377     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0x10/disp8       .                # copy *(esi+16) to eax
    3378     # . *$Elf_p_filesz = eax
    3379     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_filesz/disp32              # copy eax to *$Elf_p_filesz
    3380     # *$Elf_p_memsz = curr-segment->size
    3381     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_memsz/disp32               # copy eax to *$Elf_p_memsz
    3382     # if (!string-equal?(name, "code") goto next check
    3383     # . var name/eax: (addr array byte) = lookup(curr-segment->name)
    3384     # . . push args
    3385     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
    3386     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
    3387     # . . call
    3388     e8/call  lookup/disp32
    3389     # . . discard args
    3390     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3391     # . eax = string-equal?(name, "code")
    3392     # . . push args
    3393     68/push  "code"/imm32
    3394     50/push-eax
    3395     # . . call
    3396     e8/call  string-equal?/disp32
    3397     # . . discard args
    3398     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3399     # . if (eax == false) goto next check
    3400     3d/compare-eax-and  0/imm32/false
    3401     74/jump-if-=  $emit-elf-program-header-entry:data/disp8
    3402     # *$Elf_p_flags = r-x
    3403     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           $Elf_p_flags/disp32  5/imm32      # copy to *$Elf_p_flags
    3404     eb/jump  $emit-elf-program-header-entry:really-emit/disp8
    3405 $emit-elf-program-header-entry:data:
    3406     # otherwise *$Elf_p_flags = rw-
    3407     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           $Elf_p_flags/disp32  6/imm32      # copy to *$Elf_p_flags
    3408 $emit-elf-program-header-entry:really-emit:
    3409     # emit-hex-array(out, Elf_program_header_entry)
    3410     # . . push args
    3411     68/push  Elf_program_header_entry/imm32
    3412     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3413     # . . call
    3414     e8/call  emit-hex-array/disp32
    3415     # . . discard args
    3416     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3417     # write-buffered(out, "\n")
    3418     # . . push args
    3419     68/push  Newline/imm32
    3420     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3421     # . . call
    3422     e8/call  write-buffered/disp32
    3423     # . . discard args
    3424     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3425 $emit-elf-program-header-entry:end:
    3426     # . restore registers
    3427     5e/pop-to-esi
    3428     58/pop-to-eax
    3429     # . epilogue
    3430     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3431     5d/pop-to-ebp
    3432     c3/return
    3433
    3434 # - some helpers for tests
    3435
    3436 stream-add5:  # in: (addr stream byte), key: handle, val1: addr, val2: addr, val3: addr
    3437     # . prologue
    3438     55/push-ebp
    3439     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3440     # . save registers
    3441     50/push-eax
    3442     51/push-ecx
    3443     52/push-edx
    3444     56/push-esi
    3445     # esi = in
    3446     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    3447     # curr/eax = &in->data[in->write]
    3448     # . eax = in->write
    3449     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    3450     # . eax = esi+eax+12
    3451     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
    3452     # max/edx = &in->data[in->size]
    3453     # . edx = in->size
    3454     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
    3455     # . edx = esi+edx+12
    3456     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
    3457     # if (curr >= max) abort
    3458     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3459     73/jump-if-addr>=  $stream-add5:abort/disp8
    3460     # *curr = key->alloc-id
    3461     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
    3462     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3463     # curr += 4
    3464     05/add-to-eax  4/imm32
    3465     # if (curr >= max) abort
    3466     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3467     73/jump-if-addr>=  $stream-add5:abort/disp8
    3468     # *curr = key->payload
    3469     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
    3470     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3471     # curr += 4
    3472     05/add-to-eax  4/imm32
    3473     # if (curr >= max) abort
    3474     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3475     73/jump-if-addr>=  $stream-add5:abort/disp8
    3476     # *curr = val1
    3477     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
    3478     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3479     # curr += 4
    3480     05/add-to-eax  4/imm32
    3481     # if (curr >= max) abort
    3482     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3483     73/jump-if-addr>=  $stream-add5:abort/disp8
    3484     # *curr = val2
    3485     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x18/disp8      .                 # copy *(ebp+24) to ecx
    3486     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3487     # curr += 4
    3488     05/add-to-eax  4/imm32
    3489     # if (curr >= max) abort
    3490     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3491     73/jump-if-addr>=  $stream-add5:abort/disp8
    3492     # *curr = val3
    3493     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x1c/disp8      .                 # copy *(ebp+28) to ecx
    3494     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3495     # in->write += 20
    3496     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               0x14/imm32        # add to *esi
    3497 $stream-add5:end:
    3498     # . restore registers
    3499     5e/pop-to-esi
    3500     5a/pop-to-edx
    3501     59/pop-to-ecx
    3502     58/pop-to-eax
    3503     # . epilogue
    3504     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3505     5d/pop-to-ebp
    3506     c3/return
    3507
    3508 $stream-add5:abort:
    3509     # . _write(2/stderr, error)
    3510     # . . push args
    3511     68/push  "overflow in stream-add5\n"/imm32
    3512     68/push  2/imm32/stderr
    3513     # . . call
    3514     e8/call  _write/disp32
    3515     # . . discard args
    3516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3517     # . syscall(exit, 1)
    3518     bb/copy-to-ebx  1/imm32
    3519     e8/call  syscall_exit/disp32
    3520     # never gets here
    3521
    3522 stream-add6:  # in: (addr stream byte), key: handle, val1: addr, val2: addr, val3: addr, val4: addr
    3523     # . prologue
    3524     55/push-ebp
    3525     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3526     # . save registers
    3527     50/push-eax
    3528     51/push-ecx
    3529     52/push-edx
    3530     56/push-esi
    3531     # esi = in
    3532     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
    3533     # curr/eax = &in->data[in->write]
    3534     # . eax = in->write
    3535     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
    3536     # . eax = esi+eax+12
    3537     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
    3538     # max/edx = &in->data[in->size]
    3539     # . edx = in->size
    3540     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
    3541     # . edx = esi+edx+12
    3542     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
    3543     # if (curr >= max) abort
    3544     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3545     73/jump-if-addr>=  $stream-add6:abort/disp8
    3546     # *curr = key->alloc-id
    3547     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
    3548     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3549     # curr += 4
    3550     05/add-to-eax  4/imm32
    3551     # if (curr >= max) abort
    3552     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3553     73/jump-if-addr>=  $stream-add6:abort/disp8
    3554     # *curr = key->payload
    3555     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
    3556     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3557     # curr += 4
    3558     05/add-to-eax  4/imm32
    3559     # if (curr >= max) abort
    3560     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3561     73/jump-if-addr>=  $stream-add6:abort/disp8
    3562     # *curr = val1
    3563     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
    3564     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3565     # curr += 4
    3566     05/add-to-eax  4/imm32
    3567     # if (curr >= max) abort
    3568     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3569     73/jump-if-addr>=  $stream-add6:abort/disp8
    3570     # *curr = val2
    3571     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x18/disp8      .                 # copy *(ebp+24) to ecx
    3572     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3573     # curr += 4
    3574     05/add-to-eax  4/imm32
    3575     # if (curr >= max) abort
    3576     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3577     73/jump-if-addr>=  $stream-add6:abort/disp8
    3578 $aa-write-segment-offset:
    3579     # *curr = val3
    3580     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x1c/disp8      .                 # copy *(ebp+28) to ecx
    3581     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3582     # curr += 4
    3583     05/add-to-eax  4/imm32
    3584     # if (curr >= max) abort
    3585     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
    3586     73/jump-if-addr>=  $stream-add6:abort/disp8
    3587     # *curr = val4
    3588     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x20/disp8      .                 # copy *(ebp+32) to ecx
    3589     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
    3590     # in->write += 24
    3591     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               0x18/imm32        # add to *esi
    3592 $stream-add6:end:
    3593     # . restore registers
    3594     5e/pop-to-esi
    3595     5a/pop-to-edx
    3596     59/pop-to-ecx
    3597     58/pop-to-eax
    3598     # . epilogue
    3599     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3600     5d/pop-to-ebp
    3601     c3/return
    3602
    3603 $stream-add6:abort:
    3604     # . _write(2/stderr, error)
    3605     # . . push args
    3606     68/push  "overflow in stream-add6\n"/imm32
    3607     68/push  2/imm32/stderr
    3608     # . . call
    3609     e8/call  _write/disp32
    3610     # . . discard args
    3611     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3612     # . syscall(exit, 1)
    3613     bb/copy-to-ebx  1/imm32
    3614     e8/call  syscall_exit/disp32
    3615     # never gets here
    3616
    3617 # some variants of 'trace' that take multiple arguments in different combinations of types:
    3618 #   n: int
    3619 #   c: character [4-bytes, will eventually be UTF-8]
    3620 #   s: (addr array byte)
    3621 #   l: (addr slice)
    3622 # one gotcha: 's5' must not be empty
    3623
    3624 trace-sssns:  # s1: (addr array byte), s2: (addr array byte), s3: (addr array byte), n4: int, s5: (addr array byte)
    3625     # . prologue
    3626     55/push-ebp
    3627     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3628     # write(*Trace-stream, s1)
    3629     # . . push args
    3630     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3631     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3632     # . . call
    3633     e8/call  write/disp32
    3634     # . . discard args
    3635     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3636     # write(*Trace-stream, s2)
    3637     # . . push args
    3638     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3639     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3640     # . . call
    3641     e8/call  write/disp32
    3642     # . . discard args
    3643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3644     # write(*Trace-stream, s3)
    3645     # . . push args
    3646     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    3647     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3648     # . . call
    3649     e8/call  write/disp32
    3650     # . . discard args
    3651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3652     # write-int32-hex(*Trace-stream, n4)
    3653     # . . push args
    3654     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    3655     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3656     # . . call
    3657     e8/call  write-int32-hex/disp32
    3658     # . . discard args
    3659     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3660     # trace(s5)  # implicitly adds a newline and finalizes the trace line
    3661     # . . push args
    3662     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    3663     # . . call
    3664     e8/call  trace/disp32
    3665     # . . discard args
    3666     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3667 $trace-sssns:end:
    3668     # . epilogue
    3669     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3670     5d/pop-to-ebp
    3671     c3/return
    3672
    3673 test-trace-sssns:
    3674     # . prologue
    3675     55/push-ebp
    3676     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3677     # setup
    3678     # . *Trace-stream->write = 0
    3679     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
    3680     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
    3681     # trace-sssns("A" "b" "c " 3 " e")
    3682     # . . push args
    3683     68/push  " e"/imm32
    3684     68/push  3/imm32
    3685     68/push  "c "/imm32
    3686     68/push  "b"/imm32
    3687     68/push  "A"/imm32
    3688     # . . call
    3689     e8/call  trace-sssns/disp32
    3690     # . . discard args
    3691     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    3692 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    3718     # check-trace-contains("Abc 0x00000003 e")
    3719     # . . push args
    3720     68/push  "F - test-trace-sssns"/imm32
    3721     68/push  "Abc 0x00000003 e"/imm32
    3722     # . . call
    3723     e8/call  check-trace-contains/disp32
    3724     # . . discard args
    3725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3726     # . epilogue
    3727     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3728     5d/pop-to-ebp
    3729     c3/return
    3730
    3731 trace-slsls:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), l4: (addr slice), s5: (addr array byte)
    3732     # . prologue
    3733     55/push-ebp
    3734     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3735     # write(*Trace-stream, s1)
    3736     # . . push args
    3737     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3738     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3739     # . . call
    3740     e8/call  write/disp32
    3741     # . . discard args
    3742     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3743     # write-slice(*Trace-stream, l2)
    3744     # . . push args
    3745     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3746     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3747     # . . call
    3748     e8/call  write-slice/disp32
    3749     # . . discard args
    3750     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3751     # write(*Trace-stream, s3)
    3752     # . . push args
    3753     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    3754     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3755     # . . call
    3756     e8/call  write/disp32
    3757     # . . discard args
    3758     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3759     # write-slice(*Trace-stream, l4)
    3760     # . . push args
    3761     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    3762     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3763     # . . call
    3764     e8/call  write-slice/disp32
    3765     # . . discard args
    3766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3767     # trace(s5)  # implicitly adds a newline and finalizes the trace line
    3768     # . . push args
    3769     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    3770     # . . call
    3771     e8/call  trace/disp32
    3772     # . . discard args
    3773     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3774 $trace-slsls:end:
    3775     # . epilogue
    3776     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3777     5d/pop-to-ebp
    3778     c3/return
    3779
    3780 test-trace-slsls:
    3781     # . prologue
    3782     55/push-ebp
    3783     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3784     # setup
    3785     # . *Trace-stream->write = 0
    3786     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
    3787     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
    3788     # (eax..ecx) = "b"
    3789     b8/copy-to-eax  "b"/imm32
    3790     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    3791     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
    3792     05/add-to-eax  4/imm32
    3793     # var b/ebx: slice = {eax, ecx}
    3794     51/push-ecx
    3795     50/push-eax
    3796     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    3797     # (eax..ecx) = "d"
    3798     b8/copy-to-eax  "d"/imm32
    3799     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    3800     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
    3801     05/add-to-eax  4/imm32
    3802     # var d/edx: slice = {eax, ecx}
    3803     51/push-ecx
    3804     50/push-eax
    3805     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
    3806     # trace-slsls("A" b "c" d "e")
    3807     # . . push args
    3808     68/push  "e"/imm32
    3809     52/push-edx
    3810     68/push  "c"/imm32
    3811     53/push-ebx
    3812     68/push  "A"/imm32
    3813     # . . call
    3814     e8/call  trace-slsls/disp32
    3815     # . . discard args
    3816     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    3817 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    3843     # check-trace-contains("Abcde")
    3844     # . . push args
    3845     68/push  "F - test-trace-slsls"/imm32
    3846     68/push  "Abcde"/imm32
    3847     # . . call
    3848     e8/call  check-trace-contains/disp32
    3849     # . . discard args
    3850     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3851     # . epilogue
    3852     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3853     5d/pop-to-ebp
    3854     c3/return
    3855
    3856 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
    3857     # . prologue
    3858     55/push-ebp
    3859     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3860     # write(*Trace-stream, s1)
    3861     # . . push args
    3862     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3863     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3864     # . . call
    3865     e8/call  write/disp32
    3866     # . . discard args
    3867     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3868     # write-slice(*Trace-stream, l2)
    3869     # . . push args
    3870     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3871     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3872     # . . call
    3873     e8/call  write-slice/disp32
    3874     # . . discard args
    3875     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3876     # write(*Trace-stream, s3)
    3877     # . . push args
    3878     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    3879     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3880     # . . call
    3881     e8/call  write/disp32
    3882     # . . discard args
    3883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3884     # write-int32-hex(*Trace-stream, n4)
    3885     # . . push args
    3886     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    3887     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3888     # . . call
    3889     e8/call  write-int32-hex/disp32
    3890     # . . discard args
    3891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3892     # trace(s5)  # implicitly adds a newline and finalizes the trace line
    3893     # . . push args
    3894     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    3895     # . . call
    3896     e8/call  trace/disp32
    3897     # . . discard args
    3898     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    3899 $trace-slsns:end:
    3900     # . epilogue
    3901     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3902     5d/pop-to-ebp
    3903     c3/return
    3904
    3905 test-trace-slsns:
    3906     # . prologue
    3907     55/push-ebp
    3908     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3909     # setup
    3910     # . *Trace-stream->write = 0
    3911     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
    3912     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
    3913     # (eax..ecx) = "b"
    3914     b8/copy-to-eax  "b"/imm32
    3915     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    3916     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
    3917     05/add-to-eax  4/imm32
    3918     # var b/ebx: slice = {eax, ecx}
    3919     51/push-ecx
    3920     50/push-eax
    3921     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    3922     # trace-slsls("A" b "c " 3 " e")
    3923     # . . push args
    3924     68/push  " e"/imm32
    3925     68/push  3/imm32
    3926     68/push  "c "/imm32
    3927     53/push-ebx
    3928     68/push  "A"/imm32
    3929     # . . call
    3930     e8/call  trace-slsns/disp32
    3931     # . . discard args
    3932     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    3933 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    3959     # check-trace-contains("Abc 0x00000003 e")
    3960     # . . push args
    3961     68/push  "F - test-trace-slsls"/imm32
    3962     68/push  "Abc 0x00000003 e"/imm32
    3963     # . . call
    3964     e8/call  check-trace-contains/disp32
    3965     # . . discard args
    3966     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3967     # . epilogue
    3968     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    3969     5d/pop-to-ebp
    3970     c3/return
    3971
    3972 trace-slsss:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), s4: (addr array byte), s5: (addr array byte)
    3973     # . prologue
    3974     55/push-ebp
    3975     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    3976     # write(*Trace-stream, s1)
    3977     # . . push args
    3978     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    3979     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3980     # . . call
    3981     e8/call  write/disp32
    3982     # . . discard args
    3983     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3984     # write-slice(*Trace-stream, l2)
    3985     # . . push args
    3986     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    3987     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3988     # . . call
    3989     e8/call  write-slice/disp32
    3990     # . . discard args
    3991     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    3992     # write(*Trace-stream, s3)
    3993     # . . push args
    3994     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    3995     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    3996     # . . call
    3997     e8/call  write/disp32
    3998     # . . discard args
    3999     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4000     # write(*Trace-stream, s4)
    4001     # . . push args
    4002     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    4003     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    4004     # . . call
    4005     e8/call  write/disp32
    4006     # . . discard args
    4007     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4008     # trace(s5)  # implicitly adds a newline and finalizes the trace line
    4009     # . . push args
    4010     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    4011     # . . call
    4012     e8/call  trace/disp32
    4013     # . . discard args
    4014     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4015 $trace-slsss:end:
    4016     # . epilogue
    4017     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4018     5d/pop-to-ebp
    4019     c3/return
    4020
    4021 test-trace-slsss:
    4022     # . prologue
    4023     55/push-ebp
    4024     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4025     # setup
    4026     # . *Trace-stream->write = 0
    4027     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
    4028     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
    4029     # (eax..ecx) = "b"
    4030     b8/copy-to-eax  "b"/imm32
    4031     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    4032     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
    4033     05/add-to-eax  4/imm32
    4034     # var b/ebx: slice = {eax, ecx}
    4035     51/push-ecx
    4036     50/push-eax
    4037     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    4038     # trace-slsss("A" b "c" "d" "e")
    4039     # . . push args
    4040     68/push  "e"/imm32
    4041     68/push  "d"/imm32
    4042     68/push  "c"/imm32
    4043     53/push-ebx
    4044     68/push  "A"/imm32
    4045     # . . call
    4046     e8/call  trace-slsss/disp32
    4047     # . . discard args
    4048     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    4049 +-- 26 lines: #?     # dump *Trace-stream -----------------------------------------------------------------------------------------------------------------------------------------------
    4075     # check-trace-contains("Abcde")
    4076     # . . push args
    4077     68/push  "F - test-trace-slsss"/imm32
    4078     68/push  "Abcde"/imm32
    4079     # . . call
    4080     e8/call  check-trace-contains/disp32
    4081     # . . discard args
    4082     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4083     # . epilogue
    4084     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4085     5d/pop-to-ebp
    4086     c3/return
    4087
    4088 num-bytes:  # line: (addr stream byte) -> eax: int
    4089     # pseudocode:
    4090     #   result = 0
    4091     #   while true
    4092     #     var word-slice = next-word(line)
    4093     #     if slice-empty?(word-slice)             # end of line
    4094     #       break
    4095     #     if slice-starts-with?(word-slice, "#")  # comment
    4096     #       break
    4097     #     if label?(word-slice)                # no need for label declarations anymore
    4098     #       break
    4099     #     if slice-equal?(word-slice, "==")
    4100     #       break                                 # no need for segment header lines
    4101     #     result += compute-width(word-slice)
    4102     #   return result
    4103     #
    4104     # . prologue
    4105     55/push-ebp
    4106     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4107     # . save registers
    4108     51/push-ecx
    4109     52/push-edx
    4110     53/push-ebx
    4111     # var result/eax = 0
    4112     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
    4113     # var word-slice/ecx: slice
    4114     68/push  0/imm32/end
    4115     68/push  0/imm32/start
    4116     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
    4117 +-- 26 lines: #?     # dump line --------------------------------------------------------------------------------------------------------------------------------------------------------
    4143     # . rewind-stream(line)
    4144     # . . push args
    4145     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    4146     # . . call
    4147     e8/call  rewind-stream/disp32
    4148     # . . discard args
    4149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4150 $num-bytes:loop:
    4151     # next-word(line, word-slice)
    4152     # . . push args
    4153     51/push-ecx
    4154     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    4155     # . . call
    4156     e8/call  next-word/disp32
    4157     # . . discard args
    4158     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4159 +-- 40 lines: #?     # dump word-slice --------------------------------------------------------------------------------------------------------------------------------------------------
    4199 $num-bytes:check0:
    4200     # if (slice-empty?(word-slice)) break
    4201     # . save result
    4202     50/push-eax
    4203     # . eax = slice-empty?(word-slice)
    4204     # . . push args
    4205     51/push-ecx
    4206     # . . call
    4207     e8/call  slice-empty?/disp32
    4208     # . . discard args
    4209     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4210     # . if (eax != false) break
    4211     3d/compare-eax-and  0/imm32/false
    4212     # . restore result now that ZF is set
    4213     58/pop-to-eax
    4214     75/jump-if-!=  $num-bytes:end/disp8
    4215 $num-bytes:check-for-comment:
    4216     # if (slice-starts-with?(word-slice, "#")) break
    4217     # . start/edx = word-slice->start
    4218     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
    4219     # . c/ebx = *start
    4220     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    4221     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           3/r32/BL    .               .                 # copy byte at *edx to BL
    4222     # . if (ebx == '#') break
    4223     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x23/imm32/hash   # compare ebx
    4224     74/jump-if-=  $num-bytes:end/disp8
    4225 $num-bytes:check-for-label:
    4226     # if (slice-ends-with?(word-slice, ":")) break
    4227     # . end/edx = word-slice->end
    4228     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
    4229     # . c/ebx = *(end-1)
    4230     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
    4231     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ecx to BL
    4232     # . if (ebx == ':') break
    4233     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x3a/imm32/colon  # compare ebx
    4234     74/jump-if-=  $num-bytes:end/disp8
    4235 $num-bytes:check-for-segment-header:
    4236     # if (slice-equal?(word-slice, "==")) break
    4237     # . push result
    4238     50/push-eax
    4239     # . eax = slice-equal?(word-slice, "==")
    4240     # . . push args
    4241     68/push  "=="/imm32
    4242     51/push-ecx
    4243     # . . call
    4244     e8/call  slice-equal?/disp32
    4245     # . . discard args
    4246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4247     # . if (eax != false) break
    4248     3d/compare-eax-and  0/imm32/false
    4249     # . restore result now that ZF is set
    4250     58/pop-to-eax
    4251     75/jump-if-!=  $num-bytes:end/disp8
    4252 $num-bytes:loop-body:
    4253     # result += compute-width-of-slice(word-slice)
    4254     # . copy result to edx
    4255     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
    4256     # . eax = compute-width-of-slice(word-slice)
    4257     # . . push args
    4258     51/push-ecx
    4259     # . . call
    4260     e8/call compute-width-of-slice/disp32
    4261     # . . discard args
    4262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4263     # . eax += result
    4264     01/add                          3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # add edx to eax
    4265     e9/jump  $num-bytes:loop/disp32
    4266 $num-bytes:end:
    4267     # . rewind-stream(line)
    4268     # . . push args
    4269     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    4270     # . . call
    4271     e8/call  rewind-stream/disp32
    4272     # . . discard args
    4273     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4274     # . reclaim locals
    4275     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4276     # . restore registers
    4277     5b/pop-to-ebx
    4278     5a/pop-to-edx
    4279     59/pop-to-ecx
    4280     # . epilogue
    4281     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4282     5d/pop-to-ebp
    4283     c3/return
    4284
    4285 test-num-bytes-handles-empty-string:
    4286     # if a line starts with '#', return 0
    4287     # . prologue
    4288     55/push-ebp
    4289     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4290     # setup
    4291     # . clear-stream(_test-input-stream)
    4292     # . . push args
    4293     68/push  _test-input-stream/imm32
    4294     # . . call
    4295     e8/call  clear-stream/disp32
    4296     # . . discard args
    4297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4298     # . clear-stream(_test-output-stream)
    4299     # . . push args
    4300     68/push  _test-output-stream/imm32
    4301     # . . call
    4302     e8/call  clear-stream/disp32
    4303     # . . discard args
    4304     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4305     # no contents in input
    4306     # eax = num-bytes(_test-input-stream)
    4307     # . . push args
    4308     68/push  _test-input-stream/imm32
    4309     # . . call
    4310     e8/call  num-bytes/disp32
    4311     # . . discard args
    4312     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4313     # check-ints-equal(eax, 0, msg)
    4314     # . . push args
    4315     68/push  "F - test-num-bytes-handles-empty-string"/imm32
    4316     68/push  0/imm32/true
    4317     50/push-eax
    4318     # . . call
    4319     e8/call  check-ints-equal/disp32
    4320     # . . discard args
    4321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4322     # . epilogue
    4323     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4324     5d/pop-to-ebp
    4325     c3/return
    4326
    4327 test-num-bytes-ignores-comments:
    4328     # if a line starts with '#', return 0
    4329     # . prologue
    4330     55/push-ebp
    4331     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4332     # setup
    4333     # . clear-stream(_test-input-stream)
    4334     # . . push args
    4335     68/push  _test-input-stream/imm32
    4336     # . . call
    4337     e8/call  clear-stream/disp32
    4338     # . . discard args
    4339     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4340     # . clear-stream(_test-output-stream)
    4341     # . . push args
    4342     68/push  _test-output-stream/imm32
    4343     # . . call
    4344     e8/call  clear-stream/disp32
    4345     # . . discard args
    4346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4347     # initialize input
    4348     # . write(_test-input-stream, "# abcd")
    4349     # . . push args
    4350     68/push  "# abcd"/imm32
    4351     68/push  _test-input-stream/imm32
    4352     # . . call
    4353     e8/call  write/disp32
    4354     # . . discard args
    4355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4356     # eax = num-bytes(_test-input-stream)
    4357     # . . push args
    4358     68/push  _test-input-stream/imm32
    4359     # . . call
    4360     e8/call  num-bytes/disp32
    4361     # . . discard args
    4362     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4363     # check-ints-equal(eax, 0, msg)
    4364     # . . push args
    4365     68/push  "F - test-num-bytes-ignores-comments"/imm32
    4366     68/push  0/imm32/true
    4367     50/push-eax
    4368     # . . call
    4369     e8/call  check-ints-equal/disp32
    4370     # . . discard args
    4371     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4372     # . epilogue
    4373     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4374     5d/pop-to-ebp
    4375     c3/return
    4376
    4377 test-num-bytes-ignores-labels:
    4378     # if the first word ends with ':', return 0
    4379     # . prologue
    4380     55/push-ebp
    4381     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4382     # setup
    4383     # . clear-stream(_test-input-stream)
    4384     # . . push args
    4385     68/push  _test-input-stream/imm32
    4386     # . . call
    4387     e8/call  clear-stream/disp32
    4388     # . . discard args
    4389     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4390     # . clear-stream(_test-output-stream)
    4391     # . . push args
    4392     68/push  _test-output-stream/imm32
    4393     # . . call
    4394     e8/call  clear-stream/disp32
    4395     # . . discard args
    4396     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4397     # initialize input
    4398     # . write(_test-input-stream, "ab: # cd")
    4399     # . . push args
    4400     68/push  "ab: # cd"/imm32
    4401     68/push  _test-input-stream/imm32
    4402     # . . call
    4403     e8/call  write/disp32
    4404     # . . discard args
    4405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4406     # eax = num-bytes(_test-input-stream)
    4407     # . . push args
    4408     68/push  _test-input-stream/imm32
    4409     # . . call
    4410     e8/call  num-bytes/disp32
    4411     # . . discard args
    4412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4413     # check-ints-equal(eax, 0, msg)
    4414     # . . push args
    4415     68/push  "F - test-num-bytes-ignores-labels"/imm32
    4416     68/push  0/imm32/true
    4417     50/push-eax
    4418     # . . call
    4419     e8/call  check-ints-equal/disp32
    4420     # . . discard args
    4421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4422     # . epilogue
    4423     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4424     5d/pop-to-ebp
    4425     c3/return
    4426
    4427 test-num-bytes-ignores-segment-headers:
    4428     # if the first word is '==', return 0
    4429     # . prologue
    4430     55/push-ebp
    4431     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4432     # setup
    4433     # . clear-stream(_test-input-stream)
    4434     # . . push args
    4435     68/push  _test-input-stream/imm32
    4436     # . . call
    4437     e8/call  clear-stream/disp32
    4438     # . . discard args
    4439     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4440     # . clear-stream(_test-output-stream)
    4441     # . . push args
    4442     68/push  _test-output-stream/imm32
    4443     # . . call
    4444     e8/call  clear-stream/disp32
    4445     # . . discard args
    4446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4447     # initialize input
    4448     # . write(_test-input-stream, "== ab cd")
    4449     # . . push args
    4450     68/push  "== ab cd"/imm32
    4451     68/push  _test-input-stream/imm32
    4452     # . . call
    4453     e8/call  write/disp32
    4454     # . . discard args
    4455     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4456     # eax = num-bytes(_test-input-stream)
    4457     # . . push args
    4458     68/push  _test-input-stream/imm32
    4459     # . . call
    4460     e8/call  num-bytes/disp32
    4461     # . . discard args
    4462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4463     # check-ints-equal(eax, 0, msg)
    4464     # . . push args
    4465     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
    4466     68/push  0/imm32/true
    4467     50/push-eax
    4468     # . . call
    4469     e8/call  check-ints-equal/disp32
    4470     # . . discard args
    4471     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4472     # . epilogue
    4473     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4474     5d/pop-to-ebp
    4475     c3/return
    4476
    4477 test-num-bytes-counts-words-by-default:
    4478     # without metadata, count words
    4479     # . prologue
    4480     55/push-ebp
    4481     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4482     # setup
    4483     # . clear-stream(_test-input-stream)
    4484     # . . push args
    4485     68/push  _test-input-stream/imm32
    4486     # . . call
    4487     e8/call  clear-stream/disp32
    4488     # . . discard args
    4489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4490     # . clear-stream(_test-output-stream)
    4491     # . . push args
    4492     68/push  _test-output-stream/imm32
    4493     # . . call
    4494     e8/call  clear-stream/disp32
    4495     # . . discard args
    4496     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4497     # initialize input
    4498     # . write(_test-input-stream, "ab cd ef")
    4499     # . . push args
    4500     68/push  "ab cd ef"/imm32
    4501     68/push  _test-input-stream/imm32
    4502     # . . call
    4503     e8/call  write/disp32
    4504     # . . discard args
    4505     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4506     # eax = num-bytes(_test-input-stream)
    4507     # . . push args
    4508     68/push  _test-input-stream/imm32
    4509     # . . call
    4510     e8/call  num-bytes/disp32
    4511     # . . discard args
    4512     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4513     # check-ints-equal(eax, 3, msg)
    4514     # . . push args
    4515     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
    4516     68/push  3/imm32/true
    4517     50/push-eax
    4518     # . . call
    4519     e8/call  check-ints-equal/disp32
    4520     # . . discard args
    4521     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4522     # . epilogue
    4523     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4524     5d/pop-to-ebp
    4525     c3/return
    4526
    4527 test-num-bytes-ignores-trailing-comment:
    4528     # trailing comments appropriately ignored
    4529     # . prologue
    4530     55/push-ebp
    4531     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4532     # setup
    4533     # . clear-stream(_test-input-stream)
    4534     # . . push args
    4535     68/push  _test-input-stream/imm32
    4536     # . . call
    4537     e8/call  clear-stream/disp32
    4538     # . . discard args
    4539     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4540     # . clear-stream(_test-output-stream)
    4541     # . . push args
    4542     68/push  _test-output-stream/imm32
    4543     # . . call
    4544     e8/call  clear-stream/disp32
    4545     # . . discard args
    4546     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4547     # initialize input
    4548     # . write(_test-input-stream, "ab cd # ef")
    4549     # . . push args
    4550     68/push  "ab cd # ef"/imm32
    4551     68/push  _test-input-stream/imm32
    4552     # . . call
    4553     e8/call  write/disp32
    4554     # . . discard args
    4555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4556     # eax = num-bytes(_test-input-stream)
    4557     # . . push args
    4558     68/push  _test-input-stream/imm32
    4559     # . . call
    4560     e8/call  num-bytes/disp32
    4561     # . . discard args
    4562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4563     # check-ints-equal(eax, 2, msg)
    4564     # . . push args
    4565     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
    4566     68/push  2/imm32/true
    4567     50/push-eax
    4568     # . . call
    4569     e8/call  check-ints-equal/disp32
    4570     # . . discard args
    4571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4572     # . epilogue
    4573     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4574     5d/pop-to-ebp
    4575     c3/return
    4576
    4577 test-num-bytes-handles-imm32:
    4578     # if a word has the /imm32 metadata, count it as 4 bytes
    4579     # . prologue
    4580     55/push-ebp
    4581     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    4582     # setup
    4583     # . clear-stream(_test-input-stream)
    4584     # . . push args
    4585     68/push  _test-input-stream/imm32
    4586     # . . call
    4587     e8/call  clear-stream/disp32
    4588     # . . discard args
    4589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4590     # . clear-stream(_test-output-stream)
    4591     # . . push args
    4592     68/push  _test-output-stream/imm32
    4593     # . . call
    4594     e8/call  clear-stream/disp32
    4595     # . . discard args
    4596     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4597     # initialize input
    4598     # . write(_test-input-stream, "ab cd/imm32 ef")
    4599     # . . push args
    4600     68/push  "ab cd/imm32 ef"/imm32
    4601     68/push  _test-input-stream/imm32
    4602     # . . call
    4603     e8/call  write/disp32
    4604     # . . discard args
    4605     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    4606     # eax = num-bytes(_test-input-stream)
    4607     # . . push args
    4608     68/push  _test-input-stream/imm32
    4609     # . . call
    4610     e8/call  num-bytes/disp32
    4611     # . . discard args
    4612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    4613     # check-ints-equal(eax, 6, msg)
    4614     # . . push args
    4615     68/push  "F - test-num-bytes-handles-imm32"/imm32
    4616     68/push  6/imm32/true
    4617     50/push-eax
    4618     # . . call
    4619     e8/call  check-ints-equal/disp32
    4620     # . . discard args
    4621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    4622     # . epilogue
    4623     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    4624     5d/pop-to-ebp
    4625     c3/return
    4626
    4627 == data
    4628
    4629 # This block of bytes gets copied to the start of the output ELF file, with
    4630 # some fields (the ones with labels capitalized) filled in.
    4631 # http://www.sco.com/developers/gabi/latest/ch4.eheader.html
    4632 Elf_header:
    4633   # - size
    4634   0x34/imm32
    4635   # - data
    4636 $e_ident:
    4637   7f 45/E 4c/L 46/F
    4638   01/32-bit  01/little-endian  01/file-version  00/no-os-extensions
    4639   00 00 00 00 00 00 00 00  # 8 bytes of padding
    4640 $e_type:
    4641   02 00
    4642 $e_machine:
    4643   03 00
    4644 $e_version:
    4645   1/imm32
    4646 $Elf_e_entry:
    4647   0x09000000/imm32  # approximate default; must be updated
    4648 $e_phoff:
    4649   0x34/imm32  # offset for the 'program header table' containing segment headers
    4650 $e_shoff:
    4651   0/imm32  # no sections
    4652 $e_flags:
    4653   0/imm32  # unused
    4654 $e_ehsize:
    4655   0x34 00
    4656 $e_phentsize:
    4657   0x20 00
    4658 $Elf_e_phnum:
    4659   00 00  # number of segments; must be updated
    4660 $e_shentsize:
    4661   00 00  # no sections
    4662 $e_shnum:
    4663   00 00
    4664 $e_shstrndx:
    4665   00 00
    4666
    4667 # This block of bytes gets copied after the Elf_header once for each segment.
    4668 # Some fields need filling in each time.
    4669 # https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html
    4670 Elf_program_header_entry:
    4671   # - size
    4672   0x20/imm32
    4673   # - data
    4674 $p_type:
    4675   1/imm32/PT_LOAD
    4676 $Elf_p_offset:
    4677   0/imm32  # byte offset in the file at which a segment begins; must be updated
    4678 $Elf_p_vaddr:
    4679   0/imm32  # starting address to store the segment at before running the program
    4680 $Elf_p_paddr:
    4681   0/imm32  # should have same value as $Elf_p_vaddr
    4682 $Elf_p_filesz:
    4683   0/imm32
    4684 $Elf_p_memsz:
    4685   0/imm32  # should have same value as $Elf_p_filesz
    4686 $Elf_p_flags:
    4687   6/imm32/rw-  # read/write/execute permissions for the segment; must be updated for the code segment
    4688 $p_align:
    4689   # we hold this constant; changing it will require adjusting the way we
    4690   # compute the starting address for each segment
    4691   0x1000/imm32
    4692
    4693 # . . vim:nowrap:textwidth=0