https://github.com/akkartik/mu/blob/master/subx/apps/pack.subx
   1 # Read a text file of SubX instructions from stdin, and convert it into a list
   2 # of whitespace-separated ascii hex bytes on stdout. Label definitions and
   3 # uses are left untouched.
   4 #
   5 # To run (from the subx/ directory):
   6 #   $ ./subx translate *.subx apps/pack.subx -o apps/pack
   7 #   $ echo '05/add-to-EAX 0x20/imm32'  |./subx run apps/pack
   8 # Expected output:
   9 #   05 20 00 00 00  # 05/add-to-EAX 0x20/imm32
  10 # The original instruction gets included as a comment at the end of each
  11 # converted line.
  12 #
  13 # There's zero error-checking. For now we assume the input program is valid.
  14 # We'll continue to rely on the C++ version for error messages.
  15 
  16 == code
  17 #   instruction                     effective address                                                   register    displacement    immediate
  18 # . op          subop               mod             rm32          base        index         scale       r32
  19 # . 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
  20 
  21 Entry:  # run tests if necessary, convert stdin if not
  22 
  23     # for debugging: run a single test
  24 #?     e8/call test-emit-non-number-with-all-hex-digits-and-metadata/disp32
  25 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  26 #?     eb/jump  $main:end/disp8
  27 
  28     # . prolog
  29     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  30     # - if argc > 1 and argv[1] == "test", then return run_tests()
  31     # . argc > 1
  32     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  33     7e/jump-if-lesser-or-equal  $run-main/disp8
  34     # . argv[1] == "test"
  35     # . . push args
  36     68/push  "test"/imm32
  37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  38     # . . call
  39     e8/call  kernel-string-equal?/disp32
  40     # . . discard args
  41     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  42     # . check result
  43     3d/compare-EAX-and  1/imm32
  44     75/jump-if-not-equal  $run-main/disp8
  45     # . run-tests()
  46     e8/call  run-tests/disp32
  47     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  48     eb/jump  $main:end/disp8
  49 $run-main:
  50     # - otherwise convert stdin
  51     # var ed/EAX : exit-descriptor
  52     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  53     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  54     # configure ed to really exit()
  55     # . ed->target = 0
  56     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  57     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  58     # . . push args
  59     50/push-EAX/ed
  60     68/push  Stderr/imm32
  61     68/push  Stdout/imm32
  62     68/push  Stdin/imm32
  63     # . . call
  64     e8/call  convert/disp32
  65     # . . discard args
  66     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  67     # . syscall(exit, 0)
  68     bb/copy-to-EBX  0/imm32
  69 $main:end:
  70     b8/copy-to-EAX  1/imm32/exit
  71     cd/syscall  0x80/imm8
  72 
  73 # - big picture
  74 # We'll operate on each line/instruction in isolation. That way we only need to
  75 # allocate memory for converting a single instruction.
  76 #
  77 # To pack an entire file, convert every segment in it
  78 # To convert a code segment, convert every instruction (line) until the next segment header
  79 # To convert a non-data segment, convert every word until the next segment header
  80 #
  81 # primary state: line
  82 #   stream of 512 bytes; abort if it ever overflows
  83 
  84 # conceptual hierarchy within a line:
  85 #   line = words separated by ' ', maybe followed by comment starting with '#'
  86 #   word = datum until '/', then 0 or more metadata separated by '/'
  87 #
  88 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
  89 #   next-token(stream, delim char) -> slice (start, end pointers)
  90 #   next-token-from-slice(start, end, delim char) -> slice
  91 #   slice-equal?(slice, string)
  92 
  93 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
  94     # pseudocode:
  95     #   var line = new-stream(512, 1)
  96     #   var in-code? = false
  97     #   while true
  98     #     clear-stream(line)
  99     #     read-line-buffered(in, line)
 100     #     if (line->write == 0) break             # end of file
 101     #     var word-slice = next-word(line)
 102     #     if slice-empty?(word-slice)             # whitespace
 103     #       write-stream-data(out, line)
 104     #     else if (slice-equal?(word-slice, "=="))
 105     #       word-slice = next-word(line)
 106     #       in-code? = slice-equal?(word-slice, "code")
 107     #       write-stream-data(out, line)
 108     #     else if (in-code?)
 109     #       rewind-stream(line)
 110     #       convert-instruction(line, out)
 111     #     else
 112     #       rewind-stream(line)
 113     #       convert-data(line, out)
 114     #   flush(out)
 115     #
 116     # . prolog
 117     55/push-EBP
 118     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 119     # . save registers
 120     50/push-EAX
 121     51/push-ECX
 122     52/push-EDX
 123     53/push-EBX
 124     # var line/ECX : (address stream byte) = stream(512)
 125     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
 126     68/push  0x200/imm32/length
 127     68/push  0/imm32/read
 128     68/push  0/imm32/write
 129     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 130     # var word-slice/EDX = {0, 0}
 131     68/push  0/imm32/end
 132     68/push  0/imm32/curr
 133     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 134     # var in-code?/EBX = false
 135     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
 136 $convert:loop:
 137     # clear-stream(line)
 138     # . . push args
 139     51/push-ECX
 140     # . . call
 141     e8/call  clear-stream/disp32
 142     # . . discard args
 143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 144     # read-line-buffered(in, line)
 145     # . . push args
 146     51/push-ECX
 147     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 148     # . . call
 149     e8/call  read-line-buffered/disp32
 150     # . . discard args
 151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 152 $convert:check0:
 153     # if (line->write == 0) break
 154     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
 155     0f 84/jump-if-equal  $convert:break/disp32
 156 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 182     # next-word(line, word-slice)
 183     # . . push args
 184     52/push-EDX
 185     51/push-ECX
 186     # . . call
 187     e8/call  next-word/disp32
 188     # . . discard args
 189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 190 $convert:check1:
 191     # if (slice-empty?(word-slice)) write-stream-data(out, line)
 192     # . EAX = slice-empty?(word-slice)
 193     # . . push args
 194     52/push-EDX
 195     # . . call
 196     e8/call  slice-empty?/disp32
 197     # . . discard args
 198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 199     # . if (EAX != 0) write-stream-data(out, line)
 200     3d/compare-EAX-and  0/imm32
 201     0f 85/jump-if-not-equal  $convert:pass-through/disp32
 202 $convert:check2:
 203 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
 245     # if (slice-equal?(word-slice, "=="))
 246     #   word-slice = next-word(line)
 247     #   in-code? = slice-equal?(word-slice, "code")
 248     #   write-stream-data(out, line)
 249     # . EAX = slice-equal?(word-slice, "==")
 250     # . . push args
 251     68/push  "=="/imm32
 252     52/push-EDX
 253     # . . call
 254     e8/call  slice-equal?/disp32
 255     # . . discard args
 256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 257     # . if (EAX == 0) goto check3
 258     3d/compare-EAX-and  0/imm32
 259     0f 84/jump-if-equal  $convert:check3/disp32
 260     # . next-word(line, word-slice)
 261     # . . push args
 262     52/push-EDX
 263     51/push-ECX
 264     # . . call
 265     e8/call  next-word/disp32
 266     # . . discard args
 267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 268 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
 310     # . in-code? = slice-equal?(word-slice, "code")
 311     # . . push args
 312     68/push  "code"/imm32
 313     52/push-EDX
 314     # . . call
 315     e8/call  slice-equal?/disp32
 316     # . . discard args
 317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 318     # . . in-code? = EAX
 319     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
 320     # . goto pass-through
 321     eb/jump  $convert:pass-through/disp8
 322 $convert:check3:
 323     # else rewind-stream(line)
 324     # . rewind-stream(line)
 325     # . . push args
 326     51/push-ECX
 327     # . . call
 328     e8/call  rewind-stream/disp32
 329     # . . discard args
 330     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 331     # if (in-code? != 0) convert-instruction(line, out)
 332     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
 333     74/jump-if-equal  $convert:data/disp8
 334 $convert:code:
 335     # . convert-instruction(line, out)
 336     # . . push args
 337     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 338     51/push-ECX
 339     # . . call
 340     e8/call  convert-instruction/disp32
 341     # . . discard args
 342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 343     # . loop
 344     e9/jump  $convert:loop/disp32
 345 $convert:data:
 346     # else convert-data(line, out)
 347     # . . push args
 348     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 349     51/push-ECX
 350     # . . call
 351     e8/call  convert-data/disp32
 352     # . . discard args
 353     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 354     # . loop
 355     e9/jump  $convert:loop/disp32
 356 $convert:pass-through:
 357     # write-stream-data(out, line)
 358     # . . push args
 359     51/push-ECX
 360     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 361     # . . call
 362     e8/call  write-stream-data/disp32
 363     # . . discard args
 364     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 365     # . loop
 366     e9/jump  $convert:loop/disp32
 367 $convert:break:
 368     # flush(out)
 369     # . . push args
 370     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 371     # . . call
 372     e8/call  flush/disp32
 373     # . . discard args
 374     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 375 $convert:end:
 376     # . reclaim locals
 377     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 378     # . restore registers
 379     5b/pop-to-EBX
 380     5a/pop-to-EDX
 381     59/pop-to-ECX
 382     58/pop-to-EAX
 383     # . epilog
 384     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 385     5d/pop-to-EBP
 386     c3/return
 387 
 388 test-convert-passes-empty-lines-through:
 389     # if a line is empty, pass it along unchanged
 390     # . prolog
 391     55/push-EBP
 392     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 393     # setup
 394     # . clear-stream(_test-input-stream)
 395     # . . push args
 396     68/push  _test-input-stream/imm32
 397     # . . call
 398     e8/call  clear-stream/disp32
 399     # . . discard args
 400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 401     # . clear-stream(_test-input-buffered-file+4)
 402     # . . push args
 403     b8/copy-to-EAX  _test-input-buffered-file/imm32
 404     05/add-to-EAX  4/imm32
 405     50/push-EAX
 406     # . . call
 407     e8/call  clear-stream/disp32
 408     # . . discard args
 409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 410     # . clear-stream(_test-output-stream)
 411     # . . push args
 412     68/push  _test-output-stream/imm32
 413     # . . call
 414     e8/call  clear-stream/disp32
 415     # . . discard args
 416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 417     # . clear-stream(_test-output-buffered-file+4)
 418     # . . push args
 419     b8/copy-to-EAX  _test-output-buffered-file/imm32
 420     05/add-to-EAX  4/imm32
 421     50/push-EAX
 422     # . . call
 423     e8/call  clear-stream/disp32
 424     # . . discard args
 425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 426     # write nothing to input
 427     # convert(_test-input-buffered-file, _test-output-buffered-file)
 428     # . . push args
 429     68/push  _test-output-buffered-file/imm32
 430     68/push  _test-input-buffered-file/imm32
 431     # . . call
 432     e8/call  convert/disp32
 433     # . . discard args
 434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 435     # check that the line just passed through
 436     # . flush(_test-output-buffered-file)
 437     # . . push args
 438     68/push  _test-output-buffered-file/imm32
 439     # . . call
 440     e8/call  flush/disp32
 441     # . . discard args
 442     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 443     # . check-stream-equal(_test-output-stream, "", msg)
 444     # . . push args
 445     68/push  "F - test-convert-passes-empty-lines-through"/imm32
 446     68/push  ""/imm32
 447     68/push  _test-output-stream/imm32
 448     # . . call
 449     e8/call  check-stream-equal/disp32
 450     # . . discard args
 451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 452     # . epilog
 453     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 454     5d/pop-to-EBP
 455     c3/return
 456 
 457 test-convert-passes-lines-with-just-whitespace-through:
 458     # if a line is empty, pass it along unchanged
 459     # . prolog
 460     55/push-EBP
 461     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 462     # setup
 463     # . clear-stream(_test-input-stream)
 464     # . . push args
 465     68/push  _test-input-stream/imm32
 466     # . . call
 467     e8/call  clear-stream/disp32
 468     # . . discard args
 469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 470     # . clear-stream(_test-input-buffered-file+4)
 471     # . . push args
 472     b8/copy-to-EAX  _test-input-buffered-file/imm32
 473     05/add-to-EAX  4/imm32
 474     50/push-EAX
 475     # . . call
 476     e8/call  clear-stream/disp32
 477     # . . discard args
 478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 479     # . clear-stream(_test-output-stream)
 480     # . . push args
 481     68/push  _test-output-stream/imm32
 482     # . . call
 483     e8/call  clear-stream/disp32
 484     # . . discard args
 485     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 486     # . clear-stream(_test-output-buffered-file+4)
 487     # . . push args
 488     b8/copy-to-EAX  _test-output-buffered-file/imm32
 489     05/add-to-EAX  4/imm32
 490     50/push-EAX
 491     # . . call
 492     e8/call  clear-stream/disp32
 493     # . . discard args
 494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 495     # initialize input
 496     # . write(_test-input-stream, "    ")
 497     # . . push args
 498     68/push  "    "/imm32
 499     68/push  _test-input-stream/imm32
 500     # . . call
 501     e8/call  write/disp32
 502     # . . discard args
 503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 504     # convert(_test-input-buffered-file, _test-output-buffered-file)
 505     # . . push args
 506     68/push  _test-output-buffered-file/imm32
 507     68/push  _test-input-buffered-file/imm32
 508     # . . call
 509     e8/call  convert/disp32
 510     # . . discard args
 511     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 512     # check that the line just passed through
 513     # . flush(_test-output-buffered-file)
 514     # . . push args
 515     68/push  _test-output-buffered-file/imm32
 516     # . . call
 517     e8/call  flush/disp32
 518     # . . discard args
 519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 520     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
 521     # . . push args
 522     68/push  "F - test-convert-passes-with-just-whitespace-through"/imm32
 523     68/push  "    "/imm32
 524     68/push  _test-output-stream/imm32
 525     # . . call
 526     e8/call  check-next-stream-line-equal/disp32
 527     # . . discard args
 528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 529     # . epilog
 530     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 531     5d/pop-to-EBP
 532     c3/return
 533 
 534 test-convert-passes-segment-headers-through:
 535     # if a line starts with '==', pass it along unchanged
 536     # . prolog
 537     55/push-EBP
 538     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 539     # setup
 540     # . clear-stream(_test-input-stream)
 541     # . . push args
 542     68/push  _test-input-stream/imm32
 543     # . . call
 544     e8/call  clear-stream/disp32
 545     # . . discard args
 546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 547     # . clear-stream(_test-input-buffered-file+4)
 548     # . . push args
 549     b8/copy-to-EAX  _test-input-buffered-file/imm32
 550     05/add-to-EAX  4/imm32
 551     50/push-EAX
 552     # . . call
 553     e8/call  clear-stream/disp32
 554     # . . discard args
 555     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 556     # . clear-stream(_test-output-stream)
 557     # . . push args
 558     68/push  _test-output-stream/imm32
 559     # . . call
 560     e8/call  clear-stream/disp32
 561     # . . discard args
 562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 563     # . clear-stream(_test-output-buffered-file+4)
 564     # . . push args
 565     b8/copy-to-EAX  _test-output-buffered-file/imm32
 566     05/add-to-EAX  4/imm32
 567     50/push-EAX
 568     # . . call
 569     e8/call  clear-stream/disp32
 570     # . . discard args
 571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 572     # initialize input
 573     # . write(_test-input-stream, "== abcd 0x1")
 574     # . . push args
 575     68/push  "== abcd 0x1"/imm32
 576     68/push  _test-input-stream/imm32
 577     # . . call
 578     e8/call  write/disp32
 579     # . . discard args
 580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 581     # convert(_test-input-buffered-file, _test-output-buffered-file)
 582     # . . push args
 583     68/push  _test-output-buffered-file/imm32
 584     68/push  _test-input-buffered-file/imm32
 585     # . . call
 586     e8/call  convert/disp32
 587     # . . discard args
 588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 589     # check that the line just passed through
 590     # . flush(_test-output-buffered-file)
 591     # . . push args
 592     68/push  _test-output-buffered-file/imm32
 593     # . . call
 594     e8/call  flush/disp32
 595     # . . discard args
 596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 597     # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg)
 598     # . . push args
 599     68/push  "F - test-convert-passes-segment-headers-through"/imm32
 600     68/push  "== abcd 0x1"/imm32
 601     68/push  _test-output-stream/imm32
 602     # . . call
 603     e8/call  check-stream-equal/disp32
 604     # . . discard args
 605     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 606     # . epilog
 607     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 608     5d/pop-to-EBP
 609     c3/return
 610 
 611 test-convert-in-data-segment:
 612     # correctly process lines in the data segment
 613     # . prolog
 614     55/push-EBP
 615     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 616     # setup
 617     # . clear-stream(_test-input-stream)
 618     # . . push args
 619     68/push  _test-input-stream/imm32
 620     # . . call
 621     e8/call  clear-stream/disp32
 622     # . . discard args
 623     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 624     # . clear-stream(_test-input-buffered-file+4)
 625     # . . push args
 626     b8/copy-to-EAX  _test-input-buffered-file/imm32
 627     05/add-to-EAX  4/imm32
 628     50/push-EAX
 629     # . . call
 630     e8/call  clear-stream/disp32
 631     # . . discard args
 632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 633     # . clear-stream(_test-output-stream)
 634     # . . push args
 635     68/push  _test-output-stream/imm32
 636     # . . call
 637     e8/call  clear-stream/disp32
 638     # . . discard args
 639     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 640     # . clear-stream(_test-output-buffered-file+4)
 641     # . . push args
 642     b8/copy-to-EAX  _test-output-buffered-file/imm32
 643     05/add-to-EAX  4/imm32
 644     50/push-EAX
 645     # . . call
 646     e8/call  clear-stream/disp32
 647     # . . discard args
 648     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 649     # initialize input
 650     #   == code 0x1
 651     #   == data 0x2
 652     #   3 4/imm32
 653     # . write(_test-input-stream, "== code 0x1")
 654     # . . push args
 655     68/push  "== code 0x1\n"/imm32
 656     68/push  _test-input-stream/imm32
 657     # . . call
 658     e8/call  write/disp32
 659     # . . discard args
 660     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 661     # . write(_test-input-stream, "== data 0x2\n")
 662     # . . push args
 663     68/push  "== data 0x2\n"/imm32
 664     68/push  _test-input-stream/imm32
 665     # . . call
 666     e8/call  write/disp32
 667     # . . discard args
 668     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 669     # . write(_test-input-stream, "3 4/imm32\n")
 670     # . . push args
 671     68/push  "3 4/imm32\n"/imm32
 672     68/push  _test-input-stream/imm32
 673     # . . call
 674     e8/call  write/disp32
 675     # . . discard args
 676     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 677     # convert(_test-input-buffered-file, _test-output-buffered-file)
 678     # . . push args
 679     68/push  _test-output-buffered-file/imm32
 680     68/push  _test-input-buffered-file/imm32
 681     # . . call
 682     e8/call  convert/disp32
 683     # . . discard args
 684     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 685     # check output
 686 +-- 26 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
 712     # . flush(_test-output-buffered-file)
 713     # . . push args
 714     68/push  _test-output-buffered-file/imm32
 715     # . . call
 716     e8/call  flush/disp32
 717     # . . discard args
 718     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 719     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
 720     # . . push args
 721     68/push  "F - test-convert-in-data-segment/0"/imm32
 722     68/push  "== code 0x1"/imm32
 723     68/push  _test-output-stream/imm32
 724     # . . call
 725     e8/call  check-next-stream-line-equal/disp32
 726     # . . discard args
 727     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 728     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
 729     # . . push args
 730     68/push  "F - test-convert-in-data-segment/1"/imm32
 731     68/push  "== data 0x2"/imm32
 732     68/push  _test-output-stream/imm32
 733     # . . call
 734     e8/call  check-next-stream-line-equal/disp32
 735     # . . discard args
 736     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 737     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 738     # . . push args
 739     68/push  "F - test-convert-in-data-segment/2"/imm32
 740     68/push  "03 04 00 00 00 "/imm32
 741     68/push  _test-output-stream/imm32
 742     # . . call
 743     e8/call  check-next-stream-line-equal/disp32
 744     # . . discard args
 745     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 746     # . epilog
 747     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 748     5d/pop-to-EBP
 749     c3/return
 750 
 751 test-convert-code-and-data-segments:
 752     # correctly process lines in both code and data segments
 753     # . prolog
 754     55/push-EBP
 755     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 756     # setup
 757     # . clear-stream(_test-input-stream)
 758     # . . push args
 759     68/push  _test-input-stream/imm32
 760     # . . call
 761     e8/call  clear-stream/disp32
 762     # . . discard args
 763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 764     # . clear-stream(_test-input-buffered-file+4)
 765     # . . push args
 766     b8/copy-to-EAX  _test-input-buffered-file/imm32
 767     05/add-to-EAX  4/imm32
 768     50/push-EAX
 769     # . . call
 770     e8/call  clear-stream/disp32
 771     # . . discard args
 772     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 773     # . clear-stream(_test-output-stream)
 774     # . . push args
 775     68/push  _test-output-stream/imm32
 776     # . . call
 777     e8/call  clear-stream/disp32
 778     # . . discard args
 779     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 780     # . clear-stream(_test-output-buffered-file+4)
 781     # . . push args
 782     b8/copy-to-EAX  _test-output-buffered-file/imm32
 783     05/add-to-EAX  4/imm32
 784     50/push-EAX
 785     # . . call
 786     e8/call  clear-stream/disp32
 787     # . . discard args
 788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 789     # initialize input
 790     #   == code 0x1
 791     #   e8/call 20/disp32
 792     #   68/push 0x20/imm8
 793     #   == data 0x2
 794     #   3 4/imm32
 795     # . write(_test-input-stream, "== code 0x1\n")
 796     # . . push args
 797     68/push  "== code 0x1\n"/imm32
 798     68/push  _test-input-stream/imm32
 799     # . . call
 800     e8/call  write/disp32
 801     # . . discard args
 802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 803     # . write(_test-input-stream, "e8/call 20/disp32\n")
 804     # . . push args
 805     68/push  "e8/call 20/disp32\n"/imm32
 806     68/push  _test-input-stream/imm32
 807     # . . call
 808     e8/call  write/disp32
 809     # . . discard args
 810     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 811     # . write(_test-input-stream, "68/push 0x20/imm8\n")
 812     # . . push args
 813     68/push  "68/push 0x20/imm8\n"/imm32
 814     68/push  _test-input-stream/imm32
 815     # . . call
 816     e8/call  write/disp32
 817     # . . discard args
 818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 819     # . write(_test-input-stream, "== data 0x2\n")
 820     # . . push args
 821     68/push  "== data 0x2\n"/imm32
 822     68/push  _test-input-stream/imm32
 823     # . . call
 824     e8/call  write/disp32
 825     # . . discard args
 826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 827     # . write(_test-input-stream, "3 4/imm32\n")
 828     # . . push args
 829     68/push  "3 4/imm32\n"/imm32
 830     68/push  _test-input-stream/imm32
 831     # . . call
 832     e8/call  write/disp32
 833     # . . discard args
 834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 835     # convert(_test-input-buffered-file, _test-output-buffered-file)
 836     # . . push args
 837     68/push  _test-output-buffered-file/imm32
 838     68/push  _test-input-buffered-file/imm32
 839     # . . call
 840     e8/call  convert/disp32
 841     # . . discard args
 842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 843     # check output
 844     #   == code 0x1
 845     #   e8 20 00 00 00  # e8/call 20/disp32
 846     #   68 20  # 68/push 0x20/imm8
 847     #   == data 0x2
 848     #   03 04 00 00 00
 849 +-- 26 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
 875     # . flush(_test-output-buffered-file)
 876     # . . push args
 877     68/push  _test-output-buffered-file/imm32
 878     # . . call
 879     e8/call  flush/disp32
 880     # . . discard args
 881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 882     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
 883     # . . push args
 884     68/push  "F - test-convert-code-and-data-segments/0"/imm32
 885     68/push  "== code 0x1"/imm32
 886     68/push  _test-output-stream/imm32
 887     # . . call
 888     e8/call  check-next-stream-line-equal/disp32
 889     # . . discard args
 890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 891     # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
 892     # . . push args
 893     68/push  "F - test-convert-code-and-data-segments/1"/imm32
 894     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
 895     68/push  _test-output-stream/imm32
 896     # . . call
 897     e8/call  check-next-stream-line-equal/disp32
 898     # . . discard args
 899     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 900     # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
 901     # . . push args
 902     68/push  "F - test-convert-code-and-data-segments/2"/imm32
 903     68/push  "68 20  # 68/push 0x20/imm8"/imm32
 904     68/push  _test-output-stream/imm32
 905     # . . call
 906     e8/call  check-next-stream-line-equal/disp32
 907     # . . discard args
 908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 909     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
 910     # . . push args
 911     68/push  "F - test-convert-code-and-data-segments/3"/imm32
 912     68/push  "== data 0x2"/imm32
 913     68/push  _test-output-stream/imm32
 914     # . . call
 915     e8/call  check-next-stream-line-equal/disp32
 916     # . . discard args
 917     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 918     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 919     # . . push args
 920     68/push  "F - test-convert-code-and-data-segments/4"/imm32
 921     68/push  "03 04 00 00 00 "/imm32
 922     68/push  _test-output-stream/imm32
 923     # . . call
 924     e8/call  check-next-stream-line-equal/disp32
 925     # . . discard args
 926     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 927     # . epilog
 928     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 929     5d/pop-to-EBP
 930     c3/return
 931 
 932 convert-data:  # line : (address stream byte), out : (address buffered-file) -> <void>
 933     # pseudocode:
 934     #   var word-slice = {0, 0}
 935     #   while true
 936     #     word-slice = next-word(line)
 937     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
 938     #       break  # skip emitting some whitespace
 939     #     if slice-starts-with?(word-slice, "#")      # comment
 940     #       write-slice-buffered(out, word-slice)
 941     #       break
 942     #     if slice-ends-with?(word-slice, ":")        # label
 943     #       write-stream-data(out, line)
 944     #       break
 945     #     if has-metadata?(word-slice, "imm32")
 946     #       emit(out, word-slice, 4)
 947     #     # disp32 is not permitted in data segments, and anything else is only a byte long
 948     #     else
 949     #       emit(out, word-slice, 1)
 950     #
 951     # . prolog
 952     55/push-EBP
 953     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 954     # . save registers
 955     50/push-EAX
 956     51/push-ECX
 957     52/push-EDX
 958     # var word-slice/ECX = {0, 0}
 959     68/push  0/imm32/end
 960     68/push  0/imm32/start
 961     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 962 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 988 $convert-data:loop:
 989     # next-word(line, word-slice)
 990     # . . push args
 991     51/push-ECX
 992     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 993     # . . call
 994     e8/call  next-word/disp32
 995     # . . discard args
 996     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 997 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
