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