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 *.subx apps/survey.subx -o apps/survey
   9 #
  10 # The expected input is a stream of bytes with segment headers, comments and
  11 # some interspersed labels.
  12 #   $ cat x
  13 #   == code 0x1
  14 #   l1:
  15 #   aa bb l1/imm8
  16 #   cc dd l2/disp32
  17 #   l2:
  18 #   ee foo/imm32
  19 #   == data 0x10
  20 #   foo:
  21 #     00
  22 #
  23 # The output is the stream of bytes without segment headers or label definitions,
  24 # and with label references replaced with numeric values/displacements.
  25 #
  26 #   $ cat x  |./subx run apps/assort
  27 #   ...ELF header bytes...
  28 #   # ELF header above will specify that code segment begins at this offset
  29 #   aa bb nn  # some computed address
  30 #   cc dd nn nn nn nn  # some computed displacement
  31 #   ee nn nn nn nn  # some computed address
  32 #   # ELF header above will specify that data segment begins at this offset
  33 #   00
  34 #
  35 # 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:
  45     # Heap = new-segment(Heap-size)
  46     # . . push args
  47     68/push  Heap/imm32
  48     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  49     # . . call
  50     e8/call  new-segment/disp32
  51     # . . discard args
  52     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  53     # initialize-trace-stream(256KB)
  54     # . . push args
  55     68/push  0x40000/imm32/256KB
  56     # . . call
  57     e8/call  initialize-trace-stream/disp32
  58     # . . discard args
  59     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
  60 
  61     # run tests if necessary, convert stdin if not
  62     # . prolog
  63     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  64     # initialize heap
  65     # - if argc > 1 and argv[1] == "test", then return run_tests()
  66     # . argc > 1
  67     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  68     7e/jump-if-lesser-or-equal  $run-main/disp8
  69     # . 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     # . check result
  78     3d/compare-EAX-and  1/imm32
  79     75/jump-if-not-equal  $run-main/disp8
  80     # . run-tests()
  81     e8/call  run-tests/disp32
  82     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  83     eb/jump  $main:end/disp8
  84 $run-main:
  85     # - otherwise convert stdin
  86     # convert(Stdin, Stdout)
  87     # . . push args
  88     68/push  Stdout/imm32
  89     68/push  Stdin/imm32
  90     # . . call
  91     e8/call  convert/disp32
  92     # . . discard args
  93     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  94 #?     # . write-stream(2/stderr, Trace-stream)
  95 #?     # . . push args
  96 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
  97 #?     68/push  2/imm32/stderr
  98 #?     # . . call
  99 #?     e8/call  write-stream/disp32
 100 #?     # . . discard args
 101 #?     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 102     # . syscall(exit, 0)
 103     bb/copy-to-EBX  0/imm32
 104 $main:end:
 105     b8/copy-to-EAX  1/imm32/exit
 106     cd/syscall  0x80/imm8
 107 
 108 # data structures:
 109 #   segment-info: {address, file-offset, size}            (12 bytes)
 110 #   segments: (address stream {string, segment-info})     (16 bytes per row)
 111 #   label-info: {segment-name, segment-offset, address}   (12 bytes)
 112 #   labels: (address stream {string, label-info})         (16 bytes per row)
 113 # these are all inefficient; use sequential scans for lookups
 114 
 115 convert:  # infile : (address buffered-file), out : (address buffered-file) -> <void>
 116     # pseudocode
 117     #   var in : (address stream byte) = stream(4096)
 118     #   slurp(infile, in)
 119     #   var segments = new-stream(10 rows, 16 bytes each)
 120     #   var labels = new-stream(Max-labels rows, 16 bytes each)
 121     #   compute-offsets(in, segments, labels)
 122     #   compute-addresses(segments, labels)
 123     #   rewind-stream(in)
 124     #   emit-output(in, out, segments, labels)
 125     #
 126     # . prolog
 127     55/push-EBP
 128     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 129     # . save registers
 130     51/push-ECX
 131     52/push-EDX
 132     56/push-ESI
 133     # var segments/ECX = stream(10 * 16)
 134     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
 135     68/push  0xa0/imm32/length
 136     68/push  0/imm32/read
 137     68/push  0/imm32/write
 138     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 139     # var labels/EDX = stream(Max-labels * 16)
 140     # . data
 141     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/ESP   Max-labels/disp32                 # subtract *Max-labels from ESP
 142     # . length
 143     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
 144     # . read
 145     68/push  0/imm32/read
 146     # . write
 147     68/push  0/imm32/write
 148     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 149     # var in/ESI = stream(Input-size * 1)
 150     # . data
 151     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/ESP   Input-size/disp32                 # subtract *Input-size from ESP
 152     # . length
 153     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
 154     # . read
 155     68/push  0/imm32/read
 156     # . write
 157     68/push  0/imm32/write
 158     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
 159 +-- 47 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 206 +--  9 lines: #?     # write(2/stderr, "slurp in\n") ---------------------------------------------------------------------------------------------------------
 215     # slurp(infile, in)
 216     # . . push args
 217     56/push-ESI
 218     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 219     # . . call
 220     e8/call  slurp/disp32
 221     # . . discard args
 222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 223 +-- 46 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 269 +-- 33 lines: #?     # dump in -------------------------------------------------------------------------------------------------------------------------------
 302 +--  9 lines: #?     # write(2/stderr, "compute-offsets\n") --------------------------------------------------------------------------------------------------
 311     # compute-offsets(in, segments, labels)
 312     # . . push args
 313     52/push-EDX
 314     51/push-ECX
 315     56/push-ESI
 316     # . . call
 317     e8/call  compute-offsets/disp32
 318     # . . discard args
 319     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 320 +--  9 lines: #?     # write(2/stderr, "compute-addresses\n") ------------------------------------------------------------------------------------------------
 329     # compute-addresses(segments, labels)
 330     # . . push args
 331     52/push-EDX
 332     51/push-ECX
 333     # . . call
 334     e8/call  compute-addresses/disp32
 335     # . . discard args
 336     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
 337     # rewind-stream(in)
 338     # . . push args
 339     56/push-ESI
 340     # . . call
 341     e8/call  rewind-stream/disp32
 342     # . . discard args
 343     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 344 +--  9 lines: #?     # write(2/stderr, "emit-output\n") ------------------------------------------------------------------------------------------------------
 353 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
 379 +-- 46 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 425     # emit-output(in, out, segments, labels)
 426     # . . push args
 427     52/push-EDX
 428     51/push-ECX
 429     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 430     56/push-ESI
 431     # . . call
 432     e8/call  emit-output/disp32
 433     # . . discard args
 434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
 435     # flush(out)
 436     # . . push args
 437     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 438     # . . call
 439     e8/call  flush/disp32
 440     # . . discard args
 441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 442 $convert:end:
 443     # . reclaim locals
 444     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x30a0/imm32      # add to ESP
 445     # . restore registers
 446     5e/pop-to-ESI
 447     5a/pop-to-EDX
 448     59/pop-to-ECX
 449     # . epilog
 450     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 451     5d/pop-to-EBP
 452     c3/return
 453 
 454 test-convert-computes-addresses:
 455     # input:
 456     #   == code 0x1
 457     #   Entry:
 458     #   ab x/imm32
 459     #   == data 0x1000
 460     #   x:
 461     #     01
 462     #
 463     # trace contains (in any order):
 464     #   label x is at address 0x1079
 465     #   segment code starts at address 0x74
 466     #   segment code has size 5
 467     #   segment data starts at address 0x1079
 468     #
 469     # . prolog
 470     55/push-EBP
 471     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 472     # setup
 473     # . clear-stream(_test-input-stream)
 474     # . . push args
 475     68/push  _test-input-stream/imm32
 476     # . . call
 477     e8/call  clear-stream/disp32
 478     # . . discard args
 479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 480     # . clear-stream(_test-input-buffered-file+4)
 481     # . . push args
 482     b8/copy-to-EAX  _test-input-buffered-file/imm32
 483     05/add-to-EAX  4/imm32
 484     50/push-EAX
 485     # . . call
 486     e8/call  clear-stream/disp32
 487     # . . discard args
 488     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 489     # . clear-stream(_test-output-stream)
 490     # . . push args
 491     68/push  _test-output-stream/imm32
 492     # . . call
 493     e8/call  clear-stream/disp32
 494     # . . discard args
 495     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 496     # . clear-stream(_test-output-buffered-file+4)
 497     # . . push args
 498     b8/copy-to-EAX  _test-output-buffered-file/imm32
 499     05/add-to-EAX  4/imm32
 500     50/push-EAX
 501     # . . call
 502     e8/call  clear-stream/disp32
 503     # . . discard args
 504     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 505     # initialize input
 506     # . write(_test-input-stream, "== code 0x1\n")
 507     # . . push args
 508     68/push  "== code 0x1\n"/imm32
 509     68/push  _test-input-stream/imm32
 510     # . . call
 511     e8/call  write/disp32
 512     # . . discard args
 513     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 514     # . write(_test-input-stream, "Entry:\n")
 515     # . . push args
 516     68/push  "Entry:\n"/imm32
 517     68/push  _test-input-stream/imm32
 518     # . . call
 519     e8/call  write/disp32
 520     # . . discard args
 521     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 522     # . write(_test-input-stream, "ab x/imm32\n")
 523     # . . push args
 524     68/push  "ab x/imm32\n"/imm32
 525     68/push  _test-input-stream/imm32
 526     # . . call
 527     e8/call  write/disp32
 528     # . . discard args
 529     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 530     # . write(_test-input-stream, "== data 0x1000\n")
 531     # . . push args
 532     68/push  "== data 0x1000\n"/imm32
 533     68/push  _test-input-stream/imm32
 534     # . . call
 535     e8/call  write/disp32
 536     # . . discard args
 537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 538     # . write(_test-input-stream, "x:\n")
 539     # . . push args
 540     68/push  "x:\n"/imm32
 541     68/push  _test-input-stream/imm32
 542     # . . call
 543     e8/call  write/disp32
 544     # . . discard args
 545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 546     # . write(_test-input-stream, "01\n")
 547     # . . push args
 548     68/push  "01\n"/imm32
 549     68/push  _test-input-stream/imm32
 550     # . . call
 551     e8/call  write/disp32
 552     # . . discard args
 553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 554     # convert(_test-input-buffered-file, _test-output-buffered-file)
 555     # . . push args
 556     68/push  _test-output-buffered-file/imm32
 557     68/push  _test-input-buffered-file/imm32
 558     # . . call
 559     e8/call  convert/disp32
 560     # . . discard args
 561     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 562     # check trace
 563 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
 589     # . check-trace-contains("label 'x' is at address 0x00001079.", msg)
 590     # . . push args
 591     68/push  "F - test-convert-computes-addresses/0"/imm32
 592     68/push  "label 'x' is at address 0x00001079."/imm32
 593     # . . call
 594     e8/call  check-trace-contains/disp32
 595     # . . discard args
 596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 597     # . check-trace-contains("segment 'code' starts at address 0x00000074.", msg)
 598     # . . push args
 599     68/push  "F - test-convert-computes-addresses/1"/imm32
 600     68/push  "segment 'code' starts at address 0x00000074."/imm32
 601     # . . call
 602     e8/call  check-trace-contains/disp32
 603     # . . discard args
 604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 605     # . check-trace-contains("segment 'code' has size 0x00000005.", msg)
 606     # . . push args
 607     68/push  "F - test-convert-computes-addresses/2"/imm32
 608     68/push  "segment 'code' has size 0x00000005."/imm32
 609     # . . call
 610     e8/call  check-trace-contains/disp32
 611     # . . discard args
 612     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 613     # . check-trace-contains("segment 'data' starts at address 0x00001079.", msg)
 614     # . . push args
 615     68/push  "F - test-convert-computes-addresses/3"/imm32
 616     68/push  "segment 'data' starts at address 0x00001079."/imm32
 617     # . . call
 618     e8/call  check-trace-contains/disp32
 619     # . . discard args
 620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 621     # . epilog
 622     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 623     5d/pop-to-EBP
 624     c3/return
 625 
 626 # global scratch space for compute-offsets in the data segment
 627 == data
 628 
 629 compute-offsets:file-offset:  # int
 630   0/imm32
 631 compute-offsets:segment-offset:  # int
 632   0/imm32
 633 compute-offsets:word-slice:
 634   0/imm32/start
 635   0/imm32/end
 636 compute-offsets:segment-tmp:  # slice
 637   0/imm32/start
 638   0/imm32/end
 639 
 640 == code
 641 
 642 compute-offsets:  # in : (address stream), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
 643     # skeleton:
 644     #   for lines in 'in'
 645     #     for words in line
 646     #       switch word
 647     #         case 1
 648     #         case 2
 649     #         ...
 650     #         default
 651     #
 652     # pseudocode:
 653     #   curr-segment-name : (address string) = 0
 654     #   var line = new-stream(512, 1)
 655     #   while true                                  # line loop
 656     #     clear-stream(line)
 657     #     read-line(in, line)
 658     #     if (line->write == 0) break               # end of file
 659     #     while true                                # word loop
 660     #       word-slice = next-word(line)
 661     #       if slice-empty?(word-slice)             # end of line
 662     #         break
 663     #       else if slice-starts-with?(word-slice, "#")  # comment
 664     #         break                                 # end of line
 665     #       else if slice-equal?(word-slice, "==")
 666     #         if curr-segment-name != 0
 667     #           seg = get-or-insert(segments, curr-segment-name)
 668     #           seg->size = *file-offset - seg->file-offset
 669     #           trace("segment '", curr-segment-name, "' has size ", seg->size)
 670     #         segment-tmp = next-word(line)
 671     #         curr-segment-name = slice-to-string(segment-tmp)
 672     #         if empty?(curr-segment-name)
 673     #           abort
 674     #         segment-tmp = next-word(line)
 675     #         if slice-empty?(segment-tmp)
 676     #           abort
 677     #         seg = get-or-insert(segments, curr-segment-name)
 678     #         seg->starting-address = parse-hex-int(segment-tmp)
 679     #         seg->file-offset = *file-offset
 680     #         trace("segment '", curr-segment-name, "' is at file offset ", seg->file-offset)
 681     #         segment-offset = 0
 682     #         break  (next line)
 683     #       else if is-label?(word-slice)
 684     #         strip trailing ':' from word-slice
 685     #         x : (address label-info) = get-or-insert(labels, name)
 686     #         x->segment-name = curr-segment-name
 687     #         trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
 688     #         x->segment-offset = segment-offset
 689     #         trace("label '", word-slice, "' is at segment offset ", segment-offset, ".")
 690     #         # labels occupy no space, so no need to increment offsets
 691     #       else
 692     #         width = compute-width-of-slice(word-slice)
 693     #         *segment-offset += width
 694     #         *file-offset += width
 695     #
 696     # . prolog
 697     55/push-EBP
 698     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 699     # . save registers
 700     50/push-EAX
 701     51/push-ECX
 702     52/push-EDX
 703     53/push-EBX
 704     56/push-ESI
 705     57/push-EDI
 706     # curr-segment-name/ESI = 0
 707     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
 708     # file-offset = 0
 709     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:file-offset/disp32  0/imm32               # copy to *compute-offsets:word-slice
 710     # segment-offset = 0
 711     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:segment-offset/disp32  0/imm32            # copy to *compute-offsets:word-slice
 712     # line/ECX = new-stream(512, 1)
 713     # . EAX = new-stream(512, 1)
 714     # . . push args
 715     68/push  1/imm32
 716     68/push  0x200/imm32
 717     68/push  Heap/imm32
 718     # . . call
 719     e8/call  new-stream/disp32
 720     # . . discard args
 721     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 722     # . line/ECX = EAX
 723     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
 724 $compute-offsets:line-loop:
 725     # clear-stream(line/ECX)
 726     51/push-ECX
 727     e8/call  clear-stream/disp32
 728     # . discard args
 729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 730     # read-line(in, line/ECX)
 731     51/push-ECX
 732     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 733     e8/call  read-line/disp32
 734     # . discard args
 735     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 736     # if (line->write == 0) break
 737     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
 738     3d/compare-EAX-and  0/imm32
 739     0f 84/jump-if-equal  $compute-offsets:break-line-loop/disp32
 740 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 773 $compute-offsets:word-loop:
 774     # EDX = word-slice
 775     ba/copy-to-EDX  compute-offsets:word-slice/imm32
 776     # next-word(line/ECX, word-slice/EDX)
 777     52/push-EDX
 778     51/push-ECX
 779     e8/call  next-word/disp32
 780     # . discard args
 781     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 782 +-- 80 lines: #?     # dump word-slice and maybe curr-segment-name -------------------------------------------------------------------------------------------
 862 $compute-offsets:case-empty:
 863     # if slice-empty?(word/EDX) break
 864     # . EAX = slice-empty?(word/EDX)
 865     52/push-EDX
 866     e8/call  slice-empty?/disp32
 867     # . . discard args
 868     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 869     # . if (EAX != 0) break
 870     3d/compare-EAX-and  0/imm32
 871     0f 85/jump-if-not-equal  $compute-offsets:line-loop/disp32
 872 $compute-offsets:case-comment:
 873     # if slice-starts-with?(word-slice, "#") continue
 874     68/push  "#"/imm32
 875     52/push-EDX
 876     e8/call  slice-starts-with?/disp32
 877     # . . discard args
 878     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 879     # . if (EAX != 0) break
 880     3d/compare-EAX-and  0/imm32
 881     0f 85/jump-if-not-equal  $compute-offsets:line-loop/disp32
 882 $compute-offsets:case-segment-header:
 883     # if (!slice-equal?(word-slice/EDX, "==")) goto next case
 884     # . EAX = slice-equal?(word-slice/EDX, "==")
 885     68/push  "=="/imm32
 886     52/push-EDX
 887     e8/call  slice-equal?/disp32
 888     # . . discard args
 889     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 890     # . if (EAX == 0) goto next case
 891     3d/compare-EAX-and  0/imm32
 892     0f 84/jump-if-equal  $compute-offsets:case-label/disp32
 893     # if (curr-segment-name == 0) goto construct-next-segment
 894     81          7/subop/compare     3/mod/direct    6/rm32/ESI    .           .             .           .           .               0/imm32           # compare ESI
 895     74/jump-if-equal  $compute-offsets:construct-next-segment/disp8
 896     # seg/EAX = get-or-insert(segments, curr-segment-name, row-size=16)
 897     # . . push args
 898     68/push  0x10/imm32/row-size
 899     56/push-ESI
 900     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 901     # . . call
 902     e8/call  get-or-insert/disp32
 903     # . . discard args
 904     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 905     # seg->size = file-offset - seg->file-offset
 906     # . save ECX
 907     51/push-ECX
 908     # . EBX = *file-offset
 909     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:file-offset/disp32 # copy *file-offset to EBX
 910     # . ECX = seg->file-offset
 911     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EAX+4) to ECX
 912     # . EBX -= ECX
 913     29/subtract                     3/mod/direct    3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EBX
 914     # . seg->size = EBX
 915     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy EBX to *(EAX+8)
 916     # . restore ECX
 917     59/pop-to-ECX
 918     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
 919     # . . push args
 920     68/push  "."/imm32
 921     53/push-EBX
 922     68/push  "' has size "/imm32
 923     56/push-ESI
 924     68/push  "segment '"/imm32
 925     # . . call
 926     e8/call  trace-sssns/disp32
 927     # . . discard args
 928     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
 929 $compute-offsets:construct-next-segment:
 930     # next-word(line/ECX, segment-tmp)
 931     68/push  compute-offsets:segment-tmp/imm32
 932     51/push-ECX
 933     e8/call  next-word/disp32
 934     # . discard args
 935     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 936 +-- 47 lines: #?     # dump curr-segment-name if not null (clobbering EAX) -----------------------------------------------------------------------------------
 983 $compute-offsets:update-curr-segment-name:
 984     # curr-segment-name = slice-to-string(segment-tmp)
 985     # . EAX = slice-to-string(Heap, segment-tmp)
 986     # . . push args
 987     68/push  compute-offsets:segment-tmp/imm32
 988     68/push  Heap/imm32
 989     # . . call
 990     e8/call  slice-to-string/disp32
 991     # . . discard args
 992     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 993     # . curr-segment-name = EAX
 994     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
 995     # if empty?(curr-segment-name) abort
 996     # . if (EAX == 0) abort
 997     3d/compare-EAX-and  0/imm32
 998     0f 84/jump-if-equal  $compute-offsets:abort/disp32
 999     # next-word(line/ECX, segment-tmp)
