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(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(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/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 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 190     # next-word(line, word-slice)
 191     # . . push args
 192     52/push-EDX
 193     51/push-ECX
 194     # . . call
 195     e8/call  next-word/disp32
 196     # . . discard args
 197     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 198 $convert:check1:
 199     # if (slice-empty?(word-slice)) write-stream-data(out, line)
 200     # . EAX = slice-empty?(word-slice)
 201     # . . push args
 202     52/push-EDX
 203     # . . call
 204     e8/call  slice-empty?/disp32
 205     # . . discard args
 206     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 207     # . if (EAX != 0) write-stream-data(out, line)
 208     3d/compare-EAX-and  0/imm32
 209     0f 85/jump-if-not-equal  $convert:pass-through/disp32
 210 $convert:check2:
 211 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
 261     # if (slice-equal?(word-slice, "=="))
 262     #   word-slice = next-word(line)
 263     #   in-code? = slice-equal?(word-slice, "code")
 264     #   write-stream-data(out, line)
 265     # . EAX = slice-equal?(word-slice, "==")
 266     # . . push args
 267     68/push  "=="/imm32
 268     52/push-EDX
 269     # . . call
 270     e8/call  slice-equal?/disp32
 271     # . . discard args
 272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 273     # . if (EAX == 0) goto check3
 274     3d/compare-EAX-and  0/imm32
 275     0f 84/jump-if-equal  $convert:check3/disp32
 276     # . next-word(line, word-slice)
 277     # . . push args
 278     52/push-EDX
 279     51/push-ECX
 280     # . . call
 281     e8/call  next-word/disp32
 282     # . . discard args
 283     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 284 +-- 50 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
 334     # . in-code? = slice-equal?(word-slice, "code")
 335     # . . push args
 336     68/push  "code"/imm32
 337     52/push-EDX
 338     # . . call
 339     e8/call  slice-equal?/disp32
 340     # . . discard args
 341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 342     # . . in-code? = EAX
 343     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
 344     # . goto pass-through
 345     eb/jump  $convert:pass-through/disp8
 346 $convert:check3:
 347     # else rewind-stream(line)
 348     # . rewind-stream(line)
 349     # . . push args
 350     51/push-ECX
 351     # . . call
 352     e8/call  rewind-stream/disp32
 353     # . . discard args
 354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 355     # if (in-code? != 0) convert-instruction(line, out)
 356     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
 357     74/jump-if-equal  $convert:data/disp8
 358 $convert:code:
 359     # . convert-instruction(line, out)
 360     # . . push args
 361     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 362     51/push-ECX
 363     # . . call
 364     e8/call  convert-instruction/disp32
 365     # . . discard args
 366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 367     # . loop
 368     e9/jump  $convert:loop/disp32
 369 $convert:data:
 370     # else convert-data(line, out)
 371     # . . push args
 372     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 373     51/push-ECX
 374     # . . call
 375     e8/call  convert-data/disp32
 376     # . . discard args
 377     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 378     # . loop
 379     e9/jump  $convert:loop/disp32
 380 $convert:pass-through:
 381     # write-stream-data(out, line)
 382     # . . push args
 383     51/push-ECX
 384     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 385     # . . call
 386     e8/call  write-stream-data/disp32
 387     # . . discard args
 388     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 389     # . loop
 390     e9/jump  $convert:loop/disp32
 391 $convert:break:
 392     # flush(out)
 393     # . . push args
 394     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 395     # . . call
 396     e8/call  flush/disp32
 397     # . . discard args
 398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 399 $convert:end:
 400     # . reclaim locals
 401     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 402     # . restore registers
 403     5b/pop-to-EBX
 404     5a/pop-to-EDX
 405     59/pop-to-ECX
 406     58/pop-to-EAX
 407     # . epilog
 408     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 409     5d/pop-to-EBP
 410     c3/return
 411 
 412 test-convert-passes-empty-lines-through:
 413     # if a line is empty, pass it along unchanged
 414     # . prolog
 415     55/push-EBP
 416     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 417     # setup
 418     # . clear-stream(_test-input-stream)
 419     # . . push args
 420     68/push  _test-input-stream/imm32
 421     # . . call
 422     e8/call  clear-stream/disp32
 423     # . . discard args
 424     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 425     # . clear-stream(_test-input-buffered-file+4)
 426     # . . push args
 427     b8/copy-to-EAX  _test-input-buffered-file/imm32
 428     05/add-to-EAX  4/imm32
 429     50/push-EAX
 430     # . . call
 431     e8/call  clear-stream/disp32
 432     # . . discard args
 433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 434     # . clear-stream(_test-output-stream)
 435     # . . push args
 436     68/push  _test-output-stream/imm32
 437     # . . call
 438     e8/call  clear-stream/disp32
 439     # . . discard args
 440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 441     # . clear-stream(_test-output-buffered-file+4)
 442     # . . push args
 443     b8/copy-to-EAX  _test-output-buffered-file/imm32
 444     05/add-to-EAX  4/imm32
 445     50/push-EAX
 446     # . . call
 447     e8/call  clear-stream/disp32
 448     # . . discard args
 449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 450     # write nothing to input
 451     # convert(_test-input-buffered-file, _test-output-buffered-file)
 452     # . . push args
 453     68/push  _test-output-buffered-file/imm32
 454     68/push  _test-input-buffered-file/imm32
 455     # . . call
 456     e8/call  convert/disp32
 457     # . . discard args
 458     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 459     # check that the line just passed through
 460     # . flush(_test-output-buffered-file)
 461     # . . push args
 462     68/push  _test-output-buffered-file/imm32
 463     # . . call
 464     e8/call  flush/disp32
 465     # . . discard args
 466     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 467     # . check-stream-equal(_test-output-stream, "", msg)
 468     # . . push args
 469     68/push  "F - test-convert-passes-empty-lines-through"/imm32
 470     68/push  ""/imm32
 471     68/push  _test-output-stream/imm32
 472     # . . call
 473     e8/call  check-stream-equal/disp32
 474     # . . discard args
 475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 476     # . epilog
 477     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 478     5d/pop-to-EBP
 479     c3/return
 480 
 481 test-convert-passes-lines-with-just-whitespace-through:
 482     # if a line is empty, pass it along unchanged
 483     # . prolog
 484     55/push-EBP
 485     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 486     # setup
 487     # . clear-stream(_test-input-stream)
 488     # . . push args
 489     68/push  _test-input-stream/imm32
 490     # . . call
 491     e8/call  clear-stream/disp32
 492     # . . discard args
 493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 494     # . clear-stream(_test-input-buffered-file+4)
 495     # . . push args
 496     b8/copy-to-EAX  _test-input-buffered-file/imm32
 497     05/add-to-EAX  4/imm32
 498     50/push-EAX
 499     # . . call
 500     e8/call  clear-stream/disp32
 501     # . . discard args
 502     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 503     # . clear-stream(_test-output-stream)
 504     # . . push args
 505     68/push  _test-output-stream/imm32
 506     # . . call
 507     e8/call  clear-stream/disp32
 508     # . . discard args
 509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 510     # . clear-stream(_test-output-buffered-file+4)
 511     # . . push args
 512     b8/copy-to-EAX  _test-output-buffered-file/imm32
 513     05/add-to-EAX  4/imm32
 514     50/push-EAX
 515     # . . call
 516     e8/call  clear-stream/disp32
 517     # . . discard args
 518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 519     # initialize input
 520     # . write(_test-input-stream, "    ")
 521     # . . push args
 522     68/push  "    "/imm32
 523     68/push  _test-input-stream/imm32
 524     # . . call
 525     e8/call  write/disp32
 526     # . . discard args
 527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 528     # convert(_test-input-buffered-file, _test-output-buffered-file)
 529     # . . push args
 530     68/push  _test-output-buffered-file/imm32
 531     68/push  _test-input-buffered-file/imm32
 532     # . . call
 533     e8/call  convert/disp32
 534     # . . discard args
 535     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 536     # check that the line just passed through
 537     # . flush(_test-output-buffered-file)
 538     # . . push args
 539     68/push  _test-output-buffered-file/imm32
 540     # . . call
 541     e8/call  flush/disp32
 542     # . . discard args
 543     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 544     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
 545     # . . push args
 546     68/push  "F - test-convert-passes-with-just-whitespace-through"/imm32
 547     68/push  "    "/imm32
 548     68/push  _test-output-stream/imm32
 549     # . . call
 550     e8/call  check-next-stream-line-equal/disp32
 551     # . . discard args
 552     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 553     # . epilog
 554     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 555     5d/pop-to-EBP
 556     c3/return
 557 
 558 test-convert-passes-segment-headers-through:
 559     # if a line starts with '==', pass it along unchanged
 560     # . prolog
 561     55/push-EBP
 562     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 563     # setup
 564     # . clear-stream(_test-input-stream)
 565     # . . push args
 566     68/push  _test-input-stream/imm32
 567     # . . call
 568     e8/call  clear-stream/disp32
 569     # . . discard args
 570     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 571     # . clear-stream(_test-input-buffered-file+4)
 572     # . . push args
 573     b8/copy-to-EAX  _test-input-buffered-file/imm32
 574     05/add-to-EAX  4/imm32
 575     50/push-EAX
 576     # . . call
 577     e8/call  clear-stream/disp32
 578     # . . discard args
 579     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 580     # . clear-stream(_test-output-stream)
 581     # . . push args
 582     68/push  _test-output-stream/imm32
 583     # . . call
 584     e8/call  clear-stream/disp32
 585     # . . discard args
 586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 587     # . clear-stream(_test-output-buffered-file+4)
 588     # . . push args
 589     b8/copy-to-EAX  _test-output-buffered-file/imm32
 590     05/add-to-EAX  4/imm32
 591     50/push-EAX
 592     # . . call
 593     e8/call  clear-stream/disp32
 594     # . . discard args
 595     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 596     # initialize input
 597     # . write(_test-input-stream, "== abcd")
 598     # . . push args
 599     68/push  "== abcd"/imm32
 600     68/push  _test-input-stream/imm32
 601     # . . call
 602     e8/call  write/disp32
 603     # . . discard args
 604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 605     # convert(_test-input-buffered-file, _test-output-buffered-file)
 606     # . . push args
 607     68/push  _test-output-buffered-file/imm32
 608     68/push  _test-input-buffered-file/imm32
 609     # . . call
 610     e8/call  convert/disp32
 611     # . . discard args
 612     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 613     # check that the line just passed through
 614     # . flush(_test-output-buffered-file)
 615     # . . push args
 616     68/push  _test-output-buffered-file/imm32
 617     # . . call
 618     e8/call  flush/disp32
 619     # . . discard args
 620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 621     # . check-stream-equal(_test-output-stream, "== abcd", msg)
 622     # . . push args
 623     68/push  "F - test-convert-passes-segment-headers-through"/imm32
 624     68/push  "== abcd"/imm32
 625     68/push  _test-output-stream/imm32
 626     # . . call
 627     e8/call  check-stream-equal/disp32
 628     # . . discard args
 629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 630     # . epilog
 631     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 632     5d/pop-to-EBP
 633     c3/return
 634 
 635 test-convert-in-data-segment:
 636     # correctly process lines in the data segment
 637     # . prolog
 638     55/push-EBP
 639     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 640     # setup
 641     # . clear-stream(_test-input-stream)
 642     # . . push args
 643     68/push  _test-input-stream/imm32
 644     # . . call
 645     e8/call  clear-stream/disp32
 646     # . . discard args
 647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 648     # . clear-stream(_test-input-buffered-file+4)
 649     # . . push args
 650     b8/copy-to-EAX  _test-input-buffered-file/imm32
 651     05/add-to-EAX  4/imm32
 652     50/push-EAX
 653     # . . call
 654     e8/call  clear-stream/disp32
 655     # . . discard args
 656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 657     # . clear-stream(_test-output-stream)
 658     # . . push args
 659     68/push  _test-output-stream/imm32
 660     # . . call
 661     e8/call  clear-stream/disp32
 662     # . . discard args
 663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 664     # . clear-stream(_test-output-buffered-file+4)
 665     # . . push args
 666     b8/copy-to-EAX  _test-output-buffered-file/imm32
 667     05/add-to-EAX  4/imm32
 668     50/push-EAX
 669     # . . call
 670     e8/call  clear-stream/disp32
 671     # . . discard args
 672     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 673     # initialize input
 674     #   == code
 675     #   == data
 676     #   3 4/imm32
 677     # . write(_test-input-stream, "== code")
 678     # . . push args
 679     68/push  "== code"/imm32
 680     68/push  _test-input-stream/imm32
 681     # . . call
 682     e8/call  write/disp32
 683     # . . discard args
 684     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 685     # . write(_test-input-stream, "\n")
 686     # . . push args
 687     68/push  Newline/imm32
 688     68/push  _test-input-stream/imm32
 689     # . . call
 690     e8/call  write/disp32
 691     # . . discard args
 692     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 693     # . write(_test-input-stream, "== data")
 694     # . . push args
 695     68/push  "== data"/imm32
 696     68/push  _test-input-stream/imm32
 697     # . . call
 698     e8/call  write/disp32
 699     # . . discard args
 700     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 701     # . write(_test-input-stream, "\n")
 702     # . . push args
 703     68/push  Newline/imm32
 704     68/push  _test-input-stream/imm32
 705     # . . call
 706     e8/call  write/disp32
 707     # . . discard args
 708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 709     # . write(_test-input-stream, "3 4/imm32")
 710     # . . push args
 711     68/push  "3 4/imm32"/imm32
 712     68/push  _test-input-stream/imm32
 713     # . . call
 714     e8/call  write/disp32
 715     # . . discard args
 716     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 717     # . write(_test-input-stream, "\n")
 718     # . . push args
 719     68/push  Newline/imm32
 720     68/push  _test-input-stream/imm32
 721     # . . call
 722     e8/call  write/disp32
 723     # . . discard args
 724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 725     # convert(_test-input-buffered-file, _test-output-buffered-file)
 726     # . . push args
 727     68/push  _test-output-buffered-file/imm32
 728     68/push  _test-input-buffered-file/imm32
 729     # . . call
 730     e8/call  convert/disp32
 731     # . . discard args
 732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 733     # check output
 734 +-- 34 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
 768     # . flush(_test-output-buffered-file)
 769     # . . push args
 770     68/push  _test-output-buffered-file/imm32
 771     # . . call
 772     e8/call  flush/disp32
 773     # . . discard args
 774     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 775     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
 776     # . . push args
 777     68/push  "F - test-convert-in-data-segment/0"/imm32
 778     68/push  "== code"/imm32
 779     68/push  _test-output-stream/imm32
 780     # . . call
 781     e8/call  check-next-stream-line-equal/disp32
 782     # . . discard args
 783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 784     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
 785     # . . push args
 786     68/push  "F - test-convert-in-data-segment/1"/imm32
 787     68/push  "== data"/imm32
 788     68/push  _test-output-stream/imm32
 789     # . . call
 790     e8/call  check-next-stream-line-equal/disp32
 791     # . . discard args
 792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 793     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 794     # . . push args
 795     68/push  "F - test-convert-in-data-segment/2"/imm32
 796     68/push  "03 04 00 00 00 "/imm32
 797     68/push  _test-output-stream/imm32
 798     # . . call
 799     e8/call  check-next-stream-line-equal/disp32
 800     # . . discard args
 801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 802     # . epilog
 803     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 804     5d/pop-to-EBP
 805     c3/return
 806 
 807 test-convert-code-and-data-segments:
 808     # correctly process lines in both code and data segments
 809     # . prolog
 810     55/push-EBP
 811     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 812     # setup
 813     # . clear-stream(_test-input-stream)
 814     # . . push args
 815     68/push  _test-input-stream/imm32
 816     # . . call
 817     e8/call  clear-stream/disp32
 818     # . . discard args
 819     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 820     # . clear-stream(_test-input-buffered-file+4)
 821     # . . push args
 822     b8/copy-to-EAX  _test-input-buffered-file/imm32
 823     05/add-to-EAX  4/imm32
 824     50/push-EAX
 825     # . . call
 826     e8/call  clear-stream/disp32
 827     # . . discard args
 828     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 829     # . clear-stream(_test-output-stream)
 830     # . . push args
 831     68/push  _test-output-stream/imm32
 832     # . . call
 833     e8/call  clear-stream/disp32
 834     # . . discard args
 835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 836     # . clear-stream(_test-output-buffered-file+4)
 837     # . . push args
 838     b8/copy-to-EAX  _test-output-buffered-file/imm32
 839     05/add-to-EAX  4/imm32
 840     50/push-EAX
 841     # . . call
 842     e8/call  clear-stream/disp32
 843     # . . discard args
 844     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 845     # initialize input
 846     #   == code
 847     #   e8/call 20/disp32
 848     #   68/push 0x20/imm8
 849     #   == data
 850     #   3 4/imm32
 851     # . write(_test-input-stream, "== code")
 852     # . . push args
 853     68/push  "== code"/imm32
 854     68/push  _test-input-stream/imm32
 855     # . . call
 856     e8/call  write/disp32
 857     # . . discard args
 858     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 859     # . write(_test-input-stream, "\n")
 860     # . . push args
 861     68/push  Newline/imm32
 862     68/push  _test-input-stream/imm32
 863     # . . call
 864     e8/call  write/disp32
 865     # . . discard args
 866     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 867     # . write(_test-input-stream, "e8/call 20/disp32")
 868     # . . push args
 869     68/push  "e8/call 20/disp32"/imm32
 870     68/push  _test-input-stream/imm32
 871     # . . call
 872     e8/call  write/disp32
 873     # . . discard args
 874     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 875     # . write(_test-input-stream, "\n")
 876     # . . push args
 877     68/push  Newline/imm32
 878     68/push  _test-input-stream/imm32
 879     # . . call
 880     e8/call  write/disp32
 881     # . . discard args
 882     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 883     # . write(_test-input-stream, "68/push 0x20/imm8")
 884     # . . push args
 885     68/push  "68/push 0x20/imm8"/imm32
 886     68/push  _test-input-stream/imm32
 887     # . . call
 888     e8/call  write/disp32
 889     # . . discard args
 890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 891     # . write(_test-input-stream, "\n")
 892     # . . push args
 893     68/push  Newline/imm32
 894     68/push  _test-input-stream/imm32
 895     # . . call
 896     e8/call  write/disp32
 897     # . . discard args
 898     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 899     # . write(_test-input-stream, "== data")
 900     # . . push args
 901     68/push  "== data"/imm32
 902     68/push  _test-input-stream/imm32
 903     # . . call
 904     e8/call  write/disp32
 905     # . . discard args
 906     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 907     # . write(_test-input-stream, "\n")
 908     # . . push args
 909     68/push  Newline/imm32
 910     68/push  _test-input-stream/imm32
 911     # . . call
 912     e8/call  write/disp32
 913     # . . discard args
 914     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 915     # . write(_test-input-stream, "3 4/imm32")
 916     # . . push args
 917     68/push  "3 4/imm32"/imm32
 918     68/push  _test-input-stream/imm32
 919     # . . call
 920     e8/call  write/disp32
 921     # . . discard args
 922     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 923     # . write(_test-input-stream, "\n")
 924     # . . push args
 925     68/push  Newline/imm32
 926     68/push  _test-input-stream/imm32
 927     # . . call
 928     e8/call  write/disp32
 929     # . . discard args
 930     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 931     # convert(_test-input-buffered-file, _test-output-buffered-file)
 932     # . . push args
 933     68/push  _test-output-buffered-file/imm32
 934     68/push  _test-input-buffered-file/imm32
 935     # . . call
 936     e8/call  convert/disp32
 937     # . . discard args
 938     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 939     # check output
 940     #   == code
 941     #   e8 20 00 00 00  # e8/call 20/disp32
 942     #   68 20  # 68/push 0x20/imm8
 943     #   == data
 944     #   03 04 00 00 00
 945 +-- 34 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
 979     # . flush(_test-output-buffered-file)
 980     # . . push args
 981     68/push  _test-output-buffered-file/imm32
 982     # . . call
 983     e8/call  flush/disp32
 984     # . . discard args
 985     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 986     # . check-next-stream-line-equal(_test-output-stream, "== code", msg)
 987     # . . push args
 988     68/push  "F - test-convert-code-and-data-segments/0"/imm32
 989     68/push  "== code"/imm32
 990     68/push  _test-output-stream/imm32
 991     # . . call
 992     e8/call  check-next-stream-line-equal/disp32
 993     # . . discard args
 994     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 995     # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
 996     # . . push args
 997     68/push  "F - test-convert-code-and-data-segments/1"/imm32
 998     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
 999     68/push  _test-output-stream/imm32
1000     # . . call
1001     e8/call  check-next-stream-line-equal/disp32
1002     # . . discard args
1003     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1004     # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
1005     # . . push args
1006     68/push  "F - test-convert-code-and-data-segments/2"/imm32
1007     68/push  "68 20  # 68/push 0x20/imm8"/imm32
1008     68/push  _test-output-stream/imm32
1009     # . . call
1010     e8/call  check-next-stream-line-equal/disp32
1011     # . . discard args
1012     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1013     # . check-next-stream-line-equal(_test-output-stream, "== data", msg)
1014     # . . push args
1015     68/push  "F - test-convert-code-and-data-segments/3"/imm32
1016     68/push  "== data"/imm32
1017     68/push  _test-output-stream/imm32
1018     # . . call
1019     e8/call  check-next-stream-line-equal/disp32
1020     # . . discard args
1021     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1022     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
1023     # . . push args
1024     68/push  "F - test-convert-code-and-data-segments/4"/imm32
1025     68/push  "03 04 00 00 00 "/imm32
1026     68/push  _test-output-stream/imm32
1027     # . . call
1028     e8/call  check-next-stream-line-equal/disp32
1029     # . . discard args
1030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1031     # . epilog
1032     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1033     5d/pop-to-EBP
1034     c3/return
1035 
1036 convert-data:  # line : (address stream byte), out : (address buffered-file) -> <void>
1037     # pseudocode:
1038     #   var word-slice = {0, 0}
1039     #   while true
1040     #     word-slice = next-word(line)
1041     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
1042     #       break  # skip emitting some whitespace
1043     #     if slice-starts-with?(word-slice, "#")      # comment
1044     #       write-slice(out, word-slice)
1045     #       break
1046     #     if slice-ends-with?(word-slice, ":")        # label
1047     #       write-stream-data(out, line)
1048     #       break
1049     #     if has-metadata?(word-slice, "imm32")
1050     #       emit(out, word-slice, 4)
1051     #     # disp32 is not permitted in data segments, and anything else is only a byte long
1052     #     else
1053     #       emit(out, word-slice, 1)
1054     #
1055     # . prolog
1056     55/push-EBP
1057     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1058     # . save registers
1059     50/push-EAX
1060     51/push-ECX
1061     52/push-EDX
1062     # var word-slice/ECX = {0, 0}
1063     68/push  0/imm32/end
1064     68/push  0/imm32/start
1065     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1066 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
1100 $convert-data:loop:
1101     # next-word(line, word-slice)
1102     # . . push args
1103     51/push-ECX
1104     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1105     # . . call
1106     e8/call  next-word/disp32
1107     # . . discard args
1108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1109 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
1159 $convert-data:check0:
1160     # if (slice-empty?(word-slice)) break
1161     # . EAX = slice-empty?(word-slice)
1162     # . . push args
1163     51/push-ECX
1164     # . . call
1165     e8/call  slice-empty?/disp32
1166     # . . discard args
1167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1168     # . if (EAX != 0) break
1169     3d/compare-EAX-and  0/imm32
1170     0f 85/jump-if-not-equal  $convert-data:break/disp32
1171 $convert-data:check-for-comment:
1172     # if (slice-starts-with?(word-slice, "#"))
1173     # . start/EDX = word-slice->start
1174     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
1175     # . c/EAX = *start
1176     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1177     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
1178     # . if (EAX != '#') goto next check
1179     3d/compare-EAX-and  0x23/imm32/hash
1180     75/jump-if-not-equal  $convert-data:check-for-label/disp8
1181 $convert-data:comment:
1182     # write-slice(out, word-slice)
1183     # . . push args
1184     51/push-ECX
1185     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1186     # . . call
1187     e8/call  write-slice/disp32
1188     # . . discard args
1189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1190     # break
1191     75/jump-if-not-equal  $convert-data:break/disp8
1192 $convert-data:check-for-label:
1193     # if (slice-ends-with?(word-slice, ":"))
1194     # . end/EDX = word-slice->end
1195     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
1196     # . c/EAX = *(end-1)
1197     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1198     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
1199     # . if (EAX != ':') goto next check
1200     3d/compare-EAX-and  0x3a/imm32/colon
1201     75/jump-if-not-equal  $convert-data:check-for-imm32/disp8
1202 $convert-data:label:
1203     # write-stream-data(out, line)
1204     # . . push args
1205     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1206     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1207     # . . call
1208     e8/call  write-stream-data/disp32
1209     # . . discard args
1210     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1211     # break
1212     75/jump-if-not-equal  $convert-data:break/disp8
1213 $convert-data:check-for-imm32:
1214     # if (has-metadata?(word-slice, "imm32"))
1215     # . EAX = has-metadata?(ECX, "imm32")
1216     # . . push args
1217     68/push  "imm32"/imm32
1218     51/push-ECX
1219     # . . call
1220     e8/call  has-metadata?/disp32
1221     # . . discard args
1222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1223     # . if (EAX == 0) process as a single byte
1224     3d/compare-EAX-and  0/imm32
1225     74/jump-if-equal  $convert-data:single-byte/disp8
1226 $convert-data:imm32:
1227     # emit(out, word-slice, 4)
1228     # . . push args
1229     68/push  4/imm32
1230     51/push-ECX
1231     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1232     # . . call
1233     e8/call  emit/disp32
1234     # . . discard args
1235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1236     e9/jump  $convert-data:loop/disp32
1237 $convert-data:single-byte:
1238     # emit(out, word-slice, 1)
1239     # . . push args
1240     68/push  1/imm32
1241     51/push-ECX
1242     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1243     # . . call
1244     e8/call  emit/disp32
1245     # . . discard args
1246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1247     e9/jump  $convert-data:loop/disp32
1248 $convert-data:break:
1249 $convert-data:end:
1250     # . reclaim locals
1251     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1252     # . restore registers
1253     5a/pop-to-EDX
1254     59/pop-to-ECX
1255     58/pop-to-EAX
1256     # . epilog
1257     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1258     5d/pop-to-EBP
1259     c3/return
1260 
1261 test-convert-data-passes-comments-through:
1262     # if a line starts with '#', pass it along unchanged
1263     # . prolog
1264     55/push-EBP
1265     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1266     # setup
1267     # . clear-stream(_test-input-stream)
1268     # . . push args
1269     68/push  _test-input-stream/imm32
1270     # . . call
1271     e8/call  clear-stream/disp32
1272     # . . discard args
1273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1274     # . clear-stream(_test-output-stream)
1275     # . . push args
1276     68/push  _test-output-stream/imm32
1277     # . . call
1278     e8/call  clear-stream/disp32
1279     # . . discard args
1280     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1281     # . clear-stream(_test-output-buffered-file+4)
1282     # . . push args
1283     b8/copy-to-EAX  _test-output-buffered-file/imm32
1284     05/add-to-EAX  4/imm32
1285     50/push-EAX
1286     # . . call
1287     e8/call  clear-stream/disp32
1288     # . . discard args
1289     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1290     # initialize input
1291     # . write(_test-input-stream, "# abcd")
1292     # . . push args
1293     68/push  "# abcd"/imm32
1294     68/push  _test-input-stream/imm32
1295     # . . call
1296     e8/call  write/disp32
1297     # . . discard args
1298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1299     # convert-data(_test-input-stream, _test-output-buffered-file)
1300     # . . push args
1301     68/push  _test-output-buffered-file/imm32
1302     68/push  _test-input-stream/imm32
1303     # . . call
1304     e8/call  convert-data/disp32
1305     # . . discard args
1306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1307     # check that the line just passed through
1308     # . flush(_test-output-buffered-file)
1309     # . . push args
1310     68/push  _test-output-buffered-file/imm32
1311     # . . call
1312     e8/call  flush/disp32
1313     # . . discard args
1314     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1315 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1349     # . check-stream-equal(_test-output-stream, "# abcd", msg)
1350     # . . push args
1351     68/push  "F - test-convert-data-passes-comments-through"/imm32
1352     68/push  "# abcd"/imm32
1353     68/push  _test-output-stream/imm32
1354     # . . call
1355     e8/call  check-stream-equal/disp32
1356     # . . discard args
1357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1358     # . epilog
1359     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1360     5d/pop-to-EBP
1361     c3/return
1362 
1363 test-convert-data-passes-labels-through:
1364     # if the first word ends with ':', pass along the entire line unchanged
1365     # . prolog
1366     55/push-EBP
1367     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1368     # setup
1369     # . clear-stream(_test-input-stream)
1370     # . . push args
1371     68/push  _test-input-stream/imm32
1372     # . . call
1373     e8/call  clear-stream/disp32
1374     # . . discard args
1375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1376     # . clear-stream(_test-output-stream)
1377     # . . push args
1378     68/push  _test-output-stream/imm32
1379     # . . call
1380     e8/call  clear-stream/disp32
1381     # . . discard args
1382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1383     # . clear-stream(_test-output-buffered-file+4)
1384     # . . push args
1385     b8/copy-to-EAX  _test-output-buffered-file/imm32
1386     05/add-to-EAX  4/imm32
1387     50/push-EAX
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     # initialize input
1393     # . write(_test-input-stream, "ab: # cd")
1394     # . . push args
1395     68/push  "ab: # cd"/imm32
1396     68/push  _test-input-stream/imm32
1397     # . . call
1398     e8/call  write/disp32
1399     # . . discard args
1400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1401     # convert-data(_test-input-stream, _test-output-buffered-file)
1402     # . . push args
1403     68/push  _test-output-buffered-file/imm32
1404     68/push  _test-input-stream/imm32
1405     # . . call
1406     e8/call  convert-data/disp32
1407     # . . discard args
1408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1409     # check that the line just passed through
1410     # . flush(_test-output-buffered-file)
1411     # . . push args
1412     68/push  _test-output-buffered-file/imm32
1413     # . . call
1414     e8/call  flush/disp32
1415     # . . discard args
1416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1417     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
1418     # . . push args
1419     68/push  "F - test-convert-data-passes-labels-through"/imm32
1420     68/push  "ab: # cd"/imm32
1421     68/push  _test-output-stream/imm32
1422     # . . call
1423     e8/call  check-stream-equal/disp32
1424     # . . discard args
1425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1426     # . epilog
1427     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1428     5d/pop-to-EBP
1429     c3/return
1430 
1431 test-convert-data-passes-names-through:
1432     # If a word is a valid name, just emit it unchanged.
1433     # Later phases will deal with it.
1434     # . prolog
1435     55/push-EBP
1436     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1437     # setup
1438     # . clear-stream(_test-input-stream)
1439     # . . push args
1440     68/push  _test-input-stream/imm32
1441     # . . call
1442     e8/call  clear-stream/disp32
1443     # . . discard args
1444     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1445     # . clear-stream(_test-output-stream)
1446     # . . push args
1447     68/push  _test-output-stream/imm32
1448     # . . call
1449     e8/call  clear-stream/disp32
1450     # . . discard args
1451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1452     # . clear-stream(_test-output-buffered-file+4)
1453     # . . push args
1454     b8/copy-to-EAX  _test-output-buffered-file/imm32
1455     05/add-to-EAX  4/imm32
1456     50/push-EAX
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     # initialize input
1462     # . write(_test-input-stream, "abcd/imm32")
1463     # . . push args
1464     68/push  "abcd/imm32"/imm32
1465     68/push  _test-input-stream/imm32
1466     # . . call
1467     e8/call  write/disp32
1468     # . . discard args
1469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1470     # convert-data(_test-input-stream, _test-output-buffered-file)
1471     # . . push args
1472     68/push  _test-output-buffered-file/imm32
1473     68/push  _test-input-stream/imm32
1474     # . . call
1475     e8/call  convert-data/disp32
1476     # . . discard args
1477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1478     # check that the line just passed through
1479     # . flush(_test-output-buffered-file)
1480     # . . push args
1481     68/push  _test-output-buffered-file/imm32
1482     # . . call
1483     e8/call  flush/disp32
1484     # . . discard args
1485     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1486     # . check-stream-equal(_test-output-stream, "abcd/imm32", msg)
1487     # . . push args
1488     68/push  "F - test-convert-data-passes-names-through"/imm32
1489     68/push  "abcd/imm32 "/imm32
1490     68/push  _test-output-stream/imm32
1491     # . . call
1492     e8/call  check-stream-equal/disp32
1493     # . . discard args
1494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1495     # . epilog
1496     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1497     5d/pop-to-EBP
1498     c3/return
1499 
1500 test-convert-data-handles-imm32:
1501     # If a word has the /imm32 metadata, emit it in 4 bytes.
1502     # . prolog
1503     55/push-EBP
1504     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1505     # setup
1506     # . clear-stream(_test-input-stream)
1507     # . . push args
1508     68/push  _test-input-stream/imm32
1509     # . . call
1510     e8/call  clear-stream/disp32
1511     # . . discard args
1512     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1513     # . clear-stream(_test-output-stream)
1514     # . . push args
1515     68/push  _test-output-stream/imm32
1516     # . . call
1517     e8/call  clear-stream/disp32
1518     # . . discard args
1519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1520     # . clear-stream(_test-output-buffered-file+4)
1521     # . . push args
1522     b8/copy-to-EAX  _test-output-buffered-file/imm32
1523     05/add-to-EAX  4/imm32
1524     50/push-EAX
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     # initialize input
1530     # . write(_test-input-stream, "30/imm32")
1531     # . . push args
1532     68/push  "30/imm32"/imm32
1533     68/push  _test-input-stream/imm32
1534     # . . call
1535     e8/call  write/disp32
1536     # . . discard args
1537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1538     # convert-data(_test-input-stream, _test-output-buffered-file)
1539     # . . push args
1540     68/push  _test-output-buffered-file/imm32
1541     68/push  _test-input-stream/imm32
1542     # . . call
1543     e8/call  convert-data/disp32
1544     # . . discard args
1545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1546     # check that 4 bytes were written
1547     # . flush(_test-output-buffered-file)
1548     # . . push args
1549     68/push  _test-output-buffered-file/imm32
1550     # . . call
1551     e8/call  flush/disp32
1552     # . . discard args
1553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1554     # . check-stream-equal(_test-output-stream, "30 00 00 00 ", msg)
1555     # . . push args
1556     68/push  "F - test-convert-data-handles-imm32"/imm32
1557     68/push  "30 00 00 00 "/imm32
1558     68/push  _test-output-stream/imm32
1559     # . . call
1560     e8/call  check-stream-equal/disp32
1561     # . . discard args
1562     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1563     # . epilog
1564     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1565     5d/pop-to-EBP
1566     c3/return
1567 
1568 test-convert-data-handles-single-byte:
1569     # Any metadata but /imm32 will emit a single byte.
1570     # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
1571     # . prolog
1572     55/push-EBP
1573     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1574     # setup
1575     # . clear-stream(_test-input-stream)
1576     # . . push args
1577     68/push  _test-input-stream/imm32
1578     # . . call
1579     e8/call  clear-stream/disp32
1580     # . . discard args
1581     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1582     # . clear-stream(_test-output-stream)
1583     # . . push args
1584     68/push  _test-output-stream/imm32
1585     # . . call
1586     e8/call  clear-stream/disp32
1587     # . . discard args
1588     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1589     # . clear-stream(_test-output-buffered-file+4)
1590     # . . push args
1591     b8/copy-to-EAX  _test-output-buffered-file/imm32
1592     05/add-to-EAX  4/imm32
1593     50/push-EAX
1594     # . . call
1595     e8/call  clear-stream/disp32
1596     # . . discard args
1597     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1598     # initialize input
1599     # . write(_test-input-stream, "30/imm16")
1600     # . . push args
1601     68/push  "30/imm16"/imm32
1602     68/push  _test-input-stream/imm32
1603     # . . call
1604     e8/call  write/disp32
1605     # . . discard args
1606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1607     # convert-data(_test-input-stream, _test-output-buffered-file)
1608     # . . push args
1609     68/push  _test-output-buffered-file/imm32
1610     68/push  _test-input-stream/imm32
1611     # . . call
1612     e8/call  convert-data/disp32
1613     # . . discard args
1614     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1615     # check that a single byte was written (imm16 is not a valid operand type)
1616     # . flush(_test-output-buffered-file)
1617     # . . push args
1618     68/push  _test-output-buffered-file/imm32
1619     # . . call
1620     e8/call  flush/disp32
1621     # . . discard args
1622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1623     # . check-stream-equal(_test-output-stream, "30 ", msg)
1624     # . . push args
1625     68/push  "F - test-convert-data-handles-single-byte"/imm32
1626     68/push  "30 "/imm32
1627     68/push  _test-output-stream/imm32
1628     # . . call
1629     e8/call  check-stream-equal/disp32
1630     # . . discard args
1631     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1632     # . epilog
1633     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1634     5d/pop-to-EBP
1635     c3/return
1636 
1637 test-convert-data-multiple-bytes:
1638     # Multiple single-byte words in input stream get processed one by one.
1639     # . prolog
1640     55/push-EBP
1641     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1642     # setup
1643     # . clear-stream(_test-input-stream)
1644     # . . push args
1645     68/push  _test-input-stream/imm32
1646     # . . call
1647     e8/call  clear-stream/disp32
1648     # . . discard args
1649     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1650     # . clear-stream(_test-output-stream)
1651     # . . push args
1652     68/push  _test-output-stream/imm32
1653     # . . call
1654     e8/call  clear-stream/disp32
1655     # . . discard args
1656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1657     # . clear-stream(_test-output-buffered-file+4)
1658     # . . push args
1659     b8/copy-to-EAX  _test-output-buffered-file/imm32
1660     05/add-to-EAX  4/imm32
1661     50/push-EAX
1662     # . . call
1663     e8/call  clear-stream/disp32
1664     # . . discard args
1665     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1666     # initialize input
1667     # . write(_test-input-stream, "1 2")
1668     # . . push args
1669     68/push  "1 2"/imm32
1670     68/push  _test-input-stream/imm32
1671     # . . call
1672     e8/call  write/disp32
1673     # . . discard args
1674     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1675     # convert-data(_test-input-stream, _test-output-buffered-file)
1676     # . . push args
1677     68/push  _test-output-buffered-file/imm32
1678     68/push  _test-input-stream/imm32
1679     # . . call
1680     e8/call  convert-data/disp32
1681     # . . discard args
1682     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1683     # check output
1684     # . flush(_test-output-buffered-file)
1685     # . . push args
1686     68/push  _test-output-buffered-file/imm32
1687     # . . call
1688     e8/call  flush/disp32
1689     # . . discard args
1690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1691     # . check-stream-equal(_test-output-stream, "01 02 ", msg)
1692     # . . push args
1693     68/push  "F - test-convert-data-multiple-bytes"/imm32
1694     68/push  "01 02 "/imm32
1695     68/push  _test-output-stream/imm32
1696     # . . call
1697     e8/call  check-stream-equal/disp32
1698     # . . discard args
1699     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1700     # . epilog
1701     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1702     5d/pop-to-EBP
1703     c3/return
1704 
1705 test-convert-data-byte-then-name:
1706     # Single-byte word followed by valid name get processed one by one.
1707     # . prolog
1708     55/push-EBP
1709     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1710     # setup
1711     # . clear-stream(_test-input-stream)
1712     # . . push args
1713     68/push  _test-input-stream/imm32
1714     # . . call
1715     e8/call  clear-stream/disp32
1716     # . . discard args
1717     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1718     # . clear-stream(_test-output-stream)
1719     # . . push args
1720     68/push  _test-output-stream/imm32
1721     # . . call
1722     e8/call  clear-stream/disp32
1723     # . . discard args
1724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1725     # . clear-stream(_test-output-buffered-file+4)
1726     # . . push args
1727     b8/copy-to-EAX  _test-output-buffered-file/imm32
1728     05/add-to-EAX  4/imm32
1729     50/push-EAX
1730     # . . call
1731     e8/call  clear-stream/disp32
1732     # . . discard args
1733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1734     # initialize input
1735     # . write(_test-input-stream, "30 abcd/o")
1736     # . . push args
1737     68/push  "30 abcd/o"/imm32
1738     68/push  _test-input-stream/imm32
1739     # . . call
1740     e8/call  write/disp32
1741     # . . discard args
1742     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1743     # convert-data(_test-input-stream, _test-output-buffered-file)
1744     # . . push args
1745     68/push  _test-output-buffered-file/imm32
1746     68/push  _test-input-stream/imm32
1747     # . . call
1748     e8/call  convert-data/disp32
1749     # . . discard args
1750     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1751     # check output
1752     # . flush(_test-output-buffered-file)
1753     # . . push args
1754     68/push  _test-output-buffered-file/imm32
1755     # . . call
1756     e8/call  flush/disp32
1757     # . . discard args
1758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1759     # . check-stream-equal(_test-output-stream, "30 abcd/o ", msg)
1760     # . . push args
1761     68/push  "F - test-convert-data-byte-then-name"/imm32
1762     68/push  "30 abcd/o "/imm32
1763     68/push  _test-output-stream/imm32
1764     # . . call
1765     e8/call  check-stream-equal/disp32
1766     # . . discard args
1767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1768     # . epilog
1769     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1770     5d/pop-to-EBP
1771     c3/return
1772 
1773 test-convert-data-multiple-words:
1774     # Multiple words in input stream get processed one by one.
1775     # . prolog
1776     55/push-EBP
1777     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1778     # setup
1779     # . clear-stream(_test-input-stream)
1780     # . . push args
1781     68/push  _test-input-stream/imm32
1782     # . . call
1783     e8/call  clear-stream/disp32
1784     # . . discard args
1785     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1786     # . clear-stream(_test-output-stream)
1787     # . . push args
1788     68/push  _test-output-stream/imm32
1789     # . . call
1790     e8/call  clear-stream/disp32
1791     # . . discard args
1792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1793     # . clear-stream(_test-output-buffered-file+4)
1794     # . . push args
1795     b8/copy-to-EAX  _test-output-buffered-file/imm32
1796     05/add-to-EAX  4/imm32
1797     50/push-EAX
1798     # . . call
1799     e8/call  clear-stream/disp32
1800     # . . discard args
1801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1802     # initialize input
1803     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
1804     # . . push args
1805     68/push  "30 abcd/o 42e1/imm32"/imm32
1806     68/push  _test-input-stream/imm32
1807     # . . call
1808     e8/call  write/disp32
1809     # . . discard args
1810     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1811     # convert-data(_test-input-stream, _test-output-buffered-file)
1812     # . . push args
1813     68/push  _test-output-buffered-file/imm32
1814     68/push  _test-input-stream/imm32
1815     # . . call
1816     e8/call  convert-data/disp32
1817     # . . discard args
1818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1819     # check output
1820     # . flush(_test-output-buffered-file)
1821     # . . push args
1822     68/push  _test-output-buffered-file/imm32
1823     # . . call
1824     e8/call  flush/disp32
1825     # . . discard args
1826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1827 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1861     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 ", msg)
1862     # . . push args
1863     68/push  "F - test-convert-data-multiple-words"/imm32
1864     68/push  "30 abcd/o e1 42 00 00 "/imm32
1865     68/push  _test-output-stream/imm32
1866     # . . call
1867     e8/call  check-stream-equal/disp32
1868     # . . discard args
1869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1870     # . epilog
1871     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1872     5d/pop-to-EBP
1873     c3/return
1874 
1875 test-convert-data-trailing-comment:
1876     # Trailing comments in data segment get appropriately ignored.
1877     # . prolog
1878     55/push-EBP
1879     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1880     # setup
1881     # . clear-stream(_test-input-stream)
1882     # . . push args
1883     68/push  _test-input-stream/imm32
1884     # . . call
1885     e8/call  clear-stream/disp32
1886     # . . discard args
1887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1888     # . clear-stream(_test-output-stream)
1889     # . . push args
1890     68/push  _test-output-stream/imm32
1891     # . . call
1892     e8/call  clear-stream/disp32
1893     # . . discard args
1894     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1895     # . clear-stream(_test-output-buffered-file+4)
1896     # . . push args
1897     b8/copy-to-EAX  _test-output-buffered-file/imm32
1898     05/add-to-EAX  4/imm32
1899     50/push-EAX
1900     # . . call
1901     e8/call  clear-stream/disp32
1902     # . . discard args
1903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1904     # initialize input
1905     # . write(_test-input-stream, "30/imm32 # comment")
1906     # . . push args
1907     68/push  "30/imm32 # comment"/imm32
1908     68/push  _test-input-stream/imm32
1909     # . . call
1910     e8/call  write/disp32
1911     # . . discard args
1912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1913     # convert-data(_test-input-stream, _test-output-buffered-file)
1914     # . . push args
1915     68/push  _test-output-buffered-file/imm32
1916     68/push  _test-input-stream/imm32
1917     # . . call
1918     e8/call  convert-data/disp32
1919     # . . discard args
1920     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1921     # check output
1922     # . flush(_test-output-buffered-file)
1923     # . . push args
1924     68/push  _test-output-buffered-file/imm32
1925     # . . call
1926     e8/call  flush/disp32
1927     # . . discard args
1928     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1929 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1963     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
1964     # . . push args
1965     68/push  "F - test-convert-data-trailing-comment"/imm32
1966     68/push  "30 00 00 00 # comment"/imm32
1967     68/push  _test-output-stream/imm32
1968     # . . call
1969     e8/call  check-stream-equal/disp32
1970     # . . discard args
1971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1972     # . epilog
1973     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1974     5d/pop-to-EBP
1975     c3/return
1976 
1977 # pack an instruction, following the C++ version
1978 #
1979 # zero error handling at the moment (continuing to rely on the C++ version for that):
1980 #   missing fields are always 0-filled
1981 #   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.
1982 #   may pick up any of duplicate operands in an instruction
1983 #   silently drop extraneous operands
1984 #   unceremoniously abort on non-numeric operands except disp or imm
1985 #   opcodes must be lowercase and zero padded
1986 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
1987 convert-instruction:  # line : (address stream byte), out : (address buffered-file) -> <void>
1988     # pseudocode:
1989     #   # some early exits
1990     #   var word-slice = next-word(line)
1991     #   if slice-empty?(word-slice)
1992     #     write-stream-data(out, line)
1993     #     return
1994     #   if slice-starts-with?(word-slice, "#")
1995     #     write-stream-data(out, line)
1996     #     return
1997     #   if slice-ends-with?(word-slice, ":")
1998     #     write-stream-data(out, line)
1999     #     return
2000     #   # really convert
2001     #   emit-opcodes(line, out)
2002     #   emit-modrm(line, out)
2003     #   emit-sib(line, out)
2004     #   emit-disp(line, out)
2005     #   emit-imm(line, out)
2006     #   emit-line-in-comment(line, out)
2007     #
2008     # . prolog
2009     55/push-EBP
2010     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2011     # . save registers
2012     50/push-EAX
2013     51/push-ECX
2014     52/push-EDX
2015     # var word-slice/ECX = {0, 0}
2016     68/push  0/imm32/end
2017     68/push  0/imm32/start
2018     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2019     # next-word(line, word-slice)
2020     # . . push args
2021     51/push-ECX
2022     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2023     # . . call
2024     e8/call  next-word/disp32
2025     # . . discard args
2026     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2027 $convert-instruction:check0:
2028     # if (slice-empty?(word-slice)) break
2029     # . EAX = slice-empty?(word-slice)
2030     # . . push args
2031     51/push-ECX
2032     # . . call
2033     e8/call  slice-empty?/disp32
2034     # . . discard args
2035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2036     # . if (EAX != 0) pass through
2037     3d/compare-EAX-and  0/imm32
2038     75/jump-if-not-equal  $convert-instruction:pass-through/disp8
2039 $convert-instruction:check1:
2040     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
2041     # . start/EDX = word-slice->start
2042     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
2043     # . c/EAX = *start
2044     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2045     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
2046     # . if (EAX == '#') pass through
2047     3d/compare-EAX-and  0x23/imm32/hash
2048     74/jump-if-equal  $convert-instruction:pass-through/disp8
2049 $convert-instruction:check2:
2050     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
2051     # . end/EDX = word-slice->end
2052     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
2053     # . c/EAX = *(end-1)
2054     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2055     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
2056     # . if (EAX == ':') pass through
2057     3d/compare-EAX-and  0x3a/imm32/colon
2058     75/jump-if-not-equal  $convert-instruction:really-convert/disp8
2059 $convert-instruction:pass-through:
2060     # write-stream-data(out, line)
2061     # . . push args
2062     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2063     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2064     # . . call
2065     e8/call  write-stream-data/disp32
2066     # . . discard args
2067     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2068     # return
2069     eb/jump  $convert-instruction:end/disp8
2070 $convert-instruction:really-convert:
2071     # emit-opcodes(line, out)
2072     # . . push args
2073     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
2074     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2075     # . . call
2076     e8/call  emit-opcodes/disp32
2077     # . . discard args
2078     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2079     # emit-modrm(line, out)
2080     # . . push args
2081     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
2082     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2083     # . . call
2084     e8/call  emit-modrm/disp32
2085     # . . discard args
2086     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2087     # emit-sib(line, out)
2088     # . . push args
2089     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
2090     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2091     # . . call
2092     e8/call  emit-sib/disp32
2093     # . . discard args
2094     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2095     # emit-disp(line, out)
2096     # . . push args
2097     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
2098     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2099     # . . call
2100     e8/call  emit-disp/disp32
2101     # . . discard args
2102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2103     # emit-imm(line, out)
2104     # . . push args
2105     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
2106     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2107     # . . call
2108     e8/call  emit-imm/disp32
2109     # . . discard args
2110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2111     # emit-line-in-comment(line, out)
2112     # . . push args
2113     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+8)
2114     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2115     # . . call
2116     e8/call  emit-line-in-comment/disp32
2117     # . . discard args
2118     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2119 $convert-instruction:end:
2120     # . restore locals
2121     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2122     # . restore registers
2123     5a/pop-to-EDX
2124     59/pop-to-ECX
2125     58/pop-to-EAX
2126     # . epilog
2127     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2128     5d/pop-to-EBP
2129     c3/return
2130 
2131 emit-opcodes:  # line : (address stream byte), out : (address buffered-file) -> <void>
2132     # opcodes occupy 1-3 bytes:
2133     #   xx
2134     #   0f xx
2135     #   f2 xx
2136     #   f3 xx
2137     #   f2 0f xx
2138     #   f3 0f xx
2139     #
2140     # pseudocode:
2141     #   rewind-stream(line)
2142     #
2143     #   var op1 = next-word(line)
2144     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
2145     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
2146     #   write-slice(out, op1)
2147     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3")
2148     #     return
2149     #
2150     #   var op2 = next-word(line)
2151     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
2152     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
2153     #   write-slice(out, op2)
2154     #   if slice-equal?(op1, "0f")
2155     #     return
2156     #   if !slice-equal?(op2, "0f")
2157     #     return
2158     #
2159     #   var op3 = next-word(line)
2160     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
2161     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
2162     #   write-slice(out, op3)
2163     #
2164     # . prolog
2165     55/push-EBP
2166     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2167     # . save registers
2168     50/push-EAX
2169     51/push-ECX
2170     52/push-EDX
2171     53/push-EBX
2172     # var op1/ECX = {0, 0}
2173     68/push  0/imm32/end
2174     68/push  0/imm32/start
2175     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2176     # var op2/EDX = {0, 0}
2177     68/push  0/imm32/end
2178     68/push  0/imm32/start
2179     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
2180     # rewind-stream(line)
2181     # . . push args
2182     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2183     # . . call
2184     e8/call  rewind-stream/disp32
2185     # . . discard args
2186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2187 $emit-opcodes:op1:
2188     # next-word(line, op1)
2189     # . . push args
2190     51/push-ECX
2191     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2192     # . . call
2193     e8/call  next-word/disp32
2194     # . . discard args
2195     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2196     # if (slice-empty?(op1)) return
2197     # . EAX = slice-empty?(op1)
2198     # . . push args
2199     51/push-ECX
2200     # . . call
2201     e8/call  slice-empty?/disp32
2202     # . . discard args
2203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2204     # . if (EAX != 0) return
2205     3d/compare-EAX-and  0/imm32
2206     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2207     # if (slice-starts-with?(op1, "#")) return
2208     # . start/EBX = op1->start
2209     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # copy *ECX to EBX
2210     # . c/EAX = *start
2211     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2212     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2213     # . if (EAX == '#') return
2214     3d/compare-EAX-and  0x23/imm32/hash
2215     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2216     # op1 = next-token-from-slice(op1->start, op1->end, '/')
2217     # . . push args
2218     51/push-ECX
2219     68/push  0x2f/imm32/slash
2220     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
2221     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
2222     # . . call
2223     e8/call  next-token-from-slice/disp32
2224     # . . discard args
2225     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2226     # write-slice(out, op1)
2227     # . . push args
2228     51/push-ECX
2229     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2230     # . . call
2231     e8/call  write-slice/disp32
2232     # . . discard args
2233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2234     # write-buffered(out, " ")
2235     # . . push args
2236     68/push  " "/imm32
2237     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2238     # . . call
2239     e8/call  write-buffered/disp32
2240     # . . discard args
2241     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2242     # if (slice-equal?(op1, "0f")) goto op2
2243     # . EAX = slice-equal?(op1, "0f")
2244     # . . push args
2245     68/push  "0f"/imm32
2246     51/push-ECX
2247     # . . call
2248     e8/call  slice-equal?/disp32
2249     # . . discard args
2250     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2251     # . if (EAX != 0) goto op2
2252     3d/compare-EAX-and  0/imm32
2253     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2254     # if (slice-equal?(op1, "f2")) goto op2
2255     # . EAX = slice-equal?(op1, "f2")
2256     # . . push args
2257     68/push  "f2"/imm32
2258     51/push-ECX
2259     # . . call
2260     e8/call  slice-equal?/disp32
2261     # . . discard args
2262     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2263     # . if (EAX != 0) goto op2
2264     3d/compare-EAX-and  0/imm32
2265     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2266     # if (slice-equal?(op1, "f3")) goto op2
2267     # . EAX = slice-equal?(op1, "f3")
2268     # . . push args
2269     68/push  "f3"/imm32
2270     51/push-ECX
2271     # . . call
2272     e8/call  slice-equal?/disp32
2273     # . . discard args
2274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2275     # . if (EAX != 0) goto op2
2276     3d/compare-EAX-and  0/imm32
2277     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2278     # otherwise return
2279     e9/jump  $emit-opcodes:end/disp32
2280 $emit-opcodes:op2:
2281     # next-word(line, op2)
2282     # . . push args
2283     52/push-EDX
2284     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2285     # . . call
2286     e8/call  next-word/disp32
2287     # . . discard args
2288     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2289     # if (slice-empty?(op2)) return
2290     # . EAX = slice-empty?(op2)
2291     # . . push args
2292     52/push-EDX
2293     # . . call
2294     e8/call  slice-empty?/disp32
2295     # . . discard args
2296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2297     # . if (EAX != 0) return
2298     3d/compare-EAX-and  0/imm32
2299     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2300     # if (slice-starts-with?(op2, "#")) return
2301     # . start/EBX = op2->start
2302     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
2303     # . c/EAX = *start
2304     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2305     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2306     # . if (EAX == '#') return
2307     3d/compare-EAX-and  0x23/imm32/hash
2308     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2309     # op2 = next-token-from-slice(op2->start, op2->end, '/')
2310     # . . push args
2311     52/push-EDX
2312     68/push  0x2f/imm32/slash
2313     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
2314     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
2315     # . . call
2316     e8/call  next-token-from-slice/disp32
2317     # . . discard args
2318     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2319     # write-slice(out, op2)
2320     # . . push args
2321     52/push-EDX
2322     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2323     # . . call
2324     e8/call  write-slice/disp32
2325     # . . discard args
2326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2327     # write-buffered(out, " ")
2328     # . . push args
2329     68/push  " "/imm32
2330     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2331     # . . call
2332     e8/call  write-buffered/disp32
2333     # . . discard args
2334     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2335     # if (slice-equal?(op1, "0f")) return
2336     # . EAX = slice-equal?(op1, "0f")
2337     # . . push args
2338     68/push  "0f"/imm32
2339     51/push-ECX
2340     # . . call
2341     e8/call  slice-equal?/disp32
2342     # . . discard args
2343     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2344     # . if (EAX != 0) return
2345     3d/compare-EAX-and  0/imm32
2346     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2347     # if (!slice-equal?(op2, "0f")) return
2348     # . EAX = slice-equal?(op2, "0f")
2349     # . . push args
2350     68/push  "0f"/imm32
2351     52/push-EDX
2352     # . . call
2353     e8/call  slice-equal?/disp32
2354     # . . discard args
2355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2356     # . if (EAX == 0) return
2357     3d/compare-EAX-and  0/imm32
2358     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2359 $emit-opcodes:op3:
2360     # next-word(line, op3)  # reuse op2/EDX
2361     # . . push args
2362     52/push-EDX
2363     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2364     # . . call
2365     e8/call  next-word/disp32
2366     # . . discard args
2367     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2368     # if (slice-empty?(op3)) return
2369     # . EAX = slice-empty?(op3)
2370     # . . push args
2371     52/push-EDX
2372     # . . call
2373     e8/call  slice-empty?/disp32
2374     # . . discard args
2375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2376     # . if (EAX != 0) return
2377     3d/compare-EAX-and  0/imm32
2378     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2379     # if (slice-starts-with?(op3, "#")) return
2380     # . start/EBX = op2->start
2381     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
2382     # . c/EAX = *start
2383     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2384     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2385     # . if (EAX == '#') return
2386     3d/compare-EAX-and  0x23/imm32/hash
2387     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2388     # op3 = next-token-from-slice(op3->start, op3->end, '/')
2389     # . . push args
2390     52/push-EDX
2391     68/push  0x2f/imm32/slash
2392     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
2393     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
2394     # . . call
2395     e8/call  next-token-from-slice/disp32
2396     # . . discard args
2397     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2398     # write-slice(out, op3)
2399     # . . push args
2400     52/push-EDX
2401     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2402     # . . call
2403     e8/call  write-slice/disp32
2404     # . . discard args
2405     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2406     # write-buffered(out, " ")
2407     # . . push args
2408     68/push  " "/imm32
2409     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2410     # . . call
2411     e8/call  write-buffered/disp32
2412     # . . discard args
2413     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2414 $emit-opcodes:end:
2415     # . restore locals
2416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2417     # . restore registers
2418     5b/pop-to-EBX
2419     5a/pop-to-EDX
2420     59/pop-to-ECX
2421     58/pop-to-EAX
2422     # . epilog
2423     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2424     5d/pop-to-EBP
2425     c3/return
2426 
2427 emit-modrm:  # line : (address stream byte), out : (address buffered-file) -> <void>
2428     # pseudocode:
2429     #   rewind-stream(line)
2430     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
2431     #   var word-slice = {0, 0}
2432     #   while true
2433     #     word-slice = next-word(line)
2434     #     if (slice-empty?(word-slice)) break
2435     #     if (slice-starts-with?(word-slice, "#")) break
2436     #     if (has-metadata?(word-slice, "mod"))
2437     #       mod = parse-hex-int(next-token-from-slice(word-slice, "/"))
2438     #       has-modrm? = true
2439     #     else if (has-metadata?(word-slice, "rm32"))
2440     #       rm32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
2441     #       has-modrm? = true
2442     #     else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop"))
2443     #       r32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
2444     #       has-modrm? = true
2445     #   if has-modrm?
2446     #     var modrm = mod & 0b11
2447     #     modrm <<= 2
2448     #     modrm |= r32 & 0b111
2449     #     modrm <<= 3
2450     #     modrm |= rm32 & 0b111
2451     #     emit-hex(out, modrm, 1)
2452     #
2453     # . prolog
2454     55/push-EBP
2455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2456     # . save registers
2457     50/push-EAX
2458     51/push-ECX
2459     52/push-EDX
2460     53/push-EBX
2461     56/push-ESI
2462     57/push-EDI
2463     # var word-slice/ECX = {0, 0}
2464     68/push  0/imm32/end
2465     68/push  0/imm32/start
2466     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2467     # var has-modrm?/EDX = false
2468     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
2469     # var mod/EBX = 0
2470     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2471     # var rm32/ESI = 0
2472     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
2473     # var r32/EDI = 0
2474     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
2475     # rewind-stream(line)
2476     # . . push args
2477     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2478     # . . call
2479     e8/call  rewind-stream/disp32
2480     # . . discard args
2481     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2482 $emit-modrm:loop:
2483 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2517     # next-word(line, word-slice)
2518     # . . push args
2519     51/push-ECX
2520     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2521     # . . call
2522     e8/call  next-word/disp32
2523     # . . discard args
2524     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2525 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2575 $emit-modrm:check0:
2576     # if (slice-empty?(word-slice)) break
2577     # . EAX = slice-empty?(word-slice)
2578     # . . push args
2579     51/push-ECX
2580     # . . call
2581     e8/call  slice-empty?/disp32
2582     # . . discard args
2583     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2584     # . if (EAX != 0) pass through
2585     3d/compare-EAX-and  0/imm32
2586     0f 85/jump-if-not-equal  $emit-modrm:break/disp32
2587 $emit-modrm:check1:
2588     # if (slice-starts-with?(word-slice, "#")) break
2589     # . spill EDX
2590     52/push-EDX
2591     # . start/EDX = word-slice->start
2592     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
2593     # . c/EAX = *start
2594     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2595     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
2596     # . restore EDX
2597     5a/pop-to-EDX
2598     # . if (EAX == '#') pass through
2599     3d/compare-EAX-and  0x23/imm32/hash
2600     0f 84/jump-if-equal  $emit-modrm:break/disp32
2601 $emit-modrm:check-for-mod:
2602     # if (has-metadata?(word-slice, "mod"))
2603     # . EAX = has-metadata?(ECX, "mod")
2604     # . . push args
2605     68/push  "mod"/imm32
2606     51/push-ECX
2607     # . . call
2608     e8/call  has-metadata?/disp32
2609     # . . discard args
2610     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2611     # . if (EAX == 0) goto next check
2612     3d/compare-EAX-and  0/imm32
2613     74/jump-if-equal  $emit-modrm:check-for-rm32/disp8
2614 $emit-modrm:mod:
2615     # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2616     # . EAX = parse-datum-of-word(word-slice)
2617     # . . push args
2618     51/push-ECX
2619     # . . call
2620     e8/call  parse-datum-of-word/disp32
2621     # . . discard args
2622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2623     # . mod = EAX
2624     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
2625     # has-modrm? = true
2626     ba/copy-to-EDX  1/imm32/true
2627     # continue
2628     e9/jump  $emit-modrm:loop/disp32
2629 $emit-modrm:check-for-rm32:
2630     # if (has-metadata?(word-slice, "rm32"))
2631     # . EAX = has-metadata?(ECX, "rm32")
2632     # . . push args
2633     68/push  "rm32"/imm32
2634     51/push-ECX
2635     # . . call
2636     e8/call  has-metadata?/disp32
2637     # . . discard args
2638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2639     # . if (EAX == 0) goto next check
2640     3d/compare-EAX-and  0/imm32
2641     74/jump-if-equal  $emit-modrm:check-for-r32/disp8
2642 $emit-modrm:rm32:
2643     # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2644     # . EAX = parse-datum-of-word(word-slice)
2645     # . . push args
2646     51/push-ECX
2647     # . . call
2648     e8/call  parse-datum-of-word/disp32
2649     # . . discard args
2650     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2651     # . rm32 = EAX
2652     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
2653     # has-modrm? = true
2654     ba/copy-to-EDX  1/imm32/true
2655     # continue
2656     e9/jump  $emit-modrm:loop/disp32
2657 $emit-modrm:check-for-r32:
2658     # if (has-metadata?(word-slice, "r32"))
2659     # . EAX = has-metadata?(ECX, "r32")
2660     # . . push args
2661     68/push  "r32"/imm32
2662     51/push-ECX
2663     # . . call
2664     e8/call  has-metadata?/disp32
2665     # . . discard args
2666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2667     # . if (EAX == 0) goto next check
2668     3d/compare-EAX-and  0/imm32
2669     74/jump-if-equal  $emit-modrm:check-for-subop/disp8
2670 $emit-modrm:r32:
2671     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2672     # . EAX = parse-datum-of-word(word-slice)
2673     # . . push args
2674     51/push-ECX
2675     # . . call
2676     e8/call  parse-datum-of-word/disp32
2677     # . . discard args
2678     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2679     # . r32 = EAX
2680     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
2681     # has-modrm? = true
2682     ba/copy-to-EDX  1/imm32/true
2683     # continue
2684     e9/jump  $emit-modrm:loop/disp32
2685 $emit-modrm:check-for-subop:
2686     # if (has-metadata?(word-slice, "subop"))
2687     # . EAX = has-metadata?(ECX, "subop")
2688     # . . push args
2689     68/push  "subop"/imm32
2690     51/push-ECX
2691     # . . call
2692     e8/call  has-metadata?/disp32
2693     # . . discard args
2694     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2695     # . if (EAX == 0) loop
2696     3d/compare-EAX-and  0/imm32
2697     0f 84/jump-if-equal  $emit-modrm:loop/disp32
2698 $emit-modrm:subop:
2699     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2700     # . EAX = parse-datum-of-word(word-slice)
2701     # . . push args
2702     51/push-ECX
2703     # . . call
2704     e8/call  parse-datum-of-word/disp32
2705     # . . discard args
2706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2707     # . r32 = EAX
2708     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
2709     # has-modrm? = true
2710     ba/copy-to-EDX  1/imm32/true
2711     # continue
2712     e9/jump  $emit-modrm:loop/disp32
2713 $emit-modrm:break:
2714     # if (!has-modrm?) return
2715     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
2716     74/jump-if-equal  $emit-modrm:end/disp8
2717 $emit-modrm:calculate:
2718     # modrm/EBX = mod & 0b11
2719     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
2720     # modrm <<= 2
2721     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
2722     # modrm |= r32 & 0b111
2723     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
2724     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
2725     # modrm <<= 3
2726     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
2727     # modrm |= rm32 & 0b111
2728     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
2729     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
2730 $emit-modrm:emit:
2731     # emit-hex(out, modrm, 1)
2732     # . . push args
2733     68/push  1/imm32
2734     53/push-EBX
2735     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2736     # . . call
2737     e8/call  emit-hex/disp32
2738     # . . discard args
2739     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2740 $emit-modrm:end:
2741     # . restore locals
2742     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2743     # . restore registers
2744     5f/pop-to-EDI
2745     5e/pop-to-ESI
2746     5b/pop-to-EBX
2747     5a/pop-to-EDX
2748     59/pop-to-ECX
2749     58/pop-to-EAX
2750     # . epilog
2751     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2752     5d/pop-to-EBP
2753     c3/return
2754 
2755 emit-sib:  # line : (address stream byte), out : (address buffered-file) -> <void>
2756     # pseudocode:
2757     #   var has-sib? = false, base = 0, index = 0, scale = 0
2758     #   var word-slice = {0, 0}
2759     #   while true
2760     #     word-slice = next-word(line)
2761     #     if (slice-empty?(word-slice)) break
2762     #     if (slice-starts-with?(word-slice, "#")) break
2763     #     if (has-metadata?(word-slice, "base")
2764     #       base = parse-hex-int(next-token-from-slice(word-slice, "/"))
2765     #       has-sib? = true
2766     #     else if (has-metadata?(word-slice, "index")
2767     #       index = parse-hex-int(next-token-from-slice(word-slice, "/"))
2768     #       has-sib? = true
2769     #     else if (has-metadata?(word-slice, "scale")
2770     #       scale = parse-hex-int(next-token-from-slice(word-slice, "/"))
2771     #       has-sib? = true
2772     #   if has-sib?
2773     #     var sib = scale & 0b11
2774     #     sib <<= 2
2775     #     sib |= index & 0b111
2776     #     sib <<= 3
2777     #     sib |= base & 0b111
2778     #     emit-hex(out, sib, 1)
2779     #
2780     # . prolog
2781     55/push-EBP
2782     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2783     # . save registers
2784     50/push-EAX
2785     51/push-ECX
2786     52/push-EDX
2787     53/push-EBX
2788     56/push-ESI
2789     57/push-EDI
2790     # var word-slice/ECX = {0, 0}
2791     68/push  0/imm32/end
2792     68/push  0/imm32/start
2793     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2794     # var has-sib?/EDX = false
2795     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
2796     # var scale/EBX = 0
2797     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2798     # var base/ESI = 0
2799     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
2800     # var index/EDI = 0
2801     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
2802     # rewind-stream(line)
2803     # . . push args
2804     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2805     # . . call
2806     e8/call  rewind-stream/disp32
2807     # . . discard args
2808     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2809 $emit-sib:loop:
2810 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2844     # next-word(line, word-slice)
2845     # . . push args
2846     51/push-ECX
2847     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2848     # . . call
2849     e8/call  next-word/disp32
2850     # . . discard args
2851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2852 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2902 $emit-sib:check0:
2903     # if (slice-empty?(word-slice)) break
2904     # . EAX = slice-empty?(word-slice)
2905     # . . push args
2906     51/push-ECX
2907     # . . call
2908     e8/call  slice-empty?/disp32
2909     # . . discard args
2910     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2911     # . if (EAX != 0) pass through
2912     3d/compare-EAX-and  0/imm32
2913     0f 85/jump-if-not-equal  $emit-sib:break/disp32
2914 $emit-sib:check1:
2915     # if (slice-starts-with?(word-slice, "#")) break
2916     # . spill EDX
2917     52/push-EDX
2918     # . start/EDX = word-slice->start
2919     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
2920     # . c/EAX = *start
2921     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2922     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
2923     # . restore EDX
2924     5a/pop-to-EDX
2925     # . if (EAX == '#') pass through
2926     3d/compare-EAX-and  0x23/imm32/hash
2927     0f 84/jump-if-equal  $emit-sib:break/disp32
2928 $emit-sib:check-for-scale:
2929     # if (has-metadata?(word-slice, "scale"))
2930     # . EAX = has-metadata?(ECX, "scale")
2931     # . . push args
2932     68/push  "scale"/imm32
2933     51/push-ECX
2934     # . . call
2935     e8/call  has-metadata?/disp32
2936     # . . discard args
2937     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2938     # . if (EAX == 0) goto next check
2939     3d/compare-EAX-and  0/imm32
2940     74/jump-if-equal  $emit-sib:check-for-base/disp8
2941 $emit-sib:scale:
2942     # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2943     # . EAX = parse-datum-of-word(word-slice)
2944     # . . push args
2945     51/push-ECX
2946     # . . call
2947     e8/call  parse-datum-of-word/disp32
2948     # . . discard args
2949     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2950     # . scale = EAX
2951     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
2952     # has-sib? = true
2953     ba/copy-to-EDX  1/imm32/true
2954     # continue
2955     e9/jump  $emit-sib:loop/disp32
2956 $emit-sib:check-for-base:
2957     # if (has-metadata?(word-slice, "base"))
2958     # . EAX = has-metadata?(ECX, "base")
2959     # . . push args
2960     68/push  "base"/imm32
2961     51/push-ECX
2962     # . . call
2963     e8/call  has-metadata?/disp32
2964     # . . discard args
2965     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2966     # . if (EAX == 0) goto next check
2967     3d/compare-EAX-and  0/imm32
2968     74/jump-if-equal  $emit-sib:check-for-index/disp8
2969 $emit-sib:base:
2970     # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2971     # . EAX = parse-datum-of-word(word-slice)
2972     # . . push args
2973     51/push-ECX
2974     # . . call
2975     e8/call  parse-datum-of-word/disp32
2976     # . . discard args
2977     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2978     # . base = EAX
2979     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
2980     # has-sib? = true
2981     ba/copy-to-EDX  1/imm32/true
2982     # continue
2983     e9/jump  $emit-sib:loop/disp32
2984 $emit-sib:check-for-index:
2985     # if (has-metadata?(word-slice, "index"))
2986     # . EAX = has-metadata?(ECX, "index")
2987     # . . push args
2988     68/push  "index"/imm32
2989     51/push-ECX
2990     # . . call
2991     e8/call  has-metadata?/disp32
2992     # . . discard args
2993     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2994     # . if (EAX == 0) loop
2995     3d/compare-EAX-and  0/imm32
2996     0f 84/jump-if-equal  $emit-sib:loop/disp32
2997 $emit-sib:index:
2998     # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2999     # . EAX = parse-datum-of-word(word-slice)
3000     # . . push args
3001     51/push-ECX
3002     # . . call
3003     e8/call  parse-datum-of-word/disp32
3004     # . . discard args
3005     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3006     # . index = EAX
3007     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
3008     # has-sib? = true
3009     ba/copy-to-EDX  1/imm32/true
3010     # continue
3011     e9/jump  $emit-sib:loop/disp32
3012 $emit-sib:break:
3013     # if (!has-sib?) return
3014     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
3015     74/jump-if-equal  $emit-sib:end/disp8
3016 $emit-sib:calculate:
3017     # sib/EBX = scale & 0b11
3018     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
3019     # sib <<= 2
3020     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
3021     # sib |= index & 0b111
3022     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
3023     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
3024     # sib <<= 3
3025     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
3026     # sib |= base & 0b111
3027     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
3028     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
3029 $emit-sib:emit:
3030     # emit-hex(out, sib, 1)
3031     # . . push args
3032     68/push  1/imm32
3033     53/push-EBX
3034     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3035     # . . call
3036     e8/call  emit-hex/disp32
3037     # . . discard args
3038     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3039 $emit-sib:end:
3040     # . restore locals
3041     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3042     # . restore registers
3043     5f/pop-to-EDI
3044     5e/pop-to-ESI
3045     5b/pop-to-EBX
3046     5a/pop-to-EDX
3047     59/pop-to-ECX
3048     58/pop-to-EAX
3049     # . epilog
3050     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3051     5d/pop-to-EBP
3052     c3/return
3053 
3054 emit-disp:  # line : (address stream byte), out : (address buffered-file) -> <void>
3055     # pseudocode:
3056     #   rewind-stream(line)
3057     #   var word-slice = {0, 0}
3058     #   while true
3059     #     word-slice = next-word(line)
3060     #     if (slice-empty?(word-slice)) break
3061     #     if (slice-starts-with?(word-slice, "#")) break
3062     #     if has-metadata?(word-slice, "disp32")
3063     #       emit(out, word-slice, 4)
3064     #       break
3065     #     if has-metadata?(word-slice, "disp16")
3066     #       emit(out, word-slice, 2)
3067     #       break
3068     #     if has-metadata?(word-slice, "disp8")
3069     #       emit(out, word-slice, 1)
3070     #       break
3071     #
3072     # . prolog
3073     55/push-EBP
3074     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3075     # . save registers
3076     50/push-EAX
3077     51/push-ECX
3078     52/push-EDX
3079     # var word-slice/ECX = {0, 0}
3080     68/push  0/imm32/end
3081     68/push  0/imm32/start
3082     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
3083     # rewind-stream(line)
3084     # . . push args
3085     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3086     # . . call
3087     e8/call  rewind-stream/disp32
3088     # . . discard args
3089     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3090 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
3124 $emit-disp:loop:
3125     # next-word(line, word-slice)
3126     # . . push args
3127     51/push-ECX
3128     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3129     # . . call
3130     e8/call  next-word/disp32
3131     # . . discard args
3132     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3133 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
3183 $emit-disp:check0:
3184     # if (slice-empty?(word-slice)) break
3185     # . EAX = slice-empty?(word-slice)
3186     # . . push args
3187     51/push-ECX
3188     # . . call
3189     e8/call  slice-empty?/disp32
3190     # . . discard args
3191     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3192     # . if (EAX != 0) pass through
3193     3d/compare-EAX-and  0/imm32
3194     0f 85/jump-if-not-equal  $emit-disp:break/disp32
3195 $emit-disp:check1:
3196     # if (slice-starts-with?(word-slice, "#")) break
3197     # . start/EDX = word-slice->start
3198     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
3199     # . c/EAX = *start
3200     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
3201     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
3202     # . if (EAX == '#') break
3203     3d/compare-EAX-and  0x23/imm32/hash
3204     0f 84/jump-if-equal  $emit-disp:break/disp32
3205 $emit-disp:check-for-disp32:
3206     # if (has-metadata?(word-slice, "disp32"))
3207     # . EAX = has-metadata?(ECX, "disp32")
3208     # . . push args
3209     68/push  "disp32"/imm32
3210     51/push-ECX
3211     # . . call
3212     e8/call  has-metadata?/disp32
3213     # . . discard args
3214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3215     # . if (EAX == 0) goto next check
3216     3d/compare-EAX-and  0/imm32
3217     74/jump-if-equal  $emit-disp:check-for-disp16/disp8
3218 $emit-disp:disp32:
3219     # emit(out, word-slice, 4)
3220     # . . push args
3221     68/push  4/imm32
3222     51/push-ECX
3223     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3224     # . . call
3225     e8/call  emit/disp32
3226     # . . discard args
3227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3228     # break
3229     e9/jump  $emit-disp:break/disp32
3230 $emit-disp:check-for-disp16:
3231     # else if (has-metadata?(word-slice, "disp16"))
3232     # . EAX = has-metadata?(ECX, "disp16")
3233     # . . push args
3234     68/push  "disp16"/imm32
3235     51/push-ECX
3236     # . . call
3237     e8/call  has-metadata?/disp32
3238     # . . discard args
3239     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3240     # . if (EAX == 0) goto next check
3241     3d/compare-EAX-and  0/imm32
3242     74/jump-if-equal  $emit-disp:check-for-disp8/disp8
3243 $emit-disp:disp16:
3244     # emit(out, word-slice, 2)
3245     # . . push args
3246     68/push  2/imm32
3247     51/push-ECX
3248     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3249     # . . call
3250     e8/call  emit/disp32
3251     # . . discard args
3252     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3253     # break
3254     e9/jump  $emit-disp:break/disp32
3255 $emit-disp:check-for-disp8:
3256     # if (has-metadata?(word-slice, "disp8"))
3257     # . EAX = has-metadata?(ECX, "disp8")
3258     # . . push args
3259     68/push  "disp8"/imm32
3260     51/push-ECX
3261     # . . call
3262     e8/call  has-metadata?/disp32
3263     # . . discard args
3264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3265     # . if (EAX == 0) loop
3266     3d/compare-EAX-and  0/imm32
3267     0f 84/jump-if-equal  $emit-disp:loop/disp32
3268 $emit-disp:disp8:
3269     # emit(out, word-slice, 1)
3270     # . . push args
3271     68/push  1/imm32
3272     51/push-ECX
3273     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3274     # . . call
3275     e8/call  emit/disp32
3276     # . . discard args
3277     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3278     # break
3279 $emit-disp:break:
3280     # . restore locals
3281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3282     # . restore registers
3283     5a/pop-to-EDX
3284     59/pop-to-ECX
3285     58/pop-to-EAX
3286     # . epilog
3287     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3288     5d/pop-to-EBP
3289     c3/return
3290 
3291 emit-imm:  # line : (address stream byte), out : (address buffered-file) -> <void>
3292     # pseudocode:
3293     #   rewind-stream(line)
3294     #   var word-slice = {0, 0}
3295     #   while true
3296     #     word-slice = next-word(line)
3297     #     if (slice-empty?(word-slice)) break
3298     #     if (slice-starts-with?(word-slice, "#")) break
3299     #     if has-metadata?(word-slice, "imm32")
3300     #       emit(out, word-slice, 4)
3301     #       break
3302     #     if has-metadata?(word-slice, "imm16")
3303     #       emit(out, word-slice, 2)
3304     #       break
3305     #     if has-metadata?(word-slice, "imm8")
3306     #       emit(out, word-slice, 1)
3307     #       break
3308     #
3309     # . prolog
3310     55/push-EBP
3311     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3312     # . save registers
3313     50/push-EAX
3314     51/push-ECX
3315     52/push-EDX
3316     # var word-slice/ECX = {0, 0}
3317     68/push  0/imm32/end
3318     68/push  0/imm32/start
3319     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
3320     # rewind-stream(line)
3321     # . . push args
3322     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3323     # . . call
3324     e8/call  rewind-stream/disp32
3325     # . . discard args
3326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3327 +-- 34 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
3361 $emit-imm:loop:
3362     # next-word(line, word-slice)
3363     # . . push args
3364     51/push-ECX
3365     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3366     # . . call
3367     e8/call  next-word/disp32
3368     # . . discard args
3369     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3370 +-- 50 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
3420 $emit-imm:check0:
3421     # if (slice-empty?(word-slice)) break
3422     # . EAX = slice-empty?(word-slice)
3423     # . . push args
3424     51/push-ECX
3425     # . . call
3426     e8/call  slice-empty?/disp32
3427     # . . discard args
3428     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3429     # . if (EAX != 0) pass through
3430     3d/compare-EAX-and  0/imm32
3431     0f 85/jump-if-not-equal  $emit-imm:break/disp32
3432 $emit-imm:check1:
3433     # if (slice-starts-with?(word-slice, "#")) break
3434     # . start/EDX = slice->start
3435     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
3436     # . c/EAX = *start
3437     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
3438     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
3439     # . if (EAX == '#') break
3440     3d/compare-EAX-and  0x23/imm32/hash
3441     0f 84/jump-if-equal  $emit-imm:break/disp32
3442 $emit-imm:check-for-imm32:
3443     # if (has-metadata?(word-slice, "imm32"))
3444     # . EAX = has-metadata?(ECX, "imm32")
3445     # . . push args
3446     68/push  "imm32"/imm32
3447     51/push-ECX
3448     # . . call
3449     e8/call  has-metadata?/disp32
3450     # . . discard args
3451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3452     # . if (EAX == 0) goto next check
3453     3d/compare-EAX-and  0/imm32
3454     74/jump-if-equal  $emit-imm:check-for-imm16/disp8
3455 $emit-imm:imm32:
3456     # emit(out, word-slice, 4)
3457     # . . push args
3458     68/push  4/imm32
3459     51/push-ECX
3460     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3461     # . . call
3462     e8/call  emit/disp32
3463     # . . discard args
3464     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3465     # break
3466     e9/jump  $emit-imm:break/disp32
3467 $emit-imm:check-for-imm16:
3468     # if (has-metadata?(word-slice, "imm16"))
3469     # . EAX = has-metadata?(ECX, "imm16")
3470     # . . push args
3471     68/push  "imm16"/imm32
3472     51/push-ECX
3473     # . . call
3474     e8/call  has-metadata?/disp32
3475     # . . discard args
3476     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3477     # . if (EAX == 0) goto next check
3478     3d/compare-EAX-and  0/imm32
3479     74/jump-if-equal  $emit-imm:check-for-imm8/disp8
3480 $emit-imm:imm16:
3481     # emit(out, word-slice, 2)
3482     # . . push args
3483     68/push  2/imm32
3484     51/push-ECX
3485     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3486     # . . call
3487     e8/call  emit/disp32
3488     # . . discard args
3489     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3490     # break
3491     e9/jump  $emit-imm:break/disp32
3492 $emit-imm:check-for-imm8:
3493     # if (has-metadata?(word-slice, "imm8"))
3494     # . EAX = has-metadata?(ECX, "imm8")
3495     # . . push args
3496     68/push  "imm8"/imm32
3497     51/push-ECX
3498     # . . call
3499     e8/call  has-metadata?/disp32
3500     # . . discard args
3501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3502     # . if (EAX == 0) loop
3503     3d/compare-EAX-and  0/imm32
3504     0f 84/jump-if-equal  $emit-imm:loop/disp32
3505 $emit-imm:imm8:
3506     # emit(out, word-slice, 1)
3507     # . . push args
3508     68/push  1/imm32
3509     51/push-ECX
3510     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3511     # . . call
3512     e8/call  emit/disp32
3513     # . . discard args
3514     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3515     # break
3516 $emit-imm:break:
3517     # . restore locals
3518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3519     # . restore registers
3520     5a/pop-to-EDX
3521     59/pop-to-ECX
3522     58/pop-to-EAX
3523     # . epilog
3524     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3525     5d/pop-to-EBP
3526     c3/return
3527 
3528 emit-line-in-comment:  # line : (address stream byte), out : (address buffered-file) -> <void>
3529     # . prolog
3530     55/push-EBP
3531     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3532     # write-buffered(out, " # ")
3533     # . . push args
3534     68/push  " # "/imm32
3535     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3536     # . . call
3537     e8/call  write-buffered/disp32
3538     # . . discard args
3539     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3540     # write-stream-data(out, line)
3541     # . . push args
3542     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3543     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3544     # . . call
3545     e8/call  write-stream-data/disp32
3546     # . . discard args
3547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3548 $emit-line-in-comment:end:
3549     # . epilog
3550     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3551     5d/pop-to-EBP
3552     c3/return
3553 
3554 test-convert-instruction-passes-comments-through:
3555     # if a line starts with '#', pass it along unchanged
3556     # . prolog
3557     55/push-EBP
3558     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3559     # setup
3560     # . clear-stream(_test-input-stream)
3561     # . . push args
3562     68/push  _test-input-stream/imm32
3563     # . . call
3564     e8/call  clear-stream/disp32
3565     # . . discard args
3566     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3567     # . clear-stream(_test-output-stream)
3568     # . . push args
3569     68/push  _test-output-stream/imm32
3570     # . . call
3571     e8/call  clear-stream/disp32
3572     # . . discard args
3573     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3574     # . clear-stream(_test-output-buffered-file+4)
3575     # . . push args
3576     b8/copy-to-EAX  _test-output-buffered-file/imm32
3577     05/add-to-EAX  4/imm32
3578     50/push-EAX
3579     # . . call
3580     e8/call  clear-stream/disp32
3581     # . . discard args
3582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3583     # initialize input
3584     # . write(_test-input-stream, "# abcd")
3585     # . . push args
3586     68/push  "# abcd"/imm32
3587     68/push  _test-input-stream/imm32
3588     # . . call
3589     e8/call  write/disp32
3590     # . . discard args
3591     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3592     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3593     # . . push args
3594     68/push  _test-output-buffered-file/imm32
3595     68/push  _test-input-stream/imm32
3596     # . . call
3597     e8/call  convert-instruction/disp32
3598     # . . discard args
3599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3600     # check that the line just passed through
3601     # . flush(_test-output-buffered-file)
3602     # . . push args
3603     68/push  _test-output-buffered-file/imm32
3604     # . . call
3605     e8/call  flush/disp32
3606     # . . discard args
3607     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3608     # . check-stream-equal(_test-output-stream, "# abcd", msg)
3609     # . . push args
3610     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
3611     68/push  "# abcd"/imm32
3612     68/push  _test-output-stream/imm32
3613     # . . call
3614     e8/call  check-stream-equal/disp32
3615     # . . discard args
3616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3617     # . epilog
3618     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3619     5d/pop-to-EBP
3620     c3/return
3621 
3622 test-convert-instruction-passes-labels-through:
3623     # if the first word ends with ':', pass along the entire line unchanged
3624     # . prolog
3625     55/push-EBP
3626     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3627     # setup
3628     # . clear-stream(_test-input-stream)
3629     # . . push args
3630     68/push  _test-input-stream/imm32
3631     # . . call
3632     e8/call  clear-stream/disp32
3633     # . . discard args
3634     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3635     # . clear-stream(_test-output-stream)
3636     # . . push args
3637     68/push  _test-output-stream/imm32
3638     # . . call
3639     e8/call  clear-stream/disp32
3640     # . . discard args
3641     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3642     # . clear-stream(_test-output-buffered-file+4)
3643     # . . push args
3644     b8/copy-to-EAX  _test-output-buffered-file/imm32
3645     05/add-to-EAX  4/imm32
3646     50/push-EAX
3647     # . . call
3648     e8/call  clear-stream/disp32
3649     # . . discard args
3650     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3651     # initialize input
3652     # . write(_test-input-stream, "ab: # cd")
3653     # . . push args
3654     68/push  "ab: # cd"/imm32
3655     68/push  _test-input-stream/imm32
3656     # . . call
3657     e8/call  write/disp32
3658     # . . discard args
3659     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3660     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3661     # . . push args
3662     68/push  _test-output-buffered-file/imm32
3663     68/push  _test-input-stream/imm32
3664     # . . call
3665     e8/call  convert-instruction/disp32
3666     # . . discard args
3667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3668     # check that the line just passed through
3669     # . flush(_test-output-buffered-file)
3670     # . . push args
3671     68/push  _test-output-buffered-file/imm32
3672     # . . call
3673     e8/call  flush/disp32
3674     # . . discard args
3675     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3676     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
3677     # . . push args
3678     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
3679     68/push  "ab: # cd"/imm32
3680     68/push  _test-output-stream/imm32
3681     # . . call
3682     e8/call  check-stream-equal/disp32
3683     # . . discard args
3684     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3685     # . epilog
3686     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3687     5d/pop-to-EBP
3688     c3/return
3689 
3690 test-convert-instruction-handles-single-opcode:
3691     # if the instruction consists of a single opcode, strip its metadata and pass it along
3692     # . prolog
3693     55/push-EBP
3694     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3695     # setup
3696     # . clear-stream(_test-input-stream)
3697     # . . push args
3698     68/push  _test-input-stream/imm32
3699     # . . call
3700     e8/call  clear-stream/disp32
3701     # . . discard args
3702     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3703     # . clear-stream(_test-output-stream)
3704     # . . push args
3705     68/push  _test-output-stream/imm32
3706     # . . call
3707     e8/call  clear-stream/disp32
3708     # . . discard args
3709     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3710     # . clear-stream(_test-output-buffered-file+4)
3711     # . . push args
3712     b8/copy-to-EAX  _test-output-buffered-file/imm32
3713     05/add-to-EAX  4/imm32
3714     50/push-EAX
3715     # . . call
3716     e8/call  clear-stream/disp32
3717     # . . discard args
3718     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3719     # initialize input
3720     # . write(_test-input-stream, "ab/cd # comment")
3721     # . . push args
3722     68/push  "ab/cd # comment"/imm32
3723     68/push  _test-input-stream/imm32
3724     # . . call
3725     e8/call  write/disp32
3726     # . . discard args
3727     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3728     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3729     # . . push args
3730     68/push  _test-output-buffered-file/imm32
3731     68/push  _test-input-stream/imm32
3732     # . . call
3733     e8/call  convert-instruction/disp32
3734     # . . discard args
3735     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3736     # check output
3737     # . flush(_test-output-buffered-file)
3738     # . . push args
3739     68/push  _test-output-buffered-file/imm32
3740     # . . call
3741     e8/call  flush/disp32
3742     # . . discard args
3743     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3744 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3778     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
3779     # . . push args
3780     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
3781     68/push  "ab  # ab/cd # comment"/imm32
3782     68/push  _test-output-stream/imm32
3783     # . . call
3784     e8/call  check-stream-equal/disp32
3785     # . . discard args
3786     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3787     # . epilog
3788     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3789     5d/pop-to-EBP
3790     c3/return
3791 
3792 test-convert-instruction-handles-0f-opcode:
3793     # if the instruction starts with 0f opcode, include a second opcode
3794     # . prolog
3795     55/push-EBP
3796     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3797     # setup
3798     # . clear-stream(_test-input-stream)
3799     # . . push args
3800     68/push  _test-input-stream/imm32
3801     # . . call
3802     e8/call  clear-stream/disp32
3803     # . . discard args
3804     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3805     # . clear-stream(_test-output-stream)
3806     # . . push args
3807     68/push  _test-output-stream/imm32
3808     # . . call
3809     e8/call  clear-stream/disp32
3810     # . . discard args
3811     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3812     # . clear-stream(_test-output-buffered-file+4)
3813     # . . push args
3814     b8/copy-to-EAX  _test-output-buffered-file/imm32
3815     05/add-to-EAX  4/imm32
3816     50/push-EAX
3817     # . . call
3818     e8/call  clear-stream/disp32
3819     # . . discard args
3820     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3821     # initialize input
3822     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
3823     # . . push args
3824     68/push  "0f/m1 ab/m2 # comment"/imm32
3825     68/push  _test-input-stream/imm32
3826     # . . call
3827     e8/call  write/disp32
3828     # . . discard args
3829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3830     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3831     # . . push args
3832     68/push  _test-output-buffered-file/imm32
3833     68/push  _test-input-stream/imm32
3834     # . . call
3835     e8/call  convert-instruction/disp32
3836     # . . discard args
3837     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3838     # check output
3839     # . flush(_test-output-buffered-file)
3840     # . . push args
3841     68/push  _test-output-buffered-file/imm32
3842     # . . call
3843     e8/call  flush/disp32
3844     # . . discard args
3845     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3846 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3880     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
3881     # . . push args
3882     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
3883     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
3884     68/push  _test-output-stream/imm32
3885     # . . call
3886     e8/call  check-stream-equal/disp32
3887     # . . discard args
3888     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3889     # . epilog
3890     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3891     5d/pop-to-EBP
3892     c3/return
3893 
3894 test-convert-instruction-handles-f2-opcode:
3895     # if the instruction starts with f2 opcode, include a second opcode
3896     # . prolog
3897     55/push-EBP
3898     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3899     # setup
3900     # . clear-stream(_test-input-stream)
3901     # . . push args
3902     68/push  _test-input-stream/imm32
3903     # . . call
3904     e8/call  clear-stream/disp32
3905     # . . discard args
3906     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3907     # . clear-stream(_test-output-stream)
3908     # . . push args
3909     68/push  _test-output-stream/imm32
3910     # . . call
3911     e8/call  clear-stream/disp32
3912     # . . discard args
3913     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3914     # . clear-stream(_test-output-buffered-file+4)
3915     # . . push args
3916     b8/copy-to-EAX  _test-output-buffered-file/imm32
3917     05/add-to-EAX  4/imm32
3918     50/push-EAX
3919     # . . call
3920     e8/call  clear-stream/disp32
3921     # . . discard args
3922     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3923     # initialize input
3924     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
3925     # . . push args
3926     68/push  "f2/m1 ab/m2 # comment"/imm32
3927     68/push  _test-input-stream/imm32
3928     # . . call
3929     e8/call  write/disp32
3930     # . . discard args
3931     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3932     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3933     # . . push args
3934     68/push  _test-output-buffered-file/imm32
3935     68/push  _test-input-stream/imm32
3936     # . . call
3937     e8/call  convert-instruction/disp32
3938     # . . discard args
3939     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3940     # check output
3941     # . flush(_test-output-buffered-file)
3942     # . . push args
3943     68/push  _test-output-buffered-file/imm32
3944     # . . call
3945     e8/call  flush/disp32
3946     # . . discard args
3947     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3948 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3982     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
3983     # . . push args
3984     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
3985     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
3986     68/push  _test-output-stream/imm32
3987     # . . call
3988     e8/call  check-stream-equal/disp32
3989     # . . discard args
3990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3991     # . epilog
3992     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3993     5d/pop-to-EBP
3994     c3/return
3995 
3996 test-convert-instruction-handles-f3-opcode:
3997     # if the instruction starts with f3 opcode, include a second opcode
3998     # . prolog
3999     55/push-EBP
4000     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4001     # setup
4002     # . clear-stream(_test-input-stream)
4003     # . . push args
4004     68/push  _test-input-stream/imm32
4005     # . . call
4006     e8/call  clear-stream/disp32
4007     # . . discard args
4008     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4009     # . clear-stream(_test-output-stream)
4010     # . . push args
4011     68/push  _test-output-stream/imm32
4012     # . . call
4013     e8/call  clear-stream/disp32
4014     # . . discard args
4015     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4016     # . clear-stream(_test-output-buffered-file+4)
4017     # . . push args
4018     b8/copy-to-EAX  _test-output-buffered-file/imm32
4019     05/add-to-EAX  4/imm32
4020     50/push-EAX
4021     # . . call
4022     e8/call  clear-stream/disp32
4023     # . . discard args
4024     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4025     # initialize input
4026     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
4027     # . . push args
4028     68/push  "f3/m1 ab/m2 # comment"/imm32
4029     68/push  _test-input-stream/imm32
4030     # . . call
4031     e8/call  write/disp32
4032     # . . discard args
4033     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4034     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4035     # . . push args
4036     68/push  _test-output-buffered-file/imm32
4037     68/push  _test-input-stream/imm32
4038     # . . call
4039     e8/call  convert-instruction/disp32
4040     # . . discard args
4041     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4042     # check output
4043     # . flush(_test-output-buffered-file)
4044     # . . push args
4045     68/push  _test-output-buffered-file/imm32
4046     # . . call
4047     e8/call  flush/disp32
4048     # . . discard args
4049     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4050 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4084     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
4085     # . . push args
4086     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
4087     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
4088     68/push  _test-output-stream/imm32
4089     # . . call
4090     e8/call  check-stream-equal/disp32
4091     # . . discard args
4092     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4093     # . epilog
4094     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4095     5d/pop-to-EBP
4096     c3/return
4097 
4098 test-convert-instruction-handles-f2-0f-opcode:
4099     # if the instruction starts with f2 0f opcode, include a second opcode
4100     # . prolog
4101     55/push-EBP
4102     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4103     # setup
4104     # . clear-stream(_test-input-stream)
4105     # . . push args
4106     68/push  _test-input-stream/imm32
4107     # . . call
4108     e8/call  clear-stream/disp32
4109     # . . discard args
4110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4111     # . clear-stream(_test-output-stream)
4112     # . . push args
4113     68/push  _test-output-stream/imm32
4114     # . . call
4115     e8/call  clear-stream/disp32
4116     # . . discard args
4117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4118     # . clear-stream(_test-output-buffered-file+4)
4119     # . . push args
4120     b8/copy-to-EAX  _test-output-buffered-file/imm32
4121     05/add-to-EAX  4/imm32
4122     50/push-EAX
4123     # . . call
4124     e8/call  clear-stream/disp32
4125     # . . discard args
4126     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4127     # initialize input
4128     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
4129     # . . push args
4130     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
4131     68/push  _test-input-stream/imm32
4132     # . . call
4133     e8/call  write/disp32
4134     # . . discard args
4135     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4136     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4137     # . . push args
4138     68/push  _test-output-buffered-file/imm32
4139     68/push  _test-input-stream/imm32
4140     # . . call
4141     e8/call  convert-instruction/disp32
4142     # . . discard args
4143     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4144     # check output
4145     # . flush(_test-output-buffered-file)
4146     # . . push args
4147     68/push  _test-output-buffered-file/imm32
4148     # . . call
4149     e8/call  flush/disp32
4150     # . . discard args
4151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4152 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4186     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
4187     # . . push args
4188     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
4189     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
4190     68/push  _test-output-stream/imm32
4191     # . . call
4192     e8/call  check-stream-equal/disp32
4193     # . . discard args
4194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4195     # . epilog
4196     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4197     5d/pop-to-EBP
4198     c3/return
4199 
4200 test-convert-instruction-handles-f3-0f-opcode:
4201     # if the instruction starts with f3 0f opcode, include a second opcode
4202     # . prolog
4203     55/push-EBP
4204     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4205     # setup
4206     # . clear-stream(_test-input-stream)
4207     # . . push args
4208     68/push  _test-input-stream/imm32
4209     # . . call
4210     e8/call  clear-stream/disp32
4211     # . . discard args
4212     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4213     # . clear-stream(_test-output-stream)
4214     # . . push args
4215     68/push  _test-output-stream/imm32
4216     # . . call
4217     e8/call  clear-stream/disp32
4218     # . . discard args
4219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4220     # . clear-stream(_test-output-buffered-file+4)
4221     # . . push args
4222     b8/copy-to-EAX  _test-output-buffered-file/imm32
4223     05/add-to-EAX  4/imm32
4224     50/push-EAX
4225     # . . call
4226     e8/call  clear-stream/disp32
4227     # . . discard args
4228     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4229     # initialize input
4230     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
4231     # . . push args
4232     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
4233     68/push  _test-input-stream/imm32
4234     # . . call
4235     e8/call  write/disp32
4236     # . . discard args
4237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4238     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4239     # . . push args
4240     68/push  _test-output-buffered-file/imm32
4241     68/push  _test-input-stream/imm32
4242     # . . call
4243     e8/call  convert-instruction/disp32
4244     # . . discard args
4245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4246     # check output
4247     # . flush(_test-output-buffered-file)
4248     # . . push args
4249     68/push  _test-output-buffered-file/imm32
4250     # . . call
4251     e8/call  flush/disp32
4252     # . . discard args
4253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4254 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4288     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4289     # . . push args
4290     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
4291     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
4292     68/push  _test-output-stream/imm32
4293     # . . call
4294     e8/call  check-stream-equal/disp32
4295     # . . discard args
4296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4297     # . epilog
4298     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4299     5d/pop-to-EBP
4300     c3/return
4301 
4302 test-convert-instruction-handles-unused-opcodes:
4303     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
4304     # . prolog
4305     55/push-EBP
4306     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4307     # setup
4308     # . clear-stream(_test-input-stream)
4309     # . . push args
4310     68/push  _test-input-stream/imm32
4311     # . . call
4312     e8/call  clear-stream/disp32
4313     # . . discard args
4314     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4315     # . clear-stream(_test-output-stream)
4316     # . . push args
4317     68/push  _test-output-stream/imm32
4318     # . . call
4319     e8/call  clear-stream/disp32
4320     # . . discard args
4321     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4322     # . clear-stream(_test-output-buffered-file+4)
4323     # . . push args
4324     b8/copy-to-EAX  _test-output-buffered-file/imm32
4325     05/add-to-EAX  4/imm32
4326     50/push-EAX
4327     # . . call
4328     e8/call  clear-stream/disp32
4329     # . . discard args
4330     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4331     # initialize input
4332     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
4333     # . . push args
4334     68/push  "ab/m1 cd/m2 # comment"/imm32
4335     68/push  _test-input-stream/imm32
4336     # . . call
4337     e8/call  write/disp32
4338     # . . discard args
4339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4340     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4341     # . . push args
4342     68/push  _test-output-buffered-file/imm32
4343     68/push  _test-input-stream/imm32
4344     # . . call
4345     e8/call  convert-instruction/disp32
4346     # . . discard args
4347     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4348     # check output
4349     # . flush(_test-output-buffered-file)
4350     # . . push args
4351     68/push  _test-output-buffered-file/imm32
4352     # . . call
4353     e8/call  flush/disp32
4354     # . . discard args
4355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4356 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4390     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4391     # . . push args
4392     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
4393     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
4394     68/push  _test-output-stream/imm32
4395     # . . call
4396     e8/call  check-stream-equal/disp32
4397     # . . discard args
4398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4399     # . epilog
4400     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4401     5d/pop-to-EBP
4402     c3/return
4403 
4404 test-convert-instruction-handles-unused-second-opcodes:
4405     # if the second opcode isn't 0f, don't include further opcodes
4406     # . prolog
4407     55/push-EBP
4408     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4409     # setup
4410     # . clear-stream(_test-input-stream)
4411     # . . push args
4412     68/push  _test-input-stream/imm32
4413     # . . call
4414     e8/call  clear-stream/disp32
4415     # . . discard args
4416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4417     # . clear-stream(_test-output-stream)
4418     # . . push args
4419     68/push  _test-output-stream/imm32
4420     # . . call
4421     e8/call  clear-stream/disp32
4422     # . . discard args
4423     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4424     # . clear-stream(_test-output-buffered-file+4)
4425     # . . push args
4426     b8/copy-to-EAX  _test-output-buffered-file/imm32
4427     05/add-to-EAX  4/imm32
4428     50/push-EAX
4429     # . . call
4430     e8/call  clear-stream/disp32
4431     # . . discard args
4432     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4433     # initialize input
4434     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
4435     # . . push args
4436     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
4437     68/push  _test-input-stream/imm32
4438     # . . call
4439     e8/call  write/disp32
4440     # . . discard args
4441     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4442     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4443     # . . push args
4444     68/push  _test-output-buffered-file/imm32
4445     68/push  _test-input-stream/imm32
4446     # . . call
4447     e8/call  convert-instruction/disp32
4448     # . . discard args
4449     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4450     # check output
4451     # . flush(_test-output-buffered-file)
4452     # . . push args
4453     68/push  _test-output-buffered-file/imm32
4454     # . . call
4455     e8/call  flush/disp32
4456     # . . discard args
4457     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4458 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4492     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
4493     # . . push args
4494     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4495     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
4496     68/push  _test-output-stream/imm32
4497     # . . call
4498     e8/call  check-stream-equal/disp32
4499     # . . discard args
4500     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4501     # . epilog
4502     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4503     5d/pop-to-EBP
4504     c3/return
4505 
4506 test-convert-instruction-handles-unused-second-opcodes-2:
4507     # if the second opcode isn't 0f, don't include further opcodes
4508     # . prolog
4509     55/push-EBP
4510     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4511     # setup
4512     # . clear-stream(_test-input-stream)
4513     # . . push args
4514     68/push  _test-input-stream/imm32
4515     # . . call
4516     e8/call  clear-stream/disp32
4517     # . . discard args
4518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4519     # . clear-stream(_test-output-stream)
4520     # . . push args
4521     68/push  _test-output-stream/imm32
4522     # . . call
4523     e8/call  clear-stream/disp32
4524     # . . discard args
4525     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4526     # . clear-stream(_test-output-buffered-file+4)
4527     # . . push args
4528     b8/copy-to-EAX  _test-output-buffered-file/imm32
4529     05/add-to-EAX  4/imm32
4530     50/push-EAX
4531     # . . call
4532     e8/call  clear-stream/disp32
4533     # . . discard args
4534     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4535     # initialize input
4536     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
4537     # . . push args
4538     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
4539     68/push  _test-input-stream/imm32
4540     # . . call
4541     e8/call  write/disp32
4542     # . . discard args
4543     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4544     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4545     # . . push args
4546     68/push  _test-output-buffered-file/imm32
4547     68/push  _test-input-stream/imm32
4548     # . . call
4549     e8/call  convert-instruction/disp32
4550     # . . discard args
4551     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4552     # check output
4553     # . flush(_test-output-buffered-file)
4554     # . . push args
4555     68/push  _test-output-buffered-file/imm32
4556     # . . call
4557     e8/call  flush/disp32
4558     # . . discard args
4559     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4560 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4594     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
4595     # . . push args
4596     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4597     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
4598     68/push  _test-output-stream/imm32
4599     # . . call
4600     e8/call  check-stream-equal/disp32
4601     # . . discard args
4602     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4603     # . epilog
4604     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4605     5d/pop-to-EBP
4606     c3/return
4607 
4608 test-convert-instruction-emits-modrm-byte:
4609     # pack mod, rm32 and r32 operands into ModR/M byte
4610     # . prolog
4611     55/push-EBP
4612     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4613     # setup
4614     # . clear-stream(_test-input-stream)
4615     # . . push args
4616     68/push  _test-input-stream/imm32
4617     # . . call
4618     e8/call  clear-stream/disp32
4619     # . . discard args
4620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4621     # . clear-stream(_test-output-stream)
4622     # . . push args
4623     68/push  _test-output-stream/imm32
4624     # . . call
4625     e8/call  clear-stream/disp32
4626     # . . discard args
4627     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4628     # . clear-stream(_test-output-buffered-file+4)
4629     # . . push args
4630     b8/copy-to-EAX  _test-output-buffered-file/imm32
4631     05/add-to-EAX  4/imm32
4632     50/push-EAX
4633     # . . call
4634     e8/call  clear-stream/disp32
4635     # . . discard args
4636     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4637     # initialize input
4638     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
4639     # . . push args
4640     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
4641     68/push  _test-input-stream/imm32
4642     # . . call
4643     e8/call  write/disp32
4644     # . . discard args
4645     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4646     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4647     # . . push args
4648     68/push  _test-output-buffered-file/imm32
4649     68/push  _test-input-stream/imm32
4650     # . . call
4651     e8/call  convert-instruction/disp32
4652     # . . discard args
4653     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4654     # check output
4655     # . flush(_test-output-buffered-file)
4656     # . . push args
4657     68/push  _test-output-buffered-file/imm32
4658     # . . call
4659     e8/call  flush/disp32
4660     # . . discard args
4661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4662 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4696     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
4697     # . . push args
4698     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
4699     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
4700     68/push  _test-output-stream/imm32
4701     # . . call
4702     e8/call  check-stream-equal/disp32
4703     # . . discard args
4704     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4705     # . epilog
4706     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4707     5d/pop-to-EBP
4708     c3/return
4709 
4710 test-convert-instruction-emits-modrm-byte-from-subop:
4711     # pack mod, rm32 and subop operands into ModR/M byte
4712     # . prolog
4713     55/push-EBP
4714     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4715     # setup
4716     # . clear-stream(_test-input-stream)
4717     # . . push args
4718     68/push  _test-input-stream/imm32
4719     # . . call
4720     e8/call  clear-stream/disp32
4721     # . . discard args
4722     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4723     # . clear-stream(_test-output-stream)
4724     # . . push args
4725     68/push  _test-output-stream/imm32
4726     # . . call
4727     e8/call  clear-stream/disp32
4728     # . . discard args
4729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4730     # . clear-stream(_test-output-buffered-file+4)
4731     # . . push args
4732     b8/copy-to-EAX  _test-output-buffered-file/imm32
4733     05/add-to-EAX  4/imm32
4734     50/push-EAX
4735     # . . call
4736     e8/call  clear-stream/disp32
4737     # . . discard args
4738     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4739     # initialize input
4740     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
4741     # . . push args
4742     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
4743     68/push  _test-input-stream/imm32
4744     # . . call
4745     e8/call  write/disp32
4746     # . . discard args
4747     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4748     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4749     # . . push args
4750     68/push  _test-output-buffered-file/imm32
4751     68/push  _test-input-stream/imm32
4752     # . . call
4753     e8/call  convert-instruction/disp32
4754     # . . discard args
4755     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4756     # check output
4757     # . flush(_test-output-buffered-file)
4758     # . . push args
4759     68/push  _test-output-buffered-file/imm32
4760     # . . call
4761     e8/call  flush/disp32
4762     # . . discard args
4763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4764 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4798     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
4799     # . . push args
4800     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
4801     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
4802     68/push  _test-output-stream/imm32
4803     # . . call
4804     e8/call  check-stream-equal/disp32
4805     # . . discard args
4806     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4807     # . epilog
4808     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4809     5d/pop-to-EBP
4810     c3/return
4811 
4812 test-convert-instruction-emits-modrm-byte-with-missing-mod:
4813     # pack rm32 and r32 operands into ModR/M byte
4814     # . prolog
4815     55/push-EBP
4816     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4817     # setup
4818     # . clear-stream(_test-input-stream)
4819     # . . push args
4820     68/push  _test-input-stream/imm32
4821     # . . call
4822     e8/call  clear-stream/disp32
4823     # . . discard args
4824     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4825     # . clear-stream(_test-output-stream)
4826     # . . push args
4827     68/push  _test-output-stream/imm32
4828     # . . call
4829     e8/call  clear-stream/disp32
4830     # . . discard args
4831     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4832     # . clear-stream(_test-output-buffered-file+4)
4833     # . . push args
4834     b8/copy-to-EAX  _test-output-buffered-file/imm32
4835     05/add-to-EAX  4/imm32
4836     50/push-EAX
4837     # . . call
4838     e8/call  clear-stream/disp32
4839     # . . discard args
4840     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4841     # initialize input
4842     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
4843     # . . push args
4844     68/push  "8b/copy 0/rm32 1/r32"/imm32
4845     68/push  _test-input-stream/imm32
4846     # . . call
4847     e8/call  write/disp32
4848     # . . discard args
4849     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4850     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4851     # . . push args
4852     68/push  _test-output-buffered-file/imm32
4853     68/push  _test-input-stream/imm32
4854     # . . call
4855     e8/call  convert-instruction/disp32
4856     # . . discard args
4857     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4858     # check output
4859     # . flush(_test-output-buffered-file)
4860     # . . push args
4861     68/push  _test-output-buffered-file/imm32
4862     # . . call
4863     e8/call  flush/disp32
4864     # . . discard args
4865     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4866 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4900     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
4901     # . . push args
4902     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
4903     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
4904     68/push  _test-output-stream/imm32
4905     # . . call
4906     e8/call  check-stream-equal/disp32
4907     # . . discard args
4908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4909     # . epilog
4910     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4911     5d/pop-to-EBP
4912     c3/return
4913 
4914 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
4915     # pack mod and r32 operands into ModR/M byte
4916     # . prolog
4917     55/push-EBP
4918     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4919     # setup
4920     # . clear-stream(_test-input-stream)
4921     # . . push args
4922     68/push  _test-input-stream/imm32
4923     # . . call
4924     e8/call  clear-stream/disp32
4925     # . . discard args
4926     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4927     # . clear-stream(_test-output-stream)
4928     # . . push args
4929     68/push  _test-output-stream/imm32
4930     # . . call
4931     e8/call  clear-stream/disp32
4932     # . . discard args
4933     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4934     # . clear-stream(_test-output-buffered-file+4)
4935     # . . push args
4936     b8/copy-to-EAX  _test-output-buffered-file/imm32
4937     05/add-to-EAX  4/imm32
4938     50/push-EAX
4939     # . . call
4940     e8/call  clear-stream/disp32
4941     # . . discard args
4942     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4943     # initialize input
4944     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
4945     # . . push args
4946     68/push  "8b/copy 0/mod 1/r32"/imm32
4947     68/push  _test-input-stream/imm32
4948     # . . call
4949     e8/call  write/disp32
4950     # . . discard args
4951     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4952     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4953     # . . push args
4954     68/push  _test-output-buffered-file/imm32
4955     68/push  _test-input-stream/imm32
4956     # . . call
4957     e8/call  convert-instruction/disp32
4958     # . . discard args
4959     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4960     # check output
4961     # . flush(_test-output-buffered-file)
4962     # . . push args
4963     68/push  _test-output-buffered-file/imm32
4964     # . . call
4965     e8/call  flush/disp32
4966     # . . discard args
4967     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4968 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5002     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
5003     # . . push args
5004     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
5005     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
5006     68/push  _test-output-stream/imm32
5007     # . . call
5008     e8/call  check-stream-equal/disp32
5009     # . . discard args
5010     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5011     # . epilog
5012     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5013     5d/pop-to-EBP
5014     c3/return
5015 
5016 test-convert-instruction-emits-modrm-byte-with-missing-r32:
5017     # pack mod and rm32 operands into ModR/M byte
5018     # . prolog
5019     55/push-EBP
5020     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5021     # setup
5022     # . clear-stream(_test-input-stream)
5023     # . . push args
5024     68/push  _test-input-stream/imm32
5025     # . . call
5026     e8/call  clear-stream/disp32
5027     # . . discard args
5028     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5029     # . clear-stream(_test-output-stream)
5030     # . . push args
5031     68/push  _test-output-stream/imm32
5032     # . . call
5033     e8/call  clear-stream/disp32
5034     # . . discard args
5035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5036     # . clear-stream(_test-output-buffered-file+4)
5037     # . . push args
5038     b8/copy-to-EAX  _test-output-buffered-file/imm32
5039     05/add-to-EAX  4/imm32
5040     50/push-EAX
5041     # . . call
5042     e8/call  clear-stream/disp32
5043     # . . discard args
5044     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5045     # initialize input
5046     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
5047     # . . push args
5048     68/push  "8b/copy 0/mod 0/rm32"/imm32
5049     68/push  _test-input-stream/imm32
5050     # . . call
5051     e8/call  write/disp32
5052     # . . discard args
5053     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5054     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5055     # . . push args
5056     68/push  _test-output-buffered-file/imm32
5057     68/push  _test-input-stream/imm32
5058     # . . call
5059     e8/call  convert-instruction/disp32
5060     # . . discard args
5061     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5062     # check output
5063     # . flush(_test-output-buffered-file)
5064     # . . push args
5065     68/push  _test-output-buffered-file/imm32
5066     # . . call
5067     e8/call  flush/disp32
5068     # . . discard args
5069     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5070 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5104     # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
5105     # . . push args
5106     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
5107     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
5108     68/push  _test-output-stream/imm32
5109     # . . call
5110     e8/call  check-stream-equal/disp32
5111     # . . discard args
5112     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5113     # . epilog
5114     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5115     5d/pop-to-EBP
5116     c3/return
5117 
5118 test-convert-instruction-emits-sib-byte:
5119     # pack base, index and scale operands into SIB byte
5120     # . prolog
5121     55/push-EBP
5122     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5123     # setup
5124     # . clear-stream(_test-input-stream)
5125     # . . push args
5126     68/push  _test-input-stream/imm32
5127     # . . call
5128     e8/call  clear-stream/disp32
5129     # . . discard args
5130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5131     # . clear-stream(_test-output-stream)
5132     # . . push args
5133     68/push  _test-output-stream/imm32
5134     # . . call
5135     e8/call  clear-stream/disp32
5136     # . . discard args
5137     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5138     # . clear-stream(_test-output-buffered-file+4)
5139     # . . push args
5140     b8/copy-to-EAX  _test-output-buffered-file/imm32
5141     05/add-to-EAX  4/imm32
5142     50/push-EAX
5143     # . . call
5144     e8/call  clear-stream/disp32
5145     # . . discard args
5146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5147     # initialize input
5148     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
5149     # . . push args
5150     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
5151     68/push  _test-input-stream/imm32
5152     # . . call
5153     e8/call  write/disp32
5154     # . . discard args
5155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5156     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5157     # . . push args
5158     68/push  _test-output-buffered-file/imm32
5159     68/push  _test-input-stream/imm32
5160     # . . call
5161     e8/call  convert-instruction/disp32
5162     # . . discard args
5163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5164     # check output
5165     # . flush(_test-output-buffered-file)
5166     # . . push args
5167     68/push  _test-output-buffered-file/imm32
5168     # . . call
5169     e8/call  flush/disp32
5170     # . . discard args
5171     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5172 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5206     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
5207     # . . push args
5208     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
5209     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
5210     68/push  _test-output-stream/imm32
5211     # . . call
5212     e8/call  check-stream-equal/disp32
5213     # . . discard args
5214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5215     # . epilog
5216     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5217     5d/pop-to-EBP
5218     c3/return
5219 
5220 test-convert-instruction-emits-sib-byte-with-missing-base:
5221     # pack index and scale operands into SIB byte
5222     # . prolog
5223     55/push-EBP
5224     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5225     # setup
5226     # . clear-stream(_test-input-stream)
5227     # . . push args
5228     68/push  _test-input-stream/imm32
5229     # . . call
5230     e8/call  clear-stream/disp32
5231     # . . discard args
5232     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5233     # . clear-stream(_test-output-stream)
5234     # . . push args
5235     68/push  _test-output-stream/imm32
5236     # . . call
5237     e8/call  clear-stream/disp32
5238     # . . discard args
5239     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5240     # . clear-stream(_test-output-buffered-file+4)
5241     # . . push args
5242     b8/copy-to-EAX  _test-output-buffered-file/imm32
5243     05/add-to-EAX  4/imm32
5244     50/push-EAX
5245     # . . call
5246     e8/call  clear-stream/disp32
5247     # . . discard args
5248     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5249     # initialize input
5250     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
5251     # . . push args
5252     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5253     68/push  _test-input-stream/imm32
5254     # . . call
5255     e8/call  write/disp32
5256     # . . discard args
5257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5258     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5259     # . . push args
5260     68/push  _test-output-buffered-file/imm32
5261     68/push  _test-input-stream/imm32
5262     # . . call
5263     e8/call  convert-instruction/disp32
5264     # . . discard args
5265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5266     # check output
5267     # . flush(_test-output-buffered-file)
5268     # . . push args
5269     68/push  _test-output-buffered-file/imm32
5270     # . . call
5271     e8/call  flush/disp32
5272     # . . discard args
5273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5274 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5308     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
5309     # . . push args
5310     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
5311     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5312     68/push  _test-output-stream/imm32
5313     # . . call
5314     e8/call  check-stream-equal/disp32
5315     # . . discard args
5316     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5317     # . epilog
5318     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5319     5d/pop-to-EBP
5320     c3/return
5321 
5322 test-convert-instruction-emits-sib-byte-with-missing-index:
5323     # pack base and scale operands into SIB byte
5324     # . prolog
5325     55/push-EBP
5326     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5327     # setup
5328     # . clear-stream(_test-input-stream)
5329     # . . push args
5330     68/push  _test-input-stream/imm32
5331     # . . call
5332     e8/call  clear-stream/disp32
5333     # . . discard args
5334     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5335     # . clear-stream(_test-output-stream)
5336     # . . push args
5337     68/push  _test-output-stream/imm32
5338     # . . call
5339     e8/call  clear-stream/disp32
5340     # . . discard args
5341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5342     # . clear-stream(_test-output-buffered-file+4)
5343     # . . push args
5344     b8/copy-to-EAX  _test-output-buffered-file/imm32
5345     05/add-to-EAX  4/imm32
5346     50/push-EAX
5347     # . . call
5348     e8/call  clear-stream/disp32
5349     # . . discard args
5350     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5351     # initialize input
5352     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
5353     # . . push args
5354     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5355     68/push  _test-input-stream/imm32
5356     # . . call
5357     e8/call  write/disp32
5358     # . . discard args
5359     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5360     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5361     # . . push args
5362     68/push  _test-output-buffered-file/imm32
5363     68/push  _test-input-stream/imm32
5364     # . . call
5365     e8/call  convert-instruction/disp32
5366     # . . discard args
5367     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5368     # check output
5369     # . flush(_test-output-buffered-file)
5370     # . . push args
5371     68/push  _test-output-buffered-file/imm32
5372     # . . call
5373     e8/call  flush/disp32
5374     # . . discard args
5375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5376 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5410     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
5411     # . . push args
5412     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
5413     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5414     68/push  _test-output-stream/imm32
5415     # . . call
5416     e8/call  check-stream-equal/disp32
5417     # . . discard args
5418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5419     # . epilog
5420     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5421     5d/pop-to-EBP
5422     c3/return
5423 
5424 test-convert-instruction-emits-sib-byte-with-missing-scale:
5425     # pack base and index operands into SIB byte
5426     # . prolog
5427     55/push-EBP
5428     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5429     # setup
5430     # . clear-stream(_test-input-stream)
5431     # . . push args
5432     68/push  _test-input-stream/imm32
5433     # . . call
5434     e8/call  clear-stream/disp32
5435     # . . discard args
5436     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5437     # . clear-stream(_test-output-stream)
5438     # . . push args
5439     68/push  _test-output-stream/imm32
5440     # . . call
5441     e8/call  clear-stream/disp32
5442     # . . discard args
5443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5444     # . clear-stream(_test-output-buffered-file+4)
5445     # . . push args
5446     b8/copy-to-EAX  _test-output-buffered-file/imm32
5447     05/add-to-EAX  4/imm32
5448     50/push-EAX
5449     # . . call
5450     e8/call  clear-stream/disp32
5451     # . . discard args
5452     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5453     # initialize input
5454     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
5455     # . . push args
5456     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5457     68/push  _test-input-stream/imm32
5458     # . . call
5459     e8/call  write/disp32
5460     # . . discard args
5461     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5462     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5463     # . . push args
5464     68/push  _test-output-buffered-file/imm32
5465     68/push  _test-input-stream/imm32
5466     # . . call
5467     e8/call  convert-instruction/disp32
5468     # . . discard args
5469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5470     # check output
5471     # . flush(_test-output-buffered-file)
5472     # . . push args
5473     68/push  _test-output-buffered-file/imm32
5474     # . . call
5475     e8/call  flush/disp32
5476     # . . discard args
5477     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5478 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5512     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
5513     # . . push args
5514     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
5515     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5516     68/push  _test-output-stream/imm32
5517     # . . call
5518     e8/call  check-stream-equal/disp32
5519     # . . discard args
5520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5521     # . epilog
5522     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5523     5d/pop-to-EBP
5524     c3/return
5525 
5526 test-convert-instruction-handles-disp32-operand:
5527     # expand /disp32 operand into 4 bytes
5528     # . prolog
5529     55/push-EBP
5530     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5531     # setup
5532     # . clear-stream(_test-input-stream)
5533     # . . push args
5534     68/push  _test-input-stream/imm32
5535     # . . call
5536     e8/call  clear-stream/disp32
5537     # . . discard args
5538     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5539     # . clear-stream(_test-output-stream)
5540     # . . push args
5541     68/push  _test-output-stream/imm32
5542     # . . call
5543     e8/call  clear-stream/disp32
5544     # . . discard args
5545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5546     # . clear-stream(_test-output-buffered-file+4)
5547     # . . push args
5548     b8/copy-to-EAX  _test-output-buffered-file/imm32
5549     05/add-to-EAX  4/imm32
5550     50/push-EAX
5551     # . . call
5552     e8/call  clear-stream/disp32
5553     # . . discard args
5554     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5555     # initialize input
5556     # . write(_test-input-stream, "e8/call 20/disp32")
5557     # . . push args
5558     68/push  "e8/call 20/disp32"/imm32
5559     68/push  _test-input-stream/imm32
5560     # . . call
5561     e8/call  write/disp32
5562     # . . discard args
5563     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5564     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5565     # . . push args
5566     68/push  _test-output-buffered-file/imm32
5567     68/push  _test-input-stream/imm32
5568     # . . call
5569     e8/call  convert-instruction/disp32
5570     # . . discard args
5571     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5572     # check output
5573     # . flush(_test-output-buffered-file)
5574     # . . push args
5575     68/push  _test-output-buffered-file/imm32
5576     # . . call
5577     e8/call  flush/disp32
5578     # . . discard args
5579     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5580 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5614     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
5615     # . . push args
5616     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
5617     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
5618     68/push  _test-output-stream/imm32
5619     # . . call
5620     e8/call  check-stream-equal/disp32
5621     # . . discard args
5622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5623     # . epilog
5624     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5625     5d/pop-to-EBP
5626     c3/return
5627 
5628 test-convert-instruction-handles-disp16-operand:
5629     # expand /disp16 operand into 2 bytes
5630     # . prolog
5631     55/push-EBP
5632     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5633     # setup
5634     # . clear-stream(_test-input-stream)
5635     # . . push args
5636     68/push  _test-input-stream/imm32
5637     # . . call
5638     e8/call  clear-stream/disp32
5639     # . . discard args
5640     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5641     # . clear-stream(_test-output-stream)
5642     # . . push args
5643     68/push  _test-output-stream/imm32
5644     # . . call
5645     e8/call  clear-stream/disp32
5646     # . . discard args
5647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5648     # . clear-stream(_test-output-buffered-file+4)
5649     # . . push args
5650     b8/copy-to-EAX  _test-output-buffered-file/imm32
5651     05/add-to-EAX  4/imm32
5652     50/push-EAX
5653     # . . call
5654     e8/call  clear-stream/disp32
5655     # . . discard args
5656     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5657     # initialize input
5658     # . write(_test-input-stream, "e8/call 20/disp16")
5659     # . . push args
5660     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
5661     68/push  _test-input-stream/imm32
5662     # . . call
5663     e8/call  write/disp32
5664     # . . discard args
5665     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5666     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5667     # . . push args
5668     68/push  _test-output-buffered-file/imm32
5669     68/push  _test-input-stream/imm32
5670     # . . call
5671     e8/call  convert-instruction/disp32
5672     # . . discard args
5673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5674     # check output
5675     # . flush(_test-output-buffered-file)
5676     # . . push args
5677     68/push  _test-output-buffered-file/imm32
5678     # . . call
5679     e8/call  flush/disp32
5680     # . . discard args
5681     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5682 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5716     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
5717     # . . push args
5718     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
5719     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
5720     68/push  _test-output-stream/imm32
5721     # . . call
5722     e8/call  check-stream-equal/disp32
5723     # . . discard args
5724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5725     # . epilog
5726     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5727     5d/pop-to-EBP
5728     c3/return
5729 
5730 test-convert-instruction-handles-disp8-operand:
5731     # expand /disp8 operand into 1 byte
5732     # . prolog
5733     55/push-EBP
5734     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5735     # setup
5736     # . clear-stream(_test-input-stream)
5737     # . . push args
5738     68/push  _test-input-stream/imm32
5739     # . . call
5740     e8/call  clear-stream/disp32
5741     # . . discard args
5742     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5743     # . clear-stream(_test-output-stream)
5744     # . . push args
5745     68/push  _test-output-stream/imm32
5746     # . . call
5747     e8/call  clear-stream/disp32
5748     # . . discard args
5749     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5750     # . clear-stream(_test-output-buffered-file+4)
5751     # . . push args
5752     b8/copy-to-EAX  _test-output-buffered-file/imm32
5753     05/add-to-EAX  4/imm32
5754     50/push-EAX
5755     # . . call
5756     e8/call  clear-stream/disp32
5757     # . . discard args
5758     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5759     # initialize input
5760     # . write(_test-input-stream, "eb/jump 20/disp8")
5761     # . . push args
5762     68/push  "eb/jump 20/disp8"/imm32
5763     68/push  _test-input-stream/imm32
5764     # . . call
5765     e8/call  write/disp32
5766     # . . discard args
5767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5768     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5769     # . . push args
5770     68/push  _test-output-buffered-file/imm32
5771     68/push  _test-input-stream/imm32
5772     # . . call
5773     e8/call  convert-instruction/disp32
5774     # . . discard args
5775     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5776     # check output
5777     # . flush(_test-output-buffered-file)
5778     # . . push args
5779     68/push  _test-output-buffered-file/imm32
5780     # . . call
5781     e8/call  flush/disp32
5782     # . . discard args
5783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5784 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5818     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
5819     # . . push args
5820     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
5821     68/push  "eb 20  # eb/jump 20/disp8"/imm32
5822     68/push  _test-output-stream/imm32
5823     # . . call
5824     e8/call  check-stream-equal/disp32
5825     # . . discard args
5826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5827     # . epilog
5828     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5829     5d/pop-to-EBP
5830     c3/return
5831 
5832 test-convert-instruction-handles-disp8-name:
5833     # pass /disp8 name directly through
5834     # . prolog
5835     55/push-EBP
5836     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5837     # setup
5838     # . clear-stream(_test-input-stream)
5839     # . . push args
5840     68/push  _test-input-stream/imm32
5841     # . . call
5842     e8/call  clear-stream/disp32
5843     # . . discard args
5844     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5845     # . clear-stream(_test-output-stream)
5846     # . . push args
5847     68/push  _test-output-stream/imm32
5848     # . . call
5849     e8/call  clear-stream/disp32
5850     # . . discard args
5851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5852     # . clear-stream(_test-output-buffered-file+4)
5853     # . . push args
5854     b8/copy-to-EAX  _test-output-buffered-file/imm32
5855     05/add-to-EAX  4/imm32
5856     50/push-EAX
5857     # . . call
5858     e8/call  clear-stream/disp32
5859     # . . discard args
5860     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5861     # initialize input
5862     # . write(_test-input-stream, "eb/jump xyz/disp8")
5863     # . . push args
5864     68/push  "eb/jump xyz/disp8"/imm32
5865     68/push  _test-input-stream/imm32
5866     # . . call
5867     e8/call  write/disp32
5868     # . . discard args
5869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5870     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5871     # . . push args
5872     68/push  _test-output-buffered-file/imm32
5873     68/push  _test-input-stream/imm32
5874     # . . call
5875     e8/call  convert-instruction/disp32
5876     # . . discard args
5877     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5878     # check output
5879     # . flush(_test-output-buffered-file)
5880     # . . push args
5881     68/push  _test-output-buffered-file/imm32
5882     # . . call
5883     e8/call  flush/disp32
5884     # . . discard args
5885     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5886 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5920     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
5921     # . . push args
5922     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
5923     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
5924     68/push  _test-output-stream/imm32
5925     # . . call
5926     e8/call  check-stream-equal/disp32
5927     # . . discard args
5928     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5929     # . epilog
5930     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5931     5d/pop-to-EBP
5932     c3/return
5933 
5934 test-convert-instruction-handles-imm32-operand:
5935     # expand /imm32 operand into 4 bytes
5936     # . prolog
5937     55/push-EBP
5938     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5939     # setup
5940     # . clear-stream(_test-input-stream)
5941     # . . push args
5942     68/push  _test-input-stream/imm32
5943     # . . call
5944     e8/call  clear-stream/disp32
5945     # . . discard args
5946     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5947     # . clear-stream(_test-output-stream)
5948     # . . push args
5949     68/push  _test-output-stream/imm32
5950     # . . call
5951     e8/call  clear-stream/disp32
5952     # . . discard args
5953     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5954     # . clear-stream(_test-output-buffered-file+4)
5955     # . . push args
5956     b8/copy-to-EAX  _test-output-buffered-file/imm32
5957     05/add-to-EAX  4/imm32
5958     50/push-EAX
5959     # . . call
5960     e8/call  clear-stream/disp32
5961     # . . discard args
5962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5963     # initialize input
5964     # . write(_test-input-stream, "68/push 0x20/imm32")
5965     # . . push args
5966     68/push  "68/push 0x20/imm32"/imm32
5967     68/push  _test-input-stream/imm32
5968     # . . call
5969     e8/call  write/disp32
5970     # . . discard args
5971     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5972     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5973     # . . push args
5974     68/push  _test-output-buffered-file/imm32
5975     68/push  _test-input-stream/imm32
5976     # . . call
5977     e8/call  convert-instruction/disp32
5978     # . . discard args
5979     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5980     # check output
5981     # . flush(_test-output-buffered-file)
5982     # . . push args
5983     68/push  _test-output-buffered-file/imm32
5984     # . . call
5985     e8/call  flush/disp32
5986     # . . discard args
5987     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5988 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
6022     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
6023     # . . push args
6024     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
6025     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
6026     68/push  _test-output-stream/imm32
6027     # . . call
6028     e8/call  check-stream-equal/disp32
6029     # . . discard args
6030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6031     # . epilog
6032     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6033     5d/pop-to-EBP
6034     c3/return
6035 
6036 test-convert-instruction-handles-imm16-operand:
6037     # expand /imm16 operand into 2 bytes
6038     # we don't have one of these at the moment, so this expands to an invalid instruction
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-input-stream)
6044     # . . push args
6045     68/push  _test-input-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     # . clear-stream(_test-output-stream)
6051     # . . push args
6052     68/push  _test-output-stream/imm32
6053     # . . call
6054     e8/call  clear-stream/disp32
6055     # . . discard args
6056     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6057     # . clear-stream(_test-output-buffered-file+4)
6058     # . . push args
6059     b8/copy-to-EAX  _test-output-buffered-file/imm32
6060     05/add-to-EAX  4/imm32
6061     50/push-EAX
6062     # . . call
6063     e8/call  clear-stream/disp32
6064     # . . discard args
6065     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6066     # initialize input
6067     # . write(_test-input-stream, "68/push 0x20/imm16")
6068     # . . push args
6069     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
6070     68/push  _test-input-stream/imm32
6071     # . . call
6072     e8/call  write/disp32
6073     # . . discard args
6074     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6075     # convert-instruction(_test-input-stream, _test-output-buffered-file)
6076     # . . push args
6077     68/push  _test-output-buffered-file/imm32
6078     68/push  _test-input-stream/imm32
6079     # . . call
6080     e8/call  convert-instruction/disp32
6081     # . . discard args
6082     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6083     # check output
6084     # . flush(_test-output-buffered-file)
6085     # . . push args
6086     68/push  _test-output-buffered-file/imm32
6087     # . . call
6088     e8/call  flush/disp32
6089     # . . discard args
6090     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6091 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
6125     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
6126     # . . push args
6127     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
6128     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
6129     68/push  _test-output-stream/imm32
6130     # . . call
6131     e8/call  check-stream-equal/disp32
6132     # . . discard args
6133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6134     # . epilog
6135     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6136     5d/pop-to-EBP
6137     c3/return
6138 
6139 test-convert-instruction-handles-imm8-operand:
6140     # expand /imm8 operand into 1 byte
6141     # we don't have one of these at the moment, so this expands to an invalid instruction
6142     # . prolog
6143     55/push-EBP
6144     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6145     # setup
6146     # . clear-stream(_test-input-stream)
6147     # . . push args
6148     68/push  _test-input-stream/imm32
6149     # . . call
6150     e8/call  clear-stream/disp32
6151     # . . discard args
6152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6153     # . clear-stream(_test-output-stream)
6154     # . . push args
6155     68/push  _test-output-stream/imm32
6156     # . . call
6157     e8/call  clear-stream/disp32
6158     # . . discard args
6159     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6160     # . clear-stream(_test-output-buffered-file+4)
6161     # . . push args
6162     b8/copy-to-EAX  _test-output-buffered-file/imm32
6163     05/add-to-EAX  4/imm32
6164     50/push-EAX
6165     # . . call
6166     e8/call  clear-stream/disp32
6167     # . . discard args
6168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6169     # initialize input
6170     # . write(_test-input-stream, "68/push 0x20/imm8")
6171     # . . push args
6172     68/push  "68/push 0x20/imm8"/imm32
6173     68/push  _test-input-stream/imm32
6174     # . . call
6175     e8/call  write/disp32
6176     # . . discard args
6177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6178     # convert-instruction(_test-input-stream, _test-output-buffered-file)
6179     # . . push args
6180     68/push  _test-output-buffered-file/imm32
6181     68/push  _test-input-stream/imm32
6182     # . . call
6183     e8/call  convert-instruction/disp32
6184     # . . discard args
6185     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6186     # check output
6187     # . flush(_test-output-buffered-file)
6188     # . . push args
6189     68/push  _test-output-buffered-file/imm32
6190     # . . call
6191     e8/call  flush/disp32
6192     # . . discard args
6193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6194 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
6228     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
6229     # . . push args
6230     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
6231     68/push  "68 20  # 68/push 0x20/imm8"/imm32
6232     68/push  _test-output-stream/imm32
6233     # . . call
6234     e8/call  check-stream-equal/disp32
6235     # . . discard args
6236     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6237     # . epilog
6238     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6239     5d/pop-to-EBP
6240     c3/return
6241 
6242 has-metadata?:  # word : (address slice), s : (address string) -> EAX : boolean
6243     # pseudocode:
6244     #   var twig : &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
6245     #   curr = twig->end
6246     #   while true
6247     #     twig = next-token-from-slice(curr, word->end, '/')
6248     #     if (twig.empty()) break
6249     #     if (slice-equal?(twig, s)) return true
6250     #     curr = twig->end
6251     #   return false
6252     # . prolog
6253     55/push-EBP
6254     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6255     # . save registers
6256     51/push-ECX
6257     52/push-EDX
6258     56/push-ESI
6259     57/push-EDI
6260     # ESI = word
6261     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
6262     # EDX = word->end
6263     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
6264     # var twig/EDI : (address slice) = {0, 0}
6265     68/push  0/imm32/end
6266     68/push  0/imm32/start
6267     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
6268     # next-token-from-slice(word->start, word->end, '/', twig)
6269     # . . push args
6270     57/push-EDI
6271     68/push  0x2f/imm32/slash
6272     52/push-EDX
6273     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
6274     # . . call
6275     e8/call  next-token-from-slice/disp32
6276     # . . discard args
6277     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
6278     # curr/ECX = twig->end
6279     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
6280 $has-metadata?:loop:
6281     # next-token-from-slice(curr, word->end, '/', twig)
6282     # . . push args
6283     57/push-EDI
6284     68/push  0x2f/imm32/slash
6285     52/push-EDX
6286     51/push-ECX
6287     # . . call
6288     e8/call  next-token-from-slice/disp32
6289     # . . discard args
6290     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
6291     # if (slice-empty?(twig)) return false
6292     # . EAX = slice-empty?(twig)
6293     # . . push args
6294     57/push-EDI
6295     # . . call
6296     e8/call  slice-empty?/disp32
6297     # . . discard args
6298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6299     # . if (EAX != 0) return false
6300     3d/compare-EAX-and  0/imm32
6301     75/jump-if-not-equal  $has-metadata?:false/disp8
6302     # if (slice-equal?(twig, s)) return true
6303     # . EAX = slice-equal?(twig, s)
6304     # . . push args
6305     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
6306     57/push-EDI
6307     # . . call
6308     e8/call  slice-equal?/disp32
6309     # . . discard args
6310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6311     # . if (EAX != 0) return true
6312     3d/compare-EAX-and  0/imm32
6313     75/jump-if-not-equal  $has-metadata?:true/disp8
6314     # curr = twig->end
6315     8b/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(EDI+4) to ECX
6316     eb/jump  $has-metadata?:loop/disp8
6317 $has-metadata?:true:
6318     b8/copy-to-EAX  1/imm32/true
6319     eb/jump  $has-metadata?:end/disp8
6320 $has-metadata?:false:
6321     b8/copy-to-EAX  0/imm32/false
6322 $has-metadata?:end:
6323     # . reclaim locals
6324     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6325     # . restore registers
6326     5f/pop-to-EDI
6327     5e/pop-to-ESI
6328     5a/pop-to-EDX
6329     59/pop-to-ECX
6330     # . epilog
6331     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6332     5d/pop-to-EBP
6333     c3/return
6334 
6335 test-has-metadata-true:
6336     # . prolog
6337     55/push-EBP
6338     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6339     # (EAX..ECX) = "ab/c"
6340     b8/copy-to-EAX  "ab/c"/imm32
6341     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6342     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
6343     05/add-to-EAX  4/imm32
6344     # var in/ESI : (address slice) = {EAX, ECX}
6345     51/push-ECX
6346     50/push-EAX
6347     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6348     # EAX = has-metadata?(ESI, "c")
6349     # . . push args
6350     68/push  "c"/imm32
6351     56/push-ESI
6352     # . . call
6353     e8/call  has-metadata?/disp32
6354     # . . discard args
6355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6356     # check-ints-equal(EAX, 1, msg)
6357     # . . push args
6358     68/push  "F - test-has-metadata-true"/imm32
6359     68/push  1/imm32/true
6360     50/push-EAX
6361     # . . call
6362     e8/call  check-ints-equal/disp32
6363     # . . discard args
6364     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6365     # . epilog
6366     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6367     5d/pop-to-EBP
6368     c3/return
6369 
6370 test-has-metadata-false:
6371     # . prolog
6372     55/push-EBP
6373     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6374     # (EAX..ECX) = "ab/c"
6375     b8/copy-to-EAX  "ab/c"/imm32
6376     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6377     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
6378     05/add-to-EAX  4/imm32
6379     # var in/ESI : (address slice) = {EAX, ECX}
6380     51/push-ECX
6381     50/push-EAX
6382     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6383     # EAX = has-metadata?(ESI, "c")
6384     # . . push args
6385     68/push  "d"/imm32
6386     56/push-ESI
6387     # . . call
6388     e8/call  has-metadata?/disp32
6389     # . . discard args
6390     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6391     # check-ints-equal(EAX, 0, msg)
6392     # . . push args
6393     68/push  "F - test-has-metadata-false"/imm32
6394     68/push  0/imm32/false
6395     50/push-EAX
6396     # . . call
6397     e8/call  check-ints-equal/disp32
6398     # . . discard args
6399     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6400     # . epilog
6401     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6402     5d/pop-to-EBP
6403     c3/return
6404 
6405 test-has-metadata-ignore-name:
6406     # . prolog
6407     55/push-EBP
6408     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6409     # (EAX..ECX) = "a/b"
6410     b8/copy-to-EAX  "a/b"/imm32
6411     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6412     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
6413     05/add-to-EAX  4/imm32
6414     # var in/ESI : (address slice) = {EAX, ECX}
6415     51/push-ECX
6416     50/push-EAX
6417     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6418     # EAX = has-metadata?(ESI, "a")
6419     # . . push args
6420     68/push  "a"/imm32
6421     56/push-ESI
6422     # . . call
6423     e8/call  has-metadata?/disp32
6424     # . . discard args
6425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6426     # check-ints-equal(EAX, 0, msg)
6427     # . . push args
6428     68/push  "F - test-has-metadata-ignore-name"/imm32
6429     68/push  0/imm32/false
6430     50/push-EAX
6431     # . . call
6432     e8/call  check-ints-equal/disp32
6433     # . . discard args
6434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
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-has-metadata-multiple-true:
6441     # . prolog
6442     55/push-EBP
6443     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6444     # (EAX..ECX) = "a/b/c"
6445     b8/copy-to-EAX  "a/b/c"/imm32
6446     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6447     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
6448     05/add-to-EAX  4/imm32
6449     # var in/ESI : (address slice) = {EAX, ECX}
6450     51/push-ECX
6451     50/push-EAX
6452     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6453     # EAX = has-metadata?(ESI, "c")
6454     # . . push args
6455     68/push  "c"/imm32
6456     56/push-ESI
6457     # . . call
6458     e8/call  has-metadata?/disp32
6459     # . . discard args
6460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6461     # check-ints-equal(EAX, 1, msg)
6462     # . . push args
6463     68/push  "F - test-has-metadata-multiple-true"/imm32
6464     68/push  1/imm32/true
6465     50/push-EAX
6466     # . . call
6467     e8/call  check-ints-equal/disp32
6468     # . . discard args
6469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6470     # . epilog
6471     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6472     5d/pop-to-EBP
6473     c3/return
6474 
6475 test-has-metadata-multiple-false:
6476     # . prolog
6477     55/push-EBP
6478     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6479     # (EAX..ECX) = "a/b/c"
6480     b8/copy-to-EAX  "a/b/c"/imm32
6481     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
6482     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
6483     05/add-to-EAX  4/imm32
6484     # var in/ESI : (address slice) = {EAX, ECX}
6485     51/push-ECX
6486     50/push-EAX
6487     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           4/r32/ESP   .               .                 # copy ESP to ESI
6488     # EAX = has-metadata?(ESI, "d")
6489     # . . push args
6490     68/push  "d"/imm32
6491     56/push-ESI
6492     # . . call
6493     e8/call  has-metadata?/disp32
6494     # . . discard args
6495     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6496     # check-ints-equal(EAX, 0, msg)
6497     # . . push args
6498     68/push  "F - test-has-metadata-multiple-false"/imm32
6499     68/push  0/imm32/false
6500     50/push-EAX
6501     # . . call
6502     e8/call  check-ints-equal/disp32
6503     # . . discard args
6504     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6505     # . epilog
6506     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6507     5d/pop-to-EBP
6508     c3/return
6509 
6510 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print
6511 # it in 'width' bytes of hex, least significant first.
6512 # Otherwise just print the entire word including metadata.
6513 # Always print a trailing space.
6514 emit:  # out : (address buffered-file), word : (address slice), width : int -> <void>
6515     # . prolog
6516     55/push-EBP
6517     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6518     # . save registers
6519     50/push-EAX
6520     56/push-ESI
6521     57/push-EDI
6522     # ESI = word
6523     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
6524     # var name/EDI : (address slice) = {0, 0}
6525     68/push  0/imm32/end
6526     68/push  0/imm32/start
6527     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDI
6528     # datum = next-token-from-slice(word->start, word->end, '/')
6529     # . . push args
6530     57/push-EDI
6531     68/push  0x2f/imm32/slash
6532     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
6533     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
6534     # . . call
6535     e8/call  next-token-from-slice/disp32
6536     # . . discard args
6537     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
6538     # if (is-valid-name?(datum)) write-slice(out, word) and return
6539     # . EAX = is-valid-name?(name)
6540     # . . push args
6541     57/push-EDI
6542     # . . call
6543     e8/call  is-valid-name?/disp32
6544     # . . discard args
6545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6546     # . if (EAX != 0)
6547     3d/compare-EAX-and  0/imm32
6548     74/jump-if-equal  $emit:hex-int/disp8
6549 $emit:name:
6550     # . write-slice(out, word)
6551     # . . push args
6552     56/push-ESI
6553     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
6554     # . . call
6555     e8/call  write-slice/disp32
6556     # . . discard args
6557     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6558     # . write-buffered(out, " ")
6559     # . . push args
6560     68/push  " "/imm32
6561     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
6562     # . . call
6563     e8/call  write-buffered/disp32
6564     # . . discard args
6565     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6566     # . return
6567     eb/jump  $emit:end/disp8
6568     # otherwise emit-hex(out, parse-hex-int(datum), width)
6569     #   (Weird shit can happen here if the datum of 'word' isn't either a valid
6570     #   name or a hex number, but we're only going to be passing in real legal
6571     #   programs. We just want to make sure that valid names aren't treated as
6572     #   (valid) hex numbers.)
6573 $emit:hex-int:
6574     # . value/EAX = parse-hex-int(datum)
6575     # . . push args
6576     57/push-EDI
6577     # . . call
6578     e8/call  parse-hex-int/disp32
6579     # . . discard args
6580     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6581     # . emit-hex(out, value, width)
6582     # . . push args
6583     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
6584     50/push-EAX
6585     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
6586     # . . call
6587     e8/call  emit-hex/disp32
6588     # . . discard args
6589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6590 $emit:end:
6591     # . reclaim locals
6592     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
6593     # . restore registers
6594     5f/pop-to-EDI
6595     5e/pop-to-ESI
6596     58/pop-to-EAX
6597     # . epilog
6598     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6599     5d/pop-to-EBP
6600     c3/return
6601 
6602 test-emit-number:
6603     # . prolog
6604     55/push-EBP
6605     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6606     # setup
6607     # . clear-stream(_test-output-stream)
6608     # . . push args
6609     68/push  _test-output-stream/imm32
6610     # . . call
6611     e8/call  clear-stream/disp32
6612     # . . discard args
6613     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6614     # . clear-stream(_test-output-buffered-file+4)
6615     # . . push args
6616     b8/copy-to-EAX  _test-output-buffered-file/imm32
6617     05/add-to-EAX  4/imm32
6618     50/push-EAX
6619     # . . call
6620     e8/call  clear-stream/disp32
6621     # . . discard args
6622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6623     # var slice/ECX = "30"
6624     68/push  _test-slice-three-zero-end/imm32/end
6625     68/push  _test-slice-three-zero/imm32/start
6626     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6627     # emit(_test-output-buffered-file, slice, 1)
6628     # . . push args
6629     68/push  1/imm32
6630     51/push-ECX
6631     68/push  _test-output-buffered-file/imm32
6632     # . . call
6633     e8/call  emit/disp32
6634     # . . discard args
6635     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6636     # flush(_test-output-buffered-file)
6637     # . . push args
6638     68/push  _test-output-buffered-file/imm32
6639     # . . call
6640     e8/call  flush/disp32
6641     # . . discard args
6642     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6643     # check-stream-equal(_test-output-stream, "30 ", msg)
6644     # . . push args
6645     68/push  "F - test-emit-number/1"/imm32
6646     68/push  "30 "/imm32
6647     68/push  _test-output-stream/imm32
6648     # . . call
6649     e8/call  check-stream-equal/disp32
6650     # . . discard args
6651     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6652     # . epilog
6653     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6654     5d/pop-to-EBP
6655     c3/return
6656 
6657 test-emit-negative-number:
6658     # test support for sign-extending negative numbers
6659     # . prolog
6660     55/push-EBP
6661     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6662     # setup
6663     # . clear-stream(_test-output-stream)
6664     # . . push args
6665     68/push  _test-output-stream/imm32
6666     # . . call
6667     e8/call  clear-stream/disp32
6668     # . . discard args
6669     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6670     # . clear-stream(_test-output-buffered-file+4)
6671     # . . push args
6672     b8/copy-to-EAX  _test-output-buffered-file/imm32
6673     05/add-to-EAX  4/imm32
6674     50/push-EAX
6675     # . . call
6676     e8/call  clear-stream/disp32
6677     # . . discard args
6678     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6679     # var slice/ECX = "-2"
6680     68/push  _test-slice-negative-two-end/imm32/end
6681     68/push  _test-slice-negative-two/imm32/start
6682     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6683     # emit(_test-output-buffered-file, slice, 2)
6684     # . . push args
6685     68/push  2/imm32
6686     51/push-ECX
6687     68/push  _test-output-buffered-file/imm32
6688     # . . call
6689     e8/call  emit/disp32
6690     # . . discard args
6691     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6692     # flush(_test-output-buffered-file)
6693     # . . push args
6694     68/push  _test-output-buffered-file/imm32
6695     # . . call
6696     e8/call  flush/disp32
6697     # . . discard args
6698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6699     # check-stream-equal(_test-output-stream, "fe ff ", msg)
6700     # . . push args
6701     68/push  "F - test-emit-number/1"/imm32
6702     68/push  "fe ff "/imm32
6703     68/push  _test-output-stream/imm32
6704     # . . call
6705     e8/call  check-stream-equal/disp32
6706     # . . discard args
6707     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6708     # . epilog
6709     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6710     5d/pop-to-EBP
6711     c3/return
6712 
6713 test-emit-number-with-metadata:
6714     # . prolog
6715     55/push-EBP
6716     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6717     # setup
6718     # . clear-stream(_test-output-stream)
6719     # . . push args
6720     68/push  _test-output-stream/imm32
6721     # . . call
6722     e8/call  clear-stream/disp32
6723     # . . discard args
6724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6725     # . clear-stream(_test-output-buffered-file+4)
6726     # . . push args
6727     b8/copy-to-EAX  _test-output-buffered-file/imm32
6728     05/add-to-EAX  4/imm32
6729     50/push-EAX
6730     # . . call
6731     e8/call  clear-stream/disp32
6732     # . . discard args
6733     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6734     # var slice/ECX = "-2/foo"
6735     68/push  _test-slice-negative-two-metadata-end/imm32/end
6736     68/push  _test-slice-negative-two/imm32/start
6737     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6738     # emit(_test-output-buffered-file, slice, 2)
6739     # . . push args
6740     68/push  2/imm32
6741     51/push-ECX
6742     68/push  _test-output-buffered-file/imm32
6743     # . . call
6744     e8/call  emit/disp32
6745     # . . discard args
6746     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6747     # flush(_test-output-buffered-file)
6748     # . . push args
6749     68/push  _test-output-buffered-file/imm32
6750     # . . call
6751     e8/call  flush/disp32
6752     # . . discard args
6753     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6754     # the '/foo' will have no impact on the output
6755     # check-stream-equal(_test-output-stream, "fe ff ", msg)
6756     # . . push args
6757     68/push  "F - test-emit-number-with-metadata"/imm32
6758     68/push  "fe ff "/imm32
6759     68/push  _test-output-stream/imm32
6760     # . . call
6761     e8/call  check-stream-equal/disp32
6762     # . . discard args
6763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6764     # . epilog
6765     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6766     5d/pop-to-EBP
6767     c3/return
6768 
6769 test-emit-non-number:
6770     # . prolog
6771     55/push-EBP
6772     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6773     # setup
6774     # . clear-stream(_test-output-stream)
6775     # . . push args
6776     68/push  _test-output-stream/imm32
6777     # . . call
6778     e8/call  clear-stream/disp32
6779     # . . discard args
6780     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6781     # . clear-stream(_test-output-buffered-file+4)
6782     # . . push args
6783     b8/copy-to-EAX  _test-output-buffered-file/imm32
6784     05/add-to-EAX  4/imm32
6785     50/push-EAX
6786     # . . call
6787     e8/call  clear-stream/disp32
6788     # . . discard args
6789     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6790     # var slice/ECX = "xyz"
6791     68/push  _test-slice-non-number-word-end/imm32/end
6792     68/push  _test-slice-non-number-word/imm32/start
6793     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6794     # emit(_test-output-buffered-file, slice, 2)
6795     # . . push args
6796     68/push  2/imm32
6797     51/push-ECX
6798     68/push  _test-output-buffered-file/imm32
6799     # . . call
6800     e8/call  emit/disp32
6801     # . . discard args
6802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6803     # flush(_test-output-buffered-file)
6804     # . . push args
6805     68/push  _test-output-buffered-file/imm32
6806     # . . call
6807     e8/call  flush/disp32
6808     # . . discard args
6809     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6810     # check-stream-equal(_test-output-stream, "xyz", msg)
6811     # . . push args
6812     68/push  "F - test-emit-non-number"/imm32
6813     68/push  "xyz "/imm32
6814     68/push  _test-output-stream/imm32
6815     # . . call
6816     e8/call  check-stream-equal/disp32
6817     # . . discard args
6818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6819     # . epilog
6820     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6821     5d/pop-to-EBP
6822     c3/return
6823 
6824 test-emit-non-number-with-metadata:
6825     # . prolog
6826     55/push-EBP
6827     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6828     # setup
6829     # . clear-stream(_test-output-stream)
6830     # . . push args
6831     68/push  _test-output-stream/imm32
6832     # . . call
6833     e8/call  clear-stream/disp32
6834     # . . discard args
6835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6836     # . clear-stream(_test-output-buffered-file+4)
6837     # . . push args
6838     b8/copy-to-EAX  _test-output-buffered-file/imm32
6839     05/add-to-EAX  4/imm32
6840     50/push-EAX
6841     # . . call
6842     e8/call  clear-stream/disp32
6843     # . . discard args
6844     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6845     # var slice/ECX = "xyz/"
6846     68/push  _test-slice-non-number-word-metadata-end/imm32/end
6847     68/push  _test-slice-non-number-word/imm32/start
6848     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6849     # emit(_test-output-buffered-file, slice, 2)
6850     # . . push args
6851     68/push  2/imm32
6852     51/push-ECX
6853     68/push  _test-output-buffered-file/imm32
6854     # . . call
6855     e8/call  emit/disp32
6856     # . . discard args
6857     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6858     # flush(_test-output-buffered-file)
6859     # . . push args
6860     68/push  _test-output-buffered-file/imm32
6861     # . . call
6862     e8/call  flush/disp32
6863     # . . discard args
6864     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6865     # check-stream-equal(_test-output-stream, "xyz/", msg)
6866     # . . push args
6867     68/push  "F - test-emit-non-number-with-metadata"/imm32
6868     68/push  "xyz/ "/imm32
6869     68/push  _test-output-stream/imm32
6870     # . . call
6871     e8/call  check-stream-equal/disp32
6872     # . . discard args
6873     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6874     # . epilog
6875     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6876     5d/pop-to-EBP
6877     c3/return
6878 
6879 test-emit-non-number-with-all-hex-digits-and-metadata:
6880     # . prolog
6881     55/push-EBP
6882     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6883     # setup
6884     # . clear-stream(_test-output-stream)
6885     # . . push args
6886     68/push  _test-output-stream/imm32
6887     # . . call
6888     e8/call  clear-stream/disp32
6889     # . . discard args
6890     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6891     # . clear-stream(_test-output-buffered-file+4)
6892     # . . push args
6893     b8/copy-to-EAX  _test-output-buffered-file/imm32
6894     05/add-to-EAX  4/imm32
6895     50/push-EAX
6896     # . . call
6897     e8/call  clear-stream/disp32
6898     # . . discard args
6899     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6900     # var slice/ECX = "abcd/xyz"
6901     68/push  _test-slice-hexlike-non-number-word-metadata-end/imm32/end
6902     68/push  _test-slice-hexlike-non-number-word/imm32/start
6903     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
6904     # emit(_test-output-buffered-file, slice, 2)
6905     # . . push args
6906     68/push  2/imm32
6907     51/push-ECX
6908     68/push  _test-output-buffered-file/imm32
6909     # . . call
6910     e8/call  emit/disp32
6911     # . . discard args
6912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6913     # flush(_test-output-buffered-file)
6914     # . . push args
6915     68/push  _test-output-buffered-file/imm32
6916     # . . call
6917     e8/call  flush/disp32
6918     # . . discard args
6919     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
6920 +-- 34 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
6954     # check-stream-equal(_test-output-stream, "abcd/xyz")
6955     # . . push args
6956     68/push  "F - test-emit-non-number-with-all-hex-digits"/imm32
6957     68/push  "abcd/xyz "/imm32
6958     68/push  _test-output-stream/imm32
6959     # . . call
6960     e8/call  check-stream-equal/disp32
6961     # . . discard args
6962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
6963     # . epilog
6964     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
6965     5d/pop-to-EBP
6966     c3/return
6967 
6968 # conditions for 'valid' names that are not at risk of looking like hex numbers
6969 # keep in sync with the rules in labels.cc
6970 #: - if it starts with a digit, it's treated as a number. If it can't be
6971 #:   parsed as hex it will raise an error.
6972 #: - if it starts with '-' it's treated as a number.
6973 #: - if it starts with '0x' it's treated as a number. (redundant)
6974 #: - if it's two characters long, it can't be a name. Either it's a hex
6975 #:   byte, or it raises an error.
6976 is-valid-name?:  # in : (address slice) -> EAX : boolean
6977     # . prolog
6978     55/push-EBP
6979     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
6980     # . save registers
6981     51/push-ECX
6982     56/push-ESI
6983     # ESI = in
6984     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
6985     # start/ECX = in->start
6986     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
6987     # end/EAX = in->end
6988     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
6989 $is-valid-name?:check0:
6990     # if (start >= end) return false
6991     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # compare ECX with EAX
6992     7d/jump-if-greater-or-equal  $is-valid-name?:false/disp8
6993 $is-valid-name?:check1:
6994     # EAX -= ECX
6995     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
6996     # if (EAX == 2) return false
6997     3d/compare-EAX-and  2/imm32
6998     74/jump-if-equal  $is-valid-name?:false/disp8
6999 $is-valid-name?:check2:
7000     # c/EAX = *ECX
7001     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
7002     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
7003     # if (c == "-") return false
7004     3d/compare-EAX-and  2d/imm32/-
7005     74/jump-if-equal  $is-valid-name?:false/disp8
7006 $is-valid-name?:check3a:
7007     # if (c < "0") return true
7008     3d/compare-EAX-with  30/imm32/0
7009     7c/jump-if-lesser  $is-valid-name?:true/disp8
7010 $is-valid-name?:check3b:
7011     # if (c > "9") return true
7012     3d/compare-EAX-with  39/imm32/9
7013     7f/jump-if-greater  $is-valid-name?:true/disp8
7014 $is-valid-name?:false:
7015     # return false
7016     b8/copy-to-EAX  0/imm32/false
7017     eb/jump  $is-valid-name?:end/disp8
7018 $is-valid-name?:true:
7019     # return true
7020     b8/copy-to-EAX  1/imm32/true
7021 $is-valid-name?:end:
7022     # . restore registers
7023     5e/pop-to-ESI
7024     59/pop-to-ECX
7025     # . epilog
7026     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7027     5d/pop-to-EBP
7028     c3/return
7029 
7030 test-is-valid-name-digit-prefix:
7031     # . prolog
7032     55/push-EBP
7033     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7034     # var slice/ECX = "34"
7035     68/push  _test-slice-hex-int-end/imm32
7036     68/push  _test-slice-hex-int/imm32
7037     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7038     # EAX = is-valid-name?(slice)
7039     # . . push args
7040     51/push-ECX
7041     # . . call
7042     e8/call  is-valid-name?/disp32
7043     # . . discard args
7044     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7045     # check-ints-equal(EAX, 0, msg)
7046     # . . push args
7047     68/push  "F - test-is-valid-name-digit-prefix"/imm32
7048     68/push  0/imm32/false
7049     50/push-EAX
7050     # . . call
7051     e8/call  check-ints-equal/disp32
7052     # . . discard args
7053     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7054     # . epilog
7055     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7056     5d/pop-to-EBP
7057     c3/return
7058 
7059 test-is-valid-name-negative-prefix:
7060     # . prolog
7061     55/push-EBP
7062     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7063     # var slice/ECX = "-0x34"
7064     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
7065     68/push  _test-slice-hex-int-with-0x-prefix-negative/imm32
7066     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7067     # EAX = is-valid-name?(slice)
7068     # . . push args
7069     51/push-ECX
7070     # . . call
7071     e8/call  is-valid-name?/disp32
7072     # . . discard args
7073     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7074     # check-ints-equal(EAX, 0, msg)
7075     # . . push args
7076     68/push  "F - test-is-valid-name-negative-prefix"/imm32
7077     68/push  0/imm32/false
7078     50/push-EAX
7079     # . . call
7080     e8/call  check-ints-equal/disp32
7081     # . . discard args
7082     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7083     # . epilog
7084     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7085     5d/pop-to-EBP
7086     c3/return
7087 
7088 test-is-valid-name-0x-prefix:
7089     # . prolog
7090     55/push-EBP
7091     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7092     # var slice/ECX = "0x34"
7093     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
7094     68/push  _test-slice-hex-int-with-0x-prefix/imm32
7095     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7096     # EAX = is-valid-name?(slice)
7097     # . . push args
7098     51/push-ECX
7099     # . . call
7100     e8/call  is-valid-name?/disp32
7101     # . . discard args
7102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7103     # check-ints-equal(EAX, 0, msg)
7104     # . . push args
7105     68/push  "F - test-is-valid-name-0x-prefix"/imm32
7106     68/push  0/imm32/false
7107     50/push-EAX
7108     # . . call
7109     e8/call  check-ints-equal/disp32
7110     # . . discard args
7111     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7112     # . epilog
7113     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7114     5d/pop-to-EBP
7115     c3/return
7116 
7117 test-is-valid-name-starts-with-pre-digit:
7118     # . prolog
7119     55/push-EBP
7120     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7121     # var slice/ECX = "/03"
7122     68/push  _test-slice-with-slash-prefix-end/imm32
7123     68/push  _test-slice-with-slash-prefix/imm32
7124     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7125     # EAX = is-valid-name?(slice)
7126     # . . push args
7127     51/push-ECX
7128     # . . call
7129     e8/call  is-valid-name?/disp32
7130     # . . discard args
7131     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7132     # check-ints-equal(EAX, 1, msg)
7133     # . . push args
7134     68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
7135     68/push  1/imm32/true
7136     50/push-EAX
7137     # . . call
7138     e8/call  check-ints-equal/disp32
7139     # . . discard args
7140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7141     # . epilog
7142     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7143     5d/pop-to-EBP
7144     c3/return
7145 
7146 test-is-valid-name-starts-with-post-digit:
7147     # . prolog
7148     55/push-EBP
7149     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7150     # var slice/ECX = "q34"
7151     68/push  _test-slice-char-and-digits-end/imm32
7152     68/push  _test-slice-char-and-digits/imm32
7153     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7154     # EAX = is-valid-name?(slice)
7155     # . . push args
7156     51/push-ECX
7157     # . . call
7158     e8/call  is-valid-name?/disp32
7159     # . . discard args
7160     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7161     # check-ints-equal(EAX, 1, msg)
7162     # . . push args
7163     68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
7164     68/push  1/imm32/true
7165     50/push-EAX
7166     # . . call
7167     e8/call  check-ints-equal/disp32
7168     # . . discard args
7169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7170     # . epilog
7171     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7172     5d/pop-to-EBP
7173     c3/return
7174 
7175 test-is-valid-name-starts-with-digit:
7176     # . prolog
7177     55/push-EBP
7178     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7179     # var slice/ECX = "0x34"
7180     68/push  _test-slice-hex-int-with-0x-prefix-end/imm32
7181     68/push  _test-slice-hex-int-with-0x-prefix/imm32
7182     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7183     # EAX = is-valid-name?(slice)
7184     # . . push args
7185     51/push-ECX
7186     # . . call
7187     e8/call  is-valid-name?/disp32
7188     # . . discard args
7189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7190     # check-ints-equal(EAX, 0, msg)
7191     # . . push args
7192     68/push  "F - test-is-valid-name-starts-with-digit"/imm32
7193     68/push  0/imm32/false
7194     50/push-EAX
7195     # . . call
7196     e8/call  check-ints-equal/disp32
7197     # . . discard args
7198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7199     # . epilog
7200     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7201     5d/pop-to-EBP
7202     c3/return
7203 
7204 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
7205 emit-hex:  # out : (address buffered-file), n : int, width : int -> <void>
7206     # . prolog
7207     55/push-EBP
7208     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7209     # . save registers
7210     50/push-EAX
7211     51/push-ECX
7212     52/push-EDX
7213     53/push-EBX
7214     57/push-EDI
7215     # EDI = out
7216     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   8/disp8         .                 # copy *(EBP+8) to EDI
7217     # EBX = n
7218     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           3/r32/EBX   0xc/disp8       .                 # copy *(EBP+12) to EBX
7219     # EDX = width
7220     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0x10/disp8      .                 # copy *(EBP+16) to EDX
7221     # var curr/ECX = 0
7222     31/xor                          3/mod/direct    1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # clear ECX
7223 $emit-hex:loop:
7224     # if (curr >= width) break
7225     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX and EDX
7226     7d/jump-if-greater-or-equal  $emit-hex:end/disp8
7227     # print-byte(out, EBX)
7228     # . . push args
7229     53/push-EBX
7230     57/push-EDI
7231     # . . call
7232     e8/call  print-byte/disp32
7233     # . . discard args
7234     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7235     # write-byte(out, ' ')
7236     # . . push args
7237     68/push  0x20/imm32/space
7238     57/push-EDI
7239     # . . call
7240     e8/call  write-byte/disp32
7241     # . . discard args
7242     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7243     # EBX = EBX >> 8
7244     c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/EBX    .           .             .           .           .               8/imm8            # shift EBX right by 8 bits, while padding zeroes
7245 $emit-hex:continue:
7246     # ++curr
7247     41/increment-ECX
7248     eb/jump  $emit-hex:loop/disp8
7249 $emit-hex:end:
7250     # . restore registers
7251     5f/pop-to-EDI
7252     5b/pop-to-EBX
7253     5a/pop-to-EDX
7254     59/pop-to-ECX
7255     58/pop-to-EAX
7256     # . epilog
7257     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7258     5d/pop-to-EBP
7259     c3/return
7260 
7261 test-emit-hex-single-byte:
7262     # setup
7263     # . clear-stream(_test-output-stream)
7264     # . . push args
7265     68/push  _test-output-stream/imm32
7266     # . . call
7267     e8/call  clear-stream/disp32
7268     # . . discard args
7269     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7270     # . clear-stream(_test-output-buffered-file+4)
7271     # . . push args
7272     b8/copy-to-EAX  _test-output-buffered-file/imm32
7273     05/add-to-EAX  4/imm32
7274     50/push-EAX
7275     # . . call
7276     e8/call  clear-stream/disp32
7277     # . . discard args
7278     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7279     # emit-hex(_test-output-buffered-file, 0xab, 1)
7280     # . . push args
7281     68/push  1/imm32
7282     68/push  0xab/imm32
7283     68/push  _test-output-buffered-file/imm32
7284     # . . call
7285     e8/call  emit-hex/disp32
7286     # . . discard args
7287     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7288     # flush(_test-output-buffered-file)
7289     # . . push args
7290     68/push  _test-output-buffered-file/imm32
7291     # . . call
7292     e8/call  flush/disp32
7293     # . . discard args
7294     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7295     # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
7296     # . . push args
7297     68/push  "F - test-emit-hex-single-byte"/imm32
7298     68/push  0x206261/imm32
7299     # . . push *_test-output-stream->data
7300     b8/copy-to-EAX  _test-output-stream/imm32
7301     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           0xc/disp8       .                 # push *(EAX+12)
7302     # . . call
7303     e8/call  check-ints-equal/disp32
7304     # . . discard args
7305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7306     # . end
7307     c3/return
7308 
7309 test-emit-hex-multiple-byte:
7310     # setup
7311     # . clear-stream(_test-output-stream)
7312     # . . push args
7313     68/push  _test-output-stream/imm32
7314     # . . call
7315     e8/call  clear-stream/disp32
7316     # . . discard args
7317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7318     # . clear-stream(_test-output-buffered-file+4)
7319     # . . push args
7320     b8/copy-to-EAX  _test-output-buffered-file/imm32
7321     05/add-to-EAX  4/imm32
7322     50/push-EAX
7323     # . . call
7324     e8/call  clear-stream/disp32
7325     # . . discard args
7326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7327     # emit-hex(_test-output-buffered-file, 0x1234, 2)
7328     # . . push args
7329     68/push  2/imm32
7330     68/push  0x1234/imm32
7331     68/push  _test-output-buffered-file/imm32
7332     # . . call
7333     e8/call  emit-hex/disp32
7334     # . . discard args
7335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7336     # flush(_test-output-buffered-file)
7337     # . . push args
7338     68/push  _test-output-buffered-file/imm32
7339     # . . call
7340     e8/call  flush/disp32
7341     # . . discard args
7342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7343     # check-stream-equal(_test-output-stream, "34 12 ", msg)
7344     # . . push args
7345     68/push  "F - test-emit-hex-multiple-byte/1"/imm32
7346     68/push  "34 12 "/imm32
7347     68/push  _test-output-stream/imm32
7348     # . . call
7349     e8/call  check-stream-equal/disp32
7350     # . . discard args
7351     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7352     # . end
7353     c3/return
7354 
7355 test-emit-hex-zero-pad:
7356     # setup
7357     # . clear-stream(_test-output-stream)
7358     # . . push args
7359     68/push  _test-output-stream/imm32
7360     # . . call
7361     e8/call  clear-stream/disp32
7362     # . . discard args
7363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7364     # . clear-stream(_test-output-buffered-file+4)
7365     # . . push args
7366     b8/copy-to-EAX  _test-output-buffered-file/imm32
7367     05/add-to-EAX  4/imm32
7368     50/push-EAX
7369     # . . call
7370     e8/call  clear-stream/disp32
7371     # . . discard args
7372     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7373     # emit-hex(_test-output-buffered-file, 0xab, 2)
7374     # . . push args
7375     68/push  2/imm32
7376     68/push  0xab/imm32
7377     68/push  _test-output-buffered-file/imm32
7378     # . . call
7379     e8/call  emit-hex/disp32
7380     # . . discard args
7381     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7382     # flush(_test-output-buffered-file)
7383     # . . push args
7384     68/push  _test-output-buffered-file/imm32
7385     # . . call
7386     e8/call  flush/disp32
7387     # . . discard args
7388     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7389     # check(_test-output-stream->data == 'ab 00 ')
7390     # . . push args
7391     68/push  "F - test-emit-hex-zero-pad/1"/imm32
7392     68/push  "ab 00 "/imm32
7393     68/push  _test-output-stream/imm32
7394     # . . call
7395     e8/call  check-stream-equal/disp32
7396     # . . discard args
7397     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7398     # . end
7399     c3/return
7400 
7401 test-emit-hex-negative:
7402     # setup
7403     # . clear-stream(_test-output-stream)
7404     # . . push args
7405     68/push  _test-output-stream/imm32
7406     # . . call
7407     e8/call  clear-stream/disp32
7408     # . . discard args
7409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7410     # . clear-stream(_test-output-buffered-file+4)
7411     # . . push args
7412     b8/copy-to-EAX  _test-output-buffered-file/imm32
7413     05/add-to-EAX  4/imm32
7414     50/push-EAX
7415     # . . call
7416     e8/call  clear-stream/disp32
7417     # . . discard args
7418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7419     # emit-hex(_test-output-buffered-file, -1, 2)
7420     # . . push args
7421     68/push  2/imm32
7422     68/push  -1/imm32
7423     68/push  _test-output-buffered-file/imm32
7424     # . . call
7425     e8/call  emit-hex/disp32
7426     # . . discard args
7427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7428     # flush(_test-output-buffered-file)
7429     # . . push args
7430     68/push  _test-output-buffered-file/imm32
7431     # . . call
7432     e8/call  flush/disp32
7433     # . . discard args
7434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7435     # check-stream-equal(_test-output-stream == "ff ff ")
7436     # . . push args
7437     68/push  "F - test-emit-hex-negative/1"/imm32
7438     68/push  "ff ff "/imm32
7439     68/push  _test-output-stream/imm32
7440     # . . call
7441     e8/call  check-stream-equal/disp32
7442     # . . discard args
7443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7444     # . end
7445     c3/return
7446 
7447 # write an entire stream's contents to a buffered-file
7448 # ways to do this:
7449 #   - construct a 'maximal slice' and pass it to write-slice
7450 #   - flush the buffered-file and pass the stream directly to its fd (disabling buffering)
7451 # we'll go with the first way for now
7452 write-stream-data:  # f : (address buffered-file), s : (address stream) -> <void>
7453     # . prolog
7454     55/push-EBP
7455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7456     # . save registers
7457     50/push-EAX
7458     51/push-ECX
7459     56/push-ESI
7460     # ESI = s
7461     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   0xc/disp8       .                 # copy *(EBP+12) to ESI
7462     # var slice/ECX = {s->data, s->data + s->write}
7463     # . push s->data + s->write
7464     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
7465     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
7466     50/push-EAX
7467     # . push s->data
7468     8d/copy-address                 1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy ESI+12 to EAX
7469     50/push-EAX
7470     # . ECX = ESP
7471     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7472     # write-slice(f, slice)
7473     # . . push args
7474     51/push-ECX
7475     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
7476     # . . call
7477     e8/call  write-slice/disp32
7478     # . . discard args
7479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7480 $write-stream-data:end:
7481     # . restore locals
7482     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7483     # . restore registers
7484     5e/pop-to-ESI
7485     59/pop-to-ECX
7486     58/pop-to-EAX
7487     # . epilog
7488     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7489     5d/pop-to-EBP
7490     c3/return
7491 
7492 test-write-stream-data:
7493     # . prolog
7494     55/push-EBP
7495     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7496     # setup
7497     # . clear-stream(_test-output-stream)
7498     # . . push args
7499     68/push  _test-output-stream/imm32
7500     # . . call
7501     e8/call  clear-stream/disp32
7502     # . . discard args
7503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7504     # . clear-stream(_test-output-buffered-file+4)
7505     # . . push args
7506     b8/copy-to-EAX  _test-output-buffered-file/imm32
7507     05/add-to-EAX  4/imm32
7508     50/push-EAX
7509     # . . call
7510     e8/call  clear-stream/disp32
7511     # . . discard args
7512     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7513     # . clear-stream(_test-tmp-stream)
7514     # . . push args
7515     68/push  _test-tmp-stream/imm32
7516     # . . call
7517     e8/call  clear-stream/disp32
7518     # . . discard args
7519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7520     # initialize input
7521     # . write(_test-tmp-stream, "abcd")
7522     # . . push args
7523     68/push  "abcd"/imm32
7524     68/push  _test-tmp-stream/imm32
7525     # . . call
7526     e8/call  write/disp32
7527     # . . discard args
7528     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7529     # write-stream-data(_test-output-buffered-file, _test-tmp-stream)
7530     # . . push args
7531     68/push  _test-tmp-stream/imm32
7532     68/push  _test-output-buffered-file/imm32
7533     # . . call
7534     e8/call  write-stream-data/disp32
7535     # . . discard args
7536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7537     # check that the write happened as expected
7538     # . flush(_test-output-buffered-file)
7539     # . . push args
7540     68/push  _test-output-buffered-file/imm32
7541     # . . call
7542     e8/call  flush/disp32
7543     # . . discard args
7544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7545     # . check-stream-equal(_test-output-stream, "abcd", msg)
7546     # . . push args
7547     68/push  "F - test-write-stream-data"/imm32
7548     68/push  "abcd"/imm32
7549     68/push  _test-output-stream/imm32
7550     # . . call
7551     e8/call  check-stream-equal/disp32
7552     # . . discard args
7553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
7554     # . epilog
7555     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7556     5d/pop-to-EBP
7557     c3/return
7558 
7559 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/'))
7560 parse-datum-of-word:  # word : (address slice) -> value/EAX
7561     # . prolog
7562     55/push-EBP
7563     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
7564     # . save registers
7565     51/push-ECX
7566     56/push-ESI
7567     # ESI = word
7568     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
7569     # var slice/ECX = {0, 0}
7570     68/push  0/imm32/end
7571     68/push  0/imm32/start
7572     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
7573     # slice = next-token-from-slice(word->start, word->end, '/')
7574     # . . push args
7575     51/push-ECX
7576     68/push  0x2f/imm32/slash
7577     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
7578     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
7579     # . . call
7580     e8/call  next-token-from-slice/disp32
7581     # . . discard args
7582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
7583     # value/EAX = parse-hex-int(slice)
7584     # . . push args
7585     51/push-ECX
7586     # . . call
7587     e8/call  parse-hex-int/disp32
7588     # . . discard args
7589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
7590 $parse-datum-of-word:end:
7591     # . reclaim locals
7592     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
7593     # . restore registers
7594     5e/pop-to-ESI
7595     59/pop-to-ECX
7596     # . epilog
7597     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
7598     5d/pop-to-EBP
7599     c3/return
7600 
7601 == data
7602 
7603 _test-slice-negative-two:
7604     2d/- 32/2
7605 _test-slice-negative-two-end:
7606     2f/slash 66/f 6f/o 6f/o
7607 _test-slice-negative-two-metadata-end:
7608 
7609 _test-slice-three-zero:
7610     33/3 30/0
7611 _test-slice-three-zero-end:
7612 
7613 _test-slice-non-number-word:
7614     78/x 79/y 7a/z
7615 _test-slice-non-number-word-end:
7616     2f/slash
7617 _test-slice-non-number-word-metadata-end:
7618 
7619 _test-input-stream:
7620     # current write index
7621     0/imm32
7622     # current read index
7623     0/imm32
7624     # length
7625     0x80/imm32  # 128 bytes
7626     # data (8 lines x 16 bytes/line)
7627     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7628     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7629     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7630     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7631     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7632     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7633     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7634     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7635 
7636 # a test buffered file for _test-input-stream
7637 _test-input-buffered-file:
7638     # file descriptor or (address stream)
7639     _test-input-stream/imm32
7640     # current write index
7641     0/imm32
7642     # current read index
7643     0/imm32
7644     # length
7645     6/imm32
7646     # data
7647     00 00 00 00 00 00  # 6 bytes
7648 
7649 _test-output-stream:
7650     # current write index
7651     0/imm32
7652     # current read index
7653     0/imm32
7654     # length
7655     0x80/imm32  # 128 bytes
7656     # data (8 lines x 16 bytes/line)
7657     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7658     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7659     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7660     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7661     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7662     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7663     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7664     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
7665 
7666 # a test buffered file for _test-output-stream
7667 _test-output-buffered-file:
7668     # file descriptor or (address stream)
7669     _test-output-stream/imm32
7670     # current write index
7671     0/imm32
7672     # current read index
7673     0/imm32
7674     # length
7675     6/imm32
7676     # data
7677     00 00 00 00 00 00  # 6 bytes
7678 
7679 _test-slice-hexlike-non-number-word:
7680     61/a 62/b 63/c 64/d
7681 _test-slice-hexlike-non-number-word-end:
7682     2f/slash
7683     78/x 79/y 7a/z
7684 _test-slice-hexlike-non-number-word-metadata-end:
7685 
7686 _test-slice-with-slash-prefix:
7687   2f/slash 30/0 33/3
7688 _test-slice-with-slash-prefix-end:
7689 
7690 # . . vim:nowrap:textwidth=0