https://github.com/akkartik/mu/blob/master/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:
   8 #   $ ./subx translate init.linux 0*.subx apps/subx-params.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/survey
  27 #   ...ELF header bytes...
  28 #   # ELF header above will specify that code segment begins at this offset
  29 #   aa bb nn  # some computed address
  30 #   cc dd nn nn nn nn  # some computed displacement
  31 #   ee nn nn nn nn  # some computed address
  32 #   # ELF header above will specify that data segment begins at this offset
  33 #   00
  34 #
  35 # The ELF format has some persnickety constraints on the starting addresses of
  36 # segments, so input headers are treated as guidelines and adjusted in the
  37 # output.
  38 
  39 == code
  40 #   instruction                     effective address                                                   register    displacement    immediate
  41 # . op          subop               mod             rm32          base        index         scale       r32
  42 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
  43 
  44 Entry:  # run tests if necessary, convert stdin if not
  45     # . prologue
  46     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  47 
  48     # Heap = new-segment(Heap-size)
  49     # . . push args
  50     68/push  Heap/imm32
  51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  52     # . . call
  53     e8/call  new-segment/disp32
  54     # . . discard args
  55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  56     # initialize-trace-stream(Trace-size)
  57     # . . push args
  58     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
  59     # . . call
  60     e8/call  initialize-trace-stream/disp32
  61     # . . discard args
  62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
  63 
  64     # - if argc > 1 and argv[1] == "test", then return run_tests()
  65     # if (argc <= 1) goto interactive
  66     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  67     7e/jump-if-<=  $subx-survey-main:interactive/disp8
  68     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  69     # . eax = kernel-string-equal?(argv[1], "test")
  70     # . . push args
  71     68/push  "test"/imm32
  72     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  73     # . . call
  74     e8/call  kernel-string-equal?/disp32
  75     # . . discard args
  76     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  77     # . if (eax == false) goto interactive
  78     3d/compare-eax-and  0/imm32/false
  79     74/jump-if-=  $subx-survey-main:interactive/disp8
  80     # run-tests()
  81     e8/call  run-tests/disp32
  82     # syscall(exit, *Num-test-failures)
  83     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  84     eb/jump  $subx-survey-main:end/disp8
  85 $subx-survey-main:interactive:
  86     # - otherwise convert stdin
  87     # subx-survey(Stdin, Stdout)
  88     # . . push args
  89     68/push  Stdout/imm32
  90     68/push  Stdin/imm32
  91     # . . call
  92     e8/call  subx-survey/disp32
  93     # . . discard args
  94     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  95 #?     # . write-stream(2/stderr, Trace-stream)
  96 #?     # . . push args
  97 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
  98 #?     68/push  2/imm32/stderr
  99 #?     # . . call
 100 #?     e8/call  write-stream/disp32
 101 #?     # . . discard args
 102 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 103     # syscall(exit, 0)
 104     bb/copy-to-ebx  0/imm32
 105 $subx-survey-main:end:
 106     b8/copy-to-eax  1/imm32/exit
 107     cd/syscall  0x80/imm8
 108 
 109 # data structures:
 110 #   segment-info: {addr, file-offset, size}            (12 bytes)
 111 #   segments: (addr stream {string, segment-info})     (16 bytes per row)
 112 #   label-info: {segment-name, segment-offset, addr}   (12 bytes)
 113 #   labels: (addr stream {string, label-info})         (16 bytes per row)
 114 # these are all inefficient; use sequential scans for lookups
 115 
 116 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
 117     # pseudocode
 118     #   var in: (stream byte 4096)
 119     #   slurp(infile, in)
 120     #   var segments: (stream segment-info)
 121     #   var labels: (stream label-info Max-labels)
 122     #   compute-offsets(in, segments, labels)
 123     #   compute-addresses(segments, labels)
 124     #   rewind-stream(in)
 125     #   emit-output(in, out, segments, labels)
 126     #
 127     # . prologue
 128     55/push-ebp
 129     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 130     # . save registers
 131     51/push-ecx
 132     52/push-edx
 133     56/push-esi
 134     # var segments/ecx: (stream {string, segment-info} 160)   # 10 rows * 16 bytes/row
 135     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
 136     68/push  0xa0/imm32/length
 137     68/push  0/imm32/read
 138     68/push  0/imm32/write
 139     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 140     # var labels/edx: (stream label-info Max-labels*16)
 141     # . data
 142     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
 143     # . length
 144     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
 145     # . read
 146     68/push  0/imm32/read
 147     # . write
 148     68/push  0/imm32/write
 149     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 150     # var in/esi: (stream byte Input-size)
 151     # . data
 152     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
 153     # . length
 154     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
 155     # . read
 156     68/push  0/imm32/read
 157     # . write
 158     68/push  0/imm32/write
 159     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 160 +-- 41 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 201 +--  9 lines: #?     # write(2/stderr, "slurp in\n") ---------------------------------------------------------------------------------------------------------
 210     # slurp(infile, in)
 211     # . . push args
 212     56/push-esi
 213     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 214     # . . call
 215     e8/call  slurp/disp32
 216     # . . discard args
 217     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 218 +-- 40 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 258 +-- 33 lines: #?     # dump in -------------------------------------------------------------------------------------------------------------------------------
 291 +--  9 lines: #?     # write(2/stderr, "compute-offsets\n") --------------------------------------------------------------------------------------------------
 300     # compute-offsets(in, segments, labels)
 301     # . . push args
 302     52/push-edx
 303     51/push-ecx
 304     56/push-esi
 305     # . . call
 306     e8/call  compute-offsets/disp32
 307     # . . discard args
 308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 309 +--  9 lines: #?     # write(2/stderr, "compute-addresses\n") ------------------------------------------------------------------------------------------------
 318     # compute-addresses(segments, labels)
 319     # . . push args
 320     52/push-edx
 321     51/push-ecx
 322     # . . call
 323     e8/call  compute-addresses/disp32
 324     # . . discard args
 325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 326     # rewind-stream(in)
 327     # . . push args
 328     56/push-esi
 329     # . . call
 330     e8/call  rewind-stream/disp32
 331     # . . discard args
 332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 333 +--  9 lines: #?     # write(2/stderr, "emit-output\n") ------------------------------------------------------------------------------------------------------
 342 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
 368 +-- 40 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 408     # emit-output(in, out, segments, labels)
 409     # . . push args
 410     52/push-edx
 411     51/push-ecx
 412     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 413     56/push-esi
 414     # . . call
 415     e8/call  emit-output/disp32
 416     # . . discard args
 417     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 418     # flush(out)
 419     # . . push args
 420     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 421     # . . call
 422     e8/call  flush/disp32
 423     # . . discard args
 424     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 425 $subx-survey:end:
 426     # . reclaim locals
 427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x30a0/imm32      # add to esp
 428     # . restore registers
 429     5e/pop-to-esi
 430     5a/pop-to-edx
 431     59/pop-to-ecx
 432     # . epilogue
 433     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 434     5d/pop-to-ebp
 435     c3/return
 436 
 437 test-subx-survey-computes-addresses:
 438     # input:
 439     #   == code 0x1
 440     #   Entry:
 441     #   ab x/imm32
 442     #   == data 0x1000
 443     #   x:
 444     #     01
 445     #
 446     # trace contains (in any order):
 447     #   label x is at address 0x1079
 448     #   segment code starts at address 0x74
 449     #   segment code has size 5
 450     #   segment data starts at address 0x1079
 451     #
 452     # . prologue
 453     55/push-ebp
 454     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 455     # setup
 456     # . clear-stream(_test-input-stream)
 457     # . . push args
 458     68/push  _test-input-stream/imm32
 459     # . . call
 460     e8/call  clear-stream/disp32
 461     # . . discard args
 462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 463     # . clear-stream($_test-input-buffered-file->buffer)
 464     # . . push args
 465     68/push  $_test-input-buffered-file->buffer/imm32
 466     # . . call
 467     e8/call  clear-stream/disp32
 468     # . . discard args
 469     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 470     # . clear-stream(_test-output-stream)
 471     # . . push args
 472     68/push  _test-output-stream/imm32
 473     # . . call
 474     e8/call  clear-stream/disp32
 475     # . . discard args
 476     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 477     # . clear-stream($_test-output-buffered-file->buffer)
 478     # . . push args
 479     68/push  $_test-output-buffered-file->buffer/imm32
 480     # . . call
 481     e8/call  clear-stream/disp32
 482     # . . discard args
 483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 484     # initialize input
 485     # . write(_test-input-stream, "== code 0x1\n")
 486     # . . push args
 487     68/push  "== code 0x1\n"/imm32
 488     68/push  _test-input-stream/imm32
 489     # . . call
 490     e8/call  write/disp32
 491     # . . discard args
 492     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 493     # . write(_test-input-stream, "Entry:\n")
 494     # . . push args
 495     68/push  "Entry:\n"/imm32
 496     68/push  _test-input-stream/imm32
 497     # . . call
 498     e8/call  write/disp32
 499     # . . discard args
 500     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 501     # . write(_test-input-stream, "ab x/imm32\n")
 502     # . . push args
 503     68/push  "ab x/imm32\n"/imm32
 504     68/push  _test-input-stream/imm32
 505     # . . call
 506     e8/call  write/disp32
 507     # . . discard args
 508     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 509     # . write(_test-input-stream, "== data 0x1000\n")
 510     # . . push args
 511     68/push  "== data 0x1000\n"/imm32
 512     68/push  _test-input-stream/imm32
 513     # . . call
 514     e8/call  write/disp32
 515     # . . discard args
 516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 517     # . write(_test-input-stream, "x:\n")
 518     # . . push args
 519     68/push  "x:\n"/imm32
 520     68/push  _test-input-stream/imm32
 521     # . . call
 522     e8/call  write/disp32
 523     # . . discard args
 524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 525     # . write(_test-input-stream, "01\n")
 526     # . . push args
 527     68/push  "01\n"/imm32
 528     68/push  _test-input-stream/imm32
 529     # . . call
 530     e8/call  write/disp32
 531     # . . discard args
 532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 533     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
 534     # . . push args
 535     68/push  _test-output-buffered-file/imm32
 536     68/push  _test-input-buffered-file/imm32
 537     # . . call
 538     e8/call  subx-survey/disp32
 539     # . . discard args
 540     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 541     # check trace
 542 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
 568     # . check-trace-contains("label 'x' is at address 0x00001079.", msg)
 569     # . . push args
 570     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
 571     68/push  "label 'x' is at address 0x00001079."/imm32
 572     # . . call
 573     e8/call  check-trace-contains/disp32
 574     # . . discard args
 575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 576     # . check-trace-contains("segment 'code' starts at address 0x00000074.", msg)
 577     # . . push args
 578     68/push  "F - test-subx-survey-computes-addresses/1"/imm32
 579     68/push  "segment 'code' starts at address 0x00000074."/imm32
 580     # . . call
 581     e8/call  check-trace-contains/disp32
 582     # . . discard args
 583     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 584     # . check-trace-contains("segment 'code' has size 0x00000005.", msg)
 585     # . . push args
 586     68/push  "F - test-subx-survey-computes-addresses/2"/imm32
 587     68/push  "segment 'code' has size 0x00000005."/imm32
 588     # . . call
 589     e8/call  check-trace-contains/disp32
 590     # . . discard args
 591     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 592     # . check-trace-contains("segment 'data' starts at address 0x00001079.", msg)
 593     # . . push args
 594     68/push  "F - test-subx-survey-computes-addresses/3"/imm32
 595     68/push  "segment 'data' starts at address 0x00001079."/imm32
 596     # . . call
 597     e8/call  check-trace-contains/disp32
 598     # . . discard args
 599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 600     # . epilogue
 601     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 602     5d/pop-to-ebp
 603     c3/return
 604 
 605 # global scratch space for compute-offsets in the data segment
 606 == data
 607 
 608 compute-offsets:file-offset:  # int
 609   0/imm32
 610 compute-offsets:segment-offset:  # int
 611   0/imm32
 612 compute-offsets:word-slice:
 613   0/imm32/start
 614   0/imm32/end
 615 compute-offsets:segment-tmp:  # slice
 616   0/imm32/start
 617   0/imm32/end
 618 
 619 == code
 620 
 621 compute-offsets:  # in: (addr stream byte), segments: (addr stream {string, segment-info}), labels: (addr stream {string, label-info})
 622     # skeleton:
 623     #   for lines in 'in'
 624     #     for words in line
 625     #       switch word
 626     #         case 1
 627     #         case 2
 628     #         ...
 629     #         default
 630     #
 631     # pseudocode:
 632     #   curr-segment-name: (addr string) = 0
 633     #   var line: (stream byte 512)
 634     #   while true                                  # line loop
 635     #     clear-stream(line)
 636     #     read-line(in, line)
 637     #     if (line->write == 0) break               # end of file
 638     #     while true                                # word loop
 639     #       word-slice = next-word(line)
 640     #       if slice-empty?(word-slice)             # end of line
 641     #         break
 642     #       else if slice-starts-with?(word-slice, "#")  # comment
 643     #         break                                 # end of line
 644     #       else if slice-equal?(word-slice, "==")
 645     #         if curr-segment-name != 0
 646     #           seg = get-or-insert(segments, curr-segment-name)
 647     #           seg->size = *file-offset - seg->file-offset
 648     #           trace("segment '", curr-segment-name, "' has size ", seg->size)
 649     #         segment-tmp = next-word(line)
 650     #         curr-segment-name = slice-to-string(segment-tmp)
 651     #         if empty?(curr-segment-name)
 652     #           abort
 653     #         segment-tmp = next-word(line)
 654     #         if slice-empty?(segment-tmp)
 655     #           abort
 656     #         seg = get-or-insert(segments, curr-segment-name)
 657     #         seg->starting-address = parse-hex-int(segment-tmp)
 658     #         seg->file-offset = *file-offset
 659     #         trace("segment '", curr-segment-name, "' is at file offset ", seg->file-offset)
 660     #         segment-offset = 0
 661     #         break  (next line)
 662     #       else if is-label?(word-slice)
 663     #         strip trailing ':' from word-slice
 664     #         x: (addr label-info) = get-or-insert(labels, name)
 665     #         x->segment-name = curr-segment-name
 666     #         trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
 667     #         x->segment-offset = segment-offset
 668     #         trace("label '", word-slice, "' is at segment offset ", segment-offset, ".")
 669     #         # labels occupy no space, so no need to increment offsets
 670     #       else
 671     #         width = compute-width-of-slice(word-slice)
 672     #         *segment-offset += width
 673     #         *file-offset += width
 674     #
 675     # . prologue
 676     55/push-ebp
 677     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 678     # . save registers
 679     50/push-eax
 680     51/push-ecx
 681     52/push-edx
 682     53/push-ebx
 683     56/push-esi
 684     57/push-edi
 685     # curr-segment-name/esi = 0
 686     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
 687     # file-offset = 0
 688     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:file-offset/disp32  0/imm32               # copy to *compute-offsets:word-slice
 689     # segment-offset = 0
 690     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:segment-offset/disp32  0/imm32            # copy to *compute-offsets:word-slice
 691     # var line/ecx: (stream byte 512)
 692     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 693     68/push  0x200/imm32/length
 694     68/push  0/imm32/read
 695     68/push  0/imm32/write
 696     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 697 $compute-offsets:line-loop:
 698     # clear-stream(line)
 699     51/push-ecx
 700     e8/call  clear-stream/disp32
 701     # . discard args
 702     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 703     # read-line(in, line)
 704     51/push-ecx
 705     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 706     e8/call  read-line/disp32
 707     # . discard args
 708     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 709     # if (line->write == 0) break
 710     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
 711     3d/compare-eax-and  0/imm32
 712     0f 84/jump-if-=  $compute-offsets:break-line-loop/disp32
 713 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 746 $compute-offsets:word-loop:
 747     # edx = word-slice
 748     ba/copy-to-edx  compute-offsets:word-slice/imm32
 749     # next-word(line, word-slice/edx)
 750     52/push-edx
 751     51/push-ecx
 752     e8/call  next-word/disp32
 753     # . discard args
 754     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 755 +-- 74 lines: #?     # dump word-slice and maybe curr-segment-name -------------------------------------------------------------------------------------------
 829 $compute-offsets:case-empty:
 830     # if slice-empty?(word/edx) break
 831     # . eax = slice-empty?(word/edx)
 832     52/push-edx
 833     e8/call  slice-empty?/disp32
 834     # . . discard args
 835     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 836     # . if (eax != false) break
 837     3d/compare-eax-and  0/imm32/false
 838     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
 839 $compute-offsets:case-comment:
 840     # if slice-starts-with?(word-slice, "#") continue
 841     68/push  "#"/imm32
 842     52/push-edx
 843     e8/call  slice-starts-with?/disp32
 844     # . . discard args
 845     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 846     # . if (eax != false) break
 847     3d/compare-eax-and  0/imm32/false
 848     0f 85/jump-if-!=  $compute-offsets:line-loop/disp32
 849 $compute-offsets:case-segment-header:
 850     # if (!slice-equal?(word-slice/edx, "==")) goto next case
 851     # . eax = slice-equal?(word-slice/edx, "==")
 852     68/push  "=="/imm32
 853     52/push-edx
 854     e8/call  slice-equal?/disp32
 855     # . . discard args
 856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 857     # . if (eax == false) goto next case
 858     3d/compare-eax-and  0/imm32/false
 859     0f 84/jump-if-=  $compute-offsets:case-label/disp32
 860     # if (curr-segment-name == 0) goto construct-next-segment
 861     81          7/subop/compare     3/mod/direct    6/rm32/esi    .           .             .           .           .               0/imm32           # compare esi
 862     74/jump-if-=  $compute-offsets:construct-next-segment/disp8
 863     # seg/eax = get-or-insert(segments, curr-segment-name, row-size=16)
 864     # . . push args
 865     68/push  0x10/imm32/row-size
 866     56/push-esi
 867     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 868     # . . call
 869     e8/call  get-or-insert/disp32
 870     # . . discard args
 871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 872     # seg->size = file-offset - seg->file-offset
 873     # . save ecx
 874     51/push-ecx
 875     # . ebx = *file-offset
 876     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:file-offset/disp32 # copy *file-offset to ebx
 877     # . ecx = seg->file-offset
 878     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(eax+4) to ecx
 879     # . ebx -= ecx
 880     29/subtract                     3/mod/direct    3/rm32/ebx    .           .             .           1/r32/ecx   .               .                 # subtract ecx from ebx
 881     # . seg->size = ebx
 882     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   8/disp8         .                 # copy ebx to *(eax+8)
 883     # . restore ecx
 884     59/pop-to-ecx
 885     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
 886     # . . push args
 887     68/push  "."/imm32
 888     53/push-ebx
 889     68/push  "' has size "/imm32
 890     56/push-esi
 891     68/push  "segment '"/imm32
 892     # . . call
 893     e8/call  trace-sssns/disp32
 894     # . . discard args
 895     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
 896 $compute-offsets:construct-next-segment:
 897     # next-word(line, segment-tmp)
 898     68/push  compute-offsets:segment-tmp/imm32
 899     51/push-ecx
 900     e8/call  next-word/disp32
 901     # . discard args
 902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 903 +-- 45 lines: #?     # dump curr-segment-name if not null (clobbering eax) -----------------------------------------------------------------------------------
 948 $compute-offsets:update-curr-segment-name:
 949     # curr-segment-name = slice-to-string(segment-tmp)
 950     # . eax = slice-to-string(Heap, segment-tmp)
 951     # . . push args
 952     68/push  compute-offsets:segment-tmp/imm32
 953     68/push  Heap/imm32
 954     # . . call
 955     e8/call  slice-to-string/disp32
 956     # . . discard args
 957     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 958     # . curr-segment-name = eax
 959     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
 960     # if empty?(curr-segment-name) abort
 961     # . if (eax == 0) abort
 962     3d/compare-eax-and  0/imm32
 963     0f 84/jump-if-=  $compute-offsets:abort/disp32
 964     # next-word(line, segment-tmp)
 965     68/push  compute-offsets:segment-tmp/imm32
 966     51/push-ecx
 967     e8/call  next-word/disp32
 968     # . discard args
 969     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 970     # if slice-empty?(segment-tmp) abort
 971     # . eax = slice-empty?(segment-tmp)
 972     68/push  compute-offsets:segment-tmp/imm32
 973     e8/call  slice-empty?/disp32
 974     # . . discard args
 975     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 976     # . if (eax != false) abort
 977     3d/compare-eax-and  0/imm32/false
 978     0f 85/jump-if-!=  $compute-offsets:abort/disp32
 979     # seg/ebx = get-or-insert(segments, curr-segment-name, row-size=16)
 980     # . . push args
 981     68/push  0x10/imm32/row-size
 982     56/push-esi
 983     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 984     # . . call
 985     e8/call  get-or-insert/disp32
 986     # . . discard args
 987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 988     # . ebx = eax
 989     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
 990     # seg->address = parse-hex-int(segment-tmp)
 991     # . eax = parse-hex-int(segment-tmp)
 992     68/push  compute-offsets:segment-tmp/imm32
 993     e8/call  parse-hex-int/disp32
 994     # . . discard args
 995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 996     # . seg->address = eax
 997     89/copy                         0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to *ebx
 998     # seg->file-offset = *file-offset
 999     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:file-offset/disp32 # copy *file-offset to eax
