https://github.com/akkartik/mu/blob/master/apps/survey.subx
   1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
   2 # (landscape).
   3 # Use the addresses assigned to:
   4 #   a) replace labels
   5 #   b) add segment headers with addresses and offsets correctly filled in
   6 #
   7 # To build:
   8 #   $ ./subx translate init.linux 0*.subx apps/subx-params.subx apps/survey.subx  -o apps/survey
   9 #
  10 # The expected input is a stream of bytes with segment headers, comments and
  11 # some interspersed labels.
  12 #   $ cat x
  13 #   == code 0x1
  14 #   l1:
  15 #   aa bb l1/imm8
  16 #   cc dd l2/disp32
  17 #   l2:
  18 #   ee foo/imm32
  19 #   == data 0x10
  20 #   foo:
  21 #     00
  22 #
  23 # The output is the stream of bytes without segment headers or label definitions,
  24 # and with label references replaced with numeric values/displacements.
  25 #
  26 #   $ cat x  |./subx run apps/survey
  27 #   ...ELF header bytes...
  28 #   # ELF header above will specify that code segment begins at this offset
  29 #   aa bb nn  # some computed address
  30 #   cc dd nn nn nn nn  # some computed displacement
  31 #   ee nn nn nn nn  # some computed address
  32 #   # ELF header above will specify that data segment begins at this offset
  33 #   00
  34 #
  35 # The ELF format has some persnickety constraints on the starting addresses of
  36 # segments, so input headers are treated as guidelines and adjusted in the
  37 # output.
  38 
  39 == code
  40 #   instruction                     effective address                                                   register    displacement    immediate
  41 # . op          subop               mod             rm32          base        index         scale       r32
  42 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
  43 
  44 Entry:  # run tests if necessary, convert stdin if not
  45     # . prologue
  46     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  47 
  48     # Heap = new-segment(Heap-size)
  49     # . . push args
  50     68/push  Heap/imm32
  51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  52     # . . call
  53     e8/call  new-segment/disp32
  54     # . . discard args
  55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  56     # initialize-trace-stream(Trace-size)
  57     # . . push args
  58     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
  59     # . . call
  60     e8/call  initialize-trace-stream/disp32
  61     # . . discard args
  62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
  63 
  64     # - if argc > 1 and argv[1] == "test", then return run_tests()
  65     # if (argc <= 1) goto interactive
  66     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  67     7e/jump-if-lesser-or-equal  $subx-survey-main:interactive/disp8
  68     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  69     # . eax = kernel-string-equal?(argv[1], "test")
  70     # . . push args
  71     68/push  "test"/imm32
  72     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  73     # . . call
  74     e8/call  kernel-string-equal?/disp32
  75     # . . discard args
  76     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  77     # . if (eax == false) goto interactive
  78     3d/compare-eax-and  0/imm32/false
  79     74/jump-if-equal  $subx-survey-main:interactive/disp8
  80     # run-tests()
  81     e8/call  run-tests/disp32
  82     # syscall(exit, *Num-test-failures)
  83     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  84     eb/jump  $subx-survey-main:end/disp8
  85 $subx-survey-main:interactive:
  86     # - otherwise convert stdin
  87     # subx-survey(Stdin, Stdout)
  88     # . . push args
  89     68/push  Stdout/imm32
  90     68/push  Stdin/imm32
  91     # . . call
  92     e8/call  subx-survey/disp32
  93     # . . discard args
  94     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  95 #?     # . write-stream(2/stderr, Trace-stream)
  96 #?     # . . push args
  97 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
  98 #?     68/push  2/imm32/stderr
  99 #?     # . . call
 100 #?     e8/call  write-stream/disp32
 101 #?     # . . discard args
 102 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 103     # syscall(exit, 0)
 104     bb/copy-to-ebx  0/imm32
 105 $subx-survey-main:end:
 106     b8/copy-to-eax  1/imm32/exit
 107     cd/syscall  0x80/imm8
 108 
 109 # data structures:
 110 #   segment-info: {addr, file-offset, size}            (12 bytes)
 111 #   segments: (addr stream {string, segment-info})     (16 bytes per row)
 112 #   label-info: {segment-name, segment-offset, addr}   (12 bytes)
 113 #   labels: (addr stream {string, label-info})         (16 bytes per row)
 114 # these are all inefficient; use sequential scans for lookups
 115 
 116 subx-survey:  # infile : (addr buffered-file), out : (addr buffered-file)
 117     # pseudocode
 118     #   var in : (ref stream byte 4096)
 119     #   slurp(infile, in)
 120     #   var segments : (stream segment-info)
 121     #   var labels : (stream label-info Max-labels)
 122     #   compute-offsets(in, segments, labels)
 123     #   compute-addresses(segments, labels)
 124     #   rewind-stream(in)
 125     #   emit-output(in, out, segments, labels)
 126     #
 127     # . prologue
 128     55/push-ebp
 129     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 130     # . save registers
 131     51/push-ecx
 132     52/push-edx
 133     56/push-esi
 134     # var segments/ecx : (ref stream {string, segment-info} 160)   # 10 rows * 16 bytes/row
 135     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xa0/imm32        # subtract from esp
 136     68/push  0xa0/imm32/length
 137     68/push  0/imm32/read
 138     68/push  0/imm32/write
 139     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 140     # var labels/edx : (ref stream label-info Max-labels*16)
 141     # . data
 142     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
 143     # . length
 144     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
 145     # . read
 146     68/push  0/imm32/read
 147     # . write
 148     68/push  0/imm32/write
 149     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 150     # var in/esi : (ref stream byte Input-size)
 151     # . data
 152     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
 153     # . length
 154     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
 155     # . read
 156     68/push  0/imm32/read
 157     # . write
 158     68/push  0/imm32/write
 159     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 160 +-- 41 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 201 +--  9 lines: #?     # write(2/stderr, "slurp in\n") ---------------------------------------------------------------------------------------------------------
 210     # slurp(infile, in)
 211     # . . push args
 212     56/push-esi
 213     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 214     # . . call
 215     e8/call  slurp/disp32
 216     # . . discard args
 217     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 218 +-- 40 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 258 +-- 33 lines: #?     # dump in -------------------------------------------------------------------------------------------------------------------------------
 291 +--  9 lines: #?     # write(2/stderr, "compute-offsets\n") --------------------------------------------------------------------------------------------------
 300     # compute-offsets(in, segments, labels)
 301     # . . push args
 302     52/push-edx
 303     51/push-ecx
 304     56/push-esi
 305     # . . call
 306     e8/call  compute-offsets/disp32
 307     # . . discard args
 308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 309 +--  9 lines: #?     # write(2/stderr, "compute-addresses\n") ------------------------------------------------------------------------------------------------
 318     # compute-addresses(segments, labels)
 319     # . . push args
 320     52/push-edx
 321     51/push-ecx
 322     # . . call
 323     e8/call  compute-addresses/disp32
 324     # . . discard args
 325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 326     # rewind-stream(in)
 327     # . . push args
 328     56/push-esi
 329     # . . call
 330     e8/call  rewind-stream/disp32
 331     # . . discard args
 332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 333 +--  9 lines: #?     # write(2/stderr, "emit-output\n") ------------------------------------------------------------------------------------------------------
 342 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
 368 +-- 40 lines: #?     # dump labels->write --------------------------------------------------------------------------------------------------------------------
 408     # emit-output(in, out, segments, labels)
 409     # . . push args
 410     52/push-edx
 411     51/push-ecx
 412     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 413     56/push-esi
 414     # . . call
 415     e8/call  emit-output/disp32
 416     # . . discard args
 417     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 418     # flush(out)
 419     # . . push args
 420     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 421     # . . call
 422     e8/call  flush/disp32
 423     # . . discard args
 424     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 425 $subx-survey:end:
 426     # . reclaim locals
 427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x30a0/imm32      # add to esp
 428     # . restore registers
 429     5e/pop-to-esi
 430     5a/pop-to-edx
 431     59/pop-to-ecx
 432     # . epilogue
 433     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 434     5d/pop-to-ebp
 435     c3/return
 436 
 437 test-subx-survey-computes-addresses:
 438     # input:
 439     #   == code 0x1
 440     #   Entry:
 441     #   ab x/imm32
 442     #   == data 0x1000
 443     #   x:
 444     #     01
 445     #
 446     # trace contains (in any order):
 447     #   label x is at address 0x1079
 448     #   segment code starts at address 0x74
 449     #   segment code has size 5
 450     #   segment data starts at address 0x1079
 451     #
 452     # . prologue
 453     55/push-ebp
 454     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 455     # setup
 456     # . clear-stream(_test-input-stream)
 457     # . . push args
 458     68/push  _test-input-stream/imm32
 459     # . . call
 460     e8/call  clear-stream/disp32
 461     # . . discard args
 462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 463     # . clear-stream($_test-input-buffered-file->buffer)
 464     # . . push args
 465     68/push  $_test-input-buffered-file->buffer/imm32
 466     # . . call
 467     e8/call  clear-stream/disp32
 468     # . . discard args
 469     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 470     # . clear-stream(_test-output-stream)
 471     # . . push args
 472     68/push  _test-output-stream/imm32
 473     # . . call
 474     e8/call  clear-stream/disp32
 475     # . . discard args
 476     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 477     # . clear-stream($_test-output-buffered-file->buffer)
 478     # . . push args
 479     68/push  $_test-output-buffered-file->buffer/imm32
 480     # . . call
 481     e8/call  clear-stream/disp32
 482     # . . discard args
 483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 484     # initialize input
 485     # . write(_test-input-stream, "== code 0x1\n")
 486     # . . push args
 487     68/push  "== code 0x1\n"/imm32
 488     68/push  _test-input-stream/imm32
 489     # . . call
 490     e8/call  write/disp32
 491     # . . discard args
 492     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 493     # . write(_test-input-stream, "Entry:\n")
 494     # . . push args
 495     68/push  "Entry:\n"/imm32
 496     68/push  _test-input-stream/imm32
 497     # . . call
 498     e8/call  write/disp32
 499     # . . discard args
 500     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 501     # . write(_test-input-stream, "ab x/imm32\n")
 502     # . . push args
 503     68/push  "ab x/imm32\n"/imm32
 504     68/push  _test-input-stream/imm32
 505     # . . call
 506     e8/call  write/disp32
 507     # . . discard args
 508     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 509     # . write(_test-input-stream, "== data 0x1000\n")
 510     # . . push args
 511     68/push  "== data 0x1000\n"/imm32
 512     68/push  _test-input-stream/imm32
 513     # . . call
 514     e8/call  write/disp32
 515     # . . discard args
 516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 517     # . write(_test-input-stream, "x:\n")
 518     # . . push args
 519     68/push  "x:\n"/imm32
 520     68/push  _test-input-stream/imm32
 521     # . . call
 522     e8/call  write/disp32
 523     # . . discard args
 524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 525     # . write(_test-input-stream, "01\n")
 526     # . . push args
 527     68/push  "01\n"/imm32
 528     68/push  _test-input-stream/imm32
 529     # . . call
 530     e8/call  write/disp32
 531     # . . discard args
 532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 533     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
 534     # . . push args
 535     68/push  _test-output-buffered-file/imm32
 536     68/push  _test-input-buffered-file/imm32
 537     # . . call
 538     e8/call  subx-survey/disp32
 539     # . . discard args
 540     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 541     # check trace
 542 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
 568     # . check-trace-contains("label 'x' is at address 0x00001079.", msg)
 569     # . . push args
 570     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
 571     68/push  "label 'x' is at address 0x00001079."/imm32
 572     # . . call
 573     e8/call  check-trace-contains/disp32
 574     # . . discard args
 575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 576     # . check-trace-contains("segment 'code' starts at address 0x00000074.", msg)
 577     # . . push args
 578     68/push  "F - test-subx-survey-computes-addresses/1"/imm32
 579     68/push  "segment 'code' starts at address 0x00000074."/imm32
 580     # . . call
 581     e8/call  check-trace-contains/disp32
 582     # . . discard args
 583     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 584     # . check-trace-contains("segment 'code' has size 0x00000005.", msg)
 585     # . . push args
 586     68/push  "F - test-subx-survey-computes-addresses/2"/imm32
 587     68/push  "segment 'code' has size 0x00000005."/imm32
 588     # . . call
 589     e8/call  check-trace-contains/disp32
 590     # . . discard args
 591     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 592     # . check-trace-contains("segment 'data' starts at address 0x00001079.", msg)
 593     # . . push args
 594     68/push  "F - test-subx-survey-computes-addresses/3"/imm32
 595     68/push  "segment 'data' starts at address 0x00001079."/imm32
 596     # . . call
 597     e8/call  check-trace-contains/disp32
 598     # . . discard args
 599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 600     # . epilogue
 601     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 602     5d/pop-to-ebp
 603     c3/return
 604 
 605 # global scratch space for compute-offsets in the data segment
 606 == data
 607 
 608 compute-offsets:file-offset:  # int
 609   0/imm32
 610 compute-offsets:segment-offset:  # int
 611   0/imm32
 612 compute-offsets:word-slice:
 613   0/imm32/start
 614   0/imm32/end
 615 compute-offsets:segment-tmp:  # slice
 616   0/imm32/start
 617   0/imm32/end
 618 
 619 == code
 620 
 621 compute-offsets:  # in : (addr stream byte), segments : (addr stream {string, segment-info}), labels : (addr stream {string, label-info})
 622     # skeleton:
 623     #   for lines in 'in'
 624     #     for words in line
 625     #       switch word
 626     #         case 1
 627     #         case 2
 628     #         ...
 629     #         default
 630     #
 631     # pseudocode:
 632     #   curr-segment-name : (addr string) = 0
 633     #   var line : (stream byte 512)
 634     #   while true                                  # line loop
 635     #     clear-stream(line)
 636     #     read-line(in, line)
 637     #     if (line->write == 0) break               # end of file
 638     #     while true                                # word loop
 639     #       word-slice = next-word(line)
 640     #       if slice-empty?(word-slice)             # end of line
 641     #         break
 642     #       else if slice-starts-with?(word-slice, "#")  # comment
 643     #         break                                 # end of line
 644     #       else if slice-equal?(word-slice, "==")
 645     #         if curr-segment-name != 0
 646     #           seg = get-or-insert(segments, curr-segment-name)
 647     #           seg->size = *file-offset - seg->file-offset
 648     #           trace("segment '", curr-segment-name, "' has size ", seg->size)
 649     #         segment-tmp = next-word(line)
 650     #         curr-segment-name = slice-to-string(segment-tmp)
 651     #         if empty?(curr-segment-name)
 652     #           abort
 653     #         segment-tmp = next-word(line)
 654     #         if slice-empty?(segment-tmp)
 655     #           abort
 656     #         seg = get-or-insert(segments, curr-segment-name)
 657     #         seg->starting-address = parse-hex-int(segment-tmp)
 658     #         seg->file-offset = *file-offset
 659     #         trace("segment '", curr-segment-name, "' is at file offset ", seg->file-offset)
 660     #         segment-offset = 0
 661     #         break  (next line)
 662     #       else if is-label?(word-slice)
 663     #         strip trailing ':' from word-slice
 664     #         x : (addr label-info) = get-or-insert(labels, name)
 665     #         x->segment-name = curr-segment-name
 666     #         trace("label '", word-slice, "' is in segment '", curr-segment-name, "'.")
 667     #         x->segment-offset = segment-offset
 668     #         trace("label '", word-slice, "' is at segment offset ", segment-offset, ".")
 669     #         # labels occupy no space, so no need to increment offsets
 670     #       else
 671     #         width = compute-width-of-slice(word-slice)
 672     #         *segment-offset += width
 673     #         *file-offset += width
 674     #
 675     # . prologue
 676     55/push-ebp
 677     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 678     # . save registers
 679     50/push-eax
 680     51/push-ecx
 681     52/push-edx
 682     53/push-ebx
 683     56/push-esi
 684     57/push-edi
 685     # curr-segment-name/esi = 0
 686     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
 687     # file-offset = 0
 688     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:file-offset/disp32  0/imm32               # copy to *compute-offsets:word-slice
 689     # segment-offset = 0
 690     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .           compute-offsets:segment-offset/disp32  0/imm32            # copy to *compute-offsets:word-slice
 691     # var line/ecx : (stream byte 512)
 692     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 693     68/push  0x200/imm32/length
 694     68/push  0/imm32/read
 695     68/push  0/imm32/write
 696     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 697 $compute-offsets:line-loop:
 698     # clear-stream(line)
 699     51/push-ecx
 700     e8/call  clear-stream/disp32
 701     # . discard args
 702     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 703     # read-line(in, line)
 704     51/push-ecx
 705     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 706     e8/call  read-line/disp32
 707     # . discard args
 708     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 709     # if (line->write == 0) break
 710     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
 711     3d/compare-eax-and  0/imm32
 712     0f 84/jump-if-equal  $compute-offsets:break-line-loop/disp32
 713 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 746 $compute-offsets:word-loop:
 747     # edx = word-slice
 748     ba/copy-to-edx  compute-offsets:word-slice/imm32
 749     # next-word(line, word-slice/edx)
 750     52/push-edx
 751     51/push-ecx
 752     e8/call  next-word/disp32
 753     # . discard args
 754     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 755 +-- 74 lines: #?     # dump word-slice and maybe curr-segment-name -------------------------------------------------------------------------------------------
 829 $compute-offsets:case-empty:
 830     # if slice-empty?(word/edx) break
 831     # . eax = slice-empty?(word/edx)
 832     52/push-edx
 833     e8/call  slice-empty?/disp32
 834     # . . discard args
 835     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 836     # . if (eax != false) break
 837     3d/compare-eax-and  0/imm32/false
 838     0f 85/jump-if-not-equal  $compute-offsets:line-loop/disp32
 839 $compute-offsets:case-comment:
 840     # if slice-starts-with?(word-slice, "#") continue
 841     68/push  "#"/imm32
 842     52/push-edx
 843     e8/call  slice-starts-with?/disp32
 844     # . . discard args
 845     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 846     # . if (eax != false) break
 847     3d/compare-eax-and  0/imm32/false
 848     0f 85/jump-if-not-equal  $compute-offsets:line-loop/disp32
 849 $compute-offsets:case-segment-header:
 850     # if (!slice-equal?(word-slice/edx, "==")) goto next case
 851     # . eax = slice-equal?(word-slice/edx, "==")
 852     68/push  "=="/imm32
 853     52/push-edx
 854     e8/call  slice-equal?/disp32
 855     # . . discard args
 856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 857     # . if (eax == false) goto next case
 858     3d/compare-eax-and  0/imm32/false
 859     0f 84/jump-if-equal  $compute-offsets:case-label/disp32
 860     # if (curr-segment-name == 0) goto construct-next-segment
 861     81          7/subop/compare     3/mod/direct    6/rm32/esi    .           .             .           .           .               0/imm32           # compare esi
 862     74/jump-if-equal  $compute-offsets:construct-next-segment/disp8
 863     # seg/eax = get-or-insert(segments, curr-segment-name, row-size=16)
 864     # . . push args
 865     68/push  0x10/imm32/row-size
 866     56/push-esi
 867     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 868     # . . call
 869     e8/call  get-or-insert/disp32
 870     # . . discard args
 871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 872     # seg->size = file-offset - seg->file-offset
 873     # . save ecx
 874     51/push-ecx
 875     # . ebx = *file-offset
 876     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:file-offset/disp32 # copy *file-offset to ebx
 877     # . ecx = seg->file-offset
 878     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(eax+4) to ecx
 879     # . ebx -= ecx
 880     29/subtract                     3/mod/direct    3/rm32/ebx    .           .             .           1/r32/ecx   .               .                 # subtract ecx from ebx
 881     # . seg->size = ebx
 882     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   8/disp8         .                 # copy ebx to *(eax+8)
 883     # . restore ecx
 884     59/pop-to-ecx
 885     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
 886     # . . push args
 887     68/push  "."/imm32
 888     53/push-ebx
 889     68/push  "' has size "/imm32
 890     56/push-esi
 891     68/push  "segment '"/imm32
 892     # . . call
 893     e8/call  trace-sssns/disp32
 894     # . . discard args
 895     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
 896 $compute-offsets:construct-next-segment:
 897     # next-word(line, segment-tmp)
 898     68/push  compute-offsets:segment-tmp/imm32
 899     51/push-ecx
 900     e8/call  next-word/disp32
 901     # . discard args
 902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 903 +-- 45 lines: #?     # dump curr-segment-name if not null (clobbering eax) -----------------------------------------------------------------------------------
 948 $compute-offsets:update-curr-segment-name:
 949     # curr-segment-name = slice-to-string(segment-tmp)
 950     # . eax = slice-to-string(Heap, segment-tmp)
 951     # . . push args
 952     68/push  compute-offsets:segment-tmp/imm32
 953     68/push  Heap/imm32
 954     # . . call
 955     e8/call  slice-to-string/disp32
 956     # . . discard args
 957     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 958     # . curr-segment-name = eax
 959     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
 960     # if empty?(curr-segment-name) abort
 961     # . if (eax == 0) abort
 962     3d/compare-eax-and  0/imm32
 963     0f 84/jump-if-equal  $compute-offsets:abort/disp32
 964     # next-word(line, segment-tmp)
 965     68/push  compute-offsets:segment-tmp/imm32
 966     51/push-ecx
 967     e8/call  next-word/disp32
 968     # . discard args
 969     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 970     # if slice-empty?(segment-tmp) abort
 971     # . eax = slice-empty?(segment-tmp)
 972     68/push  compute-offsets:segment-tmp/imm32
 973     e8/call  slice-empty?/disp32
 974     # . . discard args
 975     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 976     # . if (eax != false) abort
 977     3d/compare-eax-and  0/imm32/false
 978     0f 85/jump-if-not-equal  $compute-offsets:abort/disp32
 979     # seg/ebx = get-or-insert(segments, curr-segment-name, row-size=16)
 980     # . . push args
 981     68/push  0x10/imm32/row-size
 982     56/push-esi
 983     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 984     # . . call
 985     e8/call  get-or-insert/disp32
 986     # . . discard args
 987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 988     # . ebx = eax
 989     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
 990     # seg->address = parse-hex-int(segment-tmp)
 991     # . eax = parse-hex-int(segment-tmp)
 992     68/push  compute-offsets:segment-tmp/imm32
 993     e8/call  parse-hex-int/disp32
 994     # . . discard args
 995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 996     # . seg->address = eax
 997     89/copy                         0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to *ebx
 998     # seg->file-offset = *file-offset
 999     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:file-offset/disp32 # copy *file-offset to eax
