From 3350c34a74844e21ea69077e01efff3bae64bdcd Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Tue, 23 Mar 2021 17:31:08 -0700 Subject: . --- html/linux/survey_elf.subx.html | 4039 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 4039 insertions(+) create mode 100644 html/linux/survey_elf.subx.html (limited to 'html/linux/survey_elf.subx.html') diff --git a/html/linux/survey_elf.subx.html b/html/linux/survey_elf.subx.html new file mode 100644 index 00000000..9136545c --- /dev/null +++ b/html/linux/survey_elf.subx.html @@ -0,0 +1,4039 @@ + + + + +Mu - linux/survey_elf.subx + + + + + + + + + + +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
+
+ + + -- cgit 1.4.1-2-gfad0