https://github.com/akkartik/mu/blob/main/linux/labels_baremetal.subx
   1 # Assign addresses (co-ordinates) to instructions (landmarks) in a program
   2 # (landscape).
   3 # Read a list of addresses and labels from a file called 'labels',
   4 # then replace labels in stdin with their addresses.
   5 #
   6 # To build:
   7 #   $ bootstrap/bootstrap translate [01]*.subx subx-params.subx labels_baremetal.subx  -o labels_baremetal
   8 #
   9 # Stdin should be a stream of bytes and some interspersed labels. Comments and
  10 # '==' segment headers are allowed, but names are ignored. The emitted code
  11 # will all lie in a single contiguous address range starting at address 0x7c00.
  12 # Addresses in segment headers are optional. If provided, this program will
  13 # insert padding in the output until the desired address is reached.
  14 #
  15 #   $ cat x
  16 #   == code
  17 #   l1:
  18 #   aa bb l1/imm8
  19 #   cc dd l2/disp32
  20 #   l2:
  21 #   ee foo/imm32
  22 #   == data 0x7c10
  23 #   foo:
  24 #     34
  25 #
  26 # The output is the stream of bytes without segment headers or label definitions,
  27 # and with label references replaced with numeric values/displacements.
  28 #
  29 #   $ cat x  |bootstrap/bootstrap run survey_baremetal labels
  30 #   # 0x7c00
  31 #   aa bb nn  # some computed address
  32 #   cc dd nn nn nn nn  # some computed displacement
  33 #   ee nn nn nn nn  # address right after this instruction
  34 #   # 0x7c0e
  35 #   00 00  # padding
  36 #   # 0x7c10
  37 #   34  # data segment interleaved with code
  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-<=  $subx-labels-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-=  $subx-labels-main:interactive/disp8
  80     # run-tests()
  81 #?     e8/call  test-emit-output-with-padding/disp32
  82     e8/call  run-tests/disp32
  83     # syscall(exit, *Num-test-failures)
  84     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  85     eb/jump  $subx-labels-main:end/disp8
  86 $subx-labels-main:interactive:
  87     # - otherwise convert stdin
  88     # var labels-file/esi: (addr buffered-file) from syscall_open("labels", READ)
  89     # . data
  90     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x1000/imm32      # subtract from esp
  91     # . size
  92     68/push  0x1000/imm32
  93     # . read
  94     68/push  0/imm32/read
  95     # . write
  96     68/push  0/imm32/write
  97     # . fd = syscall_open("labels", READ)
  98     bb/copy-to-ebx  Label-file/imm32
  99     b9/copy-to-ecx  0/imm32/read-mode
 100     ba/copy-to-edx  0x180/imm32/permissions
 101     e8/call syscall_open/disp32
 102     50/push-eax
 103     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 104     # subx-labels(Stdin, label-fd, Stdout)
 105     # . . push args
 106     68/push  Stdout/imm32
 107     56/push-esi
 108     68/push  Stdin/imm32
 109     # . . call
 110     e8/call  subx-labels/disp32
 111     # . . discard args
 112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 113 #?     # . write-stream(2/stderr, Trace-stream)
 114 #?     # . . push args
 115 #?     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
 116 #?     68/push  2/imm32/stderr
 117 #?     # . . call
 118 #?     e8/call  write-stream/disp32
 119 #?     # . . discard args
 120 #?     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 121     # syscall(exit, 0)
 122     bb/copy-to-ebx  0/imm32
 123 $subx-labels-main:end:
 124     e8/call  syscall_exit/disp32
 125 
 126 == data
 127 
 128 Label-file:
 129   6c/l 61/a 62/b 65/e 6c/l 73/s 00/nul
 130 
 131 == code
 132 
 133 subx-labels:  # infile: (addr buffered-file), labels-file: (addr buffered-file), out: (addr buffered-file)
 134     # pseudocode
 135     #   var labels: (stream {label-name, address} Max-labels)
 136     #   load-labels(labels-file, labels)
 137     #   var in: (stream byte Input-size)
 138     #   slurp(infile, in)
 139     #   emit-output(in, out, labels)
 140     #
 141     # . prologue
 142     55/push-ebp
 143     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 144     # . save registers
 145     52/push-edx
 146     56/push-esi
 147     # var labels/edx: (stream {label-name, address} Max-labels)
 148     # (we get more rows than Max-labels advertises because row size is smaller than in survey_elf)
 149     # . data
 150     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # subtract *Max-labels from esp
 151     # . size
 152     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Max-labels/disp32                 # push *Max-labels
 153     # . read
 154     68/push  0/imm32/read
 155     # . write
 156     68/push  0/imm32/write
 157     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 158     # load-labels(labels-file, labels)
 159     # . . push args
 160     52/push-edx
 161     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 162     # . . call
 163     e8/call  load-labels/disp32
 164     # . . discard args
 165     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 166     # var in/esi: (stream byte Input-size)
 167     # . data
 168     2b/subtract                     0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # subtract *Input-size from esp
 169     # . size
 170     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Input-size/disp32                 # push *Input-size
 171     # . read
 172     68/push  0/imm32/read
 173     # . write
 174     68/push  0/imm32/write
 175     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
 176     # slurp(infile, in)
 177     # . . push args
 178     56/push-esi
 179     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 180     # . . call
 181     e8/call  slurp/disp32
 182     # . . discard args
 183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 184     # emit-output(in, out, labels)
 185     # . . push args
 186     52/push-edx
 187     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 188     56/push-esi
 189     # . . call
 190     e8/call  emit-output/disp32
 191     # . . discard args
 192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 193     # flush(out)
 194     # . . push args
 195     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 196     # . . call
 197     e8/call  flush/disp32
 198     # . . discard args
 199     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 200 $subx-labels:end:
 201     # . reclaim locals
 202     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x18/imm32        # add to esp
 203     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Max-labels/disp32                 # add *Max-labels to esp
 204     03/add                          0/mod/indirect  5/rm32/.disp32            .             .           4/r32/esp   Input-size/disp32                 # add *Input-size to esp
 205     # . restore registers
 206     5e/pop-to-esi
 207     5a/pop-to-edx
 208     # . epilogue
 209     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 210     5d/pop-to-ebp
 211     c3/return
 212 
 213 load-labels:  # in: (addr buffered-file), labels: (stream {label-name, address} Max-labels)
 214     # pseudocode
 215     #   var line: (stream byte 512)
 216     #   while true
 217     #     clear-stream(line)
 218     #     read-line-buffered(in, line)
 219     #     if (line->write == 0) break               # end of file
 220     #     var word-slice/ecx: (addr slice) = next-word(line)
 221     #     var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row)
 222     #     word-slice = next-word(line)
 223     #     var address/eax: int = parse-hex-int-from-slice(word-slice)
 224     #     *dest = address
 225     #
 226     # . prologue
 227     55/push-ebp
 228     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 229     # . save registers
 230     50/push-eax
 231     51/push-ecx
 232     52/push-edx
 233     56/push-esi
 234     57/push-edi
 235     # var line/ecx: (stream byte 512)
 236     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 237     68/push  0x200/imm32/size
 238     68/push  0/imm32/read
 239     68/push  0/imm32/write
 240     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 241     # var word-slice/edx: slice
 242     68/push  0/imm32/end
 243     68/push  0/imm32/start
 244     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 245 $load-labels:loop:
 246     # clear-stream(line)
 247     # . . push args
 248     51/push-ecx
 249     # . . call
 250     e8/call  clear-stream/disp32
 251     # . . discard args
 252     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 253     # read-line-buffered(in, line)
 254     # . . push args
 255     51/push-ecx
 256     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 257     # . . call
 258     e8/call  read-line-buffered/disp32
 259     # . . discard args
 260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 261 +-- 33 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
 294     # if (line->write == 0) break
 295     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
 296     0f 84/jump-if-=  $load-labels:end/disp32
 297     # next-word(line, word-slice)
 298     # . . push args
 299     52/push-edx
 300     51/push-ecx
 301     # . . call
 302     e8/call  next-word/disp32
 303     # . . discard args
 304     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 305 +-- 33 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
 338     # var dest/edi: (addr int) = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap)
 339     # . eax = get-or-insert-slice(labels, word-slice, 12 bytes/row, Heap)
 340     # . . push args
 341     68/push  Heap/imm32
 342     68/push  0xc/imm32/row-size
 343     52/push-edx
 344     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 345     # . . call
 346     e8/call  get-or-insert-slice/disp32
 347     # . . discard args
 348     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 349     # . edi = eax
 350     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
 351     # next-word(line, word-slice)
 352     # . . push args
 353     52/push-edx
 354     51/push-ecx
 355     # . . call
 356     e8/call  next-word/disp32
 357     # . . discard args
 358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 359     # var address/esi: int = parse-hex-int-from-slice(word-slice)
 360     # . . push args
 361     52/push-edx
 362     # . . call
 363     e8/call  parse-hex-int-from-slice/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 366     # . esi = eax
 367     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
 368     # *dest = address
 369     89/copy                         0/mod/indirect  7/rm32/edi    .           .                         6/r32/esi   .               .                 # copy esi to *edi
 370     #
 371     e9/jump  $load-labels:loop/disp32
 372 $load-labels:end:
 373     # . reclaim locals
 374     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 375     # . restore registers
 376     5f/pop-to-edi
 377     5e/pop-to-esi
 378     5a/pop-to-edx
 379     59/pop-to-ecx
 380     58/pop-to-eax
 381     # . epilogue
 382     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 383     5d/pop-to-ebp
 384     c3/return
 385 
 386 # global scratch space for emit-output
 387 == data
 388 
 389 emit-output:datum:  # slice
 390   0/imm32/start
 391   0/imm32/end
 392 
 393 == code
 394 
 395 emit-output:  # in: (addr stream byte), out: (addr buffered-file), labels: (addr stream {(handle array byte), address})
 396     # pseudocode:
 397     #   var address-of-next-instruction = 0x7c00
 398     #   var line: (stream byte 512)
 399     #   line-loop:
 400     #   while true
 401     #     clear-stream(line)
 402     #     read-line(in, line)
 403     #     if (line->write == 0) break               # end of file
 404     #     address-of-next-instruction += num-bytes(line)
 405     #     var far-jump-or-call? = far-jump-or-call?(line)
 406     #     rewind-stream(line)
 407     #     while true
 408     #       var word-slice = next-word(line)
 409     #       if slice-empty?(word-slice)             # end of line
 410     #         break
 411     #       if slice-starts-with?(word-slice, "#")  # comment
 412     #         break
 413     #       if label?(word-slice)                # no need for label declarations anymore
 414     #         goto line-loop                        # don't insert empty lines
 415     #       if slice-equal?(word-slice, "==")       # no need for segment header lines
 416     #         word-slice = next-word(line)          # skip segment name
 417     #         word-slice = next-word(line)
 418     #         if !slice-empty?(word-slice)
 419     #           new-address = parse-hex-int-from-slice(word-slice)
 420     #           write-buffered(out, "# " address-of-next-instruction "\n")
 421     #           while address-of-next-instruction < new-address
 422     #             write-buffered("00")
 423     #             ++address-of-next-instruction
 424     #           write-buffered(out, "# " address-of-next-instruction "\n")
 425     #           goto line-loop                      # don't insert empty lines
 426     #       if length(word-slice) == 2
 427     #         write-slice-buffered(out, word-slice)
 428     #         write-buffered(out, " ")
 429     #         continue
 430     #       var datum: (addr slice) = next-token-from-slice(word-slice->start, word-slice->end, "/")
 431     #       var address: (addr int) = get-slice(labels, datum)
 432     #       if has-metadata?(word-slice, "imm8")
 433     #         emit(out, *address, 1)
 434     #       else if has-metadata?(word-slice, "imm16")
 435     #         emit(out, *address, 2)
 436     #       else if has-metadata?(word-slice, "imm32")
 437     #         emit(out, *address, 4)
 438     #       else if has-metadata?(word-slice, "disp8")
 439     #         value = *address - address-of-next-instruction
 440     #         emit(out, value, 1)
 441     #       else if has-metadata?(word-slice, "disp32")
 442     #         if far-jump-or-call?
 443     #           value = *address - address-of-next-instruction
 444     #         else
 445     #           value = *address
 446     #         emit(out, value, 4)
 447     #       else
 448     #         abort
 449     #     write-buffered(out, "\n")
 450     #
 451     # registers:
 452     #   line: ecx
 453     #   word-slice: edx
 454     #   address-of-next-instruction: ebx
 455     #   far-jump-or-call?: edi
 456     #   address: esi (inner loop only)
 457     #   temporaries: eax, esi (outer loop)
 458     #
 459     # . prologue
 460     55/push-ebp
 461     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 462     # . save registers
 463     50/push-eax
 464     51/push-ecx
 465     52/push-edx
 466     53/push-ebx
 467     56/push-esi
 468     57/push-edi
 469     # var line/ecx: (stream byte 512)
 470     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 471     68/push  0x200/imm32/size
 472     68/push  0/imm32/read
 473     68/push  0/imm32/write
 474     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 475     # var word-slice/edx: slice
 476     68/push  0/imm32/end
 477     68/push  0/imm32/start
 478     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 479     # var address-of-next-instruction/ebx = 0x7c00
 480     bb/copy-to-ebx  0x7c00/imm32
 481 $emit-output:line-loop:
 482     # clear-stream(line)
 483     # . . push args
 484     51/push-ecx
 485     # . . call
 486     e8/call  clear-stream/disp32
 487     # . . discard args
 488     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 489     # read-line(in, line)
 490     # . . push args
 491     51/push-ecx
 492     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 493     # . . call
 494     e8/call  read-line/disp32
 495     # . . discard args
 496     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 497 +-- 33 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
 530 $emit-output:check-for-end-of-input:
 531     # if (line->write == 0) break
 532     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
 533     0f 84/jump-if-=  $emit-output:end/disp32
 534     # address-of-next-instruction += num-bytes(line)
 535     # . eax = num-bytes(line)
 536     # . . push args
 537     51/push-ecx
 538     # . . call
 539     e8/call  num-bytes/disp32
 540     # . . discard args
 541     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 542     # . ebx += eax
 543     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
 544     # var far-jump-or-call?/edi: boolean = far-jump-or-call?(line)
 545     # . . push args
 546     51/push-ecx
 547     # . . call
 548     e8/call  far-jump-or-call?/disp32
 549     # . . discard args
 550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 551     # rewind-stream(line)
 552     # . . push args
 553     51/push-ecx
 554     # . . call
 555     e8/call  rewind-stream/disp32
 556     # . . discard args
 557     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 558 $emit-output:word-loop:
 559     # next-word(line, word-slice)
 560     # . . push args
 561     52/push-edx
 562     51/push-ecx
 563     # . . call
 564     e8/call  next-word/disp32
 565     # . . discard args
 566     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 567 +-- 33 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
 600 $emit-output:check-for-end-of-line:
 601     # if (slice-empty?(word-slice)) break
 602     # . eax = slice-empty?(word-slice)
 603     # . . push args
 604     52/push-edx
 605     # . . call
 606     e8/call  slice-empty?/disp32
 607     # . . discard args
 608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 609     # . if (eax != 0) break
 610     3d/compare-eax-and  0/imm32/false
 611     0f 85/jump-if-!=  $emit-output:next-line/disp32
 612 $emit-output:check-for-comment:
 613     # if (slice-starts-with?(word-slice, "#")) break
 614     # . start/esi = word-slice->start
 615     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           6/r32/esi   .               .                 # copy *edx to esi
 616     # . c/eax = *start
 617     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 618     8a/copy-byte                    0/mod/indirect  6/rm32/esi    .           .             .           0/r32/AL    .               .                 # copy byte at *esi to AL
 619     # . if (eax == '#') break
 620     3d/compare-eax-and  0x23/imm32/hash
 621     0f 84/jump-if-=  $emit-output:next-line/disp32
 622 $emit-output:check-for-label:
 623     # if label?(word-slice) break
 624     # . eax = label?(word-slice)
 625     # . . push args
 626     52/push-edx
 627     # . . call
 628     e8/call  label?/disp32
 629     # . . discard args
 630     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 631     # . if (eax != false) break
 632     3d/compare-eax-and  0/imm32/false
 633     0f 85/jump-if-!=  $emit-output:line-loop/disp32
 634 $emit-output:check-for-segment-header:
 635     # if !slice-equal?(word-slice, "==") goto next check
 636     # . eax = slice-equal?(word-slice, "==")
 637     # . . push args
 638     68/push  "=="/imm32
 639     52/push-edx
 640     # . . call
 641     e8/call  slice-equal?/disp32
 642     # . . discard args
 643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 644     # . if (eax == false) goto next check
 645     3d/compare-eax-and  0/imm32/false
 646     0f 84/jump-if-=  $emit-output:2-character/disp32
 647     # skip segment name
 648     # . next-word(line, word-slice)
 649     # . . push args
 650     52/push-edx
 651     51/push-ecx
 652     # . . call
 653     e8/call  next-word/disp32
 654     # . . discard args
 655     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 656     # compute segment address if it exists
 657     # . next-word(line, word-slice)
 658     # . . push args
 659     52/push-edx
 660     51/push-ecx
 661     # . . call
 662     e8/call  next-word/disp32
 663     # . . discard args
 664     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 665     # . if slice-empty?(word-slice) goto padding-done
 666     # . . push args
 667     52/push-edx
 668     # . . call
 669     e8/call  slice-empty?/disp32
 670     # . . discard args
 671     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 672     # . .
 673     3d/compare-eax-and  0/imm32/false
 674     0f 85/jump-if-!=  $emit-output:padding-done/disp32
 675     # . var new-address/eax: int = parse-hex-int-from-slice(word-slice)
 676     # . . push args
 677     52/push-edx
 678     # . . call
 679     e8/call  parse-hex-int-from-slice/disp32
 680     # . . discard args
 681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 682     # write-buffered(out, "# " address-of-next-instruction "\n")
 683     # . write-buffered(out, "# ")
 684     # . . push args
 685     68/push  "# "/imm32
 686     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 687     # . . call
 688     e8/call  write-buffered/disp32
 689     # . . discard args
 690     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 691     # . write-int32-hex-buffered(out, address-of-next-instruction)
 692     # . . push args
 693     53/push-ebx
 694     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 695     # . . call
 696     e8/call  write-int32-hex-buffered/disp32
 697     # . . discard args
 698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 699     # . write-buffered(out, "\n")
 700     # . . push args
 701     68/push  Newline/imm32
 702     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 703     # . . call
 704     e8/call  write-buffered/disp32
 705     # . . discard args
 706     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 707 $emit-output:padding-loop:
 708     # if (address-of-next-instruction >= new-address) goto padding-loop-done
 709     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # compare ebx with eax
 710     73/jump-if-addr>=  $emit-output:padding-loop-done/disp8
 711     # if (address-of-next-instruction % 8 == 0) write-buffered("\n")
 712     53/push-ebx
 713     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               7/imm32           # bitwise and of ebx
 714     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
 715     5b/pop-to-ebx
 716     75/jump-if-!=  $emit-output:padding-core/disp8
 717     # . write-buffered(out, "\n")
 718     # . . push args
 719     68/push  Newline/imm32
 720     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 721     # . . call
 722     e8/call  write-buffered/disp32
 723     # . . discard args
 724     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 725 $emit-output:padding-core:
 726     # write-buffered("00")
 727     # . . push args
 728     68/push  "00 "/imm32
 729     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 730     # . . call
 731     e8/call  write-buffered/disp32
 732     # . . discard args
 733     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 734     # ++address-of-next-instruction
 735     43/increment-ebx
 736     # loop
 737     eb/jump $emit-output:padding-loop/disp8
 738 $emit-output:padding-loop-done:
 739     # . write-buffered(out, "\n")
 740     # . . push args
 741     68/push  Newline/imm32
 742     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 743     # . . call
 744     e8/call  write-buffered/disp32
 745     # . . discard args
 746     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 747 $emit-output:padding-done:
 748     # write-buffered(out, "# " address-of-next-instruction "\n")
 749     # . write-buffered(out, "# ")
 750     # . . push args
 751     68/push  "# "/imm32
 752     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 753     # . . call
 754     e8/call  write-buffered/disp32
 755     # . . discard args
 756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 757     # . write-int32-hex-buffered(out, address-of-next-instruction)
 758     # . . push args
 759     53/push-ebx
 760     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 761     # . . call
 762     e8/call  write-int32-hex-buffered/disp32
 763     # . . discard args
 764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 765     # . write-buffered(out, "\n")
 766     # . . push args
 767     68/push  Newline/imm32
 768     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 769     # . . call
 770     e8/call  write-buffered/disp32
 771     # . . discard args
 772     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 773     #
 774     e9/jump  $emit-output:line-loop/disp32
 775 $emit-output:2-character:
 776     # if (size(word-slice) != 2) goto next check
 777     # . eax = size(word-slice)
 778     8b/copy                         1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(edx+4) to eax
 779     2b/subtract                     0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # subtract *edx from eax
 780     # . if (eax != 2) goto next check
 781     3d/compare-eax-and  2/imm32
 782     75/jump-if-!=  $emit-output:check-metadata/disp8
 783     # write-slice-buffered(out, word-slice)
 784     # . . push args
 785     52/push-edx
 786     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 787     # . . call
 788     e8/call  write-slice-buffered/disp32
 789     # . . discard args
 790     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 791     # write-buffered(out, " ")
 792     # . . push args
 793     68/push  Space/imm32
 794     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 795     # . . call
 796     e8/call  write-buffered/disp32
 797     # . . discard args
 798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 799     # continue
 800     e9/jump  $emit-output:word-loop/disp32
 801 $emit-output:check-metadata:
 802     # - if we get here, 'word-slice' must be a label to be looked up
 803     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
 804     # . . push args
 805     68/push  emit-output:datum/imm32
 806     68/push  0x2f/imm32/slash
 807     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
 808     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
 809     # . . call
 810     e8/call  next-token-from-slice/disp32
 811     # . . discard args
 812     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 813 +-- 33 lines: #?     # dump datum ------------------------------------------------------------------------------------------------------------------------------------------
 846     # address/esi: (addr int) = get-slice(labels, datum, row-size=12, "label table")
 847     # . eax = get-slice(labels, datum, row-size=24, "label table")
 848     # . . push args
 849     68/push  "label table"/imm32
 850     68/push  0xc/imm32/row-size
 851     68/push  emit-output:datum/imm32
 852     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
 853     # . . call
 854     e8/call  get-slice/disp32
 855     # . . discard args
 856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 857     # . esi = eax
 858     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
 859 $emit-output:check-imm8:
 860     # if (!has-metadata?(word-slice, "imm8")) goto next check
 861     # . eax = has-metadata?(edx, "imm8")
 862     # . . push args
 863     68/push  "imm8"/imm32
 864     52/push-edx
 865     # . . call
 866     e8/call  has-metadata?/disp32
 867     # . . discard args
 868     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 869     # . if (eax != false) abort
 870     3d/compare-eax-and  0/imm32/false
 871     74/jump-if-=  $emit-output:check-imm16/disp8
 872 $emit-output:emit-imm8:
 873     # emit-hex(out, *address, 1)
 874     # . . push args
 875     68/push  1/imm32
 876     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
 877     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 878     # . . call
 879     e8/call  emit-hex/disp32
 880     # . . discard args
 881     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 882     # continue
 883     e9/jump  $emit-output:word-loop/disp32
 884 $emit-output:check-imm16:
 885     # if (!has-metadata?(word-slice, "imm16")) goto next check
 886     # . eax = has-metadata?(edx, "imm16")
 887     # . . push args
 888     68/push  "imm16"/imm32
 889     52/push-edx
 890     # . . call
 891     e8/call  has-metadata?/disp32
 892     # . . discard args
 893     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 894     # . if (eax == false) goto next check
 895     3d/compare-eax-and  0/imm32/false
 896     74/jump-if-=  $emit-output:check-imm32/disp8
 897 +-- 33 lines: #?     # dump *address ---------------------------------------------------------------------------------------------------------------------------------------
 930 $emit-output:emit-imm16:
 931     # emit-hex(out, *address, 2)
 932     # . . push args
 933     68/push  2/imm32
 934     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
 935     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 936     # . . call
 937     e8/call  emit-hex/disp32
 938     # . . discard args
 939     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 940     # TODO: ensure that the higher 2 bytes are zero
 941     # continue
 942     e9/jump  $emit-output:word-loop/disp32
 943 $emit-output:check-imm32:
 944     # if (!has-metadata?(word-slice, "imm32")) goto next check
 945     # . eax = has-metadata?(edx, "imm32")
 946     # . . push args
 947     68/push  "imm32"/imm32
 948     52/push-edx
 949     # . . call
 950     e8/call  has-metadata?/disp32
 951     # . . discard args
 952     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 953     # . if (eax == false) goto next check
 954     3d/compare-eax-and  0/imm32/false
 955     74/jump-if-=  $emit-output:check-disp8/disp8
 956 +-- 33 lines: #?     # dump *address ---------------------------------------------------------------------------------------------------------------------------------------
 989 $emit-output:emit-imm32:
 990     # emit-hex(out, *address, 4)
 991     # . . push args
 992     68/push  4/imm32
 993     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
 994     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 995     # . . call
 996     e8/call  emit-hex/disp32
 997     # . . discard args
 998     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 999     # continue
