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