https://github.com/akkartik/mu/blob/main/linux/survey_baremetal.subx

       1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
       2 # (landscape).
       3 #
       4 # To build:
       5 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx survey_baremetal.subx  -o survey_baremetal
       6 #
       7 # The expected input is a stream of bytes and some interspersed labels.
       8 # Comments and '==' segment headers are allowed, but names are ignored. The
       9 # emitted code will all lie in a single contiguous address range starting at
      10 # address 0x7c00. Addresses in segment headers are optional. If provided, this
      11 # program will insert padding in the output until the desired address is
      12 # reached.
      13 #
      14 #   $ cat x
      15 #   == code
      16 #   l1:
      17 #   aa bb l1/imm8
      18 #   cc dd l2/disp32
      19 #   l2:
      20 #   ee foo/imm32
      21 #   == data 0x7c10
      22 #   foo:
      23 #     34
      24 #
      25 # The output is a list of labels and their computed addresses.
      26 #
      27 #   $ cat x  |bootstrap/bootstrap run survey_baremetal
      28 #   0x7c00 l1
      29 #   0x7c09 l2
      30 #   0x7c10 foo
      31
      32 == code
      33 #   instruction                     effective address                                                   register    displacement    immediate
      34 # . op          subop               mod             rm32          base        index         scale       r32
      35 # . 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
      36
      37 Entry:  # run tests if necessary, convert stdin if not
      38     # . prologue
      39     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
      40
      41     # Heap = new-segment(Heap-size)
      42     # . . push args
      43     68/push  Heap/imm32
      44     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
      45     # . . call
      46     e8/call  new-segment/disp32
      47     # . . discard args
      48     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      49     # initialize-trace-stream(Trace-size)
      50     # . . push args
      51     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-size/disp32                 # push *Heap-size
      52     # . . call
      53     e8/call  initialize-trace-stream/disp32
      54     # . . discard args
      55     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
      56
      57     # - if argc > 1 and argv[1] == "test", then return run_tests()
      58     # if (argc <= 1) goto interactive
      59     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
      60     7e/jump-if-<=  $subx-survey-main:interactive/disp8
      61     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
      62     # . eax = kernel-string-equal?(argv[1], "test")
      63     # . . push args
      64     68/push  "test"/imm32
      65     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
      66     # . . call
      67     e8/call  kernel-string-equal?/disp32
      68     # . . discard args
      69     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      70     # . if (eax == false) goto interactive
      71     3d/compare-eax-and  0/imm32/false
      72     74/jump-if-=  $subx-survey-main:interactive/disp8
      73     # run-tests()
      74     e8/call  run-tests/disp32
      75     # syscall(exit, *Num-test-failures)
      76     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
      77     eb/jump  $subx-survey-main:end/disp8
      78 $subx-survey-main:interactive:
      79     # - otherwise convert stdin
      80     # subx-survey(Stdin, Stdout)
      81     # . . push args
      82     68/push  Stdout/imm32
      83     68/push  Stdin/imm32
      84     # . . call
      85     e8/call  subx-survey/disp32
      86     # . . discard args
      87     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      88 #?     # . write-stream(2/stderr, Trace-stream)
      89 #?     # . . push args
      90 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
      91 #?     68/push  2/imm32/stderr
      92 #?     # . . call
      93 #?     e8/call  write-stream/disp32
      94 #?     # . . discard args
      95 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
      96     # syscall(exit, 0)
      97     bb/copy-to-ebx  0/imm32
      98 $subx-survey-main:end:
      99     e8/call  syscall_exit/disp32
     100
     101 subx-survey:  # infile: (addr buffered-file), out: (addr buffered-file)
     102     # pseudocode
     103     #   var in: (stream byte Input-size)
     104     #   slurp(infile, in)
     105     #   var labels: (stream {label-name, address} Max-labels)
     106     #   compute-addresses(in, labels)
     107     #   emit-labels(out, labels)
     108     #
     109     # . prologue
     110     55/push-ebp
     111     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     112     # . save registers
     113     51/push-ecx
     114     52/push-edx
     115     56/push-esi
     116     # var labels/edx: (stream {label-name, address} Max-labels)
     117     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
     118     # . data
     119     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
     120     # . size
     121     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
     122     # . read
     123     68/push  0/imm32/read
     124     # . write
     125     68/push  0/imm32/write
     126     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     127     # var in/esi: (stream byte Input-size)
     128     # . data
     129     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
     130     # . size
     131     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
     132     # . read
     133     68/push  0/imm32/read
     134     # . write
     135     68/push  0/imm32/write
     136     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
     137     # slurp(infile, in)
     138     # . . push args
     139     56/push-esi
     140     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     141     # . . call
     142     e8/call  slurp/disp32
     143     # . . discard args
     144     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     145     # compute-addresses(in, labels)
     146     # . . push args
     147     52/push-edx
     148     56/push-esi
     149     # . . call
     150     e8/call  compute-addresses/disp32
     151     # . . discard args
     152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     153     # emit-labels(out, labels)
     154     # . . push args
     155     52/push-edx
     156     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     157     # . . call
     158     e8/call  emit-labels/disp32
     159     # . . discard args
     160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     161     # flush(out)
     162     # . . push args
     163     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     164     # . . call
     165     e8/call  flush/disp32
     166     # . . discard args
     167     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     168 $subx-survey:end:
     169     # . reclaim locals
     170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
     171     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
     172     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
     173     # . restore registers
     174     5e/pop-to-esi
     175     5a/pop-to-edx
     176     59/pop-to-ecx
     177     # . epilogue
     178     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     179     5d/pop-to-ebp
     180     c3/return
     181
     182 test-subx-survey-computes-addresses:
     183     # input:
     184     #   == code
     185     #   ab x/imm32
     186     #   == data
     187     #   x:
     188     #     01
     189     #
     190     # trace contains (in any order):
     191     #   label x is at address 0x7c05
     192     #
     193     # . prologue
     194     55/push-ebp
     195     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     196     # setup
     197     # . clear-stream(_test-input-stream)
     198     # . . push args
     199     68/push  _test-input-stream/imm32
     200     # . . call
     201     e8/call  clear-stream/disp32
     202     # . . discard args
     203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     204     # . clear-stream($_test-input-buffered-file->buffer)
     205     # . . push args
     206     68/push  $_test-input-buffered-file->buffer/imm32
     207     # . . call
     208     e8/call  clear-stream/disp32
     209     # . . discard args
     210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     211     # . clear-stream(_test-output-stream)
     212     # . . push args
     213     68/push  _test-output-stream/imm32
     214     # . . call
     215     e8/call  clear-stream/disp32
     216     # . . discard args
     217     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     218     # . clear-stream($_test-output-buffered-file->buffer)
     219     # . . push args
     220     68/push  $_test-output-buffered-file->buffer/imm32
     221     # . . call
     222     e8/call  clear-stream/disp32
     223     # . . discard args
     224     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     225     # initialize input
     226     # . write(_test-input-stream, "== code\n")
     227     # . . push args
     228     68/push  "== code\n"/imm32
     229     68/push  _test-input-stream/imm32
     230     # . . call
     231     e8/call  write/disp32
     232     # . . discard args
     233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     234     # . write(_test-input-stream, "ab x/imm32\n")
     235     # . . push args
     236     68/push  "ab x/imm32\n"/imm32
     237     68/push  _test-input-stream/imm32
     238     # . . call
     239     e8/call  write/disp32
     240     # . . discard args
     241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     242     # . write(_test-input-stream, "== data\n")
     243     # . . push args
     244     68/push  "== data\n"/imm32
     245     68/push  _test-input-stream/imm32
     246     # . . call
     247     e8/call  write/disp32
     248     # . . discard args
     249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     250     # . write(_test-input-stream, "x:\n")
     251     # . . push args
     252     68/push  "x:\n"/imm32
     253     68/push  _test-input-stream/imm32
     254     # . . call
     255     e8/call  write/disp32
     256     # . . discard args
     257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     258     # . write(_test-input-stream, "01\n")
     259     # . . push args
     260     68/push  "01\n"/imm32
     261     68/push  _test-input-stream/imm32
     262     # . . call
     263     e8/call  write/disp32
     264     # . . discard args
     265     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     266     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
     267     # . . push args
     268     68/push  _test-output-buffered-file/imm32
     269     68/push  _test-input-buffered-file/imm32
     270     # . . call
     271     e8/call  subx-survey/disp32
     272     # . . discard args
     273     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     274     # check trace
     275 +-- 26 lines: #?     # dump *Trace-stream ----------------------------------------------------------------------------------------------------------------------------------
     301     # . check-trace-contains("label 'x' is at address 0x00007c05.", msg)
     302     # . . push args
     303     68/push  "F - test-subx-survey-computes-addresses/0"/imm32
     304     68/push  "label 'x' is at address 0x00007c05."/imm32
     305     # . . call
     306     e8/call  check-trace-contains/disp32
     307     # . . discard args
     308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     309     # . epilogue
     310     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     311     5d/pop-to-ebp
     312     c3/return
     313
     314 test-subx-survey-computes-addresses-with-padding:
     315     # input:
     316     #   == code
     317     #   ab x/imm32
     318     #   == data 0x7c10
     319     #   x:
     320     #     01
     321     #
     322     # trace contains (in any order):
     323     #   label x is at address 0x7c10
     324     #
     325     # . prologue
     326     55/push-ebp
     327     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     328     # setup
     329     # . clear-stream(_test-input-stream)
     330     # . . push args
     331     68/push  _test-input-stream/imm32
     332     # . . call
     333     e8/call  clear-stream/disp32
     334     # . . discard args
     335     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     336     # . clear-stream($_test-input-buffered-file->buffer)
     337     # . . push args
     338     68/push  $_test-input-buffered-file->buffer/imm32
     339     # . . call
     340     e8/call  clear-stream/disp32
     341     # . . discard args
     342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     343     # . clear-stream(_test-output-stream)
     344     # . . push args
     345     68/push  _test-output-stream/imm32
     346     # . . call
     347     e8/call  clear-stream/disp32
     348     # . . discard args
     349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     350     # . clear-stream($_test-output-buffered-file->buffer)
     351     # . . push args
     352     68/push  $_test-output-buffered-file->buffer/imm32
     353     # . . call
     354     e8/call  clear-stream/disp32
     355     # . . discard args
     356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     357     # initialize input
     358     # . write(_test-input-stream, "== code\n")
     359     # . . push args
     360     68/push  "== code\n"/imm32
     361     68/push  _test-input-stream/imm32
     362     # . . call
     363     e8/call  write/disp32
     364     # . . discard args
     365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     366     # . write(_test-input-stream, "ab x/imm32\n")
     367     # . . push args
     368     68/push  "ab x/imm32\n"/imm32
     369     68/push  _test-input-stream/imm32
     370     # . . call
     371     e8/call  write/disp32
     372     # . . discard args
     373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     374     # . write(_test-input-stream, "== data\n")
     375     # . . push args
     376     68/push  "== data 0x7c10\n"/imm32
     377     68/push  _test-input-stream/imm32
     378     # . . call
     379     e8/call  write/disp32
     380     # . . discard args
     381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     382     # . write(_test-input-stream, "x:\n")
     383     # . . push args
     384     68/push  "x:\n"/imm32
     385     68/push  _test-input-stream/imm32
     386     # . . call
     387     e8/call  write/disp32
     388     # . . discard args
     389     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     390     # . write(_test-input-stream, "01\n")
     391     # . . push args
     392     68/push  "01\n"/imm32
     393     68/push  _test-input-stream/imm32
     394     # . . call
     395     e8/call  write/disp32
     396     # . . discard args
     397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     398     # subx-survey(_test-input-buffered-file, _test-output-buffered-file)
     399     # . . push args
     400     68/push  _test-output-buffered-file/imm32
     401     68/push  _test-input-buffered-file/imm32
     402     # . . call
     403     e8/call  subx-survey/disp32
     404     # . . discard args
     405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     406     # check trace
     407 +-- 26 lines: #?     # dump *Trace-stream ----------------------------------------------------------------------------------------------------------------------------------
     433     # . check-trace-contains("label 'x' is at address 0x00007c10.", msg)
     434     # . . push args
     435     68/push  "F - test-subx-survey-computes-addresses-with-padding/0"/imm32
     436     68/push  "label 'x' is at address 0x00007c10."/imm32
     437     # . . call
     438     e8/call  check-trace-contains/disp32
     439     # . . discard args
     440     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     441     # . epilogue
     442     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     443     5d/pop-to-ebp
     444     c3/return
     445
     446 compute-addresses:  # in: (addr stream byte), labels: (addr stream {(handle array byte), address})
     447     # pseudocode:
     448     #   var current-address = 0x7c00
     449     #   var line: (stream byte 512)
     450     #   while true                                  # line loop
     451     #     clear-stream(line)
     452     #     read-line(in, line)
     453     #     if (line->write == 0) break               # end of file
     454     #     while true                                # word loop
     455     #       word-slice = next-word(line)
     456     #       if slice-empty?(word-slice)             # end of line
     457     #         break
     458     #       else if slice-starts-with?(word-slice, "#")  # comment
     459     #         break                                 # end of line
     460     #       else if slice-equal?(word-slice, "==")  # segment header
     461     #         word-slice = next-word(line)
     462     #         if slice-empty?(word-slice)
     463     #           abort
     464     #         word-slice = next-word(line)  # segment address
     465     #         if slice-empty?(word-slice)
     466     #           goto line-loop              # segment address is optional
     467     #         new-address = parse-hex-int-from-slice(word-slice)
     468     #         if new-address < current-address
     469     #           abort
     470     #         current-address = new-address
     471     #       else if label?(word-slice)
     472     #         strip trailing ':' from word-slice
     473     #         trace("label '" word-slice "' is at address " current-address ".")
     474     #         # labels occupy no space, so no need to increment offsets
     475     #       else
     476     #         width = compute-width-of-slice(word-slice)
     477     #         current-address += width
     478     #
     479     # . prologue
     480     55/push-ebp
     481     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     482     # . save registers
     483     50/push-eax
     484     51/push-ecx
     485     52/push-edx
     486     53/push-ebx
     487     56/push-esi
     488     57/push-edi
     489     # var current-address/esi: int = 0x7c00
     490     be/copy-to-esi  0x7c00/imm32
     491     # var line/ecx: (stream byte 512)
     492     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
     493     68/push  0x200/imm32/size
     494     68/push  0/imm32/read
     495     68/push  0/imm32/write
     496     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
     497     # var word-slice/edx: (addr slice)
     498     68/push  0/imm32
     499     68/push  0/imm32
     500     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     501 $compute-addresses:line-loop:
     502     # clear-stream(line)
     503     # . . push args
     504     51/push-ecx
     505     # . . call
     506     e8/call  clear-stream/disp32
     507     # . . discard args
     508     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     509     # read-line(in, line)
     510     # . . push args
     511     51/push-ecx
     512     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     513     # . . call
     514     e8/call  read-line/disp32
     515     # . . discard args
     516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     517     # if (line->write == 0) break
     518     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
     519     3d/compare-eax-and  0/imm32
     520     0f 84/jump-if-=  $compute-addresses:end/disp32
     521 +-- 33 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
     554 $compute-addresses:word-loop:
     555     # next-word(line, word-slice)
     556     # . . push args
     557     52/push-edx
     558     51/push-ecx
     559     # . . call
     560     e8/call  next-word/disp32
     561     # . . discard args
     562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     563 $compute-addresses:case-empty:
     564     # if slice-empty?(word-slice) break
     565     # . eax = slice-empty?(word-slice)
     566     # . . push args
     567     52/push-edx
     568     # . . call
     569     e8/call  slice-empty?/disp32
     570     # . . discard args
     571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     572     # . if (eax != false) break
     573     3d/compare-eax-and  0/imm32/false
     574     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
     575 $compute-addresses:case-comment:
     576     # if slice-starts-with?(word-slice, "#") break
     577     # . . push args
     578     68/push  "#"/imm32
     579     52/push-edx
     580     # . . call
     581     e8/call  slice-starts-with?/disp32
     582     # . . discard args
     583     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     584     # . if (eax != false) break
     585     3d/compare-eax-and  0/imm32/false
     586     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
     587 $compute-addresses:case-segment-header:
     588     # if !slice-equal?(word-slice, "==") goto next case
     589     # . eax = slice-equal?(word-slice, "==")
     590     # . . push args
     591     68/push  "=="/imm32
     592     52/push-edx
     593     # . . call
     594     e8/call  slice-equal?/disp32
     595     # . . discard args
     596     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     597     # . if (eax != false) break
     598     3d/compare-eax-and  0/imm32/false
     599     0f 84/jump-if-=  $compute-addresses:case-label/disp32
     600     # next-word(line, word-slice)
     601     # . . push args
     602     52/push-edx
     603     51/push-ecx
     604     # . . call
     605     e8/call  next-word/disp32
     606     # . . discard args
     607     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     608     # if slice-empty?(word-slice) abort
     609     # . eax = slice-empty?(word-slice)
     610     # . . push args
     611     52/push-edx
     612     # . . call
     613     e8/call  slice-empty?/disp32
     614     # . . discard args
     615     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     616     # . if (eax != false) abort
     617     3d/compare-eax-and  0/imm32/false
     618     0f 85/jump-if-!=  $compute-addresses:abort/disp32
     619     # next-word(line, word-slice)
     620     # . . push args
     621     52/push-edx
     622     51/push-ecx
     623     # . . call
     624     e8/call  next-word/disp32
     625     # . . discard args
     626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     627     # if slice-empty?(word-slice) break
     628     # . eax = slice-empty?(word-slice)
     629     # . . push args
     630     52/push-edx
     631     # . . call
     632     e8/call  slice-empty?/disp32
     633     # . . discard args
     634     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     635     # . if (eax != false) break
     636     3d/compare-eax-and  0/imm32/false
     637     0f 85/jump-if-!=  $compute-addresses:line-loop/disp32
     638     # var new-address/eax: int = parse-hex-int-from-slice(word-slice)
     639     # . . push args
     640     52/push-edx
     641     # . . call
     642     e8/call  parse-hex-int-from-slice/disp32
     643     # . . discard args
     644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     645     # if (new-address < current-address) abort
     646     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           6/r32/esi   .               .                 # compare eax with esi
     647     0f 82/jump-if-addr<  $compute-addresses:error-bad-segment-address/disp32
     648     # current-address = new-address
     649     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
     650     # break
     651     e9/jump  $compute-addresses:line-loop/disp32
     652 $compute-addresses:case-label:
     653     # if (!label?(word-slice)) goto next case
     654     # . eax = label?(word-slice)
     655     # . . push args
     656     52/push-edx
     657     # . . call
     658     e8/call  label?/disp32
     659     # . . discard args
     660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     661     # . if (eax == false) goto next case
     662     3d/compare-eax-and  0/imm32/false
     663     0f 84/jump-if-=  $compute-addresses:case-default/disp32
     664     # strip trailing ':' from word-slice
     665     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # decrement *(edx+4)
     666     # var tmp/eax: (addr int) = get-or-insert-slice(labels, word-slice, row-size=12)
     667     # . . push args
     668     68/push  Heap/imm32
     669     68/push  0xc/imm32/row-size
     670     52/push-edx
     671     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
     672     # . . call
     673     e8/call  get-or-insert-slice/disp32
     674     # . . discard args
     675     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
     676     # *tmp = current-address
     677     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           6/r32/esi   .               .                 # copy esi to *eax
     678     # trace-slsns("label '" word-slice "' is at address " current-address ".")
     679     # . . push args
     680     68/push  "."/imm32
     681     56/push-esi
     682     68/push  "' is at address "/imm32
     683     52/push-edx
     684     68/push  "label '"/imm32
     685     # . . call
     686     e8/call  trace-slsns/disp32
     687     # . . discard args
     688     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
     689     # continue
     690     e9/jump  $compute-addresses:word-loop/disp32
     691 $compute-addresses:case-default:
     692     # width/eax = compute-width-of-slice(word-slice)
     693     # . . push args
     694     52/push-edx
     695     # . . call
     696     e8/call compute-width-of-slice/disp32
     697     # . . discard args
     698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     699     # current-address += width
     700     01/add                          3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # add eax to *esi
     701 +-- 41 lines: #?     # dump segment-offset ---------------------------------------------------------------------------------------------------------------------------------
     742     e9/jump $compute-addresses:word-loop/disp32
     743 $compute-addresses:end:
     744     # . reclaim locals
     745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
     746     # . restore registers
     747     5f/pop-to-edi
     748     5e/pop-to-esi
     749     5b/pop-to-ebx
     750     5a/pop-to-edx
     751     59/pop-to-ecx
     752     58/pop-to-eax
     753     # . epilogue
     754     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     755     5d/pop-to-ebp
     756     c3/return
     757
     758 $compute-addresses:abort:
     759     # . _write(2/stderr, error)
     760     # . . push args
     761     68/push  "'==' must be followed by segment name and optionally an address\n"/imm32
     762     68/push  2/imm32/stderr
     763     # . . call
     764     e8/call  _write/disp32
     765     # . . discard args
     766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     767     # . syscall(exit, 1)
     768     bb/copy-to-ebx  1/imm32
     769     e8/call  syscall_exit/disp32
     770     # never gets here
     771
     772 $compute-addresses:error-bad-segment-address:
     773     # . _write(2/stderr, error)
     774     # . . push args
     775     68/push  "'==' specifies an address that implies negative padding\n"/imm32
     776     68/push  2/imm32/stderr
     777     # . . call
     778     e8/call  _write/disp32
     779     # . . discard args
     780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     781     # . syscall(exit, 1)
     782     bb/copy-to-ebx  1/imm32
     783     e8/call  syscall_exit/disp32
     784     # never gets here
     785
     786 test-compute-addresses:
     787     # input:
     788     #   == code
     789     #   ab x/imm32  # skip comment
     790     #   == data
     791     #   00
     792     #   x:
     793     #     34
     794     #
     795     # trace contains (in any order):
     796     #   label 'x' is at address 0x7c06.
     797     #
     798     # . prologue
     799     55/push-ebp
     800     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     801     # setup
     802     # . clear-stream(_test-input-stream)
     803     # . . push args
     804     68/push  _test-input-stream/imm32
     805     # . . call
     806     e8/call  clear-stream/disp32
     807     # . . discard args
     808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     809     # var labels/edx: (stream byte 2*12)
     810     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # subtract from esp
     811     68/push  0x18/imm32/size
     812     68/push  0/imm32/read
     813     68/push  0/imm32/write
     814     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
     815     # initialize input
     816     # . write(_test-input-stream, "== code\n")
     817     # . . push args
     818     68/push  "== code\n"/imm32
     819     68/push  _test-input-stream/imm32
     820     # . . call
     821     e8/call  write/disp32
     822     # . . discard args
     823     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     824     # . write(_test-input-stream, "ab x/imm32  # skip comment\n")
     825     # . . push args
     826     68/push  "ab x/imm32  # skip comment\n"/imm32
     827     68/push  _test-input-stream/imm32
     828     # . . call
     829     e8/call  write/disp32
     830     # . . discard args
     831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     832     # . write(_test-input-stream, "== data\n")
     833     # . . push args
     834     68/push  "== data\n"/imm32
     835     68/push  _test-input-stream/imm32
     836     # . . call
     837     e8/call  write/disp32
     838     # . . discard args
     839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     840     # . write(_test-input-stream, "00\n")
     841     # . . push args
     842     68/push  "00\n"/imm32
     843     68/push  _test-input-stream/imm32
     844     # . . call
     845     e8/call  write/disp32
     846     # . . discard args
     847     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     848     # . write(_test-input-stream, "x:\n")
     849     # . . push args
     850     68/push  "x:\n"/imm32
     851     68/push  _test-input-stream/imm32
     852     # . . call
     853     e8/call  write/disp32
     854     # . . discard args
     855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     856     # . write(_test-input-stream, "34\n")
     857     # . . push args
     858     68/push  "34\n"/imm32
     859     68/push  _test-input-stream/imm32
     860     # . . call
     861     e8/call  write/disp32
     862     # . . discard args
     863     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     864     # compute-addresses(_test-input-stream, labels)
     865     # . . push args
     866     52/push-edx
     867     68/push  _test-input-stream/imm32
     868     # . . call
     869     e8/call  compute-addresses/disp32
     870     # . . discard args
     871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32          # add to esp
     872 +-- 26 lines: #?     # dump *Trace-stream ----------------------------------------------------------------------------------------------------------------------------------
     898     # . check-trace-contains("label 'x' is at address 0x00007c06.", msg)
     899     # . . push args
     900     68/push  "F - test-compute-addresses"/imm32
     901     68/push  "label 'x' is at address 0x00007c06."/imm32
     902     # . . call
     903     e8/call  check-trace-contains/disp32
     904     # . . discard args
     905     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     906     # . check-ints-equal(labels->write, 0xc, msg)
     907     # . . push args
     908     68/push  "F - test-compute-addresses-maintains-labels-write-index"/imm32
     909     68/push  0xc/imm32/1-entry
     910     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
     911     # . . call
     912     e8/call  check-ints-equal/disp32
     913     # . . discard args
     914     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
     915     # . reclaim locals
     916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x24/imm32        # add to esp
     917     # . epilogue
     918     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
     919     5d/pop-to-ebp
     920     c3/return
     921
     922 emit-labels:  # out: (addr buffered-file), labels: (addr stream {(handle array byte), address})
     923     # pseudocode:
     924     #   curr = table->data
     925     #   max = &table->data[table->write]
     926     #   while curr < max
     927     #     var label: (addr array byte) = lookup(*curr)
     928     #     curr += 8
     929     #     write-buffered(out, label)
     930     #     write-buffered(out, ' ')
     931     #     write-buffered(out, *curr)
     932     #     curr += 4
     933     #     write(out, '\n')
     934     #   return 0
     935     #
     936     # . prologue
     937     55/push-ebp
     938     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
     939     # . save registers
     940     50/push-eax
     941     51/push-ecx
     942     52/push-edx
     943     56/push-esi
     944     # esi = table
     945     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
     946     # var curr/ecx: (addr handle array byte) = table->data
     947     8d/copy-address                 1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy esi+12 to ecx
     948     # var max/edx: (addr byte) = &table->data[table->write]
     949     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           2/r32/edx   .               .                 # copy *esi to edx
     950     8d/copy-address                 0/mod/indirect  4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   .               .                 # copy ecx+edx to edx
     951 $emit-labels:loop:
     952     # if (curr >= max) return null
     953     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
     954     0f 83/jump-if-addr>=  $emit-labels:end/disp32
     955     # var label/eax: (addr array byte) = lookup(*curr)
     956     # . . push args
     957     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
     958     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     959     # . . call
     960     e8/call  lookup/disp32
     961     # . . discard args
     962     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     963     # curr += 8
     964     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               8/imm32           # add to ecx
     965     # write-buffered(out, label)
     966     # . . push args
     967     50/push-eax
     968     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     969     # . . call
     970     e8/call  write-buffered/disp32
     971     # . . discard args
     972     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     973     # write-buffered(out, ' ')
     974     # . . push args
     975     68/push  Space/imm32
     976     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     977     # . . call
     978     e8/call  write-buffered/disp32
     979     # . . discard args
     980     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     981     # . flush(Stderr)
     982     # . . push args
     983     68/push  Stderr/imm32
     984     # . . call
     985     e8/call  flush/disp32
     986     # . . discard args
     987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
     988     # write-int32-hex-buffered(out, *curr)
     989     # . . push args
     990     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
     991     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
     992     # . . call
     993     e8/call  write-int32-hex-buffered/disp32
     994     # . . discard args
     995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
     996     # curr += 4
     997     81          0/subop/add         3/mod/direct    1/rm32/ecx    .           .             .           .           .               4/imm32           # add to ecx
     998     # write-buffered(out, '\n')
     999     # . . push args
    1000     68/push  Newline/imm32
    1001     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1002     # . . call
    1003     e8/call  write-buffered/disp32
    1004     # . . discard args
    1005     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1006     # loop
    1007     e9/jump  $emit-labels:loop/disp32
    1008 $emit-labels:end:
    1009     # . restore registers
    1010     5e/pop-to-esi
    1011     5a/pop-to-edx
    1012     59/pop-to-ecx
    1013     58/pop-to-eax
    1014     # . epilogue
    1015     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1016     5d/pop-to-ebp
    1017     c3/return
    1018
    1019 # - some helpers for tests
    1020
    1021 # some variants of 'trace' that take multiple arguments in different combinations of types:
    1022 #   n: int
    1023 #   c: character [4-bytes, will eventually be UTF-8]
    1024 #   s: (addr array byte)
    1025 #   l: (addr slice)
    1026 # one gotcha: 's5' must not be empty
    1027
    1028 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
    1029     # . prologue
    1030     55/push-ebp
    1031     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1032     # write(*Trace-stream, s1)
    1033     # . . push args
    1034     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
    1035     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    1036     # . . call
    1037     e8/call  write/disp32
    1038     # . . discard args
    1039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1040     # write-slice(*Trace-stream, l2)
    1041     # . . push args
    1042     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
    1043     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    1044     # . . call
    1045     e8/call  write-slice/disp32
    1046     # . . discard args
    1047     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1048     # write(*Trace-stream, s3)
    1049     # . . push args
    1050     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
    1051     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    1052     # . . call
    1053     e8/call  write/disp32
    1054     # . . discard args
    1055     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1056     # write-int32-hex(*Trace-stream, n4)
    1057     # . . push args
    1058     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
    1059     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
    1060     # . . call
    1061     e8/call  write-int32-hex/disp32
    1062     # . . discard args
    1063     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1064     # trace(s5)  # implicitly adds a newline and finalizes the trace line
    1065     # . . push args
    1066     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
    1067     # . . call
    1068     e8/call  trace/disp32
    1069     # . . discard args
    1070     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    1071 $trace-slsns:end:
    1072     # . epilogue
    1073     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1074     5d/pop-to-ebp
    1075     c3/return
    1076
    1077 test-trace-slsns:
    1078     # . prologue
    1079     55/push-ebp
    1080     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    1081     # setup
    1082     # . *Trace-stream->write = 0
    1083     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
    1084     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
    1085     # (eax..ecx) = "b"
    1086     b8/copy-to-eax  "b"/imm32
    1087     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
    1088     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
    1089     05/add-to-eax  4/imm32
    1090     # var b/ebx: slice = {eax, ecx}
    1091     51/push-ecx
    1092     50/push-eax
    1093     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
    1094     # trace-slsls("A" b "c " 3 " e")
    1095     # . . push args
    1096     68/push  " e"/imm32
    1097     68/push  3/imm32
    1098     68/push  "c "/imm32
    1099     53/push-ebx
    1100     68/push  "A"/imm32
    1101     # . . call
    1102     e8/call  trace-slsns/disp32
    1103     # . . discard args
    1104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
    1105 +-- 26 lines: #?     # dump *Trace-stream ----------------------------------------------------------------------------------------------------------------------------------
    1131     # check-trace-contains("Abc 0x00000003 e")
    1132     # . . push args
    1133     68/push  "F - test-trace-slsls"/imm32
    1134     68/push  "Abc 0x00000003 e"/imm32
    1135     # . . call
    1136     e8/call  check-trace-contains/disp32
    1137     # . . discard args
    1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    1139     # . epilogue
    1140     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    1141     5d/pop-to-ebp
    1142     c3/return
    1143
    1144 # . . vim:nowrap:textwidth=0