1000     68/push  compute-offsets:segment-tmp/imm32
1001     51/push-ECX
1002     e8/call  next-word/disp32
1003     # . discard args
1004     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1005     # if slice-empty?(segment-tmp) abort
1006     # . EAX = slice-empty?(segment-tmp)
1007     68/push  compute-offsets:segment-tmp/imm32
1008     e8/call  slice-empty?/disp32
1009     # . . discard args
1010     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1011     # . if (EAX != 0) abort
1012     3d/compare-EAX-and  0/imm32
1013     0f 85/jump-if-not-equal  $compute-offsets:abort/disp32
1014     # seg/EBX = get-or-insert(segments, curr-segment-name, row-size=16)
1015     # . . push args
1016     68/push  0x10/imm32/row-size
1017     56/push-ESI
1018     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1019     # . . call
1020     e8/call  get-or-insert/disp32
1021     # . . discard args
1022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1023     # . EBX = EAX
1024     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
1025     # seg->address = parse-hex-int(segment-tmp)
1026     # . EAX = parse-hex-int(segment-tmp)
1027     68/push  compute-offsets:segment-tmp/imm32
1028     e8/call  parse-hex-int/disp32
1029     # . . discard args
1030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1031     # . seg->address = EAX
1032     89/copy                         0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EBX
1033     # seg->file-offset = *file-offset
1034     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:file-offset/disp32 # copy *file-offset to EAX
1035     89/copy                         1/mod/*+disp8   3/rm32/EBX    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EBX+4)
1036     # trace-sssns("segment '", curr-segment-name, "' is at file offset ", seg->file-offset, "")
1037     # . . push args
1038     68/push  "."/imm32
1039     50/push-EAX
1040     68/push  "' is at file offset "/imm32
1041     56/push-ESI
1042     68/push  "segment '"/imm32
1043     # . . call
1044     e8/call  trace-sssns/disp32
1045     # . . discard args
1046     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1047     # segment-offset = 0
1048     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     compute-offsets:segment-offset/disp32  0/imm32           # copy to *segment-offset
1049     # break
1050     e9/jump $compute-offsets:line-loop/disp32
1051 $compute-offsets:case-label:
1052     # if (!is-label?(word-slice/EDX)) goto next case
1053     # . EAX = is-label?(word-slice/EDX)
1054     # . . push args
1055     52/push-EDX
1056     # . . call
1057     e8/call  is-label?/disp32
1058     # . . discard args
1059     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1060     # . if (EAX == 0) goto next case
1061     3d/compare-EAX-and  0/imm32
1062     74/jump-if-equal  $compute-offsets:case-default/disp8
1063     # strip trailing ':' from word-slice
1064     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # decrement *(EDX+4)
1065     # x/EAX = leaky-get-or-insert-slice(labels, word-slice, row-size=16)
1066     # . . push args
1067     68/push  0x10/imm32/row-size
1068     52/push-EDX
1069     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
1070     # . . call
1071     e8/call  leaky-get-or-insert-slice/disp32
1072     # . . discard args
1073     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1074 $compute-offsets:save-label-offset:
1075     # x->segment-name = curr-segment-name
1076     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           6/r32/ESI   .               .                 # copy ESI to *EAX
1077     # trace-slsss("label '" word-slice/EDX "' is in segment '" current-segment-name "'.")
1078     # . . push args
1079     68/push  "'."/imm32
1080     56/push-ESI
1081     68/push  "' is in segment '"/imm32
1082     52/push-EDX
1083     68/push  "label '"/imm32
1084     # . . call
1085     e8/call  trace-slsss/disp32
1086     # . . discard args
1087     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1088     # x->segment-offset = segment-offset
1089     # . EBX = segment-offset
1090     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:segment-offset/disp32  # copy *segment-offset to EBX
1091     # . x->segment-offset = EBX
1092     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   4/disp8         .                 # copy EBX to *(EAX+4)
1093     # trace-slsns("label '" word-slice/EDX "' is at segment offset " *segment-offset/EAX ".")
1094     # . . EAX = file-offset
1095     b8/copy-to-EAX compute-offsets:segment-offset/imm32
1096     # . . EAX = *file-offset/EAX
1097     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
1098     # . . push args
1099     68/push  "."/imm32
1100     50/push-EAX
1101     68/push  "' is at segment offset "/imm32
1102     52/push-EDX
1103     68/push  "label '"/imm32
1104     # . . call
1105     e8/call  trace-slsns/disp32
1106     # . . discard args
1107     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1108     # continue
1109     e9/jump  $compute-offsets:word-loop/disp32
1110 $compute-offsets:case-default:
1111     # width/EAX = compute-width-of-slice(word-slice)
1112     # . . push args
1113     52/push-EDX
1114     # . . call
1115     e8/call compute-width-of-slice/disp32
1116     # . . discard args
1117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1118     # segment-offset += width
1119     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:segment-offset/disp32 # add EAX to *segment-offset
1120     # file-offset += width
1121     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   compute-offsets:file-offset/disp32 # add EAX to *file-offset
1122 +-- 47 lines: #?     # dump segment-offset -------------------------------------------------------------------------------------------------------------------
1169     e9/jump $compute-offsets:word-loop/disp32
1170 $compute-offsets:break-line-loop:
1171     # seg/EAX = get-or-insert(segments, curr-segment-name, row-size=16)
1172     # . . push args
1173     68/push  0x10/imm32/row-size
1174     56/push-ESI
1175     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1176     # . . call
1177     e8/call  get-or-insert/disp32
1178     # . . discard args
1179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1180     # seg->size = file-offset - seg->file-offset
1181     # . save ECX
1182     51/push-ECX
1183     # . EBX = *file-offset
1184     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   compute-offsets:file-offset/disp32 # copy *file-offset to EBX
1185     # . ECX = seg->file-offset
1186     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EAX+4) to ECX
1187     # . EBX -= ECX
1188     29/subtract                     3/mod/direct    3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EBX
1189     # . seg->size = EBX
1190     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy EBX to *(EAX+8)
1191     # . restore ECX
1192     59/pop-to-ECX
1193     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
1194     # . trace-sssns("segment '", curr-segment-name, "' has size ", EBX, ".")
1195     # . . push args
1196     68/push  "."/imm32
1197     53/push-EBX
1198     68/push  "' has size "/imm32
1199     56/push-ESI
1200     68/push  "segment '"/imm32
1201     # . . call
1202     e8/call  trace-sssns/disp32
1203     # . . discard args
1204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1205 $compute-offsets:end:
1206     # . reclaim locals
1207     # . restore registers
1208     5f/pop-to-EDI
1209     5e/pop-to-ESI
1210     5b/pop-to-EBX
1211     5a/pop-to-EDX
1212     59/pop-to-ECX
1213     58/pop-to-EAX
1214     # . epilog
1215     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1216     5d/pop-to-EBP
1217     c3/return
1218 $compute-offsets:abort:
1219     # . _write(2/stderr, error)
1220     # . . push args
1221     68/push  "'==' must be followed by segment name and segment-start\n"/imm32
1222     68/push  2/imm32/stderr
1223     # . . call
1224     e8/call  _write/disp32
1225     # . . discard args
1226     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1227     # . syscall(exit, 1)
1228     bb/copy-to-EBX  1/imm32
1229     b8/copy-to-EAX  1/imm32/exit
1230     cd/syscall  0x80/imm8
1231     # never gets here
1232 
1233 test-compute-offsets:
1234     # input:
1235     #   == code 0x1
1236     #   ab x/imm32  # skip comment
1237     #   == data 0x1000
1238     #   00
1239     #   x:
1240     #     34
1241     #
1242     # trace contains (in any order):
1243     #   segment 'code' is at file offset 0x0.
1244     #   segment 'code' has size 0x5.
1245     #   segment 'data' is at file offset 0x5.
1246     #   segment 'data' has size 0x2.
1247     #   label 'x' is in segment 'data'.
1248     #   label 'x' is at segment offset 0x1.
1249     #
1250     # . prolog
1251     55/push-EBP
1252     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1253     # setup
1254     # . clear-stream(_test-input-stream)
1255     # . . push args
1256     68/push  _test-input-stream/imm32
1257     # . . call
1258     e8/call  clear-stream/disp32
1259     # . . discard args
1260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1261     # var segments/ECX = stream(2 * 16)
1262     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x20/imm32        # subtract from ESP
1263     68/push  0x20/imm32/length
1264     68/push  0/imm32/read
1265     68/push  0/imm32/write
1266     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1267     # var labels/EDX = stream(2 * 16)
1268     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x20/imm32        # subtract from ESP
1269     68/push  0x20/imm32/length
1270     68/push  0/imm32/read
1271     68/push  0/imm32/write
1272     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
1273     # initialize input
1274     # . write(_test-input-stream, "== code 0x1\n")
1275     # . . push args
1276     68/push  "== code 0x1\n"/imm32
1277     68/push  _test-input-stream/imm32
1278     # . . call
1279     e8/call  write/disp32
1280     # . . discard args
1281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1282     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
1283     # . . push args
1284     68/push  "ab x/imm32  # skip comment\n"/imm32
1285     68/push  _test-input-stream/imm32
1286     # . . call
1287     e8/call  write/disp32
1288     # . . discard args
1289     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1290     # . write(_test-input-stream, "== data 0x1000\n")
1291     # . . push args
1292     68/push  "== data 0x1000\n"/imm32
1293     68/push  _test-input-stream/imm32
1294     # . . call
1295     e8/call  write/disp32
1296     # . . discard args
1297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1298     # . write(_test-input-stream, "00\n")
1299     # . . push args
1300     68/push  "00\n"/imm32
1301     68/push  _test-input-stream/imm32
1302     # . . call
1303     e8/call  write/disp32
1304     # . . discard args
1305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1306     # . write(_test-input-stream, "x:\n")
1307     # . . push args
1308     68/push  "x:\n"/imm32
1309     68/push  _test-input-stream/imm32
1310     # . . call
1311     e8/call  write/disp32
1312     # . . discard args
1313     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1314     # . write(_test-input-stream, "34\n")
1315     # . . push args
1316     68/push  "34\n"/imm32
1317     68/push  _test-input-stream/imm32
1318     # . . call
1319     e8/call  write/disp32
1320     # . . discard args
1321     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1322     # compute-offsets(_test-input-stream, segments, labels)
1323     # . . push args
1324     52/push-EDX
1325     51/push-ECX
1326     68/push  _test-input-stream/imm32
1327     # . . call
1328     e8/call  compute-offsets/disp32
1329     # . . discard args
1330     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32        # add to ESP
1331 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1357     # check trace
1358     # . check-trace-contains("segment 'code' is at file offset 0x00000000.", msg)
1359     # . . push args
1360     68/push  "F - test-compute-offsets/0"/imm32
1361     68/push  "segment 'code' is at file offset 0x00000000."/imm32
1362     # . . call
1363     e8/call  check-trace-contains/disp32
1364     # . . discard args
1365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1366     # . check-trace-contains("segment 'code' has size 0x00000005", msg)
1367     # . . push args
1368     68/push  "F - test-compute-offsets/1"/imm32
1369     68/push  "segment 'code' has size 0x00000005."/imm32
1370     # . . call
1371     e8/call  check-trace-contains/disp32
1372     # . . discard args
1373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1374     # . check-trace-contains("segment 'data' is at file offset 0x00000005.", msg)
1375     # . . push args
1376     68/push  "F - test-compute-offsets/2"/imm32
1377     68/push  "segment 'data' is at file offset 0x00000005."/imm32
1378     # . . call
1379     e8/call  check-trace-contains/disp32
1380     # . . discard args
1381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1382     # . check-trace-contains("segment 'data' has size 0x00000002.", msg)
1383     # . . push args
1384     68/push  "F - test-compute-offsets/3"/imm32
1385     68/push  "segment 'data' has size 0x00000002."/imm32
1386     # . . call
1387     e8/call  check-trace-contains/disp32
1388     # . . discard args
1389     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1390     # . check-trace-contains("label 'x' is in segment 'data'.", msg)
1391     # . . push args
1392     68/push  "F - test-compute-offsets/4"/imm32
1393     68/push  "label 'x' is in segment 'data'."/imm32
1394     # . . call
1395     e8/call  check-trace-contains/disp32
1396     # . . discard args
1397     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1398     # . check-trace-contains("label 'x' is at segment offset 0x00000001.", msg)
1399     # . . push args
1400     68/push  "F - test-compute-offsets/5"/imm32
1401     68/push  "label 'x' is at segment offset 0x00000001."/imm32
1402     # . . call
1403     e8/call  check-trace-contains/disp32
1404     # . . discard args
1405     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1406     # . check-ints-equal(labels->write, 0x10, msg)
1407     # . . push args
1408     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
1409     68/push  0x10/imm32/1-entry
1410     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
1411     # . . call
1412     e8/call  check-ints-equal/disp32
1413     # . . discard args
1414     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1415     # . epilog
1416     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1417     5d/pop-to-EBP
1418     c3/return
1419 
1420 compute-addresses:  # segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
1421     # pseudocode:
1422     #   srow : (address segment-info) = segments->data
1423     #   max = segments->data + segments->write
1424     #   num-segments = segments->write / 16
1425     #   starting-offset = 0x34 + (num-segments * 0x20)
1426     #   while true
1427     #     if (srow >= max) break
1428     #     s->file-offset += starting-offset
1429     #     s->address &= 0xfffff000  # clear last 12 bits for p_align
1430     #     s->address += (s->file-offset & 0x00000fff)
1431     #     trace-sssns("segment " s->key " starts at address " s->address)
1432     #     srow += 16  # row-size
1433     #   lrow : (address label-info) = labels->data
1434     #   max = labels->data + labels->write
1435     #   while true
1436     #     if (lrow >= max) break
1437     #     seg-name : (address string) = lrow->segment-name
1438     #     label-seg : (address segment-info) = get(segments, seg-name, row-size=16)
1439     #     lrow->address = label-seg->address + lrow->segment-offset
1440     #     trace-sssns("label " lrow->key " is at address " lrow->address)
1441     #     lrow += 16  # row-size
1442     #
1443     # . prolog
1444     55/push-EBP
1445     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1446     # . save registers
1447     50/push-EAX
1448     51/push-ECX
1449     52/push-EDX
1450     53/push-EBX
1451     56/push-ESI
1452     57/push-EDI
1453     # ESI = segments
1454     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
1455     # starting-offset/EDI = 0x34 + (num-segments * 0x20)  # make room for ELF headers
1456     # . EDI = segments->write / 16 (row-size)
1457     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           7/r32/EDI   .               .                 # copy *ESI to EDI
1458     c1/shift    5/subop/logic-right 3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm8            # shift EDI right by 4 bits, while padding zeroes
1459     # . EDI = (EDI * 0x20) + 0x34
1460     c1/shift    4/subop/left        3/mod/direct    7/rm32/EDI    .           .             .           .           .               5/imm8            # shift EDI left by 5 bits
1461     81          0/subop/add         3/mod/direct    7/rm32/EDI    .           .             .           .           .               0x34/imm32        # add to EDI
1462     # srow/EAX = segments->data
1463     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
1464     # max/ECX = segments->data + segments->write
1465     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
1466     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # add ESI to ECX
1467 $compute-addresses:segment-loop:
1468     # if (srow >= max) break
1469     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
1470     73/jump-if-greater-or-equal-unsigned  $compute-addresses:segment-break/disp8
1471     # srow->file-offset += starting-offset
1472     01/add                          1/mod/*+disp8   0/rm32/EAX    .           .             .           7/r32/EDI   8/disp8         .                 # add EDI to *(EAX+8)
1473     # clear last 12 bits of srow->address for p_align=0x1000
1474     # . EDX = srow->address
1475     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(EAX+4) to EDX
1476     # . EDX &= 0xfffff000
1477     81          4/subop/and         3/mod/direct    2/rm32/EDX    .           .             .           .           .               0xfffff000/imm32  # bitwise and of EDX
1478     # update last 12 bits from srow->file-offset
1479     # . EBX = srow->file-offset
1480     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # copy *(EAX+8) to EBX
1481     # . EBX &= 0xfff
1482     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x00000fff/imm32  # bitwise and of EBX
1483     # . srow->address = EDX | EBX
1484     09/or                           3/mod/direct    2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # EDX = bitwise OR with EBX
1485     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy EDX to *(EAX+4)
1486     # trace-sssns("segment " srow " starts at address " srow->address ".")
1487     # . . push args
1488     68/push  "."/imm32
1489     52/push-EDX
1490     68/push  "' starts at address "/imm32
1491     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
1492     68/push  "segment '"/imm32
1493     # . . call
1494     e8/call  trace-sssns/disp32
1495     # . . discard args
1496     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1497     # srow += 16  # size of row
1498     05/add-to-EAX  0x10/imm32
1499     eb/jump  $compute-addresses:segment-loop/disp8
1500 $compute-addresses:segment-break:
1501 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1527     # ESI = labels
1528     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
1529     # lrow/EAX = labels->data
1530     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
1531     # max/ECX = labels->data + labels->write
1532     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
1533     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # add ESI to ECX
1534 $compute-addresses:label-loop:
1535     # if (lrow >= max) break
1536     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
1537     0f 83/jump-if-greater-or-equal-unsigned  $compute-addresses:end/disp32
1538 +-- 26 lines: #?     # dump lrow->key ------------------------------------------------------------------------------------------------------------------------
1564     # seg-name/EDX = lrow->segment-name
1565     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *EAX to EDX
1566 +-- 26 lines: #?     # dump seg-name -------------------------------------------------------------------------------------------------------------------------
1592     # label-seg/EDX : (address segment-info) = get(segments, seg-name, row-size=16)
1593     # . save EAX
1594     50/push-EAX
1595     # . EAX = get(segments, seg-name, row-size=16)
1596     # . . push args
1597     68/push  0x10/imm32/row-size
1598     52/push-EDX
1599     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1600     # . . call
1601     e8/call  get/disp32
1602     # . . discard args
1603     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1604     # . EDX = EAX
1605     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
1606     # . restore EAX
1607     58/pop-to-EAX
1608     # EBX = label-seg->address
1609     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
1610     # EBX += lrow->segment-offset
1611     03/add                          1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   8/disp8         .                 # add *(EAX+8) to EBX
1612     # lrow->address = EBX
1613     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy EBX to *(EAX+12)
1614     # trace-sssns("label " lrow->key " is at address " lrow->address ".")
1615     # . . push args
1616     68/push  "."/imm32
1617     53/push-EBX
1618     68/push  "' is at address "/imm32
1619     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
1620     68/push  "label '"/imm32
1621     # . . call
1622     e8/call  trace-sssns/disp32
1623     # . . discard args
1624     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1625     # lrow += 16  # size of row
1626     05/add-to-EAX  0x10/imm32
1627     e9/jump  $compute-addresses:label-loop/disp32
1628 $compute-addresses:end:
1629     # . restore registers
1630     5f/pop-to-EDI
1631     5e/pop-to-ESI
1632     5b/pop-to-EBX
1633     5a/pop-to-EDX
1634     59/pop-to-ECX
1635     58/pop-to-EAX
1636     # . epilog
1637     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1638     5d/pop-to-EBP
1639     c3/return
1640 
1641 test-compute-addresses:
1642     # input:
1643     #   segments:
1644     #     - 'a': {0x1000, 0, 5}
1645     #     - 'b': {0x2018, 5, 1}
1646     #     - 'c': {0x5444, 6, 12}
1647     #   labels:
1648     #     - 'l1': {'a', 3, 0}
1649     #     - 'l2': {'b', 0, 0}
1650     #
1651     # trace contains in any order (comments in parens):
1652     #   segment 'a' starts at address 0x00001094.  (0x34 + 0x20 for each segment)
1653     #   segment 'b' starts at address 0x00002099.  (0x018 discarded)
1654     #   segment 'c' starts at address 0x0000509a.  (0x444 discarded)
1655     #   label 'l1' is at address 0x00001097.       (0x1094 + segment-offset 3)
1656     #   label 'l2' is at address 0x00002099.       (0x2099 + segment-offset 0)
1657     #
1658     # . prolog
1659     55/push-EBP
1660     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1661     # setup
1662     # . var segments/ECX = stream(10 * 16)
1663     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
1664     68/push  0xa0/imm32/length
1665     68/push  0/imm32/read
1666     68/push  0/imm32/write
1667     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1668     # . var labels/EDX = stream(512 * 16)
1669     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
1670     68/push  0x2000/imm32/length
1671     68/push  0/imm32/read
1672     68/push  0/imm32/write
1673     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
1674     # . stream-add4(segments, "a", 0x1000, 0, 5)
1675     68/push  5/imm32/segment-size
1676     68/push  0/imm32/file-offset
1677     68/push  0x1000/imm32/start-address
1678     68/push  "a"/imm32/segment-name
1679     51/push-ECX
1680     # . . call
1681     e8/call  stream-add4/disp32
1682     # . . discard args
1683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1684     # . stream-add4(segments, "b", 0x2018, 5, 1)
1685     68/push  1/imm32/segment-size
1686     68/push  5/imm32/file-offset
1687     68/push  0x2018/imm32/start-address
1688     68/push  "b"/imm32/segment-name
1689     51/push-ECX
1690     # . . call
1691     e8/call  stream-add4/disp32
1692     # . . discard args
1693     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1694     # . stream-add4(segments, "c", 0x5444, 6, 12)
1695     68/push  0xc/imm32/segment-size
1696     68/push  6/imm32/file-offset
1697     68/push  0x5444/imm32/start-address
1698     68/push  "c"/imm32/segment-name
1699     51/push-ECX
1700     # . . call
1701     e8/call  stream-add4/disp32
1702     # . . discard args
1703     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1704     # . stream-add4(labels, "l1", "a", 3, 0)
1705     68/push  0/imm32/label-address
1706     68/push  3/imm32/segment-offset
1707     68/push  "a"/imm32/segment-name
1708     68/push  "l1"/imm32/label-name
1709     52/push-EDX
1710     # . . call
1711     e8/call  stream-add4/disp32
1712     # . . discard args
1713     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1714     # . stream-add4(labels, "l2", "b", 0, 0)
1715     68/push  0/imm32/label-address
1716     68/push  0/imm32/segment-offset
1717     68/push  "b"/imm32/segment-name
1718     68/push  "l2"/imm32/label-name
1719     52/push-EDX
1720     # . . call
1721     e8/call  stream-add4/disp32
1722     # . . discard args
1723     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1724     # component under test
1725     # . compute-addresses(segments, labels)
1726     # . . push args
1727     52/push-EDX
1728     51/push-ECX
1729     # . . call
1730     e8/call  compute-addresses/disp32
1731     # . . discard args
1732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1733     # checks
1734 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1760     # . check-trace-contains("segment 'a' starts at address 0x00001094.", msg)
1761     # . . push args
1762     68/push  "F - test-compute-addresses/0"/imm32
1763     68/push  "segment 'a' starts at address 0x00001094."/imm32
1764     # . . call
1765     e8/call  check-trace-contains/disp32
1766     # . . discard args
1767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1768     # . check-trace-contains("segment 'b' starts at address 0x00002099.", msg)
1769     # . . push args
1770     68/push  "F - test-compute-addresses/1"/imm32
1771     68/push  "segment 'b' starts at address 0x00002099."/imm32
1772     # . . call
1773     e8/call  check-trace-contains/disp32
1774     # . . discard args
1775     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1776     # . check-trace-contains("segment 'c' starts at address 0x0000509a.", msg)
1777     # . . push args
1778     68/push  "F - test-compute-addresses/2"/imm32
1779     68/push  "segment 'c' starts at address 0x0000509a."/imm32
1780     # . . call
1781     e8/call  check-trace-contains/disp32
1782     # . . discard args
1783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1784     # . check-trace-contains("label 'l1' is at address 0x00001097.", msg)
1785     # . . push args
1786     68/push  "F - test-compute-addresses/3"/imm32
1787     68/push  "label 'l1' is at address 0x00001097."/imm32
1788     # . . call
1789     e8/call  check-trace-contains/disp32
1790     # . . discard args
1791     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1792     # . check-trace-contains("label 'l2' is at address 0x00002099.", msg)
1793     # . . push args
1794     68/push  "F - test-compute-addresses/4"/imm32
1795     68/push  "label 'l2' is at address 0x00002099."/imm32
1796     # . . call
1797     e8/call  check-trace-contains/disp32
1798     # . . discard args
1799     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1800     # . check-ints-equal(labels->write, 0x20, msg)
1801     # . . push args
1802     68/push  "F - test-compute-addresses/maintains-labels-write-index"/imm32
1803     68/push  0x20/imm32/2-entries
1804     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
1805     # . . call
1806     e8/call  check-ints-equal/disp32
1807     # . . discard args
1808     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1809     # . epilog
1810     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1811     5d/pop-to-EBP
1812     c3/return
1813 
1814 test-compute-addresses-large-segments:
1815     # input:
1816     #   segments:
1817     #     - 'a': {0x1000, 0, 0x5604}
1818     #     - 'b': {0x2018, 0x5604, 1}
1819     #   labels:
1820     #     - 'l1': {'a', 3, 0}
1821     #
1822     # trace contains in any order (comments in parens):
1823     #   segment 'a' starts at address 0x00001074.  (0x34 + 0x20 for each segment)
1824     #   segment 'b' starts at address 0x00002678.  (0x018 discarded; last 3 nibbles from 0x1074 + 0x5604)
1825     #   label 'l1' is at address 0x00001077.       (0x1074 + segment-offset 3)
1826     #
1827     # . prolog
1828     55/push-EBP
1829     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1830     # setup
1831     # . var segments/ECX = stream(10 * 16)
1832     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
1833     68/push  0xa0/imm32/length
1834     68/push  0/imm32/read
1835     68/push  0/imm32/write
1836     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1837     # . var labels/EDX = stream(512 * 16)
1838     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
1839     68/push  0x2000/imm32/length
1840     68/push  0/imm32/read
1841     68/push  0/imm32/write
1842     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
1843     # . stream-add4(segments, "a", 0x1000, 0, 0x5604)
1844     68/push  0x5604/imm32/segment-size
1845     68/push  0/imm32/file-offset
1846     68/push  0x1000/imm32/start-address
1847     68/push  "a"/imm32/segment-name
1848     51/push-ECX
1849     # . . call
1850     e8/call  stream-add4/disp32
1851     # . . discard args
1852     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1853     # . stream-add4(segments, "b", 0x2018, 0x5604, 1)
1854     68/push  1/imm32/segment-size
1855     68/push  0x5604/imm32/file-offset
1856     68/push  0x2018/imm32/start-address
1857     68/push  "b"/imm32/segment-name
1858     51/push-ECX
1859     # . . call
1860     e8/call  stream-add4/disp32
1861     # . . discard args
1862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1863     # . stream-add4(labels, "l1", "a", 3, 0)
1864     68/push  0/imm32/label-address
1865     68/push  3/imm32/segment-offset
1866     68/push  "a"/imm32/segment-name
1867     68/push  "l1"/imm32/label-name
1868     52/push-EDX
1869     # . . call
1870     e8/call  stream-add4/disp32
1871     # . . discard args
1872     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
1873     # component under test
1874     # . compute-addresses(segments, labels)
1875     # . . push args
1876     52/push-EDX
1877     51/push-ECX
1878     # . . call
1879     e8/call  compute-addresses/disp32
1880     # . . discard args
1881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1882     # checks
1883     # . check-trace-contains("segment 'a' starts at address 0x00001074.", msg)
1884     # . . push args
1885     68/push  "F - test-compute-addresses-large-segments/0"/imm32
1886     68/push  "segment 'a' starts at address 0x00001074."/imm32
1887     # . . call
1888     e8/call  check-trace-contains/disp32
1889     # . . discard args
1890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1891     # . check-trace-contains("segment 'b' starts at address 0x00002678.", msg)
1892     # . . push args
1893     68/push  "F - test-compute-addresses-large-segments/1"/imm32
1894     68/push  "segment 'b' starts at address 0x00002678."/imm32
1895     # . . call
1896     e8/call  check-trace-contains/disp32
1897     # . . discard args
1898     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1899     # . check-trace-contains("label 'l1' is at address 0x00001077.", msg)
1900     # . . push args
1901     68/push  "F - test-compute-addresses-large-segments/3"/imm32
1902     68/push  "label 'l1' is at address 0x00001077."/imm32
1903     # . . call
1904     e8/call  check-trace-contains/disp32
1905     # . . discard args
1906     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1907     # . epilog
1908     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1909     5d/pop-to-EBP
1910     c3/return
1911 
1912 emit-output:  # in : (address stream), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
1913     # pseudocode:
1914     #   emit-headers(out, segments, labels)
1915     #   emit-segments(in, out, segments, labels)
1916     #
1917     # . prolog
1918     55/push-EBP
1919     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1920 +--  9 lines: #?     # write(2/stderr, "emit-headers\n") -----------------------------------------------------------------------------------------------------
1929     # emit-headers(out, segments, labels)
1930     # . . push args
1931     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8       .                # push *(EBP+20)
1932     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8       .                # push *(EBP+16)
1933     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8        .                # push *(EBP+12)
1934     # . . call
1935     e8/call  emit-headers/disp32
1936     # . . discard args
1937     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1938 +--  9 lines: #?     # write(2/stderr, "emit-segments\n") ----------------------------------------------------------------------------------------------------
1947     # emit-segments(in, out, segments, labels)
1948     # . . push args
1949     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8       .                # push *(EBP+20)
1950     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8       .                # push *(EBP+16)
1951     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1952     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1953     # . . call
1954     e8/call  emit-segments/disp32
1955     # . . discard args
1956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
1957 $emit-output:end:
1958     # . epilog
1959     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1960     5d/pop-to-EBP
1961     c3/return
1962 
1963 emit-segments:  # in : (address stream), out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
1964     # pseudocode:
1965     #   var offset-of-next-instruction = 0
1966     #   var line = new-stream(512, 1)
1967     #   line-loop:
1968     #   while true
1969     #     clear-stream(line)
1970     #     read-line(in, line)
1971     #     if (line->write == 0) break               # end of file
1972     #     offset-of-next-instruction += num-bytes(line)
1973     #     while true
1974     #       var word-slice = next-word(line)
1975     #       if slice-empty?(word-slice)             # end of line
1976     #         break
1977     #       if slice-starts-with?(word-slice, "#")  # comment
1978     #         break
1979     #       if is-label?(word-slice)                # no need for label declarations anymore
1980     #         goto line-loop                        # don't insert empty lines
1981     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
1982     #         goto line-loop                        # don't insert empty lines
1983     #       if length(word-slice) == 2
1984     #         write-slice-buffered(out, word-slice)
1985     #         write-buffered(out, " ")
1986     #         continue
1987     #       datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
1988     #       info = get-slice(labels, datum)
1989     #       if !string-equal?(info->segment-name, "code")
1990     #         if has-metadata?(word-slice, "disp8")
1991     #           abort
1992     #         if has-metadata?(word-slice, "imm8")
1993     #           abort
1994     #         emit(out, info->address, 4)  # global variables always translate to absolute addresses
1995     #       # code segment cases
1996     #       else if has-metadata?(word-slice, "imm8")
1997     #         abort  # label should never go to imm8
1998     #       else if has-metadata?(word-slice, "imm32")
1999     #         emit(out, info->address, 4)
2000     #       else if has-metadata?(word-slice, "disp8")
2001     #         value = info->offset - offset-of-next-instruction
2002     #         emit(out, value, 1)
2003     #       else if has-metadata?(word-slice, "disp32")
2004     #         value = info->offset - offset-of-next-instruction
2005     #         emit(out, value, 4)
2006     #       else
2007     #         abort
2008     #     write-buffered(out, "\n")
2009     #
2010     # registers:
2011     #   line: ECX
2012     #   word-slice: EDX
2013     #   offset-of-next-instruction: EBX
2014     #   datum: EDI
2015     #   info: ESI (inner loop only)
2016     #   temporaries: EAX, ESI (outer loop)
2017     #
2018     # . prolog
2019     55/push-EBP
2020     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2021     # . save registers
2022     50/push-EAX
2023     51/push-ECX
2024     52/push-EDX
2025     53/push-EBX
2026     56/push-ESI
2027     57/push-EDI
2028     # var line/ECX : (address stream byte) = stream(512)
2029     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
2030     68/push  0x200/imm32/length
2031     68/push  0/imm32/read
2032     68/push  0/imm32/write
2033     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2034     # var word-slice/EDX = {0, 0}
2035     68/push  0/imm32/end
2036     68/push  0/imm32/start
2037     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
2038     # var datum/EDI = {0, 0}
2039     68/push  0/imm32/end
2040     68/push  0/imm32/start
2041     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
2042     # offset-of-next-instruction/EBX = 0
2043     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2044 $emit-segments:line-loop:
2045     # clear-stream(line)
2046     # . . push args
2047     51/push-ECX
2048     # . . call
2049     e8/call  clear-stream/disp32
2050     # . . discard args
2051     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2052     # read-line(in, line)
2053     # . . push args
2054     51/push-ECX
2055     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2056     # . . call
2057     e8/call  read-line/disp32
2058     # . . discard args
2059     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2060 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2093 $emit-segments:check-for-end-of-input:
2094     # if (line->write == 0) break
2095     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
2096     0f 84/jump-if-equal  $emit-segments:end/disp32
2097     # offset-of-next-instruction += num-bytes(line)
2098     # . EAX = num-bytes(line)
2099     # . . push args
2100     51/push-ECX
2101     # . . call
2102     e8/call  num-bytes/disp32
2103     # . . discard args
2104     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2105     # . EBX += EAX
2106     01/add                          3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # add EAX to EBX
2107 $emit-segments:word-loop:
2108     # next-word(line, word-slice)
2109     # . . push args
2110     52/push-EDX
2111     51/push-ECX
2112     # . . call
2113     e8/call  next-word/disp32
2114     # . . discard args
2115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2116 +-- 33 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2149 $emit-segments:check-for-end-of-line:
2150     # if (slice-empty?(word-slice)) break
2151     # . EAX = slice-empty?(word-slice)
2152     # . . push args
2153     52/push-EDX
2154     # . . call
2155     e8/call  slice-empty?/disp32
2156     # . . discard args
2157     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2158     # . if (EAX != 0) break
2159     3d/compare-EAX-and  0/imm32
2160     0f 85/jump-if-not-equal  $emit-segments:next-line/disp32
2161 $emit-segments:check-for-comment:
2162     # if (slice-starts-with?(word-slice, "#")) break
2163     # . start/ESI = word-slice->start
2164     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *EDX to ESI
2165     # . c/EAX = *start
2166     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2167     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
2168     # . if (EAX == '#') break
2169     3d/compare-EAX-and  0x23/imm32/hash
2170     0f 84/jump-if-equal  $emit-segments:next-line/disp32
2171 $emit-segments:check-for-label:
2172     # if is-label?(word-slice) break
2173     # . EAX = is-label?(word-slice)
2174     # . . push args
2175     52/push-EDX
2176     # . . call
2177     e8/call  is-label?/disp32
2178     # . . discard args
2179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2180     # . if (EAX != 0) break
2181     3d/compare-EAX-and  0/imm32
2182     0f 85/jump-if-not-equal  $emit-segments:line-loop/disp32
2183 $emit-segments:check-for-segment-header:
2184     # if (slice-equal?(word-slice, "==")) break
2185     # . EAX = slice-equal?(word-slice, "==")
2186     # . . push args
2187     68/push  "=="/imm32
2188     52/push-EDX
2189     # . . call
2190     e8/call  slice-equal?/disp32
2191     # . . discard args
2192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2193     # . if (EAX != 0) break
2194     3d/compare-EAX-and  0/imm32
2195     0f 85/jump-if-not-equal  $emit-segments:line-loop/disp32
2196 $emit-segments:2-character:
2197     # if (length(word-slice) != 2) goto next check
2198     # . EAX = length(word-slice)
2199     8b/copy                         1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(EDX+4) to EAX
2200     2b/subtract                     0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # subtract *EDX from EAX
2201     # . if (EAX != 2) goto next check
2202     3d/compare-EAX-and  2/imm32
2203     75/jump-if-not-equal  $emit-segments:check-metadata/disp8
2204     # write-slice-buffered(out, word-slice)
2205     # . . push args
2206     52/push-EDX
2207     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2208     # . . call
2209     e8/call  write-slice-buffered/disp32
2210     # . . discard args
2211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2212     # write-buffered(out, " ")
2213     # . . push args
2214     68/push  " "/imm32
2215     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2216     # . . call
2217     e8/call  write-buffered/disp32
2218     # . . discard args
2219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2220     # continue
2221     e9/jump  $emit-segments:word-loop/disp32
2222 $emit-segments:check-metadata:
2223     # - if we get here, 'word-slice' must be a label to be looked up
2224     # datum/EDI = next-token-from-slice(word-slice->start, word-slice->end, "/")
2225     # . . push args
2226     57/push-EDI
2227     68/push  0x2f/imm32/slash
2228     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
2229     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
2230     # . . call
2231     e8/call  next-token-from-slice/disp32
2232     # . . discard args
2233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2234 +-- 33 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2267     # info/ESI = get-slice(labels, datum, row-size=16)
2268     # . EAX = get-slice(labels, datum, row-size=16)
2269     # . . push args
2270     68/push  0x10/imm32/row-size
2271     57/push-EDI
2272     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
2273     # . . call
2274     e8/call  get-slice/disp32
2275     # . . discard args
2276     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2277     # . ESI = EAX
2278     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
2279 $emit-segments:check-global-variable:
2280 +-- 26 lines: #?     # dump info->segment-name ---------------------------------------------------------------------------------------------------------------
2306     # if string-equal?(info->segment-name, "code") goto code label checks
2307     # . EAX = string-equal?(info->segment-name, "code")
2308     # . . push args
2309     68/push  "code"/imm32
2310     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
2311     # . . call
2312     e8/call  string-equal?/disp32
2313     # . . discard args
2314     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2315     # . if (EAX != 0) goto code label checks
2316     3d/compare-EAX-and  0/imm32
2317     0f 85/jump-if-not-equal  $emit-segments:check-code-label-for-imm8/disp32
2318 $emit-segments:check-global-variable-for-disp8:
2319     # if has-metadata?(word-slice, "disp8") abort
2320     # . EAX = has-metadata?(word-slice, "disp8")
2321     # . . push args
2322     68/push  "disp8"/imm32
2323     52/push-EDX
2324     # . . call
2325     e8/call  has-metadata?/disp32
2326     # . . discard args
2327     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2328     # . if (EAX != 0) abort
2329     3d/compare-EAX-and  0/imm32
2330     0f 85/jump-if-not-equal  $emit-segments:global-variable-abort/disp32
2331 $emit-segments:check-global-variable-for-imm8:
2332     # if has-metadata?(word-slice, "imm8") abort
2333     # . EAX = has-metadata?(word-slice, "imm8")
2334     # . . push args
2335     68/push  "imm8"/imm32
2336     52/push-EDX
2337     # . . call
2338     e8/call  has-metadata?/disp32
2339     # . . discard args
2340     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2341     # . if (EAX != 0) abort
2342     3d/compare-EAX-and  0/imm32
2343     0f 85/jump-if-not-equal  $emit-segments:global-variable-abort/disp32
2344 $emit-segments:emit-global-variable:
2345     # emit-hex(out, info->address, 4)
2346     # . . push args
2347     68/push  4/imm32
2348     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           8/disp8         .                 # push *(ESI+8)
2349     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2350     # . . call
2351     e8/call  emit-hex/disp32
2352     # . . discard args
2353     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2354     # continue
2355     e9/jump  $emit-segments:word-loop/disp32
2356 $emit-segments:check-code-label-for-imm8:
2357     # if (has-metadata?(word-slice, "imm8")) abort
2358     # . EAX = has-metadata?(EDX, "imm8")
2359     # . . push args
2360     68/push  "imm8"/imm32
2361     52/push-EDX
2362     # . . call
2363     e8/call  has-metadata?/disp32
2364     # . . discard args
2365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2366     # . if (EAX != 0) abort
2367     3d/compare-EAX-and  0/imm32
2368     0f 85/jump-if-not-equal  $emit-segments:imm8-abort/disp32
2369 $emit-segments:check-code-label-for-imm32:
2370     # if (!has-metadata?(word-slice, "imm32")) goto next check
2371     # . EAX = has-metadata?(EDX, "imm32")
2372     # . . push args
2373     68/push  "imm32"/imm32
2374     52/push-EDX
2375     # . . call
2376     e8/call  has-metadata?/disp32
2377     # . . discard args
2378     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2379     # . if (EAX == 0) goto next check
2380     3d/compare-EAX-and  0/imm32
2381     74/jump-if-equal  $emit-segments:check-code-label-for-disp8/disp8
2382 +-- 33 lines: #?     # dump info->address --------------------------------------------------------------------------------------------------------------------
2415 $emit-segments:emit-code-label-imm32:
2416     # emit-hex(out, info->address, 4)
2417     # . . push args
2418     68/push  4/imm32
2419     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           8/disp8         .                 # push *(ESI+8)
2420     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2421     # . . call
2422     e8/call  emit-hex/disp32
2423     # . . discard args
2424     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2425     # continue
2426     e9/jump  $emit-segments:word-loop/disp32
2427 $emit-segments:check-code-label-for-disp8:
2428     # if (!has-metadata?(word-slice, "disp8")) goto next check
2429     # . EAX = has-metadata?(EDX, "disp8")
2430     # . . push args
2431     68/push  "disp8"/imm32
2432     52/push-EDX
2433     # . . call
2434     e8/call  has-metadata?/disp32
2435     # . . discard args
2436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2437     # . if (EAX == 0) goto next check
2438     3d/compare-EAX-and  0/imm32
2439     74/jump-if-equal  $emit-segments:check-code-label-for-disp32/disp8
2440 $emit-segments:emit-code-label-disp8:
2441     # emit-hex(out, info->offset - offset-of-next-instruction, 1)
2442     # . . push args
2443     68/push  1/imm32
2444     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
2445     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # subtract EBX from EAX
2446     50/push-EAX
2447     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2448     # . . call
2449     e8/call  emit-hex/disp32
2450     # . . discard args
2451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2452     # continue
2453     e9/jump  $emit-segments:word-loop/disp32
2454 $emit-segments:check-code-label-for-disp32:
2455     # if (!has-metadata?(word-slice, "disp32")) abort
2456     # . EAX = has-metadata?(EDX, "disp32")
2457     # . . push args
2458     68/push  "disp32"/imm32
2459     52/push-EDX
2460     # . . call
2461     e8/call  has-metadata?/disp32
2462     # . . discard args
2463     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2464     # . if (EAX == 0) abort
2465     3d/compare-EAX-and  0/imm32
2466     0f 84/jump-if-equal  $emit-segments:abort/disp32
2467 $emit-segments:emit-code-label-disp32:
2468     # emit-hex(out, info->offset - offset-of-next-instruction, 4)
2469     # . . push args
2470     68/push  4/imm32
2471     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
2472     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           3/r32/EBX   .               .                 # subtract EBX from EAX
2473     50/push-EAX
2474     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2475     # . . call
2476     e8/call  emit-hex/disp32
2477     # . . discard args
2478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2479     # continue
2480     e9/jump  $emit-segments:word-loop/disp32
2481 $emit-segments:next-line:
2482     # write-buffered(out, "\n")
2483     # . . push args
2484     68/push  Newline/imm32
2485     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2486     # . . call
2487     e8/call  write-buffered/disp32
2488     # . . discard args
2489     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2490     # loop
2491     e9/jump  $emit-segments:line-loop/disp32
2492 $emit-segments:end:
2493     # . reclaim locals
2494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x21c/imm32       # add to ESP
2495     # . restore registers
2496     5f/pop-to-EDI
2497     5e/pop-to-ESI
2498     5b/pop-to-EBX
2499     5a/pop-to-EDX
2500     59/pop-to-ECX
2501     58/pop-to-EAX
2502     # . epilog
2503     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2504     5d/pop-to-EBP
2505     c3/return
2506 
2507 $emit-segments:global-variable-abort:
2508     # . _write(2/stderr, error)
2509     # . . push args
2510     68/push  "emit-segments: must refer to global variables with /disp32 or /imm32"/imm32
2511     68/push  2/imm32/stderr
2512     # . . call
2513     e8/call  _write/disp32
2514     # . . discard args
2515     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2516     # . syscall(exit, 1)
2517     bb/copy-to-EBX  1/imm32
2518     b8/copy-to-EAX  1/imm32/exit
2519     cd/syscall  0x80/imm8
2520     # never gets here
2521 
2522 $emit-segments:imm8-abort:
2523     # . _write(2/stderr, error)
2524     # . . push args
2525     68/push  "emit-segments: cannot refer to code labels with /imm8"/imm32
2526     68/push  2/imm32/stderr
2527     # . . call
2528     e8/call  _write/disp32
2529     # . . discard args
2530     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2531     # . syscall(exit, 1)
2532     bb/copy-to-EBX  1/imm32
2533     b8/copy-to-EAX  1/imm32/exit
2534     cd/syscall  0x80/imm8
2535     # never gets here
2536 
2537 $emit-segments:abort:
2538     # print(stderr, "missing metadata in " word-slice)
2539     # . _write(2/stderr, "missing metadata in word ")
2540     # . . push args
2541     68/push  "emit-segments: missing metadata in "/imm32
2542     68/push  2/imm32/stderr
2543     # . . call
2544     e8/call  _write/disp32
2545     # . . discard args
2546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2547     # . write-slice-buffered(Stderr, word-slice)
2548     # . . push args
2549     52/push-EDX
2550     68/push  Stderr/imm32
2551     # . . call
2552     e8/call  write-slice-buffered/disp32
2553     # . . discard args
2554     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2555     # . flush(Stderr)
2556     # . . push args
2557     68/push  Stderr/imm32
2558     # . . call
2559     e8/call  flush/disp32
2560     # . . discard args
2561     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2562     # . syscall(exit, 1)
2563     bb/copy-to-EBX  1/imm32
2564     b8/copy-to-EAX  1/imm32/exit
2565     cd/syscall  0x80/imm8
2566     # never gets here
2567 
2568 test-emit-segments-global-variable:
2569     # global variables always convert to absolute addresses, regardless of metadata
2570     #
2571     # input:
2572     #   in:
2573     #     == code 0x1000
2574     #     ab cd ef gh
2575     #     ij x/disp32
2576     #     == data 0x2000
2577     #     00
2578     #     x:
2579     #       34
2580     #   segments:
2581     #     - 'code': {0x1074, 0, 9}
2582     #     - 'data': {0x2079, 5, 2}
2583     #   labels:
2584     #     - 'x': {'data', 1, 0x207a}
2585     #
2586     # output:
2587     #   ab cd ef gh
2588     #   ij 7a 20 00 00
2589     #   00
2590     #   34
2591     #
2592     # . prolog
2593     55/push-EBP
2594     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2595     # setup
2596     # . clear-stream(_test-input-stream)
2597     # . . push args
2598     68/push  _test-input-stream/imm32
2599     # . . call
2600     e8/call  clear-stream/disp32
2601     # . . discard args
2602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2603     # . clear-stream(_test-output-stream)
2604     # . . push args
2605     68/push  _test-output-stream/imm32
2606     # . . call
2607     e8/call  clear-stream/disp32
2608     # . . discard args
2609     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2610     # . clear-stream(_test-output-buffered-file+4)
2611     # . . push args
2612     b8/copy-to-EAX  _test-output-buffered-file/imm32
2613     05/add-to-EAX  4/imm32
2614     50/push-EAX
2615     # . . call
2616     e8/call  clear-stream/disp32
2617     # . . discard args
2618     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2619     # . var segments/ECX = stream(10 * 16)
2620     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
2621     68/push  0xa0/imm32/length
2622     68/push  0/imm32/read
2623     68/push  0/imm32/write
2624     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2625     # . var labels/EDX = stream(512 * 16)
2626     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
2627     68/push  0x2000/imm32/length
2628     68/push  0/imm32/read
2629     68/push  0/imm32/write
2630     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
2631     # initialize input
2632     # . write(_test-input-stream, "== code 0x1000\n")
2633     # . . push args
2634     68/push  "== code 0x1000\n"/imm32
2635     68/push  _test-input-stream/imm32
2636     # . . call
2637     e8/call  write/disp32
2638     # . . discard args
2639     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2640     # . write(_test-input-stream, "ab cd ef gh\n")
2641     # . . push args
2642     68/push  "ab cd ef gh\n"/imm32
2643     68/push  _test-input-stream/imm32
2644     # . . call
2645     e8/call  write/disp32
2646     # . . discard args
2647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2648     # . write(_test-input-stream, "ij x/disp32\n")
2649     # . . push args
2650     68/push  "ij x/disp32\n"/imm32
2651     68/push  _test-input-stream/imm32
2652     # . . call
2653     e8/call  write/disp32
2654     # . . discard args
2655     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2656     # . write(_test-input-stream, "== data 0x2000\n")
2657     # . . push args
2658     68/push  "== data 0x2000\n"/imm32
2659     68/push  _test-input-stream/imm32
2660     # . . call
2661     e8/call  write/disp32
2662     # . . discard args
2663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2664     # . write(_test-input-stream, "00\n")
2665     # . . push args
2666     68/push  "00\n"/imm32
2667     68/push  _test-input-stream/imm32
2668     # . . call
2669     e8/call  write/disp32
2670     # . . discard args
2671     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2672     # . write(_test-input-stream, "x:\n")
2673     # . . push args
2674     68/push  "x:\n"/imm32
2675     68/push  _test-input-stream/imm32
2676     # . . call
2677     e8/call  write/disp32
2678     # . . discard args
2679     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2680     # . write(_test-input-stream, "34\n")
2681     # . . push args
2682     68/push  "34\n"/imm32
2683     68/push  _test-input-stream/imm32
2684     # . . call
2685     e8/call  write/disp32
2686     # . . discard args
2687     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2688     # . stream-add4(segments, "code", 0x1074, 0, 9)
2689     68/push  9/imm32/segment-size
2690     68/push  0/imm32/file-offset
2691     68/push  0x1074/imm32/start-address
2692     68/push  "code"/imm32/segment-name
2693     51/push-ECX
2694     # . . call
2695     e8/call  stream-add4/disp32
2696     # . . discard args
2697     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2698     # . stream-add4(segments, "data", 0x2079, 5, 2)
2699     68/push  1/imm32/segment-size
2700     68/push  5/imm32/file-offset
2701     68/push  0x2079/imm32/start-address
2702     68/push  "data"/imm32/segment-name
2703     51/push-ECX
2704     # . . call
2705     e8/call  stream-add4/disp32
2706     # . . discard args
2707     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2708     # . stream-add4(labels, "x", "data", 1, 0x207a)
2709     68/push  0x207a/imm32/label-address
2710     68/push  1/imm32/segment-offset
2711     68/push  "data"/imm32/segment-name
2712     68/push  "x"/imm32/label-name
2713     52/push-EDX
2714     # . . call
2715     e8/call  stream-add4/disp32
2716     # . . discard args
2717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2718     # component under test
2719     # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels)
2720     # . . push args
2721     52/push-EDX
2722     51/push-ECX
2723     68/push  _test-output-buffered-file/imm32
2724     68/push  _test-input-stream/imm32
2725     # . . call
2726     e8/call  emit-segments/disp32
2727     # . . discard args
2728     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2729     # checks
2730     # . flush(_test-output-buffered-file)
2731     # . . push args
2732     68/push  _test-output-buffered-file/imm32
2733     # . . call
2734     e8/call  flush/disp32
2735     # . . discard args
2736     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2737 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
2770     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
2771     # . . push args
2772     68/push  "F - test-emit-segments-global-variable/0"/imm32
2773     68/push  "ab cd ef gh "/imm32
2774     68/push  _test-output-stream/imm32
2775     # . . call
2776     e8/call  check-next-stream-line-equal/disp32
2777     # . . discard args
2778     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2779     # . check-next-stream-line-equal(_test-output-stream, "ij 7a 20 00 00 ", msg)
2780     # . . push args
2781     68/push  "F - test-emit-segments-global-variable/1"/imm32
2782     68/push  "ij 7a 20 00 00 "/imm32
2783     68/push  _test-output-stream/imm32
2784     # . . call
2785     e8/call  check-next-stream-line-equal/disp32
2786     # . . discard args
2787     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2788     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
2789     # . . push args
2790     68/push  "F - test-emit-segments-global-variable/2"/imm32
2791     68/push  "00 "/imm32
2792     68/push  _test-output-stream/imm32
2793     # . . call
2794     e8/call  check-next-stream-line-equal/disp32
2795     # . . discard args
2796     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2797     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
2798     # . . push args
2799     68/push  "F - test-emit-segments-global-variable/3"/imm32
2800     68/push  "34 "/imm32
2801     68/push  _test-output-stream/imm32
2802     # . . call
2803     e8/call  check-next-stream-line-equal/disp32
2804     # . . discard args
2805     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2806     # . epilog
2807     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2808     5d/pop-to-EBP
2809     c3/return
2810 
2811 test-emit-segments-code-label:
2812     # labels usually convert to displacements
2813     #
2814     # input:
2815     #   in:
2816     #     == code 0x1000
2817     #     ab cd
2818     #     l1:
2819     #       ef gh
2820     #       ij l1/disp32
2821     #   segments:
2822     #     - 'code': {0x1054, 0, 9}
2823     #   labels:
2824     #     - 'l1': {'code', 2, 0x1056}
2825     #
2826     # output:
2827     #   ab cd
2828     #   ef gh
2829     #   ij f9 ff ff ff  # -7
2830     #
2831     # . prolog
2832     55/push-EBP
2833     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2834     # setup
2835     # . clear-stream(_test-input-stream)
2836     # . . push args
2837     68/push  _test-input-stream/imm32
2838     # . . call
2839     e8/call  clear-stream/disp32
2840     # . . discard args
2841     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2842     # . clear-stream(_test-output-stream)
2843     # . . push args
2844     68/push  _test-output-stream/imm32
2845     # . . call
2846     e8/call  clear-stream/disp32
2847     # . . discard args
2848     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2849     # . clear-stream(_test-output-buffered-file+4)
2850     # . . push args
2851     b8/copy-to-EAX  _test-output-buffered-file/imm32
2852     05/add-to-EAX  4/imm32
2853     50/push-EAX
2854     # . . call
2855     e8/call  clear-stream/disp32
2856     # . . discard args
2857     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2858     # . var segments/ECX = stream(10 * 16)
2859     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
2860     68/push  0xa0/imm32/length
2861     68/push  0/imm32/read
2862     68/push  0/imm32/write
2863     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2864     # . var labels/EDX = stream(512 * 16)
2865     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
2866     68/push  0x2000/imm32/length
2867     68/push  0/imm32/read
2868     68/push  0/imm32/write
2869     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
2870     # initialize input
2871     # . write(_test-input-stream, "== code 0x1000\n")
2872     # . . push args
2873     68/push  "== code 0x1000\n"/imm32
2874     68/push  _test-input-stream/imm32
2875     # . . call
2876     e8/call  write/disp32
2877     # . . discard args
2878     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2879     # . write(_test-input-stream, "ab cd\n")
2880     # . . push args
2881     68/push  "ab cd\n"/imm32
2882     68/push  _test-input-stream/imm32
2883     # . . call
2884     e8/call  write/disp32
2885     # . . discard args
2886     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2887     # . write(_test-input-stream, "l1:\n")
2888     # . . push args
2889     68/push  "l1:\n"/imm32
2890     68/push  _test-input-stream/imm32
2891     # . . call
2892     e8/call  write/disp32
2893     # . . discard args
2894     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2895     # . write(_test-input-stream, "  ef gh\n")
2896     # . . push args
2897     68/push  "  ef gh\n"/imm32
2898     68/push  _test-input-stream/imm32
2899     # . . call
2900     e8/call  write/disp32
2901     # . . discard args
2902     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2903     # . write(_test-input-stream, "  ij l1/disp32\n")
2904     # . . push args
2905     68/push  "  ij l1/disp32\n"/imm32
2906     68/push  _test-input-stream/imm32
2907     # . . call
2908     e8/call  write/disp32
2909     # . . discard args
2910     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2911     # . stream-add4(segments, "code", 0x1054, 0, 9)
2912     68/push  9/imm32/segment-size
2913     68/push  0/imm32/file-offset
2914     68/push  0x1054/imm32/start-address
2915     68/push  "code"/imm32/segment-name
2916     51/push-ECX
2917     # . . call
2918     e8/call  stream-add4/disp32
2919     # . . discard args
2920     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2921     # . stream-add4(labels, "l1", "code", 2, 0x1056)
2922     68/push  0x1056/imm32/label-address
2923     68/push  2/imm32/segment-offset
2924     68/push  "code"/imm32/segment-name
2925     68/push  "l1"/imm32/label-name
2926     52/push-EDX
2927     # . . call
2928     e8/call  stream-add4/disp32
2929     # . . discard args
2930     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
2931     # component under test
2932     # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels)
2933     # . . push args
2934     52/push-EDX
2935     51/push-ECX
2936     68/push  _test-output-buffered-file/imm32
2937     68/push  _test-input-stream/imm32
2938     # . . call
2939     e8/call  emit-segments/disp32
2940     # . . discard args
2941     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2942     # checks
2943     # . flush(_test-output-buffered-file)
2944     # . . push args
2945     68/push  _test-output-buffered-file/imm32
2946     # . . call
2947     e8/call  flush/disp32
2948     # . . discard args
2949     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2950 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
2983     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
2984     # . . push args
2985     68/push  "F - test-emit-segments-code-label/0"/imm32
2986     68/push  "ab cd "/imm32
2987     68/push  _test-output-stream/imm32
2988     # . . call
2989     e8/call  check-next-stream-line-equal/disp32
2990     # . . discard args
2991     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2992     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
2993     # . . push args
2994     68/push  "F - test-emit-segments-code-label/1"/imm32
2995     68/push  "ef gh "/imm32
2996     68/push  _test-output-stream/imm32
2997     # . . call
2998     e8/call  check-next-stream-line-equal/disp32
2999     # . . discard args
3000     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3001     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
3002     # . . push args
3003     68/push  "F - test-emit-segments-code-label/2"/imm32
3004     68/push  "ij f9 ff ff ff "/imm32
3005     68/push  _test-output-stream/imm32
3006     # . . call
3007     e8/call  check-next-stream-line-equal/disp32
3008     # . . discard args
3009     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3010     # . epilog
3011     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3012     5d/pop-to-EBP
3013     c3/return
3014 
3015 test-emit-segments-code-label-absolute:
3016     # labels can also convert to absolute addresses
3017     #
3018     # input:
3019     #   in:
3020     #     == code 0x1000
3021     #     ab cd
3022     #     l1:
3023     #       ef gh
3024     #       ij l1/imm32
3025     #   segments:
3026     #     - 'code': {0x1054, 0, 9}
3027     #   labels:
3028     #     - 'l1': {'code', 2, 0x1056}
3029     #
3030     # output:
3031     #   ab cd
3032     #   ef gh
3033     #   ij 56 10 00 00
3034     #
3035     # . prolog
3036     55/push-EBP
3037     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3038     # setup
3039     # . clear-stream(_test-input-stream)
3040     # . . push args
3041     68/push  _test-input-stream/imm32
3042     # . . call
3043     e8/call  clear-stream/disp32
3044     # . . discard args
3045     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3046     # . clear-stream(_test-output-stream)
3047     # . . push args
3048     68/push  _test-output-stream/imm32
3049     # . . call
3050     e8/call  clear-stream/disp32
3051     # . . discard args
3052     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3053     # . clear-stream(_test-output-buffered-file+4)
3054     # . . push args
3055     b8/copy-to-EAX  _test-output-buffered-file/imm32
3056     05/add-to-EAX  4/imm32
3057     50/push-EAX
3058     # . . call
3059     e8/call  clear-stream/disp32
3060     # . . discard args
3061     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3062     # . var segments/ECX = stream(10 * 16)
3063     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa0/imm32        # subtract from ESP
3064     68/push  0xa0/imm32/length
3065     68/push  0/imm32/read
3066     68/push  0/imm32/write
3067     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
3068     # . var labels/EDX = stream(512 * 16)
3069     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x2000/imm32      # subtract from ESP
3070     68/push  0x2000/imm32/length
3071     68/push  0/imm32/read
3072     68/push  0/imm32/write
3073     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
3074     # initialize input
3075     # . write(_test-input-stream, "== code 0x1000\n")
3076     # . . push args
3077     68/push  "== code 0x1000\n"/imm32
3078     68/push  _test-input-stream/imm32
3079     # . . call
3080     e8/call  write/disp32
3081     # . . discard args
3082     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3083     # . write(_test-input-stream, "ab cd\n")
3084     # . . push args
3085     68/push  "ab cd\n"/imm32
3086     68/push  _test-input-stream/imm32
3087     # . . call
3088     e8/call  write/disp32
3089     # . . discard args
3090     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3091     # . write(_test-input-stream, "l1:\n")
3092     # . . push args
3093     68/push  "l1:\n"/imm32
3094     68/push  _test-input-stream/imm32
3095     # . . call
3096     e8/call  write/disp32
3097     # . . discard args
3098     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3099     # . write(_test-input-stream, "  ef gh\n")
3100     # . . push args
3101     68/push  "  ef gh\n"/imm32
3102     68/push  _test-input-stream/imm32
3103     # . . call
3104     e8/call  write/disp32
3105     # . . discard args
3106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3107     # . write(_test-input-stream, "  ij l1/imm32\n")
3108     # . . push args
3109     68/push  "  ij l1/imm32\n"/imm32
3110     68/push  _test-input-stream/imm32
3111     # . . call
3112     e8/call  write/disp32
3113     # . . discard args
3114     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3115     # . stream-add4(segments, "code", 0x1054, 0, 9)
3116     68/push  9/imm32/segment-size
3117     68/push  0/imm32/file-offset
3118     68/push  0x1054/imm32/start-address
3119     68/push  "code"/imm32/segment-name
3120     51/push-ECX
3121     # . . call
3122     e8/call  stream-add4/disp32
3123     # . . discard args
3124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
3125     # . stream-add4(labels, "l1", "code", 2, 0x1056)
3126     68/push  0x1056/imm32/label-address
3127     68/push  2/imm32/segment-offset
3128     68/push  "code"/imm32/segment-name
3129     68/push  "l1"/imm32/label-name
3130     52/push-EDX
3131     # . . call
3132     e8/call  stream-add4/disp32
3133     # . . discard args
3134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
3135     # component under test
3136     # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels)
3137     # . . push args
3138     52/push-EDX
3139     51/push-ECX
3140     68/push  _test-output-buffered-file/imm32
3141     68/push  _test-input-stream/imm32
3142     # . . call
3143     e8/call  emit-segments/disp32
3144     # . . discard args
3145     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
3146     # checks
3147     # . flush(_test-output-buffered-file)
3148     # . . push args
3149     68/push  _test-output-buffered-file/imm32
3150     # . . call
3151     e8/call  flush/disp32
3152     # . . discard args
3153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3154 +-- 33 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3187     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
3188     # . . push args
3189     68/push  "F - test-emit-segments-code-label-absolute/0"/imm32
3190     68/push  "ab cd "/imm32
3191     68/push  _test-output-stream/imm32
3192     # . . call
3193     e8/call  check-next-stream-line-equal/disp32
3194     # . . discard args
3195     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3196     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
3197     # . . push args
3198     68/push  "F - test-emit-segments-code-label-absolute/1"/imm32
3199     68/push  "ef gh "/imm32
3200     68/push  _test-output-stream/imm32
3201     # . . call
3202     e8/call  check-next-stream-line-equal/disp32
3203     # . . discard args
3204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3205     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
3206     # . . push args
3207     68/push  "F - test-emit-segments-code-label-absolute/2"/imm32
3208     68/push  "ij 56 10 00 00 "/imm32
3209     68/push  _test-output-stream/imm32
3210     # . . call
3211     e8/call  check-next-stream-line-equal/disp32
3212     # . . discard args
3213     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3214     # . epilog
3215     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3216     5d/pop-to-EBP
3217     c3/return
3218 
3219 emit-headers:  # out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
3220     # pseudocode:
3221     #   emit-elf-header(out, segments, labels)
3222     #   curr-segment = segments->data
3223     #   max = segments->data + segments->write
3224     #   while true
3225     #     if (curr-segment >= max) break
3226     #     emit-elf-program-header-entry(out, curr-segment)
3227     #     curr-segment += 16                        # size of a row
3228     #
3229     # . prolog
3230     55/push-EBP
3231     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3232     # . save registers
3233     50/push-EAX
3234     51/push-ECX
3235 +--  9 lines: #?     # write(2/stderr, "emit-elf-header\n") --------------------------------------------------------------------------------------------------
3244     # emit-elf-header(out, segments, labels)
3245     # . . push args
3246     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
3247     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3248     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3249     # . . call
3250     e8/call  emit-elf-header/disp32
3251     # . . discard args
3252     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3253     # EAX = segments
3254     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
3255     # ECX = segments->write
3256     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
3257     # curr-segment/EAX = segments->data
3258     8d/copy-address                 1/mod/*+disp8   0/rm32/EAX    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy EAX+12 to EAX
3259     # max/ECX = segments->data + segments->write
3260     01/add                          3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # add EAX to ECX
3261 $emit-headers:loop:
3262     # if (curr-segment >= max) break
3263     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # compare EAX with ECX
3264     0f 83/jump-if-greater-or-equal-unsigned  $emit-headers:end/disp32
3265 +-- 69 lines: #?     # dump curr-segment->name ---------------------------------------------------------------------------------------------------------------
3334 +--  9 lines: #?     # write(2/stderr, "emit-segment-header\n") ----------------------------------------------------------------------------------------------
3343     # emit-elf-program-header-entry(out, curr-segment)
3344     # . . push args
3345     50/push-EAX
3346     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3347     # . . call
3348     e8/call  emit-elf-program-header-entry/disp32
3349     # . . discard args
3350     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3351     # curr-segment += 16                        # size of a row
3352     81          0/subop/add         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0x10/imm32        # add to EAX
3353     e9/jump  $emit-headers:loop/disp32
3354 $emit-headers:end:
3355     # . restore registers
3356     59/pop-to-ECX
3357     58/pop-to-EAX
3358     # . epilog
3359     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3360     5d/pop-to-EBP
3361     c3/return
3362 
3363 emit-elf-header:  # out : (address buffered-file), segments : (address stream {string, segment-info}), labels : (address stream {string, label-info})
3364     # pseudocode
3365     #   *Elf_e_entry = get(labels, "Entry")->address
3366     #   *Elf_e_phnum = segments->write / 16         # size of a row
3367     #   emit-hex-array(out, Elf_header)
3368     #   write-buffered(out, "\n")
3369     #
3370     # . prolog
3371     55/push-EBP
3372     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3373     # . save registers
3374     50/push-EAX
3375     51/push-ECX
3376     52/push-EDX  # just because we need to call idiv
3377     # *Elf_e_entry = get(labels, "Entry")->address
3378     # . EAX = labels
3379     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0x10/disp8      .                 # copy *(EBP+16) to EAX
3380     # . label-info/EAX = get(labels, "Entry", row-size=16)
3381     # . . push args
3382     68/push  0x10/imm32/row-size
3383     68/push  "Entry"/imm32
3384     50/push-EAX
3385     # . . call
3386     e8/call  get/disp32
3387     # . . discard args
3388     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3389     # . EAX = label-info->address
3390     8b/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EAX+8) to EAX
3391     # . *Elf_e_entry = EAX
3392     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_e_entry/disp32                # copy EAX to *Elf_e_entry
3393     # *Elf_e_phnum = segments->write / 0x10
3394     # . EAX = segments
3395     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EAX
3396     # . len/EAX = segments->write
3397     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # copy *EAX to EAX
3398     # . EAX = len / 0x10  (destroying EDX)
3399     b9/copy-to-ECX  0x10/imm32
3400     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
3401     f7          7/subop/idiv        3/mod/direct    1/rm32/ECX    .           .             .           .           .               .                 # divide EDX:EAX by ECX, storing quotient in EAX and remainder in EDX
3402     # . *Elf_e_phnum = EAX
3403     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_e_phnum/disp32                # copy EAX to *Elf_e_phnum
3404     # emit-hex-array(out, Elf_header)
3405     # . . push args
3406     68/push  Elf_header/imm32
3407     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3408     # . . call
3409     e8/call  emit-hex-array/disp32
3410     # . . discard args
3411     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3412     # write-buffered(out, "\n")
3413     # . . push args
3414     68/push  "\n"/imm32
3415     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3416     # . . call
3417     e8/call  write-buffered/disp32
3418     # . . discard args
3419     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3420 $emit-elf-header:end:
3421     # . restore registers
3422     5a/pop-to-EDX
3423     59/pop-to-ECX
3424     58/pop-to-EAX
3425     # . epilog
3426     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3427     5d/pop-to-EBP
3428     c3/return
3429 
3430 emit-elf-program-header-entry:  # out : (address buffered-file), curr-segment : (address {string, segment-info})
3431     # pseudocode:
3432     #   *Elf_p_offset = curr-segment->file-offset
3433     #   *Elf_p_vaddr = curr-segment->address
3434     #   *Elf_p_paddr = curr-segment->address
3435     #   *Elf_p_filesz = curr-segment->size
3436     #   *Elf_p_memsz = curr-segment->size
3437     #   if curr-segment->name == "code"
3438     #     *Elf_p_flags = 5  # r-x
3439     #   else
3440     #     *Elf_p_flags = 6  # rw-
3441     #   emit-hex-array(out, Elf_program_header_entry)
3442     #   write-buffered(out, "\n")
3443     #
3444     # . prolog
3445     55/push-EBP
3446     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3447     # . save registers
3448     50/push-EAX
3449     56/push-ESI
3450     # ESI = curr-segment
3451     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
3452     # *Elf_p_offset = curr-segment->file-offset
3453     # . EAX = curr-segment->file-offset
3454     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(ESI+8) to EAX
3455     # . *Elf_p_offset = EAX
3456     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_offset/disp32               # copy EAX to *Elf_p_offset
3457     # *Elf_p_vaddr = curr-segment->address
3458     # . EAX = curr-segment->address
3459     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
3460     # . *Elf_p_vaddr = EAX
3461     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_vaddr/disp32                # copy EAX to *Elf_p_vaddr
3462     # *Elf_p_paddr = curr-segment->address
3463     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_paddr/disp32                # copy EAX to *Elf_p_paddr
3464     # *Elf_p_filesz = curr-segment->size
3465     # . EAX = curr-segment->size
3466     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(ESI+12) to EAX
3467     # . *Elf_p_filesz = EAX
3468     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_filesz/disp32               # copy EAX to *Elf_p_filesz
3469     # *Elf_p_memsz = curr-segment->size
3470     89/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Elf_p_memsz/disp32                # copy EAX to *Elf_p_memsz
3471     # if (!string-equal?(curr-segment->name, "code") goto next check
3472     # . EAX = curr-segment->name
3473     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
3474     # . EAX = string-equal?(curr-segment->name, "code")
3475     # . . push args
3476     68/push  "code"/imm32
3477     50/push-EAX
3478     # . . call
3479     e8/call  string-equal?/disp32
3480     # . . discard args
3481     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3482     # . if (EAX == 0) goto next check
3483     3d/compare-EAX-and  0/imm32
3484     74/jump-if-equal  $emit-elf-program-header-entry:data/disp8
3485     # *Elf_p_flags = r-x
3486     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           Elf_p_flags/disp32  5/imm32       # copy to *Elf_p_flags
3487     eb/jump  $emit-elf-program-header-entry:really-emit/disp8
3488 $emit-elf-program-header-entry:data:
3489     # otherwise *Elf_p_flags = rw-
3490     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           Elf_p_flags/disp32  6/imm32       # copy to *Elf_p_flags
3491 $emit-elf-program-header-entry:really-emit:
3492     # emit-hex-array(out, Elf_program_header_entry)
3493     # . . push args
3494     68/push  Elf_program_header_entry/imm32
3495     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3496     # . . call
3497     e8/call  emit-hex-array/disp32
3498     # . . discard args
3499     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3500     # write-buffered(out, "\n")
3501     # . . push args
3502     68/push  "\n"/imm32
3503     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3504     # . . call
3505     e8/call  write-buffered/disp32
3506     # . . discard args
3507     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3508 $emit-elf-program-header-entry:end:
3509     # . restore registers
3510     5e/pop-to-ESI
3511     58/pop-to-EAX
3512     # . epilog
3513     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3514     5d/pop-to-EBP
3515     c3/return
3516 
3517 # - some helpers for tests
3518 
3519 stream-add4:  # in : (address stream byte), key : address, val1 : address, val2 : address, val3 : address
3520     # . prolog
3521     55/push-EBP
3522     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3523     # . save registers
3524     50/push-EAX
3525     51/push-ECX
3526     52/push-EDX
3527     56/push-ESI
3528     # ESI = in
3529     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
3530     # curr/EAX = in->data + in->write
3531     # . EAX = in->write
3532     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
3533     # . EAX = ESI+EAX+12
3534     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
3535     # max/EDX = in->data + in->length
3536     # . EDX = in->length
3537     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   8/disp8         .                 # copy *(ESI+8) to EDX
3538     # . EDX = ESI+EDX+12
3539     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
3540     # if (curr >= max) abort
3541     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
3542     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
3543     # *curr = key
3544     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0xc/disp8       .                 # copy *(EBP+12) to ECX
3545     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
3546     # curr += 4
3547     05/add-to-EAX  4/imm32
3548     # if (curr >= max) abort
3549     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
3550     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
3551     # *curr = val1
3552     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x10/disp8      .                 # copy *(EBP+16) to ECX
3553     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
3554     # curr += 4
3555     05/add-to-EAX  4/imm32
3556     # if (curr >= max) abort
3557     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
3558     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
3559     # *curr = val2
3560     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x14/disp8      .                 # copy *(EBP+20) to ECX
3561     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
3562     # curr += 4
3563     05/add-to-EAX  4/imm32
3564     # if (curr >= max) abort
3565     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX with EDX
3566     73/jump-if-greater-or-equal-unsigned  $stream-add4:abort/disp8
3567     # *curr = val3
3568     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   0x18/disp8      .                 # copy *(EBP+24) to ECX
3569     89/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to *EAX
3570     # in->write += 16
3571     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               0x10/imm32        # add to *ESI
3572 $stream-add4:end:
3573     # . restore registers
3574     5e/pop-to-ESI
3575     5a/pop-to-EDX
3576     59/pop-to-ECX
3577     58/pop-to-EAX
3578     # . epilog
3579     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3580     5d/pop-to-EBP
3581     c3/return
3582 
3583 $stream-add4:abort:
3584     # . _write(2/stderr, error)
3585     # . . push args
3586     68/push  "overflow in stream-add4\n"/imm32
3587     68/push  2/imm32/stderr
3588     # . . call
3589     e8/call  _write/disp32
3590     # . . discard args
3591     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3592     # . syscall(exit, 1)
3593     bb/copy-to-EBX  1/imm32
3594     b8/copy-to-EAX  1/imm32/exit
3595     cd/syscall  0x80/imm8
3596     # never gets here
3597 
3598 # some variants of 'trace' that take multiple arguments in different combinations of types:
3599 #   n: int
3600 #   c: character [4-bytes, will eventually be UTF-8]
3601 #   s: (address string)
3602 #   l: (address slice)
3603 # one gotcha: 's5' must not be empty
3604 
3605 trace-sssns:  # s1 : (address string), s2 : (address string), s3 : (address string), n4 : int, s5 : (address string)
3606     # . prolog
3607     55/push-EBP
3608     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3609     # write(*Trace-stream, s1)
3610     # . . push args
3611     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3612     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3613     # . . call
3614     e8/call  write/disp32
3615     # . . discard args
3616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3617     # write(*Trace-stream, s2)
3618     # . . push args
3619     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3620     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3621     # . . call
3622     e8/call  write/disp32
3623     # . . discard args
3624     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3625     # write(*Trace-stream, s3)
3626     # . . push args
3627     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
3628     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3629     # . . call
3630     e8/call  write/disp32
3631     # . . discard args
3632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3633     # print-int32(*Trace-stream, n4)
3634     # . . push args
3635     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
3636     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3637     # . . call
3638     e8/call  print-int32/disp32
3639     # . . discard args
3640     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3641     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3642     # . . push args
3643     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
3644     # . . call
3645     e8/call  trace/disp32
3646     # . . discard args
3647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3648 $trace-sssns:end:
3649     # . epilog
3650     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3651     5d/pop-to-EBP
3652     c3/return
3653 
3654 test-trace-sssns:
3655     # . prolog
3656     55/push-EBP
3657     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3658     # setup
3659     # . *Trace-stream->write = 0
3660     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
3661     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
3662     # trace-sssns("A" "b" "c " 3 " e")
3663     # . . push args
3664     68/push  " e"/imm32
3665     68/push  3/imm32
3666     68/push  "c "/imm32
3667     68/push  "b"/imm32
3668     68/push  "A"/imm32
3669     # . . call
3670     e8/call  trace-sssns/disp32
3671     # . . discard args
3672     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
3673 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3699     # check-trace-contains("Abc 0x00000003 e")
3700     # . . push args
3701     68/push  "F - test-trace-sssns"/imm32
3702     68/push  "Abc 0x00000003 e"/imm32
3703     # . . call
3704     e8/call  check-trace-contains/disp32
3705     # . . discard args
3706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3707     # . epilog
3708     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3709     5d/pop-to-EBP
3710     c3/return
3711 
3712 trace-snsns:  # s1 : (address string), n2 : int, s3 : (address string), n4 : int, s5 : (address string)
3713     # . prolog
3714     55/push-EBP
3715     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3716     # write(*Trace-stream, s1)
3717     # . . push args
3718     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3719     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3720     # . . call
3721     e8/call  write/disp32
3722     # . . discard args
3723     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3724     # print-int32(*Trace-stream, n2)
3725     # . . push args
3726     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3727     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3728     # . . call
3729     e8/call  print-int32/disp32
3730     # . . discard args
3731     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3732     # write(*Trace-stream, s3)
3733     # . . push args
3734     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
3735     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3736     # . . call
3737     e8/call  write/disp32
3738     # . . discard args
3739     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3740     # print-int32(*Trace-stream, n4)
3741     # . . push args
3742     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
3743     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3744     # . . call
3745     e8/call  print-int32/disp32
3746     # . . discard args
3747     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3748     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3749     # . . push args
3750     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
3751     # . . call
3752     e8/call  trace/disp32
3753     # . . discard args
3754     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3755 $trace-snsns:end:
3756     # . epilog
3757     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3758     5d/pop-to-EBP
3759     c3/return
3760 
3761 test-trace-snsns:
3762     # . prolog
3763     55/push-EBP
3764     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3765     # setup
3766     # . *Trace-stream->write = 0
3767     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
3768     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
3769     # trace-snsns("A " 2 " c " 3 " e")
3770     # . . push args
3771     68/push  " e"/imm32
3772     68/push  3/imm32
3773     68/push  " c "/imm32
3774     68/push  2/imm32
3775     68/push  "A "/imm32
3776     # . . call
3777     e8/call  trace-snsns/disp32
3778     # . . discard args
3779     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
3780 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3806     # check-trace-contains("Abc 0x00000003 e")
3807     # . . push args
3808     68/push  "F - test-trace-snsns"/imm32
3809     68/push  "A 0x00000002 c 0x00000003 e"/imm32
3810     # . . call
3811     e8/call  check-trace-contains/disp32
3812     # . . discard args
3813     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3814     # . epilog
3815     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3816     5d/pop-to-EBP
3817     c3/return
3818 
3819 trace-slsls:  # s1 : (address string), l2 : (address slice), s3 : (address string), l4 : (address slice), s5 : (address string)
3820     # . prolog
3821     55/push-EBP
3822     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3823     # write(*Trace-stream, s1)
3824     # . . push args
3825     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3826     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3827     # . . call
3828     e8/call  write/disp32
3829     # . . discard args
3830     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3831     # write-slice(*Trace-stream, l2)
3832     # . . push args
3833     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3834     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3835     # . . call
3836     e8/call  write-slice/disp32
3837     # . . discard args
3838     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3839     # write(*Trace-stream, s3)
3840     # . . push args
3841     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
3842     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3843     # . . call
3844     e8/call  write/disp32
3845     # . . discard args
3846     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3847     # write-slice(*Trace-stream, l4)
3848     # . . push args
3849     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
3850     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3851     # . . call
3852     e8/call  write-slice/disp32
3853     # . . discard args
3854     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3855     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3856     # . . push args
3857     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
3858     # . . call
3859     e8/call  trace/disp32
3860     # . . discard args
3861     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3862 $trace-slsls:end:
3863     # . epilog
3864     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3865     5d/pop-to-EBP
3866     c3/return
3867 
3868 test-trace-slsls:
3869     # . prolog
3870     55/push-EBP
3871     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3872     # setup
3873     # . *Trace-stream->write = 0
3874     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
3875     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
3876     # (EAX..ECX) = "b"
3877     b8/copy-to-EAX  "b"/imm32
3878     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
3879     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
3880     05/add-to-EAX  4/imm32
3881     # var b/EBX : (address slice) = {EAX, ECX}
3882     51/push-ECX
3883     50/push-EAX
3884     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
3885     # (EAX..ECX) = "d"
3886     b8/copy-to-EAX  "d"/imm32
3887     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
3888     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
3889     05/add-to-EAX  4/imm32
3890     # var d/EDX : (address slice) = {EAX, ECX}
3891     51/push-ECX
3892     50/push-EAX
3893     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
3894     # trace-slsls("A" b "c" d "e")
3895     # . . push args
3896     68/push  "e"/imm32
3897     52/push-EDX
3898     68/push  "c"/imm32
3899     53/push-EBX
3900     68/push  "A"/imm32
3901     # . . call
3902     e8/call  trace-slsls/disp32
3903     # . . discard args
3904     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
3905 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
3931     # check-trace-contains("Abcde")
3932     # . . push args
3933     68/push  "F - test-trace-slsls"/imm32
3934     68/push  "Abcde"/imm32
3935     # . . call
3936     e8/call  check-trace-contains/disp32
3937     # . . discard args
3938     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3939     # . epilog
3940     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3941     5d/pop-to-EBP
3942     c3/return
3943 
3944 trace-slsns:  # s1 : (address string), l2 : (address slice), s3 : (address string), n4 : int, s5 : (address string)
3945     # . prolog
3946     55/push-EBP
3947     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3948     # write(*Trace-stream, s1)
3949     # . . push args
3950     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3951     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3952     # . . call
3953     e8/call  write/disp32
3954     # . . discard args
3955     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3956     # write-slice(*Trace-stream, l2)
3957     # . . push args
3958     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3959     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3960     # . . call
3961     e8/call  write-slice/disp32
3962     # . . discard args
3963     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3964     # write(*Trace-stream, s3)
3965     # . . push args
3966     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
3967     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3968     # . . call
3969     e8/call  write/disp32
3970     # . . discard args
3971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3972     # print-int32(*Trace-stream, n4)
3973     # . . push args
3974     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
3975     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
3976     # . . call
3977     e8/call  print-int32/disp32
3978     # . . discard args
3979     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3980     # trace(s5)  # implicitly adds a newline and finalizes the trace line
3981     # . . push args
3982     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
3983     # . . call
3984     e8/call  trace/disp32
3985     # . . discard args
3986     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3987 $trace-slsns:end:
3988     # . epilog
3989     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3990     5d/pop-to-EBP
3991     c3/return
3992 
3993 test-trace-slsns:
3994     # . prolog
3995     55/push-EBP
3996     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3997     # setup
3998     # . *Trace-stream->write = 0
3999     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
4000     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
4001     # (EAX..ECX) = "b"
4002     b8/copy-to-EAX  "b"/imm32
4003     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
4004     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
4005     05/add-to-EAX  4/imm32
4006     # var b/EBX : (address slice) = {EAX, ECX}
4007     51/push-ECX
4008     50/push-EAX
4009     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
4010     # trace-slsls("A" b "c " 3 " e")
4011     # . . push args
4012     68/push  " e"/imm32
4013     68/push  3/imm32
4014     68/push  "c "/imm32
4015     53/push-EBX
4016     68/push  "A"/imm32
4017     # . . call
4018     e8/call  trace-slsns/disp32
4019     # . . discard args
4020     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
4021 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
4047     # check-trace-contains("Abc 0x00000003 e")
4048     # . . push args
4049     68/push  "F - test-trace-slsls"/imm32
4050     68/push  "Abc 0x00000003 e"/imm32
4051     # . . call
4052     e8/call  check-trace-contains/disp32
4053     # . . discard args
4054     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4055     # . epilog
4056     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4057     5d/pop-to-EBP
4058     c3/return
4059 
4060 trace-slsss:  # s1 : (address string), l2 : (address slice), s3 : (address string), s4 : (address string), s5 : (address string)
4061     # . prolog
4062     55/push-EBP
4063     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4064     # write(*Trace-stream, s1)
4065     # . . push args
4066     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
4067     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
4068     # . . call
4069     e8/call  write/disp32
4070     # . . discard args
4071     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4072     # write-slice(*Trace-stream, l2)
4073     # . . push args
4074     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
4075     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
4076     # . . call
4077     e8/call  write-slice/disp32
4078     # . . discard args
4079     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4080     # write(*Trace-stream, s3)
4081     # . . push args
4082     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
4083     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
4084     # . . call
4085     e8/call  write/disp32
4086     # . . discard args
4087     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4088     # write(*Trace-stream, s4)
4089     # . . push args
4090     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
4091     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
4092     # . . call
4093     e8/call  write/disp32
4094     # . . discard args
4095     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4096     # trace(s5)  # implicitly adds a newline and finalizes the trace line
4097     # . . push args
4098     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
4099     # . . call
4100     e8/call  trace/disp32
4101     # . . discard args
4102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4103 $trace-slsss:end:
4104     # . epilog
4105     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4106     5d/pop-to-EBP
4107     c3/return
4108 
4109 test-trace-slsss:
4110     # . prolog
4111     55/push-EBP
4112     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4113     # setup
4114     # . *Trace-stream->write = 0
4115     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/EAX   Trace-stream/disp32               # copy *Trace-stream to EAX
4116     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # clear *EAX
4117     # (EAX..ECX) = "b"
4118     b8/copy-to-EAX  "b"/imm32
4119     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
4120     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
4121     05/add-to-EAX  4/imm32
4122     # var b/EBX : (address slice) = {EAX, ECX}
4123     51/push-ECX
4124     50/push-EAX
4125     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBX
4126     # trace-slsss("A" b "c" "d" "e")
4127     # . . push args
4128     68/push  "e"/imm32
4129     68/push  "d"/imm32
4130     68/push  "c"/imm32
4131     53/push-EBX
4132     68/push  "A"/imm32
4133     # . . call
4134     e8/call  trace-slsss/disp32
4135     # . . discard args
4136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
4137 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
4163     # check-trace-contains("Abcde")
4164     # . . push args
4165     68/push  "F - test-trace-slsss"/imm32
4166     68/push  "Abcde"/imm32
4167     # . . call
4168     e8/call  check-trace-contains/disp32
4169     # . . discard args
4170     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4171     # . epilog
4172     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4173     5d/pop-to-EBP
4174     c3/return
4175 
4176 num-bytes:  # line : (address stream) -> EAX : int
4177     # pseudocode:
4178     #   result = 0
4179     #   while true
4180     #     var word-slice = next-word(line)
4181     #     if slice-empty?(word-slice)             # end of line
4182     #       break
4183     #     if slice-starts-with?(word-slice, "#")  # comment
4184     #       break
4185     #     if is-label?(word-slice)                # no need for label declarations anymore
4186     #       break
4187     #     if slice-equal?(word-slice, "==")
4188     #       break                                 # no need for segment header lines
4189     #     result += compute-width(word-slice)
4190     #   return result
4191     #
4192     # . prolog
4193     55/push-EBP
4194     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4195     # . save registers
4196     51/push-ECX
4197     52/push-EDX
4198     53/push-EBX
4199     # var result/EAX = 0
4200     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
4201     # var word-slice/ECX = {0, 0}
4202     68/push  0/imm32/end
4203     68/push  0/imm32/start
4204     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
4205 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
4231     # . rewind-stream(line)
4232     # . . push args
4233     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
4234     # . . call
4235     e8/call  rewind-stream/disp32
4236     # . . discard args
4237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4238 $num-bytes:loop:
4239     # next-word(line, word-slice)
4240     # . . push args
4241     51/push-ECX
4242     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
4243     # . . call
4244     e8/call  next-word/disp32
4245     # . . discard args
4246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4247 +-- 46 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
4293 $num-bytes:check0:
4294     # if (slice-empty?(word-slice)) break
4295     # . save result
4296     50/push-EAX
4297     # . EAX = slice-empty?(word-slice)
4298     # . . push args
4299     51/push-ECX
4300     # . . call
4301     e8/call  slice-empty?/disp32
4302     # . . discard args
4303     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4304     # . if (EAX != 0) break
4305     3d/compare-EAX-and  0/imm32
4306     # . restore result now that ZF is set
4307     58/pop-to-EAX
4308     75/jump-if-not-equal  $num-bytes:end/disp8
4309 $num-bytes:check-for-comment:
4310     # if (slice-starts-with?(word-slice, "#")) break
4311     # . start/EDX = word-slice->start
4312     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
4313     # . c/EBX = *start
4314     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
4315     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/BL    .               .                 # copy byte at *EDX to BL
4316     # . if (EBX == '#') break
4317     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x23/imm32/hash   # compare EBX
4318     74/jump-if-equal  $num-bytes:end/disp8
4319 $num-bytes:check-for-label:
4320     # if (slice-ends-with?(word-slice, ":")) break
4321     # . end/EDX = word-slice->end
4322     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
4323     # . c/EBX = *(end-1)
4324     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
4325     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ECX to BL
4326     # . if (EBX == ':') break
4327     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0x3a/imm32/colon  # compare EBX
4328     74/jump-if-equal  $num-bytes:end/disp8
4329 $num-bytes:check-for-segment-header:
4330     # if (slice-equal?(word-slice, "==")) break
4331     # . push result
4332     50/push-EAX
4333     # . EAX = slice-equal?(word-slice, "==")
4334     # . . push args
4335     68/push  "=="/imm32
4336     51/push-ECX
4337     # . . call
4338     e8/call  slice-equal?/disp32
4339     # . . discard args
4340     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4341     # . if (EAX != 0) break
4342     3d/compare-EAX-and  0/imm32
4343     # . restore result now that ZF is set
4344     58/pop-to-EAX
4345     75/jump-if-not-equal  $num-bytes:end/disp8
4346 $num-bytes:loop-body:
4347     # result += compute-width-of-slice(word-slice)
4348     # . copy result to EDX
4349     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
4350     # . EAX = compute-width-of-slice(word-slice)
4351     # . . push args
4352     51/push-ECX
4353     # . . call
4354     e8/call compute-width-of-slice/disp32
4355     # . . discard args
4356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4357     # . EAX += result
4358     01/add                          3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # add EDX to EAX
4359     e9/jump  $num-bytes:loop/disp32
4360 $num-bytes:end:
4361     # . rewind-stream(line)
4362     # . . push args
4363     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
4364     # . . call
4365     e8/call  rewind-stream/disp32
4366     # . . discard args
4367     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4368     # . reclaim locals
4369     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4370     # . restore registers
4371     5b/pop-to-EBX
4372     5a/pop-to-EDX
4373     59/pop-to-ECX
4374     # . epilog
4375     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4376     5d/pop-to-EBP
4377     c3/return
4378 
4379 test-num-bytes-handles-empty-string:
4380     # if a line starts with '#', return 0
4381     # . prolog
4382     55/push-EBP
4383     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4384     # setup
4385     # . clear-stream(_test-input-stream)
4386     # . . push args
4387     68/push  _test-input-stream/imm32
4388     # . . call
4389     e8/call  clear-stream/disp32
4390     # . . discard args
4391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4392     # . clear-stream(_test-output-stream)
4393     # . . push args
4394     68/push  _test-output-stream/imm32
4395     # . . call
4396     e8/call  clear-stream/disp32
4397     # . . discard args
4398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4399     # no contents in input
4400     # EAX = num-bytes(_test-input-stream)
4401     # . . push args
4402     68/push  _test-input-stream/imm32
4403     # . . call
4404     e8/call  num-bytes/disp32
4405     # . . discard args
4406     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4407     # check-ints-equal(EAX, 0, msg)
4408     # . . push args
4409     68/push  "F - test-num-bytes-handles-empty-string"/imm32
4410     68/push  0/imm32/true
4411     50/push-EAX
4412     # . . call
4413     e8/call  check-ints-equal/disp32
4414     # . . discard args
4415     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4416     # . epilog
4417     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4418     5d/pop-to-EBP
4419     c3/return
4420 
4421 test-num-bytes-ignores-comments:
4422     # if a line starts with '#', return 0
4423     # . prolog
4424     55/push-EBP
4425     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4426     # setup
4427     # . clear-stream(_test-input-stream)
4428     # . . push args
4429     68/push  _test-input-stream/imm32
4430     # . . call
4431     e8/call  clear-stream/disp32
4432     # . . discard args
4433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4434     # . clear-stream(_test-output-stream)
4435     # . . push args
4436     68/push  _test-output-stream/imm32
4437     # . . call
4438     e8/call  clear-stream/disp32
4439     # . . discard args
4440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4441     # initialize input
4442     # . write(_test-input-stream, "# abcd")
4443     # . . push args
4444     68/push  "# abcd"/imm32
4445     68/push  _test-input-stream/imm32
4446     # . . call
4447     e8/call  write/disp32
4448     # . . discard args
4449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4450     # EAX = num-bytes(_test-input-stream)
4451     # . . push args
4452     68/push  _test-input-stream/imm32
4453     # . . call
4454     e8/call  num-bytes/disp32
4455     # . . discard args
4456     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4457     # check-ints-equal(EAX, 0, msg)
4458     # . . push args
4459     68/push  "F - test-num-bytes-ignores-comments"/imm32
4460     68/push  0/imm32/true
4461     50/push-EAX
4462     # . . call
4463     e8/call  check-ints-equal/disp32
4464     # . . discard args
4465     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4466     # . epilog
4467     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4468     5d/pop-to-EBP
4469     c3/return
4470 
4471 test-num-bytes-ignores-labels:
4472     # if the first word ends with ':', return 0
4473     # . prolog
4474     55/push-EBP
4475     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4476     # setup
4477     # . clear-stream(_test-input-stream)
4478     # . . push args
4479     68/push  _test-input-stream/imm32
4480     # . . call
4481     e8/call  clear-stream/disp32
4482     # . . discard args
4483     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4484     # . clear-stream(_test-output-stream)
4485     # . . push args
4486     68/push  _test-output-stream/imm32
4487     # . . call
4488     e8/call  clear-stream/disp32
4489     # . . discard args
4490     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4491     # initialize input
4492     # . write(_test-input-stream, "ab: # cd")
4493     # . . push args
4494     68/push  "ab: # cd"/imm32
4495     68/push  _test-input-stream/imm32
4496     # . . call
4497     e8/call  write/disp32
4498     # . . discard args
4499     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4500     # EAX = num-bytes(_test-input-stream)
4501     # . . push args
4502     68/push  _test-input-stream/imm32
4503     # . . call
4504     e8/call  num-bytes/disp32
4505     # . . discard args
4506     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4507     # check-ints-equal(EAX, 0, msg)
4508     # . . push args
4509     68/push  "F - test-num-bytes-ignores-labels"/imm32
4510     68/push  0/imm32/true
4511     50/push-EAX
4512     # . . call
4513     e8/call  check-ints-equal/disp32
4514     # . . discard args
4515     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4516     # . epilog
4517     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4518     5d/pop-to-EBP
4519     c3/return
4520 
4521 test-num-bytes-ignores-segment-headers:
4522     # if the first word is '==', return 0
4523     # . prolog
4524     55/push-EBP
4525     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4526     # setup
4527     # . clear-stream(_test-input-stream)
4528     # . . push args
4529     68/push  _test-input-stream/imm32
4530     # . . call
4531     e8/call  clear-stream/disp32
4532     # . . discard args
4533     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4534     # . clear-stream(_test-output-stream)
4535     # . . push args
4536     68/push  _test-output-stream/imm32
4537     # . . call
4538     e8/call  clear-stream/disp32
4539     # . . discard args
4540     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4541     # initialize input
4542     # . write(_test-input-stream, "== ab cd")
4543     # . . push args
4544     68/push  "== ab cd"/imm32
4545     68/push  _test-input-stream/imm32
4546     # . . call
4547     e8/call  write/disp32
4548     # . . discard args
4549     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4550     # EAX = num-bytes(_test-input-stream)
4551     # . . push args
4552     68/push  _test-input-stream/imm32
4553     # . . call
4554     e8/call  num-bytes/disp32
4555     # . . discard args
4556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4557     # check-ints-equal(EAX, 0, msg)
4558     # . . push args
4559     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
4560     68/push  0/imm32/true
4561     50/push-EAX
4562     # . . call
4563     e8/call  check-ints-equal/disp32
4564     # . . discard args
4565     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4566     # . epilog
4567     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4568     5d/pop-to-EBP
4569     c3/return
4570 
4571 test-num-bytes-counts-words-by-default:
4572     # without metadata, count words
4573     # . prolog
4574     55/push-EBP
4575     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4576     # setup
4577     # . clear-stream(_test-input-stream)
4578     # . . push args
4579     68/push  _test-input-stream/imm32
4580     # . . call
4581     e8/call  clear-stream/disp32
4582     # . . discard args
4583     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4584     # . clear-stream(_test-output-stream)
4585     # . . push args
4586     68/push  _test-output-stream/imm32
4587     # . . call
4588     e8/call  clear-stream/disp32
4589     # . . discard args
4590     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4591     # initialize input
4592     # . write(_test-input-stream, "ab cd ef")
4593     # . . push args
4594     68/push  "ab cd ef"/imm32
4595     68/push  _test-input-stream/imm32
4596     # . . call
4597     e8/call  write/disp32
4598     # . . discard args
4599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4600     # EAX = num-bytes(_test-input-stream)
4601     # . . push args
4602     68/push  _test-input-stream/imm32
4603     # . . call
4604     e8/call  num-bytes/disp32
4605     # . . discard args
4606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4607     # check-ints-equal(EAX, 3, msg)
4608     # . . push args
4609     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
4610     68/push  3/imm32/true
4611     50/push-EAX
4612     # . . call
4613     e8/call  check-ints-equal/disp32
4614     # . . discard args
4615     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4616     # . epilog
4617     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4618     5d/pop-to-EBP
4619     c3/return
4620 
4621 test-num-bytes-ignores-trailing-comment:
4622     # trailing comments appropriately ignored
4623     # . prolog
4624     55/push-EBP
4625     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4626     # setup
4627     # . clear-stream(_test-input-stream)
4628     # . . push args
4629     68/push  _test-input-stream/imm32
4630     # . . call
4631     e8/call  clear-stream/disp32
4632     # . . discard args
4633     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4634     # . clear-stream(_test-output-stream)
4635     # . . push args
4636     68/push  _test-output-stream/imm32
4637     # . . call
4638     e8/call  clear-stream/disp32
4639     # . . discard args
4640     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4641     # initialize input
4642     # . write(_test-input-stream, "ab cd # ef")
4643     # . . push args
4644     68/push  "ab cd # ef"/imm32
4645     68/push  _test-input-stream/imm32
4646     # . . call
4647     e8/call  write/disp32
4648     # . . discard args
4649     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4650     # EAX = num-bytes(_test-input-stream)
4651     # . . push args
4652     68/push  _test-input-stream/imm32
4653     # . . call
4654     e8/call  num-bytes/disp32
4655     # . . discard args
4656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4657     # check-ints-equal(EAX, 2, msg)
4658     # . . push args
4659     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
4660     68/push  2/imm32/true
4661     50/push-EAX
4662     # . . call
4663     e8/call  check-ints-equal/disp32
4664     # . . discard args
4665     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4666     # . epilog
4667     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4668     5d/pop-to-EBP
4669     c3/return
4670 
4671 test-num-bytes-handles-imm32:
4672     # if a word has the /imm32 metadata, count it as 4 bytes
4673     # . prolog
4674     55/push-EBP
4675     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4676     # setup
4677     # . clear-stream(_test-input-stream)
4678     # . . push args
4679     68/push  _test-input-stream/imm32
4680     # . . call
4681     e8/call  clear-stream/disp32
4682     # . . discard args
4683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4684     # . clear-stream(_test-output-stream)
4685     # . . push args
4686     68/push  _test-output-stream/imm32
4687     # . . call
4688     e8/call  clear-stream/disp32
4689     # . . discard args
4690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4691     # initialize input
4692     # . write(_test-input-stream, "ab cd/imm32 ef")
4693     # . . push args
4694     68/push  "ab cd/imm32 ef"/imm32
4695     68/push  _test-input-stream/imm32
4696     # . . call
4697     e8/call  write/disp32
4698     # . . discard args
4699     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4700     # EAX = num-bytes(_test-input-stream)
4701     # . . push args
4702     68/push  _test-input-stream/imm32
4703     # . . call
4704     e8/call  num-bytes/disp32
4705     # . . discard args
4706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4707     # check-ints-equal(EAX, 6, msg)
4708     # . . push args
4709     68/push  "F - test-num-bytes-handles-imm32"/imm32
4710     68/push  6/imm32/true
4711     50/push-EAX
4712     # . . call
4713     e8/call  check-ints-equal/disp32
4714     # . . discard args
4715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4716     # . epilog
4717     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4718     5d/pop-to-EBP
4719     c3/return
4720 
4721 == data
4722 
4723 # This block of bytes gets copied to the start of the output ELF file, with
4724 # some fields filled in.
4725 # http://www.sco.com/developers/gabi/latest/ch4.eheader.html
4726 Elf_header:
4727   # - length
4728   0x34/imm32
4729   # - data
4730 $e_ident:
4731   7f 45/E 4c/L 46/F
4732   01/32-bit  01/little-endian  01/file-version  00/no-os-extensions
4733   00 00 00 00 00 00 00 00  # 8 bytes of padding
4734 $e_type:
4735   02 00
4736 $e_machine:
4737   03 00
4738 $e_version:
4739   1/imm32
4740 Elf_e_entry:
4741   0x09000000/imm32  # approximate default; must be updated
4742 $e_phoff:
4743   0x34/imm32  # offset for the 'program header table' containing segment headers
4744 $e_shoff:
4745   0/imm32  # no sections
4746 $e_flags:
4747   0/imm32  # unused
4748 $e_ehsize:
4749   0x34 00
4750 $e_phentsize:
4751   0x20 00
4752 Elf_e_phnum:
4753   00 00  # number of segments; must be updated
4754 $e_shentsize:
4755   00 00  # no sections
4756 $e_shnum:
4757   00 00
4758 $e_shstrndx:
4759   00 00
4760 
4761 # This block of bytes gets copied after the Elf_header once for each segment.
4762 # Some fields need filling in each time.
4763 # https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html
4764 Elf_program_header_entry:
4765   # - length
4766   0x20/imm32
4767   # - data
4768 $p_type:
4769   1/imm32/PT_LOAD
4770 Elf_p_offset:
4771   0/imm32  # byte offset in the file at which a segment begins; must be updated
4772 Elf_p_vaddr:
4773   0/imm32  # starting address to store the segment at before running the program
4774 Elf_p_paddr:
4775   0/imm32  # should have same value as Elf_p_vaddr
4776 Elf_p_filesz:
4777   0/imm32
4778 Elf_p_memsz:
4779   0/imm32  # should have same value as Elf_p_filesz
4780 Elf_p_flags:
4781   6/imm32/rw-  # read/write/execute permissions for the segment; must be updated for the code segment
4782 $p_align:
4783   # we hold this constant; changing it will require adjusting the way we
4784   # compute the starting address for each segment
4785   0x1000/imm32
4786 
4787 # . . vim:nowrap:textwidth=0