1039 $convert-data:check0:
1040     # if (slice-empty?(word-slice)) break
1041     # . EAX = slice-empty?(word-slice)
1042     # . . push args
1043     51/push-ECX
1044     # . . call
1045     e8/call  slice-empty?/disp32
1046     # . . discard args
1047     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1048     # . if (EAX != 0) break
1049     3d/compare-EAX-and  0/imm32
1050     0f 85/jump-if-not-equal  $convert-data:break/disp32
1051 $convert-data:check-for-comment:
1052     # if (slice-starts-with?(word-slice, "#"))
1053     # . start/EDX = word-slice->start
1054     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
1055     # . c/EAX = *start
1056     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1057     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
1058     # . if (EAX != '#') goto next check
1059     3d/compare-EAX-and  0x23/imm32/hash
1060     75/jump-if-not-equal  $convert-data:check-for-label/disp8
1061 $convert-data:comment:
1062     # write-slice-buffered(out, word-slice)
1063     # . . push args
1064     51/push-ECX
1065     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1066     # . . call
1067     e8/call  write-slice-buffered/disp32
1068     # . . discard args
1069     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1070     # break
1071     75/jump-if-not-equal  $convert-data:break/disp8
1072 $convert-data:check-for-label:
1073     # if (slice-ends-with?(word-slice, ":"))
1074     # . end/EDX = word-slice->end
1075     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
1076     # . c/EAX = *(end-1)
1077     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1078     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
1079     # . if (EAX != ':') goto next check
1080     3d/compare-EAX-and  0x3a/imm32/colon
1081     75/jump-if-not-equal  $convert-data:check-for-imm32/disp8
1082 $convert-data:label:
1083     # write-stream-data(out, line)
1084     # . . push args
1085     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1086     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1087     # . . call
1088     e8/call  write-stream-data/disp32
1089     # . . discard args
1090     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1091     # break
1092     75/jump-if-not-equal  $convert-data:break/disp8
1093 $convert-data:check-for-imm32:
1094     # if (has-metadata?(word-slice, "imm32"))
1095     # . EAX = has-metadata?(ECX, "imm32")
1096     # . . push args
1097     68/push  "imm32"/imm32
1098     51/push-ECX
1099     # . . call
1100     e8/call  has-metadata?/disp32
1101     # . . discard args
1102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1103     # . if (EAX == 0) process as a single byte
1104     3d/compare-EAX-and  0/imm32
1105     74/jump-if-equal  $convert-data:single-byte/disp8
1106 $convert-data:imm32:
1107     # emit(out, word-slice, 4)
1108     # . . push args
1109     68/push  4/imm32
1110     51/push-ECX
1111     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1112     # . . call
1113     e8/call  emit/disp32
1114     # . . discard args
1115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1116     e9/jump  $convert-data:loop/disp32
1117 $convert-data:single-byte:
1118     # emit(out, word-slice, 1)
1119     # . . push args
1120     68/push  1/imm32
1121     51/push-ECX
1122     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1123     # . . call
1124     e8/call  emit/disp32
1125     # . . discard args
1126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1127     e9/jump  $convert-data:loop/disp32
1128 $convert-data:break:
1129 $convert-data:end:
1130     # . reclaim locals
1131     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1132     # . restore registers
1133     5a/pop-to-EDX
1134     59/pop-to-ECX
1135     58/pop-to-EAX
1136     # . epilog
1137     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1138     5d/pop-to-EBP
1139     c3/return
1140 
1141 test-convert-data-passes-comments-through:
1142     # if a line starts with '#', pass it along unchanged
1143     # . prolog
1144     55/push-EBP
1145     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1146     # setup
1147     # . clear-stream(_test-input-stream)
1148     # . . push args
1149     68/push  _test-input-stream/imm32
1150     # . . call
1151     e8/call  clear-stream/disp32
1152     # . . discard args
1153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1154     # . clear-stream(_test-output-stream)
1155     # . . push args
1156     68/push  _test-output-stream/imm32
1157     # . . call
1158     e8/call  clear-stream/disp32
1159     # . . discard args
1160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1161     # . clear-stream(_test-output-buffered-file+4)
1162     # . . push args
1163     b8/copy-to-EAX  _test-output-buffered-file/imm32
1164     05/add-to-EAX  4/imm32
1165     50/push-EAX
1166     # . . call
1167     e8/call  clear-stream/disp32
1168     # . . discard args
1169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1170     # initialize input
1171     # . write(_test-input-stream, "# abcd")
1172     # . . push args
1173     68/push  "# abcd"/imm32
1174     68/push  _test-input-stream/imm32
1175     # . . call
1176     e8/call  write/disp32
1177     # . . discard args
1178     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1179     # convert-data(_test-input-stream, _test-output-buffered-file)
1180     # . . push args
1181     68/push  _test-output-buffered-file/imm32
1182     68/push  _test-input-stream/imm32
1183     # . . call
1184     e8/call  convert-data/disp32
1185     # . . discard args
1186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1187     # check that the line just passed through
1188     # . flush(_test-output-buffered-file)
1189     # . . push args
1190     68/push  _test-output-buffered-file/imm32
1191     # . . call
1192     e8/call  flush/disp32
1193     # . . discard args
1194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1195 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1221     # . check-stream-equal(_test-output-stream, "# abcd", msg)
1222     # . . push args
1223     68/push  "F - test-convert-data-passes-comments-through"/imm32
1224     68/push  "# abcd"/imm32
1225     68/push  _test-output-stream/imm32
1226     # . . call
1227     e8/call  check-stream-equal/disp32
1228     # . . discard args
1229     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1230     # . epilog
1231     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1232     5d/pop-to-EBP
1233     c3/return
1234 
1235 test-convert-data-passes-labels-through:
1236     # if the first word ends with ':', pass along the entire line unchanged
1237     # . prolog
1238     55/push-EBP
1239     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1240     # setup
1241     # . clear-stream(_test-input-stream)
1242     # . . push args
1243     68/push  _test-input-stream/imm32
1244     # . . call
1245     e8/call  clear-stream/disp32
1246     # . . discard args
1247     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1248     # . clear-stream(_test-output-stream)
1249     # . . push args
1250     68/push  _test-output-stream/imm32
1251     # . . call
1252     e8/call  clear-stream/disp32
1253     # . . discard args
1254     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1255     # . clear-stream(_test-output-buffered-file+4)
1256     # . . push args
1257     b8/copy-to-EAX  _test-output-buffered-file/imm32
1258     05/add-to-EAX  4/imm32
1259     50/push-EAX
1260     # . . call
1261     e8/call  clear-stream/disp32
1262     # . . discard args
1263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1264     # initialize input
1265     # . write(_test-input-stream, "ab: # cd")
1266     # . . push args
1267     68/push  "ab: # cd"/imm32
1268     68/push  _test-input-stream/imm32
1269     # . . call
1270     e8/call  write/disp32
1271     # . . discard args
1272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1273     # convert-data(_test-input-stream, _test-output-buffered-file)
1274     # . . push args
1275     68/push  _test-output-buffered-file/imm32
1276     68/push  _test-input-stream/imm32
1277     # . . call
1278     e8/call  convert-data/disp32
1279     # . . discard args
1280     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1281     # check that the line just passed through
1282     # . flush(_test-output-buffered-file)
1283     # . . push args
1284     68/push  _test-output-buffered-file/imm32
1285     # . . call
1286     e8/call  flush/disp32
1287     # . . discard args
1288     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1289     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
1290     # . . push args
1291     68/push  "F - test-convert-data-passes-labels-through"/imm32
1292     68/push  "ab: # cd"/imm32
1293     68/push  _test-output-stream/imm32
1294     # . . call
1295     e8/call  check-stream-equal/disp32
1296     # . . discard args
1297     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1298     # . epilog
1299     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1300     5d/pop-to-EBP
1301     c3/return
1302 
1303 test-convert-data-passes-names-through:
1304     # If a word is a valid name, just emit it unchanged.
1305     # Later phases will deal with it.
1306     # . prolog
1307     55/push-EBP
1308     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1309     # setup
1310     # . clear-stream(_test-input-stream)
1311     # . . push args
1312     68/push  _test-input-stream/imm32
1313     # . . call
1314     e8/call  clear-stream/disp32
1315     # . . discard args
1316     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1317     # . clear-stream(_test-output-stream)
1318     # . . push args
1319     68/push  _test-output-stream/imm32
1320     # . . call
1321     e8/call  clear-stream/disp32
1322     # . . discard args
1323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1324     # . clear-stream(_test-output-buffered-file+4)
1325     # . . push args
1326     b8/copy-to-EAX  _test-output-buffered-file/imm32
1327     05/add-to-EAX  4/imm32
1328     50/push-EAX
1329     # . . call
1330     e8/call  clear-stream/disp32
1331     # . . discard args
1332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1333     # initialize input
1334     # . write(_test-input-stream, "abcd/imm32")
1335     # . . push args
1336     68/push  "abcd/imm32"/imm32
1337     68/push  _test-input-stream/imm32
1338     # . . call
1339     e8/call  write/disp32
1340     # . . discard args
1341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1342     # convert-data(_test-input-stream, _test-output-buffered-file)
1343     # . . push args
1344     68/push  _test-output-buffered-file/imm32
1345     68/push  _test-input-stream/imm32
1346     # . . call
1347     e8/call  convert-data/disp32
1348     # . . discard args
1349     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1350     # check that the line just passed through
1351     # . flush(_test-output-buffered-file)
1352     # . . push args
1353     68/push  _test-output-buffered-file/imm32
1354     # . . call
1355     e8/call  flush/disp32
1356     # . . discard args
1357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1358     # . check-stream-equal(_test-output-stream, "abcd/imm32", msg)
1359     # . . push args
1360     68/push  "F - test-convert-data-passes-names-through"/imm32
1361     68/push  "abcd/imm32 "/imm32
1362     68/push  _test-output-stream/imm32
1363     # . . call
1364     e8/call  check-stream-equal/disp32
1365     # . . discard args
1366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1367     # . epilog
1368     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1369     5d/pop-to-EBP
1370     c3/return
1371 
1372 test-convert-data-handles-imm32:
1373     # If a word has the /imm32 metadata, emit it in 4 bytes.
1374     # . prolog
1375     55/push-EBP
1376     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1377     # setup
1378     # . clear-stream(_test-input-stream)
1379     # . . push args
1380     68/push  _test-input-stream/imm32
1381     # . . call
1382     e8/call  clear-stream/disp32
1383     # . . discard args
1384     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1385     # . clear-stream(_test-output-stream)
1386     # . . push args
1387     68/push  _test-output-stream/imm32
1388     # . . call
1389     e8/call  clear-stream/disp32
1390     # . . discard args
1391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1392     # . clear-stream(_test-output-buffered-file+4)
1393     # . . push args
1394     b8/copy-to-EAX  _test-output-buffered-file/imm32
1395     05/add-to-EAX  4/imm32
1396     50/push-EAX
1397     # . . call
1398     e8/call  clear-stream/disp32
1399     # . . discard args
1400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1401     # initialize input
1402     # . write(_test-input-stream, "30/imm32")
1403     # . . push args
1404     68/push  "30/imm32"/imm32
1405     68/push  _test-input-stream/imm32
1406     # . . call
1407     e8/call  write/disp32
1408     # . . discard args
1409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1410     # convert-data(_test-input-stream, _test-output-buffered-file)
1411     # . . push args
1412     68/push  _test-output-buffered-file/imm32
1413     68/push  _test-input-stream/imm32
1414     # . . call
1415     e8/call  convert-data/disp32
1416     # . . discard args
1417     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1418     # check that 4 bytes were written
1419     # . flush(_test-output-buffered-file)
1420     # . . push args
1421     68/push  _test-output-buffered-file/imm32
1422     # . . call
1423     e8/call  flush/disp32
1424     # . . discard args
1425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1426     # . check-stream-equal(_test-output-stream, "30 00 00 00 ", msg)
1427     # . . push args
1428     68/push  "F - test-convert-data-handles-imm32"/imm32
1429     68/push  "30 00 00 00 "/imm32
1430     68/push  _test-output-stream/imm32
1431     # . . call
1432     e8/call  check-stream-equal/disp32
1433     # . . discard args
1434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1435     # . epilog
1436     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1437     5d/pop-to-EBP
1438     c3/return
1439 
1440 test-convert-data-handles-single-byte:
1441     # Any metadata but /imm32 will emit a single byte.
1442     # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
1443     # . prolog
1444     55/push-EBP
1445     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1446     # setup
1447     # . clear-stream(_test-input-stream)
1448     # . . push args
1449     68/push  _test-input-stream/imm32
1450     # . . call
1451     e8/call  clear-stream/disp32
1452     # . . discard args
1453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1454     # . clear-stream(_test-output-stream)
1455     # . . push args
1456     68/push  _test-output-stream/imm32
1457     # . . call
1458     e8/call  clear-stream/disp32
1459     # . . discard args
1460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1461     # . clear-stream(_test-output-buffered-file+4)
1462     # . . push args
1463     b8/copy-to-EAX  _test-output-buffered-file/imm32
1464     05/add-to-EAX  4/imm32
1465     50/push-EAX
1466     # . . call
1467     e8/call  clear-stream/disp32
1468     # . . discard args
1469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1470     # initialize input
1471     # . write(_test-input-stream, "30/imm16")
1472     # . . push args
1473     68/push  "30/imm16"/imm32
1474     68/push  _test-input-stream/imm32
1475     # . . call
1476     e8/call  write/disp32
1477     # . . discard args
1478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1479     # convert-data(_test-input-stream, _test-output-buffered-file)
1480     # . . push args
1481     68/push  _test-output-buffered-file/imm32
1482     68/push  _test-input-stream/imm32
1483     # . . call
1484     e8/call  convert-data/disp32
1485     # . . discard args
1486     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1487     # check that a single byte was written (imm16 is not a valid operand type)
1488     # . flush(_test-output-buffered-file)
1489     # . . push args
1490     68/push  _test-output-buffered-file/imm32
1491     # . . call
1492     e8/call  flush/disp32
1493     # . . discard args
1494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1495     # . check-stream-equal(_test-output-stream, "30 ", msg)
1496     # . . push args
1497     68/push  "F - test-convert-data-handles-single-byte"/imm32
1498     68/push  "30 "/imm32
1499     68/push  _test-output-stream/imm32
1500     # . . call
1501     e8/call  check-stream-equal/disp32
1502     # . . discard args
1503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1504     # . epilog
1505     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1506     5d/pop-to-EBP
1507     c3/return
1508 
1509 test-convert-data-multiple-bytes:
1510     # Multiple single-byte words in input stream get processed one by one.
1511     # . prolog
1512     55/push-EBP
1513     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1514     # setup
1515     # . clear-stream(_test-input-stream)
1516     # . . push args
1517     68/push  _test-input-stream/imm32
1518     # . . call
1519     e8/call  clear-stream/disp32
1520     # . . discard args
1521     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1522     # . clear-stream(_test-output-stream)
1523     # . . push args
1524     68/push  _test-output-stream/imm32
1525     # . . call
1526     e8/call  clear-stream/disp32
1527     # . . discard args
1528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1529     # . clear-stream(_test-output-buffered-file+4)
1530     # . . push args
1531     b8/copy-to-EAX  _test-output-buffered-file/imm32
1532     05/add-to-EAX  4/imm32
1533     50/push-EAX
1534     # . . call
1535     e8/call  clear-stream/disp32
1536     # . . discard args
1537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1538     # initialize input
1539     # . write(_test-input-stream, "1 2")
1540     # . . push args
1541     68/push  "1 2"/imm32
1542     68/push  _test-input-stream/imm32
1543     # . . call
1544     e8/call  write/disp32
1545     # . . discard args
1546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1547     # convert-data(_test-input-stream, _test-output-buffered-file)
1548     # . . push args
1549     68/push  _test-output-buffered-file/imm32
1550     68/push  _test-input-stream/imm32
1551     # . . call
1552     e8/call  convert-data/disp32
1553     # . . discard args
1554     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1555     # check output
1556     # . flush(_test-output-buffered-file)
1557     # . . push args
1558     68/push  _test-output-buffered-file/imm32
1559     # . . call
1560     e8/call  flush/disp32
1561     # . . discard args
1562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1563     # . check-stream-equal(_test-output-stream, "01 02 ", msg)
1564     # . . push args
1565     68/push  "F - test-convert-data-multiple-bytes"/imm32
1566     68/push  "01 02 "/imm32
1567     68/push  _test-output-stream/imm32
1568     # . . call
1569     e8/call  check-stream-equal/disp32
1570     # . . discard args
1571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1572     # . epilog
1573     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1574     5d/pop-to-EBP
1575     c3/return
1576 
1577 test-convert-data-byte-then-name:
1578     # Single-byte word followed by valid name get processed one by one.
1579     # . prolog
1580     55/push-EBP
1581     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1582     # setup
1583     # . clear-stream(_test-input-stream)
1584     # . . push args
1585     68/push  _test-input-stream/imm32
1586     # . . call
1587     e8/call  clear-stream/disp32
1588     # . . discard args
1589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1590     # . clear-stream(_test-output-stream)
1591     # . . push args
1592     68/push  _test-output-stream/imm32
1593     # . . call
1594     e8/call  clear-stream/disp32
1595     # . . discard args
1596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1597     # . clear-stream(_test-output-buffered-file+4)
1598     # . . push args
1599     b8/copy-to-EAX  _test-output-buffered-file/imm32
1600     05/add-to-EAX  4/imm32
1601     50/push-EAX
1602     # . . call
1603     e8/call  clear-stream/disp32
1604     # . . discard args
1605     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1606     # initialize input
1607     # . write(_test-input-stream, "30 abcd/o")
1608     # . . push args
1609     68/push  "30 abcd/o"/imm32
1610     68/push  _test-input-stream/imm32
1611     # . . call
1612     e8/call  write/disp32
1613     # . . discard args
1614     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1615     # convert-data(_test-input-stream, _test-output-buffered-file)
1616     # . . push args
1617     68/push  _test-output-buffered-file/imm32
1618     68/push  _test-input-stream/imm32
1619     # . . call
1620     e8/call  convert-data/disp32
1621     # . . discard args
1622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1623     # check output
1624     # . flush(_test-output-buffered-file)
1625     # . . push args
1626     68/push  _test-output-buffered-file/imm32
1627     # . . call
1628     e8/call  flush/disp32
1629     # . . discard args
1630     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1631     # . check-stream-equal(_test-output-stream, "30 abcd/o ", msg)
1632     # . . push args
1633     68/push  "F - test-convert-data-byte-then-name"/imm32
1634     68/push  "30 abcd/o "/imm32
1635     68/push  _test-output-stream/imm32
1636     # . . call
1637     e8/call  check-stream-equal/disp32
1638     # . . discard args
1639     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1640     # . epilog
1641     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1642     5d/pop-to-EBP
1643     c3/return
1644 
1645 test-convert-data-multiple-words:
1646     # Multiple words in input stream get processed one by one.
1647     # . prolog
1648     55/push-EBP
1649     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1650     # setup
1651     # . clear-stream(_test-input-stream)
1652     # . . push args
1653     68/push  _test-input-stream/imm32
1654     # . . call
1655     e8/call  clear-stream/disp32
1656     # . . discard args
1657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1658     # . clear-stream(_test-output-stream)
1659     # . . push args
1660     68/push  _test-output-stream/imm32
1661     # . . call
1662     e8/call  clear-stream/disp32
1663     # . . discard args
1664     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1665     # . clear-stream(_test-output-buffered-file+4)
1666     # . . push args
1667     b8/copy-to-EAX  _test-output-buffered-file/imm32
1668     05/add-to-EAX  4/imm32
1669     50/push-EAX
1670     # . . call
1671     e8/call  clear-stream/disp32
1672     # . . discard args
1673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1674     # initialize input
1675     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
1676     # . . push args
1677     68/push  "30 abcd/o 42e1/imm32"/imm32
1678     68/push  _test-input-stream/imm32
1679     # . . call
1680     e8/call  write/disp32
1681     # . . discard args
1682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1683     # convert-data(_test-input-stream, _test-output-buffered-file)
1684     # . . push args
1685     68/push  _test-output-buffered-file/imm32
1686     68/push  _test-input-stream/imm32
1687     # . . call
1688     e8/call  convert-data/disp32
1689     # . . discard args
1690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1691     # check output
1692     # . flush(_test-output-buffered-file)
1693     # . . push args
1694     68/push  _test-output-buffered-file/imm32
1695     # . . call
1696     e8/call  flush/disp32
1697     # . . discard args
1698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1699 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1725     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 ", msg)
1726     # . . push args
1727     68/push  "F - test-convert-data-multiple-words"/imm32
1728     68/push  "30 abcd/o e1 42 00 00 "/imm32
1729     68/push  _test-output-stream/imm32
1730     # . . call
1731     e8/call  check-stream-equal/disp32
1732     # . . discard args
1733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1734     # . epilog
1735     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1736     5d/pop-to-EBP
1737     c3/return
1738 
1739 test-convert-data-trailing-comment:
1740     # Trailing comments in data segment get appropriately ignored.
1741     # . prolog
1742     55/push-EBP
1743     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1744     # setup
1745     # . clear-stream(_test-input-stream)
1746     # . . push args
1747     68/push  _test-input-stream/imm32
1748     # . . call
1749     e8/call  clear-stream/disp32
1750     # . . discard args
1751     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1752     # . clear-stream(_test-output-stream)
1753     # . . push args
1754     68/push  _test-output-stream/imm32
1755     # . . call
1756     e8/call  clear-stream/disp32
1757     # . . discard args
1758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1759     # . clear-stream(_test-output-buffered-file+4)
1760     # . . push args
1761     b8/copy-to-EAX  _test-output-buffered-file/imm32
1762     05/add-to-EAX  4/imm32
1763     50/push-EAX
1764     # . . call
1765     e8/call  clear-stream/disp32
1766     # . . discard args
1767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1768     # initialize input
1769     # . write(_test-input-stream, "30/imm32 # comment")
1770     # . . push args
1771     68/push  "30/imm32 # comment"/imm32
1772     68/push  _test-input-stream/imm32
1773     # . . call
1774     e8/call  write/disp32
1775     # . . discard args
1776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1777     # convert-data(_test-input-stream, _test-output-buffered-file)
1778     # . . push args
1779     68/push  _test-output-buffered-file/imm32
1780     68/push  _test-input-stream/imm32
1781     # . . call
1782     e8/call  convert-data/disp32
1783     # . . discard args
1784     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1785     # check output
1786     # . flush(_test-output-buffered-file)
1787     # . . push args
1788     68/push  _test-output-buffered-file/imm32
1789     # . . call
1790     e8/call  flush/disp32
1791     # . . discard args
1792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1793 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1819     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
1820     # . . push args
1821     68/push  "F - test-convert-data-trailing-comment"/imm32
1822     68/push  "30 00 00 00 # comment"/imm32
1823     68/push  _test-output-stream/imm32
1824     # . . call
1825     e8/call  check-stream-equal/disp32
1826     # . . discard args
1827     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1828     # . epilog
1829     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1830     5d/pop-to-EBP
1831     c3/return
1832 
1833 # pack an instruction, following the C++ version
1834 #
1835 # zero error handling at the moment (continuing to rely on the C++ version for that):
1836 #   missing fields are always 0-filled
1837 #   bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte.
1838 #   may pick up any of duplicate operands in an instruction
1839 #   silently drop extraneous operands
1840 #   unceremoniously abort on non-numeric operands except disp or imm
1841 #   opcodes must be lowercase and zero padded
1842 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
1843 convert-instruction:  # line : (address stream byte), out : (address buffered-file) -> <void>
1844     # pseudocode:
1845     #   # some early exits
1846     #   var word-slice = next-word(line)
1847     #   if slice-empty?(word-slice)
1848     #     write-stream-data(out, line)
1849     #     return
1850     #   if slice-starts-with?(word-slice, "#")
1851     #     write-stream-data(out, line)
1852     #     return
1853     #   if slice-ends-with?(word-slice, ":")
1854     #     write-stream-data(out, line)
1855     #     return
1856     #   # really convert
1857     #   emit-opcodes(line, out)
1858     #   emit-modrm(line, out)
1859     #   emit-sib(line, out)
1860     #   emit-disp(line, out)
1861     #   emit-imm(line, out)
1862     #   emit-line-in-comment(line, out)
1863     #
1864     # . prolog
1865     55/push-EBP
1866     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1867     # . save registers
1868     50/push-EAX
1869     51/push-ECX
1870     52/push-EDX
1871     # var word-slice/ECX = {0, 0}
1872     68/push  0/imm32/end
1873     68/push  0/imm32/start
1874     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1875     # next-word(line, word-slice)
1876     # . . push args
1877     51/push-ECX
1878     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1879     # . . call
1880     e8/call  next-word/disp32
1881     # . . discard args
1882     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1883 $convert-instruction:check0:
1884     # if (slice-empty?(word-slice)) break
1885     # . EAX = slice-empty?(word-slice)
1886     # . . push args
1887     51/push-ECX
1888     # . . call
1889     e8/call  slice-empty?/disp32
1890     # . . discard args
1891     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1892     # . if (EAX != 0) pass through
1893     3d/compare-EAX-and  0/imm32
1894     75/jump-if-not-equal  $convert-instruction:pass-through/disp8
1895 $convert-instruction:check1:
1896     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
1897     # . start/EDX = word-slice->start
1898     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
1899     # . c/EAX = *start
1900     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1901     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
1902     # . if (EAX == '#') pass through
1903     3d/compare-EAX-and  0x23/imm32/hash
1904     74/jump-if-equal  $convert-instruction:pass-through/disp8
1905 $convert-instruction:check2:
1906     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
1907     # . end/EDX = word-slice->end
1908     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
1909     # . c/EAX = *(end-1)
1910     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1911     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
1912     # . if (EAX == ':') pass through
1913     3d/compare-EAX-and  0x3a/imm32/colon
1914     75/jump-if-not-equal  $convert-instruction:really-convert/disp8
1915 $convert-instruction:pass-through:
1916     # write-stream-data(out, line)
1917     # . . push args
1918     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1919     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1920     # . . call
1921     e8/call  write-stream-data/disp32
1922     # . . discard args
1923     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1924     # return
1925     eb/jump  $convert-instruction:end/disp8
1926 $convert-instruction:really-convert:
1927     # emit-opcodes(line, out)
1928     # . . push args
1929     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1930     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1931     # . . call
1932     e8/call  emit-opcodes/disp32
1933     # . . discard args
1934     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1935     # emit-modrm(line, out)
1936     # . . push args
1937     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1938     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1939     # . . call
1940     e8/call  emit-modrm/disp32
1941     # . . discard args
1942     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1943     # emit-sib(line, out)
1944     # . . push args
1945     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1946     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1947     # . . call
1948     e8/call  emit-sib/disp32
1949     # . . discard args
1950     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1951     # emit-disp(line, out)
1952     # . . push args
1953     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1954     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1955     # . . call
1956     e8/call  emit-disp/disp32
1957     # . . discard args
1958     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1959     # emit-imm(line, out)
1960     # . . push args
1961     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1962     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1963     # . . call
1964     e8/call  emit-imm/disp32
1965     # . . discard args
1966     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1967     # emit-line-in-comment(line, out)
1968     # . . push args
1969     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1970     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1971     # . . call
1972     e8/call  emit-line-in-comment/disp32
1973     # . . discard args
1974     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1975 $convert-instruction:end:
1976     # . restore locals
1977     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1978     # . restore registers
1979     5a/pop-to-EDX
1980     59/pop-to-ECX
1981     58/pop-to-EAX
1982     # . epilog
1983     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1984     5d/pop-to-EBP
1985     c3/return
1986 
1987 emit-opcodes:  # line : (address stream byte), out : (address buffered-file) -> <void>
1988     # opcodes occupy 1-3 bytes:
1989     #   xx
1990     #   0f xx
1991     #   f2 xx
1992     #   f3 xx
1993     #   f2 0f xx
1994     #   f3 0f xx
1995     #
1996     # pseudocode:
1997     #   rewind-stream(line)
1998     #
1999     #   var op1 = next-word(line)
2000     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
2001     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
2002     #   write-slice-buffered(out, op1)
2003     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3")
2004     #     return
2005     #
2006     #   var op2 = next-word(line)
2007     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
2008     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
2009     #   write-slice-buffered(out, op2)
2010     #   if slice-equal?(op1, "0f")
2011     #     return
2012     #   if !slice-equal?(op2, "0f")
2013     #     return
2014     #
2015     #   var op3 = next-word(line)
2016     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
2017     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
2018     #   write-slice-buffered(out, op3)
2019     #
2020     # . prolog
2021     55/push-EBP
2022     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2023     # . save registers
2024     50/push-EAX
2025     51/push-ECX
2026     52/push-EDX
2027     53/push-EBX
2028     # var op1/ECX = {0, 0}
2029     68/push  0/imm32/end
2030     68/push  0/imm32/start
2031     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2032     # var op2/EDX = {0, 0}
2033     68/push  0/imm32/end
2034     68/push  0/imm32/start
2035     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
2036     # rewind-stream(line)
2037     # . . push args
2038     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2039     # . . call
2040     e8/call  rewind-stream/disp32
2041     # . . discard args
2042     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2043 $emit-opcodes:op1:
2044     # next-word(line, op1)
2045     # . . push args
2046     51/push-ECX
2047     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2048     # . . call
2049     e8/call  next-word/disp32
2050     # . . discard args
2051     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2052     # if (slice-empty?(op1)) return
2053     # . EAX = slice-empty?(op1)
2054     # . . push args
2055     51/push-ECX
2056     # . . call
2057     e8/call  slice-empty?/disp32
2058     # . . discard args
2059     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2060     # . if (EAX != 0) return
2061     3d/compare-EAX-and  0/imm32
2062     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2063     # if (slice-starts-with?(op1, "#")) return
2064     # . start/EBX = op1->start
2065     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # copy *ECX to EBX
2066     # . c/EAX = *start
2067     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2068     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2069     # . if (EAX == '#') return
2070     3d/compare-EAX-and  0x23/imm32/hash
2071     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2072     # op1 = next-token-from-slice(op1->start, op1->end, '/')
2073     # . . push args
2074     51/push-ECX
2075     68/push  0x2f/imm32/slash
2076     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
2077     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
2078     # . . call
2079     e8/call  next-token-from-slice/disp32
2080     # . . discard args
2081     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2082     # write-slice-buffered(out, op1)
2083     # . . push args
2084     51/push-ECX
2085     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2086     # . . call
2087     e8/call  write-slice-buffered/disp32
2088     # . . discard args
2089     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2090     # write-buffered(out, " ")
2091     # . . push args
2092     68/push  " "/imm32
2093     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2094     # . . call
2095     e8/call  write-buffered/disp32
2096     # . . discard args
2097     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2098     # if (slice-equal?(op1, "0f")) goto op2
2099     # . EAX = slice-equal?(op1, "0f")
2100     # . . push args
2101     68/push  "0f"/imm32
2102     51/push-ECX
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 != 0) goto op2
2108     3d/compare-EAX-and  0/imm32
2109     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2110     # if (slice-equal?(op1, "f2")) goto op2
2111     # . EAX = slice-equal?(op1, "f2")
2112     # . . push args
2113     68/push  "f2"/imm32
2114     51/push-ECX
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 != 0) goto op2
2120     3d/compare-EAX-and  0/imm32
2121     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2122     # if (slice-equal?(op1, "f3")) goto op2
2123     # . EAX = slice-equal?(op1, "f3")
2124     # . . push args
2125     68/push  "f3"/imm32
2126     51/push-ECX
2127     # . . call
2128     e8/call  slice-equal?/disp32
2129     # . . discard args
2130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2131     # . if (EAX != 0) goto op2
2132     3d/compare-EAX-and  0/imm32
2133     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2134     # otherwise return
2135     e9/jump  $emit-opcodes:end/disp32
2136 $emit-opcodes:op2:
2137     # next-word(line, op2)
2138     # . . push args
2139     52/push-EDX
2140     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2141     # . . call
2142     e8/call  next-word/disp32
2143     # . . discard args
2144     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2145     # if (slice-empty?(op2)) return
2146     # . EAX = slice-empty?(op2)
2147     # . . push args
2148     52/push-EDX
2149     # . . call
2150     e8/call  slice-empty?/disp32
2151     # . . discard args
2152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2153     # . if (EAX != 0) return
2154     3d/compare-EAX-and  0/imm32
2155     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2156     # if (slice-starts-with?(op2, "#")) return
2157     # . start/EBX = op2->start
2158     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
2159     # . c/EAX = *start
2160     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2161     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2162     # . if (EAX == '#') return
2163     3d/compare-EAX-and  0x23/imm32/hash
2164     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2165     # op2 = next-token-from-slice(op2->start, op2->end, '/')
2166     # . . push args
2167     52/push-EDX
2168     68/push  0x2f/imm32/slash
2169     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
2170     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
2171     # . . call
2172     e8/call  next-token-from-slice/disp32
2173     # . . discard args
2174     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2175     # write-slice-buffered(out, op2)
2176     # . . push args
2177     52/push-EDX
2178     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2179     # . . call
2180     e8/call  write-slice-buffered/disp32
2181     # . . discard args
2182     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2183     # write-buffered(out, " ")
2184     # . . push args
2185     68/push  " "/imm32
2186     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2187     # . . call
2188     e8/call  write-buffered/disp32
2189     # . . discard args
2190     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2191     # if (slice-equal?(op1, "0f")) return
2192     # . EAX = slice-equal?(op1, "0f")
2193     # . . push args
2194     68/push  "0f"/imm32
2195     51/push-ECX
2196     # . . call
2197     e8/call  slice-equal?/disp32
2198     # . . discard args
2199     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2200     # . if (EAX != 0) return
2201     3d/compare-EAX-and  0/imm32
2202     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2203     # if (!slice-equal?(op2, "0f")) return
2204     # . EAX = slice-equal?(op2, "0f")
2205     # . . push args
2206     68/push  "0f"/imm32
2207     52/push-EDX
2208     # . . call
2209     e8/call  slice-equal?/disp32
2210     # . . discard args
2211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2212     # . if (EAX == 0) return
2213     3d/compare-EAX-and  0/imm32
2214     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2215 $emit-opcodes:op3:
2216     # next-word(line, op3)  # reuse op2/EDX
2217     # . . push args
2218     52/push-EDX
2219     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2220     # . . call
2221     e8/call  next-word/disp32
2222     # . . discard args
2223     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2224     # if (slice-empty?(op3)) return
2225     # . EAX = slice-empty?(op3)
2226     # . . push args
2227     52/push-EDX
2228     # . . call
2229     e8/call  slice-empty?/disp32
2230     # . . discard args
2231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2232     # . if (EAX != 0) return
2233     3d/compare-EAX-and  0/imm32
2234     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2235     # if (slice-starts-with?(op3, "#")) return
2236     # . start/EBX = op2->start
2237     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
2238     # . c/EAX = *start
2239     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2240     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2241     # . if (EAX == '#') return
2242     3d/compare-EAX-and  0x23/imm32/hash
2243     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2244     # op3 = next-token-from-slice(op3->start, op3->end, '/')
2245     # . . push args
2246     52/push-EDX
2247     68/push  0x2f/imm32/slash
2248     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
2249     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
2250     # . . call
2251     e8/call  next-token-from-slice/disp32
2252     # . . discard args
2253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2254     # write-slice-buffered(out, op3)
2255     # . . push args
2256     52/push-EDX
2257     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2258     # . . call
2259     e8/call  write-slice-buffered/disp32
2260     # . . discard args
2261     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2262     # write-buffered(out, " ")
2263     # . . push args
2264     68/push  " "/imm32
2265     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2266     # . . call
2267     e8/call  write-buffered/disp32
2268     # . . discard args
2269     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2270 $emit-opcodes:end:
2271     # . restore locals
2272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2273     # . restore registers
2274     5b/pop-to-EBX
2275     5a/pop-to-EDX
2276     59/pop-to-ECX
2277     58/pop-to-EAX
2278     # . epilog
2279     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2280     5d/pop-to-EBP
2281     c3/return
2282 
2283 emit-modrm:  # line : (address stream byte), out : (address buffered-file) -> <void>
2284     # pseudocode:
2285     #   rewind-stream(line)
2286     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
2287     #   var word-slice = {0, 0}
2288     #   while true
2289     #     word-slice = next-word(line)
2290     #     if (slice-empty?(word-slice)) break
2291     #     if (slice-starts-with?(word-slice, "#")) break
2292     #     if (has-metadata?(word-slice, "mod"))
2293     #       mod = parse-hex-int(next-token-from-slice(word-slice, "/"))
2294     #       has-modrm? = true
2295     #     else if (has-metadata?(word-slice, "rm32"))
2296     #       rm32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
2297     #       has-modrm? = true
2298     #     else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop"))
2299     #       r32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
2300     #       has-modrm? = true
2301     #   if has-modrm?
2302     #     var modrm = mod & 0b11
2303     #     modrm <<= 2
2304     #     modrm |= r32 & 0b111
2305     #     modrm <<= 3
2306     #     modrm |= rm32 & 0b111
2307     #     emit-hex(out, modrm, 1)
2308     #
2309     # . prolog
2310     55/push-EBP
2311     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2312     # . save registers
2313     50/push-EAX
2314     51/push-ECX
2315     52/push-EDX
2316     53/push-EBX
2317     56/push-ESI
2318     57/push-EDI
2319     # var word-slice/ECX = {0, 0}
2320     68/push  0/imm32/end
2321     68/push  0/imm32/start
2322     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2323     # var has-modrm?/EDX = false
2324     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
2325     # var mod/EBX = 0
2326     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2327     # var rm32/ESI = 0
2328     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
2329     # var r32/EDI = 0
2330     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
2331     # rewind-stream(line)
2332     # . . push args
2333     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2334     # . . call
2335     e8/call  rewind-stream/disp32
2336     # . . discard args
2337     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2338 $emit-modrm:loop:
2339 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2365     # next-word(line, word-slice)
2366     # . . push args
2367     51/push-ECX
2368     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2369     # . . call
2370     e8/call  next-word/disp32
2371     # . . discard args
2372     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2373 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2415 $emit-modrm:check0:
2416     # if (slice-empty?(word-slice)) break
2417     # . EAX = slice-empty?(word-slice)
2418     # . . push args
2419     51/push-ECX
2420     # . . call
2421     e8/call  slice-empty?/disp32
2422     # . . discard args
2423     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2424     # . if (EAX != 0) pass through
2425     3d/compare-EAX-and  0/imm32
2426     0f 85/jump-if-not-equal  $emit-modrm:break/disp32
2427 $emit-modrm:check1:
2428     # if (slice-starts-with?(word-slice, "#")) break
2429     # . spill EDX
2430     52/push-EDX
2431     # . start/EDX = word-slice->start
2432     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
2433     # . c/EAX = *start
2434     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2435     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
2436     # . restore EDX
2437     5a/pop-to-EDX
2438     # . if (EAX == '#') pass through
2439     3d/compare-EAX-and  0x23/imm32/hash
2440     0f 84/jump-if-equal  $emit-modrm:break/disp32
2441 $emit-modrm:check-for-mod:
2442     # if (has-metadata?(word-slice, "mod"))
2443     # . EAX = has-metadata?(ECX, "mod")
2444     # . . push args
2445     68/push  "mod"/imm32
2446     51/push-ECX
2447     # . . call
2448     e8/call  has-metadata?/disp32
2449     # . . discard args
2450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2451     # . if (EAX == 0) goto next check
2452     3d/compare-EAX-and  0/imm32
2453     74/jump-if-equal  $emit-modrm:check-for-rm32/disp8
2454 $emit-modrm:mod:
2455     # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2456     # . EAX = parse-datum-of-word(word-slice)
2457     # . . push args
2458     51/push-ECX
2459     # . . call
2460     e8/call  parse-datum-of-word/disp32
2461     # . . discard args
2462     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2463     # . mod = EAX
2464     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
2465     # has-modrm? = true
2466     ba/copy-to-EDX  1/imm32/true
2467     # continue
2468     e9/jump  $emit-modrm:loop/disp32
2469 $emit-modrm:check-for-rm32:
2470     # if (has-metadata?(word-slice, "rm32"))
2471     # . EAX = has-metadata?(ECX, "rm32")
2472     # . . push args
2473     68/push  "rm32"/imm32
2474     51/push-ECX
2475     # . . call
2476     e8/call  has-metadata?/disp32
2477     # . . discard args
2478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2479     # . if (EAX == 0) goto next check
2480     3d/compare-EAX-and  0/imm32
2481     74/jump-if-equal  $emit-modrm:check-for-r32/disp8
2482 $emit-modrm:rm32:
2483     # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2484     # . EAX = parse-datum-of-word(word-slice)
2485     # . . push args
2486     51/push-ECX
2487     # . . call
2488     e8/call  parse-datum-of-word/disp32
2489     # . . discard args
2490     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2491     # . rm32 = EAX
2492     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
2493     # has-modrm? = true
2494     ba/copy-to-EDX  1/imm32/true
2495     # continue
2496     e9/jump  $emit-modrm:loop/disp32
2497 $emit-modrm:check-for-r32:
2498     # if (has-metadata?(word-slice, "r32"))
2499     # . EAX = has-metadata?(ECX, "r32")
2500     # . . push args
2501     68/push  "r32"/imm32
2502     51/push-ECX
2503     # . . call
2504     e8/call  has-metadata?/disp32
2505     # . . discard args
2506     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2507     # . if (EAX == 0) goto next check
2508     3d/compare-EAX-and  0/imm32
2509     74/jump-if-equal  $emit-modrm:check-for-subop/disp8
2510 $emit-modrm:r32:
2511     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2512     # . EAX = parse-datum-of-word(word-slice)
2513     # . . push args
2514     51/push-ECX
2515     # . . call
2516     e8/call  parse-datum-of-word/disp32
2517     # . . discard args
2518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2519     # . r32 = EAX
2520     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
2521     # has-modrm? = true
2522     ba/copy-to-EDX  1/imm32/true
2523     # continue
2524     e9/jump  $emit-modrm:loop/disp32
2525 $emit-modrm:check-for-subop:
2526     # if (has-metadata?(word-slice, "subop"))
2527     # . EAX = has-metadata?(ECX, "subop")
2528     # . . push args
2529     68/push  "subop"/imm32
2530     51/push-ECX
2531     # . . call
2532     e8/call  has-metadata?/disp32
2533     # . . discard args
2534     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2535     # . if (EAX == 0) loop
2536     3d/compare-EAX-and  0/imm32
2537     0f 84/jump-if-equal  $emit-modrm:loop/disp32
2538 $emit-modrm:subop:
2539     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2540     # . EAX = parse-datum-of-word(word-slice)
2541     # . . push args
2542     51/push-ECX
2543     # . . call
2544     e8/call  parse-datum-of-word/disp32
2545     # . . discard args
2546     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2547     # . r32 = EAX
2548     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
2549     # has-modrm? = true
2550     ba/copy-to-EDX  1/imm32/true
2551     # continue
2552     e9/jump  $emit-modrm:loop/disp32
2553 $emit-modrm:break:
2554     # if (!has-modrm?) return
2555     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
2556     74/jump-if-equal  $emit-modrm:end/disp8
2557 $emit-modrm:calculate:
2558     # modrm/EBX = mod & 0b11
2559     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
2560     # modrm <<= 2
2561     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
2562     # modrm |= r32 & 0b111
2563     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
2564     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
2565     # modrm <<= 3
2566     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
2567     # modrm |= rm32 & 0b111
2568     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
2569     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
2570 $emit-modrm:emit:
2571     # emit-hex(out, modrm, 1)
2572     # . . push args
2573     68/push  1/imm32
2574     53/push-EBX
2575     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2576     # . . call
2577     e8/call  emit-hex/disp32
2578     # . . discard args
2579     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2580 $emit-modrm:end:
2581     # . restore locals
2582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2583     # . restore registers
2584     5f/pop-to-EDI
2585     5e/pop-to-ESI
2586     5b/pop-to-EBX
2587     5a/pop-to-EDX
2588     59/pop-to-ECX
2589     58/pop-to-EAX
2590     # . epilog
2591     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2592     5d/pop-to-EBP
2593     c3/return
2594 
2595 emit-sib:  # line : (address stream byte), out : (address buffered-file) -> <void>
2596     # pseudocode:
2597     #   var has-sib? = false, base = 0, index = 0, scale = 0
2598     #   var word-slice = {0, 0}
2599     #   while true
2600     #     word-slice = next-word(line)
2601     #     if (slice-empty?(word-slice)) break
2602     #     if (slice-starts-with?(word-slice, "#")) break
2603     #     if (has-metadata?(word-slice, "base")
2604     #       base = parse-hex-int(next-token-from-slice(word-slice, "/"))
2605     #       has-sib? = true
2606     #     else if (has-metadata?(word-slice, "index")
2607     #       index = parse-hex-int(next-token-from-slice(word-slice, "/"))
2608     #       has-sib? = true
2609     #     else if (has-metadata?(word-slice, "scale")
2610     #       scale = parse-hex-int(next-token-from-slice(word-slice, "/"))
2611     #       has-sib? = true
2612     #   if has-sib?
2613     #     var sib = scale & 0b11
2614     #     sib <<= 2
2615     #     sib |= index & 0b111
2616     #     sib <<= 3
2617     #     sib |= base & 0b111
2618     #     emit-hex(out, sib, 1)
2619     #
2620     # . prolog
2621     55/push-EBP
2622     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2623     # . save registers
2624     50/push-EAX
2625     51/push-ECX
2626     52/push-EDX
2627     53/push-EBX
2628     56/push-ESI
2629     57/push-EDI
2630     # var word-slice/ECX = {0, 0}
2631     68/push  0/imm32/end
2632     68/push  0/imm32/start
2633     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2634     # var has-sib?/EDX = false
2635     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
2636     # var scale/EBX = 0
2637     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2638     # var base/ESI = 0
2639     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
2640     # var index/EDI = 0
2641     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
2642     # rewind-stream(line)
2643     # . . push args
2644     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2645     # . . call
2646     e8/call  rewind-stream/disp32
2647     # . . discard args
2648     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2649 $emit-sib:loop:
2650 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2676     # next-word(line, word-slice)
2677     # . . push args
2678     51/push-ECX
2679     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2680     # . . call
2681     e8/call  next-word/disp32
2682     # . . discard args
2683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2684 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2726 $emit-sib:check0:
2727     # if (slice-empty?(word-slice)) break
2728     # . EAX = slice-empty?(word-slice)
2729     # . . push args
2730     51/push-ECX
2731     # . . call
2732     e8/call  slice-empty?/disp32
2733     # . . discard args
2734     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2735     # . if (EAX != 0) pass through
2736     3d/compare-EAX-and  0/imm32
2737     0f 85/jump-if-not-equal  $emit-sib:break/disp32
2738 $emit-sib:check1:
2739     # if (slice-starts-with?(word-slice, "#")) break
2740     # . spill EDX
2741     52/push-EDX
2742     # . start/EDX = word-slice->start
2743     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
2744     # . c/EAX = *start
2745     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2746     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
2747     # . restore EDX
2748     5a/pop-to-EDX
2749     # . if (EAX == '#') pass through
2750     3d/compare-EAX-and  0x23/imm32/hash
2751     0f 84/jump-if-equal  $emit-sib:break/disp32
2752 $emit-sib:check-for-scale:
2753     # if (has-metadata?(word-slice, "scale"))
2754     # . EAX = has-metadata?(ECX, "scale")
2755     # . . push args
2756     68/push  "scale"/imm32
2757     51/push-ECX
2758     # . . call
2759     e8/call  has-metadata?/disp32
2760     # . . discard args
2761     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2762     # . if (EAX == 0) goto next check
2763     3d/compare-EAX-and  0/imm32
2764     74/jump-if-equal  $emit-sib:check-for-base/disp8
2765 $emit-sib:scale:
2766     # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2767     # . EAX = parse-datum-of-word(word-slice)
2768     # . . push args
2769     51/push-ECX
2770     # . . call
2771     e8/call  parse-datum-of-word/disp32
2772     # . . discard args
2773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2774     # . scale = EAX
2775     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
2776     # has-sib? = true
2777     ba/copy-to-EDX  1/imm32/true
2778     # continue
2779     e9/jump  $emit-sib:loop/disp32
2780 $emit-sib:check-for-base:
2781     # if (has-metadata?(word-slice, "base"))
2782     # . EAX = has-metadata?(ECX, "base")
2783     # . . push args
2784     68/push  "base"/imm32
2785     51/push-ECX
2786     # . . call
2787     e8/call  has-metadata?/disp32
2788     # . . discard args
2789     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2790     # . if (EAX == 0) goto next check
2791     3d/compare-EAX-and  0/imm32
2792     74/jump-if-equal  $emit-sib:check-for-index/disp8
2793 $emit-sib:base:
2794     # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2795     # . EAX = parse-datum-of-word(word-slice)
2796     # . . push args
2797     51/push-ECX
2798     # . . call
2799     e8/call  parse-datum-of-word/disp32
2800     # . . discard args
2801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2802     # . base = EAX
2803     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
2804     # has-sib? = true
2805     ba/copy-to-EDX  1/imm32/true
2806     # continue
2807     e9/jump  $emit-sib:loop/disp32
2808 $emit-sib:check-for-index:
2809     # if (has-metadata?(word-slice, "index"))
2810     # . EAX = has-metadata?(ECX, "index")
2811     # . . push args
2812     68/push  "index"/imm32
2813     51/push-ECX
2814     # . . call
2815     e8/call  has-metadata?/disp32
2816     # . . discard args
2817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2818     # . if (EAX == 0) loop
2819     3d/compare-EAX-and  0/imm32
2820     0f 84/jump-if-equal  $emit-sib:loop/disp32
2821 $emit-sib:index:
2822     # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2823     # . EAX = parse-datum-of-word(word-slice)
2824     # . . push args
2825     51/push-ECX
2826     # . . call
2827     e8/call  parse-datum-of-word/disp32
2828     # . . discard args
2829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2830     # . index = EAX
2831     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
2832     # has-sib? = true
2833     ba/copy-to-EDX  1/imm32/true
2834     # continue
2835     e9/jump  $emit-sib:loop/disp32
2836 $emit-sib:break:
2837     # if (!has-sib?) return
2838     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
2839     74/jump-if-equal  $emit-sib:end/disp8
2840 $emit-sib:calculate:
2841     # sib/EBX = scale & 0b11
2842     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
2843     # sib <<= 2
2844     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
2845     # sib |= index & 0b111
2846     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
2847     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
2848     # sib <<= 3
2849     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
2850     # sib |= base & 0b111
2851     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
2852     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
2853 $emit-sib:emit:
2854     # emit-hex(out, sib, 1)
2855     # . . push args
2856     68/push  1/imm32
2857     53/push-EBX
2858     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2859     # . . call
2860     e8/call  emit-hex/disp32
2861     # . . discard args
2862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2863 $emit-sib:end:
2864     # . restore locals
2865     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2866     # . restore registers
2867     5f/pop-to-EDI
2868     5e/pop-to-ESI
2869     5b/pop-to-EBX
2870     5a/pop-to-EDX
2871     59/pop-to-ECX
2872     58/pop-to-EAX
2873     # . epilog
2874     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2875     5d/pop-to-EBP
2876     c3/return
2877 
2878 emit-disp:  # line : (address stream byte), out : (address buffered-file) -> <void>
2879     # pseudocode:
2880     #   rewind-stream(line)
2881     #   var word-slice = {0, 0}
2882     #   while true
2883     #     word-slice = next-word(line)
2884     #     if (slice-empty?(word-slice)) break
2885     #     if (slice-starts-with?(word-slice, "#")) break
2886     #     if has-metadata?(word-slice, "disp32")
2887     #       emit(out, word-slice, 4)
2888     #       break
2889     #     if has-metadata?(word-slice, "disp16")
2890     #       emit(out, word-slice, 2)
2891     #       break
2892     #     if has-metadata?(word-slice, "disp8")
2893     #       emit(out, word-slice, 1)
2894     #       break
2895     #
2896     # . prolog
2897     55/push-EBP
2898     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2899     # . save registers
2900     50/push-EAX
2901     51/push-ECX
2902     52/push-EDX
2903     # var word-slice/ECX = {0, 0}
2904     68/push  0/imm32/end
2905     68/push  0/imm32/start
2906     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2907     # rewind-stream(line)
2908     # . . push args
2909     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2910     # . . call
2911     e8/call  rewind-stream/disp32
2912     # . . discard args
2913     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2914 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2940 $emit-disp:loop:
2941     # next-word(line, word-slice)
2942     # . . push args
2943     51/push-ECX
2944     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2945     # . . call
2946     e8/call  next-word/disp32
2947     # . . discard args
2948     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2949 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2991 $emit-disp:check0:
2992     # if (slice-empty?(word-slice)) break
2993     # . EAX = slice-empty?(word-slice)
2994     # . . push args
2995     51/push-ECX
2996     # . . call
2997     e8/call  slice-empty?/disp32
2998     # . . discard args
2999     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3000     # . if (EAX != 0) pass through
3001     3d/compare-EAX-and  0/imm32
3002     0f 85/jump-if-not-equal  $emit-disp:break/disp32
3003 $emit-disp:check1:
3004     # if (slice-starts-with?(word-slice, "#")) break
3005     # . start/EDX = word-slice->start
3006     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
3007     # . c/EAX = *start
3008     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
3009     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
3010     # . if (EAX == '#') break
3011     3d/compare-EAX-and  0x23/imm32/hash
3012     0f 84/jump-if-equal  $emit-disp:break/disp32
3013 $emit-disp:check-for-disp32:
3014     # if (has-metadata?(word-slice, "disp32"))
3015     # . EAX = has-metadata?(ECX, "disp32")
3016     # . . push args
3017     68/push  "disp32"/imm32
3018     51/push-ECX
3019     # . . call
3020     e8/call  has-metadata?/disp32
3021     # . . discard args
3022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3023     # . if (EAX == 0) goto next check
3024     3d/compare-EAX-and  0/imm32
3025     74/jump-if-equal  $emit-disp:check-for-disp16/disp8
3026 $emit-disp:disp32:
3027     # emit(out, word-slice, 4)
3028     # . . push args
3029     68/push  4/imm32
3030     51/push-ECX
3031     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3032     # . . call
3033     e8/call  emit/disp32
3034     # . . discard args
3035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3036     # break
3037     e9/jump  $emit-disp:break/disp32
3038 $emit-disp:check-for-disp16:
3039     # else if (has-metadata?(word-slice, "disp16"))
3040     # . EAX = has-metadata?(ECX, "disp16")
3041     # . . push args
3042     68/push  "disp16"/imm32
3043     51/push-ECX
3044     # . . call
3045     e8/call  has-metadata?/disp32
3046     # . . discard args
3047     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3048     # . if (EAX == 0) goto next check
3049     3d/compare-EAX-and  0/imm32
3050     74/jump-if-equal  $emit-disp:check-for-disp8/disp8
3051 $emit-disp:disp16:
3052     # emit(out, word-slice, 2)
3053     # . . push args
3054     68/push  2/imm32
3055     51/push-ECX
3056     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3057     # . . call
3058     e8/call  emit/disp32
3059     # . . discard args
3060     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3061     # break
3062     e9/jump  $emit-disp:break/disp32
3063 $emit-disp:check-for-disp8:
3064     # if (has-metadata?(word-slice, "disp8"))
3065     # . EAX = has-metadata?(ECX, "disp8")
3066     # . . push args
3067     68/push  "disp8"/imm32
3068     51/push-ECX
3069     # . . call
3070     e8/call  has-metadata?/disp32
3071     # . . discard args
3072     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3073     # . if (EAX == 0) loop
3074     3d/compare-EAX-and  0/imm32
3075     0f 84/jump-if-equal  $emit-disp:loop/disp32
3076 $emit-disp:disp8:
3077     # emit(out, word-slice, 1)
3078     # . . push args
3079     68/push  1/imm32
3080     51/push-ECX
3081     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3082     # . . call
3083     e8/call  emit/disp32
3084     # . . discard args
3085     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3086     # break
3087 $emit-disp:break:
3088     # . restore locals
3089     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3090     # . restore registers
3091     5a/pop-to-EDX
3092     59/pop-to-ECX
3093     58/pop-to-EAX
3094     # . epilog
3095     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3096     5d/pop-to-EBP
3097     c3/return
3098 
3099 emit-imm:  # line : (address stream byte), out : (address buffered-file) -> <void>
3100     # pseudocode:
3101     #   rewind-stream(line)
3102     #   var word-slice = {0, 0}
3103     #   while true
3104     #     word-slice = next-word(line)
3105     #     if (slice-empty?(word-slice)) break
3106     #     if (slice-starts-with?(word-slice, "#")) break
3107     #     if has-metadata?(word-slice, "imm32")
3108     #       emit(out, word-slice, 4)
3109     #       break
3110     #     if has-metadata?(word-slice, "imm16")
3111     #       emit(out, word-slice, 2)
3112     #       break
3113     #     if has-metadata?(word-slice, "imm8")
3114     #       emit(out, word-slice, 1)
3115     #       break
3116     #
3117     # . prolog
3118     55/push-EBP
3119     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3120     # . save registers
3121     50/push-EAX
3122     51/push-ECX
3123     52/push-EDX
3124     # var word-slice/ECX = {0, 0}
3125     68/push  0/imm32/end
3126     68/push  0/imm32/start
3127     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
3128     # rewind-stream(line)
3129     # . . push args
3130     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3131     # . . call
3132     e8/call  rewind-stream/disp32
3133     # . . discard args
3134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3135 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
3161 $emit-imm:loop:
3162     # next-word(line, word-slice)
3163     # . . push args
3164     51/push-ECX
3165     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3166     # . . call
3167     e8/call  next-word/disp32
3168     # . . discard args
3169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3170 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
3212 $emit-imm:check0:
3213     # if (slice-empty?(word-slice)) break
3214     # . EAX = slice-empty?(word-slice)
3215     # . . push args
3216     51/push-ECX
3217     # . . call
3218     e8/call  slice-empty?/disp32
3219     # . . discard args
3220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3221     # . if (EAX != 0) pass through
3222     3d/compare-EAX-and  0/imm32
3223     0f 85/jump-if-not-equal  $emit-imm:break/disp32
3224 $emit-imm:check1:
3225     # if (slice-starts-with?(word-slice, "#")) break
3226     # . start/EDX = slice->start
3227     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
3228     # . c/EAX = *start
3229     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
3230     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
3231     # . if (EAX == '#') break
3232     3d/compare-EAX-and  0x23/imm32/hash
3233     0f 84/jump-if-equal  $emit-imm:break/disp32
3234 $emit-imm:check-for-imm32:
3235     # if (has-metadata?(word-slice, "imm32"))
3236     # . EAX = has-metadata?(ECX, "imm32")
3237     # . . push args
3238     68/push  "imm32"/imm32
3239     51/push-ECX
3240     # . . call
3241     e8/call  has-metadata?/disp32
3242     # . . discard args
3243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3244     # . if (EAX == 0) goto next check
3245     3d/compare-EAX-and  0/imm32
3246     74/jump-if-equal  $emit-imm:check-for-imm16/disp8
3247 $emit-imm:imm32:
3248     # emit(out, word-slice, 4)
3249     # . . push args
3250     68/push  4/imm32
3251     51/push-ECX
3252     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3253     # . . call
3254     e8/call  emit/disp32
3255     # . . discard args
3256     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3257     # break
3258     e9/jump  $emit-imm:break/disp32
3259 $emit-imm:check-for-imm16:
3260     # if (has-metadata?(word-slice, "imm16"))
3261     # . EAX = has-metadata?(ECX, "imm16")
3262     # . . push args
3263     68/push  "imm16"/imm32
3264     51/push-ECX
3265     # . . call
3266     e8/call  has-metadata?/disp32
3267     # . . discard args
3268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3269     # . if (EAX == 0) goto next check
3270     3d/compare-EAX-and  0/imm32
3271     74/jump-if-equal  $emit-imm:check-for-imm8/disp8
3272 $emit-imm:imm16:
3273     # emit(out, word-slice, 2)
3274     # . . push args
3275     68/push  2/imm32
3276     51/push-ECX
3277     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3278     # . . call
3279     e8/call  emit/disp32
3280     # . . discard args
3281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3282     # break
3283     e9/jump  $emit-imm:break/disp32
3284 $emit-imm:check-for-imm8:
3285     # if (has-metadata?(word-slice, "imm8"))
3286     # . EAX = has-metadata?(ECX, "imm8")
3287     # . . push args
3288     68/push  "imm8"/imm32
3289     51/push-ECX
3290     # . . call
3291     e8/call  has-metadata?/disp32
3292     # . . discard args
3293     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3294     # . if (EAX == 0) loop
3295     3d/compare-EAX-and  0/imm32
3296     0f 84/jump-if-equal  $emit-imm:loop/disp32
3297 $emit-imm:imm8:
3298     # emit(out, word-slice, 1)
3299     # . . push args
3300     68/push  1/imm32
3301     51/push-ECX
3302     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3303     # . . call
3304     e8/call  emit/disp32
3305     # . . discard args
3306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3307     # break
3308 $emit-imm:break:
3309     # . restore locals
3310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3311     # . restore registers
3312     5a/pop-to-EDX
3313     59/pop-to-ECX
3314     58/pop-to-EAX
3315     # . epilog
3316     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3317     5d/pop-to-EBP
3318     c3/return
3319 
3320 emit-line-in-comment:  # line : (address stream byte), out : (address buffered-file) -> <void>
3321     # . prolog
3322     55/push-EBP
3323     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3324     # write-buffered(out, " # ")
3325     # . . push args
3326     68/push  " # "/imm32
3327     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3328     # . . call
3329     e8/call  write-buffered/disp32
3330     # . . discard args
3331     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3332     # write-stream-data(out, line)
3333     # . . push args
3334     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3335     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3336     # . . call
3337     e8/call  write-stream-data/disp32
3338     # . . discard args
3339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3340 $emit-line-in-comment:end:
3341     # . epilog
3342     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3343     5d/pop-to-EBP
3344     c3/return
3345 
3346 test-convert-instruction-passes-comments-through:
3347     # if a line starts with '#', pass it along unchanged
3348     # . prolog
3349     55/push-EBP
3350     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3351     # setup
3352     # . clear-stream(_test-input-stream)
3353     # . . push args
3354     68/push  _test-input-stream/imm32
3355     # . . call
3356     e8/call  clear-stream/disp32
3357     # . . discard args
3358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3359     # . clear-stream(_test-output-stream)
3360     # . . push args
3361     68/push  _test-output-stream/imm32
3362     # . . call
3363     e8/call  clear-stream/disp32
3364     # . . discard args
3365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3366     # . clear-stream(_test-output-buffered-file+4)
3367     # . . push args
3368     b8/copy-to-EAX  _test-output-buffered-file/imm32
3369     05/add-to-EAX  4/imm32
3370     50/push-EAX
3371     # . . call
3372     e8/call  clear-stream/disp32
3373     # . . discard args
3374     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3375     # initialize input
3376     # . write(_test-input-stream, "# abcd")
3377     # . . push args
3378     68/push  "# abcd"/imm32
3379     68/push  _test-input-stream/imm32
3380     # . . call
3381     e8/call  write/disp32
3382     # . . discard args
3383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3384     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3385     # . . push args
3386     68/push  _test-output-buffered-file/imm32
3387     68/push  _test-input-stream/imm32
3388     # . . call
3389     e8/call  convert-instruction/disp32
3390     # . . discard args
3391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3392     # check that the line just passed through
3393     # . flush(_test-output-buffered-file)
3394     # . . push args
3395     68/push  _test-output-buffered-file/imm32
3396     # . . call
3397     e8/call  flush/disp32
3398     # . . discard args
3399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3400     # . check-stream-equal(_test-output-stream, "# abcd", msg)
3401     # . . push args
3402     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
3403     68/push  "# abcd"/imm32
3404     68/push  _test-output-stream/imm32
3405     # . . call
3406     e8/call  check-stream-equal/disp32
3407     # . . discard args
3408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3409     # . epilog
3410     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3411     5d/pop-to-EBP
3412     c3/return
3413 
3414 test-convert-instruction-passes-labels-through:
3415     # if the first word ends with ':', pass along the entire line unchanged
3416     # . prolog
3417     55/push-EBP
3418     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3419     # setup
3420     # . clear-stream(_test-input-stream)
3421     # . . push args
3422     68/push  _test-input-stream/imm32
3423     # . . call
3424     e8/call  clear-stream/disp32
3425     # . . discard args
3426     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3427     # . clear-stream(_test-output-stream)
3428     # . . push args
3429     68/push  _test-output-stream/imm32
3430     # . . call
3431     e8/call  clear-stream/disp32
3432     # . . discard args
3433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3434     # . clear-stream(_test-output-buffered-file+4)
3435     # . . push args
3436     b8/copy-to-EAX  _test-output-buffered-file/imm32
3437     05/add-to-EAX  4/imm32
3438     50/push-EAX
3439     # . . call
3440     e8/call  clear-stream/disp32
3441     # . . discard args
3442     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3443     # initialize input
3444     # . write(_test-input-stream, "ab: # cd")
3445     # . . push args
3446     68/push  "ab: # cd"/imm32
3447     68/push  _test-input-stream/imm32
3448     # . . call
3449     e8/call  write/disp32
3450     # . . discard args
3451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3452     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3453     # . . push args
3454     68/push  _test-output-buffered-file/imm32
3455     68/push  _test-input-stream/imm32
3456     # . . call
3457     e8/call  convert-instruction/disp32
3458     # . . discard args
3459     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3460     # check that the line just passed through
3461     # . flush(_test-output-buffered-file)
3462     # . . push args
3463     68/push  _test-output-buffered-file/imm32
3464     # . . call
3465     e8/call  flush/disp32
3466     # . . discard args
3467     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3468     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
3469     # . . push args
3470     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
3471     68/push  "ab: # cd"/imm32
3472     68/push  _test-output-stream/imm32
3473     # . . call
3474     e8/call  check-stream-equal/disp32
3475     # . . discard args
3476     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3477     # . epilog
3478     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3479     5d/pop-to-EBP
3480     c3/return
3481 
3482 test-convert-instruction-handles-single-opcode:
3483     # if the instruction consists of a single opcode, strip its metadata and pass it along
3484     # . prolog
3485     55/push-EBP
3486     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3487     # setup
3488     # . clear-stream(_test-input-stream)
3489     # . . push args
3490     68/push  _test-input-stream/imm32
3491     # . . call
3492     e8/call  clear-stream/disp32
3493     # . . discard args
3494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3495     # . clear-stream(_test-output-stream)
3496     # . . push args
3497     68/push  _test-output-stream/imm32
3498     # . . call
3499     e8/call  clear-stream/disp32
3500     # . . discard args
3501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3502     # . clear-stream(_test-output-buffered-file+4)
3503     # . . push args
3504     b8/copy-to-EAX  _test-output-buffered-file/imm32
3505     05/add-to-EAX  4/imm32
3506     50/push-EAX
3507     # . . call
3508     e8/call  clear-stream/disp32
3509     # . . discard args
3510     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3511     # initialize input
3512     # . write(_test-input-stream, "ab/cd # comment")
3513     # . . push args
3514     68/push  "ab/cd # comment"/imm32
3515     68/push  _test-input-stream/imm32
3516     # . . call
3517     e8/call  write/disp32
3518     # . . discard args
3519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3520     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3521     # . . push args
3522     68/push  _test-output-buffered-file/imm32
3523     68/push  _test-input-stream/imm32
3524     # . . call
3525     e8/call  convert-instruction/disp32
3526     # . . discard args
3527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3528     # check output
3529     # . flush(_test-output-buffered-file)
3530     # . . push args
3531     68/push  _test-output-buffered-file/imm32
3532     # . . call
3533     e8/call  flush/disp32
3534     # . . discard args
3535     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3536 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3562     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
3563     # . . push args
3564     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
3565     68/push  "ab  # ab/cd # comment"/imm32
3566     68/push  _test-output-stream/imm32
3567     # . . call
3568     e8/call  check-stream-equal/disp32
3569     # . . discard args
3570     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3571     # . epilog
3572     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3573     5d/pop-to-EBP
3574     c3/return
3575 
3576 test-convert-instruction-handles-0f-opcode:
3577     # if the instruction starts with 0f opcode, include a second opcode
3578     # . prolog
3579     55/push-EBP
3580     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3581     # setup
3582     # . clear-stream(_test-input-stream)
3583     # . . push args
3584     68/push  _test-input-stream/imm32
3585     # . . call
3586     e8/call  clear-stream/disp32
3587     # . . discard args
3588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3589     # . clear-stream(_test-output-stream)
3590     # . . push args
3591     68/push  _test-output-stream/imm32
3592     # . . call
3593     e8/call  clear-stream/disp32
3594     # . . discard args
3595     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3596     # . clear-stream(_test-output-buffered-file+4)
3597     # . . push args
3598     b8/copy-to-EAX  _test-output-buffered-file/imm32
3599     05/add-to-EAX  4/imm32
3600     50/push-EAX
3601     # . . call
3602     e8/call  clear-stream/disp32
3603     # . . discard args
3604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3605     # initialize input
3606     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
3607     # . . push args
3608     68/push  "0f/m1 ab/m2 # comment"/imm32
3609     68/push  _test-input-stream/imm32
3610     # . . call
3611     e8/call  write/disp32
3612     # . . discard args
3613     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3614     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3615     # . . push args
3616     68/push  _test-output-buffered-file/imm32
3617     68/push  _test-input-stream/imm32
3618     # . . call
3619     e8/call  convert-instruction/disp32
3620     # . . discard args
3621     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3622     # check output
3623     # . flush(_test-output-buffered-file)
3624     # . . push args
3625     68/push  _test-output-buffered-file/imm32
3626     # . . call
3627     e8/call  flush/disp32
3628     # . . discard args
3629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3630 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3656     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
3657     # . . push args
3658     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
3659     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
3660     68/push  _test-output-stream/imm32
3661     # . . call
3662     e8/call  check-stream-equal/disp32
3663     # . . discard args
3664     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3665     # . epilog
3666     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3667     5d/pop-to-EBP
3668     c3/return
3669 
3670 test-convert-instruction-handles-f2-opcode:
3671     # if the instruction starts with f2 opcode, include a second opcode
3672     # . prolog
3673     55/push-EBP
3674     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3675     # setup
3676     # . clear-stream(_test-input-stream)
3677     # . . push args
3678     68/push  _test-input-stream/imm32
3679     # . . call
3680     e8/call  clear-stream/disp32
3681     # . . discard args
3682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3683     # . clear-stream(_test-output-stream)
3684     # . . push args
3685     68/push  _test-output-stream/imm32
3686     # . . call
3687     e8/call  clear-stream/disp32
3688     # . . discard args
3689     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3690     # . clear-stream(_test-output-buffered-file+4)
3691     # . . push args
3692     b8/copy-to-EAX  _test-output-buffered-file/imm32
3693     05/add-to-EAX  4/imm32
3694     50/push-EAX
3695     # . . call
3696     e8/call  clear-stream/disp32
3697     # . . discard args
3698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3699     # initialize input
3700     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
3701     # . . push args
3702     68/push  "f2/m1 ab/m2 # comment"/imm32
3703     68/push  _test-input-stream/imm32
3704     # . . call
3705     e8/call  write/disp32
3706     # . . discard args
3707     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3708     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3709     # . . push args
3710     68/push  _test-output-buffered-file/imm32
3711     68/push  _test-input-stream/imm32
3712     # . . call
3713     e8/call  convert-instruction/disp32
3714     # . . discard args
3715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3716     # check output
3717     # . flush(_test-output-buffered-file)
3718     # . . push args
3719     68/push  _test-output-buffered-file/imm32
3720     # . . call
3721     e8/call  flush/disp32
3722     # . . discard args
3723     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3724 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3750     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
3751     # . . push args
3752     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
3753     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
3754     68/push  _test-output-stream/imm32
3755     # . . call
3756     e8/call  check-stream-equal/disp32
3757     # . . discard args
3758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3759     # . epilog
3760     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3761     5d/pop-to-EBP
3762     c3/return
3763 
3764 test-convert-instruction-handles-f3-opcode:
3765     # if the instruction starts with f3 opcode, include a second opcode
3766     # . prolog
3767     55/push-EBP
3768     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3769     # setup
3770     # . clear-stream(_test-input-stream)
3771     # . . push args
3772     68/push  _test-input-stream/imm32
3773     # . . call
3774     e8/call  clear-stream/disp32
3775     # . . discard args
3776     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3777     # . clear-stream(_test-output-stream)
3778     # . . push args
3779     68/push  _test-output-stream/imm32
3780     # . . call
3781     e8/call  clear-stream/disp32
3782     # . . discard args
3783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3784     # . clear-stream(_test-output-buffered-file+4)
3785     # . . push args
3786     b8/copy-to-EAX  _test-output-buffered-file/imm32
3787     05/add-to-EAX  4/imm32
3788     50/push-EAX
3789     # . . call
3790     e8/call  clear-stream/disp32
3791     # . . discard args
3792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3793     # initialize input
3794     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
3795     # . . push args
3796     68/push  "f3/m1 ab/m2 # comment"/imm32
3797     68/push  _test-input-stream/imm32
3798     # . . call
3799     e8/call  write/disp32
3800     # . . discard args
3801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3802     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3803     # . . push args
3804     68/push  _test-output-buffered-file/imm32
3805     68/push  _test-input-stream/imm32
3806     # . . call
3807     e8/call  convert-instruction/disp32
3808     # . . discard args
3809     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3810     # check output
3811     # . flush(_test-output-buffered-file)
3812     # . . push args
3813     68/push  _test-output-buffered-file/imm32
3814     # . . call
3815     e8/call  flush/disp32
3816     # . . discard args
3817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3818 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3844     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
3845     # . . push args
3846     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
3847     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
3848     68/push  _test-output-stream/imm32
3849     # . . call
3850     e8/call  check-stream-equal/disp32
3851     # . . discard args
3852     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3853     # . epilog
3854     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3855     5d/pop-to-EBP
3856     c3/return
3857 
3858 test-convert-instruction-handles-f2-0f-opcode:
3859     # if the instruction starts with f2 0f opcode, include a second opcode
3860     # . prolog
3861     55/push-EBP
3862     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3863     # setup
3864     # . clear-stream(_test-input-stream)
3865     # . . push args
3866     68/push  _test-input-stream/imm32
3867     # . . call
3868     e8/call  clear-stream/disp32
3869     # . . discard args
3870     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3871     # . clear-stream(_test-output-stream)
3872     # . . push args
3873     68/push  _test-output-stream/imm32
3874     # . . call
3875     e8/call  clear-stream/disp32
3876     # . . discard args
3877     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3878     # . clear-stream(_test-output-buffered-file+4)
3879     # . . push args
3880     b8/copy-to-EAX  _test-output-buffered-file/imm32
3881     05/add-to-EAX  4/imm32
3882     50/push-EAX
3883     # . . call
3884     e8/call  clear-stream/disp32
3885     # . . discard args
3886     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3887     # initialize input
3888     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
3889     # . . push args
3890     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
3891     68/push  _test-input-stream/imm32
3892     # . . call
3893     e8/call  write/disp32
3894     # . . discard args
3895     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3896     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3897     # . . push args
3898     68/push  _test-output-buffered-file/imm32
3899     68/push  _test-input-stream/imm32
3900     # . . call
3901     e8/call  convert-instruction/disp32
3902     # . . discard args
3903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3904     # check output
3905     # . flush(_test-output-buffered-file)
3906     # . . push args
3907     68/push  _test-output-buffered-file/imm32
3908     # . . call
3909     e8/call  flush/disp32
3910     # . . discard args
3911     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3912 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3938     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
3939     # . . push args
3940     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
3941     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
3942     68/push  _test-output-stream/imm32
3943     # . . call
3944     e8/call  check-stream-equal/disp32
3945     # . . discard args
3946     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3947     # . epilog
3948     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3949     5d/pop-to-EBP
3950     c3/return
3951 
3952 test-convert-instruction-handles-f3-0f-opcode:
3953     # if the instruction starts with f3 0f opcode, include a second opcode
3954     # . prolog
3955     55/push-EBP
3956     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3957     # setup
3958     # . clear-stream(_test-input-stream)
3959     # . . push args
3960     68/push  _test-input-stream/imm32
3961     # . . call
3962     e8/call  clear-stream/disp32
3963     # . . discard args
3964     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3965     # . clear-stream(_test-output-stream)
3966     # . . push args
3967     68/push  _test-output-stream/imm32
3968     # . . call
3969     e8/call  clear-stream/disp32
3970     # . . discard args
3971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3972     # . clear-stream(_test-output-buffered-file+4)
3973     # . . push args
3974     b8/copy-to-EAX  _test-output-buffered-file/imm32
3975     05/add-to-EAX  4/imm32
3976     50/push-EAX
3977     # . . call
3978     e8/call  clear-stream/disp32
3979     # . . discard args
3980     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3981     # initialize input
3982     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
3983     # . . push args
3984     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
3985     68/push  _test-input-stream/imm32
3986     # . . call
3987     e8/call  write/disp32
3988     # . . discard args
3989     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3990     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3991     # . . push args
3992     68/push  _test-output-buffered-file/imm32
3993     68/push  _test-input-stream/imm32
3994     # . . call
3995     e8/call  convert-instruction/disp32
3996     # . . discard args
3997     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3998     # check output
3999     # . flush(_test-output-buffered-file)
4000     # . . push args
4001     68/push  _test-output-buffered-file/imm32
4002     # . . call
4003     e8/call  flush/disp32
4004     # . . discard args
4005     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4006 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4032     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4033     # . . push args
4034     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
4035     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
4036     68/push  _test-output-stream/imm32
4037     # . . call
4038     e8/call  check-stream-equal/disp32
4039     # . . discard args
4040     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4041     # . epilog
4042     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4043     5d/pop-to-EBP
4044     c3/return
4045 
4046 test-convert-instruction-handles-unused-opcodes:
4047     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
4048     # . prolog
4049     55/push-EBP
4050     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4051     # setup
4052     # . clear-stream(_test-input-stream)
4053     # . . push args
4054     68/push  _test-input-stream/imm32
4055     # . . call
4056     e8/call  clear-stream/disp32
4057     # . . discard args
4058     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4059     # . clear-stream(_test-output-stream)
4060     # . . push args
4061     68/push  _test-output-stream/imm32
4062     # . . call
4063     e8/call  clear-stream/disp32
4064     # . . discard args
4065     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4066     # . clear-stream(_test-output-buffered-file+4)
4067     # . . push args
4068     b8/copy-to-EAX  _test-output-buffered-file/imm32
4069     05/add-to-EAX  4/imm32
4070     50/push-EAX
4071     # . . call
4072     e8/call  clear-stream/disp32
4073     # . . discard args
4074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4075     # initialize input
4076     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
4077     # . . push args
4078     68/push  "ab/m1 cd/m2 # comment"/imm32
4079     68/push  _test-input-stream/imm32
4080     # . . call
4081     e8/call  write/disp32
4082     # . . discard args
4083     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4084     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4085     # . . push args
4086     68/push  _test-output-buffered-file/imm32
4087     68/push  _test-input-stream/imm32
4088     # . . call
4089     e8/call  convert-instruction/disp32
4090     # . . discard args
4091     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4092     # check output
4093     # . flush(_test-output-buffered-file)
4094     # . . push args
4095     68/push  _test-output-buffered-file/imm32
4096     # . . call
4097     e8/call  flush/disp32
4098     # . . discard args
4099     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4100 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4126     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4127     # . . push args
4128     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
4129     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
4130     68/push  _test-output-stream/imm32
4131     # . . call
4132     e8/call  check-stream-equal/disp32
4133     # . . discard args
4134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4135     # . epilog
4136     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4137     5d/pop-to-EBP
4138     c3/return
4139 
4140 test-convert-instruction-handles-unused-second-opcodes:
4141     # if the second opcode isn't 0f, don't include further opcodes
4142     # . prolog
4143     55/push-EBP
4144     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4145     # setup
4146     # . clear-stream(_test-input-stream)
4147     # . . push args
4148     68/push  _test-input-stream/imm32
4149     # . . call
4150     e8/call  clear-stream/disp32
4151     # . . discard args
4152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4153     # . clear-stream(_test-output-stream)
4154     # . . push args
4155     68/push  _test-output-stream/imm32
4156     # . . call
4157     e8/call  clear-stream/disp32
4158     # . . discard args
4159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4160     # . clear-stream(_test-output-buffered-file+4)
4161     # . . push args
4162     b8/copy-to-EAX  _test-output-buffered-file/imm32
4163     05/add-to-EAX  4/imm32
4164     50/push-EAX
4165     # . . call
4166     e8/call  clear-stream/disp32
4167     # . . discard args
4168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4169     # initialize input
4170     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
4171     # . . push args
4172     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
4173     68/push  _test-input-stream/imm32
4174     # . . call
4175     e8/call  write/disp32
4176     # . . discard args
4177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4178     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4179     # . . push args
4180     68/push  _test-output-buffered-file/imm32
4181     68/push  _test-input-stream/imm32
4182     # . . call
4183     e8/call  convert-instruction/disp32
4184     # . . discard args
4185     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4186     # check output
4187     # . flush(_test-output-buffered-file)
4188     # . . push args
4189     68/push  _test-output-buffered-file/imm32
4190     # . . call
4191     e8/call  flush/disp32
4192     # . . discard args
4193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4194 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4220     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
4221     # . . push args
4222     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4223     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
4224     68/push  _test-output-stream/imm32
4225     # . . call
4226     e8/call  check-stream-equal/disp32
4227     # . . discard args
4228     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4229     # . epilog
4230     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4231     5d/pop-to-EBP
4232     c3/return
4233 
4234 test-convert-instruction-handles-unused-second-opcodes-2:
4235     # if the second opcode isn't 0f, don't include further opcodes
4236     # . prolog
4237     55/push-EBP
4238     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4239     # setup
4240     # . clear-stream(_test-input-stream)
4241     # . . push args
4242     68/push  _test-input-stream/imm32
4243     # . . call
4244     e8/call  clear-stream/disp32
4245     # . . discard args
4246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4247     # . clear-stream(_test-output-stream)
4248     # . . push args
4249     68/push  _test-output-stream/imm32
4250     # . . call
4251     e8/call  clear-stream/disp32
4252     # . . discard args
4253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4254     # . clear-stream(_test-output-buffered-file+4)
4255     # . . push args
4256     b8/copy-to-EAX  _test-output-buffered-file/imm32
4257     05/add-to-EAX  4/imm32
4258     50/push-EAX
4259     # . . call
4260     e8/call  clear-stream/disp32
4261     # . . discard args
4262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4263     # initialize input
4264     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
4265     # . . push args
4266     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
4267     68/push  _test-input-stream/imm32
4268     # . . call
4269     e8/call  write/disp32
4270     # . . discard args
4271     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4272     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4273     # . . push args
4274     68/push  _test-output-buffered-file/imm32
4275     68/push  _test-input-stream/imm32
4276     # . . call
4277     e8/call  convert-instruction/disp32
4278     # . . discard args
4279     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4280     # check output
4281     # . flush(_test-output-buffered-file)
4282     # . . push args
4283     68/push  _test-output-buffered-file/imm32
4284     # . . call
4285     e8/call  flush/disp32
4286     # . . discard args
4287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4288 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4314     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
4315     # . . push args
4316     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4317     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
4318     68/push  _test-output-stream/imm32
4319     # . . call
4320     e8/call  check-stream-equal/disp32
4321     # . . discard args
4322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4323     # . epilog
4324     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4325     5d/pop-to-EBP
4326     c3/return
4327 
4328 test-convert-instruction-emits-modrm-byte:
4329     # pack mod, rm32 and r32 operands into ModR/M byte
4330     # . prolog
4331     55/push-EBP
4332     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4333     # setup
4334     # . clear-stream(_test-input-stream)
4335     # . . push args
4336     68/push  _test-input-stream/imm32
4337     # . . call
4338     e8/call  clear-stream/disp32
4339     # . . discard args
4340     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4341     # . clear-stream(_test-output-stream)
4342     # . . push args
4343     68/push  _test-output-stream/imm32
4344     # . . call
4345     e8/call  clear-stream/disp32
4346     # . . discard args
4347     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4348     # . clear-stream(_test-output-buffered-file+4)
4349     # . . push args
4350     b8/copy-to-EAX  _test-output-buffered-file/imm32
4351     05/add-to-EAX  4/imm32
4352     50/push-EAX
4353     # . . call
4354     e8/call  clear-stream/disp32
4355     # . . discard args
4356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4357     # initialize input
4358     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
4359     # . . push args
4360     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
4361     68/push  _test-input-stream/imm32
4362     # . . call
4363     e8/call  write/disp32
4364     # . . discard args
4365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4366     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4367     # . . push args
4368     68/push  _test-output-buffered-file/imm32
4369     68/push  _test-input-stream/imm32
4370     # . . call
4371     e8/call  convert-instruction/disp32
4372     # . . discard args
4373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4374     # check output
4375     # . flush(_test-output-buffered-file)
4376     # . . push args
4377     68/push  _test-output-buffered-file/imm32
4378     # . . call
4379     e8/call  flush/disp32
4380     # . . discard args
4381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4382 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4408     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
4409     # . . push args
4410     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
4411     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
4412     68/push  _test-output-stream/imm32
4413     # . . call
4414     e8/call  check-stream-equal/disp32
4415     # . . discard args
4416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4417     # . epilog
4418     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4419     5d/pop-to-EBP
4420     c3/return
4421 
4422 test-convert-instruction-emits-modrm-byte-from-subop:
4423     # pack mod, rm32 and subop operands into ModR/M byte
4424     # . prolog
4425     55/push-EBP
4426     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4427     # setup
4428     # . clear-stream(_test-input-stream)
4429     # . . push args
4430     68/push  _test-input-stream/imm32
4431     # . . call
4432     e8/call  clear-stream/disp32
4433     # . . discard args
4434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4435     # . clear-stream(_test-output-stream)
4436     # . . push args
4437     68/push  _test-output-stream/imm32
4438     # . . call
4439     e8/call  clear-stream/disp32
4440     # . . discard args
4441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4442     # . clear-stream(_test-output-buffered-file+4)
4443     # . . push args
4444     b8/copy-to-EAX  _test-output-buffered-file/imm32
4445     05/add-to-EAX  4/imm32
4446     50/push-EAX
4447     # . . call
4448     e8/call  clear-stream/disp32
4449     # . . discard args
4450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4451     # initialize input
4452     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
4453     # . . push args
4454     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
4455     68/push  _test-input-stream/imm32
4456     # . . call
4457     e8/call  write/disp32
4458     # . . discard args
4459     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4460     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4461     # . . push args
4462     68/push  _test-output-buffered-file/imm32
4463     68/push  _test-input-stream/imm32
4464     # . . call
4465     e8/call  convert-instruction/disp32
4466     # . . discard args
4467     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4468     # check output
4469     # . flush(_test-output-buffered-file)
4470     # . . push args
4471     68/push  _test-output-buffered-file/imm32
4472     # . . call
4473     e8/call  flush/disp32
4474     # . . discard args
4475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4476 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4502     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
4503     # . . push args
4504     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
4505     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
4506     68/push  _test-output-stream/imm32
4507     # . . call
4508     e8/call  check-stream-equal/disp32
4509     # . . discard args
4510     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4511     # . epilog
4512     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4513     5d/pop-to-EBP
4514     c3/return
4515 
4516 test-convert-instruction-emits-modrm-byte-with-missing-mod:
4517     # pack rm32 and r32 operands into ModR/M byte
4518     # . prolog
4519     55/push-EBP
4520     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4521     # setup
4522     # . clear-stream(_test-input-stream)
4523     # . . push args
4524     68/push  _test-input-stream/imm32
4525     # . . call
4526     e8/call  clear-stream/disp32
4527     # . . discard args
4528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4529     # . clear-stream(_test-output-stream)
4530     # . . push args
4531     68/push  _test-output-stream/imm32
4532     # . . call
4533     e8/call  clear-stream/disp32
4534     # . . discard args
4535     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4536     # . clear-stream(_test-output-buffered-file+4)
4537     # . . push args
4538     b8/copy-to-EAX  _test-output-buffered-file/imm32
4539     05/add-to-EAX  4/imm32
4540     50/push-EAX
4541     # . . call
4542     e8/call  clear-stream/disp32
4543     # . . discard args
4544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4545     # initialize input
4546     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
4547     # . . push args
4548     68/push  "8b/copy 0/rm32 1/r32"/imm32
4549     68/push  _test-input-stream/imm32
4550     # . . call
4551     e8/call  write/disp32
4552     # . . discard args
4553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4554     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4555     # . . push args
4556     68/push  _test-output-buffered-file/imm32
4557     68/push  _test-input-stream/imm32
4558     # . . call
4559     e8/call  convert-instruction/disp32
4560     # . . discard args
4561     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4562     # check output
4563     # . flush(_test-output-buffered-file)
4564     # . . push args
4565     68/push  _test-output-buffered-file/imm32
4566     # . . call
4567     e8/call  flush/disp32
4568     # . . discard args
4569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4570 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4596     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
4597     # . . push args
4598     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
4599     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
4600     68/push  _test-output-stream/imm32
4601     # . . call
4602     e8/call  check-stream-equal/disp32
4603     # . . discard args
4604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4605     # . epilog
4606     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4607     5d/pop-to-EBP
4608     c3/return
4609 
4610 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
4611     # pack mod and r32 operands into ModR/M byte
4612     # . prolog
4613     55/push-EBP
4614     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4615     # setup
4616     # . clear-stream(_test-input-stream)
4617     # . . push args
4618     68/push  _test-input-stream/imm32
4619     # . . call
4620     e8/call  clear-stream/disp32
4621     # . . discard args
4622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4623     # . clear-stream(_test-output-stream)
4624     # . . push args
4625     68/push  _test-output-stream/imm32
4626     # . . call
4627     e8/call  clear-stream/disp32
4628     # . . discard args
4629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4630     # . clear-stream(_test-output-buffered-file+4)
4631     # . . push args
4632     b8/copy-to-EAX  _test-output-buffered-file/imm32
4633     05/add-to-EAX  4/imm32
4634     50/push-EAX
4635     # . . call
4636     e8/call  clear-stream/disp32
4637     # . . discard args
4638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4639     # initialize input
4640     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
4641     # . . push args
4642     68/push  "8b/copy 0/mod 1/r32"/imm32
4643     68/push  _test-input-stream/imm32
4644     # . . call
4645     e8/call  write/disp32
4646     # . . discard args
4647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4648     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4649     # . . push args
4650     68/push  _test-output-buffered-file/imm32
4651     68/push  _test-input-stream/imm32
4652     # . . call
4653     e8/call  convert-instruction/disp32
4654     # . . discard args
4655     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4656     # check output
4657     # . flush(_test-output-buffered-file)
4658     # . . push args
4659     68/push  _test-output-buffered-file/imm32
4660     # . . call
4661     e8/call  flush/disp32
4662     # . . discard args
4663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4664 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4690     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
4691     # . . push args
4692     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
4693     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
4694     68/push  _test-output-stream/imm32
4695     # . . call
4696     e8/call  check-stream-equal/disp32
4697     # . . discard args
4698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4699     # . epilog
4700     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4701     5d/pop-to-EBP
4702     c3/return
4703 
4704 test-convert-instruction-emits-modrm-byte-with-missing-r32:
4705     # pack mod and rm32 operands into ModR/M byte
4706     # . prolog
4707     55/push-EBP
4708     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4709     # setup
4710     # . clear-stream(_test-input-stream)
4711     # . . push args
4712     68/push  _test-input-stream/imm32
4713     # . . call
4714     e8/call  clear-stream/disp32
4715     # . . discard args
4716     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4717     # . clear-stream(_test-output-stream)
4718     # . . push args
4719     68/push  _test-output-stream/imm32
4720     # . . call
4721     e8/call  clear-stream/disp32
4722     # . . discard args
4723     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4724     # . clear-stream(_test-output-buffered-file+4)
4725     # . . push args
4726     b8/copy-to-EAX  _test-output-buffered-file/imm32
4727     05/add-to-EAX  4/imm32
4728     50/push-EAX
4729     # . . call
4730     e8/call  clear-stream/disp32
4731     # . . discard args
4732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4733     # initialize input
4734     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
4735     # . . push args
4736     68/push  "8b/copy 0/mod 0/rm32"/imm32
4737     68/push  _test-input-stream/imm32
4738     # . . call
4739     e8/call  write/disp32
4740     # . . discard args
4741     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4742     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4743     # . . push args
4744     68/push  _test-output-buffered-file/imm32
4745     68/push  _test-input-stream/imm32
4746     # . . call
4747     e8/call  convert-instruction/disp32
4748     # . . discard args
4749     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4750     # check output
4751     # . flush(_test-output-buffered-file)
4752     # . . push args
4753     68/push  _test-output-buffered-file/imm32
4754     # . . call
4755     e8/call  flush/disp32
4756     # . . discard args
4757     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4758 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4784     # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
4785     # . . push args
4786     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
4787     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
4788     68/push  _test-output-stream/imm32
4789     # . . call
4790     e8/call  check-stream-equal/disp32
4791     # . . discard args
4792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4793     # . epilog
4794     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4795     5d/pop-to-EBP
4796     c3/return
4797 
4798 test-convert-instruction-emits-sib-byte:
4799     # pack base, index and scale operands into SIB byte
4800     # . prolog
4801     55/push-EBP
4802     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4803     # setup
4804     # . clear-stream(_test-input-stream)
4805     # . . push args
4806     68/push  _test-input-stream/imm32
4807     # . . call
4808     e8/call  clear-stream/disp32
4809     # . . discard args
4810     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4811     # . clear-stream(_test-output-stream)
4812     # . . push args
4813     68/push  _test-output-stream/imm32
4814     # . . call
4815     e8/call  clear-stream/disp32
4816     # . . discard args
4817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4818     # . clear-stream(_test-output-buffered-file+4)
4819     # . . push args
4820     b8/copy-to-EAX  _test-output-buffered-file/imm32
4821     05/add-to-EAX  4/imm32
4822     50/push-EAX
4823     # . . call
4824     e8/call  clear-stream/disp32
4825     # . . discard args
4826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4827     # initialize input
4828     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
4829     # . . push args
4830     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
4831     68/push  _test-input-stream/imm32
4832     # . . call
4833     e8/call  write/disp32
4834     # . . discard args
4835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4836     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4837     # . . push args
4838     68/push  _test-output-buffered-file/imm32
4839     68/push  _test-input-stream/imm32
4840     # . . call
4841     e8/call  convert-instruction/disp32
4842     # . . discard args
4843     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4844     # check output
4845     # . flush(_test-output-buffered-file)
4846     # . . push args
4847     68/push  _test-output-buffered-file/imm32
4848     # . . call
4849     e8/call  flush/disp32
4850     # . . discard args
4851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4852 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4878     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
4879     # . . push args
4880     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
4881     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
4882     68/push  _test-output-stream/imm32
4883     # . . call
4884     e8/call  check-stream-equal/disp32
4885     # . . discard args
4886     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4887     # . epilog
4888     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4889     5d/pop-to-EBP
4890     c3/return
4891 
4892 test-convert-instruction-emits-sib-byte-with-missing-base:
4893     # pack index and scale operands into SIB byte
4894     # . prolog
4895     55/push-EBP
4896     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4897     # setup
4898     # . clear-stream(_test-input-stream)
4899     # . . push args
4900     68/push  _test-input-stream/imm32
4901     # . . call
4902     e8/call  clear-stream/disp32
4903     # . . discard args
4904     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4905     # . clear-stream(_test-output-stream)
4906     # . . push args
4907     68/push  _test-output-stream/imm32
4908     # . . call
4909     e8/call  clear-stream/disp32
4910     # . . discard args
4911     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4912     # . clear-stream(_test-output-buffered-file+4)
4913     # . . push args
4914     b8/copy-to-EAX  _test-output-buffered-file/imm32
4915     05/add-to-EAX  4/imm32
4916     50/push-EAX
4917     # . . call
4918     e8/call  clear-stream/disp32
4919     # . . discard args
4920     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4921     # initialize input
4922     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
4923     # . . push args
4924     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
4925     68/push  _test-input-stream/imm32
4926     # . . call
4927     e8/call  write/disp32
4928     # . . discard args
4929     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4930     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4931     # . . push args
4932     68/push  _test-output-buffered-file/imm32
4933     68/push  _test-input-stream/imm32
4934     # . . call
4935     e8/call  convert-instruction/disp32
4936     # . . discard args
4937     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4938     # check output
4939     # . flush(_test-output-buffered-file)
4940     # . . push args
4941     68/push  _test-output-buffered-file/imm32
4942     # . . call
4943     e8/call  flush/disp32
4944     # . . discard args
4945     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4946 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4972     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
4973     # . . push args
4974     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
4975     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
4976     68/push  _test-output-stream/imm32
4977     # . . call
4978     e8/call  check-stream-equal/disp32
4979     # . . discard args
4980     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4981     # . epilog
4982     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4983     5d/pop-to-EBP
4984     c3/return
4985 
4986 test-convert-instruction-emits-sib-byte-with-missing-index:
4987     # pack base and scale operands into SIB byte
4988     # . prolog
4989     55/push-EBP
4990     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4991     # setup
4992     # . clear-stream(_test-input-stream)
4993     # . . push args
4994     68/push  _test-input-stream/imm32
4995     # . . call
4996     e8/call  clear-stream/disp32
4997     # . . discard args
4998     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4999     # . clear-stream(_test-output-stream)
5000     # . . push args
5001     68/push  _test-output-stream/imm32
5002     # . . call
5003     e8/call  clear-stream/disp32
5004     # . . discard args
5005     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5006     # . clear-stream(_test-output-buffered-file+4)
5007     # . . push args
5008     b8/copy-to-EAX  _test-output-buffered-file/imm32
5009     05/add-to-EAX  4/imm32
5010     50/push-EAX
5011     # . . call
5012     e8/call  clear-stream/disp32
5013     # . . discard args
5014     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5015     # initialize input
5016     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
5017     # . . push args
5018     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5019     68/push  _test-input-stream/imm32
5020     # . . call
5021     e8/call  write/disp32
5022     # . . discard args
5023     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5024     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5025     # . . push args
5026     68/push  _test-output-buffered-file/imm32
5027     68/push  _test-input-stream/imm32
5028     # . . call
5029     e8/call  convert-instruction/disp32
5030     # . . discard args
5031     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5032     # check output
5033     # . flush(_test-output-buffered-file)
5034     # . . push args
5035     68/push  _test-output-buffered-file/imm32
5036     # . . call
5037     e8/call  flush/disp32
5038     # . . discard args
5039     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5040 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5066     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
5067     # . . push args
5068     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
5069     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5070     68/push  _test-output-stream/imm32
5071     # . . call
5072     e8/call  check-stream-equal/disp32
5073     # . . discard args
5074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5075     # . epilog
5076     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5077     5d/pop-to-EBP
5078     c3/return
5079 
5080 test-convert-instruction-emits-sib-byte-with-missing-scale:
5081     # pack base and index operands into SIB byte
5082     # . prolog
5083     55/push-EBP
5084     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5085     # setup
5086     # . clear-stream(_test-input-stream)
5087     # . . push args
5088     68/push  _test-input-stream/imm32
5089     # . . call
5090     e8/call  clear-stream/disp32
5091     # . . discard args
5092     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5093     # . clear-stream(_test-output-stream)
5094     # . . push args
5095     68/push  _test-output-stream/imm32
5096     # . . call
5097     e8/call  clear-stream/disp32
5098     # . . discard args
5099     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5100     # . clear-stream(_test-output-buffered-file+4)
5101     # . . push args
5102     b8/copy-to-EAX  _test-output-buffered-file/imm32
5103     05/add-to-EAX  4/imm32
5104     50/push-EAX
5105     # . . call
5106     e8/call  clear-stream/disp32
5107     # . . discard args
5108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5109     # initialize input
5110     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
5111     # . . push args
5112     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5113     68/push  _test-input-stream/imm32
5114     # . . call
5115     e8/call  write/disp32
5116     # . . discard args
5117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5118     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5119     # . . push args
5120     68/push  _test-output-buffered-file/imm32
5121     68/push  _test-input-stream/imm32
5122     # . . call
5123     e8/call  convert-instruction/disp32
5124     # . . discard args
5125     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5126     # check output
5127     # . flush(_test-output-buffered-file)
5128     # . . push args
5129     68/push  _test-output-buffered-file/imm32
5130     # . . call
5131     e8/call  flush/disp32
5132     # . . discard args
5133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5134 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5160     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
5161     # . . push args
5162     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
5163     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5164     68/push  _test-output-stream/imm32
5165     # . . call
5166     e8/call  check-stream-equal/disp32
5167     # . . discard args
5168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5169     # . epilog
5170     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5171     5d/pop-to-EBP
5172     c3/return
5173 
5174 test-convert-instruction-handles-disp32-operand:
5175     # expand /disp32 operand into 4 bytes
5176     # . prolog
5177     55/push-EBP
5178     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5179     # setup
5180     # . clear-stream(_test-input-stream)
5181     # . . push args
5182     68/push  _test-input-stream/imm32
5183     # . . call
5184     e8/call  clear-stream/disp32
5185     # . . discard args
5186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5187     # . clear-stream(_test-output-stream)
5188     # . . push args
5189     68/push  _test-output-stream/imm32
5190     # . . call
5191     e8/call  clear-stream/disp32
5192     # . . discard args
5193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5194     # . clear-stream(_test-output-buffered-file+4)
5195     # . . push args
5196     b8/copy-to-EAX  _test-output-buffered-file/imm32
5197     05/add-to-EAX  4/imm32
5198     50/push-EAX
5199     # . . call
5200     e8/call  clear-stream/disp32
5201     # . . discard args
5202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5203     # initialize input
5204     # . write(_test-input-stream, "e8/call 20/disp32")
5205     # . . push args
5206     68/push  "e8/call 20/disp32"/imm32
5207     68/push  _test-input-stream/imm32
5208     # . . call
5209     e8/call  write/disp32
5210     # . . discard args
5211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5212     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5213     # . . push args
5214     68/push  _test-output-buffered-file/imm32
5215     68/push  _test-input-stream/imm32
5216     # . . call
5217     e8/call  convert-instruction/disp32
5218     # . . discard args
5219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5220     # check output
5221     # . flush(_test-output-buffered-file)
5222     # . . push args
5223     68/push  _test-output-buffered-file/imm32
5224     # . . call
5225     e8/call  flush/disp32
5226     # . . discard args
5227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5228 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5254     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
5255     # . . push args
5256     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
5257     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
5258     68/push  _test-output-stream/imm32
5259     # . . call
5260     e8/call  check-stream-equal/disp32
5261     # . . discard args
5262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5263     # . epilog
5264     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5265     5d/pop-to-EBP
5266     c3/return
5267 
5268 test-convert-instruction-handles-disp16-operand:
5269     # expand /disp16 operand into 2 bytes
5270     # . prolog
5271     55/push-EBP
5272     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5273     # setup
5274     # . clear-stream(_test-input-stream)
5275     # . . push args
5276     68/push  _test-input-stream/imm32
5277     # . . call
5278     e8/call  clear-stream/disp32
5279     # . . discard args
5280     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5281     # . clear-stream(_test-output-stream)
5282     # . . push args
5283     68/push  _test-output-stream/imm32
5284     # . . call
5285     e8/call  clear-stream/disp32
5286     # . . discard args
5287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5288     # . clear-stream(_test-output-buffered-file+4)
5289     # . . push args
5290     b8/copy-to-EAX  _test-output-buffered-file/imm32
5291     05/add-to-EAX  4/imm32
5292     50/push-EAX
5293     # . . call
5294     e8/call  clear-stream/disp32
5295     # . . discard args
5296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5297     # initialize input
5298     # . write(_test-input-stream, "e8/call 20/disp16")
5299     # . . push args
5300     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
5301     68/push  _test-input-stream/imm32
5302     # . . call
5303     e8/call  write/disp32
5304     # . . discard args
5305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5306     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5307     # . . push args
5308     68/push  _test-output-buffered-file/imm32
5309     68/push  _test-input-stream/imm32
5310     # . . call
5311     e8/call  convert-instruction/disp32
5312     # . . discard args
5313     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5314     # check output
5315     # . flush(_test-output-buffered-file)
5316     # . . push args
5317     68/push  _test-output-buffered-file/imm32
5318     # . . call
5319     e8/call  flush/disp32
5320     # . . discard args
5321     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5322 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5348     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
5349     # . . push args
5350     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
5351     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
5352     68/push  _test-output-stream/imm32
5353     # . . call
5354     e8/call  check-stream-equal/disp32
5355     # . . discard args
5356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5357     # . epilog
5358     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5359     5d/pop-to-EBP
5360     c3/return
5361 
5362 test-convert-instruction-handles-disp8-operand:
5363     # expand /disp8 operand into 1 byte
5364     # . prolog
5365     55/push-EBP
5366     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5367     # setup
5368     # . clear-stream(_test-input-stream)
5369     # . . push args
5370     68/push  _test-input-stream/imm32
5371     # . . call
5372     e8/call  clear-stream/disp32
5373     # . . discard args
5374     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5375     # . clear-stream(_test-output-stream)
5376     # . . push args
5377     68/push  _test-output-stream/imm32
5378     # . . call
5379     e8/call  clear-stream/disp32
5380     # . . discard args
5381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5382     # . clear-stream(_test-output-buffered-file+4)
5383     # . . push args
5384     b8/copy-to-EAX  _test-output-buffered-file/imm32
5385     05/add-to-EAX  4/imm32
5386     50/push-EAX
5387     # . . call
5388     e8/call  clear-stream/disp32
5389     # . . discard args
5390     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5391     # initialize input
5392     # . write(_test-input-stream, "eb/jump 20/disp8")
5393     # . . push args
5394     68/push  "eb/jump 20/disp8"/imm32
5395     68/push  _test-input-stream/imm32
5396     # . . call
5397     e8/call  write/disp32
5398     # . . discard args
5399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5400     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5401     # . . push args
5402     68/push  _test-output-buffered-file/imm32
5403     68/push  _test-input-stream/imm32
5404     # . . call
5405     e8/call  convert-instruction/disp32
5406     # . . discard args
5407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5408     # check output
5409     # . flush(_test-output-buffered-file)
5410     # . . push args
5411     68/push  _test-output-buffered-file/imm32
5412     # . . call
5413     e8/call  flush/disp32
5414     # . . discard args
5415     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5416 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5442     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
5443     # . . push args
5444     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
5445     68/push  "eb 20  # eb/jump 20/disp8"/imm32
5446     68/push  _test-output-stream/imm32
5447     # . . call
5448     e8/call  check-stream-equal/disp32
5449     # . . discard args
5450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5451     # . epilog
5452     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5453     5d/pop-to-EBP
5454     c3/return
5455 
5456 test-convert-instruction-handles-disp8-name:
5457     # pass /disp8 name directly through
5458     # . prolog
5459     55/push-EBP
5460     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5461     # setup
5462     # . clear-stream(_test-input-stream)
5463     # . . push args
5464     68/push  _test-input-stream/imm32
5465     # . . call
5466     e8/call  clear-stream/disp32
5467     # . . discard args
5468     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5469     # . clear-stream(_test-output-stream)
5470     # . . push args
5471     68/push  _test-output-stream/imm32
5472     # . . call
5473     e8/call  clear-stream/disp32
5474     # . . discard args
5475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5476     # . clear-stream(_test-output-buffered-file+4)
5477     # . . push args
5478     b8/copy-to-EAX  _test-output-buffered-file/imm32
5479     05/add-to-EAX  4/imm32
5480     50/push-EAX
5481     # . . call
5482     e8/call  clear-stream/disp32
5483     # . . discard args
5484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5485     # initialize input
5486     # . write(_test-input-stream, "eb/jump xyz/disp8")
5487     # . . push args
5488     68/push  "eb/jump xyz/disp8"/imm32
5489     68/push  _test-input-stream/imm32
5490     # . . call
5491     e8/call  write/disp32
5492     # . . discard args
5493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5494     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5495     # . . push args
5496     68/push  _test-output-buffered-file/imm32
5497     68/push  _test-input-stream/imm32
5498     # . . call
5499     e8/call  convert-instruction/disp32
5500     # . . discard args
5501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5502     # check output
5503     # . flush(_test-output-buffered-file)
5504     # . . push args
5505     68/push  _test-output-buffered-file/imm32
5506     # . . call
5507     e8/call  flush/disp32
5508     # . . discard args
5509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5510 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5536     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
5537     # . . push args
5538     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
5539     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
5540     68/push  _test-output-stream/imm32
5541     # . . call
5542     e8/call  check-stream-equal/disp32
5543     # . . discard args
5544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5545     # . epilog
5546     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5547     5d/pop-to-EBP
5548     c3/return
5549 
5550 test-convert-instruction-handles-imm32-operand:
5551     # expand /imm32 operand into 4 bytes
5552     # . prolog
5553     55/push-EBP
5554     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5555     # setup
5556     # . clear-stream(_test-input-stream)
5557     # . . push args
5558     68/push  _test-input-stream/imm32
5559     # . . call
5560     e8/call  clear-stream/disp32
5561     # . . discard args
5562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5563     # . clear-stream(_test-output-stream)
5564     # . . push args
5565     68/push  _test-output-stream/imm32
5566     # . . call
5567     e8/call  clear-stream/disp32
5568     # . . discard args
5569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5570     # . clear-stream(_test-output-buffered-file+4)
5571     # . . push args
5572     b8/copy-to-EAX  _test-output-buffered-file/imm32
5573     05/add-to-EAX  4/imm32
5574     50/push-EAX
5575     # . . call
5576     e8/call  clear-stream/disp32
5577     # . . discard args
5578     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5579     # initialize input
5580     # . write(_test-input-stream, "68/push 0x20/imm32")
5581     # . . push args
5582     68/push  "68/push 0x20/imm32"/imm32
5583     68/push  _test-input-stream/imm32
5584     # . . call
5585     e8/call  write/disp32
5586     # . . discard args
5587     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5588     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5589     # . . push args
5590     68/push  _test-output-buffered-file/imm32
5591     68/push  _test-input-stream/imm32
5592     # . . call
5593     e8/call  convert-instruction/disp32
5594     # . . discard args
5595     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5596     # check output
5597     # . flush(_test-output-buffered-file)
5598     # . . push args
5599     68/push  _test-output-buffered-file/imm32
5600     # . . call
5601     e8/call  flush/disp32
5602     # . . discard args
5603     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5604 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5630     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
5631     # . . push args
5632     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
5633     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
5634     68/push  _test-output-stream/imm32
5635     # . . call
5636     e8/call  check-stream-equal/disp32
5637     # . . discard args
5638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5639     # . epilog
5640     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5641     5d/pop-to-EBP
5642     c3/return
5643 
5644 test-convert-instruction-handles-imm16-operand:
5645     # expand /imm16 operand into 2 bytes
5646     # we don't have one of these at the moment, so this expands to an invalid instruction
5647     # . prolog
5648     55/push-EBP
5649     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5650     # setup
5651     # . clear-stream(_test-input-stream)
5652     # . . push args
5653     68/push  _test-input-stream/imm32
5654     # . . call
5655     e8/call  clear-stream/disp32
5656     # . . discard args
5657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5658     # . clear-stream(_test-output-stream)
5659     # . . push args
5660     68/push  _test-output-stream/imm32
5661     # . . call
5662     e8/call  clear-stream/disp32
5663     # . . discard args
5664     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5665     # . clear-stream(_test-output-buffered-file+4)
5666     # . . push args
5667     b8/copy-to-EAX  _test-output-buffered-file/imm32
5668     05/add-to-EAX  4/imm32
5669     50/push-EAX
5670     # . . call
5671     e8/call  clear-stream/disp32
5672     # . . discard args
5673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5674     # initialize input
5675     # . write(_test-input-stream, "68/push 0x20/imm16")
5676     # . . push args
5677     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
5678     68/push  _test-input-stream/imm32
5679     # . . call
5680     e8/call  write/disp32
5681     # . . discard args
5682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5683     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5684     # . . push args
5685     68/push  _test-output-buffered-file/imm32
5686     68/push  _test-input-stream/imm32
5687     # . . call
5688     e8/call  convert-instruction/disp32
5689     # . . discard args
5690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5691     # check output
5692     # . flush(_test-output-buffered-file)
5693     # . . push args
5694     68/push  _test-output-buffered-file/imm32
5695     # . . call
5696     e8/call  flush/disp32
5697     # . . discard args
5698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5699 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5725     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
5726     # . . push args
5727     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
5728     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
5729     68/push  _test-output-stream/imm32
5730     # . . call
5731     e8/call  check-stream-equal/disp32
5732     # . . discard args
5733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5734     # . epilog
5735     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5736     5d/pop-to-EBP
5737     c3/return
5738 
5739 test-convert-instruction-handles-imm8-operand:
5740     # expand /imm8 operand into 1 byte
5741     # we don't have one of these at the moment, so this expands to an invalid instruction
5742     # . prolog
5743     55/push-EBP
5744     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5745     # setup
5746     # . clear-stream(_test-input-stream)
5747     # . . push args
5748     68/push  _test-input-stream/imm32
5749     # . . call
5750     e8/call  clear-stream/disp32
5751     # . . discard args
5752     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5753     # . clear-stream(_test-output-stream)
5754     # . . push args
5755     68/push  _test-output-stream/imm32
5756     # . . call
5757     e8/call  clear-stream/disp32
5758     # . . discard args
5759     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5760     # . clear-stream(_test-output-buffered-file+4)
5761     # . . push args
5762     b8/copy-to-EAX  _test-output-buffered-file/imm32
5763     05/add-to-EAX  4/imm32
5764     50/push-EAX
5765     # . . call
5766     e8/call  clear-stream/disp32
5767     # . . discard args
5768     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5769     # initialize input
5770     # . write(_test-input-stream, "68/push 0x20/imm8")
5771     # . . push args
5772     68/push  "68/push 0x20/imm8"/imm32
5773     68/push  _test-input-stream/imm32
5774     # . . call
5775     e8/call  write/disp32
5776     # . . discard args
5777     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5778     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5779     # . . push args
5780     68/push  _test-output-buffered-file/imm32
5781     68/push  _test-input-stream/imm32
5782     # . . call
5783     e8/call  convert-instruction/disp32
5784     # . . discard args
5785     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5786     # check output
5787     # . flush(_test-output-buffered-file)
5788     # . . push args
5789     68/push  _test-output-buffered-file/imm32
5790     # . . call
5791     e8/call  flush/disp32
5792     # . . discard args
5793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5794 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5820     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
5821     # . . push args
5822     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
5823     68/push  "68 20  # 68/push 0x20/imm8"/imm32
5824     68/push  _test-output-stream/imm32
5825     # . . call
5826     e8/call  check-stream-equal/disp32
5827     # . . discard args
5828     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5829     # . epilog
5830     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5831     5d/pop-to-EBP
5832     c3/return
5833 
5834 # (re)compute the bounds of the next word in the line
5835 # return empty string on reaching end of file
5836 next-word:  # line : (address stream byte), out : (address slice)
5837     # . prolog
5838     55/push-EBP
5839     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5840     # . save registers
5841     50/push-EAX
5842     51/push-ECX
5843     56/push-ESI
5844     57/push-EDI
5845     # ESI = line
5846     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
5847     # EDI = out
5848     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
5849     # skip-chars-matching(line, ' ')
5850     # . . push args
5851     68/push  0x20/imm32/space
5852     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
5853     # . . call
5854     e8/call  skip-chars-matching/disp32
5855     # . . discard args
5856     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5857 $next-word:check0:
5858     # if (line->read >= line->write) clear out and return
5859     # . EAX = line->read
5860     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
5861     # . if (EAX < line->write) goto next check
5862     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
5863     7c/jump-if-lesser  $next-word:check-for-comment/disp8
5864     # . return out = {0, 0}
5865     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
5866     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
5867     eb/jump  $next-word:end/disp8
5868 $next-word:check-for-comment:
5869     # out->start = &line->data[line->read]
5870     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
5871     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
5872     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
5873     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
5874     # . EAX = line->data[line->read]
5875     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
5876     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(ESI+ECX+12) to AL
5877     # . compare
5878     3d/compare-EAX-and  0x23/imm32/pound
5879     75/jump-if-not-equal  $next-word:regular-word/disp8
5880 $next-word:comment:
5881     # . out->end = &line->data[line->write]
5882     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
5883     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
5884     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
5885     # . line->read = line->write
5886     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
5887     # . return
5888     eb/jump  $next-word:end/disp8
5889 $next-word:regular-word:
5890     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
5891     # . . push args
5892     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
5893     # . . call
5894     e8/call  skip-chars-not-matching-whitespace/disp32
5895     # . . discard args
5896     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5897     # out->end = &line->data[line->read]
5898     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
5899     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           0/r32/EAX   0xc/disp8       .                 # copy ESI+ECX+12 to EAX
5900     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
5901 $next-word:end:
5902     # . restore registers
5903     5f/pop-to-EDI
5904     5e/pop-to-ESI
5905     59/pop-to-ECX
5906     58/pop-to-EAX
5907     # . epilog
5908     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5909     5d/pop-to-EBP
5910     c3/return
5911 
5912 test-next-word:
5913     # . prolog
5914     55/push-EBP
5915     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5916     # setup
5917     # . clear-stream(_test-stream)
5918     # . . push args
5919     68/push  _test-stream/imm32
5920     # . . call
5921     e8/call  clear-stream/disp32
5922     # . . discard args
5923     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5924     # var slice/ECX = {0, 0}
5925     68/push  0/imm32/end
5926     68/push  0/imm32/start
5927     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
5928     # write(_test-stream, "  ab")
5929     # . . push args
5930     68/push  "  ab"/imm32
5931     68/push  _test-stream/imm32
5932     # . . call
5933     e8/call  write/disp32
5934     # . . discard args
5935     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5936     # next-word(_test-stream, slice)
5937     # . . push args
5938     51/push-ECX
5939     68/push  _test-stream/imm32
5940     # . . call
5941     e8/call  next-word/disp32
5942     # . . discard args
5943     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5944     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
5945     # . check-ints-equal(slice->start - _test-stream, 14, msg)
5946     # . . push args
5947     68/push  "F - test-next-word: start"/imm32
5948     68/push  0xe/imm32
5949     # . . push slice->start - _test-stream
5950     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
5951     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
5952     50/push-EAX
5953     # . . call
5954     e8/call  check-ints-equal/disp32
5955     # . . discard args
5956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5957     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
5958     # . check-ints-equal(slice->end - _test-stream, 16, msg)
5959     # . . push args
5960     68/push  "F - test-next-word: end"/imm32
5961     68/push  0x10/imm32
5962     # . . push slice->end - _test-stream
5963     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
5964     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
5965     50/push-EAX
5966     # . . call
5967     e8/call  check-ints-equal/disp32
5968     # . . discard args
5969     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5970     # . epilog
5971     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5972     5d/pop-to-EBP
5973     c3/return
5974 
5975 test-next-word-returns-whole-comment:
5976     # . prolog
5977     55/push-EBP
5978     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5979     # setup
5980     # . clear-stream(_test-stream)
5981     # . . push args
5982     68/push  _test-stream/imm32
5983     # . . call
5984     e8/call  clear-stream/disp32
5985     # . . discard args
5986     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5987     # var slice/ECX = {0, 0}
5988     68/push  0/imm32/end
5989     68/push  0/imm32/start
5990     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
5991     # write(_test-stream, "  # a")
5992     # . . push args
5993     68/push  "  # a"/imm32
5994     68/push  _test-stream/imm32
5995     # . . call
5996     e8/call  write/disp32
5997     # . . discard args
5998     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5999     # next-word(_test-stream, slice)
6000     # . . push args
6001     51/push-ECX
6002     68/push  _test-stream/imm32
6003     # . . call
6004     e8/call  next-word/disp32
6005     # . . discard args
6006     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6007     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
6008     # . check-ints-equal(slice->start - _test-stream, 14, msg)
6009     # . . push args
6010     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
6011     68/push  0xe/imm32
6012     # . . push slice->start - _test-stream
6013     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
6014     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
6015     50/push-EAX
6016     # . . call
6017     e8/call  check-ints-equal/disp32
6018     # . . discard args
6019     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6020     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
6021     # . check-ints-equal(slice->end - _test-stream, 17, msg)
6022     # . . push args
6023     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
6024     68/push  0x11/imm32
6025     # . . push slice->end - _test-stream
6026     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
6027     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
6028     50/push-EAX
6029     # . . call
6030     e8/call  check-ints-equal/disp32
6031     # . . discard args
6032     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6033     # . epilog
6034     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6035     5d/pop-to-EBP
6036     c3/return
6037 
6038 test-next-word-returns-empty-string-on-eof:
6039     # . prolog
6040     55/push-EBP
6041     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6042     # setup
6043     # . clear-stream(_test-stream)
6044     # . . push args
6045     68/push  _test-stream/imm32
6046     # . . call
6047     e8/call  clear-stream/disp32
6048     # . . discard args
6049     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6050     # var slice/ECX = {0, 0}
6051     68/push  0/imm32/end
6052     68/push  0/imm32/start
6053     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6054     # write nothing to _test-stream
6055     # next-word(_test-stream, slice)
6056     # . . push args
6057     51/push-ECX
6058     68/push  _test-stream/imm32
6059     # . . call
6060     e8/call  next-word/disp32
6061     # . . discard args
6062     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6063     # check-ints-equal(slice->end - slice->start, 0, msg)
6064     # . . push args
6065     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
6066     68/push  0/imm32
6067     # . . push slice->end - slice->start
6068     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
6069     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
6070     50/push-EAX
6071     # . . call
6072     e8/call  check-ints-equal/disp32
6073     # . . discard args
6074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6075     # . epilog
6076     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6077     5d/pop-to-EBP
6078     c3/return
6079 
6080 has-metadata?:  # word : (address slice), s : (address string) -> EAX : boolean
6081     # pseudocode:
6082     #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
6083     #   curr = twig->end
6084     #   while true
6085     #     twig = next-token-from-slice(curr, word->end, '/')
6086     #     if (twig.empty()) break
6087     #     if (slice-equal?(twig, s)) return true
6088     #     curr = twig->end
6089     #   return false
6090     # . prolog
6091     55/push-EBP
6092     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6093     # . save registers
6094     51/push-ECX
6095     52/push-EDX
6096     56/push-ESI
6097     57/push-EDI
6098     # ESI = word
6099     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
6100     # EDX = word->end
6101     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
6102     # var twig/EDI : (address slice) = {0, 0}
6103     68/push  0/imm32/end
6104     68/push  0/imm32/start
6105     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
6106     # next-token-from-slice(word->start, word->end, '/', twig)
6107     # . . push args
6108     57/push-EDI
6109     68/push  0x2f/imm32/slash
6110     52/push-EDX
6111     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
6112     # . . call
6113     e8/call  next-token-from-slice/disp32
6114     # . . discard args
6115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
6116     # curr/ECX = twig->end
6117     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
6118 $has-metadata?:loop:
6119     # next-token-from-slice(curr, word->end, '/', twig)
6120     # . . push args
6121     57/push-EDI
6122     68/push  0x2f/imm32/slash
6123     52/push-EDX
6124     51/push-ECX
6125     # . . call
6126     e8/call  next-token-from-slice/disp32
6127     # . . discard args
6128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
6129     # if (slice-empty?(twig)) return false
6130     # . EAX = slice-empty?(twig)
6131     # . . push args
6132     57/push-EDI
6133     # . . call
6134     e8/call  slice-empty?/disp32
6135     # . . discard args
6136     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6137     # . if (EAX != 0) return false
6138     3d/compare-EAX-and  0/imm32
6139     75/jump-if-not-equal  $has-metadata?:false/disp8
6140     # if (slice-equal?(twig, s)) return true
6141     # . EAX = slice-equal?(twig, s)
6142     # . . push args
6143     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
6144     57/push-EDI
6145     # . . call
6146     e8/call  slice-equal?/disp32
6147     # . . discard args
6148     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6149     # . if (EAX != 0) return true
6150     3d/compare-EAX-and  0/imm32
6151     75/jump-if-not-equal  $has-metadata?:true/disp8
6152     # curr = twig->end
6153     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
6154     eb/jump  $has-metadata?:loop/disp8
6155 $has-metadata?:true:
6156     b8/copy-to-EAX  1/imm32/true
6157     eb/jump  $has-metadata?:end/disp8
6158 $has-metadata?:false:
6159     b8/copy-to-EAX  0/imm32/false
6160 $has-metadata?:end:
6161     # . reclaim locals
6162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6163     # . restore registers
6164     5f/pop-to-EDI
6165     5e/pop-to-ESI
6166     5a/pop-to-EDX
6167     59/pop-to-ECX
6168     # . epilog
6169     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6170     5d/pop-to-EBP
6171     c3/return
6172 
6173 test-has-metadata-true:
6174     # . prolog
6175     55/push-EBP
6176     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6177     # (EAX..ECX) = "ab/c"
6178     b8/copy-to-EAX  "ab/c"/imm32
6179     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6180     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
6181     05/add-to-EAX  4/imm32
6182     # var in/ESI : (address slice) = {EAX, ECX}
6183     51/push-ECX
6184     50/push-EAX
6185     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6186     # EAX = has-metadata?(ESI, "c")
6187     # . . push args
6188     68/push  "c"/imm32
6189     56/push-ESI
6190     # . . call
6191     e8/call  has-metadata?/disp32
6192     # . . discard args
6193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6194     # check-ints-equal(EAX, 1, msg)
6195     # . . push args
6196     68/push  "F - test-has-metadata-true"/imm32
6197     68/push  1/imm32/true
6198     50/push-EAX
6199     # . . call
6200     e8/call  check-ints-equal/disp32
6201     # . . discard args
6202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6203     # . epilog
6204     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6205     5d/pop-to-EBP
6206     c3/return
6207 
6208 test-has-metadata-false:
6209     # . prolog
6210     55/push-EBP
6211     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6212     # (EAX..ECX) = "ab/c"
6213     b8/copy-to-EAX  "ab/c"/imm32
6214     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6215     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
6216     05/add-to-EAX  4/imm32
6217     # var in/ESI : (address slice) = {EAX, ECX}
6218     51/push-ECX
6219     50/push-EAX
6220     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6221     # EAX = has-metadata?(ESI, "c")
6222     # . . push args
6223     68/push  "d"/imm32
6224     56/push-ESI
6225     # . . call
6226     e8/call  has-metadata?/disp32
6227     # . . discard args
6228     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6229     # check-ints-equal(EAX, 0, msg)
6230     # . . push args
6231     68/push  "F - test-has-metadata-false"/imm32
6232     68/push  0/imm32/false
6233     50/push-EAX
6234     # . . call
6235     e8/call  check-ints-equal/disp32
6236     # . . discard args
6237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6238     # . epilog
6239     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6240     5d/pop-to-EBP
6241     c3/return
6242 
6243 test-has-metadata-ignore-name:
6244     # . prolog
6245     55/push-EBP
6246     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6247     # (EAX..ECX) = "a/b"
6248     b8/copy-to-EAX  "a/b"/imm32
6249     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6250     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
6251     05/add-to-EAX  4/imm32
6252     # var in/ESI : (address slice) = {EAX, ECX}
6253     51/push-ECX
6254     50/push-EAX
6255     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6256     # EAX = has-metadata?(ESI, "a")
6257     # . . push args
6258     68/push  "a"/imm32
6259     56/push-ESI
6260     # . . call
6261     e8/call  has-metadata?/disp32
6262     # . . discard args
6263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6264     # check-ints-equal(EAX, 0, msg)
6265     # . . push args
6266     68/push  "F - test-has-metadata-ignore-name"/imm32
6267     68/push  0/imm32/false
6268     50/push-EAX
6269     # . . call
6270     e8/call  check-ints-equal/disp32
6271     # . . discard args
6272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6273     # . epilog
6274     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6275     5d/pop-to-EBP
6276     c3/return
6277 
6278 test-has-metadata-multiple-true:
6279     # . prolog
6280     55/push-EBP
6281     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6282     # (EAX..ECX) = "a/b/c"
6283     b8/copy-to-EAX  "a/b/c"/imm32
6284     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6285     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
6286     05/add-to-EAX  4/imm32
6287     # var in/ESI : (address slice) = {EAX, ECX}
6288     51/push-ECX
6289     50/push-EAX
6290     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6291     # EAX = has-metadata?(ESI, "c")
6292     # . . push args
6293     68/push  "c"/imm32
6294     56/push-ESI
6295     # . . call
6296     e8/call  has-metadata?/disp32
6297     # . . discard args
6298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6299     # check-ints-equal(EAX, 1, msg)
6300     # . . push args
6301     68/push  "F - test-has-metadata-multiple-true"/imm32
6302     68/push  1/imm32/true
6303     50/push-EAX
6304     # . . call
6305     e8/call  check-ints-equal/disp32
6306     # . . discard args
6307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6308     # . epilog
6309     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6310     5d/pop-to-EBP
6311     c3/return
6312 
6313 test-has-metadata-multiple-false:
6314     # . prolog
6315     55/push-EBP
6316     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6317     # (EAX..ECX) = "a/b/c"
6318     b8/copy-to-EAX  "a/b/c"/imm32
6319     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6320     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
6321     05/add-to-EAX  4/imm32
6322     # var in/ESI : (address slice) = {EAX, ECX}
6323     51/push-ECX
6324     50/push-EAX
6325     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6326     # EAX = has-metadata?(ESI, "d")
6327     # . . push args
6328     68/push  "d"/imm32
6329     56/push-ESI
6330     # . . call
6331     e8/call  has-metadata?/disp32
6332     # . . discard args
6333     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6334     # check-ints-equal(EAX, 0, msg)
6335     # . . push args
6336     68/push  "F - test-has-metadata-multiple-false"/imm32
6337     68/push  0/imm32/false
6338     50/push-EAX
6339     # . . call
6340     e8/call  check-ints-equal/disp32
6341     # . . discard args
6342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6343     # . epilog
6344     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6345     5d/pop-to-EBP
6346     c3/return
6347 
6348 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
6349 # it in 'width' bytes of hex, least significant first.
6350 # Otherwise just print the entire word including metadata.
6351 # Always print a trailing space.
6352 emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
6353     # . prolog
6354     55/push-EBP
6355     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6356     # . save registers
6357     50/push-EAX
6358     56/push-ESI
6359     57/push-EDI
6360     # ESI = word
6361     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
6362     # var name/EDI : (address slice) = {0, 0}
6363     68/push  0/imm32/end
6364     68/push  0/imm32/start
6365     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
6366     # datum = next-token-from-slice(word->start, word->end, '/')
6367     # . . push args
6368     57/push-EDI
6369     68/push  0x2f/imm32/slash
6370     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
6371     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
6372     # . . call
6373     e8/call  next-token-from-slice/disp32
6374     # . . discard args
6375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
6376     # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return
6377     # . EAX = is-valid-name?(name)
6378     # . . push args
6379     57/push-EDI
6380     # . . call
6381     e8/call  is-valid-name?/disp32
6382     # . . discard args
6383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6384     # . if (EAX != 0)
6385     3d/compare-EAX-and  0/imm32
6386     74/jump-if-equal  $emit:hex-int/disp8
6387 $emit:name:
6388     # . write-slice-buffered(out, word)
6389     # . . push args
6390     56/push-ESI
6391     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
6392     # . . call
6393     e8/call  write-slice-buffered/disp32
6394     # . . discard args
6395     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6396     # . write-buffered(out, " ")
6397     # . . push args
6398     68/push  " "/imm32
6399     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
6400     # . . call
6401     e8/call  write-buffered/disp32
6402     # . . discard args
6403     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6404     # . return
6405     eb/jump  $emit:end/disp8
6406     # otherwise emit-hex(out, parse-hex-int(datum), width)
6407     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
6408     #   name or a hex number, but we're only going to be passing in real legal
6409     #   programs. We just want to make sure that valid names aren't treated as
6410     #   (valid) hex numbers.)
6411 $emit:hex-int:
6412     # . value/EAX = parse-hex-int(datum)
6413     # . . push args
6414     57/push-EDI
6415     # . . call
6416     e8/call  parse-hex-int/disp32
6417     # . . discard args
6418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6419     # . emit-hex(out, value, width)
6420     # . . push args
6421     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
6422     50/push-EAX
6423     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
6424     # . . call
6425     e8/call  emit-hex/disp32
6426     # . . discard args
6427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6428 $emit:end:
6429     # . reclaim locals
6430     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6431     # . restore registers
6432     5f/pop-to-EDI
6433     5e/pop-to-ESI
6434     58/pop-to-EAX
6435     # . epilog
6436     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6437     5d/pop-to-EBP
6438     c3/return
6439 
6440 test-emit-number:
6441     # . prolog
6442     55/push-EBP
6443     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6444     # setup
6445     # . clear-stream(_test-output-stream)
6446     # . . push args
6447     68/push  _test-output-stream/imm32
6448     # . . call
6449     e8/call  clear-stream/disp32
6450     # . . discard args
6451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6452     # . clear-stream(_test-output-buffered-file+4)
6453     # . . push args
6454     b8/copy-to-EAX  _test-output-buffered-file/imm32
6455     05/add-to-EAX  4/imm32
6456     50/push-EAX
6457     # . . call
6458     e8/call  clear-stream/disp32
6459     # . . discard args
6460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6461     # var slice/ECX = "30"
6462     68/push  _test-slice-three-zero-end/imm32/end
6463     68/push  _test-slice-three-zero/imm32/start
6464     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6465     # emit(_test-output-buffered-file, slice, 1)
6466     # . . push args
6467     68/push  1/imm32
6468     51/push-ECX
6469     68/push  _test-output-buffered-file/imm32
6470     # . . call
6471     e8/call  emit/disp32
6472     # . . discard args
6473     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6474     # flush(_test-output-buffered-file)
6475     # . . push args
6476     68/push  _test-output-buffered-file/imm32
6477     # . . call
6478     e8/call  flush/disp32
6479     # . . discard args
6480     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6481     # check-stream-equal(_test-output-stream, "30 ", msg)
6482     # . . push args
6483     68/push  "F - test-emit-number/1"/imm32
6484     68/push  "30 "/imm32
6485     68/push  _test-output-stream/imm32
6486     # . . call
6487     e8/call  check-stream-equal/disp32
6488     # . . discard args
6489     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6490     # . epilog
6491     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6492     5d/pop-to-EBP
6493     c3/return
6494 
6495 test-emit-negative-number:
6496     # test support for sign-extending negative numbers
6497     # . prolog
6498     55/push-EBP
6499     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6500     # setup
6501     # . clear-stream(_test-output-stream)
6502     # . . push args
6503     68/push  _test-output-stream/imm32
6504     # . . call
6505     e8/call  clear-stream/disp32
6506     # . . discard args
6507     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6508     # . clear-stream(_test-output-buffered-file+4)
6509     # . . push args
6510     b8/copy-to-EAX  _test-output-buffered-file/imm32
6511     05/add-to-EAX  4/imm32
6512     50/push-EAX
6513     # . . call
6514     e8/call  clear-stream/disp32
6515     # . . discard args
6516     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6517     # var slice/ECX = "-2"
6518     68/push  _test-slice-negative-two-end/imm32/end
6519     68/push  _test-slice-negative-two/imm32/start
6520     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6521     # emit(_test-output-buffered-file, slice, 2)
6522     # . . push args
6523     68/push  2/imm32
6524     51/push-ECX
6525     68/push  _test-output-buffered-file/imm32
6526     # . . call
6527     e8/call  emit/disp32
6528     # . . discard args
6529     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6530     # flush(_test-output-buffered-file)
6531     # . . push args
6532     68/push  _test-output-buffered-file/imm32
6533     # . . call
6534     e8/call  flush/disp32
6535     # . . discard args
6536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6537     # check-stream-equal(_test-output-stream, "fe ff ", msg)
6538     # . . push args
6539     68/push  "F - test-emit-number/1"/imm32
6540     68/push  "fe ff "/imm32
6541     68/push  _test-output-stream/imm32
6542     # . . call
6543     e8/call  check-stream-equal/disp32
6544     # . . discard args
6545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6546     # . epilog
6547     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6548     5d/pop-to-EBP
6549     c3/return
6550 
6551 test-emit-number-with-metadata:
6552     # . prolog
6553     55/push-EBP
6554     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6555     # setup
6556     # . clear-stream(_test-output-stream)
6557     # . . push args
6558     68/push  _test-output-stream/imm32
6559     # . . call
6560     e8/call  clear-stream/disp32
6561     # . . discard args
6562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6563     # . clear-stream(_test-output-buffered-file+4)
6564     # . . push args
6565     b8/copy-to-EAX  _test-output-buffered-file/imm32
6566     05/add-to-EAX  4/imm32
6567     50/push-EAX
6568     # . . call
6569     e8/call  clear-stream/disp32
6570     # . . discard args
6571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6572     # var slice/ECX = "-2/foo"
6573     68/push  _test-slice-negative-two-metadata-end/imm32/end
6574     68/push  _test-slice-negative-two/imm32/start
6575     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6576     # emit(_test-output-buffered-file, slice, 2)
6577     # . . push args
6578     68/push  2/imm32
6579     51/push-ECX
6580     68/push  _test-output-buffered-file/imm32
6581     # . . call
6582     e8/call  emit/disp32
6583     # . . discard args
6584     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6585     # flush(_test-output-buffered-file)
6586     # . . push args
6587     68/push  _test-output-buffered-file/imm32
6588     # . . call
6589     e8/call  flush/disp32
6590     # . . discard args
6591     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6592     # the '/foo' will have no impact on the output
6593     # check-stream-equal(_test-output-stream, "fe ff ", msg)
6594     # . . push args
6595     68/push  "F - test-emit-number-with-metadata"/imm32
6596     68/push  "fe ff "/imm32
6597     68/push  _test-output-stream/imm32
6598     # . . call
6599     e8/call  check-stream-equal/disp32
6600     # . . discard args
6601     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6602     # . epilog
6603     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6604     5d/pop-to-EBP
6605     c3/return
6606 
6607 test-emit-non-number:
6608     # . prolog
6609     55/push-EBP
6610     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6611     # setup
6612     # . clear-stream(_test-output-stream)
6613     # . . push args
6614     68/push  _test-output-stream/imm32
6615     # . . call
6616     e8/call  clear-stream/disp32
6617     # . . discard args
6618     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6619     # . clear-stream(_test-output-buffered-file+4)
6620     # . . push args
6621     b8/copy-to-EAX  _test-output-buffered-file/imm32
6622     05/add-to-EAX  4/imm32
6623     50/push-EAX
6624     # . . call
6625     e8/call  clear-stream/disp32
6626     # . . discard args
6627     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6628     # var slice/ECX = "xyz"
6629     68/push  _test-slice-non-number-word-end/imm32/end
6630     68/push  _test-slice-non-number-word/imm32/start
6631     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6632     # emit(_test-output-buffered-file, slice, 2)
6633     # . . push args
6634     68/push  2/imm32
6635     51/push-ECX
6636     68/push  _test-output-buffered-file/imm32
6637     # . . call
6638     e8/call  emit/disp32
6639     # . . discard args
6640     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6641     # flush(_test-output-buffered-file)
6642     # . . push args
6643     68/push  _test-output-buffered-file/imm32
6644     # . . call
6645     e8/call  flush/disp32
6646     # . . discard args
6647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6648     # check-stream-equal(_test-output-stream, "xyz", msg)
6649     # . . push args
6650     68/push  "F - test-emit-non-number"/imm32
6651     68/push  "xyz "/imm32
6652     68/push  _test-output-stream/imm32
6653     # . . call
6654     e8/call  check-stream-equal/disp32
6655     # . . discard args
6656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6657     # . epilog
6658     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6659     5d/pop-to-EBP
6660     c3/return
6661 
6662 test-emit-non-number-with-metadata:
6663     # . prolog
6664     55/push-EBP
6665     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6666     # setup
6667     # . clear-stream(_test-output-stream)
6668     # . . push args
6669     68/push  _test-output-stream/imm32
6670     # . . call
6671     e8/call  clear-stream/disp32
6672     # . . discard args
6673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6674     # . clear-stream(_test-output-buffered-file+4)
6675     # . . push args
6676     b8/copy-to-EAX  _test-output-buffered-file/imm32
6677     05/add-to-EAX  4/imm32
6678     50/push-EAX
6679     # . . call
6680     e8/call  clear-stream/disp32
6681     # . . discard args
6682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6683     # var slice/ECX = "xyz/"
6684     68/push  _test-slice-non-number-word-metadata-end/imm32/end
6685     68/push  _test-slice-non-number-word/imm32/start
6686     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6687     # emit(_test-output-buffered-file, slice, 2)
6688     # . . push args
6689     68/push  2/imm32
6690     51/push-ECX
6691     68/push  _test-output-buffered-file/imm32
6692     # . . call
6693     e8/call  emit/disp32
6694     # . . discard args
6695     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6696     # flush(_test-output-buffered-file)
6697     # . . push args
6698     68/push  _test-output-buffered-file/imm32
6699     # . . call
6700     e8/call  flush/disp32
6701     # . . discard args
6702     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6703     # check-stream-equal(_test-output-stream, "xyz/", msg)
6704     # . . push args
6705     68/push  "F - test-emit-non-number-with-metadata"/imm32
6706     68/push  "xyz/ "/imm32
6707     68/push  _test-output-stream/imm32
6708     # . . call
6709     e8/call  check-stream-equal/disp32
6710     # . . discard args
6711     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6712     # . epilog
6713     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6714     5d/pop-to-EBP
6715     c3/return
6716 
6717 test-emit-non-number-with-all-hex-digits-and-metadata:
6718     # . prolog
6719     55/push-EBP
6720     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6721     # setup
6722     # . clear-stream(_test-output-stream)
6723     # . . push args
6724     68/push  _test-output-stream/imm32
6725     # . . call
6726     e8/call  clear-stream/disp32
6727     # . . discard args
6728     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6729     # . clear-stream(_test-output-buffered-file+4)
6730     # . . push args
6731     b8/copy-to-EAX  _test-output-buffered-file/imm32
6732     05/add-to-EAX  4/imm32
6733     50/push-EAX
6734     # . . call
6735     e8/call  clear-stream/disp32
6736     # . . discard args
6737     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6738     # var slice/ECX = "abcd/xyz"
6739     68/push  _test-slice-hexlike-non-number-word-metadata-end/imm32/end
6740     68/push  _test-slice-hexlike-non-number-word/imm32/start
6741     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6742     # emit(_test-output-buffered-file, slice, 2)
6743     # . . push args
6744     68/push  2/imm32
6745     51/push-ECX
6746     68/push  _test-output-buffered-file/imm32
6747     # . . call
6748     e8/call  emit/disp32
6749     # . . discard args
6750     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6751     # flush(_test-output-buffered-file)
6752     # . . push args
6753     68/push  _test-output-buffered-file/imm32
6754     # . . call
6755     e8/call  flush/disp32
6756     # . . discard args
6757     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6758 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
6784     # check-stream-equal(_test-output-stream, "abcd/xyz")
6785     # . . push args
6786     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
6787     68/push  "abcd/xyz "/imm32
6788     68/push  _test-output-stream/imm32
6789     # . . call
6790     e8/call  check-stream-equal/disp32
6791     # . . discard args
6792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6793     # . epilog
6794     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6795     5d/pop-to-EBP
6796     c3/return
6797 
6798 # conditions for 'valid' names that are not at risk of looking like hex numbers
6799 # keep in sync with the rules in labels.cc
6800 #: - if it starts with a digit, it's treated as a number. If it can't be
6801 #:   parsed as hex it will raise an error.
6802 #: - if it starts with '-' it's treated as a number.
6803 #: - if it starts with '0x' it's treated as a number. (redundant)
6804 #: - if it's two characters long, it can't be a name. Either it's a hex
6805 #:   byte, or it raises an error.
6806 is-valid-name?:  # in : (address slice) -> EAX : boolean
6807     # . prolog
6808     55/push-EBP
6809     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6810     # . save registers
6811     51/push-ECX
6812     56/push-ESI
6813     # ESI = in
6814     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
6815     # start/ECX = in->start
6816     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
6817     # end/EAX = in->end
6818     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
6819 $is-valid-name?:check0:
6820     # if (start >= end) return false
6821     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # compare ECX with EAX
6822     7d/jump-if-greater-or-equal  $is-valid-name?:false/disp8
6823 $is-valid-name?:check1:
6824     # EAX -= ECX
6825     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
6826     # if (EAX == 2) return false
6827     3d/compare-EAX-and  2/imm32
6828     74/jump-if-equal  $is-valid-name?:false/disp8
6829 $is-valid-name?:check2:
6830     # c/EAX = *ECX
6831     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
6832     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
6833     # if (c == "-") return false
6834     3d/compare-EAX-and  2d/imm32/-
6835     74/jump-if-equal  $is-valid-name?:false/disp8
6836 $is-valid-name?:check3a:
6837     # if (c < "0") return true
6838     3d/compare-EAX-with  30/imm32/0
6839     7c/jump-if-lesser  $is-valid-name?:true/disp8
6840 $is-valid-name?:check3b:
6841     # if (c > "9") return true
6842     3d/compare-EAX-with  39/imm32/9
6843     7f/jump-if-greater  $is-valid-name?:true/disp8
6844 $is-valid-name?:false:
6845     # return false
6846     b8/copy-to-EAX  0/imm32/false
6847     eb/jump  $is-valid-name?:end/disp8
6848 $is-valid-name?:true:
6849     # return true
6850     b8/copy-to-EAX  1/imm32/true
6851 $is-valid-name?:end:
6852     # . restore registers
6853     5e/pop-to-ESI
6854     59/pop-to-ECX
6855     # . epilog
6856     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6857     5d/pop-to-EBP
6858     c3/return
6859 
6860 test-is-valid-name-digit-prefix:
6861     # . prolog
6862     55/push-EBP
6863     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6864     # var slice/ECX = "34"
6865     68/push  _test-slice-hex-int-end/imm32
6866     68/push  _test-slice-hex-int/imm32
6867     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6868     # EAX = is-valid-name?(slice)
6869     # . . push args
6870     51/push-ECX
6871     # . . call
6872     e8/call  is-valid-name?/disp32
6873     # . . discard args
6874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6875     # check-ints-equal(EAX, 0, msg)
6876     # . . push args
6877     68/push  "F - test-is-valid-name-digit-prefix"/imm32
6878     68/push  0/imm32/false
6879     50/push-EAX
6880     # . . call
6881     e8/call  check-ints-equal/disp32
6882     # . . discard args
6883     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6884     # . epilog
6885     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6886     5d/pop-to-EBP
6887     c3/return
6888 
6889 test-is-valid-name-negative-prefix:
6890     # . prolog
6891     55/push-EBP
6892     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6893     # var slice/ECX = "-0x34"
6894     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
6895     68/push  _test-slice-hex-int-with-0x-prefix-negative/imm32
6896     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6897     # EAX = is-valid-name?(slice)
6898     # . . push args
6899     51/push-ECX
6900     # . . call
6901     e8/call  is-valid-name?/disp32
6902     # . . discard args
6903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6904     # check-ints-equal(EAX, 0, msg)
6905     # . . push args
6906     68/push  "F - test-is-valid-name-negative-prefix"/imm32
6907     68/push  0/imm32/false
6908     50/push-EAX
6909     # . . call
6910     e8/call  check-ints-equal/disp32
6911     # . . discard args
6912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6913     # . epilog
6914     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6915     5d/pop-to-EBP
6916     c3/return
6917 
6918 test-is-valid-name-0x-prefix:
6919     # . prolog
6920     55/push-EBP
6921     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6922     # var slice/ECX = "0x34"
6923     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
6924     68/push  _test-slice-hex-int-with-0x-prefix/imm32
6925     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6926     # EAX = is-valid-name?(slice)
6927     # . . push args
6928     51/push-ECX
6929     # . . call
6930     e8/call  is-valid-name?/disp32
6931     # . . discard args
6932     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6933     # check-ints-equal(EAX, 0, msg)
6934     # . . push args
6935     68/push  "F - test-is-valid-name-0x-prefix"/imm32
6936     68/push  0/imm32/false
6937     50/push-EAX
6938     # . . call
6939     e8/call  check-ints-equal/disp32
6940     # . . discard args
6941     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6942     # . epilog
6943     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6944     5d/pop-to-EBP
6945     c3/return
6946 
6947 test-is-valid-name-starts-with-pre-digit:
6948     # . prolog
6949     55/push-EBP
6950     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6951     # var slice/ECX = "/03"
6952     68/push  _test-slice-with-slash-prefix-end/imm32
6953     68/push  _test-slice-with-slash-prefix/imm32
6954     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6955     # EAX = is-valid-name?(slice)
6956     # . . push args
6957     51/push-ECX
6958     # . . call
6959     e8/call  is-valid-name?/disp32
6960     # . . discard args
6961     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6962     # check-ints-equal(EAX, 1, msg)
6963     # . . push args
6964     68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
6965     68/push  1/imm32/true
6966     50/push-EAX
6967     # . . call
6968     e8/call  check-ints-equal/disp32
6969     # . . discard args
6970     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6971     # . epilog
6972     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6973     5d/pop-to-EBP
6974     c3/return
6975 
6976 test-is-valid-name-starts-with-post-digit:
6977     # . prolog
6978     55/push-EBP
6979     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6980     # var slice/ECX = "q34"
6981     68/push  _test-slice-char-and-digits-end/imm32
6982     68/push  _test-slice-char-and-digits/imm32
6983     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6984     # EAX = is-valid-name?(slice)
6985     # . . push args
6986     51/push-ECX
6987     # . . call
6988     e8/call  is-valid-name?/disp32
6989     # . . discard args
6990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6991     # check-ints-equal(EAX, 1, msg)
6992     # . . push args
6993     68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
6994     68/push  1/imm32/true
6995     50/push-EAX
6996     # . . call
6997     e8/call  check-ints-equal/disp32
6998     # . . discard args
6999     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7000     # . epilog
7001     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7002     5d/pop-to-EBP
7003     c3/return
7004 
7005 test-is-valid-name-starts-with-digit:
7006     # . prolog
7007     55/push-EBP
7008     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7009     # var slice/ECX = "0x34"
7010     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
7011     68/push  _test-slice-hex-int-with-0x-prefix/imm32
7012     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7013     # EAX = is-valid-name?(slice)
7014     # . . push args
7015     51/push-ECX
7016     # . . call
7017     e8/call  is-valid-name?/disp32
7018     # . . discard args
7019     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7020     # check-ints-equal(EAX, 0, msg)
7021     # . . push args
7022     68/push  "F - test-is-valid-name-starts-with-digit"/imm32
7023     68/push  0/imm32/false
7024     50/push-EAX
7025     # . . call
7026     e8/call  check-ints-equal/disp32
7027     # . . discard args
7028     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7029     # . epilog
7030     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7031     5d/pop-to-EBP
7032     c3/return
7033 
7034 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
7035 emit-hex:  # out : (address buffered-file), n : int, width : int -> <void>
7036     # . prolog
7037     55/push-EBP
7038     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7039     # . save registers
7040     50/push-EAX
7041     51/push-ECX
7042     52/push-EDX
7043     53/push-EBX
7044     57/push-EDI
7045     # EDI = out
7046     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
7047     # EBX = n
7048     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
7049     # EDX = width
7050     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
7051     # var curr/ECX = 0
7052     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
7053 $emit-hex:loop:
7054     # if (curr >= width) break
7055     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
7056     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
7057     # print-byte-buffered(out, EBX)
7058     # . . push args
7059     53/push-EBX
7060     57/push-EDI
7061     # . . call
7062     e8/call  print-byte-buffered/disp32
7063     # . . discard args
7064     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7065     # write-byte-buffered(out, ' ')
7066     # . . push args
7067     68/push  0x20/imm32/space
7068     57/push-EDI
7069     # . . call
7070     e8/call  write-byte-buffered/disp32
7071     # . . discard args
7072     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7073     # EBX = EBX >> 8
7074     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
7075 $emit-hex:continue:
7076     # ++curr
7077     41/increment-ECX
7078     eb/jump  $emit-hex:loop/disp8
7079 $emit-hex:end:
7080     # . restore registers
7081     5f/pop-to-EDI
7082     5b/pop-to-EBX
7083     5a/pop-to-EDX
7084     59/pop-to-ECX
7085     58/pop-to-EAX
7086     # . epilog
7087     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7088     5d/pop-to-EBP
7089     c3/return
7090 
7091 test-emit-hex-single-byte:
7092     # setup
7093     # . clear-stream(_test-output-stream)
7094     # . . push args
7095     68/push  _test-output-stream/imm32
7096     # . . call
7097     e8/call  clear-stream/disp32
7098     # . . discard args
7099     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7100     # . clear-stream(_test-output-buffered-file+4)
7101     # . . push args
7102     b8/copy-to-EAX  _test-output-buffered-file/imm32
7103     05/add-to-EAX  4/imm32
7104     50/push-EAX
7105     # . . call
7106     e8/call  clear-stream/disp32
7107     # . . discard args
7108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7109     # emit-hex(_test-output-buffered-file, 0xab, 1)
7110     # . . push args
7111     68/push  1/imm32
7112     68/push  0xab/imm32
7113     68/push  _test-output-buffered-file/imm32
7114     # . . call
7115     e8/call  emit-hex/disp32
7116     # . . discard args
7117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7118     # flush(_test-output-buffered-file)
7119     # . . push args
7120     68/push  _test-output-buffered-file/imm32
7121     # . . call
7122     e8/call  flush/disp32
7123     # . . discard args
7124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7125     # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
7126     # . . push args
7127     68/push  "F - test-emit-hex-single-byte"/imm32
7128     68/push  0x206261/imm32
7129     # . . push *_test-output-stream->data
7130     b8/copy-to-EAX  _test-output-stream/imm32
7131     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
7132     # . . call
7133     e8/call  check-ints-equal/disp32
7134     # . . discard args
7135     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7136     # . end
7137     c3/return
7138 
7139 test-emit-hex-multiple-byte:
7140     # setup
7141     # . clear-stream(_test-output-stream)
7142     # . . push args
7143     68/push  _test-output-stream/imm32
7144     # . . call
7145     e8/call  clear-stream/disp32
7146     # . . discard args
7147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7148     # . clear-stream(_test-output-buffered-file+4)
7149     # . . push args
7150     b8/copy-to-EAX  _test-output-buffered-file/imm32
7151     05/add-to-EAX  4/imm32
7152     50/push-EAX
7153     # . . call
7154     e8/call  clear-stream/disp32
7155     # . . discard args
7156     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7157     # emit-hex(_test-output-buffered-file, 0x1234, 2)
7158     # . . push args
7159     68/push  2/imm32
7160     68/push  0x1234/imm32
7161     68/push  _test-output-buffered-file/imm32
7162     # . . call
7163     e8/call  emit-hex/disp32
7164     # . . discard args
7165     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7166     # flush(_test-output-buffered-file)
7167     # . . push args
7168     68/push  _test-output-buffered-file/imm32
7169     # . . call
7170     e8/call  flush/disp32
7171     # . . discard args
7172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7173     # check-stream-equal(_test-output-stream, "34 12 ", msg)
7174     # . . push args
7175     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
7176     68/push  "34 12 "/imm32
7177     68/push  _test-output-stream/imm32
7178     # . . call
7179     e8/call  check-stream-equal/disp32
7180     # . . discard args
7181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7182     # . end
7183     c3/return
7184 
7185 test-emit-hex-zero-pad:
7186     # setup
7187     # . clear-stream(_test-output-stream)
7188     # . . push args
7189     68/push  _test-output-stream/imm32
7190     # . . call
7191     e8/call  clear-stream/disp32
7192     # . . discard args
7193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7194     # . clear-stream(_test-output-buffered-file+4)
7195     # . . push args
7196     b8/copy-to-EAX  _test-output-buffered-file/imm32
7197     05/add-to-EAX  4/imm32
7198     50/push-EAX
7199     # . . call
7200     e8/call  clear-stream/disp32
7201     # . . discard args
7202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7203     # emit-hex(_test-output-buffered-file, 0xab, 2)
7204     # . . push args
7205     68/push  2/imm32
7206     68/push  0xab/imm32
7207     68/push  _test-output-buffered-file/imm32
7208     # . . call
7209     e8/call  emit-hex/disp32
7210     # . . discard args
7211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7212     # flush(_test-output-buffered-file)
7213     # . . push args
7214     68/push  _test-output-buffered-file/imm32
7215     # . . call
7216     e8/call  flush/disp32
7217     # . . discard args
7218     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7219     # check(_test-output-stream->data == 'ab 00 ')
7220     # . . push args
7221     68/push  "F - test-emit-hex-zero-pad/1"/imm32
7222     68/push  "ab 00 "/imm32
7223     68/push  _test-output-stream/imm32
7224     # . . call
7225     e8/call  check-stream-equal/disp32
7226     # . . discard args
7227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7228     # . end
7229     c3/return
7230 
7231 test-emit-hex-negative:
7232     # setup
7233     # . clear-stream(_test-output-stream)
7234     # . . push args
7235     68/push  _test-output-stream/imm32
7236     # . . call
7237     e8/call  clear-stream/disp32
7238     # . . discard args
7239     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7240     # . clear-stream(_test-output-buffered-file+4)
7241     # . . push args
7242     b8/copy-to-EAX  _test-output-buffered-file/imm32
7243     05/add-to-EAX  4/imm32
7244     50/push-EAX
7245     # . . call
7246     e8/call  clear-stream/disp32
7247     # . . discard args
7248     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7249     # emit-hex(_test-output-buffered-file, -1, 2)
7250     # . . push args
7251     68/push  2/imm32
7252     68/push  -1/imm32
7253     68/push  _test-output-buffered-file/imm32
7254     # . . call
7255     e8/call  emit-hex/disp32
7256     # . . discard args
7257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7258     # flush(_test-output-buffered-file)
7259     # . . push args
7260     68/push  _test-output-buffered-file/imm32
7261     # . . call
7262     e8/call  flush/disp32
7263     # . . discard args
7264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7265     # check-stream-equal(_test-output-stream == "ff ff ")
7266     # . . push args
7267     68/push  "F - test-emit-hex-negative/1"/imm32
7268     68/push  "ff ff "/imm32
7269     68/push  _test-output-stream/imm32
7270     # . . call
7271     e8/call  check-stream-equal/disp32
7272     # . . discard args
7273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7274     # . end
7275     c3/return
7276 
7277 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/'))
7278 parse-datum-of-word:  # word : (address slice) -> value/EAX
7279     # . prolog
7280     55/push-EBP
7281     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7282     # . save registers
7283     51/push-ECX
7284     56/push-ESI
7285     # ESI = word
7286     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
7287     # var slice/ECX = {0, 0}
7288     68/push  0/imm32/end
7289     68/push  0/imm32/start
7290     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7291     # slice = next-token-from-slice(word->start, word->end, '/')
7292     # . . push args
7293     51/push-ECX
7294     68/push  0x2f/imm32/slash
7295     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
7296     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
7297     # . . call
7298     e8/call  next-token-from-slice/disp32
7299     # . . discard args
7300     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
7301     # value/EAX = parse-hex-int(slice)
7302     # . . push args
7303     51/push-ECX
7304     # . . call
7305     e8/call  parse-hex-int/disp32
7306     # . . discard args
7307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7308 $parse-datum-of-word:end:
7309     # . reclaim locals
7310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7311     # . restore registers
7312     5e/pop-to-ESI
7313     59/pop-to-ECX
7314     # . epilog
7315     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7316     5d/pop-to-EBP
7317     c3/return
7318 
7319 == data
7320 
7321 _test-slice-negative-two:
7322     2d/- 32/2
7323 _test-slice-negative-two-end:
7324     2f/slash 66/f 6f/o 6f/o
7325 _test-slice-negative-two-metadata-end:
7326 
7327 _test-slice-three-zero:
7328     33/3 30/0
7329 _test-slice-three-zero-end:
7330 
7331 _test-slice-non-number-word:
7332     78/x 79/y 7a/z
7333 _test-slice-non-number-word-end:
7334     2f/slash
7335 _test-slice-non-number-word-metadata-end:
7336 
7337 _test-slice-hexlike-non-number-word:
7338     61/a 62/b 63/c 64/d
7339 _test-slice-hexlike-non-number-word-end:
7340     2f/slash
7341     78/x 79/y 7a/z
7342 _test-slice-hexlike-non-number-word-metadata-end:
7343 
7344 _test-slice-with-slash-prefix:
7345   2f/slash 30/0 33/3
7346 _test-slice-with-slash-prefix-end:
7347 
7348 # . . vim:nowrap:textwidth=0