1000     e9/jump  $emit-output:word-loop/disp32
1001 $emit-output:check-disp8:
1002     # if (!has-metadata?(word-slice, "disp8")) goto next check
1003     # . eax = has-metadata?(edx, "disp8")
1004     # . . push args
1005     68/push  "disp8"/imm32
1006     52/push-edx
1007     # . . call
1008     e8/call  has-metadata?/disp32
1009     # . . discard args
1010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1011     # . if (eax == false) goto next check
1012     3d/compare-eax-and  0/imm32/false
1013     74/jump-if-=  $emit-output:check-disp16/disp8
1014 $emit-output:emit-disp8:
1015     # emit-hex(out, *address - address-of-next-instruction, 1)
1016     # . . push args
1017     68/push  1/imm32
1018     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1019     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
1020     50/push-eax
1021     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1022     # . . call
1023     e8/call  emit-hex/disp32
1024     # . . discard args
1025     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1026     # continue
1027     e9/jump  $emit-output:word-loop/disp32
1028 $emit-output:check-disp16:
1029     # if (!has-metadata?(word-slice, "disp16")) goto next check
1030     # . eax = has-metadata?(edx, "disp16")
1031     # . . push args
1032     68/push  "disp16"/imm32
1033     52/push-edx
1034     # . . call
1035     e8/call  has-metadata?/disp32
1036     # . . discard args
1037     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1038     # . if (eax == false) goto next check
1039     3d/compare-eax-and  0/imm32/false
1040     74/jump-if-=  $emit-output:check-disp32/disp8
1041 $emit-output:emit-disp16:
1042     # emit-hex(out, *address - address-of-next-instruction, 2)
1043     # . . push args
1044     68/push  2/imm32
1045     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1046     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
1047     50/push-eax
1048     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1049     # . . call
1050     e8/call  emit-hex/disp32
1051     # . . discard args
1052     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1053     # continue
1054     e9/jump  $emit-output:word-loop/disp32
1055 $emit-output:check-disp32:
1056     # if (!has-metadata?(word-slice, "disp32")) abort
1057     # . eax = has-metadata?(edx, "disp32")
1058     # . . push args
1059     68/push  "disp32"/imm32
1060     52/push-edx
1061     # . . call
1062     e8/call  has-metadata?/disp32
1063     # . . discard args
1064     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1065     # . if (eax == false) abort
1066     3d/compare-eax-and  0/imm32/false
1067     0f 84/jump-if-=  $emit-output:abort/disp32
1068 $emit-output:emit-disp32:
1069     # var value/eax = *address
1070     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1071     # if (far-jump-or-call?) value -= address-of-next-instruction
1072     81          7/subop/compare     3/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32/false     # compare edi
1073     74/jump-if-=  $emit-output:really-emit-disp32/disp8
1074     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # subtract ebx from eax
1075 $emit-output:really-emit-disp32:
1076     # emit-hex(out, value, 4)
1077     # . . push args
1078     68/push  4/imm32
1079     50/push-eax
1080     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1081     # . . call
1082     e8/call  emit-hex/disp32
1083     # . . discard args
1084     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1085     # continue
1086     e9/jump  $emit-output:word-loop/disp32
1087 $emit-output:next-line:
1088     # write-buffered(out, "\n")
1089     # . . push args
1090     68/push  Newline/imm32
1091     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1092     # . . call
1093     e8/call  write-buffered/disp32
1094     # . . discard args
1095     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1096     # loop
1097     e9/jump  $emit-output:line-loop/disp32
1098 $emit-output:end:
1099     # . reclaim locals
1100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
1101     # . restore registers
1102     5f/pop-to-edi
1103     5e/pop-to-esi
1104     5b/pop-to-ebx
1105     5a/pop-to-edx
1106     59/pop-to-ecx
1107     58/pop-to-eax
1108     # . epilogue
1109     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1110     5d/pop-to-ebp
1111     c3/return
1112 
1113 $emit-output:abort:
1114     # print(stderr, "missing metadata in " word-slice)
1115     # . _write(2/stderr, "missing metadata in word ")
1116     # . . push args
1117     68/push  "emit-output: missing metadata in "/imm32
1118     68/push  2/imm32/stderr
1119     # . . call
1120     e8/call  _write/disp32
1121     # . . discard args
1122     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1123     # . write-slice-buffered(Stderr, word-slice)
1124     # . . push args
1125     52/push-edx
1126     68/push  Stderr/imm32
1127     # . . call
1128     e8/call  write-slice-buffered/disp32
1129     # . . discard args
1130     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1131     # . flush(Stderr)
1132     # . . push args
1133     68/push  Stderr/imm32
1134     # . . call
1135     e8/call  flush/disp32
1136     # . . discard args
1137     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1138     # . syscall(exit, 1)
1139     bb/copy-to-ebx  1/imm32
1140     e8/call  syscall_exit/disp32
1141     # never gets here
1142 
1143 test-emit-output-non-far-control-flow:
1144     # labels turn into absolute addresses if opcodes are not far jumps or calls
1145     #
1146     # input:
1147     #   in:
1148     #     == code
1149     #     ab cd ef gh
1150     #     ij x/disp32
1151     #     == data
1152     #     00
1153     #     34
1154     #   labels:
1155     #     - 'x': 0x11223344
1156     #
1157     # output:
1158     #   ab cd ef gh
1159     #   ij 44 33 22 11
1160     #   00
1161     #   34
1162     #
1163     # . prologue
1164     55/push-ebp
1165     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1166     # setup
1167     # . clear-stream(_test-input-stream)
1168     # . . push args
1169     68/push  _test-input-stream/imm32
1170     # . . call
1171     e8/call  clear-stream/disp32
1172     # . . discard args
1173     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1174     # . clear-stream(_test-output-stream)
1175     # . . push args
1176     68/push  _test-output-stream/imm32
1177     # . . call
1178     e8/call  clear-stream/disp32
1179     # . . discard args
1180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1181     # . clear-stream($_test-output-buffered-file->buffer)
1182     # . . push args
1183     68/push  $_test-output-buffered-file->buffer/imm32
1184     # . . call
1185     e8/call  clear-stream/disp32
1186     # . . discard args
1187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1188     # . var labels/edx: (stream byte 8*24)
1189     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
1190     68/push  0xc0/imm32/size
1191     68/push  0/imm32/read
1192     68/push  0/imm32/write
1193     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1194     # . var h/ebx: (handle array byte)
1195     68/push  0/imm32
1196     68/push  0/imm32
1197     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1198     # initialize input
1199     # . write(_test-input-stream, "== code\n")
1200     # . . push args
1201     68/push  "== code\n"/imm32
1202     68/push  _test-input-stream/imm32
1203     # . . call
1204     e8/call  write/disp32
1205     # . . discard args
1206     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1207     # . write(_test-input-stream, "ab cd ef gh\n")
1208     # . . push args
1209     68/push  "ab cd ef gh\n"/imm32
1210     68/push  _test-input-stream/imm32
1211     # . . call
1212     e8/call  write/disp32
1213     # . . discard args
1214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1215     # . write(_test-input-stream, "ij x/disp32\n")
1216     # . . push args
1217     68/push  "ij x/disp32\n"/imm32
1218     68/push  _test-input-stream/imm32
1219     # . . call
1220     e8/call  write/disp32
1221     # . . discard args
1222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1223     # . write(_test-input-stream, "== data\n")
1224     # . . push args
1225     68/push  "== data\n"/imm32
1226     68/push  _test-input-stream/imm32
1227     # . . call
1228     e8/call  write/disp32
1229     # . . discard args
1230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1231     # . write(_test-input-stream, "00\n")
1232     # . . push args
1233     68/push  "00\n"/imm32
1234     68/push  _test-input-stream/imm32
1235     # . . call
1236     e8/call  write/disp32
1237     # . . discard args
1238     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1239     # . write(_test-input-stream, "34\n")
1240     # . . push args
1241     68/push  "34\n"/imm32
1242     68/push  _test-input-stream/imm32
1243     # . . call
1244     e8/call  write/disp32
1245     # . . discard args
1246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1247     # . stream-add2(labels, "x", 0x11223344)
1248     68/push  0x11223344/imm32/label-address
1249     # . . push handle for "x"
1250     53/push-ebx
1251     68/push  "x"/imm32
1252     68/push  Heap/imm32
1253     e8/call  copy-array/disp32
1254     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1255     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
1256     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
1257     # . . push labels
1258     52/push-edx
1259     # . . call
1260     e8/call  stream-add2/disp32
1261     # . . discard args
1262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1263     # component under test
1264     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
1265     # . . push args
1266     52/push-edx
1267     68/push  _test-output-buffered-file/imm32
1268     68/push  _test-input-stream/imm32
1269     # . . call
1270     e8/call  emit-output/disp32
1271     # . . discard args
1272     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1273     # checks
1274     # . flush(_test-output-buffered-file)
1275     # . . push args
1276     68/push  _test-output-buffered-file/imm32
1277     # . . call
1278     e8/call  flush/disp32
1279     # . . discard args
1280     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1281 +-- 33 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
1314     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
1315     # . . push args
1316     68/push  "F - test-emit-output-non-far-control-flow/0"/imm32
1317     68/push  "# 0x00007c00"/imm32
1318     68/push  _test-output-stream/imm32
1319     # . . call
1320     e8/call  check-next-stream-line-equal/disp32
1321     # . . discard args
1322     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1323     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
1324     # . . push args
1325     68/push  "F - test-emit-output-non-far-control-flow/1"/imm32
1326     68/push  "ab cd ef gh "/imm32
1327     68/push  _test-output-stream/imm32
1328     # . . call
1329     e8/call  check-next-stream-line-equal/disp32
1330     # . . discard args
1331     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1332     # . check-next-stream-line-equal(_test-output-stream, "ij 44 33 22 11 ", msg)
1333     # . . push args
1334     68/push  "F - test-emit-output-non-far-control-flow/2"/imm32
1335     68/push  "ij 44 33 22 11 "/imm32
1336     68/push  _test-output-stream/imm32
1337     # . . call
1338     e8/call  check-next-stream-line-equal/disp32
1339     # . . discard args
1340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1341     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c09", msg)
1342     # . . push args
1343     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
1344     68/push  "# 0x00007c09"/imm32
1345     68/push  _test-output-stream/imm32
1346     # . . call
1347     e8/call  check-next-stream-line-equal/disp32
1348     # . . discard args
1349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1350     # . check-next-stream-line-equal(_test-output-stream, "00 ", msg)
1351     # . . push args
1352     68/push  "F - test-emit-output-non-far-control-flow/3"/imm32
1353     68/push  "00 "/imm32
1354     68/push  _test-output-stream/imm32
1355     # . . call
1356     e8/call  check-next-stream-line-equal/disp32
1357     # . . discard args
1358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1359     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
1360     # . . push args
1361     68/push  "F - test-emit-output-non-far-control-flow/4"/imm32
1362     68/push  "34 "/imm32
1363     68/push  _test-output-stream/imm32
1364     # . . call
1365     e8/call  check-next-stream-line-equal/disp32
1366     # . . discard args
1367     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1368     # . epilogue
1369     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1370     5d/pop-to-ebp
1371     c3/return
1372 
1373 test-emit-output-with-padding:
1374     # labels turn into absolute addresses if opcodes are not far jumps or calls
1375     #
1376     # input:
1377     #   in:
1378     #     == code
1379     #     ab cd ef gh
1380     #     == data 0x7c10
1381     #     34
1382     #
1383     # output:
1384     #   ab cd ef gh
1385     #   # 0x7c04
1386     #   00 00 00 00
1387     #   00 00 00 00 00 00 00 00
1388     #   # 0x7c10
1389     #   34
1390     #
1391     # . prologue
1392     55/push-ebp
1393     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1394     # setup
1395     # . clear-stream(_test-input-stream)
1396     # . . push args
1397     68/push  _test-input-stream/imm32
1398     # . . call
1399     e8/call  clear-stream/disp32
1400     # . . discard args
1401     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1402     # . clear-stream(_test-output-stream)
1403     # . . push args
1404     68/push  _test-output-stream/imm32
1405     # . . call
1406     e8/call  clear-stream/disp32
1407     # . . discard args
1408     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1409     # . clear-stream($_test-output-buffered-file->buffer)
1410     # . . push args
1411     68/push  $_test-output-buffered-file->buffer/imm32
1412     # . . call
1413     e8/call  clear-stream/disp32
1414     # . . discard args
1415     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1416     # . var labels/edx: (stream byte 8*24)
1417     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
1418     68/push  0xc0/imm32/size
1419     68/push  0/imm32/read
1420     68/push  0/imm32/write
1421     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1422     # . var h/ebx: (handle array byte)
1423     68/push  0/imm32
1424     68/push  0/imm32
1425     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1426     # initialize input
1427     # . write(_test-input-stream, "== code\n")
1428     # . . push args
1429     68/push  "== code\n"/imm32
1430     68/push  _test-input-stream/imm32
1431     # . . call
1432     e8/call  write/disp32
1433     # . . discard args
1434     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1435     # . write(_test-input-stream, "ab cd ef gh\n")
1436     # . . push args
1437     68/push  "ab cd ef gh\n"/imm32
1438     68/push  _test-input-stream/imm32
1439     # . . call
1440     e8/call  write/disp32
1441     # . . discard args
1442     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1443     # . write(_test-input-stream, "== data 0x7c10\n")
1444     # . . push args
1445     68/push  "== data 0x7c10\n"/imm32
1446     68/push  _test-input-stream/imm32
1447     # . . call
1448     e8/call  write/disp32
1449     # . . discard args
1450     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1451     # . write(_test-input-stream, "34\n")
1452     # . . push args
1453     68/push  "34\n"/imm32
1454     68/push  _test-input-stream/imm32
1455     # . . call
1456     e8/call  write/disp32
1457     # . . discard args
1458     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1459     # component under test
1460     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
1461     # . . push args
1462     52/push-edx
1463     68/push  _test-output-buffered-file/imm32
1464     68/push  _test-input-stream/imm32
1465     # . . call
1466     e8/call  emit-output/disp32
1467     # . . discard args
1468     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1469     # checks
1470     # . flush(_test-output-buffered-file)
1471     # . . push args
1472     68/push  _test-output-buffered-file/imm32
1473     # . . call
1474     e8/call  flush/disp32
1475     # . . discard args
1476     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1477 +-- 33 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
1510     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
1511     # . . push args
1512     68/push  "F - test-emit-output-with-padding/0"/imm32
1513     68/push  "# 0x00007c00"/imm32
1514     68/push  _test-output-stream/imm32
1515     # . . call
1516     e8/call  check-next-stream-line-equal/disp32
1517     # . . discard args
1518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1519     # . check-next-stream-line-equal(_test-output-stream, "ab cd ef gh ", msg)
1520     # . . push args
1521     68/push  "F - test-emit-output-with-padding/1"/imm32
1522     68/push  "ab cd ef gh "/imm32
1523     68/push  _test-output-stream/imm32
1524     # . . call
1525     e8/call  check-next-stream-line-equal/disp32
1526     # . . discard args
1527     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1528     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c04", msg)
1529     # . . push args
1530     68/push  "F - test-emit-output-with-padding/0"/imm32
1531     68/push  "# 0x00007c04"/imm32
1532     68/push  _test-output-stream/imm32
1533     # . . call
1534     e8/call  check-next-stream-line-equal/disp32
1535     # . . discard args
1536     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1537     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
1538     # . . push args
1539     68/push  "F - test-emit-output-with-padding/2"/imm32
1540     68/push  "00 00 00 00 "/imm32
1541     68/push  _test-output-stream/imm32
1542     # . . call
1543     e8/call  check-next-stream-line-equal/disp32
1544     # . . discard args
1545     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1546     # . check-next-stream-line-equal(_test-output-stream, "00 00 00 00 ", msg)
1547     # . . push args
1548     68/push  "F - test-emit-output-with-padding/3"/imm32
1549     68/push  "00 00 00 00 00 00 00 00 "/imm32
1550     68/push  _test-output-stream/imm32
1551     # . . call
1552     e8/call  check-next-stream-line-equal/disp32
1553     # . . discard args
1554     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1555     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c10", msg)
1556     # . . push args
1557     68/push  "F - test-emit-output-with-padding/0"/imm32
1558     68/push  "# 0x00007c10"/imm32
1559     68/push  _test-output-stream/imm32
1560     # . . call
1561     e8/call  check-next-stream-line-equal/disp32
1562     # . . discard args
1563     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1564     # . check-next-stream-line-equal(_test-output-stream, "34 ", msg)
1565     # . . push args
1566     68/push  "F - test-emit-output-with-padding/4"/imm32
1567     68/push  "34 "/imm32
1568     68/push  _test-output-stream/imm32
1569     # . . call
1570     e8/call  check-next-stream-line-equal/disp32
1571     # . . discard args
1572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1573     # . epilogue
1574     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1575     5d/pop-to-ebp
1576     c3/return
1577 
1578 test-emit-output-code-label:
1579     # labels turn into PC-relative addresses if opcodes are far jumps or calls
1580     #
1581     # input:
1582     #   in:
1583     #     == code
1584     #     ab cd
1585     #     ef gh
1586     #     e8 l1/disp32
1587     #   labels:
1588     #     - 'l1': 0x7c10
1589     #
1590     # output:
1591     #   ab cd
1592     #   ef gh
1593     #   e8 07 00 00 00  # 0x7c10 - 0x7c09 = 7
1594     #
1595     # . prologue
1596     55/push-ebp
1597     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1598     # setup
1599     # . clear-stream(_test-input-stream)
1600     # . . push args
1601     68/push  _test-input-stream/imm32
1602     # . . call
1603     e8/call  clear-stream/disp32
1604     # . . discard args
1605     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1606     # . clear-stream(_test-output-stream)
1607     # . . push args
1608     68/push  _test-output-stream/imm32
1609     # . . call
1610     e8/call  clear-stream/disp32
1611     # . . discard args
1612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1613     # . clear-stream($_test-output-buffered-file->buffer)
1614     # . . push args
1615     68/push  $_test-output-buffered-file->buffer/imm32
1616     # . . call
1617     e8/call  clear-stream/disp32
1618     # . . discard args
1619     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1620     # . var labels/edx: (stream byte 8*24)
1621     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
1622     68/push  0xc0/imm32/size
1623     68/push  0/imm32/read
1624     68/push  0/imm32/write
1625     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1626     # . var h/ebx: (handle array byte)
1627     68/push  0/imm32
1628     68/push  0/imm32
1629     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1630     # initialize input
1631     # . write(_test-input-stream, "== code\n")
1632     # . . push args
1633     68/push  "== code\n"/imm32
1634     68/push  _test-input-stream/imm32
1635     # . . call
1636     e8/call  write/disp32
1637     # . . discard args
1638     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1639     # . write(_test-input-stream, "ab cd\n")
1640     # . . push args
1641     68/push  "ab cd\n"/imm32
1642     68/push  _test-input-stream/imm32
1643     # . . call
1644     e8/call  write/disp32
1645     # . . discard args
1646     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1647     # . write(_test-input-stream, "ef gh\n")
1648     # . . push args
1649     68/push  "ef gh\n"/imm32
1650     68/push  _test-input-stream/imm32
1651     # . . call
1652     e8/call  write/disp32
1653     # . . discard args
1654     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1655     # . write(_test-input-stream, "e8 l1/disp32\n")
1656     # . . push args
1657     68/push  "e8 l1/disp32\n"/imm32
1658     68/push  _test-input-stream/imm32
1659     # . . call
1660     e8/call  write/disp32
1661     # . . discard args
1662     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1663     # . stream-add2(labels, "l1", 0x7c10)
1664     68/push  0x7c10/imm32/label-address
1665     # . . push handle for "l1"
1666     53/push-ebx
1667     68/push  "l1"/imm32
1668     68/push  Heap/imm32
1669     e8/call  copy-array/disp32
1670     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1671     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
1672     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
1673     # . . push labels
1674     52/push-edx
1675     # . . call
1676     e8/call  stream-add2/disp32
1677     # . . discard args
1678     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1679     # component under test
1680     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
1681     # . . push args
1682     52/push-edx
1683     68/push  _test-output-buffered-file/imm32
1684     68/push  _test-input-stream/imm32
1685     # . . call
1686     e8/call  emit-output/disp32
1687     # . . discard args
1688     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1689     # checks
1690     # . flush(_test-output-buffered-file)
1691     # . . push args
1692     68/push  _test-output-buffered-file/imm32
1693     # . . call
1694     e8/call  flush/disp32
1695     # . . discard args
1696     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1697 +-- 33 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
1730     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
1731     # . . push args
1732     68/push  "F - test-emit-output-code-label/0"/imm32
1733     68/push  "# 0x00007c00"/imm32
1734     68/push  _test-output-stream/imm32
1735     # . . call
1736     e8/call  check-next-stream-line-equal/disp32
1737     # . . discard args
1738     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1739     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
1740     # . . push args
1741     68/push  "F - test-emit-output-code-label/1"/imm32
1742     68/push  "ab cd "/imm32
1743     68/push  _test-output-stream/imm32
1744     # . . call
1745     e8/call  check-next-stream-line-equal/disp32
1746     # . . discard args
1747     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1748     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
1749     # . . push args
1750     68/push  "F - test-emit-output-code-label/2"/imm32
1751     68/push  "ef gh "/imm32
1752     68/push  _test-output-stream/imm32
1753     # . . call
1754     e8/call  check-next-stream-line-equal/disp32
1755     # . . discard args
1756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1757     # . check-next-stream-line-equal(_test-output-stream, "e8 07 00 00 00 ", msg)
1758     # . . push args
1759     68/push  "F - test-emit-output-code-label/3"/imm32
1760     68/push  "e8 07 00 00 00 "/imm32
1761     68/push  _test-output-stream/imm32
1762     # . . call
1763     e8/call  check-next-stream-line-equal/disp32
1764     # . . discard args
1765     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1766     # . epilogue
1767     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1768     5d/pop-to-ebp
1769     c3/return
1770 
1771 test-emit-output-code-label-absolute:
1772     # labels can also convert to absolute addresses
1773     #
1774     # input:
1775     #   in:
1776     #     == code
1777     #     ab cd
1778     #     ef gh
1779     #     ij l1/imm32
1780     #   labels:
1781     #     - 'l1': 0x1056
1782     #
1783     # output:
1784     #   ab cd
1785     #   ef gh
1786     #   ij 56 10 00 00
1787     #
1788     # . prologue
1789     55/push-ebp
1790     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1791     # setup
1792     # . clear-stream(_test-input-stream)
1793     # . . push args
1794     68/push  _test-input-stream/imm32
1795     # . . call
1796     e8/call  clear-stream/disp32
1797     # . . discard args
1798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1799     # . clear-stream(_test-output-stream)
1800     # . . push args
1801     68/push  _test-output-stream/imm32
1802     # . . call
1803     e8/call  clear-stream/disp32
1804     # . . discard args
1805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1806     # . clear-stream($_test-output-buffered-file->buffer)
1807     # . . push args
1808     68/push  $_test-output-buffered-file->buffer/imm32
1809     # . . call
1810     e8/call  clear-stream/disp32
1811     # . . discard args
1812     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1813     # . var labels/edx: (stream byte 8*24)
1814     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc0/imm32        # subtract from esp
1815     68/push  0xc0/imm32/size
1816     68/push  0/imm32/read
1817     68/push  0/imm32/write
1818     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1819     # . var h/ebx: (handle array byte)
1820     68/push  0/imm32
1821     68/push  0/imm32
1822     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1823     # initialize input
1824     # . write(_test-input-stream, "== code 0x1000\n")
1825     # . . push args
1826     68/push  "== code\n"/imm32
1827     68/push  _test-input-stream/imm32
1828     # . . call
1829     e8/call  write/disp32
1830     # . . discard args
1831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1832     # . write(_test-input-stream, "ab cd\n")
1833     # . . push args
1834     68/push  "ab cd\n"/imm32
1835     68/push  _test-input-stream/imm32
1836     # . . call
1837     e8/call  write/disp32
1838     # . . discard args
1839     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1840     # . write(_test-input-stream, "ef gh\n")
1841     # . . push args
1842     68/push  "ef gh\n"/imm32
1843     68/push  _test-input-stream/imm32
1844     # . . call
1845     e8/call  write/disp32
1846     # . . discard args
1847     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1848     # . write(_test-input-stream, "ij l1/imm32\n")
1849     # . . push args
1850     68/push  "ij l1/imm32\n"/imm32
1851     68/push  _test-input-stream/imm32
1852     # . . call
1853     e8/call  write/disp32
1854     # . . discard args
1855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1856     # . stream-add2(labels, "l1", 0x1056)
1857     68/push  0x1056/imm32/label-address
1858     # . . push handle for "l1"
1859     53/push-ebx
1860     68/push  "l1"/imm32
1861     68/push  Heap/imm32
1862     e8/call  copy-array/disp32
1863     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1864     ff          6/subop/push        1/mod/*+disp8   3/rm32/ebx    .           .             .           .           4/disp8         .                 # push *(ebx+4)
1865     ff          6/subop/push        0/mod/indirect  3/rm32/ebx    .           .             .           .           .               .                 # push *ebx
1866     # . . push labels
1867     52/push-edx
1868     # . . call
1869     e8/call  stream-add2/disp32
1870     # . . discard args
1871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1872     # component under test
1873     # . emit-output(_test-input-stream, _test-output-buffered-file, labels)
1874     # . . push args
1875     52/push-edx
1876     68/push  _test-output-buffered-file/imm32
1877     68/push  _test-input-stream/imm32
1878     # . . call
1879     e8/call  emit-output/disp32
1880     # . . discard args
1881     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1882     # checks
1883     # . flush(_test-output-buffered-file)
1884     # . . push args
1885     68/push  _test-output-buffered-file/imm32
1886     # . . call
1887     e8/call  flush/disp32
1888     # . . discard args
1889     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1890 +-- 33 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
1923     # . check-next-stream-line-equal(_test-output-stream, "# 0x00007c00", msg)
1924     # . . push args
1925     68/push  "F - test-emit-output-code-label-absolute/0"/imm32
1926     68/push  "# 0x00007c00"/imm32
1927     68/push  _test-output-stream/imm32
1928     # . . call
1929     e8/call  check-next-stream-line-equal/disp32
1930     # . . discard args
1931     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1932     # . check-next-stream-line-equal(_test-output-stream, "ab cd ", msg)
1933     # . . push args
1934     68/push  "F - test-emit-output-code-label-absolute/1"/imm32
1935     68/push  "ab cd "/imm32
1936     68/push  _test-output-stream/imm32
1937     # . . call
1938     e8/call  check-next-stream-line-equal/disp32
1939     # . . discard args
1940     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1941     # . check-next-stream-line-equal(_test-output-stream, "ef gh ", msg)
1942     # . . push args
1943     68/push  "F - test-emit-output-code-label-absolute/2"/imm32
1944     68/push  "ef gh "/imm32
1945     68/push  _test-output-stream/imm32
1946     # . . call
1947     e8/call  check-next-stream-line-equal/disp32
1948     # . . discard args
1949     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1950     # . check-next-stream-line-equal(_test-output-stream, "ij f9 ff ff ff ", msg)
1951     # . . push args
1952     68/push  "F - test-emit-output-code-label-absolute/3"/imm32
1953     68/push  "ij 56 10 00 00 "/imm32
1954     68/push  _test-output-stream/imm32
1955     # . . call
1956     e8/call  check-next-stream-line-equal/disp32
1957     # . . discard args
1958     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1959     # . epilogue
1960     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1961     5d/pop-to-ebp
1962     c3/return
1963 
1964 # reads line to make some checks
1965 # don't assume the read state of line after calling this function
1966 far-jump-or-call?:  # line: (addr stream byte) -> result/edi: boolean
1967     # . prologue
1968     55/push-ebp
1969     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1970     # . save registers
1971     50/push-eax
1972     51/push-ecx
1973     52/push-edx
1974     53/push-ebx
1975     # ecx = line
1976     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1977     # var word-slice/edx: slice
1978     68/push  0/imm32/end
1979     68/push  0/imm32/start
1980     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1981     # var datum-slice/ebx: slice
1982     68/push  0/imm32/end
1983     68/push  0/imm32/start
1984     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
1985     # result = false
1986     bf/copy-to-edi  0/imm32/false
1987 $far-jump-or-call?:check-first-word:
1988     # next-word(line, word-slice)
1989     # . . push args
1990     52/push-edx
1991     51/push-ecx
1992     # . . call
1993     e8/call  next-word/disp32
1994     # . . discard args
1995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1996     # if (slice-empty?(word-slice)) return false
1997     # . eax = slice-empty?(word-slice)
1998     # . . push args
1999     52/push-edx
2000     # . . call
2001     e8/call  slice-empty?/disp32
2002     # . . discard args
2003     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2004     # . if (eax != 0) return
2005     3d/compare-eax-and  0/imm32/false
2006     0f 85/jump-if-!=  $far-jump-or-call?:end/disp32
2007     # datum = next-token-from-slice(word-slice->start, word-slice->end, "/")
2008     # . . push args
2009     53/push-ebx
2010     68/push  0x2f/imm32/slash
2011     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2012     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2013     # . . call
2014     e8/call  next-token-from-slice/disp32
2015     # . . discard args
2016     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2017     # if (datum-slice == "e8") return true
2018     # . eax = slice-equal?(datum-slice, "e8")
2019     # . . push args
2020     68/push  "e8"/imm32
2021     53/push-ebx
2022     # . . call
2023     e8/call  slice-equal?/disp32
2024     # . . discard args
2025     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2026     # . if (eax != false) return true
2027     3d/compare-eax-and  0/imm32/false
2028     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
2029     # if (datum-slice == "e9") return true
2030     # . eax = slice-equal?(datum-slice, "e9")
2031     # . . push args
2032     68/push  "e9"/imm32
2033     53/push-ebx
2034     # . . call
2035     e8/call  slice-equal?/disp32
2036     # . . discard args
2037     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2038     # . if (eax != false) return true
2039     3d/compare-eax-and  0/imm32/false
2040     75/jump-if-!=  $far-jump-or-call?:return-true/disp8
2041     # if (datum-slice != "0f") return false
2042     # . eax = slice-equal?(datum-slice, "0f")
2043     # . . push args
2044     68/push  "0f"/imm32
2045     53/push-ebx
2046     # . . call
2047     e8/call  slice-equal?/disp32
2048     # . . discard args
2049     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2050     # . if (eax == false) return
2051     3d/compare-eax-and  0/imm32/false
2052     74/jump-if-=  $far-jump-or-call?:end/disp8
2053 $far-jump-or-call?:check-second-word:
2054     # next-word(line, word-slice)
2055     # . . push args
2056     52/push-edx
2057     51/push-ecx
2058     # . . call
2059     e8/call  next-word/disp32
2060     # . . discard args
2061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2062     # if (slice-empty?(word-slice)) return false
2063     # . eax = slice-empty?(word-slice)
2064     # . . push args
2065     52/push-edx
2066     # . . call
2067     e8/call  slice-empty?/disp32
2068     # . . discard args
2069     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2070     # . if (eax != 0) return
2071     3d/compare-eax-and  0/imm32/false
2072     75/jump-if-!=  $far-jump-or-call?:end/disp8
2073     # if datum of word-slice does not start with "8", return false
2074     # . start/eax = word-slice->start
2075     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy *edx to eax
2076     # . c/eax = *start
2077     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2078     25/and-eax-with  0xff/imm32
2079     # . if (eax != '8') return
2080     3d/compare-eax-and  0x38/imm32/8
2081     75/jump-if-!=  $far-jump-or-call?:end/disp8
2082     # otherwise return true
2083 $far-jump-or-call?:return-true:
2084     bf/copy-to-edi  1/imm32/true
2085 $far-jump-or-call?:end:
2086     # . reclaim locals
2087     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2088     # . restore registers
2089     5b/pop-to-ebx
2090     5a/pop-to-edx
2091     59/pop-to-ecx
2092     58/pop-to-eax
2093     # . epilogue
2094     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2095     5d/pop-to-ebp
2096     c3/return
2097 
2098 # - some helpers for tests
2099 
2100 stream-add2:  # in: (addr stream byte), key: handle, val: int
2101     # . prologue
2102     55/push-ebp
2103     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2104     # . save registers
2105     50/push-eax
2106     51/push-ecx
2107     52/push-edx
2108     56/push-esi
2109     # esi = in
2110     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2111     # curr/eax = &in->data[in->write]
2112     # . eax = in->write
2113     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2114     # . eax = esi+eax+12
2115     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
2116     # max/edx = &in->data[in->size]
2117     # . edx = in->size
2118     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   8/disp8         .                 # copy *(esi+8) to edx
2119     # . edx = esi+edx+12
2120     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
2121     # if (curr >= max) abort
2122     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
2123     73/jump-if-addr>=  $stream-add2:abort/disp8
2124     # *curr = key->alloc-id
2125     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
2126     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
2127     # curr += 4
2128     05/add-to-eax  4/imm32
2129     # if (curr >= max) abort
2130     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
2131     73/jump-if-addr>=  $stream-add2:abort/disp8
2132     # *curr = key->payload
2133     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x10/disp8      .                 # copy *(ebp+16) to ecx
2134     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
2135     # curr += 4
2136     05/add-to-eax  4/imm32
2137     # if (curr >= max) abort
2138     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax with edx
2139     73/jump-if-addr>=  $stream-add2:abort/disp8
2140     # *curr = val
2141     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   0x14/disp8      .                 # copy *(ebp+20) to ecx
2142     89/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to *eax
2143     # in->write += 0xc
2144     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               0xc/imm32         # add to *esi
2145 $stream-add2:end:
2146     # . restore registers
2147     5e/pop-to-esi
2148     5a/pop-to-edx
2149     59/pop-to-ecx
2150     58/pop-to-eax
2151     # . epilogue
2152     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2153     5d/pop-to-ebp
2154     c3/return
2155 
2156 $stream-add2:abort:
2157     # . _write(2/stderr, error)
2158     # . . push args
2159     68/push  "overflow in stream-add2\n"/imm32
2160     68/push  2/imm32/stderr
2161     # . . call
2162     e8/call  _write/disp32
2163     # . . discard args
2164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2165     # . syscall(exit, 1)
2166     bb/copy-to-ebx  1/imm32
2167     e8/call  syscall_exit/disp32
2168     # never gets here
2169 
2170 # some variants of 'trace' that take multiple arguments in different combinations of types:
2171 #   n: int
2172 #   c: character [4-bytes, will eventually be UTF-8]
2173 #   s: (addr array byte)
2174 #   l: (addr slice)
2175 # one gotcha: 's5' must not be empty
2176 
2177 trace-slsns:  # s1: (addr array byte), l2: (addr slice), s3: (addr array byte), n4: int, s5: (addr array byte)
2178     # . prologue
2179     55/push-ebp
2180     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2181     # write(*Trace-stream, s1)
2182     # . . push args
2183     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2184     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2185     # . . call
2186     e8/call  write/disp32
2187     # . . discard args
2188     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2189     # write-slice(*Trace-stream, l2)
2190     # . . push args
2191     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2192     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2193     # . . call
2194     e8/call  write-slice/disp32
2195     # . . discard args
2196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2197     # write(*Trace-stream, s3)
2198     # . . push args
2199     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
2200     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2201     # . . call
2202     e8/call  write/disp32
2203     # . . discard args
2204     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2205     # write-int32-hex(*Trace-stream, n4)
2206     # . . push args
2207     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
2208     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Trace-stream/disp32               # push *Trace-stream
2209     # . . call
2210     e8/call  write-int32-hex/disp32
2211     # . . discard args
2212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2213     # trace(s5)  # implicitly adds a newline and finalizes the trace line
2214     # . . push args
2215     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
2216     # . . call
2217     e8/call  trace/disp32
2218     # . . discard args
2219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2220 $trace-slsns:end:
2221     # . epilogue
2222     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2223     5d/pop-to-ebp
2224     c3/return
2225 
2226 test-trace-slsns:
2227     # . prologue
2228     55/push-ebp
2229     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2230     # setup
2231     # . *Trace-stream->write = 0
2232     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           0/r32/eax   Trace-stream/disp32               # copy *Trace-stream to eax
2233     c7          0/subop/copy        0/mod/direct    0/rm32/eax    .           .             .           .           .               0/imm32           # clear *eax
2234     # (eax..ecx) = "b"
2235     b8/copy-to-eax  "b"/imm32
2236     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2237     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
2238     05/add-to-eax  4/imm32
2239     # var b/ebx: slice = {eax, ecx}
2240     51/push-ecx
2241     50/push-eax
2242     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           4/r32/esp   .               .                 # copy esp to ebx
2243     # trace-slsls("A" b "c " 3 " e")
2244     # . . push args
2245     68/push  " e"/imm32
2246     68/push  3/imm32
2247     68/push  "c "/imm32
2248     53/push-ebx
2249     68/push  "A"/imm32
2250     # . . call
2251     e8/call  trace-slsns/disp32
2252     # . . discard args
2253     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
2254 +-- 26 lines: #?     # dump *Trace-stream ----------------------------------------------------------------------------------------------------------------------------------
2280     # check-trace-contains("Abc 0x00000003 e")
2281     # . . push args
2282     68/push  "F - test-trace-slsls"/imm32
2283     68/push  "Abc 0x00000003 e"/imm32
2284     # . . call
2285     e8/call  check-trace-contains/disp32
2286     # . . discard args
2287     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2288     # . epilogue
2289     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2290     5d/pop-to-ebp
2291     c3/return
2292 
2293 num-bytes:  # line: (addr stream byte) -> eax: int
2294     # pseudocode:
2295     #   result = 0
2296     #   while true
2297     #     var word-slice = next-word(line)
2298     #     if slice-empty?(word-slice)             # end of line
2299     #       break
2300     #     if slice-starts-with?(word-slice, "#")  # comment
2301     #       break
2302     #     if label?(word-slice)                # no need for label declarations anymore
2303     #       break
2304     #     if slice-equal?(word-slice, "==")
2305     #       break                                 # no need for segment header lines
2306     #     result += compute-width(word-slice)
2307     #   return result
2308     #
2309     # . prologue
2310     55/push-ebp
2311     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2312     # . save registers
2313     51/push-ecx
2314     52/push-edx
2315     53/push-ebx
2316     # var result/eax = 0
2317     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2318     # var word-slice/ecx: slice
2319     68/push  0/imm32/end
2320     68/push  0/imm32/start
2321     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2322 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
2348     # . rewind-stream(line)
2349     # . . push args
2350     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2351     # . . call
2352     e8/call  rewind-stream/disp32
2353     # . . discard args
2354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2355 $num-bytes:loop:
2356     # next-word(line, word-slice)
2357     # . . push args
2358     51/push-ecx
2359     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2360     # . . call
2361     e8/call  next-word/disp32
2362     # . . discard args
2363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2364 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
2404 $num-bytes:check0:
2405     # if (slice-empty?(word-slice)) break
2406     # . save result
2407     50/push-eax
2408     # . eax = slice-empty?(word-slice)
2409     # . . push args
2410     51/push-ecx
2411     # . . call
2412     e8/call  slice-empty?/disp32
2413     # . . discard args
2414     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2415     # . if (eax != false) break
2416     3d/compare-eax-and  0/imm32/false
2417     # . restore result now that ZF is set
2418     58/pop-to-eax
2419     75/jump-if-!=  $num-bytes:end/disp8
2420 $num-bytes:check-for-comment:
2421     # if (slice-starts-with?(word-slice, "#")) break
2422     # . start/edx = word-slice->start
2423     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2424     # . c/ebx = *start
2425     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2426     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           3/r32/BL    .               .                 # copy byte at *edx to BL
2427     # . if (ebx == '#') break
2428     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x23/imm32/hash   # compare ebx
2429     74/jump-if-=  $num-bytes:end/disp8
2430 $num-bytes:check-for-label:
2431     # if (slice-ends-with?(word-slice, ":")) break
2432     # . end/edx = word-slice->end
2433     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
2434     # . c/ebx = *(end-1)
2435     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2436     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           3/r32/BL    -1/disp8        .                 # copy byte at *ecx to BL
2437     # . if (ebx == ':') break
2438     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x3a/imm32/colon  # compare ebx
2439     74/jump-if-=  $num-bytes:end/disp8
2440 $num-bytes:check-for-segment-header:
2441     # if (slice-equal?(word-slice, "==")) break
2442     # . push result
2443     50/push-eax
2444     # . eax = slice-equal?(word-slice, "==")
2445     # . . push args
2446     68/push  "=="/imm32
2447     51/push-ecx
2448     # . . call
2449     e8/call  slice-equal?/disp32
2450     # . . discard args
2451     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2452     # . if (eax != false) break
2453     3d/compare-eax-and  0/imm32/false
2454     # . restore result now that ZF is set
2455     58/pop-to-eax
2456     75/jump-if-!=  $num-bytes:end/disp8
2457 $num-bytes:loop-body:
2458     # result += compute-width-of-slice(word-slice)
2459     # . copy result to edx
2460     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
2461     # . eax = compute-width-of-slice(word-slice)
2462     # . . push args
2463     51/push-ecx
2464     # . . call
2465     e8/call compute-width-of-slice/disp32
2466     # . . discard args
2467     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2468     # . eax += result
2469     01/add                          3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # add edx to eax
2470     e9/jump  $num-bytes:loop/disp32
2471 $num-bytes:end:
2472     # . rewind-stream(line)
2473     # . . push args
2474     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2475     # . . call
2476     e8/call  rewind-stream/disp32
2477     # . . discard args
2478     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2479     # . reclaim locals
2480     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2481     # . restore registers
2482     5b/pop-to-ebx
2483     5a/pop-to-edx
2484     59/pop-to-ecx
2485     # . epilogue
2486     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2487     5d/pop-to-ebp
2488     c3/return
2489 
2490 test-num-bytes-handles-empty-string:
2491     # if a line starts with '#', return 0
2492     # . prologue
2493     55/push-ebp
2494     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2495     # setup
2496     # . clear-stream(_test-input-stream)
2497     # . . push args
2498     68/push  _test-input-stream/imm32
2499     # . . call
2500     e8/call  clear-stream/disp32
2501     # . . discard args
2502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2503     # . clear-stream(_test-output-stream)
2504     # . . push args
2505     68/push  _test-output-stream/imm32
2506     # . . call
2507     e8/call  clear-stream/disp32
2508     # . . discard args
2509     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2510     # no contents in input
2511     # eax = num-bytes(_test-input-stream)
2512     # . . push args
2513     68/push  _test-input-stream/imm32
2514     # . . call
2515     e8/call  num-bytes/disp32
2516     # . . discard args
2517     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2518     # check-ints-equal(eax, 0, msg)
2519     # . . push args
2520     68/push  "F - test-num-bytes-handles-empty-string"/imm32
2521     68/push  0/imm32/true
2522     50/push-eax
2523     # . . call
2524     e8/call  check-ints-equal/disp32
2525     # . . discard args
2526     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2527     # . epilogue
2528     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2529     5d/pop-to-ebp
2530     c3/return
2531 
2532 test-num-bytes-ignores-comments:
2533     # if a line starts with '#', return 0
2534     # . prologue
2535     55/push-ebp
2536     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2537     # setup
2538     # . clear-stream(_test-input-stream)
2539     # . . push args
2540     68/push  _test-input-stream/imm32
2541     # . . call
2542     e8/call  clear-stream/disp32
2543     # . . discard args
2544     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2545     # . clear-stream(_test-output-stream)
2546     # . . push args
2547     68/push  _test-output-stream/imm32
2548     # . . call
2549     e8/call  clear-stream/disp32
2550     # . . discard args
2551     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2552     # initialize input
2553     # . write(_test-input-stream, "# abcd")
2554     # . . push args
2555     68/push  "# abcd"/imm32
2556     68/push  _test-input-stream/imm32
2557     # . . call
2558     e8/call  write/disp32
2559     # . . discard args
2560     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2561     # eax = num-bytes(_test-input-stream)
2562     # . . push args
2563     68/push  _test-input-stream/imm32
2564     # . . call
2565     e8/call  num-bytes/disp32
2566     # . . discard args
2567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2568     # check-ints-equal(eax, 0, msg)
2569     # . . push args
2570     68/push  "F - test-num-bytes-ignores-comments"/imm32
2571     68/push  0/imm32/true
2572     50/push-eax
2573     # . . call
2574     e8/call  check-ints-equal/disp32
2575     # . . discard args
2576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2577     # . epilogue
2578     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2579     5d/pop-to-ebp
2580     c3/return
2581 
2582 test-num-bytes-ignores-labels:
2583     # if the first word ends with ':', return 0
2584     # . prologue
2585     55/push-ebp
2586     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2587     # setup
2588     # . clear-stream(_test-input-stream)
2589     # . . push args
2590     68/push  _test-input-stream/imm32
2591     # . . call
2592     e8/call  clear-stream/disp32
2593     # . . discard args
2594     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2595     # . clear-stream(_test-output-stream)
2596     # . . push args
2597     68/push  _test-output-stream/imm32
2598     # . . call
2599     e8/call  clear-stream/disp32
2600     # . . discard args
2601     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2602     # initialize input
2603     # . write(_test-input-stream, "ab: # cd")
2604     # . . push args
2605     68/push  "ab: # cd"/imm32
2606     68/push  _test-input-stream/imm32
2607     # . . call
2608     e8/call  write/disp32
2609     # . . discard args
2610     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2611     # eax = num-bytes(_test-input-stream)
2612     # . . push args
2613     68/push  _test-input-stream/imm32
2614     # . . call
2615     e8/call  num-bytes/disp32
2616     # . . discard args
2617     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2618     # check-ints-equal(eax, 0, msg)
2619     # . . push args
2620     68/push  "F - test-num-bytes-ignores-labels"/imm32
2621     68/push  0/imm32/true
2622     50/push-eax
2623     # . . call
2624     e8/call  check-ints-equal/disp32
2625     # . . discard args
2626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2627     # . epilogue
2628     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2629     5d/pop-to-ebp
2630     c3/return
2631 
2632 test-num-bytes-ignores-segment-headers:
2633     # if the first word is '==', return 0
2634     # . prologue
2635     55/push-ebp
2636     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2637     # setup
2638     # . clear-stream(_test-input-stream)
2639     # . . push args
2640     68/push  _test-input-stream/imm32
2641     # . . call
2642     e8/call  clear-stream/disp32
2643     # . . discard args
2644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2645     # . clear-stream(_test-output-stream)
2646     # . . push args
2647     68/push  _test-output-stream/imm32
2648     # . . call
2649     e8/call  clear-stream/disp32
2650     # . . discard args
2651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2652     # initialize input
2653     # . write(_test-input-stream, "== ab cd")
2654     # . . push args
2655     68/push  "== ab cd"/imm32
2656     68/push  _test-input-stream/imm32
2657     # . . call
2658     e8/call  write/disp32
2659     # . . discard args
2660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2661     # eax = num-bytes(_test-input-stream)
2662     # . . push args
2663     68/push  _test-input-stream/imm32
2664     # . . call
2665     e8/call  num-bytes/disp32
2666     # . . discard args
2667     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2668     # check-ints-equal(eax, 0, msg)
2669     # . . push args
2670     68/push  "F - test-num-bytes-ignores-segment-headers"/imm32
2671     68/push  0/imm32/true
2672     50/push-eax
2673     # . . call
2674     e8/call  check-ints-equal/disp32
2675     # . . discard args
2676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2677     # . epilogue
2678     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2679     5d/pop-to-ebp
2680     c3/return
2681 
2682 test-num-bytes-counts-words-by-default:
2683     # without metadata, count words
2684     # . prologue
2685     55/push-ebp
2686     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2687     # setup
2688     # . clear-stream(_test-input-stream)
2689     # . . push args
2690     68/push  _test-input-stream/imm32
2691     # . . call
2692     e8/call  clear-stream/disp32
2693     # . . discard args
2694     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2695     # . clear-stream(_test-output-stream)
2696     # . . push args
2697     68/push  _test-output-stream/imm32
2698     # . . call
2699     e8/call  clear-stream/disp32
2700     # . . discard args
2701     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2702     # initialize input
2703     # . write(_test-input-stream, "ab cd ef")
2704     # . . push args
2705     68/push  "ab cd ef"/imm32
2706     68/push  _test-input-stream/imm32
2707     # . . call
2708     e8/call  write/disp32
2709     # . . discard args
2710     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2711     # eax = num-bytes(_test-input-stream)
2712     # . . push args
2713     68/push  _test-input-stream/imm32
2714     # . . call
2715     e8/call  num-bytes/disp32
2716     # . . discard args
2717     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2718     # check-ints-equal(eax, 3, msg)
2719     # . . push args
2720     68/push  "F - test-num-bytes-counts-words-by-default"/imm32
2721     68/push  3/imm32/true
2722     50/push-eax
2723     # . . call
2724     e8/call  check-ints-equal/disp32
2725     # . . discard args
2726     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2727     # . epilogue
2728     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2729     5d/pop-to-ebp
2730     c3/return
2731 
2732 test-num-bytes-ignores-trailing-comment:
2733     # trailing comments appropriately ignored
2734     # . prologue
2735     55/push-ebp
2736     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2737     # setup
2738     # . clear-stream(_test-input-stream)
2739     # . . push args
2740     68/push  _test-input-stream/imm32
2741     # . . call
2742     e8/call  clear-stream/disp32
2743     # . . discard args
2744     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2745     # . clear-stream(_test-output-stream)
2746     # . . push args
2747     68/push  _test-output-stream/imm32
2748     # . . call
2749     e8/call  clear-stream/disp32
2750     # . . discard args
2751     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2752     # initialize input
2753     # . write(_test-input-stream, "ab cd # ef")
2754     # . . push args
2755     68/push  "ab cd # ef"/imm32
2756     68/push  _test-input-stream/imm32
2757     # . . call
2758     e8/call  write/disp32
2759     # . . discard args
2760     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2761     # eax = num-bytes(_test-input-stream)
2762     # . . push args
2763     68/push  _test-input-stream/imm32
2764     # . . call
2765     e8/call  num-bytes/disp32
2766     # . . discard args
2767     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2768     # check-ints-equal(eax, 2, msg)
2769     # . . push args
2770     68/push  "F - test-num-bytes-ignores-trailing-comment"/imm32
2771     68/push  2/imm32/true
2772     50/push-eax
2773     # . . call
2774     e8/call  check-ints-equal/disp32
2775     # . . discard args
2776     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2777     # . epilogue
2778     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2779     5d/pop-to-ebp
2780     c3/return
2781 
2782 test-num-bytes-handles-imm32:
2783     # if a word has the /imm32 metadata, count it as 4 bytes
2784     # . prologue
2785     55/push-ebp
2786     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2787     # setup
2788     # . clear-stream(_test-input-stream)
2789     # . . push args
2790     68/push  _test-input-stream/imm32
2791     # . . call
2792     e8/call  clear-stream/disp32
2793     # . . discard args
2794     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2795     # . clear-stream(_test-output-stream)
2796     # . . push args
2797     68/push  _test-output-stream/imm32
2798     # . . call
2799     e8/call  clear-stream/disp32
2800     # . . discard args
2801     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2802     # initialize input
2803     # . write(_test-input-stream, "ab cd/imm32 ef")
2804     # . . push args
2805     68/push  "ab cd/imm32 ef"/imm32
2806     68/push  _test-input-stream/imm32
2807     # . . call
2808     e8/call  write/disp32
2809     # . . discard args
2810     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2811     # eax = num-bytes(_test-input-stream)
2812     # . . push args
2813     68/push  _test-input-stream/imm32
2814     # . . call
2815     e8/call  num-bytes/disp32
2816     # . . discard args
2817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2818     # check-ints-equal(eax, 6, msg)
2819     # . . push args
2820     68/push  "F - test-num-bytes-handles-imm32"/imm32
2821     68/push  6/imm32/true
2822     50/push-eax
2823     # . . call
2824     e8/call  check-ints-equal/disp32
2825     # . . discard args
2826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2827     # . epilogue
2828     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2829     5d/pop-to-ebp
2830     c3/return
2831 
2832 # . . vim:nowrap:textwidth=0