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     # initialize heap
  23     # . Heap = new-segment(64KB)
  24     # . . push args
  25     68/push  Heap/imm32
  26     68/push  0x10000/imm32/64KB
  27     # . . call
  28     e8/call  new-segment/disp32
  29     # . . discard args
  30     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  31 
  32     # . prolog
  33     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  34     # - if argc > 1 and argv[1] == "test", then return run_tests()
  35     # . argc > 1
  36     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  37     7e/jump-if-lesser-or-equal  $run-main/disp8
  38     # . argv[1] == "test"
  39     # . . push args
  40     68/push  "test"/imm32
  41     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  42     # . . call
  43     e8/call  kernel-string-equal?/disp32
  44     # . . discard args
  45     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  46     # . check result
  47     3d/compare-EAX-and  1/imm32
  48     75/jump-if-not-equal  $run-main/disp8
  49     # . run-tests()
  50     e8/call  run-tests/disp32
  51     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  52     eb/jump  $main:end/disp8
  53 $run-main:
  54     # - otherwise convert stdin
  55     # var ed/EAX : exit-descriptor
  56     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  57     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  58     # configure ed to really exit()
  59     # . ed->target = 0
  60     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  61     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  62     # . . push args
  63     50/push-EAX/ed
  64     68/push  Stderr/imm32
  65     68/push  Stdout/imm32
  66     68/push  Stdin/imm32
  67     # . . call
  68     e8/call  convert/disp32
  69     # . . discard args
  70     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  71     # . syscall(exit, 0)
  72     bb/copy-to-EBX  0/imm32
  73 $main:end:
  74     b8/copy-to-EAX  1/imm32/exit
  75     cd/syscall  0x80/imm8
  76 
  77 # - big picture
  78 # We'll operate on each line/instruction in isolation. That way we only need to
  79 # allocate memory for converting a single instruction.
  80 #
  81 # To pack an entire file, convert every segment in it
  82 # To convert a code segment, convert every instruction (line) until the next segment header
  83 # To convert a non-data segment, convert every word until the next segment header
  84 #
  85 # primary state: line
  86 #   stream of 512 bytes; abort if it ever overflows
  87 
  88 # conceptual hierarchy within a line:
  89 #   line = words separated by ' ', maybe followed by comment starting with '#'
  90 #   word = datum until '/', then 0 or more metadata separated by '/'
  91 #
  92 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
  93 #   next-token(stream, delim char) -> slice (start, end pointers)
  94 #   next-token-from-slice(start, end, delim char) -> slice
  95 #   slice-equal?(slice, string)
  96 
  97 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
  98     # pseudocode:
  99     #   var line = new-stream(512, 1)
 100     #   var in-code? = false
 101     #   while true
 102     #     clear-stream(line)
 103     #     read-line-buffered(in, line)
 104     #     if (line->write == 0) break             # end of file
 105     #     var word-slice = next-word(line)
 106     #     if slice-empty?(word-slice)             # whitespace
 107     #       write-stream-data(out, line)
 108     #     else if (slice-equal?(word-slice, "=="))
 109     #       word-slice = next-word(line)
 110     #       in-code? = slice-equal?(word-slice, "code")
 111     #       write-stream-data(out, line)
 112     #     else if (in-code?)
 113     #       rewind-stream(line)
 114     #       convert-instruction(line, out)
 115     #     else
 116     #       rewind-stream(line)
 117     #       convert-data(line, out)
 118     #   flush(out)
 119     #
 120     # . prolog
 121     55/push-EBP
 122     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 123     # . save registers
 124     50/push-EAX
 125     51/push-ECX
 126     52/push-EDX
 127     53/push-EBX
 128     # var line/ECX : (address stream byte) = stream(512)
 129     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
 130     68/push  0x200/imm32/length
 131     68/push  0/imm32/read
 132     68/push  0/imm32/write
 133     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 134     # var word-slice/EDX = {0, 0}
 135     68/push  0/imm32/end
 136     68/push  0/imm32/start
 137     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 138     # var in-code?/EBX = false
 139     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
 140 $convert:loop:
 141     # clear-stream(line)
 142     # . . push args
 143     51/push-ECX
 144     # . . call
 145     e8/call  clear-stream/disp32
 146     # . . discard args
 147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 148     # read-line-buffered(in, line)
 149     # . . push args
 150     51/push-ECX
 151     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 152     # . . call
 153     e8/call  read-line-buffered/disp32
 154     # . . discard args
 155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 156 $convert:check0:
 157     # if (line->write == 0) break
 158     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
 159     0f 84/jump-if-equal  $convert:break/disp32
 160 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 186     # next-word(line, word-slice)
 187     # . . push args
 188     52/push-EDX
 189     51/push-ECX
 190     # . . call
 191     e8/call  next-word/disp32
 192     # . . discard args
 193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 194 $convert:check1:
 195     # if (slice-empty?(word-slice)) write-stream-data(out, line)
 196     # . EAX = slice-empty?(word-slice)
 197     # . . push args
 198     52/push-EDX
 199     # . . call
 200     e8/call  slice-empty?/disp32
 201     # . . discard args
 202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 203     # . if (EAX != 0) write-stream-data(out, line)
 204     3d/compare-EAX-and  0/imm32
 205     0f 85/jump-if-not-equal  $convert:pass-through/disp32
 206 $convert:check2:
 207 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
 249     # if (!slice-equal?(word-slice, "==")) goto next check
 250     # . EAX = slice-equal?(word-slice, "==")
 251     # . . push args
 252     68/push  "=="/imm32
 253     52/push-EDX
 254     # . . call
 255     e8/call  slice-equal?/disp32
 256     # . . discard args
 257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 258     # . if (EAX == 0) goto check3
 259     3d/compare-EAX-and  0/imm32
 260     0f 84/jump-if-equal  $convert:check3/disp32
 261     # word-slice = next-word(line)
 262     # . . push args
 263     52/push-EDX
 264     51/push-ECX
 265     # . . call
 266     e8/call  next-word/disp32
 267     # . . discard args
 268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 269 +-- 42 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------
 311     # in-code? = slice-equal?(word-slice, "code")
 312     # . . push args
 313     68/push  "code"/imm32
 314     52/push-EDX
 315     # . . call
 316     e8/call  slice-equal?/disp32
 317     # . . discard args
 318     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 319     # . . in-code? = EAX
 320     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
 321     # write-stream-data(out, line)
 322     eb/jump  $convert:pass-through/disp8
 323 $convert:check3:
 324     # else rewind-stream(line)
 325     # . rewind-stream(line)
 326     # . . push args
 327     51/push-ECX
 328     # . . call
 329     e8/call  rewind-stream/disp32
 330     # . . discard args
 331     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 332     # if (in-code? != 0) convert-instruction(line, out)
 333     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
 334     74/jump-if-equal  $convert:data/disp8
 335 $convert:code:
 336     # . convert-instruction(line, out)
 337     # . . push args
 338     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 339     51/push-ECX
 340     # . . call
 341     e8/call  convert-instruction/disp32
 342     # . . discard args
 343     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 344     # . loop
 345     e9/jump  $convert:loop/disp32
 346 $convert:data:
 347     # else convert-data(line, out)
 348     # . . push args
 349     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 350     51/push-ECX
 351     # . . call
 352     e8/call  convert-data/disp32
 353     # . . discard args
 354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 355     # . loop
 356     e9/jump  $convert:loop/disp32
 357 $convert:pass-through:
 358     # write-stream-data(out, line)
 359     # . . push args
 360     51/push-ECX
 361     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 362     # . . call
 363     e8/call  write-stream-data/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 366     # . loop
 367     e9/jump  $convert:loop/disp32
 368 $convert:break:
 369     # flush(out)
 370     # . . push args
 371     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 372     # . . call
 373     e8/call  flush/disp32
 374     # . . discard args
 375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 376 $convert:end:
 377     # . reclaim locals
 378     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 379     # . restore registers
 380     5b/pop-to-EBX
 381     5a/pop-to-EDX
 382     59/pop-to-ECX
 383     58/pop-to-EAX
 384     # . epilog
 385     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 386     5d/pop-to-EBP
 387     c3/return
 388 
 389 test-convert-passes-empty-lines-through:
 390     # if a line is empty, pass it along unchanged
 391     # . prolog
 392     55/push-EBP
 393     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 394     # setup
 395     # . clear-stream(_test-input-stream)
 396     # . . push args
 397     68/push  _test-input-stream/imm32
 398     # . . call
 399     e8/call  clear-stream/disp32
 400     # . . discard args
 401     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 402     # . clear-stream(_test-input-buffered-file+4)
 403     # . . push args
 404     b8/copy-to-EAX  _test-input-buffered-file/imm32
 405     05/add-to-EAX  4/imm32
 406     50/push-EAX
 407     # . . call
 408     e8/call  clear-stream/disp32
 409     # . . discard args
 410     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 411     # . clear-stream(_test-output-stream)
 412     # . . push args
 413     68/push  _test-output-stream/imm32
 414     # . . call
 415     e8/call  clear-stream/disp32
 416     # . . discard args
 417     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 418     # . clear-stream(_test-output-buffered-file+4)
 419     # . . push args
 420     b8/copy-to-EAX  _test-output-buffered-file/imm32
 421     05/add-to-EAX  4/imm32
 422     50/push-EAX
 423     # . . call
 424     e8/call  clear-stream/disp32
 425     # . . discard args
 426     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 427     # write nothing to input
 428     # convert(_test-input-buffered-file, _test-output-buffered-file)
 429     # . . push args
 430     68/push  _test-output-buffered-file/imm32
 431     68/push  _test-input-buffered-file/imm32
 432     # . . call
 433     e8/call  convert/disp32
 434     # . . discard args
 435     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 436     # check that the line just passed through
 437     # . flush(_test-output-buffered-file)
 438     # . . push args
 439     68/push  _test-output-buffered-file/imm32
 440     # . . call
 441     e8/call  flush/disp32
 442     # . . discard args
 443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 444     # . check-stream-equal(_test-output-stream, "", msg)
 445     # . . push args
 446     68/push  "F - test-convert-passes-empty-lines-through"/imm32
 447     68/push  ""/imm32
 448     68/push  _test-output-stream/imm32
 449     # . . call
 450     e8/call  check-stream-equal/disp32
 451     # . . discard args
 452     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 453     # . epilog
 454     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 455     5d/pop-to-EBP
 456     c3/return
 457 
 458 test-convert-passes-lines-with-just-whitespace-through:
 459     # if a line is empty, pass it along unchanged
 460     # . prolog
 461     55/push-EBP
 462     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 463     # setup
 464     # . clear-stream(_test-input-stream)
 465     # . . push args
 466     68/push  _test-input-stream/imm32
 467     # . . call
 468     e8/call  clear-stream/disp32
 469     # . . discard args
 470     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 471     # . clear-stream(_test-input-buffered-file+4)
 472     # . . push args
 473     b8/copy-to-EAX  _test-input-buffered-file/imm32
 474     05/add-to-EAX  4/imm32
 475     50/push-EAX
 476     # . . call
 477     e8/call  clear-stream/disp32
 478     # . . discard args
 479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 480     # . clear-stream(_test-output-stream)
 481     # . . push args
 482     68/push  _test-output-stream/imm32
 483     # . . call
 484     e8/call  clear-stream/disp32
 485     # . . discard args
 486     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 487     # . clear-stream(_test-output-buffered-file+4)
 488     # . . push args
 489     b8/copy-to-EAX  _test-output-buffered-file/imm32
 490     05/add-to-EAX  4/imm32
 491     50/push-EAX
 492     # . . call
 493     e8/call  clear-stream/disp32
 494     # . . discard args
 495     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 496     # initialize input
 497     # . write(_test-input-stream, "    ")
 498     # . . push args
 499     68/push  "    "/imm32
 500     68/push  _test-input-stream/imm32
 501     # . . call
 502     e8/call  write/disp32
 503     # . . discard args
 504     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 505     # convert(_test-input-buffered-file, _test-output-buffered-file)
 506     # . . push args
 507     68/push  _test-output-buffered-file/imm32
 508     68/push  _test-input-buffered-file/imm32
 509     # . . call
 510     e8/call  convert/disp32
 511     # . . discard args
 512     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 513     # check that the line just passed through
 514     # . flush(_test-output-buffered-file)
 515     # . . push args
 516     68/push  _test-output-buffered-file/imm32
 517     # . . call
 518     e8/call  flush/disp32
 519     # . . discard args
 520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 521     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
 522     # . . push args
 523     68/push  "F - test-convert-passes-with-just-whitespace-through"/imm32
 524     68/push  "    "/imm32
 525     68/push  _test-output-stream/imm32
 526     # . . call
 527     e8/call  check-next-stream-line-equal/disp32
 528     # . . discard args
 529     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 530     # . epilog
 531     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 532     5d/pop-to-EBP
 533     c3/return
 534 
 535 test-convert-passes-segment-headers-through:
 536     # if a line starts with '==', pass it along unchanged
 537     # . prolog
 538     55/push-EBP
 539     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 540     # setup
 541     # . clear-stream(_test-input-stream)
 542     # . . push args
 543     68/push  _test-input-stream/imm32
 544     # . . call
 545     e8/call  clear-stream/disp32
 546     # . . discard args
 547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 548     # . clear-stream(_test-input-buffered-file+4)
 549     # . . push args
 550     b8/copy-to-EAX  _test-input-buffered-file/imm32
 551     05/add-to-EAX  4/imm32
 552     50/push-EAX
 553     # . . call
 554     e8/call  clear-stream/disp32
 555     # . . discard args
 556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 557     # . clear-stream(_test-output-stream)
 558     # . . push args
 559     68/push  _test-output-stream/imm32
 560     # . . call
 561     e8/call  clear-stream/disp32
 562     # . . discard args
 563     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 564     # . clear-stream(_test-output-buffered-file+4)
 565     # . . push args
 566     b8/copy-to-EAX  _test-output-buffered-file/imm32
 567     05/add-to-EAX  4/imm32
 568     50/push-EAX
 569     # . . call
 570     e8/call  clear-stream/disp32
 571     # . . discard args
 572     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 573     # initialize input
 574     # . write(_test-input-stream, "== abcd 0x1")
 575     # . . push args
 576     68/push  "== abcd 0x1"/imm32
 577     68/push  _test-input-stream/imm32
 578     # . . call
 579     e8/call  write/disp32
 580     # . . discard args
 581     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 582     # convert(_test-input-buffered-file, _test-output-buffered-file)
 583     # . . push args
 584     68/push  _test-output-buffered-file/imm32
 585     68/push  _test-input-buffered-file/imm32
 586     # . . call
 587     e8/call  convert/disp32
 588     # . . discard args
 589     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 590     # check that the line just passed through
 591     # . flush(_test-output-buffered-file)
 592     # . . push args
 593     68/push  _test-output-buffered-file/imm32
 594     # . . call
 595     e8/call  flush/disp32
 596     # . . discard args
 597     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 598     # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg)
 599     # . . push args
 600     68/push  "F - test-convert-passes-segment-headers-through"/imm32
 601     68/push  "== abcd 0x1"/imm32
 602     68/push  _test-output-stream/imm32
 603     # . . call
 604     e8/call  check-stream-equal/disp32
 605     # . . discard args
 606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 607     # . epilog
 608     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 609     5d/pop-to-EBP
 610     c3/return
 611 
 612 test-convert-in-data-segment:
 613     # correctly process lines in the data segment
 614     # . prolog
 615     55/push-EBP
 616     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 617     # setup
 618     # . clear-stream(_test-input-stream)
 619     # . . push args
 620     68/push  _test-input-stream/imm32
 621     # . . call
 622     e8/call  clear-stream/disp32
 623     # . . discard args
 624     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 625     # . clear-stream(_test-input-buffered-file+4)
 626     # . . push args
 627     b8/copy-to-EAX  _test-input-buffered-file/imm32
 628     05/add-to-EAX  4/imm32
 629     50/push-EAX
 630     # . . call
 631     e8/call  clear-stream/disp32
 632     # . . discard args
 633     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 634     # . clear-stream(_test-output-stream)
 635     # . . push args
 636     68/push  _test-output-stream/imm32
 637     # . . call
 638     e8/call  clear-stream/disp32
 639     # . . discard args
 640     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 641     # . clear-stream(_test-output-buffered-file+4)
 642     # . . push args
 643     b8/copy-to-EAX  _test-output-buffered-file/imm32
 644     05/add-to-EAX  4/imm32
 645     50/push-EAX
 646     # . . call
 647     e8/call  clear-stream/disp32
 648     # . . discard args
 649     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 650     # initialize input
 651     #   == code 0x1
 652     #   == data 0x2
 653     #   3 4/imm32
 654     # . write(_test-input-stream, "== code 0x1")
 655     # . . push args
 656     68/push  "== code 0x1\n"/imm32
 657     68/push  _test-input-stream/imm32
 658     # . . call
 659     e8/call  write/disp32
 660     # . . discard args
 661     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 662     # . write(_test-input-stream, "== data 0x2\n")
 663     # . . push args
 664     68/push  "== data 0x2\n"/imm32
 665     68/push  _test-input-stream/imm32
 666     # . . call
 667     e8/call  write/disp32
 668     # . . discard args
 669     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 670     # . write(_test-input-stream, "3 4/imm32\n")
 671     # . . push args
 672     68/push  "3 4/imm32\n"/imm32
 673     68/push  _test-input-stream/imm32
 674     # . . call
 675     e8/call  write/disp32
 676     # . . discard args
 677     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 678     # convert(_test-input-buffered-file, _test-output-buffered-file)
 679     # . . push args
 680     68/push  _test-output-buffered-file/imm32
 681     68/push  _test-input-buffered-file/imm32
 682     # . . call
 683     e8/call  convert/disp32
 684     # . . discard args
 685     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 686     # check output
 687 +-- 26 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
 713     # . flush(_test-output-buffered-file)
 714     # . . push args
 715     68/push  _test-output-buffered-file/imm32
 716     # . . call
 717     e8/call  flush/disp32
 718     # . . discard args
 719     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 720     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
 721     # . . push args
 722     68/push  "F - test-convert-in-data-segment/0"/imm32
 723     68/push  "== code 0x1"/imm32
 724     68/push  _test-output-stream/imm32
 725     # . . call
 726     e8/call  check-next-stream-line-equal/disp32
 727     # . . discard args
 728     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 729     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
 730     # . . push args
 731     68/push  "F - test-convert-in-data-segment/1"/imm32
 732     68/push  "== data 0x2"/imm32
 733     68/push  _test-output-stream/imm32
 734     # . . call
 735     e8/call  check-next-stream-line-equal/disp32
 736     # . . discard args
 737     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 738     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 739     # . . push args
 740     68/push  "F - test-convert-in-data-segment/2"/imm32
 741     68/push  "03 04 00 00 00 "/imm32
 742     68/push  _test-output-stream/imm32
 743     # . . call
 744     e8/call  check-next-stream-line-equal/disp32
 745     # . . discard args
 746     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 747     # . epilog
 748     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 749     5d/pop-to-EBP
 750     c3/return
 751 
 752 test-convert-code-and-data-segments:
 753     # correctly process lines in both code and data segments
 754     # . prolog
 755     55/push-EBP
 756     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 757     # setup
 758     # . clear-stream(_test-input-stream)
 759     # . . push args
 760     68/push  _test-input-stream/imm32
 761     # . . call
 762     e8/call  clear-stream/disp32
 763     # . . discard args
 764     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 765     # . clear-stream(_test-input-buffered-file+4)
 766     # . . push args
 767     b8/copy-to-EAX  _test-input-buffered-file/imm32
 768     05/add-to-EAX  4/imm32
 769     50/push-EAX
 770     # . . call
 771     e8/call  clear-stream/disp32
 772     # . . discard args
 773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 774     # . clear-stream(_test-output-stream)
 775     # . . push args
 776     68/push  _test-output-stream/imm32
 777     # . . call
 778     e8/call  clear-stream/disp32
 779     # . . discard args
 780     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 781     # . clear-stream(_test-output-buffered-file+4)
 782     # . . push args
 783     b8/copy-to-EAX  _test-output-buffered-file/imm32
 784     05/add-to-EAX  4/imm32
 785     50/push-EAX
 786     # . . call
 787     e8/call  clear-stream/disp32
 788     # . . discard args
 789     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 790     # initialize input
 791     #   == code 0x1
 792     #   e8/call 20/disp32
 793     #   68/push 0x20/imm8
 794     #   == data 0x2
 795     #   3 4/imm32
 796     # . write(_test-input-stream, "== code 0x1\n")
 797     # . . push args
 798     68/push  "== code 0x1\n"/imm32
 799     68/push  _test-input-stream/imm32
 800     # . . call
 801     e8/call  write/disp32
 802     # . . discard args
 803     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 804     # . write(_test-input-stream, "e8/call 20/disp32\n")
 805     # . . push args
 806     68/push  "e8/call 20/disp32\n"/imm32
 807     68/push  _test-input-stream/imm32
 808     # . . call
 809     e8/call  write/disp32
 810     # . . discard args
 811     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 812     # . write(_test-input-stream, "68/push 0x20/imm8\n")
 813     # . . push args
 814     68/push  "68/push 0x20/imm8\n"/imm32
 815     68/push  _test-input-stream/imm32
 816     # . . call
 817     e8/call  write/disp32
 818     # . . discard args
 819     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 820     # . write(_test-input-stream, "== data 0x2\n")
 821     # . . push args
 822     68/push  "== data 0x2\n"/imm32
 823     68/push  _test-input-stream/imm32
 824     # . . call
 825     e8/call  write/disp32
 826     # . . discard args
 827     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 828     # . write(_test-input-stream, "3 4/imm32\n")
 829     # . . push args
 830     68/push  "3 4/imm32\n"/imm32
 831     68/push  _test-input-stream/imm32
 832     # . . call
 833     e8/call  write/disp32
 834     # . . discard args
 835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 836     # convert(_test-input-buffered-file, _test-output-buffered-file)
 837     # . . push args
 838     68/push  _test-output-buffered-file/imm32
 839     68/push  _test-input-buffered-file/imm32
 840     # . . call
 841     e8/call  convert/disp32
 842     # . . discard args
 843     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 844     # check output
 845     #   == code 0x1
 846     #   e8 20 00 00 00  # e8/call 20/disp32
 847     #   68 20  # 68/push 0x20/imm8
 848     #   == data 0x2
 849     #   03 04 00 00 00
 850 +-- 26 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------
 876     # . flush(_test-output-buffered-file)
 877     # . . push args
 878     68/push  _test-output-buffered-file/imm32
 879     # . . call
 880     e8/call  flush/disp32
 881     # . . discard args
 882     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 883     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
 884     # . . push args
 885     68/push  "F - test-convert-code-and-data-segments/0"/imm32
 886     68/push  "== code 0x1"/imm32
 887     68/push  _test-output-stream/imm32
 888     # . . call
 889     e8/call  check-next-stream-line-equal/disp32
 890     # . . discard args
 891     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 892     # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
 893     # . . push args
 894     68/push  "F - test-convert-code-and-data-segments/1"/imm32
 895     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
 896     68/push  _test-output-stream/imm32
 897     # . . call
 898     e8/call  check-next-stream-line-equal/disp32
 899     # . . discard args
 900     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 901     # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
 902     # . . push args
 903     68/push  "F - test-convert-code-and-data-segments/2"/imm32
 904     68/push  "68 20  # 68/push 0x20/imm8"/imm32
 905     68/push  _test-output-stream/imm32
 906     # . . call
 907     e8/call  check-next-stream-line-equal/disp32
 908     # . . discard args
 909     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 910     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
 911     # . . push args
 912     68/push  "F - test-convert-code-and-data-segments/3"/imm32
 913     68/push  "== data 0x2"/imm32
 914     68/push  _test-output-stream/imm32
 915     # . . call
 916     e8/call  check-next-stream-line-equal/disp32
 917     # . . discard args
 918     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 919     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 920     # . . push args
 921     68/push  "F - test-convert-code-and-data-segments/4"/imm32
 922     68/push  "03 04 00 00 00 "/imm32
 923     68/push  _test-output-stream/imm32
 924     # . . call
 925     e8/call  check-next-stream-line-equal/disp32
 926     # . . discard args
 927     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 928     # . epilog
 929     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 930     5d/pop-to-EBP
 931     c3/return
 932 
 933 convert-data:  # line : (address stream byte), out : (address buffered-file) -> <void>
 934     # pseudocode:
 935     #   var word-slice = {0, 0}
 936     #   while true
 937     #     word-slice = next-word(line)
 938     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
 939     #       break  # skip emitting some whitespace
 940     #     if slice-starts-with?(word-slice, "#")      # comment
 941     #       write-slice-buffered(out, word-slice)
 942     #       return
 943     #     if slice-ends-with?(word-slice, ":")        # label
 944     #       write-stream-data(out, line)
 945     #       return
 946     #     if has-metadata?(word-slice, "imm32")
 947     #       emit(out, word-slice, 4)
 948     #     # disp32 is not permitted in data segments, and anything else is only a byte long
 949     #     else
 950     #       emit(out, word-slice, 1)
 951     #   write-buffered(out, "\n")
 952     #
 953     # . prolog
 954     55/push-EBP
 955     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 956     # . save registers
 957     50/push-EAX
 958     51/push-ECX
 959     52/push-EDX
 960     # var word-slice/ECX = {0, 0}
 961     68/push  0/imm32/end
 962     68/push  0/imm32/start
 963     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 964 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
 990 $convert-data:loop:
 991     # next-word(line, word-slice)
 992     # . . push args
 993     51/push-ECX
 994     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 995     # . . call
 996     e8/call  next-word/disp32
 997     # . . discard args
 998     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 999 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
