https://github.com/akkartik/mu/blob/master/subx/apps/survey.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 segment headers with addresses and offsets correctly filled in
   6 #
   7 # To build (from the subx/ directory):
   8 #   $ ./subx translate *.subx apps/survey.subx -o apps/survey
   9 #
  10 # The expected input is a stream of bytes with segment headers, comments and
  11 # 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  |./subx run apps/assort
  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 == code
  36 #   instruction                     effective address                                                   register    displacement    immediate
  37 # . op          subop               mod             rm32          base        index         scale       r32
  38 # . 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
  39 
  40 Entry:
  41     # Heap = new-segment(64KB)
  42     # . . push args
  43     68/push  Heap/imm32
  44     68/push  0x10000/imm32/64KB
  45     # . . call
  46     e8/call  new-segment/disp32
  47     # . . discard args
  48     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  49     # initialize-trace-stream(256KB)
  50     # . . push args
  51     68/push  0x40000/imm32/256KB
  52     # . . call
  53     e8/call  initialize-trace-stream/disp32
  54     # . . discard args
  55     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
  56 
  57     # run tests if necessary, convert stdin if not
  58     # . prolog
  59     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  60     # initialize heap
  61     # - if argc > 1 and argv[1] == "test", then return run_tests()
  62     # . argc > 1
  63     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  64     7e/jump-if-lesser-or-equal  $run-main/disp8
  65     # . argv[1] == "test"
  66     # . . push args
  67     68/push  "test"/imm32
  68     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  69     # . . call
  70     e8/call  kernel-string-equal?/disp32
  71     # . . discard args
  72     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  73     # . check result
  74     3d/compare-EAX-and  1/imm32
  75     75/jump-if-not-equal  $run-main/disp8
  76     # . run-tests()
  77     e8/call  run-tests/disp32
  78     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  79     eb/jump  $main:end/disp8
  80 $run-main:
  81     # - otherwise convert stdin
  82     # var ed/EAX : exit-descriptor
  83     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  84     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  85     # configure ed to really exit()
  86     # . ed->target = 0
  87     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  88     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  89     # . . push args
  90     50/push-EAX/ed
  91     68/push  Stderr/imm32
  92     68/push  Stdout/imm32
  93     68/push  Stdin/imm32
  94     # . . call
  95     e8/call  convert/disp32
  96     # . . discard args
  97     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  98     # . syscall(exit, 0)
  99     bb/copy-to-EBX  0/imm32
 100 $main:end:
 101     b8/copy-to-EAX  1/imm32/exit
 102     cd/syscall  0x80/imm8
 103 
 104 # data structures:
 105 #   segment-info: {address, file-offset, size}            (12 bytes)
 106 #   segments: (address stream {string, segment-info})     (16 bytes per row)
 107 #   label-info: {segment-name, segment-offset, address}   (12 bytes)
 108 #   labels: (address stream {string, label-info})         (16 bytes per row)
 109 # these are all inefficient; use sequential scans for lookups
 110 
 111 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
 112     # pseudocode
 113     #   var segments = new-stream(10 rows, 16 bytes each)
 114     #   var labels = new-stream(512 rows, 16 bytes each)
 115     #   compute-offsets(in, segments, labels)
 116     #   compute-addresses(segments, labels)
 117     #   rewind-stream(in)
 118     #   emit-output(in, out, segments, labels)
 119     #
 120     # . prolog
 121     55/push-EBP
 122     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 123     # . save registers
 124     51/push-ECX
 125     52/push-EDX
 126     # var segments/ECX = stream(10 * 16)
 127     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
 128     68/push  0xa0/imm32/length
 129     68/push  0/imm32/read
 130     68/push  0/imm32/write
 131     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 132     # var labels/EDX = stream(512 * 16)
 133     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
 134     68/push  0x2000/imm32/length
 135     68/push  0/imm32/read
 136     68/push  0/imm32/write
 137     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 138 +--  9 lines: #?     # write(2/stderr, "compute-offsets\n") --------------------------------------------------------------------------------------------------
 147     # compute-offsets(in, segments, labels)
 148     # . . push args
 149     52/push-EDX
 150     51/push-ECX
 151     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 152     # . . call
 153     e8/call  compute-offsets/disp32
 154     # . . discard args
 155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 156 +--  9 lines: #?     # write(2/stderr, "compute-addresses\n") ------------------------------------------------------------------------------------------------
 165     # compute-addresses(segments, labels)
 166     # . . push args
 167     52/push-EDX
 168     51/push-ECX
 169     # . . call
 170     e8/call  compute-addresses/disp32
 171     # . . discard args
 172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
 173     # rewind-stream(in)
 174     # . . push args
 175     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 176     # . . call
 177     e8/call  rewind-stream/disp32
 178     # . . discard args
 179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 180 +--  9 lines: #?     # write(2/stderr, "emit-output\n") ------------------------------------------------------------------------------------------------------
 189 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
 215 +-- 46 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 261     # emit-output(in, out, segments, labels)
 262     # . . push args
 263     52/push-EDX
 264     51/push-ECX
 265     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 266     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 267     # . . call
 268     e8/call  emit-output/disp32
 269     # . . discard args
 270     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 271 $convert:end:
 272     # . reclaim locals
 273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 274     # . restore registers
 275     5a/pop-to-EDX
 276     59/pop-to-ECX
 277     # . epilog
 278     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 279     5d/pop-to-EBP
 280     c3/return
 281 
 282 test-convert-computes-addresses:
 283     # input:
 284     #   == code 0x1
 285     #   Entry:
 286     #   ab x/imm32
 287     #   == data 0x1000
 288     #   x:
 289     #     01
 290     #
 291     # trace contains (in any order):
 292     #   label x is at address 0x1079
 293     #   segment code starts at address 0x74
 294     #   segment code has size 5
 295     #   segment data starts at address 0x1079
 296     #
 297     # . prolog
 298     55/push-EBP
 299     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 300     # setup
 301     # . clear-stream(_test-input-stream)
 302     # . . push args
 303     68/push  _test-input-stream/imm32
 304     # . . call
 305     e8/call  clear-stream/disp32
 306     # . . discard args
 307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 308     # . clear-stream(_test-input-buffered-file+4)
 309     # . . push args
 310     b8/copy-to-EAX  _test-input-buffered-file/imm32
 311     05/add-to-EAX  4/imm32
 312     50/push-EAX
 313     # . . call
 314     e8/call  clear-stream/disp32
 315     # . . discard args
 316     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 317     # . clear-stream(_test-output-stream)
 318     # . . push args
 319     68/push  _test-output-stream/imm32
 320     # . . call
 321     e8/call  clear-stream/disp32
 322     # . . discard args
 323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 324     # . clear-stream(_test-output-buffered-file+4)
 325     # . . push args
 326     b8/copy-to-EAX  _test-output-buffered-file/imm32
 327     05/add-to-EAX  4/imm32
 328     50/push-EAX
 329     # . . call
 330     e8/call  clear-stream/disp32
 331     # . . discard args
 332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 333     # initialize input
 334     # . write(_test-input-stream, "== code 0x1\n")
 335     # . . push args
 336     68/push  "== code 0x1\n"/imm32
 337     68/push  _test-input-stream/imm32
 338     # . . call
 339     e8/call  write/disp32
 340     # . . discard args
 341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 342     # . write(_test-input-stream, "Entry:\n")
 343     # . . push args
 344     68/push  "Entry:\n"/imm32
 345     68/push  _test-input-stream/imm32
 346     # . . call
 347     e8/call  write/disp32
 348     # . . discard args
 349     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 350     # . write(_test-input-stream, "ab x/imm32\n")
 351     # . . push args
 352     68/push  "ab x/imm32\n"/imm32
 353     68/push  _test-input-stream/imm32
 354     # . . call
 355     e8/call  write/disp32
 356     # . . discard args
 357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 358     # . write(_test-input-stream, "== data 0x1000\n")
 359     # . . push args
 360     68/push  "== data 0x1000\n"/imm32
 361     68/push  _test-input-stream/imm32
 362     # . . call
 363     e8/call  write/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 366     # . write(_test-input-stream, "x:\n")
 367     # . . push args
 368     68/push  "x:\n"/imm32
 369     68/push  _test-input-stream/imm32
 370     # . . call
 371     e8/call  write/disp32
 372     # . . discard args
 373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 374     # . write(_test-input-stream, "01\n")
 375     # . . push args
 376     68/push  "01\n"/imm32
 377     68/push  _test-input-stream/imm32
 378     # . . call
 379     e8/call  write/disp32
 380     # . . discard args
 381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 382     # convert(_test-input-buffered-file, _test-output-buffered-file)
 383     # . . push args
 384     68/push  _test-output-buffered-file/imm32
 385     68/push  _test-input-buffered-file/imm32
 386     # . . call
 387     e8/call  convert/disp32
 388     # . . discard args
 389     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 390     # check trace
 391 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
 417     # . check-trace-contains("label 'x' is at address 0x00001079.", msg)
 418     # . . push args
 419     68/push  "F - test-convert-computes-addresses/0"/imm32
 420     68/push  "label 'x' is at address 0x00001079."/imm32
 421     # . . call
 422     e8/call  check-trace-contains/disp32
 423     # . . discard args
 424     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 425     # . check-trace-contains("segment 'code' starts at address 0x00000074.", msg)
 426     # . . push args
 427     68/push  "F - test-convert-computes-addresses/1"/imm32
 428     68/push  "segment 'code' starts at address 0x00000074."/imm32
 429     # . . call
 430     e8/call  check-trace-contains/disp32
 431     # . . discard args
 432     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 433     # . check-trace-contains("segment 'code' has size 0x00000005.", msg)
 434     # . . push args
 435     68/push  "F - test-convert-computes-addresses/2"/imm32
 436     68/push  "segment 'code' has size 0x00000005."/imm32
 437     # . . call
 438     e8/call  check-trace-contains/disp32
 439     # . . discard args
 440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 441     # . check-trace-contains("segment 'data' starts at address 0x00001079.", msg)
 442     # . . push args
 443     68/push  "F - test-convert-computes-addresses/3"/imm32
 444     68/push  "segment 'data' starts at address 0x00001079."/imm32
 445     # . . call
 446     e8/call  check-trace-contains/disp32
 447     # . . discard args
 448     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 449     # . epilog
 450     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 451     5d/pop-to-EBP
 452     c3/return
 453 
 454 # global scratch space for compute-offsets in the data segment
 455 == data
 456 
 457 compute-offsets:file-offset:  # int
 458   0/imm32
 459 compute-offsets:segment-offset:  # int
 460   0/imm32
 461 compute-offsets:word-slice:
 462   0/imm32/start
 463   0/imm32/end
 464 compute-offsets:segment-tmp:  # slice
 465   0/imm32/start
 466   0/imm32/end
 467 
 468 == code
 469 
 470 compute-offsets:  # in : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
 471     # skeleton:
 472     #   for lines in 'in'
 473     #     for words in line
 474     #       switch word
 475     #         case 1
 476     #         case 2
 477     #         ...
 478     #         default
 479     #
 480     # pseudocode:
 481     #   curr-segment-name : (address string) = 0
 482     #   var line = new-stream(512, 1)
 483     #   while true                                  # line loop
 484     #     clear-stream(line)
 485     #     read-line-buffered(in, line)
 486     #     if (line->write == 0) break               # end of file
 487     #     while true                                # word loop
 488     #       word-slice = next-word(line)
 489     #       if slice-empty?(word-slice)             # end of line
 490     #         break
 491     #       else if slice-starts-with?(word-slice, "#")  # comment
 492     #         continue
 493     #       else if slice-equal?(word-slice, "==")
 494     #         if curr-segment-name != 0
 495     #           seg = get-or-insert(segments, curr-segment-name)
 496     #           seg->size = *file-offset - seg->file-offset
 497     #           trace("segment '", curr-segment-name, "' has size ", seg->size)
 498     #         segment-tmp = next-word(line)
 499     #         curr-segment-name = slice-to-string(segment-tmp)
 500     #         if empty?(curr-segment-name)
 501     #           abort
 502     #         segment-tmp = next-word(line)
 503     #         if slice-empty?(segment-tmp)
 504     #           abort
 505     #         seg = get-or-insert(segments, curr-segment-name)
 506     #         seg->starting-address = parse-hex-int(segment-tmp)
 507     #         seg->file-offset = *file-offset
 508     #         trace("segment '", curr-segment-name, "' is at file offset ", seg->file-offset)
 509     #         segment-offset = 0
 510     #         break  (next line)
 511     #       else if is-label?(word-slice)
 512     #         strip trailing ':' from word-slice
 513     #         x : (address label-info) = get-or-insert(labels, name)
 514     #         x->segment-name = curr-segment-name
 515     #         trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
 516     #         x->segment-offset = segment-offset
 517     #         trace("label '", word-slice, "' is at segment offset ", segment-offset, ".")
 518     #         # labels occupy no space, so no need to increment offsets
 519     #       else
 520     #         width = compute-width-of-slice(word-slice)
 521     #         *segment-offset += width
 522     #         *file-offset += width
 523     #
 524     # . prolog
 525     55/push-EBP
 526     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 527     # . save registers
 528     50/push-EAX
 529     51/push-ECX
 530     52/push-EDX
 531     53/push-EBX
 532     56/push-ESI
 533     57/push-EDI
 534     # curr-segment-name/ESI = 0
 535     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
 536     # file-offset = 0
 537     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:file-offset/disp32  0/imm32               # copy to *compute-offsets:word-slice
 538     # segment-offset = 0
 539     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:segment-offset/disp32  0/imm32            # copy to *compute-offsets:word-slice
 540     # line/ECX = new-stream(512, 1)
 541     # . EAX = new-stream(512, 1)
 542     # . . push args
 543     68/push  1/imm32
 544     68/push  0x200/imm32
 545     68/push  Heap/imm32
 546     # . . call
 547     e8/call  new-stream/disp32
 548     # . . discard args
 549     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 550     # . line/ECX = EAX
 551     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
 552 $compute-offsets:line-loop:
 553     # clear-stream(line/ECX)
 554     51/push-ECX
 555     e8/call  clear-stream/disp32
 556     # . discard args
 557     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 558     # read-line-buffered(in, line/ECX)
 559     51/push-ECX
 560     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 561     e8/call  read-line-buffered/disp32
 562     # . discard args
 563     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 564     # if (line->write == 0) break
 565     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
 566     3d/compare-EAX-and  0/imm32
 567     0f 84/jump-if-equal  $compute-offsets:break-line-loop/disp32
 568 $compute-offsets:word-loop:
 569     # EDX = word-slice
 570     ba/copy-to-EDX  compute-offsets:word-slice/imm32
 571     # next-word(line/ECX, word-slice/EDX)
 572     52/push-EDX
 573     51/push-ECX
 574     e8/call  next-word/disp32
 575     # . discard args
 576     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 577 +-- 80 lines: #?     # dump word-slice and maybe curr-segment-name -------------------------------------------------------------------------------------------
 657 $compute-offsets:check0:
 658     # if slice-empty?(word/EDX) break
 659     # . EAX = slice-empty?(word/EDX)
 660     52/push-EDX
 661     e8/call  slice-empty?/disp32
 662     # . . discard args
 663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 664     # . if (EAX != 0) break
 665     3d/compare-EAX-and  0/imm32
 666     0f 85/jump-if-not-equal  $compute-offsets:line-loop/disp32
 667     # if slice-starts-with?(word-slice, "#") continue
 668     68/push  "#"/imm32
 669     52/push-EDX
 670     e8/call  slice-starts-with?/disp32
 671     # . . discard args
 672     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 673     # . if (EAX != 0) continue
 674     3d/compare-EAX-and  0/imm32
 675     0f 85/jump-if-not-equal  $compute-offsets:word-loop/disp32
 676 $compute-offsets:case-segment-header:
 677     # if (!slice-equal?(word-slice/EDX, "==")) goto next case
 678     # . EAX = slice-equal?(word-slice/EDX, "==")
 679     68/push  "=="/imm32
 680     52/push-EDX
 681     e8/call  slice-equal?/disp32
 682     # . . discard args
 683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 684     # . if (EAX == 0) goto next case
 685     3d/compare-EAX-and  0/imm32
 686     0f 84/jump-if-equal  $compute-offsets:case-label/disp32
 687     # if (curr-segment-name == 0) goto construct-next-segment
 688     81          7/subop/compare     3/mod/direct    6/rm32/ESI    .           .             .           .           .               0/imm32           # compare ESI
 689     74/jump-if-equal  $compute-offsets:construct-next-segment/disp8
 690     # seg/EAX = get-or-insert(segments, curr-segment-name, row-size=16)
 691     # . . push args
 692     68/push  0x10/imm32/row-size
 693     56/push-ESI
 694     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 695     # . . call
 696     e8/call  get-or-insert/disp32
 697     # . . discard args
 698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 699     # seg->size = file-offset - seg->file-offset
 700     # . save ECX
 701     51/push-ECX
 702     # . EBX = *file-offset
 703     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:file-offset/disp32 # copy *file-offset to EBX
 704     # . ECX = seg->file-offset
 705     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EAX+4) to ECX
 706     # . EBX -= ECX
 707     29/subtract                     3/mod/direct    3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EBX
 708     # . seg->size = EBX
 709     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy EBX to *(EAX+8)
 710     # . restore ECX
 711     59/pop-to-ECX
 712     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
 713     # . . push args
 714     68/push  "."/imm32
 715     53/push-EBX
 716     68/push  "' has size "/imm32
 717     56/push-ESI
 718     68/push  "segment '"/imm32
 719     # . . call
 720     e8/call  trace-sssns/disp32
 721     # . . discard args
 722     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
 723 $compute-offsets:construct-next-segment:
 724     # next-word(line/ECX, segment-tmp)
 725     68/push  compute-offsets:segment-tmp/imm32
 726     51/push-ECX
 727     e8/call  next-word/disp32
 728     # . discard args
 729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 730 +-- 47 lines: #?     # dump curr-segment-name if not null (clobbering EAX) -----------------------------------------------------------------------------------
 777 $compute-offsets:update-curr-segment-name:
 778     # curr-segment-name = slice-to-string(segment-tmp)
 779     # . EAX = slice-to-string(Heap, segment-tmp)
 780     # . . push args
 781     68/push  compute-offsets:segment-tmp/imm32
 782     68/push  Heap/imm32
 783     # . . call
 784     e8/call  slice-to-string/disp32
 785     # . . discard args
 786     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 787     # . curr-segment-name = EAX
 788     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
 789     # if empty?(curr-segment-name) abort
 790     # . if (EAX == 0) abort
 791     3d/compare-EAX-and  0/imm32
 792     0f 84/jump-if-equal  $compute-offsets:abort/disp32
 793     # next-word(line/ECX, segment-tmp)
 794     68/push  compute-offsets:segment-tmp/imm32
 795     51/push-ECX
 796     e8/call  next-word/disp32
 797     # . discard args
 798     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 799     # if slice-empty?(segment-tmp) abort
 800     # . EAX = slice-empty?(segment-tmp)
 801     68/push  compute-offsets:segment-tmp/imm32
 802     e8/call  slice-empty?/disp32
 803     # . . discard args
 804     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 805     # . if (EAX != 0) abort
 806     3d/compare-EAX-and  0/imm32
 807     0f 85/jump-if-not-equal  $compute-offsets:abort/disp32
 808     # seg/EBX = get-or-insert(segments, curr-segment-name, row-size=16)
 809     # . . push args
 810     68/push  0x10/imm32/row-size
 811     56/push-ESI
 812     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 813     # . . call
 814     e8/call  get-or-insert/disp32
 815     # . . discard args
 816     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 817     # . EBX = EAX
 818     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
 819     # seg->address = parse-hex-int(segment-tmp)
 820     # . EAX = parse-hex-int(segment-tmp)
 821     68/push  compute-offsets:segment-tmp/imm32
 822     e8/call  parse-hex-int/disp32
 823     # . . discard args
 824     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 825     # . seg->address = EAX
 826     89/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EBX
 827     # seg->file-offset = *file-offset
 828     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:file-offset/disp32 # copy *file-offset to EAX
 829     89/copy                         1/mod/*+disp8   3/rm32/EBX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EBX+4)
 830     # trace-sssns("segment '", curr-segment-name, "' is at file offset ", seg->file-offset, "")
 831     # . . push args
 832     68/push  "."/imm32
 833     50/push-EAX
 834     68/push  "' is at file offset "/imm32
 835     56/push-ESI
 836     68/push  "segment '"/imm32
 837     # . . call
 838     e8/call  trace-sssns/disp32
 839     # . . discard args
 840     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
 841     # segment-offset = 0
 842     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     compute-offsets:segment-offset/disp32  0/imm32           # copy to *segment-offset
 843     # break
 844     e9/jump $compute-offsets:line-loop/disp32
 845 $compute-offsets:case-label:
 846     # if (!is-label?(word-slice/EDX)) goto next case
 847     # . EAX = is-label?(word-slice/EDX)
 848     # . . push args
 849     52/push-EDX
 850     # . . call
 851     e8/call  is-label?/disp32
 852     # . . discard args
 853     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 854     # . if (EAX == 0) goto next case
 855     3d/compare-EAX-and  0/imm32
 856     74/jump-if-equal  $compute-offsets:case-default/disp8
 857     # strip trailing ':' from word-slice
 858     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # decrement *(EDX+4)
 859     # x/EAX = leaky-get-or-insert-slice(labels, word-slice, row-size=16)
 860     # . . push args
 861     68/push  0x10/imm32/row-size
 862     52/push-EDX
 863     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 864     # . . call
 865     e8/call  leaky-get-or-insert-slice/disp32
 866     # . . discard args
 867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 868 $compute-offsets:save-label-offset:
 869     # x->segment-name = curr-segment-name
 870     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy ESI to *EAX
 871     # trace-slsss("label '" word-slice/EDX "' is in segment '" current-segment-name "'.")
 872     # . . push args
 873     68/push  "'."/imm32
 874     56/push-ESI
 875     68/push  "' is in segment '"/imm32
 876     52/push-EDX
 877     68/push  "label '"/imm32
 878     # . . call
 879     e8/call  trace-slsss/disp32
 880     # . . discard args
 881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
 882     # x->segment-offset = segment-offset
 883     # . EBX = segment-offset
 884     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:segment-offset/disp32  # copy *segment-offset to EBX
 885     # . x->segment-offset = EBX
 886     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EAX+4)
 887     # trace-slsns("label '" word-slice/EDX "' is at segment offset " *segment-offset/EAX ".")
 888     # . . EAX = file-offset
 889     b8/copy-to-EAX compute-offsets:segment-offset/imm32
 890     # . . EAX = *file-offset/EAX
 891     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
 892     # . . push args
 893     68/push  "."/imm32
 894     50/push-EAX
 895     68/push  "' is at segment offset "/imm32
 896     52/push-EDX
 897     68/push  "label '"/imm32
 898     # . . call
 899     e8/call  trace-slsns/disp32
 900     # . . discard args
 901     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
 902     # continue
 903     e9/jump  $compute-offsets:word-loop/disp32
 904 $compute-offsets:case-default:
 905     # width/EAX = compute-width-of-slice(word-slice)
 906     # . . push args
 907     52/push-EDX
 908     # . . call
 909     e8/call compute-width-of-slice/disp32
 910     # . . discard args
 911     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 912     # segment-offset += width
 913     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:segment-offset/disp32 # add EAX to *segment-offset
 914     # file-offset += width
 915     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:file-offset/disp32 # add EAX to *file-offset
 916 +-- 47 lines: #?     # dump segment-offset -------------------------------------------------------------------------------------------------------------------
 963     e9/jump $compute-offsets:word-loop/disp32
 964 $compute-offsets:break-line-loop:
 965     # seg/EAX = get-or-insert(segments, curr-segment-name, row-size=16)
 966     # . . push args
 967     68/push  0x10/imm32/row-size
 968     56/push-ESI
 969     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 970     # . . call
 971     e8/call  get-or-insert/disp32
 972     # . . discard args
 973     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 974     # seg->size = file-offset - seg->file-offset
 975     # . save ECX
 976     51/push-ECX
 977     # . EBX = *file-offset
 978     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:file-offset/disp32 # copy *file-offset to EBX
 979     # . ECX = seg->file-offset
 980     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EAX+4) to ECX
 981     # . EBX -= ECX
 982     29/subtract                     3/mod/direct    3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EBX
 983     # . seg->size = EBX
 984     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy EBX to *(EAX+8)
 985     # . restore ECX
 986     59/pop-to-ECX
 987     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
 988     # . trace-sssns("segment '", curr-segment-name, "' has size ", EBX, ".")
 989     # . . push args
 990     68/push  "."/imm32
 991     53/push-EBX
 992     68/push  "' has size "/imm32
 993     56/push-ESI
 994     68/push  "segment '"/imm32
 995     # . . call
 996     e8/call  trace-sssns/disp32
 997     # . . discard args
 998     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
 999 $compute-offsets:end:
1000     # . reclaim locals
1001     # . restore registers
1002     5f/pop-to-EDI
1003     5e/pop-to-ESI
1004     5b/pop-to-EBX
1005     5a/pop-to-EDX
1006     59/pop-to-ECX
1007     58/pop-to-EAX
1008     # . epilog
1009     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1010     5d/pop-to-EBP
1011     c3/return
1012 $compute-offsets:abort:
1013     # . _write(2/stderr, error)
1014     # . . push args
1015     68/push  "'==' must be followed by segment name and segment-start\n"/imm32
1016     68/push  2/imm32/stderr
1017     # . . call
1018     e8/call  _write/disp32
1019     # . . discard args
1020     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1021     # . syscall(exit, 1)
1022     bb/copy-to-EBX  1/imm32
1023     b8/copy-to-EAX  1/imm32/exit
1024     cd/syscall  0x80/imm8
1025     # never gets here
1026 
1027 test-compute-offsets:
1028     # input:
1029     #   == code 0x1
1030     #   ab x/imm32
1031     #   == data 0x1000
1032     #   00
1033     #   x:
1034     #     34
1035     #
1036     # trace contains (in any order):
1037     #   segment 'code' is at file offset 0x0.
1038     #   segment 'code' has size 0x5.
1039     #   segment 'data' is at file offset 0x5.
1040     #   segment 'data' has size 0x2.
1041     #   label 'x' is in segment 'data'.
1042     #   label 'x' is at segment offset 0x1.
1043     #
1044     # . prolog
1045     55/push-EBP
1046     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1047     # setup
1048     # . clear-stream(_test-input-stream)
1049     # . . push args
1050     68/push  _test-input-stream/imm32
1051     # . . call
1052     e8/call  clear-stream/disp32
1053     # . . discard args
1054     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1055     # . clear-stream(_test-input-buffered-file+4)
1056     # . . push args
1057     b8/copy-to-EAX  _test-input-buffered-file/imm32
1058     05/add-to-EAX  4/imm32
1059     50/push-EAX
1060     # . . call
1061     e8/call  clear-stream/disp32
1062     # . . discard args
1063     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1064     # . clear-stream(_test-output-stream)
1065     # . . push args
1066     68/push  _test-output-stream/imm32
1067     # . . call
1068     e8/call  clear-stream/disp32
1069     # . . discard args
1070     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1071     # . clear-stream(_test-output-buffered-file+4)
1072     # . . push args
1073     b8/copy-to-EAX  _test-output-buffered-file/imm32
1074     05/add-to-EAX  4/imm32
1075     50/push-EAX
1076     # . . call
1077     e8/call  clear-stream/disp32
1078     # . . discard args
1079     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1080     # var segments/ECX = stream(2 * 16)
1081     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x20/imm32        # subtract from ESP
1082     68/push  0x20/imm32/length
1083     68/push  0/imm32/read
1084     68/push  0/imm32/write
1085     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1086     # var labels/EDX = stream(2 * 16)
1087     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x20/imm32        # subtract from ESP
1088     68/push  0x20/imm32/length
1089     68/push  0/imm32/read
1090     68/push  0/imm32/write
1091     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
1092     # initialize input
1093     # . write(_test-input-stream, "== code 0x1\n")
1094     # . . push args
1095     68/push  "== code 0x1\n"/imm32
1096     68/push  _test-input-stream/imm32
1097     # . . call
1098     e8/call  write/disp32
1099     # . . discard args
1100     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1101     # . write(_test-input-stream, "ab x/imm32\n")
1102     # . . push args
1103     68/push  "ab x/imm32\n"/imm32
1104     68/push  _test-input-stream/imm32
1105     # . . call
1106     e8/call  write/disp32
1107     # . . discard args
1108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1109     # . write(_test-input-stream, "== data 0x1000\n")
1110     # . . push args
1111     68/push  "== data 0x1000\n"/imm32
1112     68/push  _test-input-stream/imm32
1113     # . . call
1114     e8/call  write/disp32
1115     # . . discard args
1116     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1117     # . write(_test-input-stream, "00\n")
1118     # . . push args
1119     68/push  "00\n"/imm32
1120     68/push  _test-input-stream/imm32
1121     # . . call
1122     e8/call  write/disp32
1123     # . . discard args
1124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1125     # . write(_test-input-stream, "x:\n")
1126     # . . push args
1127     68/push  "x:\n"/imm32
1128     68/push  _test-input-stream/imm32
1129     # . . call
1130     e8/call  write/disp32
1131     # . . discard args
1132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1133     # . write(_test-input-stream, "34\n")
1134     # . . push args
1135     68/push  "34\n"/imm32
1136     68/push  _test-input-stream/imm32
1137     # . . call
1138     e8/call  write/disp32
1139     # . . discard args
1140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1141     # compute-offsets(_test-input-buffered-file, segments, labels)
1142     # . . push args
1143     52/push-EDX
1144     51/push-ECX
1145     68/push  _test-input-buffered-file/imm32
1146     # . . call
1147     e8/call  compute-offsets/disp32
1148     # . . discard args
1149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32        # add to ESP
1150 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1176     # check trace
1177     # . check-trace-contains("segment 'code' is at file offset 0x00000000.", msg)
1178     # . . push args
1179     68/push  "F - test-compute-offsets/0"/imm32
1180     68/push  "segment 'code' is at file offset 0x00000000."/imm32
1181     # . . call
1182     e8/call  check-trace-contains/disp32
1183     # . . discard args
1184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1185     # . check-trace-contains("segment 'code' has size 0x00000005", msg)
1186     # . . push args
1187     68/push  "F - test-compute-offsets/1"/imm32
1188     68/push  "segment 'code' has size 0x00000005."/imm32
1189     # . . call
1190     e8/call  check-trace-contains/disp32
1191     # . . discard args
1192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1193     # . check-trace-contains("segment 'data' is at file offset 0x00000005.", msg)
1194     # . . push args
1195     68/push  "F - test-compute-offsets/2"/imm32
1196     68/push  "segment 'data' is at file offset 0x00000005."/imm32
1197     # . . call
1198     e8/call  check-trace-contains/disp32
1199     # . . discard args
1200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1201     # . check-trace-contains("segment 'data' has size 0x00000002.", msg)
1202     # . . push args
1203     68/push  "F - test-compute-offsets/3"/imm32
1204     68/push  "segment 'data' has size 0x00000002."/imm32
1205     # . . call
1206     e8/call  check-trace-contains/disp32
1207     # . . discard args
1208     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1209     # . check-trace-contains("label 'x' is in segment 'data'.", msg)
1210     # . . push args
1211     68/push  "F - test-compute-offsets/4"/imm32
1212     68/push  "label 'x' is in segment 'data'."/imm32
1213     # . . call
1214     e8/call  check-trace-contains/disp32
1215     # . . discard args
1216     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1217     # . check-trace-contains("label 'x' is at segment offset 0x00000001.", msg)
1218     # . . push args
1219     68/push  "F - test-compute-offsets/5"/imm32
1220     68/push  "label 'x' is at segment offset 0x00000001."/imm32
1221     # . . call
1222     e8/call  check-trace-contains/disp32
1223     # . . discard args
1224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1225     # . check-ints-equal(labels->write, 0x10, msg)
1226     # . . push args
1227     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
1228     68/push  0x10/imm32/1-entry
1229     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
1230     # . . call
1231     e8/call  check-ints-equal/disp32
1232     # . . discard args
1233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1234     # . epilog
1235     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1236     5d/pop-to-EBP
1237     c3/return
1238 
1239 compute-addresses:  # segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
1240     # pseudocode:
1241     #   srow : (address segment-info) = segments->data
1242     #   max = segments->data + segments->write
1243     #   num-segments = segments->write / 16
1244     #   starting-offset = 0x34 + (num-segments * 0x20)
1245     #   while true
1246     #     if (srow >= max) break
1247     #     s->file-offset += starting-offset
1248     #     s->address &= 0xfffff000  # clear last 12 bits for p_align
1249     #     s->address += (s->file-offset & 0x00000fff)
1250     #     trace-sssns("segment " s->key " starts at address " s->address)
1251     #     srow += 16  # row-size
1252     #   lrow : (address label-info) = labels->data
1253     #   max = labels->data + labels->write
1254     #   while true
1255     #     if (lrow >= max) break
1256     #     seg-name : (address string) = lrow->segment-name
1257     #     label-seg : (address segment-info) = get(segments, seg-name, row-size=16)
1258     #     lrow->address = label-seg->address + lrow->segment-offset
1259     #     trace-sssns("label " lrow->key " is at address " lrow->address)
1260     #     lrow += 16  # row-size
1261     #
1262     # . prolog
1263     55/push-EBP
1264     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1265     # . save registers
1266     50/push-EAX
1267     51/push-ECX
1268     52/push-EDX
1269     53/push-EBX
1270     56/push-ESI
1271     57/push-EDI
1272     # ESI = segments
1273     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
1274     # starting-offset/EDI = 0x34 + (num-segments * 0x20)  # make room for ELF headers
1275     # . EDI = segments->write / 16 (row-size)
1276     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           7/r32/EDI   .               .                 # copy *ESI to EDI
1277     c1/shift    5/subop/logic-right 3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm8            # shift EDI right by 4 bits, while padding zeroes
1278     # . EDI = (EDI * 0x20) + 0x34
1279     c1/shift    4/subop/left        3/mod/direct    7/rm32/EDI    .           .             .           .           .               5/imm8            # shift EDI left by 5 bits
1280     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               0x34/imm32        # add to EDI
1281     # srow/EAX = segments->data
1282     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
1283     # max/ECX = segments->data + segments->write
1284     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
1285     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # add ESI to ECX
1286 $compute-addresses:segment-loop:
1287     # if (srow >= max) break
1288     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
1289     73/jump-if-greater-or-equal-unsigned  $compute-addresses:segment-break/disp8
1290     # srow->file-offset += starting-offset
1291     01/add                          1/mod/*+disp8   0/rm32/EAX    .           .             .           7/r32/EDI   8/disp8         .                 # add EDI to *(EAX+8)
1292     # clear last 12 bits of srow->address for p_align=0x1000
1293     # . EDX = srow->address
1294     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(EAX+4) to EDX
1295     # . EDX &= 0xfffff000
1296     81          4/subop/and         3/mod/direct    2/rm32/EDX    .           .             .           .           .               0xfffff000/imm32  # bitwise and of EDX
1297     # update last 12 bits from srow->file-offset
1298     # . EBX = srow->file-offset
1299     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(EAX+8) to EBX
1300     # . EBX &= 0xfff
1301     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x00000fff/imm32  # bitwise and of EBX
1302     # . srow->address = EDX | EBX
1303     09/or                           3/mod/direct    2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # EDX = bitwise OR with EBX
1304     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy EDX to *(EAX+4)
1305     # trace-sssns("segment " srow " starts at address " srow->address ".")
1306     # . . push args
1307     68/push  "."/imm32
1308     52/push-EDX
1309     68/push  "' starts at address "/imm32
1310     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
1311     68/push  "segment '"/imm32
1312     # . . call
1313     e8/call  trace-sssns/disp32
1314     # . . discard args
1315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1316     # srow += 16  # size of row
1317     05/add-to-EAX  0x10/imm32
1318     eb/jump  $compute-addresses:segment-loop/disp8
1319 $compute-addresses:segment-break:
1320 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1346     # ESI = labels
1347     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
1348     # lrow/EAX = labels->data
1349     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
1350     # max/ECX = labels->data + labels->write
1351     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
1352     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # add ESI to ECX
1353 $compute-addresses:label-loop:
1354     # if (lrow >= max) break
1355     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
1356     0f 83/jump-if-greater-or-equal-unsigned  $compute-addresses:end/disp32
1357 +-- 26 lines: #?     # dump lrow->key ------------------------------------------------------------------------------------------------------------------------
1383     # seg-name/EDX = lrow->segment-name
1384     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *EAX to EDX
1385 +-- 26 lines: #?     # dump seg-name -------------------------------------------------------------------------------------------------------------------------
1411     # label-seg/EDX : (address segment-info) = get(segments, seg-name, row-size=16)
1412     # . save EAX
1413     50/push-EAX
1414     # . EAX = get(segments, seg-name, row-size=16)
1415     # . . push args
1416     68/push  0x10/imm32/row-size
1417     52/push-EDX
1418     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1419     # . . call
1420     e8/call  get/disp32
1421     # . . discard args
1422     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1423     # . EDX = EAX
1424     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
1425     # . restore EAX
1426     58/pop-to-EAX
1427     # EBX = label-seg->address
1428     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
1429     # EBX += lrow->segment-offset
1430     03/add                          1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # add *(EAX+8) to EBX
1431     # lrow->address = EBX
1432     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy EBX to *(EAX+12)
1433     # trace-sssns("label " lrow->key " is at address " lrow->address ".")
1434     # . . push args
1435     68/push  "."/imm32
1436     53/push-EBX
1437     68/push  "' is at address "/imm32
1438     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
1439     68/push  "label '"/imm32
1440     # . . call
1441     e8/call  trace-sssns/disp32
1442     # . . discard args
1443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1444     # lrow += 16  # size of row
1445     05/add-to-EAX  0x10/imm32
1446     e9/jump  $compute-addresses:label-loop/disp32
1447 $compute-addresses:end:
1448     # . restore registers
1449     5f/pop-to-EDI
1450     5e/pop-to-ESI
1451     5b/pop-to-EBX
1452     5a/pop-to-EDX
1453     59/pop-to-ECX
1454     58/pop-to-EAX
1455     # . epilog
1456     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1457     5d/pop-to-EBP
1458     c3/return
1459 
1460 test-compute-addresses:
1461     # input:
1462     #   segments:
1463     #     - 'a': {0x1000, 0, 5}
1464     #     - 'b': {0x2018, 5, 1}
1465     #     - 'c': {0x5444, 6, 12}
1466     #   labels:
1467     #     - 'l1': {'a', 3, 0}
1468     #     - 'l2': {'b', 0, 0}
1469     #
1470     # trace contains in any order (comments in parens):
1471     #   segment 'a' starts at address 0x00001094.  (0x34 + 0x20 for each segment)
1472     #   segment 'b' starts at address 0x00002099.  (0x018 discarded)
1473     #   segment 'c' starts at address 0x0000509a.  (0x444 discarded)
1474     #   label 'l1' is at address 0x00001097.       (0x1094 + segment-offset 3)
1475     #   label 'l2' is at address 0x00002099.       (0x2099 + segment-offset 0)
1476     #
1477     # . prolog
1478     55/push-EBP
1479     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1480     # setup
1481     # . var segments/ECX = stream(10 * 16)
1482     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
1483     68/push  0xa0/imm32/length
1484     68/push  0/imm32/read
1485     68/push  0/imm32/write
1486     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1487     # . var labels/EDX = stream(512 * 16)
1488     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
1489     68/push  0x2000/imm32/length
1490     68/push  0/imm32/read
1491     68/push  0/imm32/write
1492     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
1493     # . stream-add4(segments, "a", 0x1000, 0, 5)
1494     68/push  5/imm32/segment-size
1495     68/push  0/imm32/file-offset
1496     68/push  0x1000/imm32/start-address
1497     68/push  "a"/imm32/segment-name
1498     51/push-ECX
1499     # . . call
1500     e8/call  stream-add4/disp32
1501     # . . discard args
1502     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1503     # . stream-add4(segments, "b", 0x2018, 5, 1)
1504     68/push  1/imm32/segment-size
1505     68/push  5/imm32/file-offset
1506     68/push  0x2018/imm32/start-address
1507     68/push  "b"/imm32/segment-name
1508     51/push-ECX
1509     # . . call
1510     e8/call  stream-add4/disp32
1511     # . . discard args
1512     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1513     # . stream-add4(segments, "c", 0x5444, 6, 12)
1514     68/push  0xc/imm32/segment-size
1515     68/push  6/imm32/file-offset
1516     68/push  0x5444/imm32/start-address
1517     68/push  "c"/imm32/segment-name
1518     51/push-ECX
1519     # . . call
1520     e8/call  stream-add4/disp32
1521     # . . discard args
1522     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1523     # . stream-add4(labels, "l1", "a", 3, 0)
1524     68/push  0/imm32/label-address
1525     68/push  3/imm32/segment-offset
1526     68/push  "a"/imm32/segment-name
1527     68/push  "l1"/imm32/label-name
1528     52/push-EDX
1529     # . . call
1530     e8/call  stream-add4/disp32
1531     # . . discard args
1532     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1533     # . stream-add4(labels, "l2", "b", 0, 0)
1534     68/push  0/imm32/label-address
1535     68/push  0/imm32/segment-offset
1536     68/push  "b"/imm32/segment-name
1537     68/push  "l2"/imm32/label-name
1538     52/push-EDX
1539     # . . call
1540     e8/call  stream-add4/disp32
1541     # . . discard args
1542     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1543     # component under test
1544     # . compute-addresses(segments, labels)
1545     # . . push args
1546     52/push-EDX
1547     51/push-ECX
1548     # . . call
1549     e8/call  compute-addresses/disp32
1550     # . . discard args
1551     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1552     # checks
1553 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1579     # . check-trace-contains("segment 'a' starts at address 0x00001094.", msg)
1580     # . . push args
1581     68/push  "F - test-compute-addresses/0"/imm32
1582     68/push  "segment 'a' starts at address 0x00001094."/imm32
1583     # . . call
1584     e8/call  check-trace-contains/disp32
1585     # . . discard args
1586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1587     # . check-trace-contains("segment 'b' starts at address 0x00002099.", msg)
1588     # . . push args
1589     68/push  "F - test-compute-addresses/1"/imm32
1590     68/push  "segment 'b' starts at address 0x00002099."/imm32
1591     # . . call
1592     e8/call  check-trace-contains/disp32
1593     # . . discard args
1594     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1595     # . check-trace-contains("segment 'c' starts at address 0x0000509a.", msg)
1596     # . . push args
1597     68/push  "F - test-compute-addresses/2"/imm32
1598     68/push  "segment 'c' starts at address 0x0000509a."/imm32
1599     # . . call
1600     e8/call  check-trace-contains/disp32
1601     # . . discard args
1602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1603     # . check-trace-contains("label 'l1' is at address 0x00001097.", msg)
1604     # . . push args
1605     68/push  "F - test-compute-addresses/3"/imm32
1606     68/push  "label 'l1' is at address 0x00001097."/imm32
1607     # . . call
1608     e8/call  check-trace-contains/disp32
1609     # . . discard args
1610     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1611     # . check-trace-contains("label 'l2' is at address 0x00002099.", msg)
1612     # . . push args
1613     68/push  "F - test-compute-addresses/4"/imm32
1614     68/push  "label 'l2' is at address 0x00002099."/imm32
1615     # . . call
1616     e8/call  check-trace-contains/disp32
1617     # . . discard args
1618     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1619     # . check-ints-equal(labels->write, 0x20, msg)
1620     # . . push args
1621     68/push  "F - test-compute-addresses-maintains-labels-write-index"/imm32
1622     68/push  0x20/imm32/2-entries
1623     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
1624     # . . call
1625     e8/call  check-ints-equal/disp32
1626     # . . discard args
1627     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1628     # . epilog
1629     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1630     5d/pop-to-EBP
1631     c3/return
1632 
1633 emit-output:  # in : (address buffered-file), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
1634     # pseudocode:
1635     #   emit-headers(out, segments, labels)
1636     #   emit-segments(in, out, segments, labels)
1637     #
1638     # . prolog
1639     55/push-EBP
1640     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1641 +--  9 lines: #?     # write(2/stderr, "emit-headers\n") -----------------------------------------------------------------------------------------------------
1650     # emit-headers(out, segments, labels)
1651     # . . push args
1652     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8       .                # push *(EBP+20)
1653     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8       .                # push *(EBP+16)
1654     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8        .                # push *(EBP+12)
1655     # . . call
1656     e8/call  emit-headers/disp32
1657     # . . discard args
1658     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1659 +--  9 lines: #?     # write(2/stderr, "emit-segments\n") ----------------------------------------------------------------------------------------------------
1668     # emit-segments(in, out, segments, labels)
1669     # . . push args
1670     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8       .                # push *(EBP+20)
1671     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8       .                # push *(EBP+16)
1672     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1673     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1674     # . . call
1675     e8/call  emit-segments/disp32
1676     # . . discard args
1677     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
1678 $emit-output:end:
1679     # . epilog
1680     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1681     5d/pop-to-EBP
1682     c3/return
1683 
1684 emit-segments:  # in : (address buffered-file), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
1685     # pseudocode:
1686     #   var offset-of-next-instruction = 0
1687     #   var line = new-stream(512, 1)
1688     #   line-loop:
1689     #   while true
1690     #     clear-stream(line)
1691     #     read-line-buffered(in, line)
1692     #     if (line->write == 0) break               # end of file
1693     #     offset-of-next-instruction += num-bytes(line)
1694     #     while true
1695     #       var word-slice = next-word(line)
1696     #       if slice-empty?(word-slice)             # end of line
1697     #         break
1698     #       if slice-starts-with?(word-slice, "#")  # comment
1699     #         break
1700     #       if is-label?(word-slice)                # no need for label declarations anymore
1701     #         goto line-loop                        # don't insert empty lines
1702     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
1703     #         goto line-loop                        # don't insert empty lines
1704     #       if length(word-slice) == 2
1705     #         write-slice-buffered(out, word-slice)
1706     #         write-buffered(out, " ")
1707     #         continue
1708     #       datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
1709     #       info = get-slice(labels, datum)
1710     #       if has-metadata?(word-slice, "imm8")
1711     #         abort  # label should never go to imm8
1712     #       else if has-metadata?(word-slice, "imm32")
1713     #         emit(out, info->address, 4)
1714     #       else if has-metadata?(word-slice, "disp8")
1715     #         value = info->offset - offset-of-next-instruction
1716     #         emit(out, value, 1)
1717     #       else if has-metadata?(word-slice, "disp32")
1718     #         value = info->offset - offset-of-next-instruction
1719     #         emit(out, value, 4)
1720     #       else
1721     #         abort
1722     #     write-buffered(out, "\n")
1723     #
1724     # registers:
1725     #   line: ECX
1726     #   word-slice: EDX
1727     #   offset-of-next-instruction: EBX
1728     #   datum: EDI
1729     #   info: ESI (inner loop only)
1730     #   temporaries: EAX, ESI (outer loop)
1731     #
1732     # . prolog
1733     55/push-EBP
1734     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1735     # . save registers
1736     50/push-EAX
1737     51/push-ECX
1738     52/push-EDX
1739     53/push-EBX
1740     56/push-ESI
1741     57/push-EDI
1742     # var line/ECX : (address stream byte) = stream(512)
1743     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
1744     68/push  0x200/imm32/length
1745     68/push  0/imm32/read
1746     68/push  0/imm32/write
1747     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1748     # var word-slice/EDX = {0, 0}
1749     68/push  0/imm32/end
1750     68/push  0/imm32/start
1751     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
1752     # var datum/EDI = {0, 0}
1753     68/push  0/imm32/end
1754     68/push  0/imm32/start
1755     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
1756     # offset-of-next-instruction/EBX = 0
1757     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
1758 $emit-segments:line-loop:
1759     # clear-stream(line)
1760     # . . push args
1761     51/push-ECX
1762     # . . call
1763     e8/call  clear-stream/disp32
1764     # . . discard args
1765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1766     # read-line-buffered(in, line)
1767     # . . push args
1768     51/push-ECX
1769     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1770     # . . call
1771     e8/call  read-line-buffered/disp32
1772     # . . discard args
1773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1774 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
1807 $emit-segments:check0:
1808     # if (line->write == 0) break
1809     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
1810     0f 84/jump-if-equal  $emit-segments:end/disp32
1811     # offset-of-next-instruction += num-bytes(line)
1812     # . EAX = num-bytes(line)
1813     # . . push args
1814     51/push-ECX
1815     # . . call
1816     e8/call  num-bytes/disp32
1817     # . . discard args
1818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1819     # . EBX = EAX
1820     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
1821 $emit-segments:word-loop:
1822     # next-word(line, word-slice)
1823     # . . push args
1824     52/push-EDX
1825     51/push-ECX
1826     # . . call
1827     e8/call  next-word/disp32
1828     # . . discard args
1829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1830 +-- 33 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
1863 $emit-segments:check1:
1864     # if (slice-empty?(word-slice)) break
1865     # . EAX = slice-empty?(word-slice)
1866     # . . push args
1867     52/push-EDX
1868     # . . call
1869     e8/call  slice-empty?/disp32
1870     # . . discard args
1871     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1872     # . if (EAX != 0) break
1873     3d/compare-EAX-and  0/imm32
1874     0f 85/jump-if-not-equal  $emit-segments:next-line/disp32
1875 $emit-segments:check-for-comment:
1876     # if (slice-starts-with?(word-slice, "#")) break
1877     # . start/ESI = word-slice->start
1878     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *EDX to ESI
1879     # . c/EAX = *start
1880     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1881     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
1882     # . if (EAX == '#') break
1883     3d/compare-EAX-and  0x23/imm32/hash
1884     0f 84/jump-if-equal  $emit-segments:next-line/disp32
1885 $emit-segments:check-for-label:
1886     # if is-label?(word-slice) break
1887     # . EAX = is-label?(word-slice)
1888     # . . push args
1889     52/push-EDX
1890     # . . call
1891     e8/call  is-label?/disp32
1892     # . . discard args
1893     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1894     # . if (EAX != 0) break
1895     3d/compare-EAX-and  0/imm32
1896     0f 85/jump-if-not-equal  $emit-segments:line-loop/disp32
1897 $emit-segments:check-for-segment-header:
1898     # if (slice-equal?(word-slice, "==")) break
1899     # . EAX = slice-equal?(word-slice, "==")
1900     # . . push args
1901     68/push  "=="/imm32
1902     52/push-EDX
1903     # . . call
1904     e8/call  slice-equal?/disp32
1905     # . . discard args
1906     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1907     # . if (EAX != 0) break
1908     3d/compare-EAX-and  0/imm32
1909     0f 85/jump-if-not-equal  $emit-segments:line-loop/disp32
1910 $emit-segments:2-character:
1911     # if (length(word-slice) != 2) goto next check
1912     # . EAX = length(word-slice)
1913     8b/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(EDX+4) to EAX
1914     2b/subtract                     0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # subtract *EDX from EAX
1915     # . if (EAX != 2) goto next check
1916     3d/compare-EAX-and  2/imm32
1917     75/jump-if-not-equal  $emit-segments:check-metadata/disp8
1918     # write-slice-buffered(out, word-slice)
1919     # . . push args
1920     52/push-EDX
1921     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1922     # . . call
1923     e8/call  write-slice-buffered/disp32
1924     # . . discard args
1925     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1926     # write-buffered(out, " ")
1927     # . . push args
1928     68/push  " "/imm32
1929     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1930     # . . call
1931     e8/call  write-buffered/disp32
1932     # . . discard args
1933     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1934     # continue
1935     e9/jump  $emit-segments:word-loop/disp32
1936 $emit-segments:check-metadata:
1937     # - if we get here, 'word-slice' must be a label to be looked up
1938     # datum/EDI = next-token-from-slice(word-slice->start, word-slice->end, "/")
1939     # . . push args
1940     57/push-EDI
1941     68/push  0x2f/imm32/slash
1942     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
1943     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
1944     # . . call
1945     e8/call  next-token-from-slice/disp32
1946     # . . discard args
1947     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
1948 +-- 33 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
1981     # info/ESI = get-slice(labels, datum, row-size=16)
1982     # . EAX = get-slice(labels, datum, row-size=16)
1983     # . . push args
1984     68/push  0x10/imm32/row-size
1985     57/push-EDI
1986     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
1987     # . . call
1988     e8/call  get-slice/disp32
1989     # . . discard args
1990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1991     # . ESI = EAX
1992     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
1993 $emit-segments:check-for-imm8:
1994     # if (has-metadata?(word-slice, "imm8")) abort
1995     # . EAX = has-metadata?(EDX, "imm8")
1996     # . . push args
1997     68/push  "imm8"/imm32
1998     52/push-EDX
1999     # . . call
2000     e8/call  has-metadata?/disp32
2001     # . . discard args
2002     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2003     # . if (EAX != 0) abort
2004     3d/compare-EAX-and  0/imm32
2005     0f 85/jump-if-not-equal  $emit-segments:imm8-abort/disp32
2006 $emit-segments:check-for-imm32:
2007     # if (!has-metadata?(word-slice, "imm32")) goto next check
2008     # . EAX = has-metadata?(EDX, "imm32")
2009     # . . push args
2010     68/push  "imm32"/imm32
2011     52/push-EDX
2012     # . . call
2013     e8/call  has-metadata?/disp32
2014     # . . discard args
2015     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2016     # . if (EAX == 0) goto next check
2017     3d/compare-EAX-and  0/imm32
2018     74/jump-if-equal  $emit-segments:check-for-disp8/disp8
2019 +-- 33 lines: #?     # dump info->address --------------------------------------------------------------------------------------------------------------------
2052     # emit-hex(out, info->address, 4)
2053     # . . push args
2054     68/push  4/imm32
2055     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           8/disp8         .                 # push *(ESI+8)
2056     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2057     # . . call
2058     e8/call  emit-hex/disp32
2059     # . . discard args
2060     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2061     # continue
2062     e9/jump  $emit-segments:word-loop/disp32
2063 $emit-segments:check-for-disp8:
2064     # if (!has-metadata?(word-slice, "disp8")) goto next check
2065     # . EAX = has-metadata?(EDX, "disp8")
2066     # . . push args
2067     68/push  "disp8"/imm32
2068     52/push-EDX
2069     # . . call
2070     e8/call  has-metadata?/disp32
2071     # . . discard args
2072     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2073     # . if (EAX == 0) goto next check
2074     3d/compare-EAX-and  0/imm32
2075     74/jump-if-equal  $emit-segments:check-for-disp32/disp8
2076     # emit-hex(out, info->offset - offset-of-next-instruction, 1)
2077     # . . push args
2078     68/push  1/imm32
2079     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
2080     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # subtract EBX from EAX
2081     50/push-EAX
2082     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2083     # . . call
2084     e8/call  emit-hex/disp32
2085     # . . discard args
2086     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2087     # continue
2088     e9/jump  $emit-segments:word-loop/disp32
2089 $emit-segments:check-for-disp32:
2090     # if (!has-metadata?(word-slice, "disp32")) abort
2091     # . EAX = has-metadata?(EDX, "disp32")
2092     # . . push args
2093     68/push  "disp32"/imm32
2094     52/push-EDX
2095     # . . call
2096     e8/call  has-metadata?/disp32
2097     # . . discard args
2098     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2099     # . if (EAX == 0) abort
2100     3d/compare-EAX-and  0/imm32
2101     74/jump-if-equal  $emit-segments:abort/disp8
2102     # emit-hex(out, info->offset - offset-of-next-instruction, 4)
2103     # . . push args
2104     68/push  4/imm32
2105     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
2106     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # subtract EBX from EAX
2107     50/push-EAX
2108     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2109     # . . call
2110     e8/call  emit-hex/disp32
2111     # . . discard args
2112     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2113     # continue
2114     e9/jump  $emit-segments:word-loop/disp32
2115 $emit-segments:next-line:
2116     # write-buffered(out, "\n")
2117     # . . push args
2118     68/push  Newline/imm32
2119     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2120     # . . call
2121     e8/call  write-buffered/disp32
2122     # . . discard args
2123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2124     # loop
2125     e9/jump  $emit-segments:line-loop/disp32
2126 $emit-segments:end:
2127     # . reclaim locals
2128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x21c/imm32       # add to ESP
2129     # . restore registers
2130     5f/pop-to-EDI
2131     5e/pop-to-ESI
2132     5b/pop-to-EBX
2133     5a/pop-to-EDX
2134     59/pop-to-ECX
2135     58/pop-to-EAX
2136     # . epilog
2137     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2138     5d/pop-to-EBP
2139     c3/return
2140 
2141 $emit-segments:imm8-abort:
2142     # . _write(2/stderr, error)
2143     # . . push args
2144     68/push  "emit-segments: unexpected /imm8"/imm32
2145     68/push  2/imm32/stderr
2146     # . . call
2147     e8/call  _write/disp32
2148     # . . discard args
2149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2150     # . syscall(exit, 1)
2151     bb/copy-to-EBX  1/imm32
2152     b8/copy-to-EAX  1/imm32/exit
2153     cd/syscall  0x80/imm8
2154     # never gets here
2155 
2156 $emit-segments:abort:
2157     # print(stderr, "missing metadata in " word-slice)
2158     # . _write(2/stderr, "missing metadata in word ")
2159     # . . push args
2160     68/push  "emit-segments: missing metadata in "/imm32
2161     68/push  2/imm32/stderr
2162     # . . call
2163     e8/call  _write/disp32
2164     # . . discard args
2165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2166     # . write-slice-buffered(Stderr, word-slice)
2167     # . . push args
2168     52/push-EDX
2169     68/push  Stderr/imm32
2170     # . . call
2171     e8/call  write-slice-buffered/disp32
2172     # . . discard args
2173     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2174     # . flush(Stderr)
2175     # . . push args
2176     68/push  Stderr/imm32
2177     # . . call
2178     e8/call  flush/disp32
2179     # . . discard args
2180     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2181     # . syscall(exit, 1)
2182     bb/copy-to-EBX  1/imm32
2183     b8/copy-to-EAX  1/imm32/exit
2184     cd/syscall  0x80/imm8
2185     # never gets here
2186 
2187 test-emit-segments:
2188     # input:
2189     #   in:
2190     #     == code 0x1000
2191     #     ab cd ef gh
2192     #     ij x/imm32
2193     #     == data 0x2000
2194     #     00
2195     #     x:
2196     #       34
2197     #   segments:
2198     #     - 'code': {0x1074, 0, 5}
2199     #     - 'data': {0x2079, 5, 1}
2200     #   labels:
2201     #     - 'l1': {'code', 3, 0x1077}
2202     #     - 'x': {'data', 1, 0x207a}
2203     #
2204     # output:
2205     #   ab cd ef gh
2206     #   ij 7a 20 00 00
2207     #   00
2208     #   34
2209     #
2210     # . prolog
2211     55/push-EBP
2212     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2213     # setup
2214     # . clear-stream(_test-input-stream)
2215     # . . push args
2216     68/push  _test-input-stream/imm32
2217     # . . call
2218     e8/call  clear-stream/disp32
2219     # . . discard args
2220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2221     # . clear-stream(_test-input-buffered-file+4)
2222     # . . push args
2223     b8/copy-to-EAX  _test-input-buffered-file/imm32
2224     05/add-to-EAX  4/imm32
2225     50/push-EAX
2226     # . . call
2227     e8/call  clear-stream/disp32
2228     # . . discard args
2229     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2230     # . clear-stream(_test-output-stream)
2231     # . . push args
2232     68/push  _test-output-stream/imm32
2233     # . . call
2234     e8/call  clear-stream/disp32
2235     # . . discard args
2236     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2237     # . clear-stream(_test-output-buffered-file+4)
2238     # . . push args
2239     b8/copy-to-EAX  _test-output-buffered-file/imm32
2240     05/add-to-EAX  4/imm32
2241     50/push-EAX
2242     # . . call
2243     e8/call  clear-stream/disp32
2244     # . . discard args
2245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2246     # . var segments/ECX = stream(10 * 16)
2247     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
2248     68/push  0xa0/imm32/length
2249     68/push  0/imm32/read
2250     68/push  0/imm32/write
2251     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2252     # . var labels/EDX = stream(512 * 16)
2253     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
2254     68/push  0x2000/imm32/length
2255     68/push  0/imm32/read
2256     68/push  0/imm32/write
2257     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
2258     # initialize input
2259     # . write(_test-input-stream, "== code 0x1000\n")
2260     # . . push args
2261     68/push  "== code 0x1000\n"/imm32
2262     68/push  _test-input-stream/imm32
2263     # . . call
2264     e8/call  write/disp32
2265     # . . discard args
2266     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2267     # . write(_test-input-stream, "ab cd ef gh\n")
2268     # . . push args
2269     68/push  "ab cd ef gh\n"/imm32
2270     68/push  _test-input-stream/imm32
2271     # . . call
2272     e8/call  write/disp32
2273     # . . discard args
2274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2275     # . write(_test-input-stream, "ij x/imm32\n")
2276     # . . push args
2277     68/push  "ij x/imm32\n"/imm32
2278     68/push  _test-input-stream/imm32
2279     # . . call
2280     e8/call  write/disp32
2281     # . . discard args
2282     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2283     # . write(_test-input-stream, "== data 0x2000\n")
2284     # . . push args
2285     68/push  "== data 0x2000\n"/imm32
2286     68/push  _test-input-stream/imm32
2287     # . . call
2288     e8/call  write/disp32
2289     # . . discard args
2290     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2291     # . write(_test-input-stream, "00\n")
2292     # . . push args
2293     68/push  "00\n"/imm32
2294     68/push  _test-input-stream/imm32
2295     # . . call
2296     e8/call  write/disp32
2297     # . . discard args
2298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2299     # . write(_test-input-stream, "x:\n")
2300     # . . push args
2301     68/push  "x:\n"/imm32
2302     68/push  _test-input-stream/imm32
2303     # . . call
2304     e8/call  write/disp32
2305     # . . discard args
2306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2307     # . write(_test-input-stream, "34\n")
2308     # . . push args
2309     68/push  "34\n"/imm32
2310     68/push  _test-input-stream/imm32
2311     # . . call
2312     e8/call  write/disp32
2313     # . . discard args
2314     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2315     # . stream-add4(segments, "code", 0x1074, 0, 5)
2316     68/push  5/imm32/segment-size
2317     68/push  0/imm32/file-offset
2318     68/push  0x1074/imm32/start-address
2319     68/push  "code"/imm32/segment-name
2320     51/push-ECX
2321     # . . call
2322     e8/call  stream-add4/disp32
2323     # . . discard args
2324     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2325     # . stream-add4(segments, "data", 0x2079, 5, 1)
2326     68/push  1/imm32/segment-size
2327     68/push  5/imm32/file-offset
2328     68/push  0x2079/imm32/start-address
2329     68/push  "data"/imm32/segment-name
2330     51/push-ECX
2331     # . . call
2332     e8/call  stream-add4/disp32
2333     # . . discard args
2334     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2335     # . stream-add4(labels, "l1", "code", 3, 0x1077)
2336     68/push  0x1077/imm32/label-address
2337     68/push  3/imm32/segment-offset
2338     68/push  "code"/imm32/segment-name
2339     68/push  "l1"/imm32/label-name
2340     52/push-EDX
2341     # . . call
2342     e8/call  stream-add4/disp32
2343     # . . discard args
2344     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2345     # . stream-add4(labels, "x", "data", 1, 0x207a)
2346     68/push  0x207a/imm32/label-address
2347     68/push  1/imm32/segment-offset
2348     68/push  "data"/imm32/segment-name
2349     68/push  "x"/imm32/label-name
2350     52/push-EDX
2351     # . . call
2352     e8/call  stream-add4/disp32
2353     # . . discard args
2354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2355     # component under test
2356     # . emit-segments(_test-input-buffered-file, _test-output-buffered-file, segments, labels)
2357     # . . push args
2358     52/push-EDX
2359     51/push-ECX
2360     68/push  _test-output-buffered-file/imm32
2361     68/push  _test-input-buffered-file/imm32
2362     # . . call
2363     e8/call  emit-segments/disp32
2364     # . . discard args
2365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2366     # checks
2367 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
2400     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
2401     # . . push args
2402     68/push  "F - test-emit-segments/0"/imm32
2403     68/push  "ab cd ef gh "/imm32
2404     68/push  _test-output-stream/imm32
2405     # . . call
2406     e8/call  check-next-stream-line-equal/disp32
2407     # . . discard args
2408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2409     # . check-next-stream-line-equal(_test-output-stream, "ij 7a 20 00 00 ", msg)
2410     # . . push args
2411     68/push  "F - test-emit-segments/1"/imm32
2412     68/push  "ij 7a 20 00 00 "/imm32
2413     68/push  _test-output-stream/imm32
2414     # . . call
2415     e8/call  check-next-stream-line-equal/disp32
2416     # . . discard args
2417     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2418     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
2419     # . . push args
2420     68/push  "F - test-emit-segments/2"/imm32
2421     68/push  "00 "/imm32
2422     68/push  _test-output-stream/imm32
2423     # . . call
2424     e8/call  check-next-stream-line-equal/disp32
2425     # . . discard args
2426     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2427     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
2428     # . . push args
2429     68/push  "F - test-emit-segments/3"/imm32
2430     68/push  "34 "/imm32
2431     68/push  _test-output-stream/imm32
2432     # . . call
2433     e8/call  check-next-stream-line-equal/disp32
2434     # . . discard args
2435     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2436     # . epilog
2437     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2438     5d/pop-to-EBP
2439     c3/return
2440 
2441 emit-headers:  # out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
2442     # pseudocode:
2443     #   emit-elf-header(out, segments, labels)
2444     #   curr-segment = segments->data
2445     #   max = segments->data + segments->write
2446     #   while true
2447     #     if (curr-segment >= max) break
2448     #     emit-elf-program-header-entry(out, curr-segment)
2449     #     curr-segment += 16                        # size of a row
2450     #
2451     # . prolog
2452     55/push-EBP
2453     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2454     # . save registers
2455     50/push-EAX
2456     51/push-ECX
2457 +--  9 lines: #?     # write(2/stderr, "emit-elf-header\n") --------------------------------------------------------------------------------------------------
2466     # emit-elf-header(out, segments, labels)
2467     # . . push args
2468     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
2469     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2470     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2471     # . . call
2472     e8/call  emit-elf-header/disp32
2473     # . . discard args
2474     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2475     # EAX = segments
2476     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
2477     # ECX = segments->write
2478     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
2479     # curr-segment/EAX = segments->data
2480     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy EAX+12 to EAX
2481     # max/ECX = segments->data + segments->write
2482     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # add EAX to ECX
2483 $emit-headers:loop:
2484     # if (curr-segment >= max) break
2485     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
2486     0f 83/jump-if-greater-or-equal-unsigned  $emit-headers:end/disp32
2487 +-- 69 lines: #?     # dump curr-segment->name ---------------------------------------------------------------------------------------------------------------
2556 +--  9 lines: #?     # write(2/stderr, "emit-segment-header\n") ----------------------------------------------------------------------------------------------
2565     # emit-elf-program-header-entry(out, curr-segment)
2566     # . . push args
2567     50/push-EAX
2568     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2569     # . . call
2570     e8/call  emit-elf-program-header-entry/disp32
2571     # . . discard args
2572     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2573     # curr-segment += 16                        # size of a row
2574     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x10/imm32        # add to EAX
2575     e9/jump  $emit-headers:loop/disp32
2576 $emit-headers:end:
2577     # . restore registers
2578     59/pop-to-ECX
2579     58/pop-to-EAX
2580     # . epilog
2581     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2582     5d/pop-to-EBP
2583     c3/return
2584 
2585 emit-elf-header:  # out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
2586     # pseudocode
2587     #   *Elf_e_entry = get(labels, "Entry")->address
2588     #   *Elf_e_phnum = segments->write / 20         # size of a row
2589     #   emit-hex-array(out, Elf_header)
2590     #
2591     # . prolog
2592     55/push-EBP
2593     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2594     # . save registers
2595     50/push-EAX
2596     51/push-ECX
2597     52/push-EDX  # just because we need to call idiv
2598     # *Elf_e_entry = get(labels, "Entry")->address
2599     # . EAX = labels
2600     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
2601     # . label-info/EAX = get(labels, "Entry", row-size=16)
2602     # . . push args
2603     68/push  0x10/imm32/row-size
2604     68/push  "Entry"/imm32
2605     50/push-EAX
2606     # . . call
2607     e8/call  get/disp32
2608     # . . discard args
2609     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2610     # . EAX = label-info->address
2611     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EAX+8) to EAX
2612     # . *Elf_e_entry = EAX
2613     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_e_entry/disp32                # copy EAX to *Elf_e_entry
2614     # *Elf_e_phnum = segments->write / 0x20
2615     # . EAX = segments
2616     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
2617     # . len/EAX = segments->write
2618     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
2619     # . EAX = len / 0x20  (destroying EDX)
2620     b9/copy-to-ECX  0x20/imm32
2621     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
2622     f7          7/subop/idiv        3/mod/direct    1/rm32/ECX    .           .             .           .           .               .                 # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX
2623     # . *Elf_e_phnum = EAX
2624     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_e_phnum/disp32                # copy EAX to *Elf_e_phnum
2625     # emit-hex-array(out, Elf_header)
2626     # . . push args
2627     68/push  Elf_header/imm32
2628     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2629     # . . call
2630     e8/call  emit-hex-array/disp32
2631     # . . discard args
2632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2633 $emit-elf-header:end:
2634     # . restore registers
2635     5a/pop-to-EDX
2636     59/pop-to-ECX
2637     58/pop-to-EAX
2638     # . epilog
2639     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2640     5d/pop-to-EBP
2641     c3/return
2642 
2643 emit-elf-program-header-entry:  # out : (address buffered-file), curr-segment : (address {string, segment-info})
2644     # pseudocode:
2645     #   *Elf_p_offset = curr-segment->file-offset
2646     #   *Elf_p_vaddr = curr-segment->address
2647     #   *Elf_p_paddr = curr-segment->address
2648     #   *Elf_p_filesz = curr-segment->size
2649     #   *Elf_p_memsz = curr-segment->size
2650     #   if curr-segment->name == "code"
2651     #     *Elf_p_flags = 5  # r-x
2652     #   else
2653     #     *Elf_p_flags = 6  # rw-
2654     #   emit-hex-array(out, Elf_program_header_entry)
2655     #
2656     # . prolog
2657     55/push-EBP
2658     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2659     # . save registers
2660     50/push-EAX
2661     56/push-ESI
2662     # ESI = curr-segment
2663     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
2664     # *Elf_p_offset = curr-segment->file-offset
2665     # . EAX = curr-segment->file-offset
2666     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(ESI+8) to EAX
2667     # . *Elf_p_offset = EAX
2668     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_offset/disp32               # copy EAX to *Elf_p_offset
2669     # *Elf_p_vaddr = curr-segment->address
2670     # . EAX = curr-segment->address
2671     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
2672     # . *Elf_p_vaddr = EAX
2673     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_vaddr/disp32                # copy EAX to *Elf_p_vaddr
2674     # *Elf_p_paddr = curr-segment->address
2675     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_paddr/disp32                # copy EAX to *Elf_p_paddr
2676     # *Elf_p_filesz = curr-segment->size
2677     # . EAX = curr-segment->size
2678     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(ESI+12) to EAX
2679     # . *Elf_p_filesz = EAX
2680     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_filesz/disp32               # copy EAX to *Elf_p_filesz
2681     # *Elf_p_memsz = curr-segment->size
2682     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_memsz/disp32                # copy EAX to *Elf_p_memsz
2683     # if (!string-equal?(curr-segment->name, "code") goto next check
2684     # . EAX = curr-segment->name
2685     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
2686     # . EAX = string-equal?(curr-segment->name, "code")
2687     # . . push args
2688     68/push  "code"/imm32
2689     50/push-EAX
2690     # . . call
2691     e8/call  string-equal?/disp32
2692     # . . discard args
2693     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2694     # . if (EAX == 0) goto next check
2695     3d/compare-EAX-and  0/imm32
2696     74/jump-if-equal  $emit-elf-program-header-entry:data/disp8
2697     # *Elf_p_flags = rw-
2698     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           Elf_p_flags/disp32  6/imm32       # copy to *Elf_p_flags
2699 $emit-elf-program-header-entry:data:
2700     # otherwise *Elf_p_flags = r-x
2701     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           Elf_p_flags/disp32  5/imm32       # copy to *Elf_p_flags
2702     # emit-hex-array(out, Elf_program_header_entry)
2703     # . . push args
2704     68/push  Elf_program_header_entry/imm32
2705     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2706     # . . call
2707     e8/call  emit-hex-array/disp32
2708     # . . discard args
2709     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2710 $emit-elf-program-header-entry:end:
2711     # . restore registers
2712     5e/pop-to-ESI
2713     58/pop-to-EAX
2714     # . epilog
2715     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2716     5d/pop-to-EBP
2717     c3/return
2718 
2719 # - some helpers for tests
2720 
2721 stream-add4:  # in : (address stream byte), key : address, val1 : address, val2 : address, val3 : address
2722     # . prolog
2723     55/push-EBP
2724     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2725     # . save registers
2726     50/push-EAX
2727     51/push-ECX
2728     52/push-EDX
2729     56/push-ESI
2730     # ESI = in
2731     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
2732     # curr/EAX = in->data + in->write
2733     # . EAX = in->write
2734     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
2735     # . EAX = ESI+EAX+12
2736     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
2737     # max/EDX = in->data + in->length
2738     # . EDX = in->length
2739     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(ESI+8) to EDX
2740     # . EDX = ESI+EDX+12
2741     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
2742     # if (curr >= max) abort
2743     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
2744     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
2745     # *curr = key
2746     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
2747     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
2748     # curr += 4
2749     05/add-to-EAX  4/imm32
2750     # if (curr >= max) abort
2751     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
2752     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
2753     # *curr = val1
2754     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x10/disp8      .                 # copy *(EBP+16) to ECX
2755     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
2756     # curr += 4
2757     05/add-to-EAX  4/imm32
2758     # if (curr >= max) abort
2759     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
2760     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
2761     # *curr = val2
2762     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x14/disp8      .                 # copy *(EBP+20) to ECX
2763     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
2764     # curr += 4
2765     05/add-to-EAX  4/imm32
2766     # if (curr >= max) abort
2767     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
2768     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
2769     # *curr = val3
2770     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x18/disp8      .                 # copy *(EBP+24) to ECX
2771     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
2772     # in->write += 16
2773     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               0x10/imm32        # add to *ESI
2774 $stream-add4:end:
2775     # . restore registers
2776     5e/pop-to-ESI
2777     5a/pop-to-EDX
2778     59/pop-to-ECX
2779     58/pop-to-EAX
2780     # . epilog
2781     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2782     5d/pop-to-EBP
2783     c3/return
2784 
2785 $stream-add4:abort:
2786     # . _write(2/stderr, error)
2787     # . . push args
2788     68/push  "overflow in stream-add4\n"/imm32
2789     68/push  2/imm32/stderr
2790     # . . call
2791     e8/call  _write/disp32
2792     # . . discard args
2793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2794     # . syscall(exit, 1)
2795     bb/copy-to-EBX  1/imm32
2796     b8/copy-to-EAX  1/imm32/exit
2797     cd/syscall  0x80/imm8
2798     # never gets here
2799 
2800 # some variants of 'trace' that take multiple arguments in different combinations of types:
2801 #   n: int
2802 #   c: character [4-bytes, will eventually be UTF-8]
2803 #   s: (address string)
2804 #   l: (address slice)
2805 # one gotcha: 's5' must not be empty
2806 
2807 trace-sssns:  # s1 : (address string), s2 : (address string), s3 : (address string), n4 : int, s5 : (address string)
2808     # . prolog
2809     55/push-EBP
2810     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2811     # write(*Trace-stream, s1)
2812     # . . push args
2813     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2814     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2815     # . . call
2816     e8/call  write/disp32
2817     # . . discard args
2818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2819     # write(*Trace-stream, s2)
2820     # . . push args
2821     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2822     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2823     # . . call
2824     e8/call  write/disp32
2825     # . . discard args
2826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2827     # write(*Trace-stream, s3)
2828     # . . push args
2829     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
2830     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2831     # . . call
2832     e8/call  write/disp32
2833     # . . discard args
2834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2835     # print-int32(*Trace-stream, n4)
2836     # . . push args
2837     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
2838     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2839     # . . call
2840     e8/call  print-int32/disp32
2841     # . . discard args
2842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2843     # trace(s5)  # implicitly adds a newline and finalizes the trace line
2844     # . . push args
2845     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
2846     # . . call
2847     e8/call  trace/disp32
2848     # . . discard args
2849     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2850 $trace-sssns:end:
2851     # . epilog
2852     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2853     5d/pop-to-EBP
2854     c3/return
2855 
2856 test-trace-sssns:
2857     # . prolog
2858     55/push-EBP
2859     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2860     # setup
2861     # . *Trace-stream->write = 0
2862     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
2863     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
2864     # trace-sssns("A" "b" "c " 3 " e")
2865     # . . push args
2866     68/push  " e"/imm32
2867     68/push  3/imm32
2868     68/push  "c "/imm32
2869     68/push  "b"/imm32
2870     68/push  "A"/imm32
2871     # . . call
2872     e8/call  trace-sssns/disp32
2873     # . . discard args
2874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2875 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
2901     # check-trace-contains("Abc 0x00000003 e")
2902     # . . push args
2903     68/push  "F - test-trace-sssns"/imm32
2904     68/push  "Abc 0x00000003 e"/imm32
2905     # . . call
2906     e8/call  check-trace-contains/disp32
2907     # . . discard args
2908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2909     # . epilog
2910     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2911     5d/pop-to-EBP
2912     c3/return
2913 
2914 trace-snsns:  # s1 : (address string), n2 : int, s3 : (address string), n4 : int, s5 : (address string)
2915     # . prolog
2916     55/push-EBP
2917     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2918     # write(*Trace-stream, s1)
2919     # . . push args
2920     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2921     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2922     # . . call
2923     e8/call  write/disp32
2924     # . . discard args
2925     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2926     # print-int32(*Trace-stream, n2)
2927     # . . push args
2928     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2929     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2930     # . . call
2931     e8/call  print-int32/disp32
2932     # . . discard args
2933     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2934     # write(*Trace-stream, s3)
2935     # . . push args
2936     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
2937     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2938     # . . call
2939     e8/call  write/disp32
2940     # . . discard args
2941     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2942     # print-int32(*Trace-stream, n4)
2943     # . . push args
2944     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
2945     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2946     # . . call
2947     e8/call  print-int32/disp32
2948     # . . discard args
2949     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2950     # trace(s5)  # implicitly adds a newline and finalizes the trace line
2951     # . . push args
2952     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
2953     # . . call
2954     e8/call  trace/disp32
2955     # . . discard args
2956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2957 $trace-snsns:end:
2958     # . epilog
2959     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2960     5d/pop-to-EBP
2961     c3/return
2962 
2963 test-trace-snsns:
2964     # . prolog
2965     55/push-EBP
2966     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2967     # setup
2968     # . *Trace-stream->write = 0
2969     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
2970     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
2971     # trace-snsns("A " 2 " c " 3 " e")
2972     # . . push args
2973     68/push  " e"/imm32
2974     68/push  3/imm32
2975     68/push  " c "/imm32
2976     68/push  2/imm32
2977     68/push  "A "/imm32
2978     # . . call
2979     e8/call  trace-snsns/disp32
2980     # . . discard args
2981     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2982 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3008     # check-trace-contains("Abc 0x00000003 e")
3009     # . . push args
3010     68/push  "F - test-trace-snsns"/imm32
3011     68/push  "A 0x00000002 c 0x00000003 e"/imm32
3012     # . . call
3013     e8/call  check-trace-contains/disp32
3014     # . . discard args
3015     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3016     # . epilog
3017     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3018     5d/pop-to-EBP
3019     c3/return
3020 
3021 trace-slsls:  # s1 : (address string), l2 : (address slice), s3 : (address string), l4 : (address slice), s5 : (address string)
3022     # . prolog
3023     55/push-EBP
3024     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3025     # write(*Trace-stream, s1)
3026     # . . push args
3027     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3028     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3029     # . . call
3030     e8/call  write/disp32
3031     # . . discard args
3032     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3033     # write-slice(*Trace-stream, l2)
3034     # . . push args
3035     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3036     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3037     # . . call
3038     e8/call  write-slice/disp32
3039     # . . discard args
3040     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3041     # write(*Trace-stream, s3)
3042     # . . push args
3043     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
3044     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3045     # . . call
3046     e8/call  write/disp32
3047     # . . discard args
3048     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3049     # write-slice(*Trace-stream, l4)
3050     # . . push args
3051     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
3052     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3053     # . . call
3054     e8/call  write-slice/disp32
3055     # . . discard args
3056     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3057     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3058     # . . push args
3059     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
3060     # . . call
3061     e8/call  trace/disp32
3062     # . . discard args
3063     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3064 $trace-slsls:end:
3065     # . epilog
3066     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3067     5d/pop-to-EBP
3068     c3/return
3069 
3070 test-trace-slsls:
3071     # . prolog
3072     55/push-EBP
3073     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3074     # setup
3075     # . *Trace-stream->write = 0
3076     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
3077     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
3078     # (EAX..ECX) = "b"
3079     b8/copy-to-EAX  "b"/imm32
3080     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
3081     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
3082     05/add-to-EAX  4/imm32
3083     # var b/EBX : (address slice) = {EAX, ECX}
3084     51/push-ECX
3085     50/push-EAX
3086     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
3087     # (EAX..ECX) = "d"
3088     b8/copy-to-EAX  "d"/imm32
3089     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
3090     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
3091     05/add-to-EAX  4/imm32
3092     # var d/EDX : (address slice) = {EAX, ECX}
3093     51/push-ECX
3094     50/push-EAX
3095     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
3096     # trace-slsls("A" b "c" d "e")
3097     # . . push args
3098     68/push  "e"/imm32
3099     52/push-EDX
3100     68/push  "c"/imm32
3101     53/push-EBX
3102     68/push  "A"/imm32
3103     # . . call
3104     e8/call  trace-slsls/disp32
3105     # . . discard args
3106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
3107 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3133     # check-trace-contains("Abcde")
3134     # . . push args
3135     68/push  "F - test-trace-slsls"/imm32
3136     68/push  "Abcde"/imm32
3137     # . . call
3138     e8/call  check-trace-contains/disp32
3139     # . . discard args
3140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3141     # . epilog
3142     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3143     5d/pop-to-EBP
3144     c3/return
3145 
3146 trace-slsns:  # s1 : (address string), l2 : (address slice), s3 : (address string), n4 : int, s5 : (address string)
3147     # . prolog
3148     55/push-EBP
3149     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3150     # write(*Trace-stream, s1)
3151     # . . push args
3152     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3153     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3154     # . . call
3155     e8/call  write/disp32
3156     # . . discard args
3157     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3158     # write-slice(*Trace-stream, l2)
3159     # . . push args
3160     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3161     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3162     # . . call
3163     e8/call  write-slice/disp32
3164     # . . discard args
3165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3166     # write(*Trace-stream, s3)
3167     # . . push args
3168     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
3169     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3170     # . . call
3171     e8/call  write/disp32
3172     # . . discard args
3173     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3174     # print-int32(*Trace-stream, n4)
3175     # . . push args
3176     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
3177     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3178     # . . call
3179     e8/call  print-int32/disp32
3180     # . . discard args
3181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3182     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3183     # . . push args
3184     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
3185     # . . call
3186     e8/call  trace/disp32
3187     # . . discard args
3188     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3189 $trace-slsns:end:
3190     # . epilog
3191     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3192     5d/pop-to-EBP
3193     c3/return
3194 
3195 test-trace-slsns:
3196     # . prolog
3197     55/push-EBP
3198     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3199     # setup
3200     # . *Trace-stream->write = 0
3201     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
3202     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
3203     # (EAX..ECX) = "b"
3204     b8/copy-to-EAX  "b"/imm32
3205     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
3206     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
3207     05/add-to-EAX  4/imm32
3208     # var b/EBX : (address slice) = {EAX, ECX}
3209     51/push-ECX
3210     50/push-EAX
3211     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
3212     # trace-slsls("A" b "c " 3 " e")
3213     # . . push args
3214     68/push  " e"/imm32
3215     68/push  3/imm32
3216     68/push  "c "/imm32
3217     53/push-EBX
3218     68/push  "A"/imm32
3219     # . . call
3220     e8/call  trace-slsns/disp32
3221     # . . discard args
3222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
3223 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3249     # check-trace-contains("Abc 0x00000003 e")
3250     # . . push args
3251     68/push  "F - test-trace-slsls"/imm32
3252     68/push  "Abc 0x00000003 e"/imm32
3253     # . . call
3254     e8/call  check-trace-contains/disp32
3255     # . . discard args
3256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3257     # . epilog
3258     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3259     5d/pop-to-EBP
3260     c3/return
3261 
3262 trace-slsss:  # s1 : (address string), l2 : (address slice), s3 : (address string), s4 : (address string), s5 : (address string)
3263     # . prolog
3264     55/push-EBP
3265     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3266     # write(*Trace-stream, s1)
3267     # . . push args
3268     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3269     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3270     # . . call
3271     e8/call  write/disp32
3272     # . . discard args
3273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3274     # write-slice(*Trace-stream, l2)
3275     # . . push args
3276     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3277     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3278     # . . call
3279     e8/call  write-slice/disp32
3280     # . . discard args
3281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3282     # write(*Trace-stream, s3)
3283     # . . push args
3284     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
3285     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3286     # . . call
3287     e8/call  write/disp32
3288     # . . discard args
3289     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3290     # write(*Trace-stream, s4)
3291     # . . push args
3292     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
3293     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3294     # . . call
3295     e8/call  write/disp32
3296     # . . discard args
3297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3298     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3299     # . . push args
3300     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
3301     # . . call
3302     e8/call  trace/disp32
3303     # . . discard args
3304     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3305 $trace-slsss:end:
3306     # . epilog
3307     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3308     5d/pop-to-EBP
3309     c3/return
3310 
3311 test-trace-slsss:
3312     # . prolog
3313     55/push-EBP
3314     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3315     # setup
3316     # . *Trace-stream->write = 0
3317     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
3318     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
3319     # (EAX..ECX) = "b"
3320     b8/copy-to-EAX  "b"/imm32
3321     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
3322     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
3323     05/add-to-EAX  4/imm32
3324     # var b/EBX : (address slice) = {EAX, ECX}
3325     51/push-ECX
3326     50/push-EAX
3327     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
3328     # trace-slsss("A" b "c" "d" "e")
3329     # . . push args
3330     68/push  "e"/imm32
3331     68/push  "d"/imm32
3332     68/push  "c"/imm32
3333     53/push-EBX
3334     68/push  "A"/imm32
3335     # . . call
3336     e8/call  trace-slsss/disp32
3337     # . . discard args
3338     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
3339 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3365     # check-trace-contains("Abcde")
3366     # . . push args
3367     68/push  "F - test-trace-slsss"/imm32
3368     68/push  "Abcde"/imm32
3369     # . . call
3370     e8/call  check-trace-contains/disp32
3371     # . . discard args
3372     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3373     # . epilog
3374     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3375     5d/pop-to-EBP
3376     c3/return
3377 
3378 num-bytes:  # line : (address stream) -> EAX : int
3379     # pseudocode:
3380     #   result = 0
3381     #   while true
3382     #     var word-slice = next-word(line)
3383     #     if slice-empty?(word-slice)             # end of line
3384     #       break
3385     #     if slice-starts-with?(word-slice, "#")  # comment
3386     #       break
3387     #     if is-label?(word-slice)                # no need for label declarations anymore
3388     #       break
3389     #     if slice-equal?(word-slice, "==")
3390     #       break                                 # no need for segment header lines
3391     #     result += compute-width(word-slice)
3392     #   return result
3393     #
3394     # . prolog
3395     55/push-EBP
3396     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3397     # . save registers
3398     51/push-ECX
3399     52/push-EDX
3400     53/push-EBX
3401     # var result/EAX = 0
3402     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
3403     # var word-slice/ECX = {0, 0}
3404     68/push  0/imm32/end
3405     68/push  0/imm32/start
3406     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
3407 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
3433     # . rewind-stream(line)
3434     # . . push args
3435     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3436     # . . call
3437     e8/call  rewind-stream/disp32
3438     # . . discard args
3439     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3440 $num-bytes:loop:
3441     # next-word(line, word-slice)
3442     # . . push args
3443     51/push-ECX
3444     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3445     # . . call
3446     e8/call  next-word/disp32
3447     # . . discard args
3448     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3449 +-- 46 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
3495 $num-bytes:check0:
3496     # if (slice-empty?(word-slice)) break
3497     # . save result
3498     50/push-EAX
3499     # . EAX = slice-empty?(word-slice)
3500     # . . push args
3501     51/push-ECX
3502     # . . call
3503     e8/call  slice-empty?/disp32
3504     # . . discard args
3505     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3506     # . if (EAX != 0) break
3507     3d/compare-EAX-and  0/imm32
3508     # . restore result now that ZF is set
3509     58/pop-to-EAX
3510     75/jump-if-not-equal  $num-bytes:end/disp8
3511 $num-bytes:check-for-comment:
3512     # if (slice-starts-with?(word-slice, "#")) break
3513     # . start/EDX = word-slice->start
3514     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
3515     # . c/EBX = *start
3516     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
3517     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/BL    .               .                 # copy byte at *EDX to BL
3518     # . if (EBX == '#') break
3519     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x23/imm32/hash   # compare EBX
3520     74/jump-if-equal  $num-bytes:end/disp8
3521 $num-bytes:check-for-label:
3522     # if (slice-ends-with?(word-slice, ":")) break
3523     # . end/EDX = word-slice->end
3524     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
3525     # . c/EBX = *(end-1)
3526     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
3527     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ECX to BL
3528     # . if (EBX == ':') break
3529     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x3a/imm32/colon  # compare EBX
3530     74/jump-if-equal  $num-bytes:end/disp8
3531 $num-bytes:check-for-segment-header:
3532     # if (slice-equal?(word-slice, "==")) break
3533     # . push result
3534     50/push-EAX
3535     # . EAX = slice-equal?(word-slice, "==")
3536     # . . push args
3537     68/push  "=="/imm32
3538     51/push-ECX
3539     # . . call
3540     e8/call  slice-equal?/disp32
3541     # . . discard args
3542     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3543     # . if (EAX != 0) break
3544     3d/compare-EAX-and  0/imm32
3545     # . restore result now that ZF is set
3546     58/pop-to-EAX
3547     75/jump-if-not-equal  $num-bytes:end/disp8
3548 $num-bytes:loop-body:
3549     # result += compute-width-of-slice(word-slice)
3550     # . copy result to EDX
3551     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
3552     # . EAX = compute-width-of-slice(word-slice)
3553     # . . push args
3554     51/push-ECX
3555     # . . call
3556     e8/call compute-width-of-slice/disp32
3557     # . . discard args
3558     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3559     # . EAX += result
3560     01/add                          3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # add EDX to EAX
3561     e9/jump  $num-bytes:loop/disp32
3562 $num-bytes:end:
3563     # . rewind-stream(line)
3564     # . . push args
3565     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3566     # . . call
3567     e8/call  rewind-stream/disp32
3568     # . . discard args
3569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3570     # . reclaim locals
3571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3572     # . restore registers
3573     5b/pop-to-EBX
3574     5a/pop-to-EDX
3575     59/pop-to-ECX
3576     # . epilog
3577     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3578     5d/pop-to-EBP
3579     c3/return
3580 
3581 test-num-bytes-handles-empty-string:
3582     # if a line starts with '#', return 0
3583     # . prolog
3584     55/push-EBP
3585     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3586     # setup
3587     # . clear-stream(_test-input-stream)
3588     # . . push args
3589     68/push  _test-input-stream/imm32
3590     # . . call
3591     e8/call  clear-stream/disp32
3592     # . . discard args
3593     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3594     # . clear-stream(_test-output-stream)
3595     # . . push args
3596     68/push  _test-output-stream/imm32
3597     # . . call
3598     e8/call  clear-stream/disp32
3599     # . . discard args
3600     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3601     # no contents in input
3602     # EAX = num-bytes(_test-input-stream)
3603     # . . push args
3604     68/push  _test-input-stream/imm32
3605     # . . call
3606     e8/call  num-bytes/disp32
3607     # . . discard args
3608     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3609     # check-ints-equal(EAX, 0, msg)
3610     # . . push args
3611     68/push  "F - test-num-bytes-handles-empty-string"/imm32
3612     68/push  0/imm32/true
3613     50/push-EAX
3614     # . . call
3615     e8/call  check-ints-equal/disp32
3616     # . . discard args
3617     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3618     # . epilog
3619     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3620     5d/pop-to-EBP
3621     c3/return
3622 
3623 test-num-bytes-ignores-comments:
3624     # if a line starts with '#', return 0
3625     # . prolog
3626     55/push-EBP
3627     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3628     # setup
3629     # . clear-stream(_test-input-stream)
3630     # . . push args
3631     68/push  _test-input-stream/imm32
3632     # . . call
3633     e8/call  clear-stream/disp32
3634     # . . discard args
3635     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3636     # . clear-stream(_test-output-stream)
3637     # . . push args
3638     68/push  _test-output-stream/imm32
3639     # . . call
3640     e8/call  clear-stream/disp32
3641     # . . discard args
3642     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3643     # initialize input
3644     # . write(_test-input-stream, "# abcd")
3645     # . . push args
3646     68/push  "# abcd"/imm32
3647     68/push  _test-input-stream/imm32
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     # EAX = num-bytes(_test-input-stream)
3653     # . . push args
3654     68/push  _test-input-stream/imm32
3655     # . . call
3656     e8/call  num-bytes/disp32
3657     # . . discard args
3658     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3659     # check-ints-equal(EAX, 0, msg)
3660     # . . push args
3661     68/push  "F - test-num-bytes-ignores-comments"/imm32
3662     68/push  0/imm32/true
3663     50/push-EAX
3664     # . . call
3665     e8/call  check-ints-equal/disp32
3666     # . . discard args
3667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3668     # . epilog
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-num-bytes-ignores-labels:
3674     # if the first word ends with ':', return 0
3675     # . prolog
3676     55/push-EBP
3677     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3678     # setup
3679     # . clear-stream(_test-input-stream)
3680     # . . push args
3681     68/push  _test-input-stream/imm32
3682     # . . call
3683     e8/call  clear-stream/disp32
3684     # . . discard args
3685     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3686     # . clear-stream(_test-output-stream)
3687     # . . push args
3688     68/push  _test-output-stream/imm32
3689     # . . call
3690     e8/call  clear-stream/disp32
3691     # . . discard args
3692     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3693     # initialize input
3694     # . write(_test-input-stream, "ab: # cd")
3695     # . . push args
3696     68/push  "ab: # cd"/imm32
3697     68/push  _test-input-stream/imm32
3698     # . . call
3699     e8/call  write/disp32
3700     # . . discard args
3701     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3702     # EAX = num-bytes(_test-input-stream)
3703     # . . push args
3704     68/push  _test-input-stream/imm32
3705     # . . call
3706     e8/call  num-bytes/disp32
3707     # . . discard args
3708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3709     # check-ints-equal(EAX, 0, msg)
3710     # . . push args
3711     68/push  "F - test-num-bytes-ignores-labels"/imm32
3712     68/push  0/imm32/true
3713     50/push-EAX
3714     # . . call
3715     e8/call  check-ints-equal/disp32
3716     # . . discard args
3717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3718     # . epilog
3719     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3720     5d/pop-to-EBP
3721     c3/return
3722 
3723 test-num-bytes-ignores-segment-headers:
3724     # if the first word is '==', return 0
3725     # . prolog
3726     55/push-EBP
3727     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3728     # setup
3729     # . clear-stream(_test-input-stream)
3730     # . . push args
3731     68/push  _test-input-stream/imm32
3732     # . . call
3733     e8/call  clear-stream/disp32
3734     # . . discard args
3735     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3736     # . clear-stream(_test-output-stream)
3737     # . . push args
3738     68/push  _test-output-stream/imm32
3739     # . . call
3740     e8/call  clear-stream/disp32
3741     # . . discard args
3742     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3743     # initialize input
3744     # . write(_test-input-stream, "== ab cd")
3745     # . . push args
3746     68/push  "== ab cd"/imm32
3747     68/push  _test-input-stream/imm32
3748     # . . call
3749     e8/call  write/disp32
3750     # . . discard args
3751     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3752     # EAX = num-bytes(_test-input-stream)
3753     # . . push args
3754     68/push  _test-input-stream/imm32
3755     # . . call
3756     e8/call  num-bytes/disp32
3757     # . . discard args
3758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3759     # check-ints-equal(EAX, 0, msg)
3760     # . . push args
3761     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
3762     68/push  0/imm32/true
3763     50/push-EAX
3764     # . . call
3765     e8/call  check-ints-equal/disp32
3766     # . . discard args
3767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3768     # . epilog
3769     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3770     5d/pop-to-EBP
3771     c3/return
3772 
3773 test-num-bytes-counts-words-by-default:
3774     # without metadata, count words
3775     # . prolog
3776     55/push-EBP
3777     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3778     # setup
3779     # . clear-stream(_test-input-stream)
3780     # . . push args
3781     68/push  _test-input-stream/imm32
3782     # . . call
3783     e8/call  clear-stream/disp32
3784     # . . discard args
3785     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3786     # . clear-stream(_test-output-stream)
3787     # . . push args
3788     68/push  _test-output-stream/imm32
3789     # . . call
3790     e8/call  clear-stream/disp32
3791     # . . discard args
3792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3793     # initialize input
3794     # . write(_test-input-stream, "ab cd ef")
3795     # . . push args
3796     68/push  "ab cd ef"/imm32
3797     68/push  _test-input-stream/imm32
3798     # . . call
3799     e8/call  write/disp32
3800     # . . discard args
3801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3802     # EAX = num-bytes(_test-input-stream)
3803     # . . push args
3804     68/push  _test-input-stream/imm32
3805     # . . call
3806     e8/call  num-bytes/disp32
3807     # . . discard args
3808     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3809     # check-ints-equal(EAX, 3, msg)
3810     # . . push args
3811     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
3812     68/push  3/imm32/true
3813     50/push-EAX
3814     # . . call
3815     e8/call  check-ints-equal/disp32
3816     # . . discard args
3817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3818     # . epilog
3819     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3820     5d/pop-to-EBP
3821     c3/return
3822 
3823 test-num-bytes-ignores-trailing-comment:
3824     # trailing comments appropriately ignored
3825     # . prolog
3826     55/push-EBP
3827     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3828     # setup
3829     # . clear-stream(_test-input-stream)
3830     # . . push args
3831     68/push  _test-input-stream/imm32
3832     # . . call
3833     e8/call  clear-stream/disp32
3834     # . . discard args
3835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3836     # . clear-stream(_test-output-stream)
3837     # . . push args
3838     68/push  _test-output-stream/imm32
3839     # . . call
3840     e8/call  clear-stream/disp32
3841     # . . discard args
3842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3843     # initialize input
3844     # . write(_test-input-stream, "ab cd # ef")
3845     # . . push args
3846     68/push  "ab cd # ef"/imm32
3847     68/push  _test-input-stream/imm32
3848     # . . call
3849     e8/call  write/disp32
3850     # . . discard args
3851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3852     # EAX = num-bytes(_test-input-stream)
3853     # . . push args
3854     68/push  _test-input-stream/imm32
3855     # . . call
3856     e8/call  num-bytes/disp32
3857     # . . discard args
3858     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3859     # check-ints-equal(EAX, 2, msg)
3860     # . . push args
3861     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
3862     68/push  2/imm32/true
3863     50/push-EAX
3864     # . . call
3865     e8/call  check-ints-equal/disp32
3866     # . . discard args
3867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3868     # . epilog
3869     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3870     5d/pop-to-EBP
3871     c3/return
3872 
3873 test-num-bytes-handles-imm32:
3874     # if a word has the /imm32 metadata, count it as 4 bytes
3875     # . prolog
3876     55/push-EBP
3877     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3878     # setup
3879     # . clear-stream(_test-input-stream)
3880     # . . push args
3881     68/push  _test-input-stream/imm32
3882     # . . call
3883     e8/call  clear-stream/disp32
3884     # . . discard args
3885     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3886     # . clear-stream(_test-output-stream)
3887     # . . push args
3888     68/push  _test-output-stream/imm32
3889     # . . call
3890     e8/call  clear-stream/disp32
3891     # . . discard args
3892     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3893     # initialize input
3894     # . write(_test-input-stream, "ab cd/imm32 ef")
3895     # . . push args
3896     68/push  "ab cd/imm32 ef"/imm32
3897     68/push  _test-input-stream/imm32
3898     # . . call
3899     e8/call  write/disp32
3900     # . . discard args
3901     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3902     # EAX = num-bytes(_test-input-stream)
3903     # . . push args
3904     68/push  _test-input-stream/imm32
3905     # . . call
3906     e8/call  num-bytes/disp32
3907     # . . discard args
3908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3909     # check-ints-equal(EAX, 6, msg)
3910     # . . push args
3911     68/push  "F - test-num-bytes-handles-imm32"/imm32
3912     68/push  6/imm32/true
3913     50/push-EAX
3914     # . . call
3915     e8/call  check-ints-equal/disp32
3916     # . . discard args
3917     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3918     # . epilog
3919     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3920     5d/pop-to-EBP
3921     c3/return
3922 
3923 == data
3924 
3925 Segment-size:
3926   0x1000/imm32/4KB
3927 
3928 # This block of bytes gets copied to the start of the output ELF file, with
3929 # some fields filled in.
3930 # http://www.sco.com/developers/gabi/latest/ch4.eheader.html
3931 Elf_header:
3932   # - length
3933   0x34/imm32
3934   # - data
3935 $e_ident:
3936   7f 45/E 4c/L 46/F
3937   01/32-bit  01/little-endian  01/file-version  00/no-os-extensions
3938   00 00 00 00 00 00 00 00  # 8 bytes of padding
3939 $e_type:
3940   02 00
3941 $e_machine:
3942   03 00
3943 $e_version:
3944   1/imm32
3945 Elf_e_entry:
3946   0x09000000/imm32  # approximate default; must be updated
3947 $e_phoff:
3948   0x34/imm32  # offset for the 'program header table' containing segment headers
3949 $e_shoff:
3950   0/imm32  # no sections
3951 $e_flags:
3952   0/imm32  # unused
3953 $e_ehsize:
3954   0x34 00
3955 $e_phentsize:
3956   0x20 00
3957 Elf_e_phnum:
3958   00 00  # number of segments; must be updated
3959 $e_shentsize:
3960   00 00  # no sections
3961 $e_shnum:
3962   00 00
3963 $e_shstrndx:
3964   00 00
3965 
3966 # This block of bytes gets copied after the Elf_header once for each segment.
3967 # Some fields need filling in each time.
3968 # https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html
3969 Elf_program_header_entry:
3970   # - length
3971   0x20/imm32
3972   # - data
3973 $p_type:
3974   1/imm32/PT_LOAD
3975 Elf_p_offset:
3976   0/imm32  # byte offset in the file at which a segment begins; must be updated
3977 Elf_p_vaddr:
3978   0/imm32  # starting address to store the segment at before running the program
3979 Elf_p_paddr:
3980   0/imm32  # should have same value as Elf_p_vaddr
3981 Elf_p_filesz:
3982   0/imm32
3983 Elf_p_memsz:
3984   0/imm32  # should have same value as Elf_p_filesz
3985 Elf_p_flags:
3986   6/imm32/rw-  # read/write/execute permissions for the segment; must be updated for the code segment
3987 $p_align:
3988   # we hold this constant; changing it will require adjusting the way we
3989   # compute the starting address for each segment
3990   0x1000/imm32
3991 
3992 # . . vim:nowrap:textwidth=0