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 subx-params.subx survey_elf.subx  -o 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     25/and-eax-with  0xff/imm32
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     05/add-to-eax  0x14/imm32
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