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