1041 $convert-data:check0:
1042     # if (slice-empty?(word-slice)) break
1043     # . EAX = slice-empty?(word-slice)
1044     # . . push args
1045     51/push-ECX
1046     # . . call
1047     e8/call  slice-empty?/disp32
1048     # . . discard args
1049     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1050     # . if (EAX != 0) break
1051     3d/compare-EAX-and  0/imm32
1052     0f 85/jump-if-not-equal  $convert-data:break/disp32
1053 $convert-data:check-for-comment:
1054     # if (slice-starts-with?(word-slice, "#"))
1055     # . start/EDX = word-slice->start
1056     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
1057     # . c/EAX = *start
1058     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1059     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
1060     # . if (EAX != '#') goto next check
1061     3d/compare-EAX-and  0x23/imm32/hash
1062     75/jump-if-not-equal  $convert-data:check-for-label/disp8
1063 $convert-data:comment:
1064     # write-slice-buffered(out, word-slice)
1065     # . . push args
1066     51/push-ECX
1067     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1068     # . . call
1069     e8/call  write-slice-buffered/disp32
1070     # . . discard args
1071     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1072     # return
1073     0f 85/jump-if-not-equal  $convert-data:end/disp32
1074 $convert-data:check-for-label:
1075     # if (slice-ends-with?(word-slice, ":"))
1076     # . end/EDX = word-slice->end
1077     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
1078     # . c/EAX = *(end-1)
1079     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1080     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
1081     # . if (EAX != ':') goto next check
1082     3d/compare-EAX-and  0x3a/imm32/colon
1083     75/jump-if-not-equal  $convert-data:check-for-imm32/disp8
1084 $convert-data:label:
1085     # write-stream-data(out, line)
1086     # . . push args
1087     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1088     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1089     # . . call
1090     e8/call  write-stream-data/disp32
1091     # . . discard args
1092     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1093     # return
1094     75/jump-if-not-equal  $convert-data:end/disp8
1095 $convert-data:check-for-imm32:
1096     # if (has-metadata?(word-slice, "imm32"))
1097     # . EAX = has-metadata?(ECX, "imm32")
1098     # . . push args
1099     68/push  "imm32"/imm32
1100     51/push-ECX
1101     # . . call
1102     e8/call  has-metadata?/disp32
1103     # . . discard args
1104     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1105     # . if (EAX == 0) process as a single byte
1106     3d/compare-EAX-and  0/imm32
1107     74/jump-if-equal  $convert-data:single-byte/disp8
1108 $convert-data:imm32:
1109     # emit(out, word-slice, 4)
1110     # . . push args
1111     68/push  4/imm32
1112     51/push-ECX
1113     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1114     # . . call
1115     e8/call  emit/disp32
1116     # . . discard args
1117     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1118     e9/jump  $convert-data:loop/disp32
1119 $convert-data:single-byte:
1120     # emit(out, word-slice, 1)
1121     # . . push args
1122     68/push  1/imm32
1123     51/push-ECX
1124     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1125     # . . call
1126     e8/call  emit/disp32
1127     # . . discard args
1128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1129     e9/jump  $convert-data:loop/disp32
1130 $convert-data:break:
1131     # write-buffered(out, "\n")
1132     # . . push args
1133     68/push  "\n"/imm32
1134     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1135     # . . call
1136     e8/call  write-buffered/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1139 $convert-data:end:
1140     # . reclaim locals
1141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1142     # . restore registers
1143     5a/pop-to-EDX
1144     59/pop-to-ECX
1145     58/pop-to-EAX
1146     # . epilog
1147     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1148     5d/pop-to-EBP
1149     c3/return
1150 
1151 test-convert-data-passes-comments-through:
1152     # if a line starts with '#', pass it along unchanged
1153     # . prolog
1154     55/push-EBP
1155     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1156     # setup
1157     # . clear-stream(_test-input-stream)
1158     # . . push args
1159     68/push  _test-input-stream/imm32
1160     # . . call
1161     e8/call  clear-stream/disp32
1162     # . . discard args
1163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1164     # . clear-stream(_test-output-stream)
1165     # . . push args
1166     68/push  _test-output-stream/imm32
1167     # . . call
1168     e8/call  clear-stream/disp32
1169     # . . discard args
1170     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1171     # . clear-stream(_test-output-buffered-file+4)
1172     # . . push args
1173     b8/copy-to-EAX  _test-output-buffered-file/imm32
1174     05/add-to-EAX  4/imm32
1175     50/push-EAX
1176     # . . call
1177     e8/call  clear-stream/disp32
1178     # . . discard args
1179     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1180     # initialize input
1181     # . write(_test-input-stream, "# abcd")
1182     # . . push args
1183     68/push  "# abcd"/imm32
1184     68/push  _test-input-stream/imm32
1185     # . . call
1186     e8/call  write/disp32
1187     # . . discard args
1188     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1189     # convert-data(_test-input-stream, _test-output-buffered-file)
1190     # . . push args
1191     68/push  _test-output-buffered-file/imm32
1192     68/push  _test-input-stream/imm32
1193     # . . call
1194     e8/call  convert-data/disp32
1195     # . . discard args
1196     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1197     # check that the line just passed through
1198     # . flush(_test-output-buffered-file)
1199     # . . push args
1200     68/push  _test-output-buffered-file/imm32
1201     # . . call
1202     e8/call  flush/disp32
1203     # . . discard args
1204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1205 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1231     # . check-stream-equal(_test-output-stream, "# abcd", msg)
1232     # . . push args
1233     68/push  "F - test-convert-data-passes-comments-through"/imm32
1234     68/push  "# abcd"/imm32
1235     68/push  _test-output-stream/imm32
1236     # . . call
1237     e8/call  check-stream-equal/disp32
1238     # . . discard args
1239     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1240     # . epilog
1241     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1242     5d/pop-to-EBP
1243     c3/return
1244 
1245 test-convert-data-passes-labels-through:
1246     # if the first word ends with ':', pass along the entire line unchanged
1247     # . prolog
1248     55/push-EBP
1249     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1250     # setup
1251     # . clear-stream(_test-input-stream)
1252     # . . push args
1253     68/push  _test-input-stream/imm32
1254     # . . call
1255     e8/call  clear-stream/disp32
1256     # . . discard args
1257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1258     # . clear-stream(_test-output-stream)
1259     # . . push args
1260     68/push  _test-output-stream/imm32
1261     # . . call
1262     e8/call  clear-stream/disp32
1263     # . . discard args
1264     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1265     # . clear-stream(_test-output-buffered-file+4)
1266     # . . push args
1267     b8/copy-to-EAX  _test-output-buffered-file/imm32
1268     05/add-to-EAX  4/imm32
1269     50/push-EAX
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     # initialize input
1275     # . write(_test-input-stream, "ab: # cd")
1276     # . . push args
1277     68/push  "ab: # cd"/imm32
1278     68/push  _test-input-stream/imm32
1279     # . . call
1280     e8/call  write/disp32
1281     # . . discard args
1282     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1283     # convert-data(_test-input-stream, _test-output-buffered-file)
1284     # . . push args
1285     68/push  _test-output-buffered-file/imm32
1286     68/push  _test-input-stream/imm32
1287     # . . call
1288     e8/call  convert-data/disp32
1289     # . . discard args
1290     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1291     # check that the line just passed through
1292     # . flush(_test-output-buffered-file)
1293     # . . push args
1294     68/push  _test-output-buffered-file/imm32
1295     # . . call
1296     e8/call  flush/disp32
1297     # . . discard args
1298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1299     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
1300     # . . push args
1301     68/push  "F - test-convert-data-passes-labels-through"/imm32
1302     68/push  "ab: # cd"/imm32
1303     68/push  _test-output-stream/imm32
1304     # . . call
1305     e8/call  check-stream-equal/disp32
1306     # . . discard args
1307     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1308     # . epilog
1309     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1310     5d/pop-to-EBP
1311     c3/return
1312 
1313 test-convert-data-passes-names-through:
1314     # If a word is a valid name, just emit it unchanged.
1315     # Later phases will deal with it.
1316     # . prolog
1317     55/push-EBP
1318     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1319     # setup
1320     # . clear-stream(_test-input-stream)
1321     # . . push args
1322     68/push  _test-input-stream/imm32
1323     # . . call
1324     e8/call  clear-stream/disp32
1325     # . . discard args
1326     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1327     # . clear-stream(_test-output-stream)
1328     # . . push args
1329     68/push  _test-output-stream/imm32
1330     # . . call
1331     e8/call  clear-stream/disp32
1332     # . . discard args
1333     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1334     # . clear-stream(_test-output-buffered-file+4)
1335     # . . push args
1336     b8/copy-to-EAX  _test-output-buffered-file/imm32
1337     05/add-to-EAX  4/imm32
1338     50/push-EAX
1339     # . . call
1340     e8/call  clear-stream/disp32
1341     # . . discard args
1342     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1343     # initialize input
1344     # . write(_test-input-stream, "abcd/imm32")
1345     # . . push args
1346     68/push  "abcd/imm32"/imm32
1347     68/push  _test-input-stream/imm32
1348     # . . call
1349     e8/call  write/disp32
1350     # . . discard args
1351     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1352     # convert-data(_test-input-stream, _test-output-buffered-file)
1353     # . . push args
1354     68/push  _test-output-buffered-file/imm32
1355     68/push  _test-input-stream/imm32
1356     # . . call
1357     e8/call  convert-data/disp32
1358     # . . discard args
1359     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1360     # check that the line just passed through
1361     # . flush(_test-output-buffered-file)
1362     # . . push args
1363     68/push  _test-output-buffered-file/imm32
1364     # . . call
1365     e8/call  flush/disp32
1366     # . . discard args
1367     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1368     # . check-stream-equal(_test-output-stream, "abcd/imm32 \n", msg)
1369     # . . push args
1370     68/push  "F - test-convert-data-passes-names-through"/imm32
1371     68/push  "abcd/imm32 \n"/imm32
1372     68/push  _test-output-stream/imm32
1373     # . . call
1374     e8/call  check-stream-equal/disp32
1375     # . . discard args
1376     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1377     # . epilog
1378     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1379     5d/pop-to-EBP
1380     c3/return
1381 
1382 test-convert-data-handles-imm32:
1383     # If a word has the /imm32 metadata, emit it in 4 bytes.
1384     # . prolog
1385     55/push-EBP
1386     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1387     # setup
1388     # . clear-stream(_test-input-stream)
1389     # . . push args
1390     68/push  _test-input-stream/imm32
1391     # . . call
1392     e8/call  clear-stream/disp32
1393     # . . discard args
1394     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1395     # . clear-stream(_test-output-stream)
1396     # . . push args
1397     68/push  _test-output-stream/imm32
1398     # . . call
1399     e8/call  clear-stream/disp32
1400     # . . discard args
1401     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1402     # . clear-stream(_test-output-buffered-file+4)
1403     # . . push args
1404     b8/copy-to-EAX  _test-output-buffered-file/imm32
1405     05/add-to-EAX  4/imm32
1406     50/push-EAX
1407     # . . call
1408     e8/call  clear-stream/disp32
1409     # . . discard args
1410     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1411     # initialize input
1412     # . write(_test-input-stream, "30/imm32")
1413     # . . push args
1414     68/push  "30/imm32"/imm32
1415     68/push  _test-input-stream/imm32
1416     # . . call
1417     e8/call  write/disp32
1418     # . . discard args
1419     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1420     # convert-data(_test-input-stream, _test-output-buffered-file)
1421     # . . push args
1422     68/push  _test-output-buffered-file/imm32
1423     68/push  _test-input-stream/imm32
1424     # . . call
1425     e8/call  convert-data/disp32
1426     # . . discard args
1427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1428     # check that 4 bytes were written
1429     # . flush(_test-output-buffered-file)
1430     # . . push args
1431     68/push  _test-output-buffered-file/imm32
1432     # . . call
1433     e8/call  flush/disp32
1434     # . . discard args
1435     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1436     # . check-stream-equal(_test-output-stream, "30 00 00 00 \n", msg)
1437     # . . push args
1438     68/push  "F - test-convert-data-handles-imm32"/imm32
1439     68/push  "30 00 00 00 \n"/imm32
1440     68/push  _test-output-stream/imm32
1441     # . . call
1442     e8/call  check-stream-equal/disp32
1443     # . . discard args
1444     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1445     # . epilog
1446     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1447     5d/pop-to-EBP
1448     c3/return
1449 
1450 test-convert-data-handles-single-byte:
1451     # Any metadata but /imm32 will emit a single byte.
1452     # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
1453     # . prolog
1454     55/push-EBP
1455     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1456     # setup
1457     # . clear-stream(_test-input-stream)
1458     # . . push args
1459     68/push  _test-input-stream/imm32
1460     # . . call
1461     e8/call  clear-stream/disp32
1462     # . . discard args
1463     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1464     # . clear-stream(_test-output-stream)
1465     # . . push args
1466     68/push  _test-output-stream/imm32
1467     # . . call
1468     e8/call  clear-stream/disp32
1469     # . . discard args
1470     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1471     # . clear-stream(_test-output-buffered-file+4)
1472     # . . push args
1473     b8/copy-to-EAX  _test-output-buffered-file/imm32
1474     05/add-to-EAX  4/imm32
1475     50/push-EAX
1476     # . . call
1477     e8/call  clear-stream/disp32
1478     # . . discard args
1479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1480     # initialize input
1481     # . write(_test-input-stream, "30/imm16")
1482     # . . push args
1483     68/push  "30/imm16"/imm32
1484     68/push  _test-input-stream/imm32
1485     # . . call
1486     e8/call  write/disp32
1487     # . . discard args
1488     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1489     # convert-data(_test-input-stream, _test-output-buffered-file)
1490     # . . push args
1491     68/push  _test-output-buffered-file/imm32
1492     68/push  _test-input-stream/imm32
1493     # . . call
1494     e8/call  convert-data/disp32
1495     # . . discard args
1496     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1497     # check that a single byte was written (imm16 is not a valid operand type)
1498     # . flush(_test-output-buffered-file)
1499     # . . push args
1500     68/push  _test-output-buffered-file/imm32
1501     # . . call
1502     e8/call  flush/disp32
1503     # . . discard args
1504     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1505     # . check-stream-equal(_test-output-stream, "30 \n", msg)
1506     # . . push args
1507     68/push  "F - test-convert-data-handles-single-byte"/imm32
1508     68/push  "30 \n"/imm32
1509     68/push  _test-output-stream/imm32
1510     # . . call
1511     e8/call  check-stream-equal/disp32
1512     # . . discard args
1513     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1514     # . epilog
1515     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1516     5d/pop-to-EBP
1517     c3/return
1518 
1519 test-convert-data-multiple-bytes:
1520     # Multiple single-byte words in input stream get processed one by one.
1521     # . prolog
1522     55/push-EBP
1523     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1524     # setup
1525     # . clear-stream(_test-input-stream)
1526     # . . push args
1527     68/push  _test-input-stream/imm32
1528     # . . call
1529     e8/call  clear-stream/disp32
1530     # . . discard args
1531     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1532     # . clear-stream(_test-output-stream)
1533     # . . push args
1534     68/push  _test-output-stream/imm32
1535     # . . call
1536     e8/call  clear-stream/disp32
1537     # . . discard args
1538     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1539     # . clear-stream(_test-output-buffered-file+4)
1540     # . . push args
1541     b8/copy-to-EAX  _test-output-buffered-file/imm32
1542     05/add-to-EAX  4/imm32
1543     50/push-EAX
1544     # . . call
1545     e8/call  clear-stream/disp32
1546     # . . discard args
1547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1548     # initialize input
1549     # . write(_test-input-stream, "1 2")
1550     # . . push args
1551     68/push  "1 2"/imm32
1552     68/push  _test-input-stream/imm32
1553     # . . call
1554     e8/call  write/disp32
1555     # . . discard args
1556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1557     # convert-data(_test-input-stream, _test-output-buffered-file)
1558     # . . push args
1559     68/push  _test-output-buffered-file/imm32
1560     68/push  _test-input-stream/imm32
1561     # . . call
1562     e8/call  convert-data/disp32
1563     # . . discard args
1564     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1565     # check output
1566     # . flush(_test-output-buffered-file)
1567     # . . push args
1568     68/push  _test-output-buffered-file/imm32
1569     # . . call
1570     e8/call  flush/disp32
1571     # . . discard args
1572     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1573     # . check-stream-equal(_test-output-stream, "01 02 \n", msg)
1574     # . . push args
1575     68/push  "F - test-convert-data-multiple-bytes"/imm32
1576     68/push  "01 02 \n"/imm32
1577     68/push  _test-output-stream/imm32
1578     # . . call
1579     e8/call  check-stream-equal/disp32
1580     # . . discard args
1581     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1582     # . epilog
1583     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1584     5d/pop-to-EBP
1585     c3/return
1586 
1587 test-convert-data-byte-then-name:
1588     # Single-byte word followed by valid name get processed one by one.
1589     # . prolog
1590     55/push-EBP
1591     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1592     # setup
1593     # . clear-stream(_test-input-stream)
1594     # . . push args
1595     68/push  _test-input-stream/imm32
1596     # . . call
1597     e8/call  clear-stream/disp32
1598     # . . discard args
1599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1600     # . clear-stream(_test-output-stream)
1601     # . . push args
1602     68/push  _test-output-stream/imm32
1603     # . . call
1604     e8/call  clear-stream/disp32
1605     # . . discard args
1606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1607     # . clear-stream(_test-output-buffered-file+4)
1608     # . . push args
1609     b8/copy-to-EAX  _test-output-buffered-file/imm32
1610     05/add-to-EAX  4/imm32
1611     50/push-EAX
1612     # . . call
1613     e8/call  clear-stream/disp32
1614     # . . discard args
1615     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1616     # initialize input
1617     # . write(_test-input-stream, "30 abcd/o")
1618     # . . push args
1619     68/push  "30 abcd/o"/imm32
1620     68/push  _test-input-stream/imm32
1621     # . . call
1622     e8/call  write/disp32
1623     # . . discard args
1624     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1625     # convert-data(_test-input-stream, _test-output-buffered-file)
1626     # . . push args
1627     68/push  _test-output-buffered-file/imm32
1628     68/push  _test-input-stream/imm32
1629     # . . call
1630     e8/call  convert-data/disp32
1631     # . . discard args
1632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1633     # check output
1634     # . flush(_test-output-buffered-file)
1635     # . . push args
1636     68/push  _test-output-buffered-file/imm32
1637     # . . call
1638     e8/call  flush/disp32
1639     # . . discard args
1640     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1641     # . check-stream-equal(_test-output-stream, "30 abcd/o \n", msg)
1642     # . . push args
1643     68/push  "F - test-convert-data-byte-then-name"/imm32
1644     68/push  "30 abcd/o \n"/imm32
1645     68/push  _test-output-stream/imm32
1646     # . . call
1647     e8/call  check-stream-equal/disp32
1648     # . . discard args
1649     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1650     # . epilog
1651     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1652     5d/pop-to-EBP
1653     c3/return
1654 
1655 test-convert-data-multiple-words:
1656     # Multiple words in input stream get processed one by one.
1657     # . prolog
1658     55/push-EBP
1659     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1660     # setup
1661     # . clear-stream(_test-input-stream)
1662     # . . push args
1663     68/push  _test-input-stream/imm32
1664     # . . call
1665     e8/call  clear-stream/disp32
1666     # . . discard args
1667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1668     # . clear-stream(_test-output-stream)
1669     # . . push args
1670     68/push  _test-output-stream/imm32
1671     # . . call
1672     e8/call  clear-stream/disp32
1673     # . . discard args
1674     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1675     # . clear-stream(_test-output-buffered-file+4)
1676     # . . push args
1677     b8/copy-to-EAX  _test-output-buffered-file/imm32
1678     05/add-to-EAX  4/imm32
1679     50/push-EAX
1680     # . . call
1681     e8/call  clear-stream/disp32
1682     # . . discard args
1683     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1684     # initialize input
1685     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
1686     # . . push args
1687     68/push  "30 abcd/o 42e1/imm32"/imm32
1688     68/push  _test-input-stream/imm32
1689     # . . call
1690     e8/call  write/disp32
1691     # . . discard args
1692     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1693     # convert-data(_test-input-stream, _test-output-buffered-file)
1694     # . . push args
1695     68/push  _test-output-buffered-file/imm32
1696     68/push  _test-input-stream/imm32
1697     # . . call
1698     e8/call  convert-data/disp32
1699     # . . discard args
1700     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1701     # check output
1702     # . flush(_test-output-buffered-file)
1703     # . . push args
1704     68/push  _test-output-buffered-file/imm32
1705     # . . call
1706     e8/call  flush/disp32
1707     # . . discard args
1708     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1709 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1735     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 \n", msg)
1736     # . . push args
1737     68/push  "F - test-convert-data-multiple-words"/imm32
1738     68/push  "30 abcd/o e1 42 00 00 \n"/imm32
1739     68/push  _test-output-stream/imm32
1740     # . . call
1741     e8/call  check-stream-equal/disp32
1742     # . . discard args
1743     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1744     # . epilog
1745     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1746     5d/pop-to-EBP
1747     c3/return
1748 
1749 test-convert-data-trailing-comment:
1750     # Trailing comments in data segment get appropriately ignored.
1751     # . prolog
1752     55/push-EBP
1753     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1754     # setup
1755     # . clear-stream(_test-input-stream)
1756     # . . push args
1757     68/push  _test-input-stream/imm32
1758     # . . call
1759     e8/call  clear-stream/disp32
1760     # . . discard args
1761     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1762     # . clear-stream(_test-output-stream)
1763     # . . push args
1764     68/push  _test-output-stream/imm32
1765     # . . call
1766     e8/call  clear-stream/disp32
1767     # . . discard args
1768     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1769     # . clear-stream(_test-output-buffered-file+4)
1770     # . . push args
1771     b8/copy-to-EAX  _test-output-buffered-file/imm32
1772     05/add-to-EAX  4/imm32
1773     50/push-EAX
1774     # . . call
1775     e8/call  clear-stream/disp32
1776     # . . discard args
1777     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1778     # initialize input
1779     # . write(_test-input-stream, "30/imm32 # comment")
1780     # . . push args
1781     68/push  "30/imm32 # comment"/imm32
1782     68/push  _test-input-stream/imm32
1783     # . . call
1784     e8/call  write/disp32
1785     # . . discard args
1786     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1787     # convert-data(_test-input-stream, _test-output-buffered-file)
1788     # . . push args
1789     68/push  _test-output-buffered-file/imm32
1790     68/push  _test-input-stream/imm32
1791     # . . call
1792     e8/call  convert-data/disp32
1793     # . . discard args
1794     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1795     # check output
1796     # . flush(_test-output-buffered-file)
1797     # . . push args
1798     68/push  _test-output-buffered-file/imm32
1799     # . . call
1800     e8/call  flush/disp32
1801     # . . discard args
1802     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1803 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1829     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
1830     # . . push args
1831     68/push  "F - test-convert-data-trailing-comment"/imm32
1832     68/push  "30 00 00 00 # comment"/imm32
1833     68/push  _test-output-stream/imm32
1834     # . . call
1835     e8/call  check-stream-equal/disp32
1836     # . . discard args
1837     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1838     # . epilog
1839     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1840     5d/pop-to-EBP
1841     c3/return
1842 
1843 # pack an instruction, following the C++ version
1844 #
1845 # zero error handling at the moment (continuing to rely on the C++ version for that):
1846 #   missing fields are always 0-filled
1847 #   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.
1848 #   may pick up any of duplicate operands in an instruction
1849 #   silently drop extraneous operands
1850 #   unceremoniously abort on non-numeric operands except disp or imm
1851 #   opcodes must be lowercase and zero padded
1852 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
1853 convert-instruction:  # line : (address stream byte), out : (address buffered-file) -> <void>
1854     # pseudocode:
1855     #   # some early exits
1856     #   var word-slice = next-word(line)
1857     #   if slice-empty?(word-slice)
1858     #     write-stream-data(out, line)
1859     #     return
1860     #   if slice-starts-with?(word-slice, "#")
1861     #     write-stream-data(out, line)
1862     #     return
1863     #   if slice-ends-with?(word-slice, ":")
1864     #     write-stream-data(out, line)
1865     #     return
1866     #   # really convert
1867     #   emit-opcodes(line, out)
1868     #   emit-modrm(line, out)
1869     #   emit-sib(line, out)
1870     #   emit-disp(line, out)
1871     #   emit-imm(line, out)
1872     #   emit-line-in-comment(line, out)
1873     #
1874     # . prolog
1875     55/push-EBP
1876     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
1877     # . save registers
1878     50/push-EAX
1879     51/push-ECX
1880     52/push-EDX
1881     # var word-slice/ECX = {0, 0}
1882     68/push  0/imm32/end
1883     68/push  0/imm32/start
1884     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1885     # next-word(line, word-slice)
1886     # . . push args
1887     51/push-ECX
1888     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1889     # . . call
1890     e8/call  next-word/disp32
1891     # . . discard args
1892     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1893 $convert-instruction:check0:
1894     # if (slice-empty?(word-slice)) break
1895     # . EAX = slice-empty?(word-slice)
1896     # . . push args
1897     51/push-ECX
1898     # . . call
1899     e8/call  slice-empty?/disp32
1900     # . . discard args
1901     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1902     # . if (EAX != 0) pass through
1903     3d/compare-EAX-and  0/imm32
1904     75/jump-if-not-equal  $convert-instruction:pass-through/disp8
1905 $convert-instruction:check1:
1906     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
1907     # . start/EDX = word-slice->start
1908     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
1909     # . c/EAX = *start
1910     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1911     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
1912     # . if (EAX == '#') pass through
1913     3d/compare-EAX-and  0x23/imm32/hash
1914     74/jump-if-equal  $convert-instruction:pass-through/disp8
1915 $convert-instruction:check2:
1916     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
1917     # . end/EDX = word-slice->end
1918     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
1919     # . c/EAX = *(end-1)
1920     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
1921     8a/copy-byte                    1/mod/*+disp8   2/rm32/EDX    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ECX to AL
1922     # . if (EAX == ':') pass through
1923     3d/compare-EAX-and  0x3a/imm32/colon
1924     75/jump-if-not-equal  $convert-instruction:really-convert/disp8
1925 $convert-instruction:pass-through:
1926     # write-stream-data(out, line)
1927     # . . push args
1928     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1929     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1930     # . . call
1931     e8/call  write-stream-data/disp32
1932     # . . discard args
1933     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1934     # return
1935     eb/jump  $convert-instruction:end/disp8
1936 $convert-instruction:really-convert:
1937     # emit-opcodes(line, out)
1938     # . . push args
1939     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1940     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1941     # . . call
1942     e8/call  emit-opcodes/disp32
1943     # . . discard args
1944     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1945     # emit-modrm(line, out)
1946     # . . push args
1947     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1948     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1949     # . . call
1950     e8/call  emit-modrm/disp32
1951     # . . discard args
1952     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1953     # emit-sib(line, out)
1954     # . . push args
1955     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1956     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1957     # . . call
1958     e8/call  emit-sib/disp32
1959     # . . discard args
1960     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1961     # emit-disp(line, out)
1962     # . . push args
1963     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1964     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1965     # . . call
1966     e8/call  emit-disp/disp32
1967     # . . discard args
1968     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1969     # emit-imm(line, out)
1970     # . . push args
1971     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1972     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1973     # . . call
1974     e8/call  emit-imm/disp32
1975     # . . discard args
1976     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1977     # emit-line-in-comment(line, out)
1978     # . . push args
1979     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
1980     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
1981     # . . call
1982     e8/call  emit-line-in-comment/disp32
1983     # . . discard args
1984     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1985 $convert-instruction:end:
1986     # . restore locals
1987     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1988     # . restore registers
1989     5a/pop-to-EDX
1990     59/pop-to-ECX
1991     58/pop-to-EAX
1992     # . epilog
1993     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1994     5d/pop-to-EBP
1995     c3/return
1996 
1997 emit-opcodes:  # line : (address stream byte), out : (address buffered-file) -> <void>
1998     # opcodes occupy 1-3 bytes:
1999     #   xx
2000     #   0f xx
2001     #   f2 xx
2002     #   f3 xx
2003     #   f2 0f xx
2004     #   f3 0f xx
2005     #
2006     # pseudocode:
2007     #   rewind-stream(line)
2008     #
2009     #   var op1 = next-word(line)
2010     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
2011     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
2012     #   write-slice-buffered(out, op1)
2013     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3")
2014     #     return
2015     #
2016     #   var op2 = next-word(line)
2017     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
2018     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
2019     #   write-slice-buffered(out, op2)
2020     #   if slice-equal?(op1, "0f")
2021     #     return
2022     #   if !slice-equal?(op2, "0f")
2023     #     return
2024     #
2025     #   var op3 = next-word(line)
2026     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
2027     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
2028     #   write-slice-buffered(out, op3)
2029     #
2030     # . prolog
2031     55/push-EBP
2032     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2033     # . save registers
2034     50/push-EAX
2035     51/push-ECX
2036     52/push-EDX
2037     53/push-EBX
2038     # var op1/ECX = {0, 0}
2039     68/push  0/imm32/end
2040     68/push  0/imm32/start
2041     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2042     # var op2/EDX = {0, 0}
2043     68/push  0/imm32/end
2044     68/push  0/imm32/start
2045     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
2046     # rewind-stream(line)
2047     # . . push args
2048     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2049     # . . call
2050     e8/call  rewind-stream/disp32
2051     # . . discard args
2052     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2053 $emit-opcodes:op1:
2054     # next-word(line, op1)
2055     # . . push args
2056     51/push-ECX
2057     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2058     # . . call
2059     e8/call  next-word/disp32
2060     # . . discard args
2061     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2062     # if (slice-empty?(op1)) return
2063     # . EAX = slice-empty?(op1)
2064     # . . push args
2065     51/push-ECX
2066     # . . call
2067     e8/call  slice-empty?/disp32
2068     # . . discard args
2069     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2070     # . if (EAX != 0) return
2071     3d/compare-EAX-and  0/imm32
2072     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2073     # if (slice-starts-with?(op1, "#")) return
2074     # . start/EBX = op1->start
2075     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           3/r32/EBX   .               .                 # copy *ECX to EBX
2076     # . c/EAX = *start
2077     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2078     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2079     # . if (EAX == '#') return
2080     3d/compare-EAX-and  0x23/imm32/hash
2081     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2082     # op1 = next-token-from-slice(op1->start, op1->end, '/')
2083     # . . push args
2084     51/push-ECX
2085     68/push  0x2f/imm32/slash
2086     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
2087     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
2088     # . . call
2089     e8/call  next-token-from-slice/disp32
2090     # . . discard args
2091     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2092     # write-slice-buffered(out, op1)
2093     # . . push args
2094     51/push-ECX
2095     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2096     # . . call
2097     e8/call  write-slice-buffered/disp32
2098     # . . discard args
2099     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2100     # write-buffered(out, " ")
2101     # . . push args
2102     68/push  " "/imm32
2103     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2104     # . . call
2105     e8/call  write-buffered/disp32
2106     # . . discard args
2107     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2108     # if (slice-equal?(op1, "0f")) goto op2
2109     # . EAX = slice-equal?(op1, "0f")
2110     # . . push args
2111     68/push  "0f"/imm32
2112     51/push-ECX
2113     # . . call
2114     e8/call  slice-equal?/disp32
2115     # . . discard args
2116     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2117     # . if (EAX != 0) goto op2
2118     3d/compare-EAX-and  0/imm32
2119     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2120     # if (slice-equal?(op1, "f2")) goto op2
2121     # . EAX = slice-equal?(op1, "f2")
2122     # . . push args
2123     68/push  "f2"/imm32
2124     51/push-ECX
2125     # . . call
2126     e8/call  slice-equal?/disp32
2127     # . . discard args
2128     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2129     # . if (EAX != 0) goto op2
2130     3d/compare-EAX-and  0/imm32
2131     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2132     # if (slice-equal?(op1, "f3")) goto op2
2133     # . EAX = slice-equal?(op1, "f3")
2134     # . . push args
2135     68/push  "f3"/imm32
2136     51/push-ECX
2137     # . . call
2138     e8/call  slice-equal?/disp32
2139     # . . discard args
2140     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2141     # . if (EAX != 0) goto op2
2142     3d/compare-EAX-and  0/imm32
2143     75/jump-if-not-equal  $emit-opcodes:op2/disp8
2144     # otherwise return
2145     e9/jump  $emit-opcodes:end/disp32
2146 $emit-opcodes:op2:
2147     # next-word(line, op2)
2148     # . . push args
2149     52/push-EDX
2150     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2151     # . . call
2152     e8/call  next-word/disp32
2153     # . . discard args
2154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2155     # if (slice-empty?(op2)) return
2156     # . EAX = slice-empty?(op2)
2157     # . . push args
2158     52/push-EDX
2159     # . . call
2160     e8/call  slice-empty?/disp32
2161     # . . discard args
2162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2163     # . if (EAX != 0) return
2164     3d/compare-EAX-and  0/imm32
2165     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2166     # if (slice-starts-with?(op2, "#")) return
2167     # . start/EBX = op2->start
2168     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
2169     # . c/EAX = *start
2170     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2171     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2172     # . if (EAX == '#') return
2173     3d/compare-EAX-and  0x23/imm32/hash
2174     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2175     # op2 = next-token-from-slice(op2->start, op2->end, '/')
2176     # . . push args
2177     52/push-EDX
2178     68/push  0x2f/imm32/slash
2179     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
2180     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
2181     # . . call
2182     e8/call  next-token-from-slice/disp32
2183     # . . discard args
2184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2185     # write-slice-buffered(out, op2)
2186     # . . push args
2187     52/push-EDX
2188     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2189     # . . call
2190     e8/call  write-slice-buffered/disp32
2191     # . . discard args
2192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2193     # write-buffered(out, " ")
2194     # . . push args
2195     68/push  " "/imm32
2196     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2197     # . . call
2198     e8/call  write-buffered/disp32
2199     # . . discard args
2200     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2201     # if (slice-equal?(op1, "0f")) return
2202     # . EAX = slice-equal?(op1, "0f")
2203     # . . push args
2204     68/push  "0f"/imm32
2205     51/push-ECX
2206     # . . call
2207     e8/call  slice-equal?/disp32
2208     # . . discard args
2209     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2210     # . if (EAX != 0) return
2211     3d/compare-EAX-and  0/imm32
2212     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2213     # if (!slice-equal?(op2, "0f")) return
2214     # . EAX = slice-equal?(op2, "0f")
2215     # . . push args
2216     68/push  "0f"/imm32
2217     52/push-EDX
2218     # . . call
2219     e8/call  slice-equal?/disp32
2220     # . . discard args
2221     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2222     # . if (EAX == 0) return
2223     3d/compare-EAX-and  0/imm32
2224     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2225 $emit-opcodes:op3:
2226     # next-word(line, op3)  # reuse op2/EDX
2227     # . . push args
2228     52/push-EDX
2229     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2230     # . . call
2231     e8/call  next-word/disp32
2232     # . . discard args
2233     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2234     # if (slice-empty?(op3)) return
2235     # . EAX = slice-empty?(op3)
2236     # . . push args
2237     52/push-EDX
2238     # . . call
2239     e8/call  slice-empty?/disp32
2240     # . . discard args
2241     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2242     # . if (EAX != 0) return
2243     3d/compare-EAX-and  0/imm32
2244     0f 85/jump-if-not-equal  $emit-opcodes:end/disp32
2245     # if (slice-starts-with?(op3, "#")) return
2246     # . start/EBX = op2->start
2247     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
2248     # . c/EAX = *start
2249     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2250     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
2251     # . if (EAX == '#') return
2252     3d/compare-EAX-and  0x23/imm32/hash
2253     0f 84/jump-if-equal  $emit-opcodes:end/disp32
2254     # op3 = next-token-from-slice(op3->start, op3->end, '/')
2255     # . . push args
2256     52/push-EDX
2257     68/push  0x2f/imm32/slash
2258     ff          6/subop/push        1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # push *(EDX+4)
2259     ff          6/subop/push        0/mod/indirect  2/rm32/EDX    .           .             .           .           .               .                 # push *EDX
2260     # . . call
2261     e8/call  next-token-from-slice/disp32
2262     # . . discard args
2263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2264     # write-slice-buffered(out, op3)
2265     # . . push args
2266     52/push-EDX
2267     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2268     # . . call
2269     e8/call  write-slice-buffered/disp32
2270     # . . discard args
2271     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2272     # write-buffered(out, " ")
2273     # . . push args
2274     68/push  " "/imm32
2275     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2276     # . . call
2277     e8/call  write-buffered/disp32
2278     # . . discard args
2279     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2280 $emit-opcodes:end:
2281     # . restore locals
2282     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
2283     # . restore registers
2284     5b/pop-to-EBX
2285     5a/pop-to-EDX
2286     59/pop-to-ECX
2287     58/pop-to-EAX
2288     # . epilog
2289     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2290     5d/pop-to-EBP
2291     c3/return
2292 
2293 emit-modrm:  # line : (address stream byte), out : (address buffered-file) -> <void>
2294     # pseudocode:
2295     #   rewind-stream(line)
2296     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
2297     #   var word-slice = {0, 0}
2298     #   while true
2299     #     word-slice = next-word(line)
2300     #     if (slice-empty?(word-slice)) break
2301     #     if (slice-starts-with?(word-slice, "#")) break
2302     #     if (has-metadata?(word-slice, "mod"))
2303     #       mod = parse-hex-int(next-token-from-slice(word-slice, "/"))
2304     #       has-modrm? = true
2305     #     else if (has-metadata?(word-slice, "rm32"))
2306     #       rm32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
2307     #       has-modrm? = true
2308     #     else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop"))
2309     #       r32 = parse-hex-int(next-token-from-slice(word-slice, "/"))
2310     #       has-modrm? = true
2311     #   if has-modrm?
2312     #     var modrm = mod & 0b11
2313     #     modrm <<= 3
2314     #     modrm |= r32 & 0b111
2315     #     modrm <<= 3
2316     #     modrm |= rm32 & 0b111
2317     #     emit-hex(out, modrm, 1)
2318     #
2319     # . prolog
2320     55/push-EBP
2321     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2322     # . save registers
2323     50/push-EAX
2324     51/push-ECX
2325     52/push-EDX
2326     53/push-EBX
2327     56/push-ESI
2328     57/push-EDI
2329     # var word-slice/ECX = {0, 0}
2330     68/push  0/imm32/end
2331     68/push  0/imm32/start
2332     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2333     # var has-modrm?/EDX = false
2334     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
2335     # var mod/EBX = 0
2336     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2337     # var rm32/ESI = 0
2338     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
2339     # var r32/EDI = 0
2340     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
2341     # rewind-stream(line)
2342     # . . push args
2343     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2344     # . . call
2345     e8/call  rewind-stream/disp32
2346     # . . discard args
2347     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2348 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2381 $emit-modrm:loop:
2382     # next-word(line, word-slice)
2383     # . . push args
2384     51/push-ECX
2385     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2386     # . . call
2387     e8/call  next-word/disp32
2388     # . . discard args
2389     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2390 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2432 $emit-modrm:check0:
2433     # if (slice-empty?(word-slice)) break
2434     # . EAX = slice-empty?(word-slice)
2435     # . . push args
2436     51/push-ECX
2437     # . . call
2438     e8/call  slice-empty?/disp32
2439     # . . discard args
2440     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2441     # . if (EAX != 0) pass through
2442     3d/compare-EAX-and  0/imm32
2443     0f 85/jump-if-not-equal  $emit-modrm:break/disp32
2444 $emit-modrm:check1:
2445     # if (slice-starts-with?(word-slice, "#")) break
2446     # . spill EDX
2447     52/push-EDX
2448     # . start/EDX = word-slice->start
2449     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
2450     # . c/EAX = *start
2451     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2452     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
2453     # . restore EDX
2454     5a/pop-to-EDX
2455     # . if (EAX == '#') pass through
2456     3d/compare-EAX-and  0x23/imm32/hash
2457     0f 84/jump-if-equal  $emit-modrm:break/disp32
2458 $emit-modrm:check-for-mod:
2459     # if (has-metadata?(word-slice, "mod"))
2460     # . EAX = has-metadata?(ECX, "mod")
2461     # . . push args
2462     68/push  "mod"/imm32
2463     51/push-ECX
2464     # . . call
2465     e8/call  has-metadata?/disp32
2466     # . . discard args
2467     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2468     # . if (EAX == 0) goto next check
2469     3d/compare-EAX-and  0/imm32
2470     74/jump-if-equal  $emit-modrm:check-for-rm32/disp8
2471 $emit-modrm:mod:
2472     # mod = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2473     # . EAX = parse-datum-of-word(word-slice)
2474     # . . push args
2475     51/push-ECX
2476     # . . call
2477     e8/call  parse-datum-of-word/disp32
2478     # . . discard args
2479     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2480     # . mod = EAX
2481     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
2482     # has-modrm? = true
2483     ba/copy-to-EDX  1/imm32/true
2484     # continue
2485     e9/jump  $emit-modrm:loop/disp32
2486 $emit-modrm:check-for-rm32:
2487     # if (has-metadata?(word-slice, "rm32"))
2488     # . EAX = has-metadata?(ECX, "rm32")
2489     # . . push args
2490     68/push  "rm32"/imm32
2491     51/push-ECX
2492     # . . call
2493     e8/call  has-metadata?/disp32
2494     # . . discard args
2495     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2496     # . if (EAX == 0) goto next check
2497     3d/compare-EAX-and  0/imm32
2498     74/jump-if-equal  $emit-modrm:check-for-r32/disp8
2499 $emit-modrm:rm32:
2500     # rm32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2501     # . EAX = parse-datum-of-word(word-slice)
2502     # . . push args
2503     51/push-ECX
2504     # . . call
2505     e8/call  parse-datum-of-word/disp32
2506     # . . discard args
2507     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2508     # . rm32 = EAX
2509     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
2510     # has-modrm? = true
2511     ba/copy-to-EDX  1/imm32/true
2512     # continue
2513     e9/jump  $emit-modrm:loop/disp32
2514 $emit-modrm:check-for-r32:
2515     # if (has-metadata?(word-slice, "r32"))
2516     # . EAX = has-metadata?(ECX, "r32")
2517     # . . push args
2518     68/push  "r32"/imm32
2519     51/push-ECX
2520     # . . call
2521     e8/call  has-metadata?/disp32
2522     # . . discard args
2523     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2524     # . if (EAX == 0) goto next check
2525     3d/compare-EAX-and  0/imm32
2526     74/jump-if-equal  $emit-modrm:check-for-subop/disp8
2527 $emit-modrm:r32:
2528     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2529     # . EAX = parse-datum-of-word(word-slice)
2530     # . . push args
2531     51/push-ECX
2532     # . . call
2533     e8/call  parse-datum-of-word/disp32
2534     # . . discard args
2535     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2536     # . r32 = EAX
2537     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
2538     # has-modrm? = true
2539     ba/copy-to-EDX  1/imm32/true
2540     # continue
2541     e9/jump  $emit-modrm:loop/disp32
2542 $emit-modrm:check-for-subop:
2543     # if (has-metadata?(word-slice, "subop"))
2544     # . EAX = has-metadata?(ECX, "subop")
2545     # . . push args
2546     68/push  "subop"/imm32
2547     51/push-ECX
2548     # . . call
2549     e8/call  has-metadata?/disp32
2550     # . . discard args
2551     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2552     # . if (EAX == 0) loop
2553     3d/compare-EAX-and  0/imm32
2554     0f 84/jump-if-equal  $emit-modrm:loop/disp32
2555 $emit-modrm:subop:
2556     # r32 = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2557     # . EAX = parse-datum-of-word(word-slice)
2558     # . . push args
2559     51/push-ECX
2560     # . . call
2561     e8/call  parse-datum-of-word/disp32
2562     # . . discard args
2563     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2564     # . r32 = EAX
2565     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
2566     # has-modrm? = true
2567     ba/copy-to-EDX  1/imm32/true
2568     # continue
2569     e9/jump  $emit-modrm:loop/disp32
2570 $emit-modrm:break:
2571     # if (!has-modrm?) return
2572     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
2573     74/jump-if-equal  $emit-modrm:end/disp8
2574 $emit-modrm:calculate:
2575     # modrm/EBX = mod & 0b11
2576     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
2577     # modrm <<= 3
2578     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
2579     # modrm |= r32 & 0b111
2580     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
2581     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
2582     # modrm <<= 3
2583     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
2584     # modrm |= rm32 & 0b111
2585     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
2586     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
2587 $emit-modrm:emit:
2588     # emit-hex(out, modrm, 1)
2589     # . . push args
2590     68/push  1/imm32
2591     53/push-EBX
2592     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2593     # . . call
2594     e8/call  emit-hex/disp32
2595     # . . discard args
2596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2597 $emit-modrm:end:
2598     # . restore locals
2599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2600     # . restore registers
2601     5f/pop-to-EDI
2602     5e/pop-to-ESI
2603     5b/pop-to-EBX
2604     5a/pop-to-EDX
2605     59/pop-to-ECX
2606     58/pop-to-EAX
2607     # . epilog
2608     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2609     5d/pop-to-EBP
2610     c3/return
2611 
2612 emit-sib:  # line : (address stream byte), out : (address buffered-file) -> <void>
2613     # pseudocode:
2614     #   var has-sib? = false, base = 0, index = 0, scale = 0
2615     #   var word-slice = {0, 0}
2616     #   while true
2617     #     word-slice = next-word(line)
2618     #     if (slice-empty?(word-slice)) break
2619     #     if (slice-starts-with?(word-slice, "#")) break
2620     #     if (has-metadata?(word-slice, "base")
2621     #       base = parse-hex-int(next-token-from-slice(word-slice, "/"))
2622     #       has-sib? = true
2623     #     else if (has-metadata?(word-slice, "index")
2624     #       index = parse-hex-int(next-token-from-slice(word-slice, "/"))
2625     #       has-sib? = true
2626     #     else if (has-metadata?(word-slice, "scale")
2627     #       scale = parse-hex-int(next-token-from-slice(word-slice, "/"))
2628     #       has-sib? = true
2629     #   if has-sib?
2630     #     var sib = scale & 0b11
2631     #     sib <<= 2
2632     #     sib |= index & 0b111
2633     #     sib <<= 3
2634     #     sib |= base & 0b111
2635     #     emit-hex(out, sib, 1)
2636     #
2637     # . prolog
2638     55/push-EBP
2639     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2640     # . save registers
2641     50/push-EAX
2642     51/push-ECX
2643     52/push-EDX
2644     53/push-EBX
2645     56/push-ESI
2646     57/push-EDI
2647     # var word-slice/ECX = {0, 0}
2648     68/push  0/imm32/end
2649     68/push  0/imm32/start
2650     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2651     # var has-sib?/EDX = false
2652     31/xor                          3/mod/direct    2/rm32/EDX    .           .             .           2/r32/EDX   .               .                 # clear EDX
2653     # var scale/EBX = 0
2654     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
2655     # var base/ESI = 0
2656     31/xor                          3/mod/direct    6/rm32/ESI    .           .             .           6/r32/ESI   .               .                 # clear ESI
2657     # var index/EDI = 0
2658     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
2659     # rewind-stream(line)
2660     # . . push args
2661     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2662     # . . call
2663     e8/call  rewind-stream/disp32
2664     # . . discard args
2665     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2666 $emit-sib:loop:
2667 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2693     # next-word(line, word-slice)
2694     # . . push args
2695     51/push-ECX
2696     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2697     # . . call
2698     e8/call  next-word/disp32
2699     # . . discard args
2700     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2701 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
2743 $emit-sib:check0:
2744     # if (slice-empty?(word-slice)) break
2745     # . EAX = slice-empty?(word-slice)
2746     # . . push args
2747     51/push-ECX
2748     # . . call
2749     e8/call  slice-empty?/disp32
2750     # . . discard args
2751     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2752     # . if (EAX != 0) pass through
2753     3d/compare-EAX-and  0/imm32
2754     0f 85/jump-if-not-equal  $emit-sib:break/disp32
2755 $emit-sib:check1:
2756     # if (slice-starts-with?(word-slice, "#")) break
2757     # . spill EDX
2758     52/push-EDX
2759     # . start/EDX = word-slice->start
2760     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
2761     # . c/EAX = *start
2762     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
2763     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
2764     # . restore EDX
2765     5a/pop-to-EDX
2766     # . if (EAX == '#') pass through
2767     3d/compare-EAX-and  0x23/imm32/hash
2768     0f 84/jump-if-equal  $emit-sib:break/disp32
2769 $emit-sib:check-for-scale:
2770     # if (has-metadata?(word-slice, "scale"))
2771     # . EAX = has-metadata?(ECX, "scale")
2772     # . . push args
2773     68/push  "scale"/imm32
2774     51/push-ECX
2775     # . . call
2776     e8/call  has-metadata?/disp32
2777     # . . discard args
2778     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2779     # . if (EAX == 0) goto next check
2780     3d/compare-EAX-and  0/imm32
2781     74/jump-if-equal  $emit-sib:check-for-base/disp8
2782 $emit-sib:scale:
2783     # scale = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2784     # . EAX = parse-datum-of-word(word-slice)
2785     # . . push args
2786     51/push-ECX
2787     # . . call
2788     e8/call  parse-datum-of-word/disp32
2789     # . . discard args
2790     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2791     # . scale = EAX
2792     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
2793     # has-sib? = true
2794     ba/copy-to-EDX  1/imm32/true
2795     # continue
2796     e9/jump  $emit-sib:loop/disp32
2797 $emit-sib:check-for-base:
2798     # if (has-metadata?(word-slice, "base"))
2799     # . EAX = has-metadata?(ECX, "base")
2800     # . . push args
2801     68/push  "base"/imm32
2802     51/push-ECX
2803     # . . call
2804     e8/call  has-metadata?/disp32
2805     # . . discard args
2806     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2807     # . if (EAX == 0) goto next check
2808     3d/compare-EAX-and  0/imm32
2809     74/jump-if-equal  $emit-sib:check-for-index/disp8
2810 $emit-sib:base:
2811     # base = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2812     # . EAX = parse-datum-of-word(word-slice)
2813     # . . push args
2814     51/push-ECX
2815     # . . call
2816     e8/call  parse-datum-of-word/disp32
2817     # . . discard args
2818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2819     # . base = EAX
2820     89/copy                         3/mod/direct    6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to ESI
2821     # has-sib? = true
2822     ba/copy-to-EDX  1/imm32/true
2823     # continue
2824     e9/jump  $emit-sib:loop/disp32
2825 $emit-sib:check-for-index:
2826     # if (has-metadata?(word-slice, "index"))
2827     # . EAX = has-metadata?(ECX, "index")
2828     # . . push args
2829     68/push  "index"/imm32
2830     51/push-ECX
2831     # . . call
2832     e8/call  has-metadata?/disp32
2833     # . . discard args
2834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2835     # . if (EAX == 0) loop
2836     3d/compare-EAX-and  0/imm32
2837     0f 84/jump-if-equal  $emit-sib:loop/disp32
2838 $emit-sib:index:
2839     # index = parse-hex-int(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2840     # . EAX = parse-datum-of-word(word-slice)
2841     # . . push args
2842     51/push-ECX
2843     # . . call
2844     e8/call  parse-datum-of-word/disp32
2845     # . . discard args
2846     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2847     # . index = EAX
2848     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
2849     # has-sib? = true
2850     ba/copy-to-EDX  1/imm32/true
2851     # continue
2852     e9/jump  $emit-sib:loop/disp32
2853 $emit-sib:break:
2854     # if (!has-sib?) return
2855     81          7/subop/compare     3/mod/direct    2/rm32/EDX    .           .             .           .           .               0/imm32           # compare EDX
2856     74/jump-if-equal  $emit-sib:end/disp8
2857 $emit-sib:calculate:
2858     # sib/EBX = scale & 0b11
2859     81          4/subop/and         3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm32/0b11      # bitwise and of EBX
2860     # sib <<= 2
2861     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               2/imm8            # shift EBX left by 2 bits
2862     # sib |= index & 0b111
2863     81          4/subop/and         3/mod/direct    7/rm32/EDI    .           .             .           .           .               7/imm32/0b111     # bitwise and of EDI
2864     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           7/r32/EDI   .               .                 # EBX = bitwise OR with EDI
2865     # sib <<= 3
2866     c1/shift    4/subop/left        3/mod/direct    3/rm32/EBX    .           .             .           .           .               3/imm8            # shift EBX left by 3 bits
2867     # sib |= base & 0b111
2868     81          4/subop/and         3/mod/direct    6/rm32/ESI    .           .             .           .           .               7/imm32/0b111     # bitwise and of ESI
2869     09/or                           3/mod/direct    3/rm32/EBX    .           .             .           6/r32/ESI   .               .                 # EBX = bitwise OR with ESI
2870 $emit-sib:emit:
2871     # emit-hex(out, sib, 1)
2872     # . . push args
2873     68/push  1/imm32
2874     53/push-EBX
2875     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
2876     # . . call
2877     e8/call  emit-hex/disp32
2878     # . . discard args
2879     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
2880 $emit-sib:end:
2881     # . restore locals
2882     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2883     # . restore registers
2884     5f/pop-to-EDI
2885     5e/pop-to-ESI
2886     5b/pop-to-EBX
2887     5a/pop-to-EDX
2888     59/pop-to-ECX
2889     58/pop-to-EAX
2890     # . epilog
2891     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
2892     5d/pop-to-EBP
2893     c3/return
2894 
2895 emit-disp:  # line : (address stream byte), out : (address buffered-file) -> <void>
2896     # pseudocode:
2897     #   rewind-stream(line)
2898     #   var word-slice = {0, 0}
2899     #   while true
2900     #     word-slice = next-word(line)
2901     #     if (slice-empty?(word-slice)) break
2902     #     if (slice-starts-with?(word-slice, "#")) break
2903     #     if has-metadata?(word-slice, "disp32")
2904     #       emit(out, word-slice, 4)
2905     #       break
2906     #     if has-metadata?(word-slice, "disp16")
2907     #       emit(out, word-slice, 2)
2908     #       break
2909     #     if has-metadata?(word-slice, "disp8")
2910     #       emit(out, word-slice, 1)
2911     #       break
2912     #
2913     # . prolog
2914     55/push-EBP
2915     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
2916     # . save registers
2917     50/push-EAX
2918     51/push-ECX
2919     52/push-EDX
2920     # var word-slice/ECX = {0, 0}
2921     68/push  0/imm32/end
2922     68/push  0/imm32/start
2923     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
2924     # rewind-stream(line)
2925     # . . push args
2926     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2927     # . . call
2928     e8/call  rewind-stream/disp32
2929     # . . discard args
2930     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
2931 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
2957 $emit-disp:loop:
2958     # next-word(line, word-slice)
2959     # . . push args
2960     51/push-ECX
2961     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
2962     # . . call
2963     e8/call  next-word/disp32
2964     # . . discard args
2965     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
2966 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
3008 $emit-disp:check0:
3009     # if (slice-empty?(word-slice)) break
3010     # . EAX = slice-empty?(word-slice)
3011     # . . push args
3012     51/push-ECX
3013     # . . call
3014     e8/call  slice-empty?/disp32
3015     # . . discard args
3016     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3017     # . if (EAX != 0) pass through
3018     3d/compare-EAX-and  0/imm32
3019     0f 85/jump-if-not-equal  $emit-disp:break/disp32
3020 $emit-disp:check1:
3021     # if (slice-starts-with?(word-slice, "#")) break
3022     # . start/EDX = word-slice->start
3023     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
3024     # . c/EAX = *start
3025     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
3026     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
3027     # . if (EAX == '#') break
3028     3d/compare-EAX-and  0x23/imm32/hash
3029     0f 84/jump-if-equal  $emit-disp:break/disp32
3030 $emit-disp:check-for-disp32:
3031     # if (has-metadata?(word-slice, "disp32"))
3032     # . EAX = has-metadata?(ECX, "disp32")
3033     # . . push args
3034     68/push  "disp32"/imm32
3035     51/push-ECX
3036     # . . call
3037     e8/call  has-metadata?/disp32
3038     # . . discard args
3039     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3040     # . if (EAX == 0) goto next check
3041     3d/compare-EAX-and  0/imm32
3042     74/jump-if-equal  $emit-disp:check-for-disp16/disp8
3043 $emit-disp:disp32:
3044     # emit(out, word-slice, 4)
3045     # . . push args
3046     68/push  4/imm32
3047     51/push-ECX
3048     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3049     # . . call
3050     e8/call  emit/disp32
3051     # . . discard args
3052     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3053     # break
3054     e9/jump  $emit-disp:break/disp32
3055 $emit-disp:check-for-disp16:
3056     # else if (has-metadata?(word-slice, "disp16"))
3057     # . EAX = has-metadata?(ECX, "disp16")
3058     # . . push args
3059     68/push  "disp16"/imm32
3060     51/push-ECX
3061     # . . call
3062     e8/call  has-metadata?/disp32
3063     # . . discard args
3064     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3065     # . if (EAX == 0) goto next check
3066     3d/compare-EAX-and  0/imm32
3067     74/jump-if-equal  $emit-disp:check-for-disp8/disp8
3068 $emit-disp:disp16:
3069     # emit(out, word-slice, 2)
3070     # . . push args
3071     68/push  2/imm32
3072     51/push-ECX
3073     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3074     # . . call
3075     e8/call  emit/disp32
3076     # . . discard args
3077     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3078     # break
3079     e9/jump  $emit-disp:break/disp32
3080 $emit-disp:check-for-disp8:
3081     # if (has-metadata?(word-slice, "disp8"))
3082     # . EAX = has-metadata?(ECX, "disp8")
3083     # . . push args
3084     68/push  "disp8"/imm32
3085     51/push-ECX
3086     # . . call
3087     e8/call  has-metadata?/disp32
3088     # . . discard args
3089     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3090     # . if (EAX == 0) loop
3091     3d/compare-EAX-and  0/imm32
3092     0f 84/jump-if-equal  $emit-disp:loop/disp32
3093 $emit-disp:disp8:
3094     # emit(out, word-slice, 1)
3095     # . . push args
3096     68/push  1/imm32
3097     51/push-ECX
3098     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3099     # . . call
3100     e8/call  emit/disp32
3101     # . . discard args
3102     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3103     # break
3104 $emit-disp:break:
3105     # . restore locals
3106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3107     # . restore registers
3108     5a/pop-to-EDX
3109     59/pop-to-ECX
3110     58/pop-to-EAX
3111     # . epilog
3112     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3113     5d/pop-to-EBP
3114     c3/return
3115 
3116 emit-imm:  # line : (address stream byte), out : (address buffered-file) -> <void>
3117     # pseudocode:
3118     #   rewind-stream(line)
3119     #   var word-slice = {0, 0}
3120     #   while true
3121     #     word-slice = next-word(line)
3122     #     if (slice-empty?(word-slice)) break
3123     #     if (slice-starts-with?(word-slice, "#")) break
3124     #     if has-metadata?(word-slice, "imm32")
3125     #       emit(out, word-slice, 4)
3126     #       break
3127     #     if has-metadata?(word-slice, "imm16")
3128     #       emit(out, word-slice, 2)
3129     #       break
3130     #     if has-metadata?(word-slice, "imm8")
3131     #       emit(out, word-slice, 1)
3132     #       break
3133     #
3134     # . prolog
3135     55/push-EBP
3136     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3137     # . save registers
3138     50/push-EAX
3139     51/push-ECX
3140     52/push-EDX
3141     # var word-slice/ECX = {0, 0}
3142     68/push  0/imm32/end
3143     68/push  0/imm32/start
3144     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
3145     # rewind-stream(line)
3146     # . . push args
3147     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3148     # . . call
3149     e8/call  rewind-stream/disp32
3150     # . . discard args
3151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3152 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------
3178 $emit-imm:loop:
3179     # next-word(line, word-slice)
3180     # . . push args
3181     51/push-ECX
3182     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3183     # . . call
3184     e8/call  next-word/disp32
3185     # . . discard args
3186     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3187 +-- 42 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
3229 $emit-imm:check0:
3230     # if (slice-empty?(word-slice)) break
3231     # . EAX = slice-empty?(word-slice)
3232     # . . push args
3233     51/push-ECX
3234     # . . call
3235     e8/call  slice-empty?/disp32
3236     # . . discard args
3237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3238     # . if (EAX != 0) pass through
3239     3d/compare-EAX-and  0/imm32
3240     0f 85/jump-if-not-equal  $emit-imm:break/disp32
3241 $emit-imm:check1:
3242     # if (slice-starts-with?(word-slice, "#")) break
3243     # . start/EDX = slice->start
3244     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # copy *ECX to EDX
3245     # . c/EAX = *start
3246     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
3247     8a/copy-byte                    0/mod/indirect  2/rm32/EDX    .           .             .           0/r32/AL    .               .                 # copy byte at *EDX to AL
3248     # . if (EAX == '#') break
3249     3d/compare-EAX-and  0x23/imm32/hash
3250     0f 84/jump-if-equal  $emit-imm:break/disp32
3251 $emit-imm:check-for-imm32:
3252     # if (has-metadata?(word-slice, "imm32"))
3253     # . EAX = has-metadata?(ECX, "imm32")
3254     # . . push args
3255     68/push  "imm32"/imm32
3256     51/push-ECX
3257     # . . call
3258     e8/call  has-metadata?/disp32
3259     # . . discard args
3260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3261     # . if (EAX == 0) goto next check
3262     3d/compare-EAX-and  0/imm32
3263     74/jump-if-equal  $emit-imm:check-for-imm16/disp8
3264 $emit-imm:imm32:
3265     # emit(out, word-slice, 4)
3266     # . . push args
3267     68/push  4/imm32
3268     51/push-ECX
3269     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3270     # . . call
3271     e8/call  emit/disp32
3272     # . . discard args
3273     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3274     # break
3275     e9/jump  $emit-imm:break/disp32
3276 $emit-imm:check-for-imm16:
3277     # if (has-metadata?(word-slice, "imm16"))
3278     # . EAX = has-metadata?(ECX, "imm16")
3279     # . . push args
3280     68/push  "imm16"/imm32
3281     51/push-ECX
3282     # . . call
3283     e8/call  has-metadata?/disp32
3284     # . . discard args
3285     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3286     # . if (EAX == 0) goto next check
3287     3d/compare-EAX-and  0/imm32
3288     74/jump-if-equal  $emit-imm:check-for-imm8/disp8
3289 $emit-imm:imm16:
3290     # emit(out, word-slice, 2)
3291     # . . push args
3292     68/push  2/imm32
3293     51/push-ECX
3294     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3295     # . . call
3296     e8/call  emit/disp32
3297     # . . discard args
3298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3299     # break
3300     e9/jump  $emit-imm:break/disp32
3301 $emit-imm:check-for-imm8:
3302     # if (has-metadata?(word-slice, "imm8"))
3303     # . EAX = has-metadata?(ECX, "imm8")
3304     # . . push args
3305     68/push  "imm8"/imm32
3306     51/push-ECX
3307     # . . call
3308     e8/call  has-metadata?/disp32
3309     # . . discard args
3310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3311     # . if (EAX == 0) loop
3312     3d/compare-EAX-and  0/imm32
3313     0f 84/jump-if-equal  $emit-imm:loop/disp32
3314 $emit-imm:imm8:
3315     # emit(out, word-slice, 1)
3316     # . . push args
3317     68/push  1/imm32
3318     51/push-ECX
3319     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3320     # . . call
3321     e8/call  emit/disp32
3322     # . . discard args
3323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3324     # break
3325 $emit-imm:break:
3326     # . restore locals
3327     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3328     # . restore registers
3329     5a/pop-to-EDX
3330     59/pop-to-ECX
3331     58/pop-to-EAX
3332     # . epilog
3333     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3334     5d/pop-to-EBP
3335     c3/return
3336 
3337 emit-line-in-comment:  # line : (address stream byte), out : (address buffered-file) -> <void>
3338     # . prolog
3339     55/push-EBP
3340     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3341     # write-buffered(out, " # ")
3342     # . . push args
3343     68/push  " # "/imm32
3344     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3345     # . . call
3346     e8/call  write-buffered/disp32
3347     # . . discard args
3348     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3349     # write-stream-data(out, line)
3350     # . . push args
3351     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
3352     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
3353     # . . call
3354     e8/call  write-stream-data/disp32
3355     # . . discard args
3356     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3357 $emit-line-in-comment:end:
3358     # . epilog
3359     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3360     5d/pop-to-EBP
3361     c3/return
3362 
3363 test-convert-instruction-passes-comments-through:
3364     # if a line starts with '#', pass it along unchanged
3365     # . prolog
3366     55/push-EBP
3367     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3368     # setup
3369     # . clear-stream(_test-input-stream)
3370     # . . push args
3371     68/push  _test-input-stream/imm32
3372     # . . call
3373     e8/call  clear-stream/disp32
3374     # . . discard args
3375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3376     # . clear-stream(_test-output-stream)
3377     # . . push args
3378     68/push  _test-output-stream/imm32
3379     # . . call
3380     e8/call  clear-stream/disp32
3381     # . . discard args
3382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3383     # . clear-stream(_test-output-buffered-file+4)
3384     # . . push args
3385     b8/copy-to-EAX  _test-output-buffered-file/imm32
3386     05/add-to-EAX  4/imm32
3387     50/push-EAX
3388     # . . call
3389     e8/call  clear-stream/disp32
3390     # . . discard args
3391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3392     # initialize input
3393     # . write(_test-input-stream, "# abcd")
3394     # . . push args
3395     68/push  "# abcd"/imm32
3396     68/push  _test-input-stream/imm32
3397     # . . call
3398     e8/call  write/disp32
3399     # . . discard args
3400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3401     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3402     # . . push args
3403     68/push  _test-output-buffered-file/imm32
3404     68/push  _test-input-stream/imm32
3405     # . . call
3406     e8/call  convert-instruction/disp32
3407     # . . discard args
3408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3409     # check that the line just passed through
3410     # . flush(_test-output-buffered-file)
3411     # . . push args
3412     68/push  _test-output-buffered-file/imm32
3413     # . . call
3414     e8/call  flush/disp32
3415     # . . discard args
3416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3417     # . check-stream-equal(_test-output-stream, "# abcd", msg)
3418     # . . push args
3419     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
3420     68/push  "# abcd"/imm32
3421     68/push  _test-output-stream/imm32
3422     # . . call
3423     e8/call  check-stream-equal/disp32
3424     # . . discard args
3425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3426     # . epilog
3427     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3428     5d/pop-to-EBP
3429     c3/return
3430 
3431 test-convert-instruction-passes-labels-through:
3432     # if the first word ends with ':', pass along the entire line unchanged
3433     # . prolog
3434     55/push-EBP
3435     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3436     # setup
3437     # . clear-stream(_test-input-stream)
3438     # . . push args
3439     68/push  _test-input-stream/imm32
3440     # . . call
3441     e8/call  clear-stream/disp32
3442     # . . discard args
3443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3444     # . clear-stream(_test-output-stream)
3445     # . . push args
3446     68/push  _test-output-stream/imm32
3447     # . . call
3448     e8/call  clear-stream/disp32
3449     # . . discard args
3450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3451     # . clear-stream(_test-output-buffered-file+4)
3452     # . . push args
3453     b8/copy-to-EAX  _test-output-buffered-file/imm32
3454     05/add-to-EAX  4/imm32
3455     50/push-EAX
3456     # . . call
3457     e8/call  clear-stream/disp32
3458     # . . discard args
3459     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3460     # initialize input
3461     # . write(_test-input-stream, "ab: # cd")
3462     # . . push args
3463     68/push  "ab: # cd"/imm32
3464     68/push  _test-input-stream/imm32
3465     # . . call
3466     e8/call  write/disp32
3467     # . . discard args
3468     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3469     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3470     # . . push args
3471     68/push  _test-output-buffered-file/imm32
3472     68/push  _test-input-stream/imm32
3473     # . . call
3474     e8/call  convert-instruction/disp32
3475     # . . discard args
3476     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3477     # check that the line just passed through
3478     # . flush(_test-output-buffered-file)
3479     # . . push args
3480     68/push  _test-output-buffered-file/imm32
3481     # . . call
3482     e8/call  flush/disp32
3483     # . . discard args
3484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3485     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
3486     # . . push args
3487     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
3488     68/push  "ab: # cd"/imm32
3489     68/push  _test-output-stream/imm32
3490     # . . call
3491     e8/call  check-stream-equal/disp32
3492     # . . discard args
3493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3494     # . epilog
3495     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3496     5d/pop-to-EBP
3497     c3/return
3498 
3499 test-convert-instruction-handles-single-opcode:
3500     # if the instruction consists of a single opcode, strip its metadata and pass it along
3501     # . prolog
3502     55/push-EBP
3503     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3504     # setup
3505     # . clear-stream(_test-input-stream)
3506     # . . push args
3507     68/push  _test-input-stream/imm32
3508     # . . call
3509     e8/call  clear-stream/disp32
3510     # . . discard args
3511     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3512     # . clear-stream(_test-output-stream)
3513     # . . push args
3514     68/push  _test-output-stream/imm32
3515     # . . call
3516     e8/call  clear-stream/disp32
3517     # . . discard args
3518     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3519     # . clear-stream(_test-output-buffered-file+4)
3520     # . . push args
3521     b8/copy-to-EAX  _test-output-buffered-file/imm32
3522     05/add-to-EAX  4/imm32
3523     50/push-EAX
3524     # . . call
3525     e8/call  clear-stream/disp32
3526     # . . discard args
3527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3528     # initialize input
3529     # . write(_test-input-stream, "ab/cd # comment")
3530     # . . push args
3531     68/push  "ab/cd # comment"/imm32
3532     68/push  _test-input-stream/imm32
3533     # . . call
3534     e8/call  write/disp32
3535     # . . discard args
3536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3537     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3538     # . . push args
3539     68/push  _test-output-buffered-file/imm32
3540     68/push  _test-input-stream/imm32
3541     # . . call
3542     e8/call  convert-instruction/disp32
3543     # . . discard args
3544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3545     # check output
3546     # . flush(_test-output-buffered-file)
3547     # . . push args
3548     68/push  _test-output-buffered-file/imm32
3549     # . . call
3550     e8/call  flush/disp32
3551     # . . discard args
3552     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3553 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3579     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
3580     # . . push args
3581     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
3582     68/push  "ab  # ab/cd # comment"/imm32
3583     68/push  _test-output-stream/imm32
3584     # . . call
3585     e8/call  check-stream-equal/disp32
3586     # . . discard args
3587     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3588     # . epilog
3589     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3590     5d/pop-to-EBP
3591     c3/return
3592 
3593 test-convert-instruction-handles-0f-opcode:
3594     # if the instruction starts with 0f opcode, include a second opcode
3595     # . prolog
3596     55/push-EBP
3597     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3598     # setup
3599     # . clear-stream(_test-input-stream)
3600     # . . push args
3601     68/push  _test-input-stream/imm32
3602     # . . call
3603     e8/call  clear-stream/disp32
3604     # . . discard args
3605     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3606     # . clear-stream(_test-output-stream)
3607     # . . push args
3608     68/push  _test-output-stream/imm32
3609     # . . call
3610     e8/call  clear-stream/disp32
3611     # . . discard args
3612     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3613     # . clear-stream(_test-output-buffered-file+4)
3614     # . . push args
3615     b8/copy-to-EAX  _test-output-buffered-file/imm32
3616     05/add-to-EAX  4/imm32
3617     50/push-EAX
3618     # . . call
3619     e8/call  clear-stream/disp32
3620     # . . discard args
3621     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3622     # initialize input
3623     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
3624     # . . push args
3625     68/push  "0f/m1 ab/m2 # comment"/imm32
3626     68/push  _test-input-stream/imm32
3627     # . . call
3628     e8/call  write/disp32
3629     # . . discard args
3630     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3631     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3632     # . . push args
3633     68/push  _test-output-buffered-file/imm32
3634     68/push  _test-input-stream/imm32
3635     # . . call
3636     e8/call  convert-instruction/disp32
3637     # . . discard args
3638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3639     # check output
3640     # . flush(_test-output-buffered-file)
3641     # . . push args
3642     68/push  _test-output-buffered-file/imm32
3643     # . . call
3644     e8/call  flush/disp32
3645     # . . discard args
3646     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3647 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3673     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
3674     # . . push args
3675     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
3676     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
3677     68/push  _test-output-stream/imm32
3678     # . . call
3679     e8/call  check-stream-equal/disp32
3680     # . . discard args
3681     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3682     # . epilog
3683     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3684     5d/pop-to-EBP
3685     c3/return
3686 
3687 test-convert-instruction-handles-f2-opcode:
3688     # if the instruction starts with f2 opcode, include a second opcode
3689     # . prolog
3690     55/push-EBP
3691     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3692     # setup
3693     # . clear-stream(_test-input-stream)
3694     # . . push args
3695     68/push  _test-input-stream/imm32
3696     # . . call
3697     e8/call  clear-stream/disp32
3698     # . . discard args
3699     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3700     # . clear-stream(_test-output-stream)
3701     # . . push args
3702     68/push  _test-output-stream/imm32
3703     # . . call
3704     e8/call  clear-stream/disp32
3705     # . . discard args
3706     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3707     # . clear-stream(_test-output-buffered-file+4)
3708     # . . push args
3709     b8/copy-to-EAX  _test-output-buffered-file/imm32
3710     05/add-to-EAX  4/imm32
3711     50/push-EAX
3712     # . . call
3713     e8/call  clear-stream/disp32
3714     # . . discard args
3715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3716     # initialize input
3717     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
3718     # . . push args
3719     68/push  "f2/m1 ab/m2 # comment"/imm32
3720     68/push  _test-input-stream/imm32
3721     # . . call
3722     e8/call  write/disp32
3723     # . . discard args
3724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3725     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3726     # . . push args
3727     68/push  _test-output-buffered-file/imm32
3728     68/push  _test-input-stream/imm32
3729     # . . call
3730     e8/call  convert-instruction/disp32
3731     # . . discard args
3732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3733     # check output
3734     # . flush(_test-output-buffered-file)
3735     # . . push args
3736     68/push  _test-output-buffered-file/imm32
3737     # . . call
3738     e8/call  flush/disp32
3739     # . . discard args
3740     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3741 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3767     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
3768     # . . push args
3769     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
3770     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
3771     68/push  _test-output-stream/imm32
3772     # . . call
3773     e8/call  check-stream-equal/disp32
3774     # . . discard args
3775     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3776     # . epilog
3777     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3778     5d/pop-to-EBP
3779     c3/return
3780 
3781 test-convert-instruction-handles-f3-opcode:
3782     # if the instruction starts with f3 opcode, include a second opcode
3783     # . prolog
3784     55/push-EBP
3785     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3786     # setup
3787     # . clear-stream(_test-input-stream)
3788     # . . push args
3789     68/push  _test-input-stream/imm32
3790     # . . call
3791     e8/call  clear-stream/disp32
3792     # . . discard args
3793     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3794     # . clear-stream(_test-output-stream)
3795     # . . push args
3796     68/push  _test-output-stream/imm32
3797     # . . call
3798     e8/call  clear-stream/disp32
3799     # . . discard args
3800     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3801     # . clear-stream(_test-output-buffered-file+4)
3802     # . . push args
3803     b8/copy-to-EAX  _test-output-buffered-file/imm32
3804     05/add-to-EAX  4/imm32
3805     50/push-EAX
3806     # . . call
3807     e8/call  clear-stream/disp32
3808     # . . discard args
3809     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3810     # initialize input
3811     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
3812     # . . push args
3813     68/push  "f3/m1 ab/m2 # comment"/imm32
3814     68/push  _test-input-stream/imm32
3815     # . . call
3816     e8/call  write/disp32
3817     # . . discard args
3818     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3819     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3820     # . . push args
3821     68/push  _test-output-buffered-file/imm32
3822     68/push  _test-input-stream/imm32
3823     # . . call
3824     e8/call  convert-instruction/disp32
3825     # . . discard args
3826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3827     # check output
3828     # . flush(_test-output-buffered-file)
3829     # . . push args
3830     68/push  _test-output-buffered-file/imm32
3831     # . . call
3832     e8/call  flush/disp32
3833     # . . discard args
3834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3835 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3861     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
3862     # . . push args
3863     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
3864     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
3865     68/push  _test-output-stream/imm32
3866     # . . call
3867     e8/call  check-stream-equal/disp32
3868     # . . discard args
3869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3870     # . epilog
3871     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3872     5d/pop-to-EBP
3873     c3/return
3874 
3875 test-convert-instruction-handles-f2-0f-opcode:
3876     # if the instruction starts with f2 0f opcode, include a second opcode
3877     # . prolog
3878     55/push-EBP
3879     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3880     # setup
3881     # . clear-stream(_test-input-stream)
3882     # . . push args
3883     68/push  _test-input-stream/imm32
3884     # . . call
3885     e8/call  clear-stream/disp32
3886     # . . discard args
3887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3888     # . clear-stream(_test-output-stream)
3889     # . . push args
3890     68/push  _test-output-stream/imm32
3891     # . . call
3892     e8/call  clear-stream/disp32
3893     # . . discard args
3894     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3895     # . clear-stream(_test-output-buffered-file+4)
3896     # . . push args
3897     b8/copy-to-EAX  _test-output-buffered-file/imm32
3898     05/add-to-EAX  4/imm32
3899     50/push-EAX
3900     # . . call
3901     e8/call  clear-stream/disp32
3902     # . . discard args
3903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3904     # initialize input
3905     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
3906     # . . push args
3907     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
3908     68/push  _test-input-stream/imm32
3909     # . . call
3910     e8/call  write/disp32
3911     # . . discard args
3912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3913     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3914     # . . push args
3915     68/push  _test-output-buffered-file/imm32
3916     68/push  _test-input-stream/imm32
3917     # . . call
3918     e8/call  convert-instruction/disp32
3919     # . . discard args
3920     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
3921     # check output
3922     # . flush(_test-output-buffered-file)
3923     # . . push args
3924     68/push  _test-output-buffered-file/imm32
3925     # . . call
3926     e8/call  flush/disp32
3927     # . . discard args
3928     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3929 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3955     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
3956     # . . push args
3957     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
3958     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
3959     68/push  _test-output-stream/imm32
3960     # . . call
3961     e8/call  check-stream-equal/disp32
3962     # . . discard args
3963     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
3964     # . epilog
3965     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
3966     5d/pop-to-EBP
3967     c3/return
3968 
3969 test-convert-instruction-handles-f3-0f-opcode:
3970     # if the instruction starts with f3 0f opcode, include a second opcode
3971     # . prolog
3972     55/push-EBP
3973     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
3974     # setup
3975     # . clear-stream(_test-input-stream)
3976     # . . push args
3977     68/push  _test-input-stream/imm32
3978     # . . call
3979     e8/call  clear-stream/disp32
3980     # . . discard args
3981     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3982     # . clear-stream(_test-output-stream)
3983     # . . push args
3984     68/push  _test-output-stream/imm32
3985     # . . call
3986     e8/call  clear-stream/disp32
3987     # . . discard args
3988     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3989     # . clear-stream(_test-output-buffered-file+4)
3990     # . . push args
3991     b8/copy-to-EAX  _test-output-buffered-file/imm32
3992     05/add-to-EAX  4/imm32
3993     50/push-EAX
3994     # . . call
3995     e8/call  clear-stream/disp32
3996     # . . discard args
3997     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
3998     # initialize input
3999     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
4000     # . . push args
4001     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
4002     68/push  _test-input-stream/imm32
4003     # . . call
4004     e8/call  write/disp32
4005     # . . discard args
4006     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4007     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4008     # . . push args
4009     68/push  _test-output-buffered-file/imm32
4010     68/push  _test-input-stream/imm32
4011     # . . call
4012     e8/call  convert-instruction/disp32
4013     # . . discard args
4014     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4015     # check output
4016     # . flush(_test-output-buffered-file)
4017     # . . push args
4018     68/push  _test-output-buffered-file/imm32
4019     # . . call
4020     e8/call  flush/disp32
4021     # . . discard args
4022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4023 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4049     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4050     # . . push args
4051     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
4052     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
4053     68/push  _test-output-stream/imm32
4054     # . . call
4055     e8/call  check-stream-equal/disp32
4056     # . . discard args
4057     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4058     # . epilog
4059     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4060     5d/pop-to-EBP
4061     c3/return
4062 
4063 test-convert-instruction-handles-unused-opcodes:
4064     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
4065     # . prolog
4066     55/push-EBP
4067     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4068     # setup
4069     # . clear-stream(_test-input-stream)
4070     # . . push args
4071     68/push  _test-input-stream/imm32
4072     # . . call
4073     e8/call  clear-stream/disp32
4074     # . . discard args
4075     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4076     # . clear-stream(_test-output-stream)
4077     # . . push args
4078     68/push  _test-output-stream/imm32
4079     # . . call
4080     e8/call  clear-stream/disp32
4081     # . . discard args
4082     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4083     # . clear-stream(_test-output-buffered-file+4)
4084     # . . push args
4085     b8/copy-to-EAX  _test-output-buffered-file/imm32
4086     05/add-to-EAX  4/imm32
4087     50/push-EAX
4088     # . . call
4089     e8/call  clear-stream/disp32
4090     # . . discard args
4091     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4092     # initialize input
4093     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
4094     # . . push args
4095     68/push  "ab/m1 cd/m2 # comment"/imm32
4096     68/push  _test-input-stream/imm32
4097     # . . call
4098     e8/call  write/disp32
4099     # . . discard args
4100     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4101     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4102     # . . push args
4103     68/push  _test-output-buffered-file/imm32
4104     68/push  _test-input-stream/imm32
4105     # . . call
4106     e8/call  convert-instruction/disp32
4107     # . . discard args
4108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4109     # check output
4110     # . flush(_test-output-buffered-file)
4111     # . . push args
4112     68/push  _test-output-buffered-file/imm32
4113     # . . call
4114     e8/call  flush/disp32
4115     # . . discard args
4116     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4117 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4143     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4144     # . . push args
4145     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
4146     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
4147     68/push  _test-output-stream/imm32
4148     # . . call
4149     e8/call  check-stream-equal/disp32
4150     # . . discard args
4151     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4152     # . epilog
4153     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4154     5d/pop-to-EBP
4155     c3/return
4156 
4157 test-convert-instruction-handles-unused-second-opcodes:
4158     # if the second opcode isn't 0f, don't include further opcodes
4159     # . prolog
4160     55/push-EBP
4161     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4162     # setup
4163     # . clear-stream(_test-input-stream)
4164     # . . push args
4165     68/push  _test-input-stream/imm32
4166     # . . call
4167     e8/call  clear-stream/disp32
4168     # . . discard args
4169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4170     # . clear-stream(_test-output-stream)
4171     # . . push args
4172     68/push  _test-output-stream/imm32
4173     # . . call
4174     e8/call  clear-stream/disp32
4175     # . . discard args
4176     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4177     # . clear-stream(_test-output-buffered-file+4)
4178     # . . push args
4179     b8/copy-to-EAX  _test-output-buffered-file/imm32
4180     05/add-to-EAX  4/imm32
4181     50/push-EAX
4182     # . . call
4183     e8/call  clear-stream/disp32
4184     # . . discard args
4185     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4186     # initialize input
4187     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
4188     # . . push args
4189     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
4190     68/push  _test-input-stream/imm32
4191     # . . call
4192     e8/call  write/disp32
4193     # . . discard args
4194     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4195     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4196     # . . push args
4197     68/push  _test-output-buffered-file/imm32
4198     68/push  _test-input-stream/imm32
4199     # . . call
4200     e8/call  convert-instruction/disp32
4201     # . . discard args
4202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4203     # check output
4204     # . flush(_test-output-buffered-file)
4205     # . . push args
4206     68/push  _test-output-buffered-file/imm32
4207     # . . call
4208     e8/call  flush/disp32
4209     # . . discard args
4210     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4211 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4237     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
4238     # . . push args
4239     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4240     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
4241     68/push  _test-output-stream/imm32
4242     # . . call
4243     e8/call  check-stream-equal/disp32
4244     # . . discard args
4245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4246     # . epilog
4247     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4248     5d/pop-to-EBP
4249     c3/return
4250 
4251 test-convert-instruction-handles-unused-second-opcodes-2:
4252     # if the second opcode isn't 0f, don't include further opcodes
4253     # . prolog
4254     55/push-EBP
4255     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4256     # setup
4257     # . clear-stream(_test-input-stream)
4258     # . . push args
4259     68/push  _test-input-stream/imm32
4260     # . . call
4261     e8/call  clear-stream/disp32
4262     # . . discard args
4263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4264     # . clear-stream(_test-output-stream)
4265     # . . push args
4266     68/push  _test-output-stream/imm32
4267     # . . call
4268     e8/call  clear-stream/disp32
4269     # . . discard args
4270     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4271     # . clear-stream(_test-output-buffered-file+4)
4272     # . . push args
4273     b8/copy-to-EAX  _test-output-buffered-file/imm32
4274     05/add-to-EAX  4/imm32
4275     50/push-EAX
4276     # . . call
4277     e8/call  clear-stream/disp32
4278     # . . discard args
4279     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4280     # initialize input
4281     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
4282     # . . push args
4283     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
4284     68/push  _test-input-stream/imm32
4285     # . . call
4286     e8/call  write/disp32
4287     # . . discard args
4288     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4289     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4290     # . . push args
4291     68/push  _test-output-buffered-file/imm32
4292     68/push  _test-input-stream/imm32
4293     # . . call
4294     e8/call  convert-instruction/disp32
4295     # . . discard args
4296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4297     # check output
4298     # . flush(_test-output-buffered-file)
4299     # . . push args
4300     68/push  _test-output-buffered-file/imm32
4301     # . . call
4302     e8/call  flush/disp32
4303     # . . discard args
4304     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4305 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4331     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
4332     # . . push args
4333     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4334     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
4335     68/push  _test-output-stream/imm32
4336     # . . call
4337     e8/call  check-stream-equal/disp32
4338     # . . discard args
4339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4340     # . epilog
4341     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4342     5d/pop-to-EBP
4343     c3/return
4344 
4345 test-convert-instruction-emits-modrm-byte:
4346     # pack mod, rm32 and r32 operands into ModR/M byte
4347     # . prolog
4348     55/push-EBP
4349     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4350     # setup
4351     # . clear-stream(_test-input-stream)
4352     # . . push args
4353     68/push  _test-input-stream/imm32
4354     # . . call
4355     e8/call  clear-stream/disp32
4356     # . . discard args
4357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4358     # . clear-stream(_test-output-stream)
4359     # . . push args
4360     68/push  _test-output-stream/imm32
4361     # . . call
4362     e8/call  clear-stream/disp32
4363     # . . discard args
4364     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4365     # . clear-stream(_test-output-buffered-file+4)
4366     # . . push args
4367     b8/copy-to-EAX  _test-output-buffered-file/imm32
4368     05/add-to-EAX  4/imm32
4369     50/push-EAX
4370     # . . call
4371     e8/call  clear-stream/disp32
4372     # . . discard args
4373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4374     # initialize input
4375     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
4376     # . . push args
4377     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
4378     68/push  _test-input-stream/imm32
4379     # . . call
4380     e8/call  write/disp32
4381     # . . discard args
4382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4383     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4384     # . . push args
4385     68/push  _test-output-buffered-file/imm32
4386     68/push  _test-input-stream/imm32
4387     # . . call
4388     e8/call  convert-instruction/disp32
4389     # . . discard args
4390     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4391     # check output
4392     # . flush(_test-output-buffered-file)
4393     # . . push args
4394     68/push  _test-output-buffered-file/imm32
4395     # . . call
4396     e8/call  flush/disp32
4397     # . . discard args
4398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4399 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4425     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
4426     # . . push args
4427     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
4428     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
4429     68/push  _test-output-stream/imm32
4430     # . . call
4431     e8/call  check-stream-equal/disp32
4432     # . . discard args
4433     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4434     # . epilog
4435     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4436     5d/pop-to-EBP
4437     c3/return
4438 
4439 test-convert-instruction-emits-modrm-byte-with-non-zero-mod:
4440     # . prolog
4441     55/push-EBP
4442     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4443     # setup
4444     # . clear-stream(_test-input-stream)
4445     # . . push args
4446     68/push  _test-input-stream/imm32
4447     # . . call
4448     e8/call  clear-stream/disp32
4449     # . . discard args
4450     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4451     # . clear-stream(_test-output-stream)
4452     # . . push args
4453     68/push  _test-output-stream/imm32
4454     # . . call
4455     e8/call  clear-stream/disp32
4456     # . . discard args
4457     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4458     # . clear-stream(_test-output-buffered-file+4)
4459     # . . push args
4460     b8/copy-to-EAX  _test-output-buffered-file/imm32
4461     05/add-to-EAX  4/imm32
4462     50/push-EAX
4463     # . . call
4464     e8/call  clear-stream/disp32
4465     # . . discard args
4466     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4467     # initialize input
4468     # . write(_test-input-stream, "01/add 3/mod/direct 3/rm32/EBX 1/r32/ECX")
4469     # . . push args
4470     68/push  "01/add 3/mod/direct 3/rm32/EBX 1/r32/ECX"/imm32
4471     68/push  _test-input-stream/imm32
4472     # . . call
4473     e8/call  write/disp32
4474     # . . discard args
4475     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4476     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4477     # . . push args
4478     68/push  _test-output-buffered-file/imm32
4479     68/push  _test-input-stream/imm32
4480     # . . call
4481     e8/call  convert-instruction/disp32
4482     # . . discard args
4483     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4484     # . flush(_test-output-buffered-file)
4485     # . . push args
4486     68/push  _test-output-buffered-file/imm32
4487     # . . call
4488     e8/call  flush/disp32
4489     # . . discard args
4490     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4491 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4517     # check output
4518     # . check-stream-equal(_test-output-stream, "# abcd", msg)
4519     # . . push args
4520     68/push  "F - test-convert-instruction-foo"/imm32
4521     68/push  "01 cb  # 01/add 3/mod/direct 3/rm32/EBX 1/r32/ECX"/imm32
4522     68/push  _test-output-stream/imm32
4523     # . . call
4524     e8/call  check-stream-equal/disp32
4525     # . . discard args
4526     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4527     # . epilog
4528     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4529     5d/pop-to-EBP
4530     c3/return
4531 
4532 test-convert-instruction-emits-modrm-byte-from-subop:
4533     # pack mod, rm32 and subop operands into ModR/M byte
4534     # . prolog
4535     55/push-EBP
4536     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4537     # setup
4538     # . clear-stream(_test-input-stream)
4539     # . . push args
4540     68/push  _test-input-stream/imm32
4541     # . . call
4542     e8/call  clear-stream/disp32
4543     # . . discard args
4544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4545     # . clear-stream(_test-output-stream)
4546     # . . push args
4547     68/push  _test-output-stream/imm32
4548     # . . call
4549     e8/call  clear-stream/disp32
4550     # . . discard args
4551     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4552     # . clear-stream(_test-output-buffered-file+4)
4553     # . . push args
4554     b8/copy-to-EAX  _test-output-buffered-file/imm32
4555     05/add-to-EAX  4/imm32
4556     50/push-EAX
4557     # . . call
4558     e8/call  clear-stream/disp32
4559     # . . discard args
4560     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4561     # initialize input
4562     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
4563     # . . push args
4564     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
4565     68/push  _test-input-stream/imm32
4566     # . . call
4567     e8/call  write/disp32
4568     # . . discard args
4569     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4570     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4571     # . . push args
4572     68/push  _test-output-buffered-file/imm32
4573     68/push  _test-input-stream/imm32
4574     # . . call
4575     e8/call  convert-instruction/disp32
4576     # . . discard args
4577     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4578     # check output
4579     # . flush(_test-output-buffered-file)
4580     # . . push args
4581     68/push  _test-output-buffered-file/imm32
4582     # . . call
4583     e8/call  flush/disp32
4584     # . . discard args
4585     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4586 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4612     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
4613     # . . push args
4614     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
4615     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
4616     68/push  _test-output-stream/imm32
4617     # . . call
4618     e8/call  check-stream-equal/disp32
4619     # . . discard args
4620     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4621     # . epilog
4622     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4623     5d/pop-to-EBP
4624     c3/return
4625 
4626 test-convert-instruction-emits-modrm-byte-with-missing-mod:
4627     # pack rm32 and r32 operands into ModR/M byte
4628     # . prolog
4629     55/push-EBP
4630     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4631     # setup
4632     # . clear-stream(_test-input-stream)
4633     # . . push args
4634     68/push  _test-input-stream/imm32
4635     # . . call
4636     e8/call  clear-stream/disp32
4637     # . . discard args
4638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4639     # . clear-stream(_test-output-stream)
4640     # . . push args
4641     68/push  _test-output-stream/imm32
4642     # . . call
4643     e8/call  clear-stream/disp32
4644     # . . discard args
4645     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4646     # . clear-stream(_test-output-buffered-file+4)
4647     # . . push args
4648     b8/copy-to-EAX  _test-output-buffered-file/imm32
4649     05/add-to-EAX  4/imm32
4650     50/push-EAX
4651     # . . call
4652     e8/call  clear-stream/disp32
4653     # . . discard args
4654     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4655     # initialize input
4656     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
4657     # . . push args
4658     68/push  "8b/copy 0/rm32 1/r32"/imm32
4659     68/push  _test-input-stream/imm32
4660     # . . call
4661     e8/call  write/disp32
4662     # . . discard args
4663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4664     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4665     # . . push args
4666     68/push  _test-output-buffered-file/imm32
4667     68/push  _test-input-stream/imm32
4668     # . . call
4669     e8/call  convert-instruction/disp32
4670     # . . discard args
4671     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4672     # check output
4673     # . flush(_test-output-buffered-file)
4674     # . . push args
4675     68/push  _test-output-buffered-file/imm32
4676     # . . call
4677     e8/call  flush/disp32
4678     # . . discard args
4679     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4680 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4706     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
4707     # . . push args
4708     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
4709     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
4710     68/push  _test-output-stream/imm32
4711     # . . call
4712     e8/call  check-stream-equal/disp32
4713     # . . discard args
4714     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4715     # . epilog
4716     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4717     5d/pop-to-EBP
4718     c3/return
4719 
4720 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
4721     # pack mod and r32 operands into ModR/M byte
4722     # . prolog
4723     55/push-EBP
4724     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4725     # setup
4726     # . clear-stream(_test-input-stream)
4727     # . . push args
4728     68/push  _test-input-stream/imm32
4729     # . . call
4730     e8/call  clear-stream/disp32
4731     # . . discard args
4732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4733     # . clear-stream(_test-output-stream)
4734     # . . push args
4735     68/push  _test-output-stream/imm32
4736     # . . call
4737     e8/call  clear-stream/disp32
4738     # . . discard args
4739     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4740     # . clear-stream(_test-output-buffered-file+4)
4741     # . . push args
4742     b8/copy-to-EAX  _test-output-buffered-file/imm32
4743     05/add-to-EAX  4/imm32
4744     50/push-EAX
4745     # . . call
4746     e8/call  clear-stream/disp32
4747     # . . discard args
4748     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4749     # initialize input
4750     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
4751     # . . push args
4752     68/push  "8b/copy 0/mod 1/r32"/imm32
4753     68/push  _test-input-stream/imm32
4754     # . . call
4755     e8/call  write/disp32
4756     # . . discard args
4757     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4758     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4759     # . . push args
4760     68/push  _test-output-buffered-file/imm32
4761     68/push  _test-input-stream/imm32
4762     # . . call
4763     e8/call  convert-instruction/disp32
4764     # . . discard args
4765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4766     # check output
4767     # . flush(_test-output-buffered-file)
4768     # . . push args
4769     68/push  _test-output-buffered-file/imm32
4770     # . . call
4771     e8/call  flush/disp32
4772     # . . discard args
4773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4774 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4800     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
4801     # . . push args
4802     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
4803     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
4804     68/push  _test-output-stream/imm32
4805     # . . call
4806     e8/call  check-stream-equal/disp32
4807     # . . discard args
4808     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4809     # . epilog
4810     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4811     5d/pop-to-EBP
4812     c3/return
4813 
4814 test-convert-instruction-emits-modrm-byte-with-missing-r32:
4815     # pack mod and rm32 operands into ModR/M byte
4816     # . prolog
4817     55/push-EBP
4818     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4819     # setup
4820     # . clear-stream(_test-input-stream)
4821     # . . push args
4822     68/push  _test-input-stream/imm32
4823     # . . call
4824     e8/call  clear-stream/disp32
4825     # . . discard args
4826     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4827     # . clear-stream(_test-output-stream)
4828     # . . push args
4829     68/push  _test-output-stream/imm32
4830     # . . call
4831     e8/call  clear-stream/disp32
4832     # . . discard args
4833     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4834     # . clear-stream(_test-output-buffered-file+4)
4835     # . . push args
4836     b8/copy-to-EAX  _test-output-buffered-file/imm32
4837     05/add-to-EAX  4/imm32
4838     50/push-EAX
4839     # . . call
4840     e8/call  clear-stream/disp32
4841     # . . discard args
4842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4843     # initialize input
4844     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
4845     # . . push args
4846     68/push  "8b/copy 0/mod 0/rm32"/imm32
4847     68/push  _test-input-stream/imm32
4848     # . . call
4849     e8/call  write/disp32
4850     # . . discard args
4851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4852     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4853     # . . push args
4854     68/push  _test-output-buffered-file/imm32
4855     68/push  _test-input-stream/imm32
4856     # . . call
4857     e8/call  convert-instruction/disp32
4858     # . . discard args
4859     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4860     # check output
4861     # . flush(_test-output-buffered-file)
4862     # . . push args
4863     68/push  _test-output-buffered-file/imm32
4864     # . . call
4865     e8/call  flush/disp32
4866     # . . discard args
4867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4868 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4894     # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
4895     # . . push args
4896     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
4897     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
4898     68/push  _test-output-stream/imm32
4899     # . . call
4900     e8/call  check-stream-equal/disp32
4901     # . . discard args
4902     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4903     # . epilog
4904     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4905     5d/pop-to-EBP
4906     c3/return
4907 
4908 test-convert-instruction-emits-sib-byte:
4909     # pack base, index and scale operands into SIB byte
4910     # . prolog
4911     55/push-EBP
4912     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
4913     # setup
4914     # . clear-stream(_test-input-stream)
4915     # . . push args
4916     68/push  _test-input-stream/imm32
4917     # . . call
4918     e8/call  clear-stream/disp32
4919     # . . discard args
4920     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4921     # . clear-stream(_test-output-stream)
4922     # . . push args
4923     68/push  _test-output-stream/imm32
4924     # . . call
4925     e8/call  clear-stream/disp32
4926     # . . discard args
4927     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4928     # . clear-stream(_test-output-buffered-file+4)
4929     # . . push args
4930     b8/copy-to-EAX  _test-output-buffered-file/imm32
4931     05/add-to-EAX  4/imm32
4932     50/push-EAX
4933     # . . call
4934     e8/call  clear-stream/disp32
4935     # . . discard args
4936     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4937     # initialize input
4938     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
4939     # . . push args
4940     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
4941     68/push  _test-input-stream/imm32
4942     # . . call
4943     e8/call  write/disp32
4944     # . . discard args
4945     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4946     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4947     # . . push args
4948     68/push  _test-output-buffered-file/imm32
4949     68/push  _test-input-stream/imm32
4950     # . . call
4951     e8/call  convert-instruction/disp32
4952     # . . discard args
4953     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
4954     # check output
4955     # . flush(_test-output-buffered-file)
4956     # . . push args
4957     68/push  _test-output-buffered-file/imm32
4958     # . . call
4959     e8/call  flush/disp32
4960     # . . discard args
4961     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
4962 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
4988     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
4989     # . . push args
4990     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
4991     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
4992     68/push  _test-output-stream/imm32
4993     # . . call
4994     e8/call  check-stream-equal/disp32
4995     # . . discard args
4996     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
4997     # . epilog
4998     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
4999     5d/pop-to-EBP
5000     c3/return
5001 
5002 test-convert-instruction-emits-sib-byte-with-missing-base:
5003     # pack index and scale operands into SIB byte
5004     # . prolog
5005     55/push-EBP
5006     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5007     # setup
5008     # . clear-stream(_test-input-stream)
5009     # . . push args
5010     68/push  _test-input-stream/imm32
5011     # . . call
5012     e8/call  clear-stream/disp32
5013     # . . discard args
5014     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5015     # . clear-stream(_test-output-stream)
5016     # . . push args
5017     68/push  _test-output-stream/imm32
5018     # . . call
5019     e8/call  clear-stream/disp32
5020     # . . discard args
5021     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5022     # . clear-stream(_test-output-buffered-file+4)
5023     # . . push args
5024     b8/copy-to-EAX  _test-output-buffered-file/imm32
5025     05/add-to-EAX  4/imm32
5026     50/push-EAX
5027     # . . call
5028     e8/call  clear-stream/disp32
5029     # . . discard args
5030     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5031     # initialize input
5032     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
5033     # . . push args
5034     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5035     68/push  _test-input-stream/imm32
5036     # . . call
5037     e8/call  write/disp32
5038     # . . discard args
5039     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5040     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5041     # . . push args
5042     68/push  _test-output-buffered-file/imm32
5043     68/push  _test-input-stream/imm32
5044     # . . call
5045     e8/call  convert-instruction/disp32
5046     # . . discard args
5047     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5048     # check output
5049     # . flush(_test-output-buffered-file)
5050     # . . push args
5051     68/push  _test-output-buffered-file/imm32
5052     # . . call
5053     e8/call  flush/disp32
5054     # . . discard args
5055     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5056 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5082     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
5083     # . . push args
5084     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
5085     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5086     68/push  _test-output-stream/imm32
5087     # . . call
5088     e8/call  check-stream-equal/disp32
5089     # . . discard args
5090     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5091     # . epilog
5092     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5093     5d/pop-to-EBP
5094     c3/return
5095 
5096 test-convert-instruction-emits-sib-byte-with-missing-index:
5097     # pack base and scale operands into SIB byte
5098     # . prolog
5099     55/push-EBP
5100     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5101     # setup
5102     # . clear-stream(_test-input-stream)
5103     # . . push args
5104     68/push  _test-input-stream/imm32
5105     # . . call
5106     e8/call  clear-stream/disp32
5107     # . . discard args
5108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5109     # . clear-stream(_test-output-stream)
5110     # . . push args
5111     68/push  _test-output-stream/imm32
5112     # . . call
5113     e8/call  clear-stream/disp32
5114     # . . discard args
5115     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5116     # . clear-stream(_test-output-buffered-file+4)
5117     # . . push args
5118     b8/copy-to-EAX  _test-output-buffered-file/imm32
5119     05/add-to-EAX  4/imm32
5120     50/push-EAX
5121     # . . call
5122     e8/call  clear-stream/disp32
5123     # . . discard args
5124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5125     # initialize input
5126     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
5127     # . . push args
5128     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5129     68/push  _test-input-stream/imm32
5130     # . . call
5131     e8/call  write/disp32
5132     # . . discard args
5133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5134     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5135     # . . push args
5136     68/push  _test-output-buffered-file/imm32
5137     68/push  _test-input-stream/imm32
5138     # . . call
5139     e8/call  convert-instruction/disp32
5140     # . . discard args
5141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5142     # check output
5143     # . flush(_test-output-buffered-file)
5144     # . . push args
5145     68/push  _test-output-buffered-file/imm32
5146     # . . call
5147     e8/call  flush/disp32
5148     # . . discard args
5149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5150 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5176     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
5177     # . . push args
5178     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
5179     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5180     68/push  _test-output-stream/imm32
5181     # . . call
5182     e8/call  check-stream-equal/disp32
5183     # . . discard args
5184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5185     # . epilog
5186     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5187     5d/pop-to-EBP
5188     c3/return
5189 
5190 test-convert-instruction-emits-sib-byte-with-missing-scale:
5191     # pack base and index operands into SIB byte
5192     # . prolog
5193     55/push-EBP
5194     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5195     # setup
5196     # . clear-stream(_test-input-stream)
5197     # . . push args
5198     68/push  _test-input-stream/imm32
5199     # . . call
5200     e8/call  clear-stream/disp32
5201     # . . discard args
5202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5203     # . clear-stream(_test-output-stream)
5204     # . . push args
5205     68/push  _test-output-stream/imm32
5206     # . . call
5207     e8/call  clear-stream/disp32
5208     # . . discard args
5209     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5210     # . clear-stream(_test-output-buffered-file+4)
5211     # . . push args
5212     b8/copy-to-EAX  _test-output-buffered-file/imm32
5213     05/add-to-EAX  4/imm32
5214     50/push-EAX
5215     # . . call
5216     e8/call  clear-stream/disp32
5217     # . . discard args
5218     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5219     # initialize input
5220     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
5221     # . . push args
5222     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5223     68/push  _test-input-stream/imm32
5224     # . . call
5225     e8/call  write/disp32
5226     # . . discard args
5227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5228     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5229     # . . push args
5230     68/push  _test-output-buffered-file/imm32
5231     68/push  _test-input-stream/imm32
5232     # . . call
5233     e8/call  convert-instruction/disp32
5234     # . . discard args
5235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5236     # check output
5237     # . flush(_test-output-buffered-file)
5238     # . . push args
5239     68/push  _test-output-buffered-file/imm32
5240     # . . call
5241     e8/call  flush/disp32
5242     # . . discard args
5243     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5244 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5270     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
5271     # . . push args
5272     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
5273     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5274     68/push  _test-output-stream/imm32
5275     # . . call
5276     e8/call  check-stream-equal/disp32
5277     # . . discard args
5278     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5279     # . epilog
5280     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5281     5d/pop-to-EBP
5282     c3/return
5283 
5284 test-convert-instruction-handles-disp32-operand:
5285     # expand /disp32 operand into 4 bytes
5286     # . prolog
5287     55/push-EBP
5288     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5289     # setup
5290     # . clear-stream(_test-input-stream)
5291     # . . push args
5292     68/push  _test-input-stream/imm32
5293     # . . call
5294     e8/call  clear-stream/disp32
5295     # . . discard args
5296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5297     # . clear-stream(_test-output-stream)
5298     # . . push args
5299     68/push  _test-output-stream/imm32
5300     # . . call
5301     e8/call  clear-stream/disp32
5302     # . . discard args
5303     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5304     # . clear-stream(_test-output-buffered-file+4)
5305     # . . push args
5306     b8/copy-to-EAX  _test-output-buffered-file/imm32
5307     05/add-to-EAX  4/imm32
5308     50/push-EAX
5309     # . . call
5310     e8/call  clear-stream/disp32
5311     # . . discard args
5312     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5313     # initialize input
5314     # . write(_test-input-stream, "e8/call 20/disp32")
5315     # . . push args
5316     68/push  "e8/call 20/disp32"/imm32
5317     68/push  _test-input-stream/imm32
5318     # . . call
5319     e8/call  write/disp32
5320     # . . discard args
5321     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5322     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5323     # . . push args
5324     68/push  _test-output-buffered-file/imm32
5325     68/push  _test-input-stream/imm32
5326     # . . call
5327     e8/call  convert-instruction/disp32
5328     # . . discard args
5329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5330     # check output
5331     # . flush(_test-output-buffered-file)
5332     # . . push args
5333     68/push  _test-output-buffered-file/imm32
5334     # . . call
5335     e8/call  flush/disp32
5336     # . . discard args
5337     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5338 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5364     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
5365     # . . push args
5366     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
5367     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
5368     68/push  _test-output-stream/imm32
5369     # . . call
5370     e8/call  check-stream-equal/disp32
5371     # . . discard args
5372     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5373     # . epilog
5374     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5375     5d/pop-to-EBP
5376     c3/return
5377 
5378 test-convert-instruction-handles-disp16-operand:
5379     # expand /disp16 operand into 2 bytes
5380     # . prolog
5381     55/push-EBP
5382     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5383     # setup
5384     # . clear-stream(_test-input-stream)
5385     # . . push args
5386     68/push  _test-input-stream/imm32
5387     # . . call
5388     e8/call  clear-stream/disp32
5389     # . . discard args
5390     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5391     # . clear-stream(_test-output-stream)
5392     # . . push args
5393     68/push  _test-output-stream/imm32
5394     # . . call
5395     e8/call  clear-stream/disp32
5396     # . . discard args
5397     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5398     # . clear-stream(_test-output-buffered-file+4)
5399     # . . push args
5400     b8/copy-to-EAX  _test-output-buffered-file/imm32
5401     05/add-to-EAX  4/imm32
5402     50/push-EAX
5403     # . . call
5404     e8/call  clear-stream/disp32
5405     # . . discard args
5406     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5407     # initialize input
5408     # . write(_test-input-stream, "e8/call 20/disp16")
5409     # . . push args
5410     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
5411     68/push  _test-input-stream/imm32
5412     # . . call
5413     e8/call  write/disp32
5414     # . . discard args
5415     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5416     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5417     # . . push args
5418     68/push  _test-output-buffered-file/imm32
5419     68/push  _test-input-stream/imm32
5420     # . . call
5421     e8/call  convert-instruction/disp32
5422     # . . discard args
5423     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5424     # check output
5425     # . flush(_test-output-buffered-file)
5426     # . . push args
5427     68/push  _test-output-buffered-file/imm32
5428     # . . call
5429     e8/call  flush/disp32
5430     # . . discard args
5431     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5432 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5458     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
5459     # . . push args
5460     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
5461     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
5462     68/push  _test-output-stream/imm32
5463     # . . call
5464     e8/call  check-stream-equal/disp32
5465     # . . discard args
5466     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5467     # . epilog
5468     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5469     5d/pop-to-EBP
5470     c3/return
5471 
5472 test-convert-instruction-handles-disp8-operand:
5473     # expand /disp8 operand into 1 byte
5474     # . prolog
5475     55/push-EBP
5476     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5477     # setup
5478     # . clear-stream(_test-input-stream)
5479     # . . push args
5480     68/push  _test-input-stream/imm32
5481     # . . call
5482     e8/call  clear-stream/disp32
5483     # . . discard args
5484     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5485     # . clear-stream(_test-output-stream)
5486     # . . push args
5487     68/push  _test-output-stream/imm32
5488     # . . call
5489     e8/call  clear-stream/disp32
5490     # . . discard args
5491     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5492     # . clear-stream(_test-output-buffered-file+4)
5493     # . . push args
5494     b8/copy-to-EAX  _test-output-buffered-file/imm32
5495     05/add-to-EAX  4/imm32
5496     50/push-EAX
5497     # . . call
5498     e8/call  clear-stream/disp32
5499     # . . discard args
5500     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5501     # initialize input
5502     # . write(_test-input-stream, "eb/jump 20/disp8")
5503     # . . push args
5504     68/push  "eb/jump 20/disp8"/imm32
5505     68/push  _test-input-stream/imm32
5506     # . . call
5507     e8/call  write/disp32
5508     # . . discard args
5509     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5510     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5511     # . . push args
5512     68/push  _test-output-buffered-file/imm32
5513     68/push  _test-input-stream/imm32
5514     # . . call
5515     e8/call  convert-instruction/disp32
5516     # . . discard args
5517     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5518     # check output
5519     # . flush(_test-output-buffered-file)
5520     # . . push args
5521     68/push  _test-output-buffered-file/imm32
5522     # . . call
5523     e8/call  flush/disp32
5524     # . . discard args
5525     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5526 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5552     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
5553     # . . push args
5554     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
5555     68/push  "eb 20  # eb/jump 20/disp8"/imm32
5556     68/push  _test-output-stream/imm32
5557     # . . call
5558     e8/call  check-stream-equal/disp32
5559     # . . discard args
5560     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5561     # . epilog
5562     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5563     5d/pop-to-EBP
5564     c3/return
5565 
5566 test-convert-instruction-handles-disp8-name:
5567     # pass /disp8 name directly through
5568     # . prolog
5569     55/push-EBP
5570     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5571     # setup
5572     # . clear-stream(_test-input-stream)
5573     # . . push args
5574     68/push  _test-input-stream/imm32
5575     # . . call
5576     e8/call  clear-stream/disp32
5577     # . . discard args
5578     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5579     # . clear-stream(_test-output-stream)
5580     # . . push args
5581     68/push  _test-output-stream/imm32
5582     # . . call
5583     e8/call  clear-stream/disp32
5584     # . . discard args
5585     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5586     # . clear-stream(_test-output-buffered-file+4)
5587     # . . push args
5588     b8/copy-to-EAX  _test-output-buffered-file/imm32
5589     05/add-to-EAX  4/imm32
5590     50/push-EAX
5591     # . . call
5592     e8/call  clear-stream/disp32
5593     # . . discard args
5594     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5595     # initialize input
5596     # . write(_test-input-stream, "eb/jump xyz/disp8")
5597     # . . push args
5598     68/push  "eb/jump xyz/disp8"/imm32
5599     68/push  _test-input-stream/imm32
5600     # . . call
5601     e8/call  write/disp32
5602     # . . discard args
5603     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5604     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5605     # . . push args
5606     68/push  _test-output-buffered-file/imm32
5607     68/push  _test-input-stream/imm32
5608     # . . call
5609     e8/call  convert-instruction/disp32
5610     # . . discard args
5611     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5612     # check output
5613     # . flush(_test-output-buffered-file)
5614     # . . push args
5615     68/push  _test-output-buffered-file/imm32
5616     # . . call
5617     e8/call  flush/disp32
5618     # . . discard args
5619     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5620 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5646     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
5647     # . . push args
5648     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
5649     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
5650     68/push  _test-output-stream/imm32
5651     # . . call
5652     e8/call  check-stream-equal/disp32
5653     # . . discard args
5654     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5655     # . epilog
5656     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5657     5d/pop-to-EBP
5658     c3/return
5659 
5660 test-convert-instruction-handles-imm32-operand:
5661     # expand /imm32 operand into 4 bytes
5662     # . prolog
5663     55/push-EBP
5664     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5665     # setup
5666     # . clear-stream(_test-input-stream)
5667     # . . push args
5668     68/push  _test-input-stream/imm32
5669     # . . call
5670     e8/call  clear-stream/disp32
5671     # . . discard args
5672     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5673     # . clear-stream(_test-output-stream)
5674     # . . push args
5675     68/push  _test-output-stream/imm32
5676     # . . call
5677     e8/call  clear-stream/disp32
5678     # . . discard args
5679     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5680     # . clear-stream(_test-output-buffered-file+4)
5681     # . . push args
5682     b8/copy-to-EAX  _test-output-buffered-file/imm32
5683     05/add-to-EAX  4/imm32
5684     50/push-EAX
5685     # . . call
5686     e8/call  clear-stream/disp32
5687     # . . discard args
5688     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5689     # initialize input
5690     # . write(_test-input-stream, "68/push 0x20/imm32")
5691     # . . push args
5692     68/push  "68/push 0x20/imm32"/imm32
5693     68/push  _test-input-stream/imm32
5694     # . . call
5695     e8/call  write/disp32
5696     # . . discard args
5697     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5698     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5699     # . . push args
5700     68/push  _test-output-buffered-file/imm32
5701     68/push  _test-input-stream/imm32
5702     # . . call
5703     e8/call  convert-instruction/disp32
5704     # . . discard args
5705     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5706     # check output
5707     # . flush(_test-output-buffered-file)
5708     # . . push args
5709     68/push  _test-output-buffered-file/imm32
5710     # . . call
5711     e8/call  flush/disp32
5712     # . . discard args
5713     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5714 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5740     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
5741     # . . push args
5742     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
5743     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
5744     68/push  _test-output-stream/imm32
5745     # . . call
5746     e8/call  check-stream-equal/disp32
5747     # . . discard args
5748     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5749     # . epilog
5750     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5751     5d/pop-to-EBP
5752     c3/return
5753 
5754 test-convert-instruction-handles-imm16-operand:
5755     # expand /imm16 operand into 2 bytes
5756     # we don't have one of these at the moment, so this expands to an invalid instruction
5757     # . prolog
5758     55/push-EBP
5759     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5760     # setup
5761     # . clear-stream(_test-input-stream)
5762     # . . push args
5763     68/push  _test-input-stream/imm32
5764     # . . call
5765     e8/call  clear-stream/disp32
5766     # . . discard args
5767     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5768     # . clear-stream(_test-output-stream)
5769     # . . push args
5770     68/push  _test-output-stream/imm32
5771     # . . call
5772     e8/call  clear-stream/disp32
5773     # . . discard args
5774     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5775     # . clear-stream(_test-output-buffered-file+4)
5776     # . . push args
5777     b8/copy-to-EAX  _test-output-buffered-file/imm32
5778     05/add-to-EAX  4/imm32
5779     50/push-EAX
5780     # . . call
5781     e8/call  clear-stream/disp32
5782     # . . discard args
5783     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5784     # initialize input
5785     # . write(_test-input-stream, "68/push 0x20/imm16")
5786     # . . push args
5787     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
5788     68/push  _test-input-stream/imm32
5789     # . . call
5790     e8/call  write/disp32
5791     # . . discard args
5792     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5793     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5794     # . . push args
5795     68/push  _test-output-buffered-file/imm32
5796     68/push  _test-input-stream/imm32
5797     # . . call
5798     e8/call  convert-instruction/disp32
5799     # . . discard args
5800     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5801     # check output
5802     # . flush(_test-output-buffered-file)
5803     # . . push args
5804     68/push  _test-output-buffered-file/imm32
5805     # . . call
5806     e8/call  flush/disp32
5807     # . . discard args
5808     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5809 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5835     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
5836     # . . push args
5837     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
5838     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
5839     68/push  _test-output-stream/imm32
5840     # . . call
5841     e8/call  check-stream-equal/disp32
5842     # . . discard args
5843     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5844     # . epilog
5845     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5846     5d/pop-to-EBP
5847     c3/return
5848 
5849 test-convert-instruction-handles-imm8-operand:
5850     # expand /imm8 operand into 1 byte
5851     # we don't have one of these at the moment, so this expands to an invalid instruction
5852     # . prolog
5853     55/push-EBP
5854     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5855     # setup
5856     # . clear-stream(_test-input-stream)
5857     # . . push args
5858     68/push  _test-input-stream/imm32
5859     # . . call
5860     e8/call  clear-stream/disp32
5861     # . . discard args
5862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5863     # . clear-stream(_test-output-stream)
5864     # . . push args
5865     68/push  _test-output-stream/imm32
5866     # . . call
5867     e8/call  clear-stream/disp32
5868     # . . discard args
5869     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5870     # . clear-stream(_test-output-buffered-file+4)
5871     # . . push args
5872     b8/copy-to-EAX  _test-output-buffered-file/imm32
5873     05/add-to-EAX  4/imm32
5874     50/push-EAX
5875     # . . call
5876     e8/call  clear-stream/disp32
5877     # . . discard args
5878     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5879     # initialize input
5880     # . write(_test-input-stream, "68/push 0x20/imm8")
5881     # . . push args
5882     68/push  "68/push 0x20/imm8"/imm32
5883     68/push  _test-input-stream/imm32
5884     # . . call
5885     e8/call  write/disp32
5886     # . . discard args
5887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5888     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5889     # . . push args
5890     68/push  _test-output-buffered-file/imm32
5891     68/push  _test-input-stream/imm32
5892     # . . call
5893     e8/call  convert-instruction/disp32
5894     # . . discard args
5895     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5896     # check output
5897     # . flush(_test-output-buffered-file)
5898     # . . push args
5899     68/push  _test-output-buffered-file/imm32
5900     # . . call
5901     e8/call  flush/disp32
5902     # . . discard args
5903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5904 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
5930     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
5931     # . . push args
5932     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
5933     68/push  "68 20  # 68/push 0x20/imm8"/imm32
5934     68/push  _test-output-stream/imm32
5935     # . . call
5936     e8/call  check-stream-equal/disp32
5937     # . . discard args
5938     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
5939     # . epilog
5940     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5941     5d/pop-to-EBP
5942     c3/return
5943 
5944 # shortcut for parse-hex-int(next-token-from-slice(word->start, word->end, '/'))
5945 parse-datum-of-word:  # word : (address slice) -> value/EAX
5946     # . prolog
5947     55/push-EBP
5948     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
5949     # . save registers
5950     51/push-ECX
5951     56/push-ESI
5952     # ESI = word
5953     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
5954     # var slice/ECX = {0, 0}
5955     68/push  0/imm32/end
5956     68/push  0/imm32/start
5957     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
5958     # slice = next-token-from-slice(word->start, word->end, '/')
5959     # . . push args
5960     51/push-ECX
5961     68/push  0x2f/imm32/slash
5962     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
5963     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
5964     # . . call
5965     e8/call  next-token-from-slice/disp32
5966     # . . discard args
5967     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
5968     # value/EAX = parse-hex-int(slice)
5969     # . . push args
5970     51/push-ECX
5971     # . . call
5972     e8/call  parse-hex-int/disp32
5973     # . . discard args
5974     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
5975 $parse-datum-of-word:end:
5976     # . reclaim locals
5977     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
5978     # . restore registers
5979     5e/pop-to-ESI
5980     59/pop-to-ECX
5981     # . epilog
5982     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
5983     5d/pop-to-EBP
5984     c3/return
5985 
5986 # . . vim:nowrap:textwidth=0