1000     89/copy                         1/mod/*+disp8   3/rm32/ebx    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(ebx+4)
1001     # trace-sssns("segment '", curr-segment-name, "' is at file offset ", seg->file-offset, "")
1002     # . . push args
1003     68/push  "."/imm32
1004     50/push-eax
1005     68/push  "' is at file offset "/imm32
1006     56/push-esi
1007     68/push  "segment '"/imm32
1008     # . . call
1009     e8/call  trace-sssns/disp32
1010     # . . discard args
1011     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1012     # segment-offset = 0
1013     c7          0/subop/copy        0/mod/indirect  5/rm32/.disp32            .             .           .     compute-offsets:segment-offset/disp32  0/imm32           # copy to *segment-offset
1014     # break
1015     e9/jump $compute-offsets:line-loop/disp32
1016 $compute-offsets:case-label:
1017     # if (!is-label?(word-slice/edx)) goto next case
1018     # . eax = is-label?(word-slice/edx)
1019     # . . push args
1020     52/push-edx
1021     # . . call
1022     e8/call  is-label?/disp32
1023     # . . discard args
1024     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1025     # . if (eax == false) goto next case
1026     3d/compare-eax-and  0/imm32/false
1027     74/jump-if-equal  $compute-offsets:case-default/disp8
1028     # strip trailing ':' from word-slice
1029     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
1030     # x/eax = leaky-get-or-insert-slice(labels, word-slice, row-size=16)
1031     # . . push args
1032     68/push  0x10/imm32/row-size
1033     52/push-edx
1034     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
1035     # . . call
1036     e8/call  leaky-get-or-insert-slice/disp32
1037     # . . discard args
1038     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1039 $compute-offsets:save-label-offset:
1040     # x->segment-name = curr-segment-name
1041     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to *eax
1042     # trace-slsss("label '" word-slice/edx "' is in segment '" current-segment-name "'.")
1043     # . . push args
1044     68/push  "'."/imm32
1045     56/push-esi
1046     68/push  "' is in segment '"/imm32
1047     52/push-edx
1048     68/push  "label '"/imm32
1049     # . . call
1050     e8/call  trace-slsss/disp32
1051     # . . discard args
1052     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1053     # x->segment-offset = segment-offset
1054     # . ebx = segment-offset
1055     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:segment-offset/disp32  # copy *segment-offset to ebx
1056     # . x->segment-offset = ebx
1057     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   4/disp8         .                 # copy ebx to *(eax+4)
1058     # trace-slsns("label '" word-slice/edx "' is at segment offset " *segment-offset/eax ".")
1059     # . . eax = file-offset
1060     b8/copy-to-eax compute-offsets:segment-offset/imm32
1061     # . . eax = *file-offset/eax
1062     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           0/r32/eax   .               .                 # copy *eax to eax
1063     # . . push args
1064     68/push  "."/imm32
1065     50/push-eax
1066     68/push  "' is at segment offset "/imm32
1067     52/push-edx
1068     68/push  "label '"/imm32
1069     # . . call
1070     e8/call  trace-slsns/disp32
1071     # . . discard args
1072     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1073     # continue
1074     e9/jump  $compute-offsets:word-loop/disp32
1075 $compute-offsets:case-default:
1076     # width/eax = compute-width-of-slice(word-slice)
1077     # . . push args
1078     52/push-edx
1079     # . . call
1080     e8/call compute-width-of-slice/disp32
1081     # . . discard args
1082     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1083     # segment-offset += width
1084     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:segment-offset/disp32 # add eax to *segment-offset
1085     # file-offset += width
1086     01/add                          0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   compute-offsets:file-offset/disp32 # add eax to *file-offset
1087 +-- 41 lines: #?     # dump segment-offset -------------------------------------------------------------------------------------------------------------------
1128     e9/jump $compute-offsets:word-loop/disp32
1129 $compute-offsets:break-line-loop:
1130     # seg/eax = get-or-insert(segments, curr-segment-name, row-size=16)
1131     # . . push args
1132     68/push  0x10/imm32/row-size
1133     56/push-esi
1134     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1135     # . . call
1136     e8/call  get-or-insert/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1139     # seg->size = file-offset - seg->file-offset
1140     # . save ecx
1141     51/push-ecx
1142     # . ebx = *file-offset
1143     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   compute-offsets:file-offset/disp32 # copy *file-offset to ebx
1144     # . ecx = seg->file-offset
1145     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(eax+4) to ecx
1146     # . ebx -= ecx
1147     29/subtract                     3/mod/direct    3/rm32/ebx    .           .             .           1/r32/ecx   .               .                 # subtract ecx from ebx
1148     # . seg->size = ebx
1149     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   8/disp8         .                 # copy ebx to *(eax+8)
1150     # . restore ecx
1151     59/pop-to-ecx
1152     # trace-sssns("segment '", curr-segment-name, "' has size ", seg->size, ".")
1153     # . trace-sssns("segment '", curr-segment-name, "' has size ", ebx, ".")
1154     # . . push args
1155     68/push  "."/imm32
1156     53/push-ebx
1157     68/push  "' has size "/imm32
1158     56/push-esi
1159     68/push  "segment '"/imm32
1160     # . . call
1161     e8/call  trace-sssns/disp32
1162     # . . discard args
1163     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
1164 $compute-offsets:end:
1165     # . reclaim locals
1166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x20c/imm32       # add to esp
1167     # . restore registers
1168     5f/pop-to-edi
1169     5e/pop-to-esi
1170     5b/pop-to-ebx
1171     5a/pop-to-edx
1172     59/pop-to-ecx
1173     58/pop-to-eax
1174     # . epilogue
1175     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1176     5d/pop-to-ebp
1177     c3/return
1178 
1179 $compute-offsets:abort:
1180     # . _write(2/stderr, error)
1181     # . . push args
1182     68/push  "'==' must be followed by segment name and segment-start\n"/imm32
1183     68/push  2/imm32/stderr
1184     # . . call
1185     e8/call  _write/disp32
1186     # . . discard args
1187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1188     # . syscall(exit, 1)
1189     bb/copy-to-ebx  1/imm32
1190     b8/copy-to-eax  1/imm32/exit
1191     cd/syscall  0x80/imm8
1192     # never gets here
1193 
1194 test-compute-offsets:
1195     # input:
1196     #   == code 0x1
1197     #   ab x/imm32  # skip comment
1198     #   == data 0x1000
1199     #   00
1200     #   x:
1201     #     34
1202     #
1203     # trace contains (in any order):
1204     #   segment 'code' is at file offset 0x0.
1205     #   segment 'code' has size 0x5.
1206     #   segment 'data' is at file offset 0x5.
1207     #   segment 'data' has size 0x2.
1208     #   label 'x' is in segment 'data'.
1209     #   label 'x' is at segment offset 0x1.
1210     #
1211     # . prologue
1212     55/push-ebp
1213     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1214     # setup
1215     # . clear-stream(_test-input-stream)
1216     # . . push args
1217     68/push  _test-input-stream/imm32
1218     # . . call
1219     e8/call  clear-stream/disp32
1220     # . . discard args
1221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1222     # var segments/ecx : (ref stream byte 2*16)
1223     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x20/imm32        # subtract from esp
1224     68/push  0x20/imm32/length
1225     68/push  0/imm32/read
1226     68/push  0/imm32/write
1227     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1228     # var labels/edx : (ref stream byte 2*16)
1229     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x20/imm32        # subtract from esp
1230     68/push  0x20/imm32/length
1231     68/push  0/imm32/read
1232     68/push  0/imm32/write
1233     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1234     # initialize input
1235     # . write(_test-input-stream, "== code 0x1\n")
1236     # . . push args
1237     68/push  "== code 0x1\n"/imm32
1238     68/push  _test-input-stream/imm32
1239     # . . call
1240     e8/call  write/disp32
1241     # . . discard args
1242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1243     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
1244     # . . push args
1245     68/push  "ab x/imm32  # skip comment\n"/imm32
1246     68/push  _test-input-stream/imm32
1247     # . . call
1248     e8/call  write/disp32
1249     # . . discard args
1250     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1251     # . write(_test-input-stream, "== data 0x1000\n")
1252     # . . push args
1253     68/push  "== data 0x1000\n"/imm32
1254     68/push  _test-input-stream/imm32
1255     # . . call
1256     e8/call  write/disp32
1257     # . . discard args
1258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1259     # . write(_test-input-stream, "00\n")
1260     # . . push args
1261     68/push  "00\n"/imm32
1262     68/push  _test-input-stream/imm32
1263     # . . call
1264     e8/call  write/disp32
1265     # . . discard args
1266     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1267     # . write(_test-input-stream, "x:\n")
1268     # . . push args
1269     68/push  "x:\n"/imm32
1270     68/push  _test-input-stream/imm32
1271     # . . call
1272     e8/call  write/disp32
1273     # . . discard args
1274     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1275     # . write(_test-input-stream, "34\n")
1276     # . . push args
1277     68/push  "34\n"/imm32
1278     68/push  _test-input-stream/imm32
1279     # . . call
1280     e8/call  write/disp32
1281     # . . discard args
1282     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1283     # compute-offsets(_test-input-stream, segments, labels)
1284     # . . push args
1285     52/push-edx
1286     51/push-ecx
1287     68/push  _test-input-stream/imm32
1288     # . . call
1289     e8/call  compute-offsets/disp32
1290     # . . discard args
1291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32        # add to esp
1292 +-- 26 lines: #?     # dump *Trace-stream --------------------------------------------------------------------------------------------------------------------
1318     # check trace
1319     # . check-trace-contains("segment 'code' is at file offset 0x00000000.", msg)
1320     # . . push args
1321     68/push  "F - test-compute-offsets/0"/imm32
1322     68/push  "segment 'code' is at file offset 0x00000000."/imm32
1323     # . . call
1324     e8/call  check-trace-contains/disp32
1325     # . . discard args
1326     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1327     # . check-trace-contains("segment 'code' has size 0x00000005", msg)
1328     # . . push args
1329     68/push  "F - test-compute-offsets/1"/imm32
1330     68/push  "segment 'code' has size 0x00000005."/imm32
1331     # . . call
1332     e8/call  check-trace-contains/disp32
1333     # . . discard args
1334     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1335     # . check-trace-contains("segment 'data' is at file offset 0x00000005.", msg)
1336     # . . push args
1337     68/push  "F - test-compute-offsets/2"/imm32
1338     68/push  "segment 'data' is at file offset 0x00000005."/imm32
1339     # . . call
1340     e8/call  check-trace-contains/disp32
1341     # . . discard args
1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1343     # . check-trace-contains("segment 'data' has size 0x00000002.", msg)
1344     # . . push args
1345     68/push  "F - test-compute-offsets/3"/imm32
1346     68/push  "segment 'data' has size 0x00000002."/imm32
1347     # . . call
1348     e8/call  check-trace-contains/disp32
1349     # . . discard args
1350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1351     # . check-trace-contains("label 'x' is in segment 'data'.", msg)
1352     # . . push args
1353     68/push  "F - test-compute-offsets/4"/imm32
1354     68/push  "label 'x' is in segment 'data'."/imm32
1355     # . . call
1356     e8/call  check-trace-contains/disp32
1357     # . . discard args
1358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1359     # . check-trace-contains("label 'x' is at segment offset 0x00000001.", msg)
1360     # . . push args
1361     68/push  "F - test-compute-offsets/5"/imm32
1362     68/push  "label 'x' is at segment offset 0x00000001."/imm32
1363     # . . call
1364     e8/call  check-trace-contains/disp32
1365     # . . discard args
1366     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1367     # . check-ints-equal(labels->write, 0x10, msg)
1368     # . . push args
1369     68/push  "F - test-compute-offsets-maintains-labels-write-index"/imm32
1370     68/push  0x10/imm32/1-entry
1371     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
1372     # . . call
1373     e8/call  check-ints-equal/disp32
1374     # . . discard args
1375     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1376     # . epilogue
1377     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1378     5d/pop-to-ebp
1379     c3/return
1380 
1381 compute-addresses:  # segments : (addr stream {string, segment-info}), labels : (addr stream {string, label-info})
1382     # pseudocode:
1383     #   srow : (addr segment-info) = segments->data
1384     #   max = &segments->data[segments->write]
1385     #   num-segments = segments->write / 16
1386     #   starting-offset = 0x34 + (num-segments * 0x20)
1387     #   while true
1388     #     if (srow >= max) break
1389     #     s->file-offset += starting-offset
1390     #     s->address &= 0xfffff000  # clear last 12 bits for p_align
1391     #     s->address += (s->file-offset & 0x00000fff)
1392     #     trace-sssns("segment " s->key " starts at address " s->address)
1393     #     srow += 16  # row-size
1394     #   lrow : (addr label-info) = labels->data
1395     #   max = &labels->data[labels->write]
1396     #   while true
1397     #     if (lrow >= max) break
1398     #     seg-name : (addr string) = lrow->segment-name
1399     #     label-seg : (addr segment-info) = get(segments, seg-name)
1400     #     lrow->address = label-seg->address + lrow->segment-offset
1401     #     trace-sssns("label " lrow->key " is at address " lrow->address)
1402     #     lrow += 16  # row-size
1403     #
1404     # . prologue
1405     55/push-ebp
1406     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1407     # . save registers
1408     50/push-eax
1409     51/push-ecx
1410     52/push-edx
1411     53/push-ebx
1412     56/push-esi
1413     57/push-edi
1414     # esi = segments
1415     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
1416     # starting-offset/edi = 0x34 + (num-segments * 0x20)  # make room for ELF headers
1417     # . edi = segments->write / 16 (row-size)
1418     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           7/r32/edi   .               .                 # copy *esi to edi
1419     c1/shift    5/subop/logic-right 3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi right by 4 bits, while padding zeroes
1420     # . edi = (edi * 0x20) + 0x34
1421     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               5/imm8            # shift edi left by 5 bits
1422     81          0/subop/add         3/mod/direct    7/rm32/edi    .           .             .           .           .               0x34/imm32        # add to edi
1423     # srow/eax = segments->data
1424     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   0xc/disp8       .                 # copy esi+12 to eax
1425     # max/ecx = &segments->data[segments->write]
1426     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
1427     01/add                          3/mod/direct    1/rm32/ecx    .           .             .           6/r32/esi   .               .                 # add esi to ecx
1428 $compute-addresses:segment-loop:
1429     # if (srow >= max) break
1430     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
1431     73/jump-if-greater-or-equal-unsigned  $compute-addresses:segment-break/disp8
1432     # srow->file-offset += starting-offset
1433     01/add                          1/mod/*+disp8   0/rm32/eax    .           .             .           7/r32/edi   8/disp8         .                 # add edi to *(eax+8)
1434     # clear last 12 bits of srow->address for p_align=0x1000
1435     # . edx = srow->address
1436     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           2/r32/edx   4/disp8         .                 # copy *(eax+4) to edx
1437     # . edx &= 0xfffff000
1438     81          4/subop/and         3/mod/direct    2/rm32/edx    .           .             .           .           .               0xfffff000/imm32  # bitwise and of edx
1439     # update last 12 bits from srow->file-offset
1440     # . ebx = srow->file-offset
1441     8b/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           3/r32/ebx   8/disp8         .                 # copy *(eax+8) to ebx
1442     # . ebx &= 0xfff
1443     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x00000fff/imm32  # bitwise and of ebx
1444     # . srow->address = edx | ebx
1445     09/or                           3/mod/direct    2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # edx = bitwise OR with ebx
1446     89/copy                         1/mod/*+disp8   0/rm32/eax    .           .             .           2/r32/edx   4/disp8         .                 # copy edx to *(eax+4)
1447     # trace-sssns("segment " srow " starts at address " srow->address ".")
1448     # . . push args
1449     68/push  "."/imm32
1450     52/push-edx
1451     68/push  "' starts at address "/imm32
1452     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1453     68/push  "segment '"/imm32
1454     # . . call
1455     e8/call  trace-sssns/disp32
1456     # . . discard args
1457     81          0/subop/add         3/mo
;;; init-minibuffer.el --- Minibuffer Completion Configuration File -*- lexical-binding: t -*-
;;; Commentary:
;; Config for completion etc in the minibuffer (vertico, embark, consult, etc)
;; Most of it is taken from the READMEs and wikis of those packages.
;; Relies on orderless config in init-completion.el
;;; Code:

(use-package vertico
  :ensure (vertico :files (:defaults "extensions/*"))
  :hook (elpaca-after-init . vertico-mode)
  :custom (vertico-cycle t)
  :config
  ;; Do not allow the cursor in the minibuffer prompt
  (setq minibuffer-prompt-properties
        '(read-only t cursor-intangible t face minibuffer-prompt))
  (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode)
  (setq enable-recursive-minibuffers t)
  (minibuffer-depth-indicate-mode t)

  ;; Add prompt indicator to `completing-read-multiple'.
  ;; We display [CRM<separator>], e.g., [CRM,] if the separator is a comma.
  (defun crm-indicator (args)
    (cons (format "[CRM%s] %s"
                  (replace-regexp-in-string
                   "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                   crm-separator)
                  (car args))
          (cdr args)))
  (advice-add #'completing-read-multiple :filter-args #'crm-indicator)

  ;; Emacs 28: Hide commands in M-x which do not work in the current mode.
  ;; Vertico commands are hidden in normal buffers.
  (setq read-extended-command-predicate
        #'command-completion-default-include-p)

  (advice-add #'vertico--format-candidate :around
              (lambda (orig cand prefix suffix index start)
                (setq cand (funcall orig cand prefix suffix index start))
                (concat
                 (if (= vertico--index index)
                     (propertize "» " 'face 'vertico-current)
                   "  ")
                 cand)))

  (defun down-from-outside ()
    "Move to next candidate in minibuffer, even when minibuffer isn't selected."
    (interactive)
    (with-selected-window (active-minibuffer-window)
      (execute-kbd-macro [down])))

  (defun up-from-outside ()
    "Move to previous candidate in minibuffer, even when minibuffer isn't selected."
    (interactive)
    (with-selected-window (active-minibuffer-window)
      (execute-kbd-macro [up])))

  (defun preview-from-outside ()
    "Preview the selected candidate, even when minibuffer isn't selected."
    (interactive)
    (with-selected-window (active-minibuffer-window)
      (execute-kbd-macro (kbd "M-."))))

  (defun to-and-fro-minibuffer ()
    "Go back and forth between minibuffer and other window."
    (interactive)
    (if (window-minibuffer-p (selected-window))
        (select-window (minibuffer-selected-window))
      (select-window (active-minibuffer-window))))

  (defun minibuffer-really-quit ()
    "Quit minibuffer session, even if it is not the selected window."
    (interactive)
    (with-selected-window (active-minibuffer-window)
      (minibuffer-keyboard-quit)))

  :bind (("C-M-<" . up-from-outside)
         ("C-M->" . down-from-outside)
         ("C-M-+" . preview-from-outside)
         ("M-X" . to-and-fro-minibuffer)
         ("C-M-S-g" . minibuffer-really-quit)
         (:map vertico-map ("M-RET" . minibuffer-force-complete-and-exit))))

(use-extension vertico vertico-directory
  :config
  (defvar switching-project nil)
  (defun vertico-directory-enter-or-select-project ()
    "vertico-directory-enter wrapper that plays nicely with selecting new projects."
    (interactive)
    ;; When selecting a project, use this to return, instead of entering the directory
    (if switching-project
        (vertico-exit)
      (vertico-directory-enter)))
  (defun vertico-directory-slash ()
    (interactive)
    (if (and (>= vertico--index 0)
             (string-suffix-p "/" (vertico--candidate))
             (eq 'file (vertico--metadata-get 'category)))
        (vertico-insert)
      (insert "/")))
  (defun vertico-directory-home ()
    (interactive)
    (if (and (string-suffix-p "/" (vertico--candidate))
             (eq 'file (vertico--metadata-get 'category)))
        (insert "~/")
      (insert "~")))
  (defun read-project (orig &rest args)
    (let ((switching-project t))
      (apply orig args)))
  (advice-add 'project-prompt-project-dir :around
              'read-project)

  ;; TODO this should be part of the vertico config
  (defun define-vertico-key (key &rest defs)
    "Define KEY conditionally in the vertico keymap.
DEFS is a plist associating completion categories to commands."
    (let ((default-command (lookup-key vertico-map (kbd key))))
      (define-key vertico-map (kbd key)
        (list 'menu-item nil defs :filter
              (lambda (d)
                (or (plist-get d (completion-metadata-get
                                  (completion-metadata (minibuffer-contents)
                                                       minibuffer-completion-table
                                                       minibuffer-completion-predicate)
                                  'category))
                    default-command))))))
  (define-vertico-key "/"
    'file #'vertico-directory-slash
    'project-file #'vertico-directory-slash)
  (define-vertico-key "RET"
    'file #'vertico-directory-enter-or-select-project
    'project-file #'vertico-directory-enter)
  (define-vertico-key "~"
    'file #'vertico-directory-home)
  (define-vertico-key "DEL"
    'file #'vertico-directory-delete-char
    'project-file #'vertico-directory-delete-char)
  (define-vertico-key "M-DEL"
    'file #'vertico-directory-delete-word
    'project-file #'vertico-directory-delete-word)
  :commands (vertico-directory-enter vertico-directory-delete-word vertico-directory-delete-char)
  ;; Tidy shadowed file names
  :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))

(use-extension vertico vertico-repeat
  :after savehist
  :bind
  ("C-\\" . vertico-repeat)
  ("C-|" . vertico-repeat-select)
  :hook (minibuffer-setup . vertico-repeat-save)
  :config
  (add-to-list 'savehist-additional-variables 'vertico-repeat-history))

(use-extension vertico vertico-indexed
  :config
  (defmacro define-choose (n)
    `(defun ,(intern (format "vertico-indexed-choose-%s" n)) ()
       ,(format "Exit minibuffer with candidate %s." n)
       (interactive)
       (let ((vertico--index ,n))
         (funcall-interactively 'vertico-exit))))
  (defmacro define-insert (n)
    `(defun ,(intern (format "vertico-indexed-insert-%s" n)) ()
       ,(format "Insert candidate %s in minibuffer." n)
       (interactive)
       (let ((vertico--index ,n))
         (funcall-interactively 'vertico-insert))))
  (dotimes (n 10)
    (eval `(define-choose ,n))
    (eval `(define-insert ,n))
    (define-key vertico-map (kbd (format "C-%s" n)) (intern (format "vertico-indexed-choose-%s" n)))
    (define-key vertico-map (kbd (format "M-%s" n)) (intern (format "vertico-indexed-insert-%s" n))))
  (vertico-indexed-mode 1))

(use-extension vertico vertico-quick
  :bind (:map vertico-map
              ("M-;" . vertico-quick-insert)
              ("M-'" . vertico-quick-exit)))

(use-package consult
  :bind (;; C-c bindings (mode-specific-map)
         ("C-c h" . consult-history)
         ("C-c m" . consult-mode-command)
         ("C-c b" . consult-bookmark)
         ("C-c k" . consult-kmacro)
         ;; C-x bindings (ctl-x-map)
         ("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
         ("C-x b" . consult-buffer) ;; orig. switch-to-buffer
         ("C-x B" . consult-buffer-no-preview) ;; orig. switch-to-buffer
         ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window
         ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame
         ;; Custom M-# bindings for fast register access
         ("M-#" . consult-register-load)
         ("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
         ("C-M-#" . consult-register)
         ;; Other custom bindings
         ("C-S-s" . consult-line)
         ("M-*" . consult-line-thing-at-point)
         ("C-c f" . consult-recent-file)
         ("C-c r" . consult-ripgrep)
         ;; TODO find an alternative to C-c c?
         ("C-c c r" . consult-ripgrep-auto-preview)
         ("C-c c s" . consult-ripgrep-case-sensitive)
         ("C-c c z" . consult-z-ripgrep)
         ("C-c C-*" . consult-ripgrep-thing-at-point)
         ("C-c C-^" . consult-ripgrep-parent)
         ("M-y" . consult-yank-pop)     ;; orig. yank-pop
         ("<help> a" . consult-apropos) ;; orig. apropos-command
         ;; M-g bindings (goto-map)
         ("M-g e" . consult-compile-error)
         ("M-g f" . consult-flycheck)
         ("M-g g" . consult-goto-line)   ;; orig. goto-line
         ("M-g M-g" . consult-goto-line) ;; orig. goto-line
         ("M-g o" . consult-outline) ;; Alternative: consult-org-heading
         ("M-g m" . consult-mark)
         ("M-g k" . consult-global-mark)
         ("M-g i" . consult-imenu)
         ("M-g I" . consult-imenu-multi)
         (:map isearch-mode-map
               ("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
               ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
               ("M-s l" . consult-line)) ;; needed by consult-line to detect isearch
         (:map search-map
               ("f" . consult-fd)
               ("F" . consult-find)
               ("M-f" . consult-locate)
               ("g" . consult-grep)
               ("G" . consult-git-grep)
               ("r" . consult-ripgrep)
               ("R" . consult-ripgrep) ;; can't use r in isearch-mode, so add R too
               ("u" . consult-ripgrep-unrestricted)
               ("*" . consult-ripgrep-thing-at-point)
               ("z" . consult-z-ripgrep)
               ("^" . consult-ripgrep-parent)
               ("l" . consult-line)
               ("L" . consult-line-multi)
               ("m" . consult-multi-occur)
               ("k" . consult-keep-lines)
               ("C-f" . consult-focus-lines)
               ("e" . consult-isearch-history))
         (:map vertico-map
               ;; These are used for previewing with some consult commands (see consult-customize call below)
               ("C-S-p" . vertico-previous)
               ("C-S-n" . vertico-next)
               ;; Toggle preview on/off without changing preview-key
               ("M-P" . consult-toggle-preview)
               ("C-x C-M-x" . remove-leading-hash)))

  :init
  ;; Use Consult to select xref locations with preview
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)

  :config

  ;; Optionally configure the register formatting. This improves the register
  ;; preview for `consult-register', `consult-register-load',
  ;; `consult-register-store' and the Emacs built-ins.
  (setq register-preview-delay 0
        register-preview-function #'consult-register-format)

  ;; Optionally tweak the register preview window.
  ;; This adds thin lines, sorting and hides the mode line of the window.
  (advice-add #'register-preview :override #'consult-register-window)

  (add-to-list 'consult-mode-histories '(cider-repl-mode cider-repl-input-history))

  (defun consult-ripgrep-auto-preview (&optional dir initial)
    (interactive "P")
    (consult-ripgrep dir initial))
  (defun consult-ripgrep-unrestricted (&optional dir initial)
    (interactive "P")
    (let ((consult-ripgrep-args (replace-regexp-in-string "\\." "-uu ." consult-ripgrep-args)))
      (consult-ripgrep dir initial)))
  (defun consult-z-ripgrep (&optional dir initial)
    (interactive "P")
    (let ((consult-ripgrep-args (replace-regexp-in-string "\\." "-z ." consult-ripgrep-args)))
      (consult-ripgrep dir initial)))
  (defun consult-ripgrep-case-sensitive (&optional dir initial)
    (interactive "P")
    (let ((consult-ripgrep-args (replace-regexp-in-string "\\." "-s ." consult-ripgrep-args)))
      (consult-ripgrep dir initial)))
  (defun consult-buffer-no-preview ()
    (interactive)
    (consult-buffer))
  (defun consult-ripgrep-parent (&optional initial)
    (interactive "P")
    (consult-ripgrep (file-name-directory (directory-file-name (persp-current-project-root))) initial))

  (defalias 'consult-line-thing-at-point 'consult-line)
  (defalias 'consult-ripgrep-thing-at-point 'consult-ripgrep)

  (consult-customize
   consult-theme
   :preview-key '(:debounce 0.2 any)
   ;; For these commands we can use C-N/C-P to scroll and preview, or M-. to preview
   consult-git-grep consult-grep
   consult-ripgrep-parent consult-ripgrep consult-ripgrep-case-sensitive
   consult-ripgrep-unrestricted consult-z-ripgrep consult-ripgrep-thing-at-point
   consult-bookmark consult-recent-file consult-xref consult-buffer-no-preview
   consult--source-recent-file consult--source-project-recent-file consult--source-bookmark
   :preview-key '("M-." :debounce 0.2 "C-S-n" :debounce 0.2 "C-S-p")
   consult-ripgrep-thing-at-point
   :initial (concat "#" (thing-at-point 'symbol))
   consult-line-thing-at-point
   :initial (thing-at-point 'symbol))

  (defvar-local consult-toggle-preview-orig nil)
  (defun consult-toggle-preview ()
    "Command to enable/disable preview."
    (interactive)
    (if consult-toggle-preview-orig
        (setq consult--preview-function consult-toggle-preview-orig
              consult-toggle-preview-orig nil)
      (setq consult-toggle-preview-orig consult--preview-function
            consult--preview-function #'ignore)))

  (setq consult-narrow-key "<")
  (append-to-list* 'consult-buffer-filter
                   "^\\*helpful"
                   "^\\*Warnings\\*"
                   "^\\*cider-test-report\\*"
                   "^\\*cider-error\\*"
                   "^\\*cider-inspect\\*")

  (setq consult-project-function (lambda (_) (persp-current-project-root)))

  ;; Switches perspective if we select a buffer from another perspective, but note that previewing
  ;; a buffer adds it to the current perspective, so preview should be disabled before removing
  ;; perspective narrowing
  (defun consult--persp-buffer-action (orig &rest args)
    (when (not (cdr args)) ;; (cdr args) is norecord, which should distinguish preview/non-preview
      (let ((buffer (window-normalize-buffer-to-switch-to (car args))))
        (unless (persp-is-current-buffer buffer)
          (let ((other-persp (persp-buffer-in-other-p buffer)))
            (when (eq (car-safe other-persp) (selected-frame))
              (persp-switch (cdr other-persp)))))))
    (apply orig args))
  (advice-add 'consult--buffer-action :around 'consult--persp-buffer-action)

  (defvar consult-initial-narrow-config
    '((consult-buffer . ?x)
      (consult-buffer-no-preview . ?x)
      (consult-buffer-other-window . ?x)
      (consult-project-extra-find . ?f)))
  ;; Add initial narrowing hook
  (defun consult-initial-narrow ()
    (when-let (key (alist-get this-command consult-initial-narrow-config))
      (setq unread-command-events (append unread-command-events (list key 32)))))
  (add-hook 'minibuffer-setup-hook #'consult-initial-narrow)

  (defvar consult--source-perspective-buffer
    `(:name     "Perspective Buffer"
                :narrow   (?x . "Perspective")
                :hidden   t
                :category buffer
                :face     consult-buffer
                :history  buffer-name-history
                :state    ,#'consult--buffer-state
                :enabled  ,(lambda () persp-mode)
                :items
                ,(lambda ()
                   (consult--buffer-query :sort 'visibility
                                          :predicate #'persp-is-current-buffer
                                          :as #'buffer-name)))
    "Perspective buffer candidate source for `consult-buffer'.")
  (add-to-list 'consult-buffer-sources 'consult--source-perspective-buffer t)

  ;; Copy of consult--source-project-file to use with perspective narrowing (identical except for narrowing key)
  ;; Put before consult--source-project-file so we get recentf behaviour here
  (defvar consult--source-perspective-files
    (plist-put (plist-put (copy-sequence  consult--source-project-recent-file)
                          :name "Project File")
               :narrow '(?x . "Perspective")))
  (add-to-list 'consult-buffer-sources 'consult--source-perspective-files t)

  ;; Versions of consult--source-project-buffer and consult--source-project-file for use by consult-project-buffer
  ;; They allow narrowing with b, f and a (instead of p)
  ;; f is the recentf version provided by consult
  ;; a is an "all files" version based on fd (respecting .gitignore, hidden by default)
  (defvar consult--project-source-project-buffer
    (plist-put (plist-put (copy-sequence consult--source-project-buffer)
                          :hidden nil)
               :narrow '(?b . "Buffer")))
  (defvar consult--project-source-project-file-recentf
    (plist-put (plist-put (copy-sequence consult--source-project-recent-file)
                          :hidden nil)
               :narrow '(?f . "File (Recentf)")))
  (defvar consult--project-source-project-file-all
    (plist-put (plist-put (copy-sequence consult--source-project-recent-file)
                          :narrow '(?a . "File (All)"))
               :items '(lambda ()
                         (when (eq 0 (call-process-shell-command "fd"))
                           (when-let (root (consult--project-root))
                             (let ((len (length root))
                                   (inv-root (propertize root 'invisible t)))
                               (mapcar (lambda (x)
                                         (concat inv-root (substring x len)))
                                       (split-string
                                        (shell-command-to-string
                                         (format  "fd --color never -t f -0 . %s" root))
                                        "\0" t))))))))

  (defun remove-leading-hash ()
    "Remove a # character from the beginning of the current line.

Designed to be used for consult commands that automatically add a # at the beginning of the minibuffer.
See `+become' and the functions that call it (e.g. `+become-consult-line')."
    (interactive)
    (save-excursion
      (beginning-of-line)
      (when (= ?# (char-after))
        (delete-forward-char 1))))

  (defun consult-project-buffer ()
    "Version of `consult-buffer' that only uses project-related sources."
    (interactive)
    (let ((consult-buffer-sources '(consult--project-source-project-buffer
                                    consult--project-source-project-file-recentf
                                    consult--project-source-project-file-all)))
      (consult-buffer))))

(use-package consult-flycheck)

(use-package consult-lsp
  :bind (:map lsp-mode-map
         ([remap xref-find-apropos] . consult-lsp-symbols)))

(use-package consult-dir
  :bind (("C-x C-d" . consult-dir)
         :map vertico-map
         ("C-x C-d" . consult-dir)
         ("C-x C-j" . consult-dir-jump-file)))

(use-package consult-git-log-grep
  :bind ("C-c g l" . consult-git-log-grep)
  :custom (consult-git-log-grep-open-function #'magit-show-commit))

(use-package consult-ls-git
  :bind ("C-c g f" . consult-ls-git))

(use-package consult-project-extra)

(use-package marginalia
  :hook (elpaca-after-init . marginalia-mode)
  :config
  ;; crux-recentf-find-file
  (add-to-list 'marginalia-prompt-categories '("Choose recent file" . file)))

(use-package embark
  :bind
  (("C-." . embark-act)
   ([remap xref-find-definitions] . embark-dwim)
   ("C-c C-o" . embark-export)
   ("C-h b" . embark-bindings)
   ("C-h B" . describe-bindings)
   (:map minibuffer-local-map
         ("M-." . embark-preview)
         ("C-," . embark-become))
   (:map embark-become-file+buffer-map
         ("e" . consult-project-extra-find)
         ("E" . project-switch-consult-project-extra-find)))
  :custom
  (prefix-help-command 'embark-prefix-help-command)
  :config
  (defun embark-preview ()
    "Previews candidate in vertico buffer, unless it's a consult command"
    (interactive)
    (unless (bound-and-true-p consult--preview-function)
      (save-selected-window
        (let ((embark-quit-after-action nil))
          (embark-dwim)))))

  ;; Hide the mode line of the Embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

(use-package embark-consult
  :after (embark consult)
  ;; demand, combined with after means that this will load after embark and consult
  ;; See https://github.com/oantolin/embark/commit/47daded610b245caf01a97d74c940aff91fe14e2#r46010972
  :demand t
  :config
  (defun +become (fn)
    "Remove the leading # from the minibuffer, and call `FN'.
Useful with embark-become, when changing from a command that uses # as a separator, to one that doesn't."
    (interactive)
    (setq unread-command-events (listify-key-sequence "\C-x\C-\M-x"))
    (funcall-interactively fn))
  (defun +become-consult-line ()
    "A version of `consult-line', designed for use with `embark-become'.
The leading # added by other consult commands is removed."
    (interactive)
    (+become #'consult-line))
  (defun +become-consult-line ()
    "A version of `consult-imenu', designed for use with `embark-become'.
The leading # added by other consult commands is removed."
    (interactive)
    (+become #'consult-imenu))
  :bind
  (:map embark-consult-async-search-map
        ("l" . +become-consult-line)
        ("f" . consult-focus-lines)
        ("i" . +become-consult-imenu)
        ("^" . consult-ripgrep-parent)
        ("u" . consult-ripgrep-unrestricted)
        ("c" . consult-ripgrep-case-sensitive)
        ("z" . consult-z-ripgrep))
  :hook
  (embark-collect-mode . consult-preview-at-point-mode))

(use-package consult-todo
  ;; TODO use consult-todo-project when it works
  :bind ("C-c c t t" . consult-todo))

(provide 'init-minibuffer)
;;; init-minibuffer.el ends here
;/imm32 2422 52/push-edx 2423 # . . call 2424 e8/call has-metadata?/disp32 2425 # . . discard args 2426 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2427 # . if (eax == false) abort 2428 3d/compare-eax-and 0/imm32/false 2429 0f 84/jump-if-equal $emit-segments:abort/disp32 2430 $emit-segments:emit-code-label-disp32: 2431 # emit-hex(out, info->offset - offset-of-next-instruction, 4) 2432 # . . push args 2433 68/push 4/imm32 2434 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax 2435 29/subtract 3/mod/direct 0/rm32/eax . . . 3/r32/ebx . . # subtract ebx from eax 2436 50/push-eax 2437 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 2438 # . . call 2439 e8/call emit-hex/disp32 2440 # . . discard args 2441 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 2442 # continue 2443 e9/jump $emit-segments:word-loop/disp32 2444 $emit-segments:next-line: 2445 # write-buffered(out, "\n") 2446 # . . push args 2447 68/push Newline/imm32 2448 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 2449 # . . call 2450 e8/call write-buffered/disp32 2451 # . . discard args 2452 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2453 # loop 2454 e9/jump $emit-segments:line-loop/disp32 2455 $emit-segments:end: 2456 # . reclaim locals 2457 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x21c/imm32 # add to esp 2458 # . restore registers 2459 5f/pop-to-edi 2460 5e/pop-to-esi 2461 5b/pop-to-ebx 2462 5a/pop-to-edx 2463 59/pop-to-ecx 2464 58/pop-to-eax 2465 # . epilogue 2466 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 2467 5d/pop-to-ebp 2468 c3/return 2469 2470 $emit-segments:global-variable-abort: 2471 # . _write(2/stderr, error) 2472 # . . push args 2473 68/push "emit-segments: must refer to global variables with /disp32 or /imm32"/imm32 2474 68/push 2/imm32/stderr 2475 # . . call 2476 e8/call _write/disp32 2477 # . . discard args 2478 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2479 # . syscall(exit, 1) 2480 bb/copy-to-ebx 1/imm32 2481 b8/copy-to-eax 1/imm32/exit 2482 cd/syscall 0x80/imm8 2483 # never gets here 2484 2485 $emit-segments:imm8-abort: 2486 # . _write(2/stderr, error) 2487 # . . push args 2488 68/push "emit-segments: cannot refer to code labels with /imm8"/imm32 2489 68/push 2/imm32/stderr 2490 # . . call 2491 e8/call _write/disp32 2492 # . . discard args 2493 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2494 # . syscall(exit, 1) 2495 bb/copy-to-ebx 1/imm32 2496 b8/copy-to-eax 1/imm32/exit 2497 cd/syscall 0x80/imm8 2498 # never gets here 2499 2500 $emit-segments:abort: 2501 # print(stderr, "missing metadata in " word-slice) 2502 # . _write(2/stderr, "missing metadata in word ") 2503 # . . push args 2504 68/push "emit-segments: missing metadata in "/imm32 2505 68/push 2/imm32/stderr 2506 # . . call 2507 e8/call _write/disp32 2508 # . . discard args 2509 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2510 # . write-slice-buffered(Stderr, word-slice) 2511 # . . push args 2512 52/push-edx 2513 68/push Stderr/imm32 2514 # . . call 2515 e8/call write-slice-buffered/disp32 2516 # . . discard args 2517 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2518 # . flush(Stderr) 2519 # . . push args 2520 68/push Stderr/imm32 2521 # . . call 2522 e8/call flush/disp32 2523 # . . discard args 2524 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2525 # . syscall(exit, 1) 2526 bb/copy-to-ebx 1/imm32 2527 b8/copy-to-eax 1/imm32/exit 2528 cd/syscall 0x80/imm8 2529 # never gets here 2530 2531 test-emit-segments-global-variable: 2532 # global variables always convert to absolute addresses, regardless of metadata 2533 # 2534 # input: 2535 # in: 2536 # == code 0x1000 2537 # ab cd ef gh 2538 # ij x/disp32 2539 # == data 0x2000 2540 # 00 2541 # x: 2542 # 34 2543 # segments: 2544 # - 'code': {0x1074, 0, 9} 2545 # - 'data': {0x2079, 5, 2} 2546 # labels: 2547 # - 'x': {'data', 1, 0x207a} 2548 # 2549 # output: 2550 # ab cd ef gh 2551 # ij 7a 20 00 00 2552 # 00 2553 # 34 2554 # 2555 # . prologue 2556 55/push-ebp 2557 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 2558 # setup 2559 # . clear-stream(_test-input-stream) 2560 # . . push args 2561 68/push _test-input-stream/imm32 2562 # . . call 2563 e8/call clear-stream/disp32 2564 # . . discard args 2565 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2566 # . clear-stream(_test-output-stream) 2567 # . . push args 2568 68/push _test-output-stream/imm32 2569 # . . call 2570 e8/call clear-stream/disp32 2571 # . . discard args 2572 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2573 # . clear-stream($_test-output-buffered-file->buffer) 2574 # . . push args 2575 68/push $_test-output-buffered-file->buffer/imm32 2576 # . . call 2577 e8/call clear-stream/disp32 2578 # . . discard args 2579 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2580 # . var segments/ecx : (ref stream byte 10*16) 2581 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xa0/imm32 # subtract from esp 2582 68/push 0xa0/imm32/length 2583 68/push 0/imm32/read 2584 68/push 0/imm32/write 2585 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 2586 # . var labels/edx : (ref stream byte 512*16) 2587 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x2000/imm32 # subtract from esp 2588 68/push 0x2000/imm32/length 2589 68/push 0/imm32/read 2590 68/push 0/imm32/write 2591 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx 2592 # initialize input 2593 # . write(_test-input-stream, "== code 0x1000\n") 2594 # . . push args 2595 68/push "== code 0x1000\n"/imm32 2596 68/push _test-input-stream/imm32 2597 # . . call 2598 e8/call write/disp32 2599 # . . discard args 2600 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2601 # . write(_test-input-stream, "ab cd ef gh\n") 2602 # . . push args 2603 68/push "ab cd ef gh\n"/imm32 2604 68/push _test-input-stream/imm32 2605 # . . call 2606 e8/call write/disp32 2607 # . . discard args 2608 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2609 # . write(_test-input-stream, "ij x/disp32\n") 2610 # . . push args 2611 68/push "ij x/disp32\n"/imm32 2612 68/push _test-input-stream/imm32 2613 # . . call 2614 e8/call write/disp32 2615 # . . discard args 2616 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2617 # . write(_test-input-stream, "== data 0x2000\n") 2618 # . . push args 2619 68/push "== data 0x2000\n"/imm32 2620 68/push _test-input-stream/imm32 2621 # . . call 2622 e8/call write/disp32 2623 # . . discard args 2624 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2625 # . write(_test-input-stream, "00\n") 2626 # . . push args 2627 68/push "00\n"/imm32 2628 68/push _test-input-stream/imm32 2629 # . . call 2630 e8/call write/disp32 2631 # . . discard args 2632 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2633 # . write(_test-input-stream, "x:\n") 2634 # . . push args 2635 68/push "x:\n"/imm32 2636 68/push _test-input-stream/imm32 2637 # . . call 2638 e8/call write/disp32 2639 # . . discard args 2640 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2641 # . write(_test-input-stream, "34\n") 2642 # . . push args 2643 68/push "34\n"/imm32 2644 68/push _test-input-stream/imm32 2645 # . . call 2646 e8/call write/disp32 2647 # . . discard args 2648 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2649 # . stream-add4(segments, "code", 0x1074, 0, 9) 2650 68/push 9/imm32/segment-size 2651 68/push 0/imm32/file-offset 2652 68/push 0x1074/imm32/start-address 2653 68/push "code"/imm32/segment-name 2654 51/push-ecx 2655 # . . call 2656 e8/call stream-add4/disp32 2657 # . . discard args 2658 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 2659 # . stream-add4(segments, "data", 0x2079, 5, 2) 2660 68/push 1/imm32/segment-size 2661 68/push 5/imm32/file-offset 2662 68/push 0x2079/imm32/start-address 2663 68/push "data"/imm32/segment-name 2664 51/push-ecx 2665 # . . call 2666 e8/call stream-add4/disp32 2667 # . . discard args 2668 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 2669 # . stream-add4(labels, "x", "data", 1, 0x207a) 2670 68/push 0x207a/imm32/label-address 2671 68/push 1/imm32/segment-offset 2672 68/push "data"/imm32/segment-name 2673 68/push "x"/imm32/label-name 2674 52/push-edx 2675 # . . call 2676 e8/call stream-add4/disp32 2677 # . . discard args 2678 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 2679 # component under test 2680 # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels) 2681 # . . push args 2682 52/push-edx 2683 51/push-ecx 2684 68/push _test-output-buffered-file/imm32 2685 68/push _test-input-stream/imm32 2686 # . . call 2687 e8/call emit-segments/disp32 2688 # . . discard args 2689 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 2690 # checks 2691 # . flush(_test-output-buffered-file) 2692 # . . push args 2693 68/push _test-output-buffered-file/imm32 2694 # . . call 2695 e8/call flush/disp32 2696 # . . discard args 2697 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2698 +-- 33 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- 2731 # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg) 2732 # . . push args 2733 68/push "F - test-emit-segments-global-variable/0"/imm32 2734 68/push "ab cd ef gh "/imm32 2735 68/push _test-output-stream/imm32 2736 # . . call 2737 e8/call check-next-stream-line-equal/disp32 2738 # . . discard args 2739 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 2740 # . check-next-stream-line-equal(_test-output-stream, "ij 7a 20 00 00 ", msg) 2741 # . . push args 2742 68/push "F - test-emit-segments-global-variable/1"/imm32 2743 68/push "ij 7a 20 00 00 "/imm32 2744 68/push _test-output-stream/imm32 2745 # . . call 2746 e8/call check-next-stream-line-equal/disp32 2747 # . . discard args 2748 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 2749 # . check-next-stream-line-equal(_test-output-stream, "00 ", msg) 2750 # . . push args 2751 68/push "F - test-emit-segments-global-variable/2"/imm32 2752 68/push "00 "/imm32 2753 68/push _test-output-stream/imm32 2754 # . . call 2755 e8/call check-next-stream-line-equal/disp32 2756 # . . discard args 2757 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 2758 # . check-next-stream-line-equal(_test-output-stream, "34 ", msg) 2759 # . . push args 2760 68/push "F - test-emit-segments-global-variable/3"/imm32 2761 68/push "34 "/imm32 2762 68/push _test-output-stream/imm32 2763 # . . call 2764 e8/call check-next-stream-line-equal/disp32 2765 # . . discard args 2766 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 2767 # . epilogue 2768 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 2769 5d/pop-to-ebp 2770 c3/return 2771 2772 test-emit-segments-code-label: 2773 # labels usually convert to displacements 2774 # 2775 # input: 2776 # in: 2777 # == code 0x1000 2778 # ab cd 2779 # l1: 2780 # ef gh 2781 # ij l1/disp32 2782 # segments: 2783 # - 'code': {0x1054, 0, 9} 2784 # labels: 2785 # - 'l1': {'code', 2, 0x1056} 2786 # 2787 # output: 2788 # ab cd 2789 # ef gh 2790 # ij f9 ff ff ff # -7 2791 # 2792 # . prologue 2793 55/push-ebp 2794 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 2795 # setup 2796 # . clear-stream(_test-input-stream) 2797 # . . push args 2798 68/push _test-input-stream/imm32 2799 # . . call 2800 e8/call clear-stream/disp32 2801 # . . discard args 2802 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2803 # . clear-stream(_test-output-stream) 2804 # . . push args 2805 68/push _test-output-stream/imm32 2806 # . . call 2807 e8/call clear-stream/disp32 2808 # . . discard args 2809 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2810 # . clear-stream($_test-output-buffered-file->buffer) 2811 # . . push args 2812 68/push $_test-output-buffered-file->buffer/imm32 2813 # . . call 2814 e8/call clear-stream/disp32 2815 # . . discard args 2816 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2817 # . var segments/ecx : (ref stream byte 10*16) 2818 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xa0/imm32 # subtract from esp 2819 68/push 0xa0/imm32/length 2820 68/push 0/imm32/read 2821 68/push 0/imm32/write 2822 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 2823 # . var labels/edx : (ref stream byte 512*16) 2824 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x2000/imm32 # subtract from esp 2825 68/push 0x2000/imm32/length 2826 68/push 0/imm32/read 2827 68/push 0/imm32/write 2828 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx 2829 # initialize input 2830 # . write(_test-input-stream, "== code 0x1000\n") 2831 # . . push args 2832 68/push "== code 0x1000\n"/imm32 2833 68/push _test-input-stream/imm32 2834 # . . call 2835 e8/call write/disp32 2836 # . . discard args 2837 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2838 # . write(_test-input-stream, "ab cd\n") 2839 # . . push args 2840 68/push "ab cd\n"/imm32 2841 68/push _test-input-stream/imm32 2842 # . . call 2843 e8/call write/disp32 2844 # . . discard args 2845 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2846 # . write(_test-input-stream, "l1:\n") 2847 # . . push args 2848 68/push "l1:\n"/imm32 2849 68/push _test-input-stream/imm32 2850 # . . call 2851 e8/call write/disp32 2852 # . . discard args 2853 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2854 # . write(_test-input-stream, " ef gh\n") 2855 # . . push args 2856 68/push " ef gh\n"/imm32 2857 68/push _test-input-stream/imm32 2858 # . . call 2859 e8/call write/disp32 2860 # . . discard args 2861 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2862 # . write(_test-input-stream, " ij l1/disp32\n") 2863 # . . push args 2864 68/push " ij l1/disp32\n"/imm32 2865 68/push _test-input-stream/imm32 2866 # . . call 2867 e8/call write/disp32 2868 # . . discard args 2869 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 2870 # . stream-add4(segments, "code", 0x1054, 0, 9) 2871 68/push 9/imm32/segment-size 2872 68/push 0/imm32/file-offset 2873 68/push 0x1054/imm32/start-address 2874 68/push "code"/imm32/segment-name 2875 51/push-ecx 2876 # . . call 2877 e8/call stream-add4/disp32 2878 # . . discard args 2879 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 2880 # . stream-add4(labels, "l1", "code", 2, 0x1056) 2881 68/push 0x1056/imm32/label-address 2882 68/push 2/imm32/segment-offset 2883 68/push "code"/imm32/segment-name 2884 68/push "l1"/imm32/label-name 2885 52/push-edx 2886 # . . call 2887 e8/call stream-add4/disp32 2888 # . . discard args 2889 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 2890 # component under test 2891 # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels) 2892 # . . push args 2893 52/push-edx 2894 51/push-ecx 2895 68/push _test-output-buffered-file/imm32 2896 68/push _test-input-stream/imm32 2897 # . . call 2898 e8/call emit-segments/disp32 2899 # . . discard args 2900 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 2901 # checks 2902 # . flush(_test-output-buffered-file) 2903 # . . push args 2904 68/push _test-output-buffered-file/imm32 2905 # . . call 2906 e8/call flush/disp32 2907 # . . discard args 2908 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 2909 +-- 33 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- 2942 # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg) 2943 # . . push args 2944 68/push "F - test-emit-segments-code-label/0"/imm32 2945 68/push "ab cd "/imm32 2946 68/push _test-output-stream/imm32 2947 # . . call 2948 e8/call check-next-stream-line-equal/disp32 2949 # . . discard args 2950 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 2951 # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg) 2952 # . . push args 2953 68/push "F - test-emit-segments-code-label/1"/imm32 2954 68/push "ef gh "/imm32 2955 68/push _test-output-stream/imm32 2956 # . . call 2957 e8/call check-next-stream-line-equal/disp32 2958 # . . discard args 2959 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 2960 # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg) 2961 # . . push args 2962 68/push "F - test-emit-segments-code-label/2"/imm32 2963 68/push "ij f9 ff ff ff "/imm32 2964 68/push _test-output-stream/imm32 2965 # . . call 2966 e8/call check-next-stream-line-equal/disp32 2967 # . . discard args 2968 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 2969 # . epilogue 2970 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 2971 5d/pop-to-ebp 2972 c3/return 2973 2974 test-emit-segments-code-label-absolute: 2975 # labels can also convert to absolute addresses 2976 # 2977 # input: 2978 # in: 2979 # == code 0x1000 2980 # ab cd 2981 # l1: 2982 # ef gh 2983 # ij l1/imm32 2984 # segments: 2985 # - 'code': {0x1054, 0, 9} 2986 # labels: 2987 # - 'l1': {'code', 2, 0x1056} 2988 # 2989 # output: 2990 # ab cd 2991 # ef gh 2992 # ij 56 10 00 00 2993 # 2994 # . prologue 2995 55/push-ebp 2996 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 2997 # setup 2998 # . clear-stream(_test-input-stream) 2999 # . . push args 3000 68/push _test-input-stream/imm32 3001 # . . call 3002 e8/call clear-stream/disp32 3003 # . . discard args 3004 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 3005 # . clear-stream(_test-output-stream) 3006 # . . push args 3007 68/push _test-output-stream/imm32 3008 # . . call 3009 e8/call clear-stream/disp32 3010 # . . discard args 3011 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 3012 # . clear-stream($_test-output-buffered-file->buffer) 3013 # . . push args 3014 68/push $_test-output-buffered-file->buffer/imm32 3015 # . . call 3016 e8/call clear-stream/disp32 3017 # . . discard args 3018 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 3019 # . var segments/ecx : (ref stream byte 10*16) 3020 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0xa0/imm32 # subtract from esp 3021 68/push 0xa0/imm32/length 3022 68/push 0/imm32/read 3023 68/push 0/imm32/write 3024 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 3025 # . var labels/edx : (ref stream byte 512*16) 3026 81 5/subop/subtract 3/mod/direct 4/rm32/esp . . . . . 0x2000/imm32 # subtract from esp 3027 68/push 0x2000/imm32/length 3028 68/push 0/imm32/read 3029 68/push 0/imm32/write 3030 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx 3031 # initialize input 3032 # . write(_test-input-stream, "== code 0x1000\n") 3033 # . . push args 3034 68/push "== code 0x1000\n"/imm32 3035 68/push _test-input-stream/imm32 3036 # . . call 3037 e8/call write/disp32 3038 # . . discard args 3039 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3040 # . write(_test-input-stream, "ab cd\n") 3041 # . . push args 3042 68/push "ab cd\n"/imm32 3043 68/push _test-input-stream/imm32 3044 # . . call 3045 e8/call write/disp32 3046 # . . discard args 3047 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3048 # . write(_test-input-stream, "l1:\n") 3049 # . . push args 3050 68/push "l1:\n"/imm32 3051 68/push _test-input-stream/imm32 3052 # . . call 3053 e8/call write/disp32 3054 # . . discard args 3055 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3056 # . write(_test-input-stream, " ef gh\n") 3057 # . . push args 3058 68/push " ef gh\n"/imm32 3059 68/push _test-input-stream/imm32 3060 # . . call 3061 e8/call write/disp32 3062 # . . discard args 3063 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3064 # . write(_test-input-stream, " ij l1/imm32\n") 3065 # . . push args 3066 68/push " ij l1/imm32\n"/imm32 3067 68/push _test-input-stream/imm32 3068 # . . call 3069 e8/call write/disp32 3070 # . . discard args 3071 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3072 # . stream-add4(segments, "code", 0x1054, 0, 9) 3073 68/push 9/imm32/segment-size 3074 68/push 0/imm32/file-offset 3075 68/push 0x1054/imm32/start-address 3076 68/push "code"/imm32/segment-name 3077 51/push-ecx 3078 # . . call 3079 e8/call stream-add4/disp32 3080 # . . discard args 3081 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 3082 # . stream-add4(labels, "l1", "code", 2, 0x1056) 3083 68/push 0x1056/imm32/label-address 3084 68/push 2/imm32/segment-offset 3085 68/push "code"/imm32/segment-name 3086 68/push "l1"/imm32/label-name 3087 52/push-edx 3088 # . . call 3089 e8/call stream-add4/disp32 3090 # . . discard args 3091 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 3092 # component under test 3093 # . emit-segments(_test-input-stream, _test-output-buffered-file, segments, labels) 3094 # . . push args 3095 52/push-edx 3096 51/push-ecx 3097 68/push _test-output-buffered-file/imm32 3098 68/push _test-input-stream/imm32 3099 # . . call 3100 e8/call emit-segments/disp32 3101 # . . discard args 3102 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 3103 # checks 3104 # . flush(_test-output-buffered-file) 3105 # . . push args 3106 68/push _test-output-buffered-file/imm32 3107 # . . call 3108 e8/call flush/disp32 3109 # . . discard args 3110 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 3111 +-- 33 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- 3144 # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg) 3145 # . . push args 3146 68/push "F - test-emit-segments-code-label-absolute/0"/imm32 3147 68/push "ab cd "/imm32 3148 68/push _test-output-stream/imm32 3149 # . . call 3150 e8/call check-next-stream-line-equal/disp32 3151 # . . discard args 3152 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 3153 # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg) 3154 # . . push args 3155 68/push "F - test-emit-segments-code-label-absolute/1"/imm32 3156 68/push "ef gh "/imm32 3157 68/push _test-output-stream/imm32 3158 # . . call 3159 e8/call check-next-stream-line-equal/disp32 3160 # . . discard args 3161 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 3162 # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg) 3163 # . . push args 3164 68/push "F - test-emit-segments-code-label-absolute/2"/imm32 3165 68/push "ij 56 10 00 00 "/imm32 3166 68/push _test-output-stream/imm32 3167 # . . call 3168 e8/call check-next-stream-line-equal/disp32 3169 # . . discard args 3170 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 3171 # . epilogue 3172 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3173 5d/pop-to-ebp 3174 c3/return 3175 3176 emit-headers: # out : (addr buffered-file), segments : (addr stream {string, segment-info}), labels : (addr stream {string, label-info}) 3177 # pseudocode: 3178 # emit-elf-header(out, segments, labels) 3179 # curr-segment = segments->data 3180 # max = &segments->data[segments->write] 3181 # while true 3182 # if (curr-segment >= max) break 3183 # emit-elf-program-header-entry(out, curr-segment) 3184 # curr-segment += 16 # size of a row 3185 # 3186 # . prologue 3187 55/push-ebp 3188 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3189 # . save registers 3190 50/push-eax 3191 51/push-ecx 3192 +-- 9 lines: #? # write(2/stderr, "emit-elf-header\n") -------------------------------------------------------------------------------------------------- 3201 # emit-elf-header(out, segments, labels) 3202 # . . push args 3203 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 3204 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 3205 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3206 # . . call 3207 e8/call emit-elf-header/disp32 3208 # . . discard args 3209 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 3210 # eax = segments 3211 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax 3212 # ecx = segments->write 3213 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 3214 # curr-segment/eax = segments->data 3215 8d/copy-address 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 0xc/disp8 . # copy eax+12 to eax 3216 # max/ecx = &segments->data[segments->write] 3217 01/add 3/mod/direct 1/rm32/ecx . . . 0/r32/eax . . # add eax to ecx 3218 $emit-headers:loop: 3219 # if (curr-segment >= max) break 3220 39/compare 3/mod/direct 0/rm32/eax . . . 1/r32/ecx . . # compare eax with ecx 3221 0f 83/jump-if-greater-or-equal-unsigned $emit-headers:end/disp32 3222 +-- 63 lines: #? # dump curr-segment->name --------------------------------------------------------------------------------------------------------------- 3285 +-- 9 lines: #? # write(2/stderr, "emit-segment-header\n") ---------------------------------------------------------------------------------------------- 3294 # emit-elf-program-header-entry(out, curr-segment) 3295 # . . push args 3296 50/push-eax 3297 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3298 # . . call 3299 e8/call emit-elf-program-header-entry/disp32 3300 # . . discard args 3301 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3302 # curr-segment += 16 # size of a row 3303 81 0/subop/add 3/mod/direct 0/rm32/eax . . . . . 0x10/imm32 # add to eax 3304 e9/jump $emit-headers:loop/disp32 3305 $emit-headers:end: 3306 # . restore registers 3307 59/pop-to-ecx 3308 58/pop-to-eax 3309 # . epilogue 3310 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3311 5d/pop-to-ebp 3312 c3/return 3313 3314 emit-elf-header: # out : (addr buffered-file), segments : (addr stream {string, segment-info}), labels : (addr stream {string, label-info}) 3315 # pseudocode 3316 # *$Elf_e_entry = get(labels, "Entry")->address 3317 # *$Elf_e_phnum = segments->write / 16 # size of a row 3318 # emit-hex-array(out, Elf_header) 3319 # write-buffered(out, "\n") 3320 # 3321 # . prologue 3322 55/push-ebp 3323 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3324 # . save registers 3325 50/push-eax 3326 51/push-ecx 3327 52/push-edx # just because we need to call idiv 3328 # *$Elf_e_entry = get(labels, "Entry")->address 3329 # . eax = labels 3330 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0x10/disp8 . # copy *(ebp+16) to eax 3331 # . label-info/eax = get(labels, "Entry", row-size=16, "label table") 3332 # . . push args 3333 68/push "label table"/imm32 3334 68/push 0x10/imm32/row-size 3335 68/push "Entry"/imm32 3336 50/push-eax 3337 # . . call 3338 e8/call get/disp32 3339 # . . discard args 3340 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x10/imm32 # add to esp 3341 # . eax = label-info->address 3342 8b/copy 1/mod/*+disp8 0/rm32/eax . . . 0/r32/eax 8/disp8 . # copy *(eax+8) to eax 3343 # . *$Elf_e_entry = eax 3344 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_e_entry/disp32 # copy eax to *$Elf_e_entry 3345 # *$Elf_e_phnum = segments->write / 0x10 3346 # . eax = segments 3347 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 0/r32/eax 0xc/disp8 . # copy *(ebp+12) to eax 3348 # . len/eax = segments->write 3349 8b/copy 0/mod/indirect 0/rm32/eax . . . 0/r32/eax . . # copy *eax to eax 3350 # . eax = len / 0x10 (destroying edx) 3351 b9/copy-to-ecx 0x10/imm32 3352 31/xor 3/mod/direct 2/rm32/edx . . . 2/r32/edx . . # clear edx 3353 f7 7/subop/idiv 3/mod/direct 1/rm32/ecx . . . . . . # divide edx:eax by ecx, storing quotient in eax and remainder in edx 3354 # . *$Elf_e_phnum = eax 3355 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_e_phnum/disp32 # copy eax to *$Elf_e_phnum 3356 # emit-hex-array(out, Elf_header) 3357 # . . push args 3358 68/push Elf_header/imm32 3359 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3360 # . . call 3361 e8/call emit-hex-array/disp32 3362 # . . discard args 3363 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3364 # write-buffered(out, "\n") 3365 # . . push args 3366 68/push Newline/imm32 3367 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3368 # . . call 3369 e8/call write-buffered/disp32 3370 # . . discard args 3371 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3372 $emit-elf-header:end: 3373 # . restore registers 3374 5a/pop-to-edx 3375 59/pop-to-ecx 3376 58/pop-to-eax 3377 # . epilogue 3378 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3379 5d/pop-to-ebp 3380 c3/return 3381 3382 emit-elf-program-header-entry: # out : (addr buffered-file), curr-segment : (addr {string, segment-info}) 3383 # pseudocode: 3384 # *$Elf_p_offset = curr-segment->file-offset 3385 # *$Elf_p_vaddr = curr-segment->address 3386 # *$Elf_p_paddr = curr-segment->address 3387 # *$Elf_p_filesz = curr-segment->size 3388 # *$Elf_p_memsz = curr-segment->size 3389 # if curr-segment->name == "code" 3390 # *$Elf_p_flags = 5 # r-x 3391 # else 3392 # *$Elf_p_flags = 6 # rw- 3393 # emit-hex-array(out, Elf_program_header_entry) 3394 # write-buffered(out, "\n") 3395 # 3396 # . prologue 3397 55/push-ebp 3398 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3399 # . save registers 3400 50/push-eax 3401 56/push-esi 3402 # esi = curr-segment 3403 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 0xc/disp8 . # copy *(ebp+12) to esi 3404 # *$Elf_p_offset = curr-segment->file-offset 3405 # . eax = curr-segment->file-offset 3406 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 8/disp8 . # copy *(esi+8) to eax 3407 # . *$Elf_p_offset = eax 3408 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_offset/disp32 # copy eax to *$Elf_p_offset 3409 # *$Elf_p_vaddr = curr-segment->address 3410 # . eax = curr-segment->address 3411 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 4/disp8 . # copy *(esi+4) to eax 3412 # . *$Elf_p_vaddr = eax 3413 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_vaddr/disp32 # copy eax to *$Elf_p_vaddr 3414 # *$Elf_p_paddr = curr-segment->address 3415 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_paddr/disp32 # copy eax to *$Elf_p_paddr 3416 # *$Elf_p_filesz = curr-segment->size 3417 # . eax = curr-segment->size 3418 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 0/r32/eax 0xc/disp8 . # copy *(esi+12) to eax 3419 # . *$Elf_p_filesz = eax 3420 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_filesz/disp32 # copy eax to *$Elf_p_filesz 3421 # *$Elf_p_memsz = curr-segment->size 3422 89/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax $Elf_p_memsz/disp32 # copy eax to *$Elf_p_memsz 3423 # if (!string-equal?(curr-segment->name, "code") goto next check 3424 # . eax = curr-segment->name 3425 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax 3426 # . eax = string-equal?(curr-segment->name, "code") 3427 # . . push args 3428 68/push "code"/imm32 3429 50/push-eax 3430 # . . call 3431 e8/call string-equal?/disp32 3432 # . . discard args 3433 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3434 # . if (eax == false) goto next check 3435 3d/compare-eax-and 0/imm32/false 3436 74/jump-if-equal $emit-elf-program-header-entry:data/disp8 3437 # *$Elf_p_flags = r-x 3438 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . $Elf_p_flags/disp32 5/imm32 # copy to *$Elf_p_flags 3439 eb/jump $emit-elf-program-header-entry:really-emit/disp8 3440 $emit-elf-program-header-entry:data: 3441 # otherwise *$Elf_p_flags = rw- 3442 c7 0/subop/copy 0/mod/indirect 5/rm32/.disp32 . . . $Elf_p_flags/disp32 6/imm32 # copy to *$Elf_p_flags 3443 $emit-elf-program-header-entry:really-emit: 3444 # emit-hex-array(out, Elf_program_header_entry) 3445 # . . push args 3446 68/push Elf_program_header_entry/imm32 3447 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3448 # . . call 3449 e8/call emit-hex-array/disp32 3450 # . . discard args 3451 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3452 # write-buffered(out, "\n") 3453 # . . push args 3454 68/push Newline/imm32 3455 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3456 # . . call 3457 e8/call write-buffered/disp32 3458 # . . discard args 3459 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3460 $emit-elf-program-header-entry:end: 3461 # . restore registers 3462 5e/pop-to-esi 3463 58/pop-to-eax 3464 # . epilogue 3465 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3466 5d/pop-to-ebp 3467 c3/return 3468 3469 # - some helpers for tests 3470 3471 stream-add4: # in : (addr stream byte), key : addr, val1 : addr, val2 : addr, val3 : addr 3472 # . prologue 3473 55/push-ebp 3474 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3475 # . save registers 3476 50/push-eax 3477 51/push-ecx 3478 52/push-edx 3479 56/push-esi 3480 # esi = in 3481 8b/copy 1/mod/*+disp8 5/rm32/ebp . . . 6/r32/esi 8/disp8 . # copy *(ebp+8) to esi 3482 # curr/eax = &in->data[in->write] 3483 # . eax = in->write 3484 8b/copy 0/mod/indirect 6/rm32/esi . . . 0/r32/eax . . # copy *esi to eax 3485 # . eax = esi+eax+12 3486 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 0/index/eax . 0/r32/eax 0xc/disp8 . # copy esi+eax+12 to eax 3487 # max/edx = &in->data[in->length] 3488 # . edx = in->length 3489 8b/copy 1/mod/*+disp8 6/rm32/esi . . . 2/r32/edx 8/disp8 . # copy *(esi+8) to edx 3490 # . edx = esi+edx+12 3491 8d/copy-address 1/mod/*+disp8 4/rm32/sib 6/base/esi 2/index/edx . 2/r32/edx 0xc/disp8 . # copy esi+edx+12 to edx 3492 # if (curr >= max) abort 3493 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx 3494 73/jump-if-greater-or-equal-unsigned $stream-add4:abort/disp8 3495 # *curr = key 3496 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0xc/disp8 . # copy *(ebp+12) to ecx 3497 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax 3498 # curr += 4 3499 05/add-to-eax 4/imm32 3500 # if (curr >= max) abort 3501 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx 3502 73/jump-if-greater-or-equal-unsigned $stream-add4:abort/disp8 3503 # *curr = val1 3504 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x10/disp8 . # copy *(ebp+16) to ecx 3505 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax 3506 # curr += 4 3507 05/add-to-eax 4/imm32 3508 # if (curr >= max) abort 3509 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx 3510 73/jump-if-greater-or-equal-unsigned $stream-add4:abort/disp8 3511 # *curr = val2 3512 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x14/disp8 . # copy *(ebp+20) to ecx 3513 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax 3514 # curr += 4 3515 05/add-to-eax 4/imm32 3516 # if (curr >= max) abort 3517 39/compare 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # compare eax with edx 3518 73/jump-if-greater-or-equal-unsigned $stream-add4:abort/disp8 3519 # *curr = val3 3520 8b/copy 1/mod/*+disp8 5/rm32/ebp . . 1/r32/ecx 0x18/disp8 . # copy *(ebp+24) to ecx 3521 89/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy ecx to *eax 3522 # in->write += 16 3523 81 0/subop/add 0/mod/indirect 6/rm32/esi . . . . . 0x10/imm32 # add to *esi 3524 $stream-add4:end: 3525 # . restore registers 3526 5e/pop-to-esi 3527 5a/pop-to-edx 3528 59/pop-to-ecx 3529 58/pop-to-eax 3530 # . epilogue 3531 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3532 5d/pop-to-ebp 3533 c3/return 3534 3535 $stream-add4:abort: 3536 # . _write(2/stderr, error) 3537 # . . push args 3538 68/push "overflow in stream-add4\n"/imm32 3539 68/push 2/imm32/stderr 3540 # . . call 3541 e8/call _write/disp32 3542 # . . discard args 3543 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3544 # . syscall(exit, 1) 3545 bb/copy-to-ebx 1/imm32 3546 b8/copy-to-eax 1/imm32/exit 3547 cd/syscall 0x80/imm8 3548 # never gets here 3549 3550 # some variants of 'trace' that take multiple arguments in different combinations of types: 3551 # n: int 3552 # c: character [4-bytes, will eventually be UTF-8] 3553 # s: (addr string) 3554 # l: (addr slice) 3555 # one gotcha: 's5' must not be empty 3556 3557 trace-sssns: # s1 : (addr string), s2 : (addr string), s3 : (addr string), n4 : int, s5 : (addr string) 3558 # . prologue 3559 55/push-ebp 3560 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3561 # write(*Trace-stream, s1) 3562 # . . push args 3563 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3564 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3565 # . . call 3566 e8/call write/disp32 3567 # . . discard args 3568 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3569 # write(*Trace-stream, s2) 3570 # . . push args 3571 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 3572 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3573 # . . call 3574 e8/call write/disp32 3575 # . . discard args 3576 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3577 # write(*Trace-stream, s3) 3578 # . . push args 3579 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 3580 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3581 # . . call 3582 e8/call write/disp32 3583 # . . discard args 3584 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3585 # print-int32(*Trace-stream, n4) 3586 # . . push args 3587 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) 3588 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3589 # . . call 3590 e8/call print-int32/disp32 3591 # . . discard args 3592 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3593 # trace(s5) # implicitly adds a newline and finalizes the trace line 3594 # . . push args 3595 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) 3596 # . . call 3597 e8/call trace/disp32 3598 # . . discard args 3599 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 3600 $trace-sssns:end: 3601 # . epilogue 3602 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3603 5d/pop-to-ebp 3604 c3/return 3605 3606 test-trace-sssns: 3607 # . prologue 3608 55/push-ebp 3609 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3610 # setup 3611 # . *Trace-stream->write = 0 3612 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax 3613 c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax 3614 # trace-sssns("A" "b" "c " 3 " e") 3615 # . . push args 3616 68/push " e"/imm32 3617 68/push 3/imm32 3618 68/push "c "/imm32 3619 68/push "b"/imm32 3620 68/push "A"/imm32 3621 # . . call 3622 e8/call trace-sssns/disp32 3623 # . . discard args 3624 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 3625 +-- 26 lines: #? # dump *Trace-stream -------------------------------------------------------------------------------------------------------------------- 3651 # check-trace-contains("Abc 0x00000003 e") 3652 # . . push args 3653 68/push "F - test-trace-sssns"/imm32 3654 68/push "Abc 0x00000003 e"/imm32 3655 # . . call 3656 e8/call check-trace-contains/disp32 3657 # . . discard args 3658 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3659 # . epilogue 3660 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3661 5d/pop-to-ebp 3662 c3/return 3663 3664 trace-snsns: # s1 : (addr string), n2 : int, s3 : (addr string), n4 : int, s5 : (addr string) 3665 # . prologue 3666 55/push-ebp 3667 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3668 # write(*Trace-stream, s1) 3669 # . . push args 3670 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3671 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3672 # . . call 3673 e8/call write/disp32 3674 # . . discard args 3675 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3676 # print-int32(*Trace-stream, n2) 3677 # . . push args 3678 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 3679 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3680 # . . call 3681 e8/call print-int32/disp32 3682 # . . discard args 3683 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3684 # write(*Trace-stream, s3) 3685 # . . push args 3686 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 3687 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3688 # . . call 3689 e8/call write/disp32 3690 # . . discard args 3691 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3692 # print-int32(*Trace-stream, n4) 3693 # . . push args 3694 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) 3695 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3696 # . . call 3697 e8/call print-int32/disp32 3698 # . . discard args 3699 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3700 # trace(s5) # implicitly adds a newline and finalizes the trace line 3701 # . . push args 3702 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) 3703 # . . call 3704 e8/call trace/disp32 3705 # . . discard args 3706 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 3707 $trace-snsns:end: 3708 # . epilogue 3709 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3710 5d/pop-to-ebp 3711 c3/return 3712 3713 test-trace-snsns: 3714 # . prologue 3715 55/push-ebp 3716 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3717 # setup 3718 # . *Trace-stream->write = 0 3719 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax 3720 c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax 3721 # trace-snsns("A " 2 " c " 3 " e") 3722 # . . push args 3723 68/push " e"/imm32 3724 68/push 3/imm32 3725 68/push " c "/imm32 3726 68/push 2/imm32 3727 68/push "A "/imm32 3728 # . . call 3729 e8/call trace-snsns/disp32 3730 # . . discard args 3731 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 3732 +-- 26 lines: #? # dump *Trace-stream -------------------------------------------------------------------------------------------------------------------- 3758 # check-trace-contains("Abc 0x00000003 e") 3759 # . . push args 3760 68/push "F - test-trace-snsns"/imm32 3761 68/push "A 0x00000002 c 0x00000003 e"/imm32 3762 # . . call 3763 e8/call check-trace-contains/disp32 3764 # . . discard args 3765 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3766 # . epilogue 3767 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3768 5d/pop-to-ebp 3769 c3/return 3770 3771 trace-slsls: # s1 : (addr string), l2 : (addr slice), s3 : (addr string), l4 : (addr slice), s5 : (addr string) 3772 # . prologue 3773 55/push-ebp 3774 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3775 # write(*Trace-stream, s1) 3776 # . . push args 3777 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3778 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3779 # . . call 3780 e8/call write/disp32 3781 # . . discard args 3782 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3783 # write-slice(*Trace-stream, l2) 3784 # . . push args 3785 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 3786 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3787 # . . call 3788 e8/call write-slice/disp32 3789 # . . discard args 3790 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3791 # write(*Trace-stream, s3) 3792 # . . push args 3793 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 3794 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3795 # . . call 3796 e8/call write/disp32 3797 # . . discard args 3798 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3799 # write-slice(*Trace-stream, l4) 3800 # . . push args 3801 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) 3802 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3803 # . . call 3804 e8/call write-slice/disp32 3805 # . . discard args 3806 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3807 # trace(s5) # implicitly adds a newline and finalizes the trace line 3808 # . . push args 3809 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) 3810 # . . call 3811 e8/call trace/disp32 3812 # . . discard args 3813 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 3814 $trace-slsls:end: 3815 # . epilogue 3816 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3817 5d/pop-to-ebp 3818 c3/return 3819 3820 test-trace-slsls: 3821 # . prologue 3822 55/push-ebp 3823 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3824 # setup 3825 # . *Trace-stream->write = 0 3826 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax 3827 c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax 3828 # (eax..ecx) = "b" 3829 b8/copy-to-eax "b"/imm32 3830 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 3831 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx 3832 05/add-to-eax 4/imm32 3833 # var b/ebx : (ref slice) = {eax, ecx} 3834 51/push-ecx 3835 50/push-eax 3836 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx 3837 # (eax..ecx) = "d" 3838 b8/copy-to-eax "d"/imm32 3839 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 3840 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx 3841 05/add-to-eax 4/imm32 3842 # var d/edx : (ref slice) = {eax, ecx} 3843 51/push-ecx 3844 50/push-eax 3845 89/copy 3/mod/direct 2/rm32/edx . . . 4/r32/esp . . # copy esp to edx 3846 # trace-slsls("A" b "c" d "e") 3847 # . . push args 3848 68/push "e"/imm32 3849 52/push-edx 3850 68/push "c"/imm32 3851 53/push-ebx 3852 68/push "A"/imm32 3853 # . . call 3854 e8/call trace-slsls/disp32 3855 # . . discard args 3856 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 3857 +-- 26 lines: #? # dump *Trace-stream -------------------------------------------------------------------------------------------------------------------- 3883 # check-trace-contains("Abcde") 3884 # . . push args 3885 68/push "F - test-trace-slsls"/imm32 3886 68/push "Abcde"/imm32 3887 # . . call 3888 e8/call check-trace-contains/disp32 3889 # . . discard args 3890 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3891 # . epilogue 3892 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3893 5d/pop-to-ebp 3894 c3/return 3895 3896 trace-slsns: # s1 : (addr string), l2 : (addr slice), s3 : (addr string), n4 : int, s5 : (addr string) 3897 # . prologue 3898 55/push-ebp 3899 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3900 # write(*Trace-stream, s1) 3901 # . . push args 3902 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 3903 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3904 # . . call 3905 e8/call write/disp32 3906 # . . discard args 3907 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3908 # write-slice(*Trace-stream, l2) 3909 # . . push args 3910 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 3911 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3912 # . . call 3913 e8/call write-slice/disp32 3914 # . . discard args 3915 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3916 # write(*Trace-stream, s3) 3917 # . . push args 3918 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 3919 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3920 # . . call 3921 e8/call write/disp32 3922 # . . discard args 3923 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3924 # print-int32(*Trace-stream, n4) 3925 # . . push args 3926 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) 3927 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 3928 # . . call 3929 e8/call print-int32/disp32 3930 # . . discard args 3931 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 3932 # trace(s5) # implicitly adds a newline and finalizes the trace line 3933 # . . push args 3934 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) 3935 # . . call 3936 e8/call trace/disp32 3937 # . . discard args 3938 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 3939 $trace-slsns:end: 3940 # . epilogue 3941 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 3942 5d/pop-to-ebp 3943 c3/return 3944 3945 test-trace-slsns: 3946 # . prologue 3947 55/push-ebp 3948 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 3949 # setup 3950 # . *Trace-stream->write = 0 3951 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax 3952 c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax 3953 # (eax..ecx) = "b" 3954 b8/copy-to-eax "b"/imm32 3955 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 3956 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx 3957 05/add-to-eax 4/imm32 3958 # var b/ebx : (ref slice) = {eax, ecx} 3959 51/push-ecx 3960 50/push-eax 3961 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx 3962 # trace-slsls("A" b "c " 3 " e") 3963 # . . push args 3964 68/push " e"/imm32 3965 68/push 3/imm32 3966 68/push "c "/imm32 3967 53/push-ebx 3968 68/push "A"/imm32 3969 # . . call 3970 e8/call trace-slsns/disp32 3971 # . . discard args 3972 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 3973 +-- 26 lines: #? # dump *Trace-stream -------------------------------------------------------------------------------------------------------------------- 3999 # check-trace-contains("Abc 0x00000003 e") 4000 # . . push args 4001 68/push "F - test-trace-slsls"/imm32 4002 68/push "Abc 0x00000003 e"/imm32 4003 # . . call 4004 e8/call check-trace-contains/disp32 4005 # . . discard args 4006 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4007 # . epilogue 4008 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4009 5d/pop-to-ebp 4010 c3/return 4011 4012 trace-slsss: # s1 : (addr string), l2 : (addr slice), s3 : (addr string), s4 : (addr string), s5 : (addr string) 4013 # . prologue 4014 55/push-ebp 4015 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4016 # write(*Trace-stream, s1) 4017 # . . push args 4018 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 4019 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 4020 # . . call 4021 e8/call write/disp32 4022 # . . discard args 4023 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4024 # write-slice(*Trace-stream, l2) 4025 # . . push args 4026 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0xc/disp8 . # push *(ebp+12) 4027 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 4028 # . . call 4029 e8/call write-slice/disp32 4030 # . . discard args 4031 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4032 # write(*Trace-stream, s3) 4033 # . . push args 4034 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x10/disp8 . # push *(ebp+16) 4035 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 4036 # . . call 4037 e8/call write/disp32 4038 # . . discard args 4039 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4040 # write(*Trace-stream, s4) 4041 # . . push args 4042 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x14/disp8 . # push *(ebp+20) 4043 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Trace-stream/disp32 # push *Trace-stream 4044 # . . call 4045 e8/call write/disp32 4046 # . . discard args 4047 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4048 # trace(s5) # implicitly adds a newline and finalizes the trace line 4049 # . . push args 4050 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 0x18/disp8 . # push *(ebp+24) 4051 # . . call 4052 e8/call trace/disp32 4053 # . . discard args 4054 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4055 $trace-slsss:end: 4056 # . epilogue 4057 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4058 5d/pop-to-ebp 4059 c3/return 4060 4061 test-trace-slsss: 4062 # . prologue 4063 55/push-ebp 4064 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4065 # setup 4066 # . *Trace-stream->write = 0 4067 8b/copy 0/mod/indirect 5/rm32/.disp32 . . 0/r32/eax Trace-stream/disp32 # copy *Trace-stream to eax 4068 c7 0/subop/copy 0/mod/direct 0/rm32/eax . . . . . 0/imm32 # clear *eax 4069 # (eax..ecx) = "b" 4070 b8/copy-to-eax "b"/imm32 4071 8b/copy 0/mod/indirect 0/rm32/eax . . . 1/r32/ecx . . # copy *eax to ecx 4072 8d/copy-address 1/mod/*+disp8 4/rm32/sib 0/base/eax 1/index/ecx . 1/r32/ecx 4/disp8 . # copy eax+ecx+4 to ecx 4073 05/add-to-eax 4/imm32 4074 # var b/ebx : (ref slice) = {eax, ecx} 4075 51/push-ecx 4076 50/push-eax 4077 89/copy 3/mod/direct 3/rm32/ebx . . . 4/r32/esp . . # copy esp to ebx 4078 # trace-slsss("A" b "c" "d" "e") 4079 # . . push args 4080 68/push "e"/imm32 4081 68/push "d"/imm32 4082 68/push "c"/imm32 4083 53/push-ebx 4084 68/push "A"/imm32 4085 # . . call 4086 e8/call trace-slsss/disp32 4087 # . . discard args 4088 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0x14/imm32 # add to esp 4089 +-- 26 lines: #? # dump *Trace-stream -------------------------------------------------------------------------------------------------------------------- 4115 # check-trace-contains("Abcde") 4116 # . . push args 4117 68/push "F - test-trace-slsss"/imm32 4118 68/push "Abcde"/imm32 4119 # . . call 4120 e8/call check-trace-contains/disp32 4121 # . . discard args 4122 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4123 # . epilogue 4124 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4125 5d/pop-to-ebp 4126 c3/return 4127 4128 num-bytes: # line : (addr stream byte) -> eax : int 4129 # pseudocode: 4130 # result = 0 4131 # while true 4132 # var word-slice = next-word(line) 4133 # if slice-empty?(word-slice) # end of line 4134 # break 4135 # if slice-starts-with?(word-slice, "#") # comment 4136 # break 4137 # if is-label?(word-slice) # no need for label declarations anymore 4138 # break 4139 # if slice-equal?(word-slice, "==") 4140 # break # no need for segment header lines 4141 # result += compute-width(word-slice) 4142 # return result 4143 # 4144 # . prologue 4145 55/push-ebp 4146 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4147 # . save registers 4148 51/push-ecx 4149 52/push-edx 4150 53/push-ebx 4151 # var result/eax = 0 4152 31/xor 3/mod/direct 0/rm32/eax . . . 0/r32/eax . . # clear eax 4153 # var word-slice/ecx : (ref slice) 4154 68/push 0/imm32/end 4155 68/push 0/imm32/start 4156 89/copy 3/mod/direct 1/rm32/ecx . . . 4/r32/esp . . # copy esp to ecx 4157 +-- 26 lines: #? # dump line ----------------------------------------------------------------------------------------------------------------------------- 4183 # . rewind-stream(line) 4184 # . . push args 4185 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 4186 # . . call 4187 e8/call rewind-stream/disp32 4188 # . . discard args 4189 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4190 $num-bytes:loop: 4191 # next-word(line, word-slice) 4192 # . . push args 4193 51/push-ecx 4194 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 4195 # . . call 4196 e8/call next-word/disp32 4197 # . . discard args 4198 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4199 +-- 40 lines: #? # dump word-slice ----------------------------------------------------------------------------------------------------------------------- 4239 $num-bytes:check0: 4240 # if (slice-empty?(word-slice)) break 4241 # . save result 4242 50/push-eax 4243 # . eax = slice-empty?(word-slice) 4244 # . . push args 4245 51/push-ecx 4246 # . . call 4247 e8/call slice-empty?/disp32 4248 # . . discard args 4249 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4250 # . if (eax != false) break 4251 3d/compare-eax-and 0/imm32/false 4252 # . restore result now that ZF is set 4253 58/pop-to-eax 4254 75/jump-if-not-equal $num-bytes:end/disp8 4255 $num-bytes:check-for-comment: 4256 # if (slice-starts-with?(word-slice, "#")) break 4257 # . start/edx = word-slice->start 4258 8b/copy 0/mod/indirect 1/rm32/ecx . . . 2/r32/edx . . # copy *ecx to edx 4259 # . c/ebx = *start 4260 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx 4261 8a/copy-byte 0/mod/indirect 2/rm32/edx . . . 3/r32/BL . . # copy byte at *edx to BL 4262 # . if (ebx == '#') break 4263 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x23/imm32/hash # compare ebx 4264 74/jump-if-equal $num-bytes:end/disp8 4265 $num-bytes:check-for-label: 4266 # if (slice-ends-with?(word-slice, ":")) break 4267 # . end/edx = word-slice->end 4268 8b/copy 1/mod/*+disp8 1/rm32/ecx . . . 2/r32/edx 4/disp8 . # copy *(ecx+4) to edx 4269 # . c/ebx = *(end-1) 4270 31/xor 3/mod/direct 3/rm32/ebx . . . 3/r32/ebx . . # clear ebx 4271 8a/copy-byte 1/mod/*+disp8 2/rm32/edx . . . 3/r32/BL -1/disp8 . # copy byte at *ecx to BL 4272 # . if (ebx == ':') break 4273 81 7/subop/compare 3/mod/direct 3/rm32/ebx . . . . . 0x3a/imm32/colon # compare ebx 4274 74/jump-if-equal $num-bytes:end/disp8 4275 $num-bytes:check-for-segment-header: 4276 # if (slice-equal?(word-slice, "==")) break 4277 # . push result 4278 50/push-eax 4279 # . eax = slice-equal?(word-slice, "==") 4280 # . . push args 4281 68/push "=="/imm32 4282 51/push-ecx 4283 # . . call 4284 e8/call slice-equal?/disp32 4285 # . . discard args 4286 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4287 # . if (eax != false) break 4288 3d/compare-eax-and 0/imm32/false 4289 # . restore result now that ZF is set 4290 58/pop-to-eax 4291 75/jump-if-not-equal $num-bytes:end/disp8 4292 $num-bytes:loop-body: 4293 # result += compute-width-of-slice(word-slice) 4294 # . copy result to edx 4295 89/copy 3/mod/direct 2/rm32/edx . . . 0/r32/eax . . # copy eax to edx 4296 # . eax = compute-width-of-slice(word-slice) 4297 # . . push args 4298 51/push-ecx 4299 # . . call 4300 e8/call compute-width-of-slice/disp32 4301 # . . discard args 4302 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4303 # . eax += result 4304 01/add 3/mod/direct 0/rm32/eax . . . 2/r32/edx . . # add edx to eax 4305 e9/jump $num-bytes:loop/disp32 4306 $num-bytes:end: 4307 # . rewind-stream(line) 4308 # . . push args 4309 ff 6/subop/push 1/mod/*+disp8 5/rm32/ebp . . . . 8/disp8 . # push *(ebp+8) 4310 # . . call 4311 e8/call rewind-stream/disp32 4312 # . . discard args 4313 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4314 # . reclaim locals 4315 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4316 # . restore registers 4317 5b/pop-to-ebx 4318 5a/pop-to-edx 4319 59/pop-to-ecx 4320 # . epilogue 4321 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4322 5d/pop-to-ebp 4323 c3/return 4324 4325 test-num-bytes-handles-empty-string: 4326 # if a line starts with '#', return 0 4327 # . prologue 4328 55/push-ebp 4329 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4330 # setup 4331 # . clear-stream(_test-input-stream) 4332 # . . push args 4333 68/push _test-input-stream/imm32 4334 # . . call 4335 e8/call clear-stream/disp32 4336 # . . discard args 4337 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4338 # . clear-stream(_test-output-stream) 4339 # . . push args 4340 68/push _test-output-stream/imm32 4341 # . . call 4342 e8/call clear-stream/disp32 4343 # . . discard args 4344 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4345 # no contents in input 4346 # eax = num-bytes(_test-input-stream) 4347 # . . push args 4348 68/push _test-input-stream/imm32 4349 # . . call 4350 e8/call num-bytes/disp32 4351 # . . discard args 4352 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4353 # check-ints-equal(eax, 0, msg) 4354 # . . push args 4355 68/push "F - test-num-bytes-handles-empty-string"/imm32 4356 68/push 0/imm32/true 4357 50/push-eax 4358 # . . call 4359 e8/call check-ints-equal/disp32 4360 # . . discard args 4361 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 4362 # . epilogue 4363 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4364 5d/pop-to-ebp 4365 c3/return 4366 4367 test-num-bytes-ignores-comments: 4368 # if a line starts with '#', return 0 4369 # . prologue 4370 55/push-ebp 4371 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4372 # setup 4373 # . clear-stream(_test-input-stream) 4374 # . . push args 4375 68/push _test-input-stream/imm32 4376 # . . call 4377 e8/call clear-stream/disp32 4378 # . . discard args 4379 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4380 # . clear-stream(_test-output-stream) 4381 # . . push args 4382 68/push _test-output-stream/imm32 4383 # . . call 4384 e8/call clear-stream/disp32 4385 # . . discard args 4386 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4387 # initialize input 4388 # . write(_test-input-stream, "# abcd") 4389 # . . push args 4390 68/push "# abcd"/imm32 4391 68/push _test-input-stream/imm32 4392 # . . call 4393 e8/call write/disp32 4394 # . . discard args 4395 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4396 # eax = num-bytes(_test-input-stream) 4397 # . . push args 4398 68/push _test-input-stream/imm32 4399 # . . call 4400 e8/call num-bytes/disp32 4401 # . . discard args 4402 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4403 # check-ints-equal(eax, 0, msg) 4404 # . . push args 4405 68/push "F - test-num-bytes-ignores-comments"/imm32 4406 68/push 0/imm32/true 4407 50/push-eax 4408 # . . call 4409 e8/call check-ints-equal/disp32 4410 # . . discard args 4411 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 4412 # . epilogue 4413 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4414 5d/pop-to-ebp 4415 c3/return 4416 4417 test-num-bytes-ignores-labels: 4418 # if the first word ends with ':', return 0 4419 # . prologue 4420 55/push-ebp 4421 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4422 # setup 4423 # . clear-stream(_test-input-stream) 4424 # . . push args 4425 68/push _test-input-stream/imm32 4426 # . . call 4427 e8/call clear-stream/disp32 4428 # . . discard args 4429 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4430 # . clear-stream(_test-output-stream) 4431 # . . push args 4432 68/push _test-output-stream/imm32 4433 # . . call 4434 e8/call clear-stream/disp32 4435 # . . discard args 4436 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4437 # initialize input 4438 # . write(_test-input-stream, "ab: # cd") 4439 # . . push args 4440 68/push "ab: # cd"/imm32 4441 68/push _test-input-stream/imm32 4442 # . . call 4443 e8/call write/disp32 4444 # . . discard args 4445 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4446 # eax = num-bytes(_test-input-stream) 4447 # . . push args 4448 68/push _test-input-stream/imm32 4449 # . . call 4450 e8/call num-bytes/disp32 4451 # . . discard args 4452 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4453 # check-ints-equal(eax, 0, msg) 4454 # . . push args 4455 68/push "F - test-num-bytes-ignores-labels"/imm32 4456 68/push 0/imm32/true 4457 50/push-eax 4458 # . . call 4459 e8/call check-ints-equal/disp32 4460 # . . discard args 4461 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 4462 # . epilogue 4463 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4464 5d/pop-to-ebp 4465 c3/return 4466 4467 test-num-bytes-ignores-segment-headers: 4468 # if the first word is '==', return 0 4469 # . prologue 4470 55/push-ebp 4471 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4472 # setup 4473 # . clear-stream(_test-input-stream) 4474 # . . push args 4475 68/push _test-input-stream/imm32 4476 # . . call 4477 e8/call clear-stream/disp32 4478 # . . discard args 4479 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4480 # . clear-stream(_test-output-stream) 4481 # . . push args 4482 68/push _test-output-stream/imm32 4483 # . . call 4484 e8/call clear-stream/disp32 4485 # . . discard args 4486 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4487 # initialize input 4488 # . write(_test-input-stream, "== ab cd") 4489 # . . push args 4490 68/push "== ab cd"/imm32 4491 68/push _test-input-stream/imm32 4492 # . . call 4493 e8/call write/disp32 4494 # . . discard args 4495 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4496 # eax = num-bytes(_test-input-stream) 4497 # . . push args 4498 68/push _test-input-stream/imm32 4499 # . . call 4500 e8/call num-bytes/disp32 4501 # . . discard args 4502 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4503 # check-ints-equal(eax, 0, msg) 4504 # . . push args 4505 68/push "F - test-num-bytes-ignores-segment-headers"/imm32 4506 68/push 0/imm32/true 4507 50/push-eax 4508 # . . call 4509 e8/call check-ints-equal/disp32 4510 # . . discard args 4511 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 4512 # . epilogue 4513 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4514 5d/pop-to-ebp 4515 c3/return 4516 4517 test-num-bytes-counts-words-by-default: 4518 # without metadata, count words 4519 # . prologue 4520 55/push-ebp 4521 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4522 # setup 4523 # . clear-stream(_test-input-stream) 4524 # . . push args 4525 68/push _test-input-stream/imm32 4526 # . . call 4527 e8/call clear-stream/disp32 4528 # . . discard args 4529 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4530 # . clear-stream(_test-output-stream) 4531 # . . push args 4532 68/push _test-output-stream/imm32 4533 # . . call 4534 e8/call clear-stream/disp32 4535 # . . discard args 4536 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4537 # initialize input 4538 # . write(_test-input-stream, "ab cd ef") 4539 # . . push args 4540 68/push "ab cd ef"/imm32 4541 68/push _test-input-stream/imm32 4542 # . . call 4543 e8/call write/disp32 4544 # . . discard args 4545 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4546 # eax = num-bytes(_test-input-stream) 4547 # . . push args 4548 68/push _test-input-stream/imm32 4549 # . . call 4550 e8/call num-bytes/disp32 4551 # . . discard args 4552 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4553 # check-ints-equal(eax, 3, msg) 4554 # . . push args 4555 68/push "F - test-num-bytes-counts-words-by-default"/imm32 4556 68/push 3/imm32/true 4557 50/push-eax 4558 # . . call 4559 e8/call check-ints-equal/disp32 4560 # . . discard args 4561 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 4562 # . epilogue 4563 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4564 5d/pop-to-ebp 4565 c3/return 4566 4567 test-num-bytes-ignores-trailing-comment: 4568 # trailing comments appropriately ignored 4569 # . prologue 4570 55/push-ebp 4571 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4572 # setup 4573 # . clear-stream(_test-input-stream) 4574 # . . push args 4575 68/push _test-input-stream/imm32 4576 # . . call 4577 e8/call clear-stream/disp32 4578 # . . discard args 4579 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4580 # . clear-stream(_test-output-stream) 4581 # . . push args 4582 68/push _test-output-stream/imm32 4583 # . . call 4584 e8/call clear-stream/disp32 4585 # . . discard args 4586 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4587 # initialize input 4588 # . write(_test-input-stream, "ab cd # ef") 4589 # . . push args 4590 68/push "ab cd # ef"/imm32 4591 68/push _test-input-stream/imm32 4592 # . . call 4593 e8/call write/disp32 4594 # . . discard args 4595 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4596 # eax = num-bytes(_test-input-stream) 4597 # . . push args 4598 68/push _test-input-stream/imm32 4599 # . . call 4600 e8/call num-bytes/disp32 4601 # . . discard args 4602 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4603 # check-ints-equal(eax, 2, msg) 4604 # . . push args 4605 68/push "F - test-num-bytes-ignores-trailing-comment"/imm32 4606 68/push 2/imm32/true 4607 50/push-eax 4608 # . . call 4609 e8/call check-ints-equal/disp32 4610 # . . discard args 4611 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 4612 # . epilogue 4613 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4614 5d/pop-to-ebp 4615 c3/return 4616 4617 test-num-bytes-handles-imm32: 4618 # if a word has the /imm32 metadata, count it as 4 bytes 4619 # . prologue 4620 55/push-ebp 4621 89/copy 3/mod/direct 5/rm32/ebp . . . 4/r32/esp . . # copy esp to ebp 4622 # setup 4623 # . clear-stream(_test-input-stream) 4624 # . . push args 4625 68/push _test-input-stream/imm32 4626 # . . call 4627 e8/call clear-stream/disp32 4628 # . . discard args 4629 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4630 # . clear-stream(_test-output-stream) 4631 # . . push args 4632 68/push _test-output-stream/imm32 4633 # . . call 4634 e8/call clear-stream/disp32 4635 # . . discard args 4636 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4637 # initialize input 4638 # . write(_test-input-stream, "ab cd/imm32 ef") 4639 # . . push args 4640 68/push "ab cd/imm32 ef"/imm32 4641 68/push _test-input-stream/imm32 4642 # . . call 4643 e8/call write/disp32 4644 # . . discard args 4645 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 8/imm32 # add to esp 4646 # eax = num-bytes(_test-input-stream) 4647 # . . push args 4648 68/push _test-input-stream/imm32 4649 # . . call 4650 e8/call num-bytes/disp32 4651 # . . discard args 4652 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 4/imm32 # add to esp 4653 # check-ints-equal(eax, 6, msg) 4654 # . . push args 4655 68/push "F - test-num-bytes-handles-imm32"/imm32 4656 68/push 6/imm32/true 4657 50/push-eax 4658 # . . call 4659 e8/call check-ints-equal/disp32 4660 # . . discard args 4661 81 0/subop/add 3/mod/direct 4/rm32/esp . . . . . 0xc/imm32 # add to esp 4662 # . epilogue 4663 89/copy 3/mod/direct 4/rm32/esp . . . 5/r32/ebp . . # copy ebp to esp 4664 5d/pop-to-ebp 4665 c3/return 4666 4667 == data 4668 4669 # This block of bytes gets copied to the start of the output ELF file, with 4670 # some fields (the ones with labels capitalized) filled in. 4671 # http://www.sco.com/developers/gabi/latest/ch4.eheader.html 4672 Elf_header: 4673 # - length 4674 0x34/imm32 4675 # - data 4676 $e_ident: 4677 7f 45/E 4c/L 46/F 4678 01/32-bit 01/little-endian 01/file-version 00/no-os-extensions 4679 00 00 00 00 00 00 00 00 # 8 bytes of padding 4680 $e_type: 4681 02 00 4682 $e_machine: 4683 03 00 4684 $e_version: 4685 1/imm32 4686 $Elf_e_entry: 4687 0x09000000/imm32 # approximate default; must be updated 4688 $e_phoff: 4689 0x34/imm32 # offset for the 'program header table' containing segment headers 4690 $e_shoff: 4691 0/imm32 # no sections 4692 $e_flags: 4693 0/imm32 # unused 4694 $e_ehsize: 4695 0x34 00 4696 $e_phentsize: 4697 0x20 00 4698 $Elf_e_phnum: 4699 00 00 # number of segments; must be updated 4700 $e_shentsize: 4701 00 00 # no sections 4702 $e_shnum: 4703 00 00 4704 $e_shstrndx: 4705 00 00 4706 4707 # This block of bytes gets copied after the Elf_header once for each segment. 4708 # Some fields need filling in each time. 4709 # https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html 4710 Elf_program_header_entry: 4711 # - length 4712 0x20/imm32 4713 # - data 4714 $p_type: 4715 1/imm32/PT_LOAD 4716 $Elf_p_offset: 4717 0/imm32 # byte offset in the file at which a segment begins; must be updated 4718 $Elf_p_vaddr: 4719 0/imm32 # starting address to store the segment at before running the program 4720 $Elf_p_paddr: 4721 0/imm32 # should have same value as $Elf_p_vaddr 4722 $Elf_p_filesz: 4723 0/imm32 4724 $Elf_p_memsz: 4725 0/imm32 # should have same value as $Elf_p_filesz 4726 $Elf_p_flags: 4727 6/imm32/rw- # read/write/execute permissions for the segment; must be updated for the code segment 4728 $p_align: 4729 # we hold this constant; changing it will require adjusting the way we 4730 # compute the starting address for each segment 4731 0x1000/imm32 4732 4733 # . . vim:nowrap:textwidth=0