1000     89/copy                         1/mod/*+disp8   3/rm32/ebx    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(ebx+4)
1001     # trace-sssns("segment '", curr-segment-name, "' is at file offset ", seg->file-offset, "")
1002     # . . push args
1003     68/push  "."/imm32
1004     50/push-eax
1005     68/push  "' is at file offset "/imm32
1006     56/push-esi
1007     68/push  "segment '"/imm32
1008     # . . call
1009     e8/call  trace-sssns/disp32
1010     # . . discard args
1011     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1012     # segment-offset = 0
1013     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     compute-offsets:segment-offset/disp32  0/imm32           # copy to *segment-offset
1014     # break
1015     e9/jump $compute-offsets:line-loop/disp32
1016 $compute-offsets:case-label:
1017     # if (!is-label?(word-slice/edx)) goto next case
1018     # . eax = is-label?(word-slice/edx)
1019     # . . push args
1020     52/push-edx
1021     # . . call
1022     e8/call  is-label?/disp32
1023     # . . discard args
1024     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1025     # . if (eax == false) goto next case
1026     3d/compare-eax-and  0/imm32/false
1027     74/jump-if-=  $compute-offsets:case-default/disp8
1028     # strip trailing ':' from word-slice
1029     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
1030     # x/eax = leaky-get-or-insert-slice(labels, word-slice, row-size=16)
1031     # . . push args
1032     68/push  0x10/imm32/row-size
1033     52/push-edx
1034     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
1035     # . . call
1036     e8/call  leaky-get-or-insert-slice/disp32
1037     # . . discard args
1038     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1039 $compute-offsets:save-label-offset:
1040     # x->segment-name = curr-segment-name
1041     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to *eax
1042     # trace-slsss("label '" word-slice/edx "' is in segment '" current-segment-name "'.")
1043     # . . push args
1044     68/push  "'."/imm32
1045     56/push-esi
1046     68/push  "' is in segment '"/imm32
1047     52/push-edx
1048     68/push  "label '"/imm32
1049     # . . call
1050     e8/call  trace-slsss/disp32
1051     # . . discard args
1052     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1053     # x->segment-offset = segment-offset
1054     # . ebx = segment-offset
1055     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:segment-offset/disp32  # copy *segment-offset to ebx
1056     # . x->segment-offset = ebx
1057     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   4/disp8         .                 # copy ebx to *(eax+4)
1058     # trace-slsns("label '" word-slice/edx "' is at segment offset " *segment-offset/eax ".")
1059     # . . eax = file-offset
1060     b8/copy-to-eax compute-offsets:segment-offset/imm32
1061     # . . eax = *file-offset/eax
1062     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           0/r32/eax   .               .                 # copy *eax to eax
1063     # . . push args
1064     68/push  "."/imm32
1065     50/push-eax
1066     68/push  "' is at segment offset "/imm32
1067     52/push-edx
1068     68/push  "label '"/imm32
1069     # . . call
1070     e8/call  trace-slsns/disp32
1071     # . . discard args
1072     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1073     # continue
1074     e9/jump  $compute-offsets:word-loop/disp32
1075 $compute-offsets:case-default:
1076     # width/eax = compute-width-of-slice(word-slice)
1077     # . . push args
1078     52/push-edx
1079     # . . call
1080     e8/call compute-width-of-slice/disp32
1081     # . . discard args
1082     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1083     # segment-offset += width
1084     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:segment-offset/disp32 # add eax to *segment-offset
1085     # file-offset += width
1086     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:file-offset/disp32 # add eax to *file-offset
1087 +-- 41 lines: #?     # dump segment-offset -------------------------------------------------------------------------------------------------------------------
1128     e9/jump $compute-offsets:word-loop/disp32
1129 $compute-offsets:break-line-loop:
1130     # seg/eax = get-or-insert(segments, curr-segment-name, row-size=16)
1131     # . . push args
1132     68/push  0x10/imm32/row-size
1133     56/push-esi
1134     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1135     # . . call
1136     e8/call  get-or-insert/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1139     # seg->size = file-offset - seg->file-offset
1140     # . save ecx
1141     51/push-ecx
1142     # . ebx = *file-offset
1143     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:file-offset/disp32 # copy *file-offset to ebx
1144     # . ecx = seg->file-offset
1145     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(eax+4) to ecx
1146     # . ebx -= ecx
1147     29/subtract                     3/mod/direct    3/rm32/ebx    .           .             .           1/r32/ecx   .               .                 # subtract ecx from ebx
1148     # . seg->size = ebx
1149     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   8/disp8         .                 # copy ebx to *(eax+8)
1150     # . restore ecx
1151     59/pop-to-ecx
1152     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
1153     # . trace-sssns("segment '", curr-segment-name, "' has size ", ebx, ".")
1154     # . . push args
1155     68/push  "."/imm32
1156     53/push-ebx
1157     68/push  "' has size "/imm32
1158     56/push-esi
1159     68/push  "segment '"/imm32
1160     # . . call
1161     e8/call  trace-sssns/disp32
1162     # . . discard args
1163     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1164 $compute-offsets:end:
1165     # . reclaim locals
1166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x20c/imm32       # add to esp
1167     # . restore registers
1168     5f/pop-to-edi
1169     5e/pop-to-esi
1170     5b/pop-to-ebx
1171     5a/pop-to-edx
1172     59/pop-to-ecx
1173     58/pop-to-eax
1174     # . epilogue
1175     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1176     5d/pop-to-ebp
1177     c3/return
1178 
1179 $compute-offsets:abort:
1180     # . _write(2/stderr, error)
1181     # . . push args
1182     68/push  "'==' must be followed by segment name and segment-start\n"/imm32
1183     68/push  2/imm32/stderr
1184     # . . call
1185     e8/call  _write/disp32
1186     # . . discard args
1187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1188     # . syscall(exit, 1)
1189     bb/copy-to-ebx  1/imm32
1190     b8/copy-to-eax  1/imm32/exit
1191     cd/syscall  0x80/imm8
1192     # never gets here
1193 
1194 test-compute-offsets:
1195     # input:
1196     #   == code 0x1
1197     #   ab x/imm32  # skip comment
1198     #   == data 0x1000
1199     #   00
1200     #   x:
1201     #     34
1202     #
1203     # trace contains (in any order):
1204     #   segment 'code' is at file offset 0x0.
1205     #   segment 'code' has size 0x5.
1206     #   segment 'data' is at file offset 0x5.
1207     #   segment 'data' has size 0x2.
1208     #   label 'x' is in segment 'data'.
1209     #   label 'x' is at segment offset 0x1.
1210     #
1211     # . prologue
1212     55/push-ebp
1213     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1214     # setup
1215     # . clear-stream(_test-input-stream)
1216     # . . push args
1217     68/push  _test-input-stream/imm32
1218     # . . call
1219     e8/call  clear-stream/disp32
1220     # . . discard args
1221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1222     # var segments/ecx: (stream byte 2*16)
1223     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x20/imm32        # subtract from esp
1224     68/push  0x20/imm32/length
1225     68/push  0/imm32/read
1226     68/push  0/imm32/write
1227     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1228     # var labels/edx: (stream byte 2*16)
1229     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x20/imm32        # subtract from esp
1230     68/push  0x20/imm32/length
1231     68/push  0/imm32/read
1232     68/push  0/imm32/write
1233     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1234     # initialize input
1235     # . write(_test-input-stream, "== code 0x1\n")
1236     # . . push args
1237     68/push  "== code 0x1\n"/imm32
1238     68/push  _test-input-stream/imm32
1239     # . . call
1240     e8/call  write/disp32
1241     # . . discard args
1242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1243     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
1244     # . . push args
1245     68/push  "ab x/imm32  # skip comment\n"/imm32
1246     68/push  _test-input-stream/imm32
1247     # . . call
1248     e8/call  write/disp32
1249     # . . discard args
1250     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1251     # . write(_test-input-stream, "== data 0x1000\n")
1252     # . . push args
1253     68/push  "== data 0x1000\n"/imm32
1254     68/push  _test-input-stream/imm32
1255     # . . call
1256     e8/call  write/disp32
1257     # . . discard args
1258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1259     # . write(_test-input-stream, "00\n")
1260     # . . push args
1261     68/push  "00\n"/imm32
1262     68/push  _test-input-stream/imm32
1263     # . . call
1264     e8/call  write/disp32
1265     # . . discard args
1266     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1267     # . write(_test-input-stream, "x:\n")
1268     # . . push args
1269     68/push  "x:\n"/imm32
1270     68/push  _test-input-stream/imm32
1271     # . . call
1272     e8/call  write/disp32
1273     # . . discard args
1274     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1275     # . write(_test-input-stream, "34\n")
1276     # . . push args
1277     68/push  "34\n"/imm32
1278     68/push  _test-input-stream/imm32
1279     # . . call
1280     e8/call  write/disp32
1281     # . . discard args
1282     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1283     # compute-offsets(_test-input-stream, segments, labels)
1284     # . . push args
1285     52/push-edx
1286     51/push-ecx
1287     68/push  _test-input-stream/imm32
1288     # . . call
1289     e8/call  compute-offsets/disp32
1290     # . . discard args
1291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32        # add to esp
1292 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1318     # check trace
1319     # . check-trace-contains("segment 'code' is at file offset 0x00000000.", msg)
1320     # . . push args
1321     68/push  "F - test-compute-offsets/0"/imm32
1322     68/push  "segment 'code' is at file offset 0x00000000."/imm32
1323     # . . call
1324     e8/call  check-trace-contains/disp32
1325     # . . discard args
1326     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1327     # . check-trace-contains("segment 'code' has size 0x00000005", msg)
1328     # . . push args
1329     68/push  "F - test-compute-offsets/1"/imm32
1330     68/push  "segment 'code' has size 0x00000005."/imm32
1331     # . . call
1332     e8/call  check-trace-contains/disp32
1333     # . . discard args
1334     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1335     # . check-trace-contains("segment 'data' is at file offset 0x00000005.", msg)
1336     # . . push args
1337     68/push  "F - test-compute-offsets/2"/imm32
1338     68/push  "segment 'data' is at file offset 0x00000005."/imm32
1339     # . . call
1340     e8/call  check-trace-contains/disp32
1341     # . . discard args
1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1343     # . check-trace-contains("segment 'data' has size 0x00000002.", msg)
1344     # . . push args
1345     68/push  "F - test-compute-offsets/3"/imm32
1346     68/push  "segment 'data' has size 0x00000002."/imm32
1347     # . . call
1348     e8/call  check-trace-contains/disp32
1349     # . . discard args
1350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1351     # . check-trace-contains("label 'x' is in segment 'data'.", msg)
1352     # . . push args
1353     68/push  "F - test-compute-offsets/4"/imm32
1354     68/push  "label 'x' is in segment 'data'."/imm32
1355     # . . call
1356     e8/call  check-trace-contains/disp32
1357     # . . discard args
1358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1359     # . check-trace-contains("label 'x' is at segment offset 0x00000001.", msg)
1360     # . . push args
1361     68/push  "F - test-compute-offsets/5"/imm32
1362     68/push  "label 'x' is at segment offset 0x00000001."/imm32
1363     # . . call
1364     e8/call  check-trace-contains/disp32
1365     # . . discard args
1366     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1367     # . check-ints-equal(labels->write, 0x10, msg)
1368     # . . push args
1369     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
1370     68/push  0x10/imm32/1-entry
1371     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
1372     # . . call
1373     e8/call  check-ints-equal/disp32
1374     # . . discard args
1375     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1376     # . epilogue
1377     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1378     5d/pop-to-ebp
1379     c3/return
1380 
1381 compute-addresses:  # segments: (addr stream {string, segment-info}), labels: (addr stream {string, label-info})
1382     # pseudocode:
1383     #   srow: (addr segment-info) = segments->data
1384     #   max = &segments->data[segments->write]
1385     #   num-segments = segments->write / 16
1386     #   starting-offset = 0x34 + (num-segments * 0x20)
1387     #   while true
1388     #     if (srow >= max) break
1389     #     s->file-offset += starting-offset
1390     #     s->address &= 0xfffff000  # clear last 12 bits for p_align
1391     #     s->address += (s->file-offset & 0x00000fff)
1392     #     trace-sssns("segment " s->key " starts at address " s->address)
1393     #     srow += 16  # row-size
1394     #   lrow: (addr label-info) = labels->data
1395     #   max = &labels->data[labels->write]
1396     #   while true
1397     #     if (lrow >= max) break
1398     #     var seg-name: (addr string) = lrow->segment-name
1399     #     var label-seg: (addr segment-info) = get(segments, seg-name)
1400     #     lrow->address = label-seg->address + lrow->segment-offset
1401     #     trace-sssns("label " lrow->key " is at address " lrow->address)
1402     #     lrow += 16  # row-size
1403     #
1404     # . prologue
1405     55/push-ebp
1406     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1407     # . save registers
1408     50/push-eax
1409     51/push-ecx
1410     52/push-edx
1411     53/push-ebx
1412     56/push-esi
1413     57/push-edi
1414     # esi = segments
1415     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
1416     # starting-offset/edi = 0x34 + (num-segments * 0x20)  # make room for ELF headers
1417     # . edi = segments->write / 16 (row-size)
1418     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           7/r32/edi   .               .                 # copy *esi to edi
1419     c1/shift    5/subop/logic-right 3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi right by 4 bits, while padding zeroes
1420     # . edi = (edi * 0x20) + 0x34
1421     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               5/imm8            # shift edi left by 5 bits
1422     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               0x34/imm32        # add to edi
1423     # srow/eax = segments->data
1424     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy esi+12 to eax
1425     # max/ecx = &segments->data[segments->write]
1426     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
1427     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           6/r32/esi   .               .                 # add esi to ecx
1428 $compute-addresses:segment-loop:
1429     # if (srow >= max) break
1430     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
1431     73/jump-if-addr>=  $compute-addresses:segment-break/disp8
1432     # srow->file-offset += starting-offset
1433     01/add                          1/mod/*+disp8   0/rm32/eax    .           .             .           7/r32/edi   8/disp8         .                 # add edi to *(eax+8)
1434     # clear last 12 bits of srow->address for p_align=0x1000
1435     # . edx = srow->address
1436     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           2/r32/edx   4/disp8         .                 # copy *(eax+4) to edx
1437     # . edx &= 0xfffff000
1438     81          4/subop/and         3/mod/direct    2/rm32/edx    .           .             .           .           .               0xfffff000/imm32  # bitwise and of edx
1439     # update last 12 bits from srow->file-offset
1440     # . ebx = srow->file-offset
1441     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   8/disp8         .                 # copy *(eax+8) to ebx
1442     # . ebx &= 0xfff
1443     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x00000fff/imm32  # bitwise and of ebx
1444     # . srow->address = edx | ebx
1445     09/or                           3/mod/direct    2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # edx = bitwise OR with ebx
1446     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           2/r32/edx   4/disp8         .                 # copy edx to *(eax+4)
1447     # trace-sssns("segment " srow " starts at address " srow->address ".")
1448     # . . push args
1449     68/push  "."/imm32
1450     52/push-edx
1451     68/push  "' starts at address "/imm32
1452     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1453     68/push  "segment '"/imm32
1454     # . . call
1455     e8/call  trace-sssns/disp32
1456     # . . discard args
1457     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1458     # srow += 16  # size of row
1459     05/add-to-eax  0x10/imm32
1460     eb/jump  $compute-addresses:segment-loop/disp8
1461 $compute-addresses:segment-break:
1462 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1488     # esi = labels
1489     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
1490     # lrow/eax = labels->data
1491     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy esi+12 to eax
1492     # max/ecx = &labels->data[labels->write]
1493     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
1494     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           6/r32/esi   .               .                 # add esi to ecx
1495 $compute-addresses:label-loop:
1496     # if (lrow >= max) break
1497     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
1498     0f 83/jump-if-addr>=  $compute-addresses:end/disp32
1499 +-- 26 lines: #?     # dump lrow->key ------------------------------------------------------------------------------------------------------------------------
1525     # var seg-name/edx: (addr array byte) = lrow->segment-name
1526     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           2/r32/edx   4/disp8         .                 # copy *eax to edx
1527 +-- 26 lines: #?     # dump seg-name -------------------------------------------------------------------------------------------------------------------------
1553     # var label-seg/edx: (addr segment-info) = get(segments, seg-name, row-size=16, "segment table")
1554     # . save eax
1555     50/push-eax
1556     # . eax = get(segments, seg-name, row-size=16)
1557     # . . push args
1558     68/push  "segment table"/imm32
1559     68/push  0x10/imm32/row-size
1560     52/push-edx
1561     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1562     # . . call
1563     e8/call  get/disp32
1564     # . . discard args
1565     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1566     # . edx = eax
1567     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
1568     # . restore eax
1569     58/pop-to-eax
1570     # ebx = label-seg->address
1571     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
1572     # ebx += lrow->segment-offset
1573     03/add                          1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   8/disp8         .                 # add *(eax+8) to ebx
1574     # lrow->address = ebx
1575     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy ebx to *(eax+12)
1576     # trace-sssns("label " lrow->key " is at address " lrow->address ".")
1577     # . . push args
1578     68/push  "."/imm32
1579     53/push-ebx
1580     68/push  "' is at address "/imm32
1581     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1582     68/push  "label '"/imm32
1583     # . . call
1584     e8/call  trace-sssns/disp32
1585     # . . discard args
1586     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1587     # lrow += 16  # size of row
1588     05/add-to-eax  0x10/imm32
1589     e9/jump  $compute-addresses:label-loop/disp32
1590 $compute-addresses:end:
1591     # . restore registers
1592     5f/pop-to-edi
1593     5e/pop-to-esi
1594     5b/pop-to-ebx
1595     5a/pop-to-edx
1596     59/pop-to-ecx
1597     58/pop-to-eax
1598     # . epilogue
1599     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1600     5d/pop-to-ebp
1601     c3/return
1602 
1603 test-compute-addresses:
1604     # input:
1605     #   segments:
1606     #     - 'a': {0x1000, 0, 5}
1607     #     - 'b': {0x2018, 5, 1}
1608     #     - 'c': {0x5444, 6, 12}
1609     #   labels:
1610     #     - 'l1': {'a', 3, 0}
1611     #     - 'l2': {'b', 0, 0}
1612     #
1613     # trace contains in any order (comments in parens):
1614     #   segment 'a' starts at address 0x00001094.  (0x34 + 0x20 for each segment)
1615     #   segment 'b' starts at address 0x00002099.  (0x018 discarded)
1616     #   segment 'c' starts at address 0x0000509a.  (0x444 discarded)
1617     #   label 'l1' is at address 0x00001097.       (0x1094 + segment-offset 3)
1618     #   label 'l2' is at address 0x00002099.       (0x2099 + segment-offset 0)
1619     #
1620     # . prologue
1621     55/push-ebp
1622     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1623     # setup
1624     # . var segments/ecx: (stream byte 10*16)
1625     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
1626     68/push  0xa0/imm32/length
1627     68/push  0/imm32/read
1628     68/push  0/imm32/write
1629     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1630     # . var labels/edx: (stream byte 512*16)
1631     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
1632     68/push  0x2000/imm32/length
1633     68/push  0/imm32/read
1634     68/push  0/imm32/write
1635     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1636     # . stream-add4(segments, "a", 0x1000, 0, 5)
1637     68/push  5/imm32/segment-size
1638     68/push  0/imm32/file-offset
1639     68/push  0x1000/imm32/start-address
1640     68/push  "a"/imm32/segment-name
1641     51/push-ecx
1642     # . . call
1643     e8/call  stream-add4/disp32
1644     # . . discard args
1645     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1646     # . stream-add4(segments, "b", 0x2018, 5, 1)
1647     68/push  1/imm32/segment-size
1648     68/push  5/imm32/file-offset
1649     68/push  0x2018/imm32/start-address
1650     68/push  "b"/imm32/segment-name
1651     51/push-ecx
1652     # . . call
1653     e8/call  stream-add4/disp32
1654     # . . discard args
1655     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1656     # . stream-add4(segments, "c", 0x5444, 6, 12)
1657     68/push  0xc/imm32/segment-size
1658     68/push  6/imm32/file-offset
1659     68/push  0x5444/imm32/start-address
1660     68/push  "c"/imm32/segment-name
1661     51/push-ecx
1662     # . . call
1663     e8/call  stream-add4/disp32
1664     # . . discard args
1665     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1666     # . stream-add4(labels, "l1", "a", 3, 0)
1667     68/push  0/imm32/label-address
1668     68/push  3/imm32/segment-offset
1669     68/push  "a"/imm32/segment-name
1670     68/push  "l1"/imm32/label-name
1671     52/push-edx
1672     # . . call
1673     e8/call  stream-add4/disp32
1674     # . . discard args
1675     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1676     # . stream-add4(labels, "l2", "b", 0, 0)
1677     68/push  0/imm32/label-address
1678     68/push  0/imm32/segment-offset
1679     68/push  "b"/imm32/segment-name
1680     68/push  "l2"/imm32/label-name
1681     52/push-edx
1682     # . . call
1683     e8/call  stream-add4/disp32
1684     # . . discard args
1685     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1686     # component under test
1687     # . compute-addresses(segments, labels)
1688     # . . push args
1689     52/push-edx
1690     51/push-ecx
1691     # . . call
1692     e8/call  compute-addresses/disp32
1693     # . . discard args
1694     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1695     # checks
1696 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1722     # . check-trace-contains("segment 'a' starts at address 0x00001094.", msg)
1723     # . . push args
1724     68/push  "F - test-compute-addresses/0"/imm32
1725     68/push  "segment 'a' starts at address 0x00001094."/imm32
1726     # . . call
1727     e8/call  check-trace-contains/disp32
1728     # . . discard args
1729     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1730     # . check-trace-contains("segment 'b' starts at address 0x00002099.", msg)
1731     # . . push args
1732     68/push  "F - test-compute-addresses/1"/imm32
1733     68/push  "segment 'b' starts at address 0x00002099."/imm32
1734     # . . call
1735     e8/call  check-trace-contains/disp32
1736     # . . discard args
1737     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1738     # . check-trace-contains("segment 'c' starts at address 0x0000509a.", msg)
1739     # . . push args
1740     68/push  "F - test-compute-addresses/2"/imm32
1741     68/push  "segment 'c' starts at address 0x0000509a."/imm32
1742     # . . call
1743     e8/call  check-trace-contains/disp32
1744     # . . discard args
1745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1746     # . check-trace-contains("label 'l1' is at address 0x00001097.", msg)
1747     # . . push args
1748     68/push  "F - test-compute-addresses/3"/imm32
1749     68/push  "label 'l1' is at address 0x00001097."/imm32
1750     # . . call
1751     e8/call  check-trace-contains/disp32
1752     # . . discard args
1753     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1754     # . check-trace-contains("label 'l2' is at address 0x00002099.", msg)
1755     # . . push args
1756     68/push  "F - test-compute-addresses/4"/imm32
1757     68/push  "label 'l2' is at address 0x00002099."/imm32
1758     # . . call
1759     e8/call  check-trace-contains/disp32
1760     # . . discard args
1761     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1762     # . check-ints-equal(labels->write, 0x20, msg)
1763     # . . push args
1764     68/push  "F - test-compute-addresses/maintains-labels-write-index"/imm32
1765     68/push  0x20/imm32/2-entries
1766     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
1767     # . . call
1768     e8/call  check-ints-equal/disp32
1769     # . . discard args
1770     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1771     # . epilogue
1772     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1773     5d/pop-to-ebp
1774     c3/return
1775 
1776 test-compute-addresses-large-segments:
1777     # input:
1778     #   segments:
1779     #     - 'a': {0x1000, 0, 0x5604}
1780     #     - 'b': {0x2018, 0x5604, 1}
1781     #   labels:
1782     #     - 'l1': {'a', 3, 0}
1783     #
1784     # trace contains in any order (comments in parens):
1785     #   segment 'a' starts at address 0x00001074.  (0x34 + 0x20 for each segment)
1786     #   segment 'b' starts at address 0x00002678.  (0x018 discarded; last 3 nibbles from 0x1074 + 0x5604)
1787     #   label 'l1' is at address 0x00001077.       (0x1074 + segment-offset 3)
1788     #
1789     # . prologue
1790     55/push-ebp
1791     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1792     # setup
1793     # . var segments/ecx: (stream byte 10*16)
1794     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
1795     68/push  0xa0/imm32/length
1796     68/push  0/imm32/read
1797     68/push  0/imm32/write
1798     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1799     # . var labels/edx: (stream byte 512*16)
1800     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
1801     68/push  0x2000/imm32/length
1802     68/push  0/imm32/read
1803     68/push  0/imm32/write
1804     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1805     # . stream-add4(segments, "a", 0x1000, 0, 0x5604)
1806     68/push  0x5604/imm32/segment-size
1807     68/push  0/imm32/file-offset
1808     68/push  0x1000/imm32/start-address
1809     68/push  "a"/imm32/segment-name
1810     51/push-ecx
1811     # . . call
1812     e8/call  stream-add4/disp32
1813     # . . discard args
1814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1815     # . stream-add4(segments, "b", 0x2018, 0x5604, 1)
1816     68/push  1/imm32/segment-size
1817     68/push  0x5604/imm32/file-offset
1818     68/push  0x2018/imm32/start-address
1819     68/push  "b"/imm32/segment-name
1820     51/push-ecx
1821     # . . call
1822     e8/call  stream-add4/disp32
1823     # . . discard args
1824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1825     # . stream-add4(labels, "l1", "a", 3, 0)
1826     68/push  0/imm32/label-address
1827     68/push  3/imm32/segment-offset
1828     68/push  "a"/imm32/segment-name
1829     68/push  "l1"/imm32/label-name
1830     52/push-edx
1831     # . . call
1832     e8/call  stream-add4/disp32
1833     # . . discard args
1834     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1835     # component under test
1836     # . compute-addresses(segments, labels)
1837     # . . push args
1838     52/push-edx
1839     51/push-ecx
1840     # . . call
1841     e8/call  compute-addresses/disp32
1842     # . . discard args
1843     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1844     # checks
1845     # . check-trace-contains("segment 'a' starts at address 0x00001074.", msg)
1846     # . . push args
1847     68/push  "F - test-compute-addresses-large-segments/0"/imm32
1848     68/push  "segment 'a' starts at address 0x00001074."/imm32
1849     # . . call
1850     e8/call  check-trace-contains/disp32
1851     # . . discard args
1852     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1853     # . check-trace-contains("segment 'b' starts at address 0x00002678.", msg)
1854     # . . push args
1855     68/push  "F - test-compute-addresses-large-segments/1"/imm32
1856     68/push  "segment 'b' starts at address 0x00002678."/imm32
1857     # . . call
1858     e8/call  check-trace-contains/disp32
1859     # . . discard args
1860     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1861     # . check-trace-contains("label 'l1' is at address 0x00001077.", msg)
1862     # . . push args
1863     68/push  "F - test-compute-addresses-large-segments/3"/imm32
1864     68/push  "label 'l1' is at address 0x00001077."/imm32
1865     # . . call
1866     e8/call  check-trace-contains/disp32
1867     # . . discard args
1868     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1869     # . epilogue
1870     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1871     5d/pop-to-ebp
1872     c3/return
1873 
1874 emit-output:  # in: (addr stream byte), out: (addr buffered-file), segments: (addr stream {string, segment-info}), labels: (addr stream {string, label-info})
1875     # pseudocode:
1876     #   emit-headers(out, segments, labels)
1877     #   emit-segments(in, out, segments, labels)
1878     #
1879     # . prologue
1880     55/push-ebp
1881     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1882 +--  9 lines: #?     # write(2/stderr, "emit-headers\n") -----------------------------------------------------------------------------------------------------
1891     # emit-headers(out, segments, labels)
1892     # . . push args
1893     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8       .                # push *(ebp+20)
1894     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8       .                # push *(ebp+16)
1895     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8        .                # push *(ebp+12)
1896     # . . call
1897     e8/call  emit-headers/disp32
1898     # . . discard args
1899     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1900 +--  9 lines: #?     # write(2/stderr, "emit-segments\n") ----------------------------------------------------------------------------------------------------
1909     # emit-segments(in, out, segments, labels)
1910     # . . push args
1911     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8       .                # push *(ebp+20)
1912     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8       .                # push *(ebp+16)
1913     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1914     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1915     # . . call
1916     e8/call  emit-segments/disp32
1917     # . . discard args
1918     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1919 $emit-output:end:
1920     # . epilogue
1921     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1922     5d/pop-to-ebp
1923     c3/return
1924 
1925 emit-segments:  # in: (addr stream byte), out: (addr buffered-file), segments: (addr stream {string, segment-info}), labels: (addr stream {string, label-info})
1926     # pseudocode:
1927     #   var offset-of-next-instruction = 0
1928     #   var line: (stream byte 512)
1929     #   line-loop:
1930     #   while true
1931     #     clear-stream(line)
1932     #     read-line(in, line)
1933     #     if (line->write == 0) break               # end of file
1934     #     offset-of-next-instruction += num-bytes(line)
1935     #     while true
1936     #       var word-slice = next-word(line)
1937     #       if slice-empty?(word-slice)             # end of line
1938     #         break
1939     #       if slice-starts-with?(word-slice, "#")  # comment
1940     #         break
1941     #       if is-label?(word-slice)                # no need for label declarations anymore
1942     #         goto line-loop                        # don't insert empty lines
1943     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
1944     #         goto line-loop                        # don't insert empty lines
1945     #       if length(word-slice) == 2
1946     #         write-slice-buffered(out, word-slice)
1947     #         write-buffered(out, " ")
1948     #         continue
1949     #       datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
1950     #       info = get-slice(labels, datum)
1951     #       if !string-equal?(info->segment-name, "code")
1952     #         if has-metadata?(word-slice, "disp8")
1953     #           abort
1954     #         if has-metadata?(word-slice, "imm8")
1955     #           abort
1956     #         emit(out, info->address, 4)  # global variables always translate init.linux to absolute addresses
1957     #       # code segment cases
1958     #       else if has-metadata?(word-slice, "imm8")
1959     #         abort  # label should never go to imm8
1960     #       else if has-metadata?(word-slice, "imm32")
1961     #         emit(out, info->address, 4)
1962     #       else if has-metadata?(word-slice, "disp8")
1963     #         value = info->offset - offset-of-next-instruction
1964     #         emit(out, value, 1)
1965     #       else if has-metadata?(word-slice, "disp32")
1966     #         value = info->offset - offset-of-next-instruction
1967     #         emit(out, value, 4)
1968     #       else
1969     #         abort
1970     #     write-buffered(out, "\n")
1971     #
1972     # registers:
1973     #   line: ecx
1974     #   word-slice: edx
1975     #   offset-of-next-instruction: ebx
1976     #   datum: edi
1977     #   info: esi (inner loop only)
1978     #   temporaries: eax, esi (outer loop)
1979     #
1980     # . prologue
1981     55/push-ebp
1982     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1983     # . save registers
1984     50/push-eax
1985     51/push-ecx
1986     52/push-edx
1987     53/push-ebx
1988     56/push-esi
1989     57/push-edi
1990     # var line/ecx: (stream byte 512)
1991     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
1992     68/push  0x200/imm32/length
1993     68/push  0/imm32/read
1994     68/push  0/imm32/write
1995     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1996     # var word-slice/edx: slice
1997     68/push  0/imm32/end
1998     68/push  0/imm32/start
1999     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
2000     # var datum/edi: slice
2001     68/push  0/imm32/end
2002     68/push  0/imm32/start
2003     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
2004     # offset-of-next-instruction/ebx = 0
2005     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2006 $emit-segments:line-loop:
2007     # clear-stream(line)
2008     # . . push args
2009     51/push-ecx
2010     # . . call
2011     e8/call  clear-stream/disp32
2012     # . . discard args
2013     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2014     # read-line(in, line)
2015     # . . push args
2016     51/push-ecx
2017     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2018     # . . call
2019     e8/call  read-line/disp32
2020     # . . discard args
2021     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2022 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2055 $emit-segments:check-for-end-of-input:
2056     # if (line->write == 0) break
2057     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
2058     0f 84/jump-if-=  $emit-segments:end/disp32
2059     # offset-of-next-instruction += num-bytes(line)
2060     # . eax = num-bytes(line)
2061     # . . push args
2062     51/push-ecx
2063     # . . call
2064     e8/call  num-bytes/disp32
2065     # . . discard args
2066     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2067     # . ebx += eax
2068     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
2069 $emit-segments:word-loop:
2070     # next-word(line, word-slice)
2071     # . . push args
2072     52/push-edx
2073     51/push-ecx
2074     # . . call
2075     e8/call  next-word/disp32
2076     # . . discard args
2077     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2078 +-- 33 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2111 $emit-segments:check-for-end-of-line:
2112     # if (slice-empty?(word-slice)) break
2113     # . eax = slice-empty?(word-slice)
2114     # . . push args
2115     52/push-edx
2116     # . . call
2117     e8/call  slice-empty?/disp32
2118     # . . discard args
2119     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2120     # . if (eax != 0) break
2121     3d/compare-eax-and  0/imm32
2122     0f 85/jump-if-!=  $emit-segments:next-line/disp32
2123 $emit-segments:check-for-comment:
2124     # if (slice-starts-with?(word-slice, "#")) break
2125     # . start/esi = word-slice->start
2126     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
2127     # . c/eax = *start
2128     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2129     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
2130     # . if (eax == '#') break
2131     3d/compare-eax-and  0x23/imm32/hash
2132     0f 84/jump-if-=  $emit-segments:next-line/disp32
2133 $emit-segments:check-for-label:
2134     # if is-label?(word-slice) break
2135     # . eax = is-label?(word-slice)
2136     # . . push args
2137     52/push-edx
2138     # . . call
2139     e8/call  is-label?/disp32
2140     # . . discard args
2141     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2142     # . if (eax != false) break
2143     3d/compare-eax-and  0/imm32/false
2144     0f 85/jump-if-!=  $emit-segments:line-loop/disp32
2145 $emit-segments:check-for-segment-header:
2146     # if (slice-equal?(word-slice, "==")) break
2147     # . eax = slice-equal?(word-slice, "==")
2148     # . . push args
2149     68/push  "=="/imm32
2150     52/push-edx
2151     # . . call
2152     e8/call  slice-equal?/disp32
2153     # . . discard args
2154     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2155     # . if (eax != false) break
2156     3d/compare-eax-and  0/imm32/false
2157     0f 85/jump-if-!=  $emit-segments:line-loop/disp32
2158 $emit-segments:2-character:
2159     # if (length(word-slice) != 2) goto next check
2160     # . eax = length(word-slice)
2161     8b/copy                         1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(edx+4) to eax
2162     2b/subtract                     0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # subtract *edx from eax
2163     # . if (eax != 2) goto next check
2164     3d/compare-eax-and  2/imm32
2165     75/jump-if-!=  $emit-segments:check-metadata/disp8
2166     # write-slice-buffered(out, word-slice)
2167     # . . push args
2168     52/push-edx
2169     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
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     # write-buffered(out, " ")
2175     # . . push args
2176     68/push  Space/imm32
2177     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2178     # . . call
2179     e8/call  write-buffered/disp32
2180     # . . discard args
2181     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2182     # continue
2183     e9/jump  $emit-segments:word-loop/disp32
2184 $emit-segments:check-metadata:
2185     # - if we get here, 'word-slice' must be a label to be looked up
2186     # datum/edi = next-token-from-slice(word-slice->start, word-slice->end, "/")
2187     # . . push args
2188     57/push-edi
2189     68/push  0x2f/imm32/slash
2190     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2191     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2192     # . . call
2193     e8/call  next-token-from-slice/disp32
2194     # . . discard args
2195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2196 +-- 33 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2229     # info/esi = get-slice(labels, datum, row-size=16, "label table")
2230     # . eax = get-slice(labels, datum, row-size=16, "label table")
2231     # . . push args
2232     68/push  "label table"/imm32
2233     68/push  0x10/imm32/row-size
2234     57/push-edi
2235     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
2236     # . . call
2237     e8/call  get-slice/disp32
2238     # . . discard args
2239     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2240     # . esi = eax
2241     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2242 $emit-segments:check-global-variable:
2243 +-- 26 lines: #?     # dump info->segment-name ---------------------------------------------------------------------------------------------------------------
2269     # if string-equal?(info->segment-name, "code") goto code label checks
2270     # . eax = string-equal?(info->segment-name, "code")
2271     # . . push args
2272     68/push  "code"/imm32
2273     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2274     # . . call
2275     e8/call  string-equal?/disp32
2276     # . . discard args
2277     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2278     # . if (eax != false) goto code label checks
2279     3d/compare-eax-and  0/imm32/false
2280     0f 85/jump-if-!=  $emit-segments:check-code-label-for-imm8/disp32
2281 $emit-segments:check-global-variable-for-disp8:
2282     # if has-metadata?(word-slice, "disp8") abort
2283     # . eax = has-metadata?(word-slice, "disp8")
2284     # . . push args
2285     68/push  "disp8"/imm32
2286     52/push-edx
2287     # . . call
2288     e8/call  has-metadata?/disp32
2289     # . . discard args
2290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2291     # . if (eax != false) abort
2292     3d/compare-eax-and  0/imm32/false
2293     0f 85/jump-if-!=  $emit-segments:global-variable-abort/disp32
2294 $emit-segments:check-global-variable-for-imm8:
2295     # if has-metadata?(word-slice, "imm8") abort
2296     # . eax = has-metadata?(word-slice, "imm8")
2297     # . . push args
2298     68/push  "imm8"/imm32
2299     52/push-edx
2300     # . . call
2301     e8/call  has-metadata?/disp32
2302     # . . discard args
2303     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2304     # . if (eax != false) abort
2305     3d/compare-eax-and  0/imm32/false
2306     0f 85/jump-if-!=  $emit-segments:global-variable-abort/disp32
2307 $emit-segments:emit-global-variable:
2308     # emit-hex(out, info->address, 4)
2309     # . . push args
2310     68/push  4/imm32
2311     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           8/disp8         .                 # push *(esi+8)
2312     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2313     # . . call
2314     e8/call  emit-hex/disp32
2315     # . . discard args
2316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2317     # continue
2318     e9/jump  $emit-segments:word-loop/disp32
2319 $emit-segments:check-code-label-for-imm8:
2320     # if (has-metadata?(word-slice, "imm8")) abort
2321     # . eax = has-metadata?(edx, "imm8")
2322     # . . push args
2323     68/push  "imm8"/imm32
2324     52/push-edx
2325     # . . call
2326     e8/call  has-metadata?/disp32
2327     # . . discard args
2328     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2329     # . if (eax != false) abort
2330     3d/compare-eax-and  0/imm32/false
2331     0f 85/jump-if-!=  $emit-segments:imm8-abort/disp32
2332 $emit-segments:check-code-label-for-imm32:
2333     # if (!has-metadata?(word-slice, "imm32")) goto next check
2334     # . eax = has-metadata?(edx, "imm32")
2335     # . . push args
2336     68/push  "imm32"/imm32
2337     52/push-edx
2338     # . . call
2339     e8/call  has-metadata?/disp32
2340     # . . discard args
2341     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2342     # . if (eax == false) goto next check
2343     3d/compare-eax-and  0/imm32
2344     74/jump-if-=  $emit-segments:check-code-label-for-disp8/disp8
2345 +-- 33 lines: #?     # dump info->address --------------------------------------------------------------------------------------------------------------------
2378 $emit-segments:emit-code-label-imm32:
2379     # emit-hex(out, info->address, 4)
2380     # . . push args
2381     68/push  4/imm32
2382     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           8/disp8         .                 # push *(esi+8)
2383     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2384     # . . call
2385     e8/call  emit-hex/disp32
2386     # . . discard args
2387     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2388     # continue
2389     e9/jump  $emit-segments:word-loop/disp32
2390 $emit-segments:check-code-label-for-disp8:
2391     # if (!has-metadata?(word-slice, "disp8")) goto next check
2392     # . eax = has-metadata?(edx, "disp8")
2393     # . . push args
2394     68/push  "disp8"/imm32
2395     52/push-edx
2396     # . . call
2397     e8/call  has-metadata?/disp32
2398     # . . discard args
2399     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2400     # . if (eax == false) goto next check
2401     3d/compare-eax-and  0/imm32/false
2402     74/jump-if-=  $emit-segments:check-code-label-for-disp32/disp8
2403 $emit-segments:emit-code-label-disp8:
2404     # emit-hex(out, info->offset - offset-of-next-instruction, 1)
2405     # . . push args
2406     68/push  1/imm32
2407     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
2408     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
2409     50/push-eax
2410     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2411     # . . call
2412     e8/call  emit-hex/disp32
2413     # . . discard args
2414     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2415     # continue
2416     e9/jump  $emit-segments:word-loop/disp32
2417 $emit-segments:check-code-label-for-disp32:
2418     # if (!has-metadata?(word-slice, "disp32")) abort
2419     # . eax = has-metadata?(edx, "disp32")
2420     # . . push args
2421     68/push  "disp32"/imm32
2422     52/push-edx
2423     # . . call
2424     e8/call  has-metadata?/disp32
2425     # . . discard args
2426     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2427     # . if (eax == false) abort
2428     3d/compare-eax-and  0/imm32/false
2429     0f 84/jump-if-=  $emit-segments:abort/disp32
2430 $emit-segments:emit-code-label-disp32:
2431     # emit-hex(out, info->offset - offset-of-next-instruction, 4)
2432     # . . push args
2433     68/push  4/imm32
2434     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
2435     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
2436     50/push-eax
2437     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2438     # . . call
2439     e8/call  emit-hex/disp32
2440     # . . discard args
2441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2442     # continue
2443     e9/jump  $emit-segments:word-loop/disp32
2444 $emit-segments:next-line:
2445     # write-buffered(out, "\n")
2446     # . . push args
2447     68/push  Newline/imm32
2448     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2449     # . . call
2450     e8/call  write-buffered/disp32
2451     # . . discard args
2452     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2453     # loop
2454     e9/jump  $emit-segments:line-loop/disp32
2455 $emit-segments:end:
2456     # . reclaim locals
2457     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x21c/imm32       # add to esp
2458     # . restore registers
2459     5f/pop-to-edi
2460     5e/pop-to-esi
2461     5b/pop-to-ebx
2462     5a/pop-to-edx
2463     59/pop-to-ecx
2464     58/pop-to-eax
2465     # . epilogue
2466     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2467     5d/pop-to-ebp
2468     c3/return
2469 
2470 $emit-segments:global-variable-abort:
2471     # . _write(2/stderr, error)
2472     # . . push args
2473     68/push  "emit-segments: must refer to global variables with /disp32 or /imm32"/imm32
2474     68/push  2/imm32/stderr
2475     # . . call
2476     e8/call  _write/disp32
2477     # . . discard args
2478     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2479     # . syscall(exit, 1)
2480     bb/copy-to-ebx  1/imm32
2481     b8/copy-to-eax  1/imm32/exit
2482     cd/syscall  0x80/imm8
2483     # never gets here
2484 
2485 $emit-segments:imm8-abort:
2486     # . _write(2/stderr, error)
2487     # . . push args
2488     68/push  "emit-segments: cannot refer to code labels with /imm8"/imm32
2489     68/push  2/imm32/stderr
2490     # . . call
2491     e8/call  _write/disp32
2492     # . . discard args
2493     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2494     # . syscall(exit, 1)
2495     bb/copy-to-ebx  1/imm32
2496     b8/copy-to-eax  1/imm32/exit
2497     cd/syscall  0x80/imm8
2498     # never gets here
2499 
2500 $emit-segments:abort:
2501     # print(stderr, "missing metadata in " word-slice)
2502     # . _write(2/stderr, "missing metadata in word ")
2503     # . . push args
2504     68/push  "emit-segments: missing metadata in "/imm32
2505     68/push  2/imm32/stderr
2506     # . . call
2507     e8/call  _write/disp32
2508     # . . discard args
2509     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2510     # . write-slice-buffered(Stderr, word-slice)
2511     # . . push args
2512     52/push-edx
2513     68/push  Stderr/imm32
2514     # . . call
2515     e8/call  write-slice-buffered/disp32
2516     # . . discard args
2517     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2518     # . flush(Stderr)
2519     # . . push args
2520     68/push  Stderr/imm32
2521     # . . call
2522     e8/call  flush/disp32
2523     # . . discard args
2524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2525     # . syscall(exit, 1)
2526     bb/copy-to-ebx  1/imm32
2527     b8/copy-to-eax  1/imm32/exit
2528     cd/syscall  0x80/imm8
2529     # never gets here
2530 
2531 test-emit-segments-global-variable:
2532     # global variables always convert to absolute addresses, regardless of metadata
2533     #
2534     # input:
2535     #   in:
2536     #     == code 0x1000
2537     #     ab cd ef gh
2538     #     ij x/disp32
2539     #     == data 0x2000
2540     #     00
2541     #     x:
2542     #       34
2543     #   segments:
2544     #     - 'code': {0x1074, 0, 9}
2545     #     - 'data': {0x2079, 5, 2}
2546     #   labels:
2547     #     - 'x': {'data', 1, 0x207a}
2548     #
2549     # output:
2550     #   ab cd ef gh
2551     #   ij 7a 20 00 00
2552     #   00
2553     #   34
2554     #
2555     # . prologue
2556     55/push-ebp
2557     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2558     # setup
2559     # . clear-stream(_test-input-stream)
2560     # . . push args
2561     68/push  _test-input-stream/imm32
2562     # . . call
2563     e8/call  clear-stream/disp32
2564     # . . discard args
2565     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2566     # . clear-stream(_test-output-stream)
2567     # . . push args
2568     68/push  _test-output-stream/imm32
2569     # . . call
2570     e8/call  clear-stream/disp32
2571     # . . discard args
2572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2573     # . clear-stream($_test-output-buffered-file->buffer)
2574     # . . push args
2575     68/push  $_test-output-buffered-file->buffer/imm32
2576     # . . call
2577     e8/call  clear-stream/disp32
2578     # . . discard args
2579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2580     # . var segments/ecx: (stream byte 10*16)
2581     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
2582     68/push  0xa0/imm32/length
2583     68/push  0/imm32/read
2584     68/push  0/imm32/write
2585     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2586     # . var labels/edx: (stream byte 512*16)
2587     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
2588     68/push  0x2000/imm32/length
2589     68/push  0/imm32/read
2590     68/push  0/imm32/write
2591     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
2592     # initialize input
2593     # . write(_test-input-stream, "== code 0x1000\n")
2594     # . . push args
2595     68/push  "== code 0x1000\n"/imm32
2596     68/push  _test-input-stream/imm32
2597     # . . call
2598     e8/call  write/disp32
2599     # . . discard args
2600     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2601     # . write(_test-input-stream, "ab cd ef gh\n")
2602     # . . push args
2603     68/push  "ab cd ef gh\n"/imm32
2604     68/push  _test-input-stream/imm32
2605     # . . call
2606     e8/call  write/disp32
2607     # . . discard args
2608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2609     # . write(_test-input-stream, "ij x/disp32\n")
2610     # . . push args
2611     68/push  "ij x/disp32\n"/imm32
2612     68/push  _test-input-stream/imm32
2613     # . . call
2614     e8/call  write/disp32
2615     # . . discard args
2616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2617     # . write(_test-input-stream, "== data 0x2000\n")
2618     # . . push args
2619     68/push  "== data 0x2000\n"/imm32
2620     68/push  _test-input-stream/imm32
2621     # . . call
2622     e8/call  write/disp32
2623     # . . discard args
2624     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2625     # . write(_test-input-stream, "00\n")
2626     # . . push args
2627     68/push  "00\n"/imm32
2628     68/push  _test-input-stream/imm32
2629     # . . call
2630     e8/call  write/disp32
2631     # . . discard args
2632     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2633     # . write(_test-input-stream, "x:\n")
2634     # . . push args
2635     68/push  "x:\n"/imm32
2636     68/push  _test-input-stream/imm32
2637     # . . call
2638     e8/call  write/disp32
2639     # . . discard args
2640     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2641     # . write(_test-input-stream, "34\n")
2642     # . . push args
2643     68/push  "34\n"/imm32
2644     68/push  _test-input-stream/imm32
2645     # . . call
2646     e8/call  write/disp32
2647     # . . discard args
2648     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2649     # . stream-add4(segments, "code", 0x1074, 0, 9)
2650     68/push  9/imm32/segment-size
2651     68/push  0/imm32/file-offset
2652     68/push  0x1074/imm32/start-address
2653     68/push  "code"/imm32/segment-name
2654     51/push-ecx
2655     # . . call
2656     e8/call  stream-add4/disp32
2657     # . . discard args
2658     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
2659     # . stream-add4(segments, "data", 0x2079, 5, 2)
2660     68/push  1/imm32/segment-size
2661     68/push  5/imm32/file-offset
2662     68/push  0x2079/imm32/start-address
2663     68/push  "data"/imm32/segment-name
2664     51/push-ecx
2665     # . . call
2666     e8/call  stream-add4/disp32
2667     # . . discard args
2668     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
2669     # . stream-add4(labels, "x", "data", 1, 0x207a)
2670     68/push  0x207a/imm32/label-address
2671     68/push  1/imm32/segment-offset
2672     68/push  "data"/imm32/segment-name
2673     68/push  "x"/imm32/label-name
2674     52/push-edx
2675     # . . call
2676     e8/call  stream-add4/disp32
2677     # . . discard args
2678     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
2679     # component under test
2680     # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels)
2681     # . . push args
2682     52/push-edx
2683     51/push-ecx
2684     68/push  _test-output-buffered-file/imm32
2685     68/push  _test-input-stream/imm32
2686     # . . call
2687     e8/call  emit-segments/disp32
2688     # . . discard args
2689     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2690     # checks
2691     # . flush(_test-output-buffered-file)
2692     # . . push args
2693     68/push  _test-output-buffered-file/imm32
2694     # . . call
2695     e8/call  flush/disp32
2696     # . . discard args
2697     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2698 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
2731     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
2732     # . . push args
2733     68/push  "F - test-emit-segments-global-variable/0"/imm32
2734     68/push  "ab cd ef gh "/imm32
2735     68/push  _test-output-stream/imm32
2736     # . . call
2737     e8/call  check-next-stream-line-equal/disp32
2738     # . . discard args
2739     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2740     # . check-next-stream-line-equal(_test-output-stream, "ij 7a 20 00 00 ", msg)
2741     # . . push args
2742     68/push  "F - test-emit-segments-global-variable/1"/imm32
2743     68/push  "ij 7a 20 00 00 "/imm32
2744     68/push  _test-output-stream/imm32
2745     # . . call
2746     e8/call  check-next-stream-line-equal/disp32
2747     # . . discard args
2748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2749     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
2750     # . . push args
2751     68/push  "F - test-emit-segments-global-variable/2"/imm32
2752     68/push  "00 "/imm32
2753     68/push  _test-output-stream/imm32
2754     # . . call
2755     e8/call  check-next-stream-line-equal/disp32
2756     # . . discard args
2757     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2758     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
2759     # . . push args
2760     68/push  "F - test-emit-segments-global-variable/3"/imm32
2761     68/push  "34 "/imm32
2762     68/push  _test-output-stream/imm32
2763     # . . call
2764     e8/call  check-next-stream-line-equal/disp32
2765     # . . discard args
2766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2767     # . epilogue
2768     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2769     5d/pop-to-ebp
2770     c3/return
2771 
2772 test-emit-segments-code-label:
2773     # labels usually convert to displacements
2774     #
2775     # input:
2776     #   in:
2777     #     == code 0x1000
2778     #     ab cd
2779     #     l1:
2780     #       ef gh
2781     #       ij l1/disp32
2782     #   segments:
2783     #     - 'code': {0x1054, 0, 9}
2784     #   labels:
2785     #     - 'l1': {'code', 2, 0x1056}
2786     #
2787     # output:
2788     #   ab cd
2789     #   ef gh
2790     #   ij f9 ff ff ff  # -7
2791     #
2792     # . prologue
2793     55/push-ebp
2794     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2795     # setup
2796     # . clear-stream(_test-input-stream)
2797     # . . push args
2798     68/push  _test-input-stream/imm32
2799     # . . call
2800     e8/call  clear-stream/disp32
2801     # . . discard args
2802     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2803     # . clear-stream(_test-output-stream)
2804     # . . push args
2805     68/push  _test-output-stream/imm32
2806     # . . call
2807     e8/call  clear-stream/disp32
2808     # . . discard args
2809     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2810     # . clear-stream($_test-output-buffered-file->buffer)
2811     # . . push args
2812     68/push  $_test-output-buffered-file->buffer/imm32
2813     # . . call
2814     e8/call  clear-stream/disp32
2815     # . . discard args
2816     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2817     # . var segments/ecx: (stream byte 10*16)
2818     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
2819     68/push  0xa0/imm32/length
2820     68/push  0/imm32/read
2821     68/push  0/imm32/write
2822     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2823     # . var labels/edx: (stream byte 512*16)
2824     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
2825     68/push  0x2000/imm32/length
2826     68/push  0/imm32/read
2827     68/push  0/imm32/write
2828     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
2829     # initialize input
2830     # . write(_test-input-stream, "== code 0x1000\n")
2831     # . . push args
2832     68/push  "== code 0x1000\n"/imm32
2833     68/push  _test-input-stream/imm32
2834     # . . call
2835     e8/call  write/disp32
2836     # . . discard args
2837     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2838     # . write(_test-input-stream, "ab cd\n")
2839     # . . push args
2840     68/push  "ab cd\n"/imm32
2841     68/push  _test-input-stream/imm32
2842     # . . call
2843     e8/call  write/disp32
2844     # . . discard args
2845     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2846     # . write(_test-input-stream, "l1:\n")
2847     # . . push args
2848     68/push  "l1:\n"/imm32
2849     68/push  _test-input-stream/imm32
2850     # . . call
2851     e8/call  write/disp32
2852     # . . discard args
2853     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2854     # . write(_test-input-stream, "  ef gh\n")
2855     # . . push args
2856     68/push  "  ef gh\n"/imm32
2857     68/push  _test-input-stream/imm32
2858     # . . call
2859     e8/call  write/disp32
2860     # . . discard args
2861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2862     # . write(_test-input-stream, "  ij l1/disp32\n")
2863     # . . push args
2864     68/push  "  ij l1/disp32\n"/imm32
2865     68/push  _test-input-stream/imm32
2866     # . . call
2867     e8/call  write/disp32
2868     # . . discard args
2869     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2870     # . stream-add4(segments, "code", 0x1054, 0, 9)
2871     68/push  9/imm32/segment-size
2872     68/push  0/imm32/file-offset
2873     68/push  0x1054/imm32/start-address
2874     68/push  "code"/imm32/segment-name
2875     51/push-ecx
2876     # . . call
2877     e8/call  stream-add4/disp32
2878     # . . discard args
2879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
2880     # . stream-add4(labels, "l1", "code", 2, 0x1056)
2881     68/push  0x1056/imm32/label-address
2882     68/push  2/imm32/segment-offset
2883     68/push  "code"/imm32/segment-name
2884     68/push  "l1"/imm32/label-name
2885     52/push-edx
2886     # . . call
2887     e8/call  stream-add4/disp32
2888     # . . discard args
2889     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
2890     # component under test
2891     # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels)
2892     # . . push args
2893     52/push-edx
2894     51/push-ecx
2895     68/push  _test-output-buffered-file/imm32
2896     68/push  _test-input-stream/imm32
2897     # . . call
2898     e8/call  emit-segments/disp32
2899     # . . discard args
2900     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2901     # checks
2902     # . flush(_test-output-buffered-file)
2903     # . . push args
2904     68/push  _test-output-buffered-file/imm32
2905     # . . call
2906     e8/call  flush/disp32
2907     # . . discard args
2908     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2909 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
2942     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
2943     # . . push args
2944     68/push  "F - test-emit-segments-code-label/0"/imm32
2945     68/push  "ab cd "/imm32
2946     68/push  _test-output-stream/imm32
2947     # . . call
2948     e8/call  check-next-stream-line-equal/disp32
2949     # . . discard args
2950     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2951     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
2952     # . . push args
2953     68/push  "F - test-emit-segments-code-label/1"/imm32
2954     68/push  "ef gh "/imm32
2955     68/push  _test-output-stream/imm32
2956     # . . call
2957     e8/call  check-next-stream-line-equal/disp32
2958     # . . discard args
2959     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2960     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
2961     # . . push args
2962     68/push  "F - test-emit-segments-code-label/2"/imm32
2963     68/push  "ij f9 ff ff ff "/imm32
2964     68/push  _test-output-stream/imm32
2965     # . . call
2966     e8/call  check-next-stream-line-equal/disp32
2967     # . . discard args
2968     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2969     # . epilogue
2970     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2971     5d/pop-to-ebp
2972     c3/return
2973 
2974 test-emit-segments-code-label-absolute:
2975     # labels can also convert to absolute addresses
2976     #
2977     # input:
2978     #   in:
2979     #     == code 0x1000
2980     #     ab cd
2981     #     l1:
2982     #       ef gh
2983     #       ij l1/imm32
2984     #   segments:
2985     #     - 'code': {0x1054, 0, 9}
2986     #   labels:
2987     #     - 'l1': {'code', 2, 0x1056}
2988     #
2989     # output:
2990     #   ab cd
2991     #   ef gh
2992     #   ij 56 10 00 00
2993     #
2994     # . prologue
2995     55/push-ebp
2996     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2997     # setup
2998     # . clear-stream(_test-input-stream)
2999     # . . push args
3000     68/push  _test-input-stream/imm32
3001     # . . call
3002     e8/call  clear-stream/disp32
3003     # . . discard args
3004     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3005     # . clear-stream(_test-output-stream)
3006     # . . push args
3007     68/push  _test-output-stream/imm32
3008     # . . call
3009     e8/call  clear-stream/disp32
3010     # . . discard args
3011     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3012     # . clear-stream($_test-output-buffered-file->buffer)
3013     # . . push args
3014     68/push  $_test-output-buffered-file->buffer/imm32
3015     # . . call
3016     e8/call  clear-stream/disp32
3017     # . . discard args
3018     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3019     # . var segments/ecx: (stream byte 10*16)
3020     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
3021     68/push  0xa0/imm32/length
3022     68/push  0/imm32/read
3023     68/push  0/imm32/write
3024     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3025     # . var labels/edx: (stream byte 512*16)
3026     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x2000/imm32      # subtract from esp
3027     68/push  0x2000/imm32/length
3028     68/push  0/imm32/read
3029     68/push  0/imm32/write
3030     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
3031     # initialize input
3032     # . write(_test-input-stream, "== code 0x1000\n")
3033     # . . push args
3034     68/push  "== code 0x1000\n"/imm32
3035     68/push  _test-input-stream/imm32
3036     # . . call
3037     e8/call  write/disp32
3038     # . . discard args
3039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3040     # . write(_test-input-stream, "ab cd\n")
3041     # . . push args
3042     68/push  "ab cd\n"/imm32
3043     68/push  _test-input-stream/imm32
3044     # . . call
3045     e8/call  write/disp32
3046     # . . discard args
3047     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3048     # . write(_test-input-stream, "l1:\n")
3049     # . . push args
3050     68/push  "l1:\n"/imm32
3051     68/push  _test-input-stream/imm32
3052     # . . call
3053     e8/call  write/disp32
3054     # . . discard args
3055     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3056     # . write(_test-input-stream, "  ef gh\n")
3057     # . . push args
3058     68/push  "  ef gh\n"/imm32
3059     68/push  _test-input-stream/imm32
3060     # . . call
3061     e8/call  write/disp32
3062     # . . discard args
3063     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3064     # . write(_test-input-stream, "  ij l1/imm32\n")
3065     # . . push args
3066     68/push  "  ij l1/imm32\n"/imm32
3067     68/push  _test-input-stream/imm32
3068     # . . call
3069     e8/call  write/disp32
3070     # . . discard args
3071     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3072     # . stream-add4(segments, "code", 0x1054, 0, 9)
3073     68/push  9/imm32/segment-size
3074     68/push  0/imm32/file-offset
3075     68/push  0x1054/imm32/start-address
3076     68/push  "code"/imm32/segment-name
3077     51/push-ecx
3078     # . . call
3079     e8/call  stream-add4/disp32
3080     # . . discard args
3081     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3082     # . stream-add4(labels, "l1", "code", 2, 0x1056)
3083     68/push  0x1056/imm32/label-address
3084     68/push  2/imm32/segment-offset
3085     68/push  "code"/imm32/segment-name
3086     68/push  "l1"/imm32/label-name
3087     52/push-edx
3088     # . . call
3089     e8/call  stream-add4/disp32
3090     # . . discard args
3091     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3092     # component under test
3093     # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels)
3094     # . . push args
3095     52/push-edx
3096     51/push-ecx
3097     68/push  _test-output-buffered-file/imm32
3098     68/push  _test-input-stream/imm32
3099     # . . call
3100     e8/call  emit-segments/disp32
3101     # . . discard args
3102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
3103     # checks
3104     # . flush(_test-output-buffered-file)
3105     # . . push args
3106     68/push  _test-output-buffered-file/imm32
3107     # . . call
3108     e8/call  flush/disp32
3109     # . . discard args
3110     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3111 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3144     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
3145     # . . push args
3146     68/push  "F - test-emit-segments-code-label-absolute/0"/imm32
3147     68/push  "ab cd "/imm32
3148     68/push  _test-output-stream/imm32
3149     # . . call
3150     e8/call  check-next-stream-line-equal/disp32
3151     # . . discard args
3152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3153     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
3154     # . . push args
3155     68/push  "F - test-emit-segments-code-label-absolute/1"/imm32
3156     68/push  "ef gh "/imm32
3157     68/push  _test-output-stream/imm32
3158     # . . call
3159     e8/call  check-next-stream-line-equal/disp32
3160     # . . discard args
3161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3162     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
3163     # . . push args
3164     68/push  "F - test-emit-segments-code-label-absolute/2"/imm32
3165     68/push  "ij 56 10 00 00 "/imm32
3166     68/push  _test-output-stream/imm32
3167     # . . call
3168     e8/call  check-next-stream-line-equal/disp32
3169     # . . discard args
3170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3171     # . epilogue
3172     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3173     5d/pop-to-ebp
3174     c3/return
3175 
3176 emit-headers:  # out: (addr buffered-file), segments: (addr stream {string, segment-info}), labels: (addr stream {string, label-info})
3177     # pseudocode:
3178     #   emit-elf-header(out, segments, labels)
3179     #   curr-segment = segments->data
3180     #   max = &segments->data[segments->write]
3181     #   while true
3182     #     if (curr-segment >= max) break
3183     #     emit-elf-program-header-entry(out, curr-segment)
3184     #     curr-segment += 16                        # size of a row
3185     #
3186     # . prologue
3187     55/push-ebp
3188     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3189     # . save registers
3190     50/push-eax
3191     51/push-ecx
3192 +--  9 lines: #?     # write(2/stderr, "emit-elf-header\n") --------------------------------------------------------------------------------------------------
3201     # emit-elf-header(out, segments, labels)
3202     # . . push args
3203     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3204     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3205     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3206     # . . call
3207     e8/call  emit-elf-header/disp32
3208     # . . discard args
3209     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3210     # eax = segments
3211     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
3212     # ecx = segments->write
3213     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3214     # curr-segment/eax = segments->data
3215     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           0/r32/eax   0xc/disp8       .                 # copy eax+12 to eax
3216     # max/ecx = &segments->data[segments->write]
3217     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # add eax to ecx
3218 $emit-headers:loop:
3219     # if (curr-segment >= max) break
3220     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
3221     0f 83/jump-if-addr>=  $emit-headers:end/disp32
3222 +-- 63 lines: #?     # dump curr-segment->name ---------------------------------------------------------------------------------------------------------------
3285 +--  9 lines: #?     # write(2/stderr, "emit-segment-header\n") ----------------------------------------------------------------------------------------------
3294     # emit-elf-program-header-entry(out, curr-segment)
3295     # . . push args
3296     50/push-eax
3297     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3298     # . . call
3299     e8/call  emit-elf-program-header-entry/disp32
3300     # . . discard args
3301     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3302     # curr-segment += 16                        # size of a row
3303     81          0/subop/add         3/mod/direct    0/rm32/eax    .           .             .           .           .               0x10/imm32        # add to eax
3304     e9/jump  $emit-headers:loop/disp32
3305 $emit-headers:end:
3306     # . restore registers
3307     59/pop-to-ecx
3308     58/pop-to-eax
3309     # . epilogue
3310     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3311     5d/pop-to-ebp
3312     c3/return
3313 
3314 emit-elf-header:  # out: (addr buffered-file), segments: (addr stream {string, segment-info}), labels: (addr stream {string, label-info})
3315     # pseudocode
3316     #   *$Elf_e_entry = get(labels, "Entry")->address
3317     #   *$Elf_e_phnum = segments->write / 16         # size of a row
3318     #   emit-hex-array(out, Elf_header)
3319     #   write-buffered(out, "\n")
3320     #
3321     # . prologue
3322     55/push-ebp
3323     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3324     # . save registers
3325     50/push-eax
3326     51/push-ecx
3327     52/push-edx  # just because we need to call idiv
3328     # *$Elf_e_entry = get(labels, "Entry")->address
3329     # . eax = labels
3330     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0x10/disp8      .                 # copy *(ebp+16) to eax
3331     # . label-info/eax = get(labels, "Entry", row-size=16, "label table")
3332     # . . push args
3333     68/push  "label table"/imm32
3334     68/push  0x10/imm32/row-size
3335     68/push  "Entry"/imm32
3336     50/push-eax
3337     # . . call
3338     e8/call  get/disp32
3339     # . . discard args
3340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
3341     # . eax = label-info->address
3342     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           0/r32/eax   8/disp8         .                 # copy *(eax+8) to eax
3343     # . *$Elf_e_entry = eax
3344     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_e_entry/disp32               # copy eax to *$Elf_e_entry
3345     # *$Elf_e_phnum = segments->write / 0x10
3346     # . eax = segments
3347     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
3348     # . len/eax = segments->write
3349     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           0/r32/eax   .               .                 # copy *eax to eax
3350     # . eax = len / 0x10  (destroying edx)
3351     b9/copy-to-ecx  0x10/imm32
3352     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
3353     f7          7/subop/idiv        3/mod/direct    1/rm32/ecx    .           .             .           .           .               .                 # divide edx:eax by ecx, storing quotient in eax and remainder in edx
3354     # . *$Elf_e_phnum = eax
3355     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_e_phnum/disp32               # copy eax to *$Elf_e_phnum
3356     # emit-hex-array(out, Elf_header)
3357     # . . push args
3358     68/push  Elf_header/imm32
3359     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3360     # . . call
3361     e8/call  emit-hex-array/disp32
3362     # . . discard args
3363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3364     # write-buffered(out, "\n")
3365     # . . push args
3366     68/push  Newline/imm32
3367     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3368     # . . call
3369     e8/call  write-buffered/disp32
3370     # . . discard args
3371     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3372 $emit-elf-header:end:
3373     # . restore registers
3374     5a/pop-to-edx
3375     59/pop-to-ecx
3376     58/pop-to-eax
3377     # . epilogue
3378     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3379     5d/pop-to-ebp
3380     c3/return
3381 
3382 emit-elf-program-header-entry:  # out: (addr buffered-file), curr-segment: (addr {string, segment-info})
3383     # pseudocode:
3384     #   *$Elf_p_offset = curr-segment->file-offset
3385     #   *$Elf_p_vaddr = curr-segment->address
3386     #   *$Elf_p_paddr = curr-segment->address
3387     #   *$Elf_p_filesz = curr-segment->size
3388     #   *$Elf_p_memsz = curr-segment->size
3389     #   if curr-segment->name == "code"
3390     #     *$Elf_p_flags = 5  # r-x
3391     #   else
3392     #     *$Elf_p_flags = 6  # rw-
3393     #   emit-hex-array(out, Elf_program_header_entry)
3394     #   write-buffered(out, "\n")
3395     #
3396     # . prologue
3397     55/push-ebp
3398     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3399     # . save registers
3400     50/push-eax
3401     56/push-esi
3402     # esi = curr-segment
3403     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
3404     # *$Elf_p_offset = curr-segment->file-offset
3405     # . eax = curr-segment->file-offset
3406     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   8/disp8         .                 # copy *(esi+8) to eax
3407     # . *$Elf_p_offset = eax
3408     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_offset/disp32              # copy eax to *$Elf_p_offset
3409     # *$Elf_p_vaddr = curr-segment->address
3410     # . eax = curr-segment->address
3411     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
3412     # . *$Elf_p_vaddr = eax
3413     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_vaddr/disp32               # copy eax to *$Elf_p_vaddr
3414     # *$Elf_p_paddr = curr-segment->address
3415     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_paddr/disp32               # copy eax to *$Elf_p_paddr
3416     # *$Elf_p_filesz = curr-segment->size
3417     # . eax = curr-segment->size
3418     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(esi+12) to eax
3419     # . *$Elf_p_filesz = eax
3420     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_filesz/disp32              # copy eax to *$Elf_p_filesz
3421     # *$Elf_p_memsz = curr-segment->size
3422     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   $Elf_p_memsz/disp32               # copy eax to *$Elf_p_memsz
3423     # if (!string-equal?(curr-segment->name, "code") goto next check
3424     # . eax = curr-segment->name
3425     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
3426     # . eax = string-equal?(curr-segment->name, "code")
3427     # . . push args
3428     68/push  "code"/imm32
3429     50/push-eax
3430     # . . call
3431     e8/call  string-equal?/disp32
3432     # . . discard args
3433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3434     # . if (eax == false) goto next check
3435     3d/compare-eax-and  0/imm32/false
3436     74/jump-if-=  $emit-elf-program-header-entry:data/disp8
3437     # *$Elf_p_flags = r-x
3438     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           $Elf_p_flags/disp32  5/imm32      # copy to *$Elf_p_flags
3439     eb/jump  $emit-elf-program-header-entry:really-emit/disp8
3440 $emit-elf-program-header-entry:data:
3441     # otherwise *$Elf_p_flags = rw-
3442     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           $Elf_p_flags/disp32  6/imm32      # copy to *$Elf_p_flags
3443 $emit-elf-program-header-entry:really-emit:
3444     # emit-hex-array(out, Elf_program_header_entry)
3445     # . . push args
3446     68/push  Elf_program_header_entry/imm32
3447     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3448     # . . call
3449     e8/call  emit-hex-array/disp32
3450     # . . discard args
3451     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3452     # write-buffered(out, "\n")
3453     # . . push args
3454     68/push  Newline/imm32
3455     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3456     # . . call
3457     e8/call  write-buffered/disp32
3458     # . . discard args
3459     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3460 $emit-elf-program-header-entry:end:
3461     # . restore registers
3462     5e/pop-to-esi
3463     58/pop-to-eax
3464     # . epilogue
3465     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3466     5d/pop-to-ebp
3467     c3/return
3468 
3469 # - some helpers for tests
3470 
3471 stream-add4:  # in: (addr stream byte), key: addr, val1: addr, val2: addr, val3: addr
3472     # . prologue
3473     55/push-ebp
3474     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3475     # . save registers
3476     50/push-eax
3477     51/push-ecx
3478     52/push-edx
3479     56/push-esi
3480     # esi = in
3481     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
3482     # curr/eax = &in->data[in->write]
3483     # . eax = in->write
3484     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
3485     # . eax = esi+eax+12
3486     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
3487     # max/edx = &in->data[in->length]
3488     # . edx = in->length
3489     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
3490     # . edx = esi+edx+12
3491     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
3492     # if (curr >= max) abort
3493     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
3494     73/jump-if-addr>=  $stream-add4:abort/disp8
3495     # *curr = key
3496     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
3497     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
3498     # curr += 4
3499     05/add-to-eax  4/imm32
3500     # if (curr >= max) abort
3501     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
3502     73/jump-if-addr>=  $stream-add4:abort/disp8
3503     # *curr = val1
3504     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
3505     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
3506     # curr += 4
3507     05/add-to-eax  4/imm32
3508     # if (curr >= max) abort
3509     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
3510     73/jump-if-addr>=  $stream-add4:abort/disp8
3511     # *curr = val2
3512     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
3513     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
3514     # curr += 4
3515     05/add-to-eax  4/imm32
3516     # if (curr >= max) abort
3517     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
3518     73/jump-if-addr>=  $stream-add4:abort/disp8
3519     # *curr = val3
3520     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x18/disp8      .                 # copy *(ebp+24) to ecx
3521     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
3522     # in->write += 16
3523     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               0x10/imm32        # add to *esi
3524 $stream-add4:end:
3525     # . restore registers
3526     5e/pop-to-esi
3527     5a/pop-to-edx
3528     59/pop-to-ecx
3529     58/pop-to-eax
3530     # . epilogue
3531     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3532     5d/pop-to-ebp
3533     c3/return
3534 
3535 $stream-add4:abort:
3536     # . _write(2/stderr, error)
3537     # . . push args
3538     68/push  "overflow in stream-add4\n"/imm32
3539     68/push  2/imm32/stderr
3540     # . . call
3541     e8/call  _write/disp32
3542     # . . discard args
3543     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3544     # . syscall(exit, 1)
3545     bb/copy-to-ebx  1/imm32
3546     b8/copy-to-eax  1/imm32/exit
3547     cd/syscall  0x80/imm8
3548     # never gets here
3549 
3550 # some variants of 'trace' that take multiple arguments in different combinations of types:
3551 #   n: int
3552 #   c: character [4-bytes, will eventually be UTF-8]
3553 #   s: (addr string)
3554 #   l: (addr slice)
3555 # one gotcha: 's5' must not be empty
3556 
3557 trace-sssns:  # s1: (addr string), s2: (addr string), s3: (addr string), n4: int, s5: (addr string)
3558     # . prologue
3559     55/push-ebp
3560     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3561     # write(*Trace-stream, s1)
3562     # . . push args
3563     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3564     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3565     # . . call
3566     e8/call  write/disp32
3567     # . . discard args
3568     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3569     # write(*Trace-stream, s2)
3570     # . . push args
3571     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3572     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3573     # . . call
3574     e8/call  write/disp32
3575     # . . discard args
3576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3577     # write(*Trace-stream, s3)
3578     # . . push args
3579     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3580     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3581     # . . call
3582     e8/call  write/disp32
3583     # . . discard args
3584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3585     # print-int32(*Trace-stream, n4)
3586     # . . push args
3587     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
3588     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3589     # . . call
3590     e8/call  print-int32/disp32
3591     # . . discard args
3592     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3593     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3594     # . . push args
3595     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3596     # . . call
3597     e8/call  trace/disp32
3598     # . . discard args
3599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3600 $trace-sssns:end:
3601     # . epilogue
3602     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3603     5d/pop-to-ebp
3604     c3/return
3605 
3606 test-trace-sssns:
3607     # . prologue
3608     55/push-ebp
3609     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3610     # setup
3611     # . *Trace-stream->write = 0
3612     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
3613     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
3614     # trace-sssns("A" "b" "c " 3 " e")
3615     # . . push args
3616     68/push  " e"/imm32
3617     68/push  3/imm32
3618     68/push  "c "/imm32
3619     68/push  "b"/imm32
3620     68/push  "A"/imm32
3621     # . . call
3622     e8/call  trace-sssns/disp32
3623     # . . discard args
3624     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3625 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3651     # check-trace-contains("Abc 0x00000003 e")
3652     # . . push args
3653     68/push  "F - test-trace-sssns"/imm32
3654     68/push  "Abc 0x00000003 e"/imm32
3655     # . . call
3656     e8/call  check-trace-contains/disp32
3657     # . . discard args
3658     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3659     # . epilogue
3660     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3661     5d/pop-to-ebp
3662     c3/return
3663 
3664 trace-snsns:  # s1: (addr string), n2: int, s3: (addr string), n4: int, s5: (addr string)
3665     # . prologue
3666     55/push-ebp
3667     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3668     # write(*Trace-stream, s1)
3669     # . . push args
3670     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3671     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3672     # . . call
3673     e8/call  write/disp32
3674     # . . discard args
3675     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3676     # print-int32(*Trace-stream, n2)
3677     # . . push args
3678     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3679     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3680     # . . call
3681     e8/call  print-int32/disp32
3682     # . . discard args
3683     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3684     # write(*Trace-stream, s3)
3685     # . . push args
3686     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3687     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3688     # . . call
3689     e8/call  write/disp32
3690     # . . discard args
3691     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3692     # print-int32(*Trace-stream, n4)
3693     # . . push args
3694     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
3695     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3696     # . . call
3697     e8/call  print-int32/disp32
3698     # . . discard args
3699     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3700     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3701     # . . push args
3702     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3703     # . . call
3704     e8/call  trace/disp32
3705     # . . discard args
3706     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3707 $trace-snsns:end:
3708     # . epilogue
3709     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3710     5d/pop-to-ebp
3711     c3/return
3712 
3713 test-trace-snsns:
3714     # . prologue
3715     55/push-ebp
3716     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3717     # setup
3718     # . *Trace-stream->write = 0
3719     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
3720     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
3721     # trace-snsns("A " 2 " c " 3 " e")
3722     # . . push args
3723     68/push  " e"/imm32
3724     68/push  3/imm32
3725     68/push  " c "/imm32
3726     68/push  2/imm32
3727     68/push  "A "/imm32
3728     # . . call
3729     e8/call  trace-snsns/disp32
3730     # . . discard args
3731     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3732 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3758     # check-trace-contains("Abc 0x00000003 e")
3759     # . . push args
3760     68/push  "F - test-trace-snsns"/imm32
3761     68/push  "A 0x00000002 c 0x00000003 e"/imm32
3762     # . . call
3763     e8/call  check-trace-contains/disp32
3764     # . . discard args
3765     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3766     # . epilogue
3767     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3768     5d/pop-to-ebp
3769     c3/return
3770 
3771 trace-slsls:  # s1: (addr string), l2: (addr slice), s3: (addr string), l4: (addr slice), s5: (addr string)
3772     # . prologue
3773     55/push-ebp
3774     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3775     # write(*Trace-stream, s1)
3776     # . . push args
3777     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3778     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3779     # . . call
3780     e8/call  write/disp32
3781     # . . discard args
3782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3783     # write-slice(*Trace-stream, l2)
3784     # . . push args
3785     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3786     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3787     # . . call
3788     e8/call  write-slice/disp32
3789     # . . discard args
3790     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3791     # write(*Trace-stream, s3)
3792     # . . push args
3793     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3794     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3795     # . . call
3796     e8/call  write/disp32
3797     # . . discard args
3798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3799     # write-slice(*Trace-stream, l4)
3800     # . . push args
3801     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
3802     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3803     # . . call
3804     e8/call  write-slice/disp32
3805     # . . discard args
3806     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3807     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3808     # . . push args
3809     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3810     # . . call
3811     e8/call  trace/disp32
3812     # . . discard args
3813     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3814 $trace-slsls:end:
3815     # . epilogue
3816     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3817     5d/pop-to-ebp
3818     c3/return
3819 
3820 test-trace-slsls:
3821     # . prologue
3822     55/push-ebp
3823     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3824     # setup
3825     # . *Trace-stream->write = 0
3826     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
3827     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
3828     # (eax..ecx) = "b"
3829     b8/copy-to-eax  "b"/imm32
3830     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3831     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
3832     05/add-to-eax  4/imm32
3833     # var b/ebx: slice = {eax, ecx}
3834     51/push-ecx
3835     50/push-eax
3836     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
3837     # (eax..ecx) = "d"
3838     b8/copy-to-eax  "d"/imm32
3839     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3840     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
3841     05/add-to-eax  4/imm32
3842     # var d/edx: slice = {eax, ecx}
3843     51/push-ecx
3844     50/push-eax
3845     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
3846     # trace-slsls("A" b "c" d "e")
3847     # . . push args
3848     68/push  "e"/imm32
3849     52/push-edx
3850     68/push  "c"/imm32
3851     53/push-ebx
3852     68/push  "A"/imm32
3853     # . . call
3854     e8/call  trace-slsls/disp32
3855     # . . discard args
3856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3857 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3883     # check-trace-contains("Abcde")
3884     # . . push args
3885     68/push  "F - test-trace-slsls"/imm32
3886     68/push  "Abcde"/imm32
3887     # . . call
3888     e8/call  check-trace-contains/disp32
3889     # . . discard args
3890     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3891     # . epilogue
3892     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3893     5d/pop-to-ebp
3894     c3/return
3895 
3896 trace-slsns:  # s1: (addr string), l2: (addr slice), s3: (addr string), n4: int, s5: (addr string)
3897     # . prologue
3898     55/push-ebp
3899     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3900     # write(*Trace-stream, s1)
3901     # . . push args
3902     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3903     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3904     # . . call
3905     e8/call  write/disp32
3906     # . . discard args
3907     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3908     # write-slice(*Trace-stream, l2)
3909     # . . push args
3910     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3911     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3912     # . . call
3913     e8/call  write-slice/disp32
3914     # . . discard args
3915     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3916     # write(*Trace-stream, s3)
3917     # . . push args
3918     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3919     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3920     # . . call
3921     e8/call  write/disp32
3922     # . . discard args
3923     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3924     # print-int32(*Trace-stream, n4)
3925     # . . push args
3926     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
3927     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3928     # . . call
3929     e8/call  print-int32/disp32
3930     # . . discard args
3931     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3932     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3933     # . . push args
3934     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3935     # . . call
3936     e8/call  trace/disp32
3937     # . . discard args
3938     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3939 $trace-slsns:end:
3940     # . epilogue
3941     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3942     5d/pop-to-ebp
3943     c3/return
3944 
3945 test-trace-slsns:
3946     # . prologue
3947     55/push-ebp
3948     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3949     # setup
3950     # . *Trace-stream->write = 0
3951     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
3952     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
3953     # (eax..ecx) = "b"
3954     b8/copy-to-eax  "b"/imm32
3955     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3956     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
3957     05/add-to-eax  4/imm32
3958     # var b/ebx: slice = {eax, ecx}
3959     51/push-ecx
3960     50/push-eax
3961     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
3962     # trace-slsls("A" b "c " 3 " e")
3963     # . . push args
3964     68/push  " e"/imm32
3965     68/push  3/imm32
3966     68/push  "c "/imm32
3967     53/push-ebx
3968     68/push  "A"/imm32
3969     # . . call
3970     e8/call  trace-slsns/disp32
3971     # . . discard args
3972     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3973 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3999     # check-trace-contains("Abc 0x00000003 e")
4000     # . . push args
4001     68/push  "F - test-trace-slsls"/imm32
4002     68/push  "Abc 0x00000003 e"/imm32
4003     # . . call
4004     e8/call  check-trace-contains/disp32
4005     # . . discard args
4006     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4007     # . epilogue
4008     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4009     5d/pop-to-ebp
4010     c3/return
4011 
4012 trace-slsss:  # s1: (addr string), l2: (addr slice), s3: (addr string), s4: (addr string), s5: (addr string)
4013     # . prologue
4014     55/push-ebp
4015     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4016     # write(*Trace-stream, s1)
4017     # . . push args
4018     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
4019     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
4020     # . . call
4021     e8/call  write/disp32
4022     # . . discard args
4023     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4024     # write-slice(*Trace-stream, l2)
4025     # . . push args
4026     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
4027     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
4028     # . . call
4029     e8/call  write-slice/disp32
4030     # . . discard args
4031     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4032     # write(*Trace-stream, s3)
4033     # . . push args
4034     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
4035     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
4036     # . . call
4037     e8/call  write/disp32
4038     # . . discard args
4039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4040     # write(*Trace-stream, s4)
4041     # . . push args
4042     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
4043     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
4044     # . . call
4045     e8/call  write/disp32
4046     # . . discard args
4047     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4048     # trace(s5)  # implicitly adds a newline and finalizes the trace line
4049     # . . push args
4050     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
4051     # . . call
4052     e8/call  trace/disp32
4053     # . . discard args
4054     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4055 $trace-slsss:end:
4056     # . epilogue
4057     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4058     5d/pop-to-ebp
4059     c3/return
4060 
4061 test-trace-slsss:
4062     # . prologue
4063     55/push-ebp
4064     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4065     # setup
4066     # . *Trace-stream->write = 0
4067     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
4068     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
4069     # (eax..ecx) = "b"
4070     b8/copy-to-eax  "b"/imm32
4071     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4072     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
4073     05/add-to-eax  4/imm32
4074     # var b/ebx: slice = {eax, ecx}
4075     51/push-ecx
4076     50/push-eax
4077     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
4078     # trace-slsss("A" b "c" "d" "e")
4079     # . . push args
4080     68/push  "e"/imm32
4081     68/push  "d"/imm32
4082     68/push  "c"/imm32
4083     53/push-ebx
4084     68/push  "A"/imm32
4085     # . . call
4086     e8/call  trace-slsss/disp32
4087     # . . discard args
4088     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
4089 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
4115     # check-trace-contains("Abcde")
4116     # . . push args
4117     68/push  "F - test-trace-slsss"/imm32
4118     68/push  "Abcde"/imm32
4119     # . . call
4120     e8/call  check-trace-contains/disp32
4121     # . . discard args
4122     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4123     # . epilogue
4124     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4125     5d/pop-to-ebp
4126     c3/return
4127 
4128 num-bytes:  # line: (addr stream byte) -> eax: int
4129     # pseudocode:
4130     #   result = 0
4131     #   while true
4132     #     var word-slice = next-word(line)
4133     #     if slice-empty?(word-slice)             # end of line
4134     #       break
4135     #     if slice-starts-with?(word-slice, "#")  # comment
4136     #       break
4137     #     if is-label?(word-slice)                # no need for label declarations anymore
4138     #       break
4139     #     if slice-equal?(word-slice, "==")
4140     #       break                                 # no need for segment header lines
4141     #     result += compute-width(word-slice)
4142     #   return result
4143     #
4144     # . prologue
4145     55/push-ebp
4146     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4147     # . save registers
4148     51/push-ecx
4149     52/push-edx
4150     53/push-ebx
4151     # var result/eax = 0
4152     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4153     # var word-slice/ecx: slice
4154     68/push  0/imm32/end
4155     68/push  0/imm32/start
4156     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4157 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
4183     # . rewind-stream(line)
4184     # . . push args
4185     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
4186     # . . call
4187     e8/call  rewind-stream/disp32
4188     # . . discard args
4189     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4190 $num-bytes:loop:
4191     # next-word(line, word-slice)
4192     # . . push args
4193     51/push-ecx
4194     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
4195     # . . call
4196     e8/call  next-word/disp32
4197     # . . discard args
4198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4199 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
4239 $num-bytes:check0:
4240     # if (slice-empty?(word-slice)) break
4241     # . save result
4242     50/push-eax
4243     # . eax = slice-empty?(word-slice)
4244     # . . push args
4245     51/push-ecx
4246     # . . call
4247     e8/call  slice-empty?/disp32
4248     # . . discard args
4249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4250     # . if (eax != false) break
4251     3d/compare-eax-and  0/imm32/false
4252     # . restore result now that ZF is set
4253     58/pop-to-eax
4254     75/jump-if-!=  $num-bytes:end/disp8
4255 $num-bytes:check-for-comment:
4256     # if (slice-starts-with?(word-slice, "#")) break
4257     # . start/edx = word-slice->start
4258     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
4259     # . c/ebx = *start
4260     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4261     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           3/r32/BL    .               .                 # copy byte at *edx to BL
4262     # . if (ebx == '#') break
4263     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x23/imm32/hash   # compare ebx
4264     74/jump-if-=  $num-bytes:end/disp8
4265 $num-bytes:check-for-label:
4266     # if (slice-ends-with?(word-slice, ":")) break
4267     # . end/edx = word-slice->end
4268     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
4269     # . c/ebx = *(end-1)
4270     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4271     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ecx to BL
4272     # . if (ebx == ':') break
4273     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x3a/imm32/colon  # compare ebx
4274     74/jump-if-=  $num-bytes:end/disp8
4275 $num-bytes:check-for-segment-header:
4276     # if (slice-equal?(word-slice, "==")) break
4277     # . push result
4278     50/push-eax
4279     # . eax = slice-equal?(word-slice, "==")
4280     # . . push args
4281     68/push  "=="/imm32
4282     51/push-ecx
4283     # . . call
4284     e8/call  slice-equal?/disp32
4285     # . . discard args
4286     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4287     # . if (eax != false) break
4288     3d/compare-eax-and  0/imm32/false
4289     # . restore result now that ZF is set
4290     58/pop-to-eax
4291     75/jump-if-!=  $num-bytes:end/disp8
4292 $num-bytes:loop-body:
4293     # result += compute-width-of-slice(word-slice)
4294     # . copy result to edx
4295     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
4296     # . eax = compute-width-of-slice(word-slice)
4297     # . . push args
4298     51/push-ecx
4299     # . . call
4300     e8/call compute-width-of-slice/disp32
4301     # . . discard args
4302     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4303     # . eax += result
4304     01/add                          3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # add edx to eax
4305     e9/jump  $num-bytes:loop/disp32
4306 $num-bytes:end:
4307     # . rewind-stream(line)
4308     # . . push args
4309     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
4310     # . . call
4311     e8/call  rewind-stream/disp32
4312     # . . discard args
4313     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4314     # . reclaim locals
4315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4316     # . restore registers
4317     5b/pop-to-ebx
4318     5a/pop-to-edx
4319     59/pop-to-ecx
4320     # . epilogue
4321     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4322     5d/pop-to-ebp
4323     c3/return
4324 
4325 test-num-bytes-handles-empty-string:
4326     # if a line starts with '#', return 0
4327     # . prologue
4328     55/push-ebp
4329     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4330     # setup
4331     # . clear-stream(_test-input-stream)
4332     # . . push args
4333     68/push  _test-input-stream/imm32
4334     # . . call
4335     e8/call  clear-stream/disp32
4336     # . . discard args
4337     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4338     # . clear-stream(_test-output-stream)
4339     # . . push args
4340     68/push  _test-output-stream/imm32
4341     # . . call
4342     e8/call  clear-stream/disp32
4343     # . . discard args
4344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4345     # no contents in input
4346     # eax = num-bytes(_test-input-stream)
4347     # . . push args
4348     68/push  _test-input-stream/imm32
4349     # . . call
4350     e8/call  num-bytes/disp32
4351     # . . discard args
4352     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4353     # check-ints-equal(eax, 0, msg)
4354     # . . push args
4355     68/push  "F - test-num-bytes-handles-empty-string"/imm32
4356     68/push  0/imm32/true
4357     50/push-eax
4358     # . . call
4359     e8/call  check-ints-equal/disp32
4360     # . . discard args
4361     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4362     # . epilogue
4363     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4364     5d/pop-to-ebp
4365     c3/return
4366 
4367 test-num-bytes-ignores-comments:
4368     # if a line starts with '#', return 0
4369     # . prologue
4370     55/push-ebp
4371     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4372     # setup
4373     # . clear-stream(_test-input-stream)
4374     # . . push args
4375     68/push  _test-input-stream/imm32
4376     # . . call
4377     e8/call  clear-stream/disp32
4378     # . . discard args
4379     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4380     # . clear-stream(_test-output-stream)
4381     # . . push args
4382     68/push  _test-output-stream/imm32
4383     # . . call
4384     e8/call  clear-stream/disp32
4385     # . . discard args
4386     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4387     # initialize input
4388     # . write(_test-input-stream, "# abcd")
4389     # . . push args
4390     68/push  "# abcd"/imm32
4391     68/push  _test-input-stream/imm32
4392     # . . call
4393     e8/call  write/disp32
4394     # . . discard args
4395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4396     # eax = num-bytes(_test-input-stream)
4397     # . . push args
4398     68/push  _test-input-stream/imm32
4399     # . . call
4400     e8/call  num-bytes/disp32
4401     # . . discard args
4402     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4403     # check-ints-equal(eax, 0, msg)
4404     # . . push args
4405     68/push  "F - test-num-bytes-ignores-comments"/imm32
4406     68/push  0/imm32/true
4407     50/push-eax
4408     # . . call
4409     e8/call  check-ints-equal/disp32
4410     # . . discard args
4411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4412     # . epilogue
4413     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4414     5d/pop-to-ebp
4415     c3/return
4416 
4417 test-num-bytes-ignores-labels:
4418     # if the first word ends with ':', return 0
4419     # . prologue
4420     55/push-ebp
4421     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4422     # setup
4423     # . clear-stream(_test-input-stream)
4424     # . . push args
4425     68/push  _test-input-stream/imm32
4426     # . . call
4427     e8/call  clear-stream/disp32
4428     # . . discard args
4429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4430     # . clear-stream(_test-output-stream)
4431     # . . push args
4432     68/push  _test-output-stream/imm32
4433     # . . call
4434     e8/call  clear-stream/disp32
4435     # . . discard args
4436     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4437     # initialize input
4438     # . write(_test-input-stream, "ab: # cd")
4439     # . . push args
4440     68/push  "ab: # cd"/imm32
4441     68/push  _test-input-stream/imm32
4442     # . . call
4443     e8/call  write/disp32
4444     # . . discard args
4445     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4446     # eax = num-bytes(_test-input-stream)
4447     # . . push args
4448     68/push  _test-input-stream/imm32
4449     # . . call
4450     e8/call  num-bytes/disp32
4451     # . . discard args
4452     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4453     # check-ints-equal(eax, 0, msg)
4454     # . . push args
4455     68/push  "F - test-num-bytes-ignores-labels"/imm32
4456     68/push  0/imm32/true
4457     50/push-eax
4458     # . . call
4459     e8/call  check-ints-equal/disp32
4460     # . . discard args
4461     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4462     # . epilogue
4463     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4464     5d/pop-to-ebp
4465     c3/return
4466 
4467 test-num-bytes-ignores-segment-headers:
4468     # if the first word is '==', return 0
4469     # . prologue
4470     55/push-ebp
4471     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4472     # setup
4473     # . clear-stream(_test-input-stream)
4474     # . . push args
4475     68/push  _test-input-stream/imm32
4476     # . . call
4477     e8/call  clear-stream/disp32
4478     # . . discard args
4479     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4480     # . clear-stream(_test-output-stream)
4481     # . . push args
4482     68/push  _test-output-stream/imm32
4483     # . . call
4484     e8/call  clear-stream/disp32
4485     # . . discard args
4486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4487     # initialize input
4488     # . write(_test-input-stream, "== ab cd")
4489     # . . push args
4490     68/push  "== ab cd"/imm32
4491     68/push  _test-input-stream/imm32
4492     # . . call
4493     e8/call  write/disp32
4494     # . . discard args
4495     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4496     # eax = num-bytes(_test-input-stream)
4497     # . . push args
4498     68/push  _test-input-stream/imm32
4499     # . . call
4500     e8/call  num-bytes/disp32
4501     # . . discard args
4502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4503     # check-ints-equal(eax, 0, msg)
4504     # . . push args
4505     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
4506     68/push  0/imm32/true
4507     50/push-eax
4508     # . . call
4509     e8/call  check-ints-equal/disp32
4510     # . . discard args
4511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4512     # . epilogue
4513     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4514     5d/pop-to-ebp
4515     c3/return
4516 
4517 test-num-bytes-counts-words-by-default:
4518     # without metadata, count words
4519     # . prologue
4520     55/push-ebp
4521     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4522     # setup
4523     # . clear-stream(_test-input-stream)
4524     # . . push args
4525     68/push  _test-input-stream/imm32
4526     # . . call
4527     e8/call  clear-stream/disp32
4528     # . . discard args
4529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4530     # . clear-stream(_test-output-stream)
4531     # . . push args
4532     68/push  _test-output-stream/imm32
4533     # . . call
4534     e8/call  clear-stream/disp32
4535     # . . discard args
4536     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4537     # initialize input
4538     # . write(_test-input-stream, "ab cd ef")
4539     # . . push args
4540     68/push  "ab cd ef"/imm32
4541     68/push  _test-input-stream/imm32
4542     # . . call
4543     e8/call  write/disp32
4544     # . . discard args
4545     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4546     # eax = num-bytes(_test-input-stream)
4547     # . . push args
4548     68/push  _test-input-stream/imm32
4549     # . . call
4550     e8/call  num-bytes/disp32
4551     # . . discard args
4552     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4553     # check-ints-equal(eax, 3, msg)
4554     # . . push args
4555     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
4556     68/push  3/imm32/true
4557     50/push-eax
4558     # . . call
4559     e8/call  check-ints-equal/disp32
4560     # . . discard args
4561     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4562     # . epilogue
4563     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4564     5d/pop-to-ebp
4565     c3/return
4566 
4567 test-num-bytes-ignores-trailing-comment:
4568     # trailing comments appropriately ignored
4569     # . prologue
4570     55/push-ebp
4571     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4572     # setup
4573     # . clear-stream(_test-input-stream)
4574     # . . push args
4575     68/push  _test-input-stream/imm32
4576     # . . call
4577     e8/call  clear-stream/disp32
4578     # . . discard args
4579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4580     # . clear-stream(_test-output-stream)
4581     # . . push args
4582     68/push  _test-output-stream/imm32
4583     # . . call
4584     e8/call  clear-stream/disp32
4585     # . . discard args
4586     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4587     # initialize input
4588     # . write(_test-input-stream, "ab cd # ef")
4589     # . . push args
4590     68/push  "ab cd # ef"/imm32
4591     68/push  _test-input-stream/imm32
4592     # . . call
4593     e8/call  write/disp32
4594     # . . discard args
4595     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4596     # eax = num-bytes(_test-input-stream)
4597     # . . push args
4598     68/push  _test-input-stream/imm32
4599     # . . call
4600     e8/call  num-bytes/disp32
4601     # . . discard args
4602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4603     # check-ints-equal(eax, 2, msg)
4604     # . . push args
4605     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
4606     68/push  2/imm32/true
4607     50/push-eax
4608     # . . call
4609     e8/call  check-ints-equal/disp32
4610     # . . discard args
4611     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4612     # . epilogue
4613     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4614     5d/pop-to-ebp
4615     c3/return
4616 
4617 test-num-bytes-handles-imm32:
4618     # if a word has the /imm32 metadata, count it as 4 bytes
4619     # . prologue
4620     55/push-ebp
4621     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4622     # setup
4623     # . clear-stream(_test-input-stream)
4624     # . . push args
4625     68/push  _test-input-stream/imm32
4626     # . . call
4627     e8/call  clear-stream/disp32
4628     # . . discard args
4629     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4630     # . clear-stream(_test-output-stream)
4631     # . . push args
4632     68/push  _test-output-stream/imm32
4633     # . . call
4634     e8/call  clear-stream/disp32
4635     # . . discard args
4636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4637     # initialize input
4638     # . write(_test-input-stream, "ab cd/imm32 ef")
4639     # . . push args
4640     68/push  "ab cd/imm32 ef"/imm32
4641     68/push  _test-input-stream/imm32
4642     # . . call
4643     e8/call  write/disp32
4644     # . . discard args
4645     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4646     # eax = num-bytes(_test-input-stream)
4647     # . . push args
4648     68/push  _test-input-stream/imm32
4649     # . . call
4650     e8/call  num-bytes/disp32
4651     # . . discard args
4652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4653     # check-ints-equal(eax, 6, msg)
4654     # . . push args
4655     68/push  "F - test-num-bytes-handles-imm32"/imm32
4656     68/push  6/imm32/true
4657     50/push-eax
4658     # . . call
4659     e8/call  check-ints-equal/disp32
4660     # . . discard args
4661     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4662     # . epilogue
4663     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4664     5d/pop-to-ebp
4665     c3/return
4666 
4667 == data
4668 
4669 # This block of bytes gets copied to the start of the output ELF file, with
4670 # some fields (the ones with labels capitalized) filled in.
4671 # http://www.sco.com/developers/gabi/latest/ch4.eheader.html
4672 Elf_header:
4673   # - length
4674   0x34/imm32
4675   # - data
4676 $e_ident:
4677   7f 45/E 4c/L 46/F
4678   01/32-bit  01/little-endian  01/file-version  00/no-os-extensions
4679   00 00 00 00 00 00 00 00  # 8 bytes of padding
4680 $e_type:
4681   02 00
4682 $e_machine:
4683   03 00
4684 $e_version:
4685   1/imm32
4686 $Elf_e_entry:
4687   0x09000000/imm32  # approximate default; must be updated
4688 $e_phoff:
4689   0x34/imm32  # offset for the 'program header table' containing segment headers
4690 $e_shoff:
4691   0/imm32  # no sections
4692 $e_flags:
4693   0/imm32  # unused
4694 $e_ehsize:
4695   0x34 00
4696 $e_phentsize:
4697   0x20 00
4698 $Elf_e_phnum:
4699   00 00  # number of segments; must be updated
4700 $e_shentsize:
4701   00 00  # no sections
4702 $e_shnum:
4703   00 00
4704 $e_shstrndx:
4705   00 00
4706 
4707 # This block of bytes gets copied after the Elf_header once for each segment.
4708 # Some fields need filling in each time.
4709 # https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html
4710 Elf_program_header_entry:
4711   # - length
4712   0x20/imm32
4713   # - data
4714 $p_type:
4715   1/imm32/PT_LOAD
4716 $Elf_p_offset:
4717   0/imm32  # byte offset in the file at which a segment begins; must be updated
4718 $Elf_p_vaddr:
4719   0/imm32  # starting address to store the segment at before running the program
4720 $Elf_p_paddr:
4721   0/imm32  # should have same value as $Elf_p_vaddr
4722 $Elf_p_filesz:
4723   0/imm32
4724 $Elf_p_memsz:
4725   0/imm32  # should have same value as $Elf_p_filesz
4726 $Elf_p_flags:
4727   6/imm32/rw-  # read/write/execute permissions for the segment; must be updated for the code segment
4728 $p_align:
4729   # we hold this constant; changing it will require adjusting the way we
4730   # compute the starting address for each segment
4731   0x1000/imm32
4732 
4733 # . . vim:nowrap:textwidth=0