https://github.com/akkartik/mu/blob/master/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:
   6 #   $ ./bootstrap translate init.linux 0*.subx apps/subx-params.subx apps/pack.subx  -o apps/pack
   7 #   $ echo '05/add-to-eax 0x20/imm32'  |./bootstrap 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     # . prologue
  23     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  24 
  25     # initialize heap
  26     # . Heap = new-segment(Heap-size)
  27     # . . push args
  28     68/push  Heap/imm32
  29     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  30     # . . call
  31     e8/call  new-segment/disp32
  32     # . . discard args
  33     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  34 
  35     # - if argc > 1 and argv[1] == "test", then return run_tests()
  36     # if (argc <= 1) goto interactive
  37     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  38     7e/jump-if-<=  $subx-pack-main:interactive/disp8
  39     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  40     # . eax = kernel-string-equal?(argv[1], "test")
  41     # . . push args
  42     68/push  "test"/imm32
  43     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  44     # . . call
  45     e8/call  kernel-string-equal?/disp32
  46     # . . discard args
  47     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  48     # . if (eax == false) goto interactive
  49     3d/compare-eax-and  0/imm32/false
  50     74/jump-if-=  $subx-pack-main:interactive/disp8
  51     # run-tests()
  52     e8/call  run-tests/disp32
  53     # syscall(exit, *Num-test-failures)
  54     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  55     eb/jump  $subx-pack-main:end/disp8
  56 $subx-pack-main:interactive:
  57     # - otherwise convert stdin
  58     # subx-pack(Stdin, Stdout)
  59     # . . push args
  60     68/push  Stdout/imm32
  61     68/push  Stdin/imm32
  62     # . . call
  63     e8/call  subx-pack/disp32
  64     # . . discard args
  65     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x8/imm32         # add to esp
  66     # syscall(exit, 0)
  67     bb/copy-to-ebx  0/imm32
  68 $subx-pack-main:end:
  69     e8/call  syscall_exit/disp32
  70 
  71 # - big picture
  72 # We'll operate on each line/instruction in isolation. That way we only need to
  73 # allocate memory for converting a single instruction.
  74 #
  75 # To pack an entire file, convert every segment in it
  76 # To convert a code segment, convert every instruction (line) until the next segment header
  77 # To convert a non-data segment, convert every word until the next segment header
  78 #
  79 # primary state: line
  80 #   stream of 512 bytes; abort if it ever overflows
  81 
  82 # conceptual hierarchy within a line:
  83 #   line = words separated by ' ', maybe followed by comment starting with '#'
  84 #   word = datum until '/', then 0 or more metadata separated by '/'
  85 #
  86 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
  87 #   next-token(stream, delim char) -> slice (start, end pointers)
  88 #   next-token-from-slice(start, end, delim char) -> slice
  89 #   slice-equal?(slice, string)
  90 
  91 subx-pack:  # in: (addr buffered-file), out: (addr buffered-file)
  92     # pseudocode:
  93     #   var line: (stream byte 512)
  94     #   var in-code? = false
  95     #   while true
  96     #     clear-stream(line)
  97     #     read-line-buffered(in, line)
  98     #     if (line->write == 0) break             # end of file
  99     #     var word-slice = next-word(line)
 100     #     if slice-empty?(word-slice)             # whitespace
 101     #       write-stream-data(out, line)
 102     #     else if (slice-equal?(word-slice, "=="))
 103     #       word-slice = next-word(line)
 104     #       in-code? = slice-equal?(word-slice, "code")
 105     #       write-stream-data(out, line)
 106     #     else if (in-code?)
 107     #       rewind-stream(line)
 108     #       convert-instruction(line, out)
 109     #     else
 110     #       rewind-stream(line)
 111     #       convert-data(line, out)
 112     #   flush(out)
 113     #
 114     # . prologue
 115     55/push-ebp
 116     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 117     # . save registers
 118     50/push-eax
 119     51/push-ecx
 120     52/push-edx
 121     53/push-ebx
 122     # var line/ecx: (stream byte 512)
 123     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 124     68/push  0x200/imm32/length
 125     68/push  0/imm32/read
 126     68/push  0/imm32/write
 127     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 128     # var word-slice/edx: slice
 129     68/push  0/imm32/end
 130     68/push  0/imm32/start
 131     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 132     # var in-code?/ebx: boolean = false
 133     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 134 $subx-pack:loop:
 135     # clear-stream(line)
 136     # . . push args
 137     51/push-ecx
 138     # . . call
 139     e8/call  clear-stream/disp32
 140     # . . discard args
 141     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 142     # read-line-buffered(in, line)
 143     # . . push args
 144     51/push-ecx
 145     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 146     # . . call
 147     e8/call  read-line-buffered/disp32
 148     # . . discard args
 149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 150 $subx-pack:check0:
 151     # if (line->write == 0) break
 152     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
 153     0f 84/jump-if-=  $subx-pack:break/disp32
 154 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 180     # next-word(line, word-slice)
 181     # . . push args
 182     52/push-edx
 183     51/push-ecx
 184     # . . call
 185     e8/call  next-word/disp32
 186     # . . discard args
 187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 188 $subx-pack:check1:
 189     # if (slice-empty?(word-slice)) write-stream-data(out, line)
 190     # . eax = slice-empty?(word-slice)
 191     # . . push args
 192     52/push-edx
 193     # . . call
 194     e8/call  slice-empty?/disp32
 195     # . . discard args
 196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 197     # . if (eax != false) write-stream-data(out, line)
 198     3d/compare-eax-and  0/imm32/false
 199     0f 85/jump-if-!=  $subx-pack:pass-through/disp32
 200 $subx-pack:check2:
 201 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 241     # if (!slice-equal?(word-slice, "==")) goto next check
 242     # . eax = slice-equal?(word-slice, "==")
 243     # . . push args
 244     68/push  "=="/imm32
 245     52/push-edx
 246     # . . call
 247     e8/call  slice-equal?/disp32
 248     # . . discard args
 249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 250     # . if (eax == false) goto check3
 251     3d/compare-eax-and  0/imm32/false
 252     0f 84/jump-if-=  $subx-pack:check3/disp32
 253     # word-slice = next-word(line)
 254     # . . push args
 255     52/push-edx
 256     51/push-ecx
 257     # . . call
 258     e8/call  next-word/disp32
 259     # . . discard args
 260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 261 +-- 40 lines: #?     # dump segment name ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 301     # in-code? = slice-equal?(word-slice, "code")
 302     # . . push args
 303     68/push  "code"/imm32
 304     52/push-edx
 305     # . . call
 306     e8/call  slice-equal?/disp32
 307     # . . discard args
 308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 309     # . . in-code? = eax
 310     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
 311     # write-stream-data(out, line)
 312     eb/jump  $subx-pack:pass-through/disp8
 313 $subx-pack:check3:
 314     # else rewind-stream(line)
 315     # . rewind-stream(line)
 316     # . . push args
 317     51/push-ecx
 318     # . . call
 319     e8/call  rewind-stream/disp32
 320     # . . discard args
 321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 322     # if (in-code? != false) convert-instruction(line, out)
 323     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32/false     # compare ebx
 324     74/jump-if-=  $subx-pack:data/disp8
 325 $subx-pack:code:
 326     # . convert-instruction(line, out)
 327     # . . push args
 328     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 329     51/push-ecx
 330     # . . call
 331     e8/call  convert-instruction/disp32
 332     # . . discard args
 333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 334     # . loop
 335     e9/jump  $subx-pack:loop/disp32
 336 $subx-pack:data:
 337     # else convert-data(line, out)
 338     # . . push args
 339     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 340     51/push-ecx
 341     # . . call
 342     e8/call  convert-data/disp32
 343     # . . discard args
 344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 345     # . loop
 346     e9/jump  $subx-pack:loop/disp32
 347 $subx-pack:pass-through:
 348     # write-stream-data(out, line)
 349     # . . push args
 350     51/push-ecx
 351     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 352     # . . call
 353     e8/call  write-stream-data/disp32
 354     # . . discard args
 355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 356     # . loop
 357     e9/jump  $subx-pack:loop/disp32
 358 $subx-pack:break:
 359     # flush(out)
 360     # . . push args
 361     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 362     # . . call
 363     e8/call  flush/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 366 $subx-pack:end:
 367     # . reclaim locals
 368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 369     # . restore registers
 370     5b/pop-to-ebx
 371     5a/pop-to-edx
 372     59/pop-to-ecx
 373     58/pop-to-eax
 374     # . epilogue
 375     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 376     5d/pop-to-ebp
 377     c3/return
 378 
 379 test-subx-pack-passes-empty-lines-through:
 380     # if a line is empty, pass it along unchanged
 381     # . prologue
 382     55/push-ebp
 383     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 384     # setup
 385     # . clear-stream(_test-input-stream)
 386     # . . push args
 387     68/push  _test-input-stream/imm32
 388     # . . call
 389     e8/call  clear-stream/disp32
 390     # . . discard args
 391     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 392     # . clear-stream($_test-input-buffered-file->buffer)
 393     # . . push args
 394     68/push  $_test-input-buffered-file->buffer/imm32
 395     # . . call
 396     e8/call  clear-stream/disp32
 397     # . . discard args
 398     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 399     # . clear-stream(_test-output-stream)
 400     # . . push args
 401     68/push  _test-output-stream/imm32
 402     # . . call
 403     e8/call  clear-stream/disp32
 404     # . . discard args
 405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 406     # . clear-stream($_test-output-buffered-file->buffer)
 407     # . . push args
 408     68/push  $_test-output-buffered-file->buffer/imm32
 409     # . . call
 410     e8/call  clear-stream/disp32
 411     # . . discard args
 412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 413     # write nothing to input
 414     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 415     # . . push args
 416     68/push  _test-output-buffered-file/imm32
 417     68/push  _test-input-buffered-file/imm32
 418     # . . call
 419     e8/call  subx-pack/disp32
 420     # . . discard args
 421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 422     # check that the line just passed through
 423     # . flush(_test-output-buffered-file)
 424     # . . push args
 425     68/push  _test-output-buffered-file/imm32
 426     # . . call
 427     e8/call  flush/disp32
 428     # . . discard args
 429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 430     # . check-stream-equal(_test-output-stream, "", msg)
 431     # . . push args
 432     68/push  "F - test-subx-pack-passes-empty-lines-through"/imm32
 433     68/push  ""/imm32
 434     68/push  _test-output-stream/imm32
 435     # . . call
 436     e8/call  check-stream-equal/disp32
 437     # . . discard args
 438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 439     # . epilogue
 440     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 441     5d/pop-to-ebp
 442     c3/return
 443 
 444 test-subx-pack-passes-lines-with-just-whitespace-through:
 445     # if a line is empty, pass it along unchanged
 446     # . prologue
 447     55/push-ebp
 448     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 449     # setup
 450     # . clear-stream(_test-input-stream)
 451     # . . push args
 452     68/push  _test-input-stream/imm32
 453     # . . call
 454     e8/call  clear-stream/disp32
 455     # . . discard args
 456     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 457     # . clear-stream($_test-input-buffered-file->buffer)
 458     # . . push args
 459     68/push  $_test-input-buffered-file->buffer/imm32
 460     # . . call
 461     e8/call  clear-stream/disp32
 462     # . . discard args
 463     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 464     # . clear-stream(_test-output-stream)
 465     # . . push args
 466     68/push  _test-output-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-output-buffered-file->buffer)
 472     # . . push args
 473     68/push  $_test-output-buffered-file->buffer/imm32
 474     # . . call
 475     e8/call  clear-stream/disp32
 476     # . . discard args
 477     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 478     # initialize input
 479     # . write(_test-input-stream, "    ")
 480     # . . push args
 481     68/push  "    "/imm32
 482     68/push  _test-input-stream/imm32
 483     # . . call
 484     e8/call  write/disp32
 485     # . . discard args
 486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 487     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 488     # . . push args
 489     68/push  _test-output-buffered-file/imm32
 490     68/push  _test-input-buffered-file/imm32
 491     # . . call
 492     e8/call  subx-pack/disp32
 493     # . . discard args
 494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 495     # check that the line just passed through
 496     # . flush(_test-output-buffered-file)
 497     # . . push args
 498     68/push  _test-output-buffered-file/imm32
 499     # . . call
 500     e8/call  flush/disp32
 501     # . . discard args
 502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 503     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
 504     # . . push args
 505     68/push  "F - test-subx-pack-passes-with-just-whitespace-through"/imm32
 506     68/push  "    "/imm32
 507     68/push  _test-output-stream/imm32
 508     # . . call
 509     e8/call  check-next-stream-line-equal/disp32
 510     # . . discard args
 511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 512     # . epilogue
 513     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 514     5d/pop-to-ebp
 515     c3/return
 516 
 517 test-subx-pack-passes-segment-headers-through:
 518     # if a line starts with '==', pass it along unchanged
 519     # . prologue
 520     55/push-ebp
 521     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 522     # setup
 523     # . clear-stream(_test-input-stream)
 524     # . . push args
 525     68/push  _test-input-stream/imm32
 526     # . . call
 527     e8/call  clear-stream/disp32
 528     # . . discard args
 529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 530     # . clear-stream($_test-input-buffered-file->buffer)
 531     # . . push args
 532     68/push  $_test-input-buffered-file->buffer/imm32
 533     # . . call
 534     e8/call  clear-stream/disp32
 535     # . . discard args
 536     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 537     # . clear-stream(_test-output-stream)
 538     # . . push args
 539     68/push  _test-output-stream/imm32
 540     # . . call
 541     e8/call  clear-stream/disp32
 542     # . . discard args
 543     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 544     # . clear-stream($_test-output-buffered-file->buffer)
 545     # . . push args
 546     68/push  $_test-output-buffered-file->buffer/imm32
 547     # . . call
 548     e8/call  clear-stream/disp32
 549     # . . discard args
 550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 551     # initialize input
 552     # . write(_test-input-stream, "== abcd 0x1")
 553     # . . push args
 554     68/push  "== abcd 0x1"/imm32
 555     68/push  _test-input-stream/imm32
 556     # . . call
 557     e8/call  write/disp32
 558     # . . discard args
 559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 560     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 561     # . . push args
 562     68/push  _test-output-buffered-file/imm32
 563     68/push  _test-input-buffered-file/imm32
 564     # . . call
 565     e8/call  subx-pack/disp32
 566     # . . discard args
 567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 568     # check that the line just passed through
 569     # . flush(_test-output-buffered-file)
 570     # . . push args
 571     68/push  _test-output-buffered-file/imm32
 572     # . . call
 573     e8/call  flush/disp32
 574     # . . discard args
 575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 576     # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg)
 577     # . . push args
 578     68/push  "F - test-subx-pack-passes-segment-headers-through"/imm32
 579     68/push  "== abcd 0x1"/imm32
 580     68/push  _test-output-stream/imm32
 581     # . . call
 582     e8/call  check-stream-equal/disp32
 583     # . . discard args
 584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 585     # . epilogue
 586     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 587     5d/pop-to-ebp
 588     c3/return
 589 
 590 test-subx-pack-in-data-segment:
 591     # correctly process lines in the data segment
 592     # . prologue
 593     55/push-ebp
 594     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 595     # setup
 596     # . clear-stream(_test-input-stream)
 597     # . . push args
 598     68/push  _test-input-stream/imm32
 599     # . . call
 600     e8/call  clear-stream/disp32
 601     # . . discard args
 602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 603     # . clear-stream($_test-input-buffered-file->buffer)
 604     # . . push args
 605     68/push  $_test-input-buffered-file->buffer/imm32
 606     # . . call
 607     e8/call  clear-stream/disp32
 608     # . . discard args
 609     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 610     # . clear-stream(_test-output-stream)
 611     # . . push args
 612     68/push  _test-output-stream/imm32
 613     # . . call
 614     e8/call  clear-stream/disp32
 615     # . . discard args
 616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 617     # . clear-stream($_test-output-buffered-file->buffer)
 618     # . . push args
 619     68/push  $_test-output-buffered-file->buffer/imm32
 620     # . . call
 621     e8/call  clear-stream/disp32
 622     # . . discard args
 623     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 624     # initialize input
 625     #   == code 0x1
 626     #   == data 0x2
 627     #   3 4/imm32
 628     # . write(_test-input-stream, "== code 0x1")
 629     # . . push args
 630     68/push  "== code 0x1\n"/imm32
 631     68/push  _test-input-stream/imm32
 632     # . . call
 633     e8/call  write/disp32
 634     # . . discard args
 635     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 636     # . write(_test-input-stream, "== data 0x2\n")
 637     # . . push args
 638     68/push  "== data 0x2\n"/imm32
 639     68/push  _test-input-stream/imm32
 640     # . . call
 641     e8/call  write/disp32
 642     # . . discard args
 643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 644     # . write(_test-input-stream, "3 4/imm32\n")
 645     # . . push args
 646     68/push  "3 4/imm32\n"/imm32
 647     68/push  _test-input-stream/imm32
 648     # . . call
 649     e8/call  write/disp32
 650     # . . discard args
 651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 652     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 653     # . . push args
 654     68/push  _test-output-buffered-file/imm32
 655     68/push  _test-input-buffered-file/imm32
 656     # . . call
 657     e8/call  subx-pack/disp32
 658     # . . discard args
 659     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 660     # check output
 661 +-- 26 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 687     # . flush(_test-output-buffered-file)
 688     # . . push args
 689     68/push  _test-output-buffered-file/imm32
 690     # . . call
 691     e8/call  flush/disp32
 692     # . . discard args
 693     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 694     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
 695     # . . push args
 696     68/push  "F - test-subx-pack-in-data-segment/0"/imm32
 697     68/push  "== code 0x1"/imm32
 698     68/push  _test-output-stream/imm32
 699     # . . call
 700     e8/call  check-next-stream-line-equal/disp32
 701     # . . discard args
 702     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 703     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
 704     # . . push args
 705     68/push  "F - test-subx-pack-in-data-segment/1"/imm32
 706     68/push  "== data 0x2"/imm32
 707     68/push  _test-output-stream/imm32
 708     # . . call
 709     e8/call  check-next-stream-line-equal/disp32
 710     # . . discard args
 711     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 712     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 713     # . . push args
 714     68/push  "F - test-subx-pack-in-data-segment/2"/imm32
 715     68/push  "03 04 00 00 00 "/imm32
 716     68/push  _test-output-stream/imm32
 717     # . . call
 718     e8/call  check-next-stream-line-equal/disp32
 719     # . . discard args
 720     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 721     # . epilogue
 722     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 723     5d/pop-to-ebp
 724     c3/return
 725 
 726 test-subx-pack-code-and-data-segments:
 727     # correctly process lines in both code and data segments
 728     # . prologue
 729     55/push-ebp
 730     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 731     # setup
 732     # . clear-stream(_test-input-stream)
 733     # . . push args
 734     68/push  _test-input-stream/imm32
 735     # . . call
 736     e8/call  clear-stream/disp32
 737     # . . discard args
 738     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 739     # . clear-stream($_test-input-buffered-file->buffer)
 740     # . . push args
 741     68/push  $_test-input-buffered-file->buffer/imm32
 742     # . . call
 743     e8/call  clear-stream/disp32
 744     # . . discard args
 745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 746     # . clear-stream(_test-output-stream)
 747     # . . push args
 748     68/push  _test-output-stream/imm32
 749     # . . call
 750     e8/call  clear-stream/disp32
 751     # . . discard args
 752     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 753     # . clear-stream($_test-output-buffered-file->buffer)
 754     # . . push args
 755     68/push  $_test-output-buffered-file->buffer/imm32
 756     # . . call
 757     e8/call  clear-stream/disp32
 758     # . . discard args
 759     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 760     # initialize input
 761     #   == code 0x1
 762     #   e8/call 20/disp32
 763     #   68/push 0x20/imm8
 764     #   == data 0x2
 765     #   3 4/imm32
 766     # . write(_test-input-stream, "== code 0x1\n")
 767     # . . push args
 768     68/push  "== code 0x1\n"/imm32
 769     68/push  _test-input-stream/imm32
 770     # . . call
 771     e8/call  write/disp32
 772     # . . discard args
 773     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 774     # . write(_test-input-stream, "e8/call 20/disp32\n")
 775     # . . push args
 776     68/push  "e8/call 20/disp32\n"/imm32
 777     68/push  _test-input-stream/imm32
 778     # . . call
 779     e8/call  write/disp32
 780     # . . discard args
 781     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 782     # . write(_test-input-stream, "68/push 0x20/imm8\n")
 783     # . . push args
 784     68/push  "68/push 0x20/imm8\n"/imm32
 785     68/push  _test-input-stream/imm32
 786     # . . call
 787     e8/call  write/disp32
 788     # . . discard args
 789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 790     # . write(_test-input-stream, "== data 0x2\n")
 791     # . . push args
 792     68/push  "== data 0x2\n"/imm32
 793     68/push  _test-input-stream/imm32
 794     # . . call
 795     e8/call  write/disp32
 796     # . . discard args
 797     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 798     # . write(_test-input-stream, "3 4/imm32\n")
 799     # . . push args
 800     68/push  "3 4/imm32\n"/imm32
 801     68/push  _test-input-stream/imm32
 802     # . . call
 803     e8/call  write/disp32
 804     # . . discard args
 805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 806     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 807     # . . push args
 808     68/push  _test-output-buffered-file/imm32
 809     68/push  _test-input-buffered-file/imm32
 810     # . . call
 811     e8/call  subx-pack/disp32
 812     # . . discard args
 813     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 814     # check output
 815     #   == code 0x1
 816     #   e8 20 00 00 00  # e8/call 20/disp32
 817     #   68 20  # 68/push 0x20/imm8
 818     #   == data 0x2
 819     #   03 04 00 00 00
 820 +-- 26 lines: #?     # debug print ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 846     # . flush(_test-output-buffered-file)
 847     # . . push args
 848     68/push  _test-output-buffered-file/imm32
 849     # . . call
 850     e8/call  flush/disp32
 851     # . . discard args
 852     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 853     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
 854     # . . push args
 855     68/push  "F - test-subx-pack-code-and-data-segments/0"/imm32
 856     68/push  "== code 0x1"/imm32
 857     68/push  _test-output-stream/imm32
 858     # . . call
 859     e8/call  check-next-stream-line-equal/disp32
 860     # . . discard args
 861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 862     # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
 863     # . . push args
 864     68/push  "F - test-subx-pack-code-and-data-segments/1"/imm32
 865     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
 866     68/push  _test-output-stream/imm32
 867     # . . call
 868     e8/call  check-next-stream-line-equal/disp32
 869     # . . discard args
 870     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 871     # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
 872     # . . push args
 873     68/push  "F - test-subx-pack-code-and-data-segments/2"/imm32
 874     68/push  "68 20  # 68/push 0x20/imm8"/imm32
 875     68/push  _test-output-stream/imm32
 876     # . . call
 877     e8/call  check-next-stream-line-equal/disp32
 878     # . . discard args
 879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 880     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
 881     # . . push args
 882     68/push  "F - test-subx-pack-code-and-data-segments/3"/imm32
 883     68/push  "== data 0x2"/imm32
 884     68/push  _test-output-stream/imm32
 885     # . . call
 886     e8/call  check-next-stream-line-equal/disp32
 887     # . . discard args
 888     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 889     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 890     # . . push args
 891     68/push  "F - test-subx-pack-code-and-data-segments/4"/imm32
 892     68/push  "03 04 00 00 00 "/imm32
 893     68/push  _test-output-stream/imm32
 894     # . . call
 895     e8/call  check-next-stream-line-equal/disp32
 896     # . . discard args
 897     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 898     # . epilogue
 899     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 900     5d/pop-to-ebp
 901     c3/return
 902 
 903 convert-data:  # line: (addr stream byte), out: (addr buffered-file)
 904     # pseudocode:
 905     #   var word-slice: slice
 906     #   while true
 907     #     word-slice = next-word(line)
 908     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
 909     #       break  # skip emitting some whitespace
 910     #     if slice-starts-with?(word-slice, "#")      # comment
 911     #       write-slice-buffered(out, word-slice)
 912     #       return
 913     #     if slice-ends-with?(word-slice, ":")        # label
 914     #       write-stream-data(out, line)
 915     #       return
 916     #     if has-metadata?(word-slice, "imm32")
 917     #       emit(out, word-slice, 4)
 918     #     # disp32 is not permitted in data segments, and anything else is only a byte long
 919     #     else
 920     #       emit(out, word-slice, 1)
 921     #   write-buffered(out, "\n")
 922     #
 923     # . prologue
 924     55/push-ebp
 925     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 926     # . save registers
 927     50/push-eax
 928     51/push-ecx
 929     52/push-edx
 930     # var word-slice/ecx: slice
 931     68/push  0/imm32/end
 932     68/push  0/imm32/start
 933     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 934 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 960 $convert-data:loop:
 961     # next-word(line, word-slice)
 962     # . . push args
 963     51/push-ecx
 964     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 965     # . . call
 966     e8/call  next-word/disp32
 967     # . . discard args
 968     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 969 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1009 $convert-data:check0:
1010     # if (slice-empty?(word-slice)) break
1011     # . eax = slice-empty?(word-slice)
1012     # . . push args
1013     51/push-ecx
1014     # . . call
1015     e8/call  slice-empty?/disp32
1016     # . . discard args
1017     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1018     # . if (eax != false) break
1019     3d/compare-eax-and  0/imm32/false
1020     0f 85/jump-if-!=  $convert-data:break/disp32
1021 $convert-data:check-for-comment:
1022     # if (slice-starts-with?(word-slice, "#"))
1023     # . var start/edx: (addr byte) = word-slice->start
1024     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
1025     # . var c/eax: byte = *start
1026     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1027     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
1028     # . if (c != '#') goto next check
1029     3d/compare-eax-and  0x23/imm32/hash
1030     75/jump-if-!=  $convert-data:check-for-label/disp8
1031 $convert-data:comment:
1032     # write-slice-buffered(out, word-slice)
1033     # . . push args
1034     51/push-ecx
1035     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1036     # . . call
1037     e8/call  write-slice-buffered/disp32
1038     # . . discard args
1039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1040     # return
1041     0f 85/jump-if-!=  $convert-data:end/disp32
1042 $convert-data:check-for-label:
1043     # if (slice-ends-with?(word-slice, ":"))
1044     # . var end/edx: (addr byte) = word-slice->end
1045     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1046     # . var c/eax: byte = *(end-1)
1047     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1048     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
1049     # . if (c != ':') goto next check
1050     3d/compare-eax-and  0x3a/imm32/colon
1051     75/jump-if-!=  $convert-data:check-for-imm32/disp8
1052 $convert-data:label:
1053     # write-stream-data(out, line)
1054     # . . push args
1055     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1056     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1057     # . . call
1058     e8/call  write-stream-data/disp32
1059     # . . discard args
1060     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1061     # return
1062     75/jump-if-!=  $convert-data:end/disp8
1063 $convert-data:check-for-imm32:
1064     # if (has-metadata?(word-slice, "imm32"))
1065     # . eax = has-metadata?(ecx, "imm32")
1066     # . . push args
1067     68/push  "imm32"/imm32
1068     51/push-ecx
1069     # . . call
1070     e8/call  has-metadata?/disp32
1071     # . . discard args
1072     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1073     # . if (eax == false) process as a single byte
1074     3d/compare-eax-and  0/imm32/false
1075     74/jump-if-=  $convert-data:single-byte/disp8
1076 $convert-data:imm32:
1077     # emit(out, word-slice, 4)
1078     # . . push args
1079     68/push  4/imm32
1080     51/push-ecx
1081     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1082     # . . call
1083     e8/call  emit/disp32
1084     # . . discard args
1085     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1086     e9/jump  $convert-data:loop/disp32
1087 $convert-data:single-byte:
1088     # emit(out, word-slice, 1)
1089     # . . push args
1090     68/push  1/imm32
1091     51/push-ecx
1092     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1093     # . . call
1094     e8/call  emit/disp32
1095     # . . discard args
1096     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1097     e9/jump  $convert-data:loop/disp32
1098 $convert-data:break:
1099     # write-buffered(out, "\n")
1100     # . . push args
1101     68/push  Newline/imm32
1102     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1103     # . . call
1104     e8/call  write-buffered/disp32
1105     # . . discard args
1106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1107 $convert-data:end:
1108     # . reclaim locals
1109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1110     # . restore registers
1111     5a/pop-to-edx
1112     59/pop-to-ecx
1113     58/pop-to-eax
1114     # . epilogue
1115     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1116     5d/pop-to-ebp
1117     c3/return
1118 
1119 test-convert-data-passes-comments-through:
1120     # if a line starts with '#', pass it along unchanged
1121     # . prologue
1122     55/push-ebp
1123     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1124     # setup
1125     # . clear-stream(_test-input-stream)
1126     # . . push args
1127     68/push  _test-input-stream/imm32
1128     # . . call
1129     e8/call  clear-stream/disp32
1130     # . . discard args
1131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1132     # . clear-stream(_test-output-stream)
1133     # . . push args
1134     68/push  _test-output-stream/imm32
1135     # . . call
1136     e8/call  clear-stream/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1139     # . clear-stream($_test-output-buffered-file->buffer)
1140     # . . push args
1141     68/push  $_test-output-buffered-file->buffer/imm32
1142     # . . call
1143     e8/call  clear-stream/disp32
1144     # . . discard args
1145     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1146     # initialize input
1147     # . write(_test-input-stream, "# abcd")
1148     # . . push args
1149     68/push  "# abcd"/imm32
1150     68/push  _test-input-stream/imm32
1151     # . . call
1152     e8/call  write/disp32
1153     # . . discard args
1154     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1155     # convert-data(_test-input-stream, _test-output-buffered-file)
1156     # . . push args
1157     68/push  _test-output-buffered-file/imm32
1158     68/push  _test-input-stream/imm32
1159     # . . call
1160     e8/call  convert-data/disp32
1161     # . . discard args
1162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1163     # check that the line just passed through
1164     # . flush(_test-output-buffered-file)
1165     # . . push args
1166     68/push  _test-output-buffered-file/imm32
1167     # . . call
1168     e8/call  flush/disp32
1169     # . . discard args
1170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1171 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1197     # . check-stream-equal(_test-output-stream, "# abcd", msg)
1198     # . . push args
1199     68/push  "F - test-convert-data-passes-comments-through"/imm32
1200     68/push  "# abcd"/imm32
1201     68/push  _test-output-stream/imm32
1202     # . . call
1203     e8/call  check-stream-equal/disp32
1204     # . . discard args
1205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1206     # . epilogue
1207     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1208     5d/pop-to-ebp
1209     c3/return
1210 
1211 test-convert-data-passes-labels-through:
1212     # if the first word ends with ':', pass along the entire line unchanged
1213     # . prologue
1214     55/push-ebp
1215     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1216     # setup
1217     # . clear-stream(_test-input-stream)
1218     # . . push args
1219     68/push  _test-input-stream/imm32
1220     # . . call
1221     e8/call  clear-stream/disp32
1222     # . . discard args
1223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1224     # . clear-stream(_test-output-stream)
1225     # . . push args
1226     68/push  _test-output-stream/imm32
1227     # . . call
1228     e8/call  clear-stream/disp32
1229     # . . discard args
1230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1231     # . clear-stream($_test-output-buffered-file->buffer)
1232     # . . push args
1233     68/push  $_test-output-buffered-file->buffer/imm32
1234     # . . call
1235     e8/call  clear-stream/disp32
1236     # . . discard args
1237     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1238     # initialize input
1239     # . write(_test-input-stream, "ab: # cd")
1240     # . . push args
1241     68/push  "ab: # cd"/imm32
1242     68/push  _test-input-stream/imm32
1243     # . . call
1244     e8/call  write/disp32
1245     # . . discard args
1246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1247     # convert-data(_test-input-stream, _test-output-buffered-file)
1248     # . . push args
1249     68/push  _test-output-buffered-file/imm32
1250     68/push  _test-input-stream/imm32
1251     # . . call
1252     e8/call  convert-data/disp32
1253     # . . discard args
1254     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1255     # check that the line just passed through
1256     # . flush(_test-output-buffered-file)
1257     # . . push args
1258     68/push  _test-output-buffered-file/imm32
1259     # . . call
1260     e8/call  flush/disp32
1261     # . . discard args
1262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1263     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
1264     # . . push args
1265     68/push  "F - test-convert-data-passes-labels-through"/imm32
1266     68/push  "ab: # cd"/imm32
1267     68/push  _test-output-stream/imm32
1268     # . . call
1269     e8/call  check-stream-equal/disp32
1270     # . . discard args
1271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1272     # . epilogue
1273     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1274     5d/pop-to-ebp
1275     c3/return
1276 
1277 test-convert-data-passes-names-through:
1278     # If a word is a valid name, just emit it unchanged.
1279     # Later phases will deal with it.
1280     # . prologue
1281     55/push-ebp
1282     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1283     # setup
1284     # . clear-stream(_test-input-stream)
1285     # . . push args
1286     68/push  _test-input-stream/imm32
1287     # . . call
1288     e8/call  clear-stream/disp32
1289     # . . discard args
1290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1291     # . clear-stream(_test-output-stream)
1292     # . . push args
1293     68/push  _test-output-stream/imm32
1294     # . . call
1295     e8/call  clear-stream/disp32
1296     # . . discard args
1297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1298     # . clear-stream($_test-output-buffered-file->buffer)
1299     # . . push args
1300     68/push  $_test-output-buffered-file->buffer/imm32
1301     # . . call
1302     e8/call  clear-stream/disp32
1303     # . . discard args
1304     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1305     # initialize input
1306     # . write(_test-input-stream, "abcd/imm32")
1307     # . . push args
1308     68/push  "abcd/imm32"/imm32
1309     68/push  _test-input-stream/imm32
1310     # . . call
1311     e8/call  write/disp32
1312     # . . discard args
1313     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1314     # convert-data(_test-input-stream, _test-output-buffered-file)
1315     # . . push args
1316     68/push  _test-output-buffered-file/imm32
1317     68/push  _test-input-stream/imm32
1318     # . . call
1319     e8/call  convert-data/disp32
1320     # . . discard args
1321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1322     # check that the line just passed through
1323     # . flush(_test-output-buffered-file)
1324     # . . push args
1325     68/push  _test-output-buffered-file/imm32
1326     # . . call
1327     e8/call  flush/disp32
1328     # . . discard args
1329     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1330     # . check-stream-equal(_test-output-stream, "abcd/imm32 \n", msg)
1331     # . . push args
1332     68/push  "F - test-convert-data-passes-names-through"/imm32
1333     68/push  "abcd/imm32 \n"/imm32
1334     68/push  _test-output-stream/imm32
1335     # . . call
1336     e8/call  check-stream-equal/disp32
1337     # . . discard args
1338     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1339     # . epilogue
1340     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1341     5d/pop-to-ebp
1342     c3/return
1343 
1344 test-convert-data-handles-imm32:
1345     # If a word has the /imm32 metadata, emit it in 4 bytes.
1346     # . prologue
1347     55/push-ebp
1348     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1349     # setup
1350     # . clear-stream(_test-input-stream)
1351     # . . push args
1352     68/push  _test-input-stream/imm32
1353     # . . call
1354     e8/call  clear-stream/disp32
1355     # . . discard args
1356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1357     # . clear-stream(_test-output-stream)
1358     # . . push args
1359     68/push  _test-output-stream/imm32
1360     # . . call
1361     e8/call  clear-stream/disp32
1362     # . . discard args
1363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1364     # . clear-stream($_test-output-buffered-file->buffer)
1365     # . . push args
1366     68/push  $_test-output-buffered-file->buffer/imm32
1367     # . . call
1368     e8/call  clear-stream/disp32
1369     # . . discard args
1370     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1371     # initialize input
1372     # . write(_test-input-stream, "30/imm32")
1373     # . . push args
1374     68/push  "30/imm32"/imm32
1375     68/push  _test-input-stream/imm32
1376     # . . call
1377     e8/call  write/disp32
1378     # . . discard args
1379     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1380     # convert-data(_test-input-stream, _test-output-buffered-file)
1381     # . . push args
1382     68/push  _test-output-buffered-file/imm32
1383     68/push  _test-input-stream/imm32
1384     # . . call
1385     e8/call  convert-data/disp32
1386     # . . discard args
1387     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1388     # check that 4 bytes were written
1389     # . flush(_test-output-buffered-file)
1390     # . . push args
1391     68/push  _test-output-buffered-file/imm32
1392     # . . call
1393     e8/call  flush/disp32
1394     # . . discard args
1395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1396     # . check-stream-equal(_test-output-stream, "30 00 00 00 \n", msg)
1397     # . . push args
1398     68/push  "F - test-convert-data-handles-imm32"/imm32
1399     68/push  "30 00 00 00 \n"/imm32
1400     68/push  _test-output-stream/imm32
1401     # . . call
1402     e8/call  check-stream-equal/disp32
1403     # . . discard args
1404     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1405     # . epilogue
1406     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1407     5d/pop-to-ebp
1408     c3/return
1409 
1410 test-convert-data-handles-single-byte:
1411     # Any metadata but /imm32 will emit a single byte.
1412     # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
1413     # . prologue
1414     55/push-ebp
1415     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1416     # setup
1417     # . clear-stream(_test-input-stream)
1418     # . . push args
1419     68/push  _test-input-stream/imm32
1420     # . . call
1421     e8/call  clear-stream/disp32
1422     # . . discard args
1423     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1424     # . clear-stream(_test-output-stream)
1425     # . . push args
1426     68/push  _test-output-stream/imm32
1427     # . . call
1428     e8/call  clear-stream/disp32
1429     # . . discard args
1430     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1431     # . clear-stream($_test-output-buffered-file->buffer)
1432     # . . push args
1433     68/push  $_test-output-buffered-file->buffer/imm32
1434     # . . call
1435     e8/call  clear-stream/disp32
1436     # . . discard args
1437     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1438     # initialize input
1439     # . write(_test-input-stream, "30/imm16")
1440     # . . push args
1441     68/push  "30/imm16"/imm32
1442     68/push  _test-input-stream/imm32
1443     # . . call
1444     e8/call  write/disp32
1445     # . . discard args
1446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1447     # convert-data(_test-input-stream, _test-output-buffered-file)
1448     # . . push args
1449     68/push  _test-output-buffered-file/imm32
1450     68/push  _test-input-stream/imm32
1451     # . . call
1452     e8/call  convert-data/disp32
1453     # . . discard args
1454     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1455     # check that a single byte was written (imm16 is not a valid operand type)
1456     # . flush(_test-output-buffered-file)
1457     # . . push args
1458     68/push  _test-output-buffered-file/imm32
1459     # . . call
1460     e8/call  flush/disp32
1461     # . . discard args
1462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1463     # . check-stream-equal(_test-output-stream, "30 \n", msg)
1464     # . . push args
1465     68/push  "F - test-convert-data-handles-single-byte"/imm32
1466     68/push  "30 \n"/imm32
1467     68/push  _test-output-stream/imm32
1468     # . . call
1469     e8/call  check-stream-equal/disp32
1470     # . . discard args
1471     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1472     # . epilogue
1473     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1474     5d/pop-to-ebp
1475     c3/return
1476 
1477 test-convert-data-multiple-bytes:
1478     # Multiple single-byte words in input stream get processed one by one.
1479     # . prologue
1480     55/push-ebp
1481     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1482     # setup
1483     # . clear-stream(_test-input-stream)
1484     # . . push args
1485     68/push  _test-input-stream/imm32
1486     # . . call
1487     e8/call  clear-stream/disp32
1488     # . . discard args
1489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1490     # . clear-stream(_test-output-stream)
1491     # . . push args
1492     68/push  _test-output-stream/imm32
1493     # . . call
1494     e8/call  clear-stream/disp32
1495     # . . discard args
1496     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1497     # . clear-stream($_test-output-buffered-file->buffer)
1498     # . . push args
1499     68/push  $_test-output-buffered-file->buffer/imm32
1500     # . . call
1501     e8/call  clear-stream/disp32
1502     # . . discard args
1503     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1504     # initialize input
1505     # . write(_test-input-stream, "1 2")
1506     # . . push args
1507     68/push  "1 2"/imm32
1508     68/push  _test-input-stream/imm32
1509     # . . call
1510     e8/call  write/disp32
1511     # . . discard args
1512     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1513     # convert-data(_test-input-stream, _test-output-buffered-file)
1514     # . . push args
1515     68/push  _test-output-buffered-file/imm32
1516     68/push  _test-input-stream/imm32
1517     # . . call
1518     e8/call  convert-data/disp32
1519     # . . discard args
1520     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1521     # check output
1522     # . flush(_test-output-buffered-file)
1523     # . . push args
1524     68/push  _test-output-buffered-file/imm32
1525     # . . call
1526     e8/call  flush/disp32
1527     # . . discard args
1528     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1529     # . check-stream-equal(_test-output-stream, "01 02 \n", msg)
1530     # . . push args
1531     68/push  "F - test-convert-data-multiple-bytes"/imm32
1532     68/push  "01 02 \n"/imm32
1533     68/push  _test-output-stream/imm32
1534     # . . call
1535     e8/call  check-stream-equal/disp32
1536     # . . discard args
1537     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1538     # . epilogue
1539     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1540     5d/pop-to-ebp
1541     c3/return
1542 
1543 test-convert-data-byte-then-name:
1544     # Single-byte word followed by valid name get processed one by one.
1545     # . prologue
1546     55/push-ebp
1547     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1548     # setup
1549     # . clear-stream(_test-input-stream)
1550     # . . push args
1551     68/push  _test-input-stream/imm32
1552     # . . call
1553     e8/call  clear-stream/disp32
1554     # . . discard args
1555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1556     # . clear-stream(_test-output-stream)
1557     # . . push args
1558     68/push  _test-output-stream/imm32
1559     # . . call
1560     e8/call  clear-stream/disp32
1561     # . . discard args
1562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1563     # . clear-stream($_test-output-buffered-file->buffer)
1564     # . . push args
1565     68/push  $_test-output-buffered-file->buffer/imm32
1566     # . . call
1567     e8/call  clear-stream/disp32
1568     # . . discard args
1569     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1570     # initialize input
1571     # . write(_test-input-stream, "30 abcd/o")
1572     # . . push args
1573     68/push  "30 abcd/o"/imm32
1574     68/push  _test-input-stream/imm32
1575     # . . call
1576     e8/call  write/disp32
1577     # . . discard args
1578     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1579     # convert-data(_test-input-stream, _test-output-buffered-file)
1580     # . . push args
1581     68/push  _test-output-buffered-file/imm32
1582     68/push  _test-input-stream/imm32
1583     # . . call
1584     e8/call  convert-data/disp32
1585     # . . discard args
1586     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1587     # check output
1588     # . flush(_test-output-buffered-file)
1589     # . . push args
1590     68/push  _test-output-buffered-file/imm32
1591     # . . call
1592     e8/call  flush/disp32
1593     # . . discard args
1594     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1595     # . check-stream-equal(_test-output-stream, "30 abcd/o \n", msg)
1596     # . . push args
1597     68/push  "F - test-convert-data-byte-then-name"/imm32
1598     68/push  "30 abcd/o \n"/imm32
1599     68/push  _test-output-stream/imm32
1600     # . . call
1601     e8/call  check-stream-equal/disp32
1602     # . . discard args
1603     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1604     # . epilogue
1605     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1606     5d/pop-to-ebp
1607     c3/return
1608 
1609 test-convert-data-multiple-words:
1610     # Multiple words in input stream get processed one by one.
1611     # . prologue
1612     55/push-ebp
1613     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1614     # setup
1615     # . clear-stream(_test-input-stream)
1616     # . . push args
1617     68/push  _test-input-stream/imm32
1618     # . . call
1619     e8/call  clear-stream/disp32
1620     # . . discard args
1621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1622     # . clear-stream(_test-output-stream)
1623     # . . push args
1624     68/push  _test-output-stream/imm32
1625     # . . call
1626     e8/call  clear-stream/disp32
1627     # . . discard args
1628     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1629     # . clear-stream($_test-output-buffered-file->buffer)
1630     # . . push args
1631     68/push  $_test-output-buffered-file->buffer/imm32
1632     # . . call
1633     e8/call  clear-stream/disp32
1634     # . . discard args
1635     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1636     # initialize input
1637     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
1638     # . . push args
1639     68/push  "30 abcd/o 42e1/imm32"/imm32
1640     68/push  _test-input-stream/imm32
1641     # . . call
1642     e8/call  write/disp32
1643     # . . discard args
1644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1645     # convert-data(_test-input-stream, _test-output-buffered-file)
1646     # . . push args
1647     68/push  _test-output-buffered-file/imm32
1648     68/push  _test-input-stream/imm32
1649     # . . call
1650     e8/call  convert-data/disp32
1651     # . . discard args
1652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1653     # check output
1654     # . flush(_test-output-buffered-file)
1655     # . . push args
1656     68/push  _test-output-buffered-file/imm32
1657     # . . call
1658     e8/call  flush/disp32
1659     # . . discard args
1660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1661 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1687     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 \n", msg)
1688     # . . push args
1689     68/push  "F - test-convert-data-multiple-words"/imm32
1690     68/push  "30 abcd/o e1 42 00 00 \n"/imm32
1691     68/push  _test-output-stream/imm32
1692     # . . call
1693     e8/call  check-stream-equal/disp32
1694     # . . discard args
1695     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1696     # . epilogue
1697     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1698     5d/pop-to-ebp
1699     c3/return
1700 
1701 test-convert-data-trailing-comment:
1702     # Trailing comments in data segment get appropriately ignored.
1703     # . prologue
1704     55/push-ebp
1705     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1706     # setup
1707     # . clear-stream(_test-input-stream)
1708     # . . push args
1709     68/push  _test-input-stream/imm32
1710     # . . call
1711     e8/call  clear-stream/disp32
1712     # . . discard args
1713     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1714     # . clear-stream(_test-output-stream)
1715     # . . push args
1716     68/push  _test-output-stream/imm32
1717     # . . call
1718     e8/call  clear-stream/disp32
1719     # . . discard args
1720     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1721     # . clear-stream($_test-output-buffered-file->buffer)
1722     # . . push args
1723     68/push  $_test-output-buffered-file->buffer/imm32
1724     # . . call
1725     e8/call  clear-stream/disp32
1726     # . . discard args
1727     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1728     # initialize input
1729     # . write(_test-input-stream, "30/imm32 # comment")
1730     # . . push args
1731     68/push  "30/imm32 # comment"/imm32
1732     68/push  _test-input-stream/imm32
1733     # . . call
1734     e8/call  write/disp32
1735     # . . discard args
1736     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1737     # convert-data(_test-input-stream, _test-output-buffered-file)
1738     # . . push args
1739     68/push  _test-output-buffered-file/imm32
1740     68/push  _test-input-stream/imm32
1741     # . . call
1742     e8/call  convert-data/disp32
1743     # . . discard args
1744     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1745     # check output
1746     # . flush(_test-output-buffered-file)
1747     # . . push args
1748     68/push  _test-output-buffered-file/imm32
1749     # . . call
1750     e8/call  flush/disp32
1751     # . . discard args
1752     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1753 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1779     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
1780     # . . push args
1781     68/push  "F - test-convert-data-trailing-comment"/imm32
1782     68/push  "30 00 00 00 # comment"/imm32
1783     68/push  _test-output-stream/imm32
1784     # . . call
1785     e8/call  check-stream-equal/disp32
1786     # . . discard args
1787     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1788     # . epilogue
1789     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1790     5d/pop-to-ebp
1791     c3/return
1792 
1793 # pack an instruction, following the C++ version
1794 #
1795 # zero error handling at the moment (continuing to rely on the C++ version for that):
1796 #   missing fields are always 0-filled
1797 #   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.
1798 #   may pick up any of duplicate operands in an instruction
1799 #   silently drop extraneous operands
1800 #   unceremoniously abort on non-numeric operands except disp or imm
1801 #   opcodes must be lowercase and zero padded
1802 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
1803 convert-instruction:  # line: (addr stream byte), out: (addr buffered-file)
1804     # pseudocode:
1805     #   # some early exits
1806     #   var word-slice = next-word(line)
1807     #   if slice-empty?(word-slice)
1808     #     write-stream-data(out, line)
1809     #     return
1810     #   if slice-starts-with?(word-slice, "#")
1811     #     write-stream-data(out, line)
1812     #     return
1813     #   if slice-ends-with?(word-slice, ":")
1814     #     write-stream-data(out, line)
1815     #     return
1816     #   # really convert
1817     #   emit-opcodes(line, out)
1818     #   emit-modrm(line, out)
1819     #   emit-sib(line, out)
1820     #   emit-disp(line, out)
1821     #   emit-imm(line, out)
1822     #   emit-line-in-comment(line, out)
1823     #
1824     # . prologue
1825     55/push-ebp
1826     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1827     # . save registers
1828     50/push-eax
1829     51/push-ecx
1830     52/push-edx
1831     # var word-slice/ecx: slice
1832     68/push  0/imm32/end
1833     68/push  0/imm32/start
1834     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1835     # next-word(line, word-slice)
1836     # . . push args
1837     51/push-ecx
1838     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1839     # . . call
1840     e8/call  next-word/disp32
1841     # . . discard args
1842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1843 $convert-instruction:check0:
1844     # if (slice-empty?(word-slice)) break
1845     # . eax = slice-empty?(word-slice)
1846     # . . push args
1847     51/push-ecx
1848     # . . call
1849     e8/call  slice-empty?/disp32
1850     # . . discard args
1851     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1852     # . if (eax != false) pass through
1853     3d/compare-eax-and  0/imm32/false
1854     75/jump-if-!=  $convert-instruction:pass-through/disp8
1855 $convert-instruction:check1:
1856     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
1857     # . var start/edx: (addr byte) = word-slice->start
1858     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
1859     # . var c/eax: byte = *start
1860     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1861     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
1862     # . if (c == '#') pass through
1863     3d/compare-eax-and  0x23/imm32/hash
1864     74/jump-if-=  $convert-instruction:pass-through/disp8
1865 $convert-instruction:check2:
1866     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
1867     # . var end/edx: (addr byte) = word-slice->end
1868     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1869     # . var c/eax: byte = *(end-1)
1870     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1871     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
1872     # . if (c == ':') pass through
1873     3d/compare-eax-and  0x3a/imm32/colon
1874     75/jump-if-!=  $convert-instruction:really-convert/disp8
1875 $convert-instruction:pass-through:
1876     # write-stream-data(out, line)
1877     # . . push args
1878     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1879     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1880     # . . call
1881     e8/call  write-stream-data/disp32
1882     # . . discard args
1883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1884     # return
1885     eb/jump  $convert-instruction:end/disp8
1886 $convert-instruction:really-convert:
1887     # emit-opcodes(line, out)
1888     # . . push args
1889     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1890     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1891     # . . call
1892     e8/call  emit-opcodes/disp32
1893     # . . discard args
1894     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1895     # emit-modrm(line, out)
1896     # . . push args
1897     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1898     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1899     # . . call
1900     e8/call  emit-modrm/disp32
1901     # . . discard args
1902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1903     # emit-sib(line, out)
1904     # . . push args
1905     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1906     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1907     # . . call
1908     e8/call  emit-sib/disp32
1909     # . . discard args
1910     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1911     # emit-disp(line, out)
1912     # . . push args
1913     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1914     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1915     # . . call
1916     e8/call  emit-disp/disp32
1917     # . . discard args
1918     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1919     # emit-imm(line, out)
1920     # . . push args
1921     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1922     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1923     # . . call
1924     e8/call  emit-imm/disp32
1925     # . . discard args
1926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1927     # emit-line-in-comment(line, out)
1928     # . . push args
1929     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1930     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1931     # . . call
1932     e8/call  emit-line-in-comment/disp32
1933     # . . discard args
1934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1935 $convert-instruction:end:
1936     # . restore locals
1937     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1938     # . restore registers
1939     5a/pop-to-edx
1940     59/pop-to-ecx
1941     58/pop-to-eax
1942     # . epilogue
1943     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1944     5d/pop-to-ebp
1945     c3/return
1946 
1947 emit-opcodes:  # line: (addr stream byte), out: (addr buffered-file)
1948     # opcodes occupy 1-3 bytes:
1949     #   xx
1950     #   0f xx
1951     #   f2 xx
1952     #   f3 xx
1953     #   f2 0f xx
1954     #   f3 0f xx
1955     #
1956     # pseudocode:
1957     #   rewind-stream(line)
1958     #
1959     #   var op1 = next-word(line)
1960     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
1961     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
1962     #   write-slice-buffered(out, op1)
1963     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3")
1964     #     return
1965     #
1966     #   var op2 = next-word(line)
1967     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
1968     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
1969     #   write-slice-buffered(out, op2)
1970     #   if slice-equal?(op1, "0f")
1971     #     return
1972     #   if !slice-equal?(op2, "0f")
1973     #     return
1974     #
1975     #   var op3 = next-word(line)
1976     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
1977     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
1978     #   write-slice-buffered(out, op3)
1979     #
1980     # . prologue
1981     55/push-ebp
1982     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1983     # . save registers
1984     50/push-eax
1985     51/push-ecx
1986     52/push-edx
1987     53/push-ebx
1988     # var op1/ecx: slice
1989     68/push  0/imm32/end
1990     68/push  0/imm32/start
1991     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1992     # var op2/edx: slice
1993     68/push  0/imm32/end
1994     68/push  0/imm32/start
1995     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1996     # rewind-stream(line)
1997     # . . push args
1998     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1999     # . . call
2000     e8/call  rewind-stream/disp32
2001     # . . discard args
2002     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2003 $emit-opcodes:op1:
2004     # next-word(line, op1)
2005     # . . push args
2006     51/push-ecx
2007     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2008     # . . call
2009     e8/call  next-word/disp32
2010     # . . discard args
2011     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2012     # if (slice-empty?(op1)) return
2013     # . eax = slice-empty?(op1)
2014     # . . push args
2015     51/push-ecx
2016     # . . call
2017     e8/call  slice-empty?/disp32
2018     # . . discard args
2019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2020     # . if (eax != false) return
2021     3d/compare-eax-and  0/imm32/false
2022     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2023     # if (slice-starts-with?(op1, "#")) return
2024     # . var start/ebx: (addr byte) = op1->start
2025     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # copy *ecx to ebx
2026     # . var c/eax: byte = *start
2027     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2028     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2029     # . if (c == '#') return
2030     3d/compare-eax-and  0x23/imm32/hash
2031     0f 84/jump-if-=  $emit-opcodes:end/disp32
2032     # op1 = next-token-from-slice(op1->start, op1->end, '/')
2033     # . . push args
2034     51/push-ecx
2035     68/push  0x2f/imm32/slash
2036     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
2037     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
2038     # . . call
2039     e8/call  next-token-from-slice/disp32
2040     # . . discard args
2041     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2042     # write-slice-buffered(out, op1)
2043     # . . push args
2044     51/push-ecx
2045     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2046     # . . call
2047     e8/call  write-slice-buffered/disp32
2048     # . . discard args
2049     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2050     # write-buffered(out, " ")
2051     # . . push args
2052     68/push  Space/imm32
2053     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2054     # . . call
2055     e8/call  write-buffered/disp32
2056     # . . discard args
2057     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2058     # if (slice-equal?(op1, "0f")) goto op2
2059     # . eax = slice-equal?(op1, "0f")
2060     # . . push args
2061     68/push  "0f"/imm32
2062     51/push-ecx
2063     # . . call
2064     e8/call  slice-equal?/disp32
2065     # . . discard args
2066     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2067     # . if (eax != false) goto op2
2068     3d/compare-eax-and  0/imm32/false
2069     75/jump-if-!=  $emit-opcodes:op2/disp8
2070     # if (slice-equal?(op1, "f2")) goto op2
2071     # . eax = slice-equal?(op1, "f2")
2072     # . . push args
2073     68/push  "f2"/imm32
2074     51/push-ecx
2075     # . . call
2076     e8/call  slice-equal?/disp32
2077     # . . discard args
2078     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2079     # . if (eax != false) goto op2
2080     3d/compare-eax-and  0/imm32/false
2081     75/jump-if-!=  $emit-opcodes:op2/disp8
2082     # if (slice-equal?(op1, "f3")) goto op2
2083     # . eax = slice-equal?(op1, "f3")
2084     # . . push args
2085     68/push  "f3"/imm32
2086     51/push-ecx
2087     # . . call
2088     e8/call  slice-equal?/disp32
2089     # . . discard args
2090     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2091     # . if (eax != false) goto op2
2092     3d/compare-eax-and  0/imm32/false
2093     75/jump-if-!=  $emit-opcodes:op2/disp8
2094     # otherwise return
2095     e9/jump  $emit-opcodes:end/disp32
2096 $emit-opcodes:op2:
2097     # next-word(line, op2)
2098     # . . push args
2099     52/push-edx
2100     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2101     # . . call
2102     e8/call  next-word/disp32
2103     # . . discard args
2104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2105     # if (slice-empty?(op2)) return
2106     # . eax = slice-empty?(op2)
2107     # . . push args
2108     52/push-edx
2109     # . . call
2110     e8/call  slice-empty?/disp32
2111     # . . discard args
2112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2113     # . if (eax != false) return
2114     3d/compare-eax-and  0/imm32/false
2115     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2116     # if (slice-starts-with?(op2, "#")) return
2117     # . var start/ebx: (addr byte) = op2->start
2118     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
2119     # . var c/eax: byte = *start
2120     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2121     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2122     # . if (c == '#') return
2123     3d/compare-eax-and  0x23/imm32/hash
2124     0f 84/jump-if-=  $emit-opcodes:end/disp32
2125     # op2 = next-token-from-slice(op2->start, op2->end, '/')
2126     # . . push args
2127     52/push-edx
2128     68/push  0x2f/imm32/slash
2129     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2130     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2131     # . . call
2132     e8/call  next-token-from-slice/disp32
2133     # . . discard args
2134     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2135     # write-slice-buffered(out, op2)
2136     # . . push args
2137     52/push-edx
2138     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2139     # . . call
2140     e8/call  write-slice-buffered/disp32
2141     # . . discard args
2142     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2143     # write-buffered(out, " ")
2144     # . . push args
2145     68/push  Space/imm32
2146     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2147     # . . call
2148     e8/call  write-buffered/disp32
2149     # . . discard args
2150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2151     # if (slice-equal?(op1, "0f")) return
2152     # . eax = slice-equal?(op1, "0f")
2153     # . . push args
2154     68/push  "0f"/imm32
2155     51/push-ecx
2156     # . . call
2157     e8/call  slice-equal?/disp32
2158     # . . discard args
2159     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2160     # . if (eax != false) return
2161     3d/compare-eax-and  0/imm32/false
2162     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2163     # if (!slice-equal?(op2, "0f")) return
2164     # . eax = slice-equal?(op2, "0f")
2165     # . . push args
2166     68/push  "0f"/imm32
2167     52/push-edx
2168     # . . call
2169     e8/call  slice-equal?/disp32
2170     # . . discard args
2171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2172     # . if (eax == false) return
2173     3d/compare-eax-and  0/imm32/false
2174     0f 84/jump-if-=  $emit-opcodes:end/disp32
2175 $emit-opcodes:op3:
2176     # next-word(line, op3)  # reuse op2/edx
2177     # . . push args
2178     52/push-edx
2179     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2180     # . . call
2181     e8/call  next-word/disp32
2182     # . . discard args
2183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2184     # if (slice-empty?(op3)) return
2185     # . eax = slice-empty?(op3)
2186     # . . push args
2187     52/push-edx
2188     # . . call
2189     e8/call  slice-empty?/disp32
2190     # . . discard args
2191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2192     # . if (eax != false) return
2193     3d/compare-eax-and  0/imm32/false
2194     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2195     # if (slice-starts-with?(op3, "#")) return
2196     # . var start/ebx: (addr byte) = op2->start
2197     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
2198     # . var c/eax: byte = *start
2199     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2200     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2201     # . if (c == '#') return
2202     3d/compare-eax-and  0x23/imm32/hash
2203     0f 84/jump-if-=  $emit-opcodes:end/disp32
2204     # op3 = next-token-from-slice(op3->start, op3->end, '/')
2205     # . . push args
2206     52/push-edx
2207     68/push  0x2f/imm32/slash
2208     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2209     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2210     # . . call
2211     e8/call  next-token-from-slice/disp32
2212     # . . discard args
2213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2214     # write-slice-buffered(out, op3)
2215     # . . push args
2216     52/push-edx
2217     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2218     # . . call
2219     e8/call  write-slice-buffered/disp32
2220     # . . discard args
2221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2222     # write-buffered(out, " ")
2223     # . . push args
2224     68/push  Space/imm32
2225     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2226     # . . call
2227     e8/call  write-buffered/disp32
2228     # . . discard args
2229     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2230 $emit-opcodes:end:
2231     # . restore locals
2232     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2233     # . restore registers
2234     5b/pop-to-ebx
2235     5a/pop-to-edx
2236     59/pop-to-ecx
2237     58/pop-to-eax
2238     # . epilogue
2239     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2240     5d/pop-to-ebp
2241     c3/return
2242 
2243 emit-modrm:  # line: (addr stream byte), out: (addr buffered-file)
2244     # pseudocode:
2245     #   rewind-stream(line)
2246     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
2247     #   var word-slice: slice
2248     #   while true
2249     #     word-slice = next-word(line)
2250     #     if (slice-empty?(word-slice)) break
2251     #     if (slice-starts-with?(word-slice, "#")) break
2252     #     if (has-metadata?(word-slice, "mod"))
2253     #       mod = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2254     #       has-modrm? = true
2255     #     else if (has-metadata?(word-slice, "rm32"))
2256     #       rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2257     #       has-modrm? = true
2258     #     else if (has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "subop"))
2259     #       r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2260     #       has-modrm? = true
2261     #   if has-modrm?
2262     #     var modrm = mod & 0b11
2263     #     modrm <<= 3
2264     #     modrm |= r32 & 0b111
2265     #     modrm <<= 3
2266     #     modrm |= rm32 & 0b111
2267     #     emit-hex(out, modrm, 1)
2268     #
2269     # . prologue
2270     55/push-ebp
2271     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2272     # . save registers
2273     50/push-eax
2274     51/push-ecx
2275     52/push-edx
2276     53/push-ebx
2277     56/push-esi
2278     57/push-edi
2279     # var word-slice/ecx: slice
2280     68/push  0/imm32/end
2281     68/push  0/imm32/start
2282     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2283     # var has-modrm?/edx: boolean = false
2284     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
2285     # var mod/ebx: byte = 0
2286     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2287     # var rm32/esi: byte = 0
2288     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
2289     # var r32/edi: byte = 0
2290     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
2291     # rewind-stream(line)
2292     # . . push args
2293     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2294     # . . call
2295     e8/call  rewind-stream/disp32
2296     # . . discard args
2297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2298 +-- 33 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2331 $emit-modrm:loop:
2332     # next-word(line, word-slice)
2333     # . . push args
2334     51/push-ecx
2335     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2336     # . . call
2337     e8/call  next-word/disp32
2338     # . . discard args
2339     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2340 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2380 $emit-modrm:check0:
2381     # if (slice-empty?(word-slice)) break
2382     # . eax = slice-empty?(word-slice)
2383     # . . push args
2384     51/push-ecx
2385     # . . call
2386     e8/call  slice-empty?/disp32
2387     # . . discard args
2388     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2389     # . if (eax != false) pass through
2390     3d/compare-eax-and  0/imm32/false
2391     0f 85/jump-if-!=  $emit-modrm:break/disp32
2392 $emit-modrm:check1:
2393     # if (slice-starts-with?(word-slice, "#")) break
2394     # . spill edx
2395     52/push-edx
2396     # . var start/edx: (addr byte) = word-slice->start
2397     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2398     # . var c/eax: byte = *start
2399     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2400     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
2401     # . restore edx
2402     5a/pop-to-edx
2403     # . if (c == '#') pass through
2404     3d/compare-eax-and  0x23/imm32/hash
2405     0f 84/jump-if-=  $emit-modrm:break/disp32
2406 $emit-modrm:check-for-mod:
2407     # if (has-metadata?(word-slice, "mod"))
2408     # . eax = has-metadata?(ecx, "mod")
2409     # . . push args
2410     68/push  "mod"/imm32
2411     51/push-ecx
2412     # . . call
2413     e8/call  has-metadata?/disp32
2414     # . . discard args
2415     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2416     # . if (eax == false) goto next check
2417     3d/compare-eax-and  0/imm32/false
2418     74/jump-if-=  $emit-modrm:check-for-rm32/disp8
2419 $emit-modrm:mod:
2420     # mod = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2421     # . eax = parse-datum-of-word(word-slice)
2422     # . . push args
2423     51/push-ecx
2424     # . . call
2425     e8/call  parse-datum-of-word/disp32
2426     # . . discard args
2427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2428     # . mod = eax
2429     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2430     # has-modrm? = true
2431     ba/copy-to-edx  1/imm32/true
2432     # continue
2433     e9/jump  $emit-modrm:loop/disp32
2434 $emit-modrm:check-for-rm32:
2435     # if (has-metadata?(word-slice, "rm32"))
2436     # . eax = has-metadata?(ecx, "rm32")
2437     # . . push args
2438     68/push  "rm32"/imm32
2439     51/push-ecx
2440     # . . call
2441     e8/call  has-metadata?/disp32
2442     # . . discard args
2443     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2444     # . if (eax == false) goto next check
2445     3d/compare-eax-and  0/imm32/false
2446     74/jump-if-=  $emit-modrm:check-for-r32/disp8
2447 $emit-modrm:rm32:
2448     # rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2449     # . eax = parse-datum-of-word(word-slice)
2450     # . . push args
2451     51/push-ecx
2452     # . . call
2453     e8/call  parse-datum-of-word/disp32
2454     # . . discard args
2455     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2456     # . rm32 = eax
2457     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2458     # has-modrm? = true
2459     ba/copy-to-edx  1/imm32/true
2460     # continue
2461     e9/jump  $emit-modrm:loop/disp32
2462 $emit-modrm:check-for-r32:
2463     # if (has-metadata?(word-slice, "r32"))
2464     # . eax = has-metadata?(ecx, "r32")
2465     # . . push args
2466     68/push  "r32"/imm32
2467     51/push-ecx
2468     # . . call
2469     e8/call  has-metadata?/disp32
2470     # . . discard args
2471     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2472     # . if (eax == false) goto next check
2473     3d/compare-eax-and  0/imm32/false
2474     74/jump-if-=  $emit-modrm:check-for-subop/disp8
2475 $emit-modrm:r32:
2476     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2477     # . eax = parse-datum-of-word(word-slice)
2478     # . . push args
2479     51/push-ecx
2480     # . . call
2481     e8/call  parse-datum-of-word/disp32
2482     # . . discard args
2483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2484     # . r32 = eax
2485     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2486     # has-modrm? = true
2487     ba/copy-to-edx  1/imm32/true
2488     # continue
2489     e9/jump  $emit-modrm:loop/disp32
2490 $emit-modrm:check-for-subop:
2491     # if (has-metadata?(word-slice, "subop"))
2492     # . eax = has-metadata?(ecx, "subop")
2493     # . . push args
2494     68/push  "subop"/imm32
2495     51/push-ecx
2496     # . . call
2497     e8/call  has-metadata?/disp32
2498     # . . discard args
2499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2500     # . if (eax == false) loop
2501     3d/compare-eax-and  0/imm32/false
2502     0f 84/jump-if-=  $emit-modrm:loop/disp32
2503 $emit-modrm:subop:
2504     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2505     # . eax = parse-datum-of-word(word-slice)
2506     # . . push args
2507     51/push-ecx
2508     # . . call
2509     e8/call  parse-datum-of-word/disp32
2510     # . . discard args
2511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2512     # . r32 = eax
2513     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2514     # has-modrm? = true
2515     ba/copy-to-edx  1/imm32/true
2516     # continue
2517     e9/jump  $emit-modrm:loop/disp32
2518 $emit-modrm:break:
2519     # if (!has-modrm?) return
2520     81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
2521     74/jump-if-=  $emit-modrm:end/disp8
2522 $emit-modrm:calculate:
2523     # var modrm/ebx: byte = mod & 0b11
2524     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
2525     # modrm <<= 3
2526     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2527     # modrm |= r32 & 0b111
2528     81          4/subop/and         3/mod/direct    7/rm32/edi    .           .             .           .           .               7/imm32/0b111     # bitwise and of edi
2529     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           7/r32/edi   .               .                 # ebx = bitwise OR with edi
2530     # modrm <<= 3
2531     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2532     # modrm |= rm32 & 0b111
2533     81          4/subop/and         3/mod/direct    6/rm32/esi    .           .             .           .           .               7/imm32/0b111     # bitwise and of esi
2534     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           6/r32/esi   .               .                 # ebx = bitwise OR with esi
2535 $emit-modrm:emit:
2536     # emit-hex(out, modrm, 1)
2537     # . . push args
2538     68/push  1/imm32
2539     53/push-ebx
2540     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2541     # . . call
2542     e8/call  emit-hex/disp32
2543     # . . discard args
2544     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2545 $emit-modrm:end:
2546     # . restore locals
2547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2548     # . restore registers
2549     5f/pop-to-edi
2550     5e/pop-to-esi
2551     5b/pop-to-ebx
2552     5a/pop-to-edx
2553     59/pop-to-ecx
2554     58/pop-to-eax
2555     # . epilogue
2556     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2557     5d/pop-to-ebp
2558     c3/return
2559 
2560 emit-sib:  # line: (addr stream byte), out: (addr buffered-file)
2561     # pseudocode:
2562     #   var has-sib? = false, base = 0, index = 0, scale = 0
2563     #   var word-slice: slice
2564     #   while true
2565     #     word-slice = next-word(line)
2566     #     if (slice-empty?(word-slice)) break
2567     #     if (slice-starts-with?(word-slice, "#")) break
2568     #     if (has-metadata?(word-slice, "base")
2569     #       base = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2570     #       has-sib? = true
2571     #     else if (has-metadata?(word-slice, "index")
2572     #       index = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2573     #       has-sib? = true
2574     #     else if (has-metadata?(word-slice, "scale")
2575     #       scale = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2576     #       has-sib? = true
2577     #   if has-sib?
2578     #     var sib = scale & 0b11
2579     #     sib <<= 3
2580     #     sib |= index & 0b111
2581     #     sib <<= 3
2582     #     sib |= base & 0b111
2583     #     emit-hex(out, sib, 1)
2584     #
2585     # . prologue
2586     55/push-ebp
2587     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2588     # . save registers
2589     50/push-eax
2590     51/push-ecx
2591     52/push-edx
2592     53/push-ebx
2593     56/push-esi
2594     57/push-edi
2595     # var word-slice/ecx: slice
2596     68/push  0/imm32/end
2597     68/push  0/imm32/start
2598     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2599     # var has-sib?/edx: boolean = false
2600     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
2601     # var scale/ebx: byte = 0
2602     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2603     # var base/esi: byte = 0
2604     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
2605     # var index/edi: byte = 0
2606     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
2607     # rewind-stream(line)
2608     # . . push args
2609     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2610     # . . call
2611     e8/call  rewind-stream/disp32
2612     # . . discard args
2613     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2614 $emit-sib:loop:
2615 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2641     # next-word(line, word-slice)
2642     # . . push args
2643     51/push-ecx
2644     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2645     # . . call
2646     e8/call  next-word/disp32
2647     # . . discard args
2648     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2649 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2689 $emit-sib:check0:
2690     # if (slice-empty?(word-slice)) break
2691     # . eax = slice-empty?(word-slice)
2692     # . . push args
2693     51/push-ecx
2694     # . . call
2695     e8/call  slice-empty?/disp32
2696     # . . discard args
2697     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2698     # . if (eax != false) pass through
2699     3d/compare-eax-and  0/imm32/false
2700     0f 85/jump-if-!=  $emit-sib:break/disp32
2701 $emit-sib:check1:
2702     # if (slice-starts-with?(word-slice, "#")) break
2703     # . spill edx
2704     52/push-edx
2705     # . var start/edx: (addr byte) = word-slice->start
2706     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2707     # . var c/eax: byte = *start
2708     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2709     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
2710     # . restore edx
2711     5a/pop-to-edx
2712     # . if (c == '#') pass through
2713     3d/compare-eax-and  0x23/imm32/hash
2714     0f 84/jump-if-=  $emit-sib:break/disp32
2715 $emit-sib:check-for-scale:
2716     # if (has-metadata?(word-slice, "scale"))
2717     # . eax = has-metadata?(ecx, "scale")
2718     # . . push args
2719     68/push  "scale"/imm32
2720     51/push-ecx
2721     # . . call
2722     e8/call  has-metadata?/disp32
2723     # . . discard args
2724     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2725     # . if (eax == false) goto next check
2726     3d/compare-eax-and  0/imm32/false
2727     74/jump-if-=  $emit-sib:check-for-base/disp8
2728 $emit-sib:scale:
2729     # scale = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2730     # . eax = parse-datum-of-word(word-slice)
2731     # . . push args
2732     51/push-ecx
2733     # . . call
2734     e8/call  parse-datum-of-word/disp32
2735     # . . discard args
2736     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2737     # . scale = eax
2738     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2739     # has-sib? = true
2740     ba/copy-to-edx  1/imm32/true
2741     # continue
2742     e9/jump  $emit-sib:loop/disp32
2743 $emit-sib:check-for-base:
2744     # if (has-metadata?(word-slice, "base"))
2745     # . eax = has-metadata?(ecx, "base")
2746     # . . push args
2747     68/push  "base"/imm32
2748     51/push-ecx
2749     # . . call
2750     e8/call  has-metadata?/disp32
2751     # . . discard args
2752     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2753     # . if (eax == false) goto next check
2754     3d/compare-eax-and  0/imm32/false
2755     74/jump-if-=  $emit-sib:check-for-index/disp8
2756 $emit-sib:base:
2757     # base = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2758     # . eax = parse-datum-of-word(word-slice)
2759     # . . push args
2760     51/push-ecx
2761     # . . call
2762     e8/call  parse-datum-of-word/disp32
2763     # . . discard args
2764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2765     # . base = eax
2766     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2767     # has-sib? = true
2768     ba/copy-to-edx  1/imm32/true
2769     # continue
2770     e9/jump  $emit-sib:loop/disp32
2771 $emit-sib:check-for-index:
2772     # if (has-metadata?(word-slice, "index"))
2773     # . eax = has-metadata?(ecx, "index")
2774     # . . push args
2775     68/push  "index"/imm32
2776     51/push-ecx
2777     # . . call
2778     e8/call  has-metadata?/disp32
2779     # . . discard args
2780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2781     # . if (eax == false) loop
2782     3d/compare-eax-and  0/imm32/false
2783     0f 84/jump-if-=  $emit-sib:loop/disp32
2784 $emit-sib:index:
2785     # index = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2786     # . eax = parse-datum-of-word(word-slice)
2787     # . . push args
2788     51/push-ecx
2789     # . . call
2790     e8/call  parse-datum-of-word/disp32
2791     # . . discard args
2792     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2793     # . index = eax
2794     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2795     # has-sib? = true
2796     ba/copy-to-edx  1/imm32/true
2797     # continue
2798     e9/jump  $emit-sib:loop/disp32
2799 $emit-sib:break:
2800     # if (!has-sib?) return
2801     81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
2802     74/jump-if-=  $emit-sib:end/disp8
2803 $emit-sib:calculate:
2804     # var sib/ebx: byte = scale & 0b11
2805     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
2806     # sib <<= 2
2807     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2808     # sib |= index & 0b111
2809     81          4/subop/and         3/mod/direct    7/rm32/edi    .           .             .           .           .               7/imm32/0b111     # bitwise and of edi
2810     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           7/r32/edi   .               .                 # ebx = bitwise OR with edi
2811     # sib <<= 3
2812     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2813     # sib |= base & 0b111
2814     81          4/subop/and         3/mod/direct    6/rm32/esi    .           .             .           .           .               7/imm32/0b111     # bitwise and of esi
2815     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           6/r32/esi   .               .                 # ebx = bitwise OR with esi
2816 $emit-sib:emit:
2817     # emit-hex(out, sib, 1)
2818     # . . push args
2819     68/push  1/imm32
2820     53/push-ebx
2821     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2822     # . . call
2823     e8/call  emit-hex/disp32
2824     # . . discard args
2825     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2826 $emit-sib:end:
2827     # . restore locals
2828     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2829     # . restore registers
2830     5f/pop-to-edi
2831     5e/pop-to-esi
2832     5b/pop-to-ebx
2833     5a/pop-to-edx
2834     59/pop-to-ecx
2835     58/pop-to-eax
2836     # . epilogue
2837     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2838     5d/pop-to-ebp
2839     c3/return
2840 
2841 emit-disp:  # line: (addr stream byte), out: (addr buffered-file)
2842     # pseudocode:
2843     #   rewind-stream(line)
2844     #   var word-slice: slice
2845     #   while true
2846     #     word-slice = next-word(line)
2847     #     if (slice-empty?(word-slice)) break
2848     #     if (slice-starts-with?(word-slice, "#")) break
2849     #     if has-metadata?(word-slice, "disp32")
2850     #       emit(out, word-slice, 4)
2851     #       break
2852     #     if has-metadata?(word-slice, "disp16")
2853     #       emit(out, word-slice, 2)
2854     #       break
2855     #     if has-metadata?(word-slice, "disp8")
2856     #       emit(out, word-slice, 1)
2857     #       break
2858     #
2859     # . prologue
2860     55/push-ebp
2861     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2862     # . save registers
2863     50/push-eax
2864     51/push-ecx
2865     52/push-edx
2866     # var word-slice/ecx: slice
2867     68/push  0/imm32/end
2868     68/push  0/imm32/start
2869     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2870     # rewind-stream(line)
2871     # . . push args
2872     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2873     # . . call
2874     e8/call  rewind-stream/disp32
2875     # . . discard args
2876     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2877 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2903 $emit-disp:loop:
2904     # next-word(line, word-slice)
2905     # . . push args
2906     51/push-ecx
2907     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2908     # . . call
2909     e8/call  next-word/disp32
2910     # . . discard args
2911     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2912 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2952 $emit-disp:check0:
2953     # if (slice-empty?(word-slice)) break
2954     # . eax = slice-empty?(word-slice)
2955     # . . push args
2956     51/push-ecx
2957     # . . call
2958     e8/call  slice-empty?/disp32
2959     # . . discard args
2960     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2961     # . if (eax != false) pass through
2962     3d/compare-eax-and  0/imm32/false
2963     0f 85/jump-if-!=  $emit-disp:break/disp32
2964 $emit-disp:check1:
2965     # if (slice-starts-with?(word-slice, "#")) break
2966     # . var start/edx: (addr byte) = word-slice->start
2967     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2968     # . var c/eax: byte = *start
2969     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2970     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
2971     # . if (c == '#') break
2972     3d/compare-eax-and  0x23/imm32/hash
2973     0f 84/jump-if-=  $emit-disp:break/disp32
2974 $emit-disp:check-for-disp32:
2975     # if (has-metadata?(word-slice, "disp32"))
2976     # . eax = has-metadata?(ecx, "disp32")
2977     # . . push args
2978     68/push  "disp32"/imm32
2979     51/push-ecx
2980     # . . call
2981     e8/call  has-metadata?/disp32
2982     # . . discard args
2983     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2984     # . if (eax == false) goto next check
2985     3d/compare-eax-and  0/imm32/false
2986     74/jump-if-=  $emit-disp:check-for-disp16/disp8
2987 $emit-disp:disp32:
2988     # emit(out, word-slice, 4)
2989     # . . push args
2990     68/push  4/imm32
2991     51/push-ecx
2992     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2993     # . . call
2994     e8/call  emit/disp32
2995     # . . discard args
2996     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2997     # break
2998     e9/jump  $emit-disp:break/disp32
2999 $emit-disp:check-for-disp16:
3000     # else if (has-metadata?(word-slice, "disp16"))
3001     # . eax = has-metadata?(ecx, "disp16")
3002     # . . push args
3003     68/push  "disp16"/imm32
3004     51/push-ecx
3005     # . . call
3006     e8/call  has-metadata?/disp32
3007     # . . discard args
3008     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3009     # . if (eax == false) goto next check
3010     3d/compare-eax-and  0/imm32/false
3011     74/jump-if-=  $emit-disp:check-for-disp8/disp8
3012 $emit-disp:disp16:
3013     # emit(out, word-slice, 2)
3014     # . . push args
3015     68/push  2/imm32
3016     51/push-ecx
3017     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3018     # . . call
3019     e8/call  emit/disp32
3020     # . . discard args
3021     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3022     # break
3023     e9/jump  $emit-disp:break/disp32
3024 $emit-disp:check-for-disp8:
3025     # if (has-metadata?(word-slice, "disp8"))
3026     # . eax = has-metadata?(ecx, "disp8")
3027     # . . push args
3028     68/push  "disp8"/imm32
3029     51/push-ecx
3030     # . . call
3031     e8/call  has-metadata?/disp32
3032     # . . discard args
3033     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3034     # . if (eax == false) loop
3035     3d/compare-eax-and  0/imm32/false
3036     0f 84/jump-if-=  $emit-disp:loop/disp32
3037 $emit-disp:disp8:
3038     # emit(out, word-slice, 1)
3039     # . . push args
3040     68/push  1/imm32
3041     51/push-ecx
3042     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3043     # . . call
3044     e8/call  emit/disp32
3045     # . . discard args
3046     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3047     # break
3048 $emit-disp:break:
3049     # . restore locals
3050     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3051     # . restore registers
3052     5a/pop-to-edx
3053     59/pop-to-ecx
3054     58/pop-to-eax
3055     # . epilogue
3056     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3057     5d/pop-to-ebp
3058     c3/return
3059 
3060 emit-imm:  # line: (addr stream byte), out: (addr buffered-file)
3061     # pseudocode:
3062     #   rewind-stream(line)
3063     #   var word-slice: slice
3064     #   while true
3065     #     word-slice = next-word(line)
3066     #     if (slice-empty?(word-slice)) break
3067     #     if (slice-starts-with?(word-slice, "#")) break
3068     #     if has-metadata?(word-slice, "imm32")
3069     #       emit(out, word-slice, 4)
3070     #       break
3071     #     if has-metadata?(word-slice, "imm16")
3072     #       emit(out, word-slice, 2)
3073     #       break
3074     #     if has-metadata?(word-slice, "imm8")
3075     #       emit(out, word-slice, 1)
3076     #       break
3077     #
3078     # . prologue
3079     55/push-ebp
3080     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3081     # . save registers
3082     50/push-eax
3083     51/push-ecx
3084     52/push-edx
3085     # var word-slice/ecx: slice
3086     68/push  0/imm32/end
3087     68/push  0/imm32/start
3088     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3089     # rewind-stream(line)
3090     # . . push args
3091     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3092     # . . call
3093     e8/call  rewind-stream/disp32
3094     # . . discard args
3095     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3096 +-- 26 lines: #?     # dump line -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3122 $emit-imm:loop:
3123     # next-word(line, word-slice)
3124     # . . push args
3125     51/push-ecx
3126     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3127     # . . call
3128     e8/call  next-word/disp32
3129     # . . discard args
3130     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3131 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3171 $emit-imm:check0:
3172     # if (slice-empty?(word-slice)) break
3173     # . eax = slice-empty?(word-slice)
3174     # . . push args
3175     51/push-ecx
3176     # . . call
3177     e8/call  slice-empty?/disp32
3178     # . . discard args
3179     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3180     # . if (eax != false) pass through
3181     3d/compare-eax-and  0/imm32/false
3182     0f 85/jump-if-!=  $emit-imm:break/disp32
3183 $emit-imm:check1:
3184     # if (slice-starts-with?(word-slice, "#")) break
3185     # . var start/edx: (addr byte) = slice->start
3186     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
3187     # . var c/eax: byte = *start
3188     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
3189     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
3190     # . if (c == '#') break
3191     3d/compare-eax-and  0x23/imm32/hash
3192     0f 84/jump-if-=  $emit-imm:break/disp32
3193 $emit-imm:check-for-imm32:
3194     # if (has-metadata?(word-slice, "imm32"))
3195     # . eax = has-metadata?(ecx, "imm32")
3196     # . . push args
3197     68/push  "imm32"/imm32
3198     51/push-ecx
3199     # . . call
3200     e8/call  has-metadata?/disp32
3201     # . . discard args
3202     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3203     # . if (eax == false) goto next check
3204     3d/compare-eax-and  0/imm32/false
3205     74/jump-if-=  $emit-imm:check-for-imm16/disp8
3206 $emit-imm:imm32:
3207     # emit(out, word-slice, 4)
3208     # . . push args
3209     68/push  4/imm32
3210     51/push-ecx
3211     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3212     # . . call
3213     e8/call  emit/disp32
3214     # . . discard args
3215     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3216     # break
3217     e9/jump  $emit-imm:break/disp32
3218 $emit-imm:check-for-imm16:
3219     # if (has-metadata?(word-slice, "imm16"))
3220     # . eax = has-metadata?(ecx, "imm16")
3221     # . . push args
3222     68/push  "imm16"/imm32
3223     51/push-ecx
3224     # . . call
3225     e8/call  has-metadata?/disp32
3226     # . . discard args
3227     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3228     # . if (eax == false) goto next check
3229     3d/compare-eax-and  0/imm32/false
3230     74/jump-if-=  $emit-imm:check-for-imm8/disp8
3231 $emit-imm:imm16:
3232     # emit(out, word-slice, 2)
3233     # . . push args
3234     68/push  2/imm32
3235     51/push-ecx
3236     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3237     # . . call
3238     e8/call  emit/disp32
3239     # . . discard args
3240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3241     # break
3242     e9/jump  $emit-imm:break/disp32
3243 $emit-imm:check-for-imm8:
3244     # if (has-metadata?(word-slice, "imm8"))
3245     # . eax = has-metadata?(ecx, "imm8")
3246     # . . push args
3247     68/push  "imm8"/imm32
3248     51/push-ecx
3249     # . . call
3250     e8/call  has-metadata?/disp32
3251     # . . discard args
3252     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3253     # . if (eax == false) loop
3254     3d/compare-eax-and  0/imm32/false
3255     0f 84/jump-if-=  $emit-imm:loop/disp32
3256 $emit-imm:imm8:
3257     # emit(out, word-slice, 1)
3258     # . . push args
3259     68/push  1/imm32
3260     51/push-ecx
3261     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3262     # . . call
3263     e8/call  emit/disp32
3264     # . . discard args
3265     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3266     # break
3267 $emit-imm:break:
3268     # . restore locals
3269     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3270     # . restore registers
3271     5a/pop-to-edx
3272     59/pop-to-ecx
3273     58/pop-to-eax
3274     # . epilogue
3275     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3276     5d/pop-to-ebp
3277     c3/return
3278 
3279 emit-line-in-comment:  # line: (addr stream byte), out: (addr buffered-file)
3280     # . prologue
3281     55/push-ebp
3282     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3283     # write-buffered(out, " # ")
3284     # . . push args
3285     68/push  " # "/imm32
3286     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3287     # . . call
3288     e8/call  write-buffered/disp32
3289     # . . discard args
3290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3291     # write-stream-data(out, line)
3292     # . . push args
3293     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3294     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3295     # . . call
3296     e8/call  write-stream-data/disp32
3297     # . . discard args
3298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3299 $emit-line-in-comment:end:
3300     # . epilogue
3301     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3302     5d/pop-to-ebp
3303     c3/return
3304 
3305 test-convert-instruction-passes-comments-through:
3306     # if a line starts with '#', pass it along unchanged
3307     # . prologue
3308     55/push-ebp
3309     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3310     # setup
3311     # . clear-stream(_test-input-stream)
3312     # . . push args
3313     68/push  _test-input-stream/imm32
3314     # . . call
3315     e8/call  clear-stream/disp32
3316     # . . discard args
3317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3318     # . clear-stream(_test-output-stream)
3319     # . . push args
3320     68/push  _test-output-stream/imm32
3321     # . . call
3322     e8/call  clear-stream/disp32
3323     # . . discard args
3324     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3325     # . clear-stream($_test-output-buffered-file->buffer)
3326     # . . push args
3327     68/push  $_test-output-buffered-file->buffer/imm32
3328     # . . call
3329     e8/call  clear-stream/disp32
3330     # . . discard args
3331     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3332     # initialize input
3333     # . write(_test-input-stream, "# abcd")
3334     # . . push args
3335     68/push  "# abcd"/imm32
3336     68/push  _test-input-stream/imm32
3337     # . . call
3338     e8/call  write/disp32
3339     # . . discard args
3340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3341     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3342     # . . push args
3343     68/push  _test-output-buffered-file/imm32
3344     68/push  _test-input-stream/imm32
3345     # . . call
3346     e8/call  convert-instruction/disp32
3347     # . . discard args
3348     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3349     # check that the line just passed through
3350     # . flush(_test-output-buffered-file)
3351     # . . push args
3352     68/push  _test-output-buffered-file/imm32
3353     # . . call
3354     e8/call  flush/disp32
3355     # . . discard args
3356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3357     # . check-stream-equal(_test-output-stream, "# abcd", msg)
3358     # . . push args
3359     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
3360     68/push  "# abcd"/imm32
3361     68/push  _test-output-stream/imm32
3362     # . . call
3363     e8/call  check-stream-equal/disp32
3364     # . . discard args
3365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3366     # . epilogue
3367     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3368     5d/pop-to-ebp
3369     c3/return
3370 
3371 test-convert-instruction-passes-labels-through:
3372     # if the first word ends with ':', pass along the entire line unchanged
3373     # . prologue
3374     55/push-ebp
3375     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3376     # setup
3377     # . clear-stream(_test-input-stream)
3378     # . . push args
3379     68/push  _test-input-stream/imm32
3380     # . . call
3381     e8/call  clear-stream/disp32
3382     # . . discard args
3383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3384     # . clear-stream(_test-output-stream)
3385     # . . push args
3386     68/push  _test-output-stream/imm32
3387     # . . call
3388     e8/call  clear-stream/disp32
3389     # . . discard args
3390     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3391     # . clear-stream($_test-output-buffered-file->buffer)
3392     # . . push args
3393     68/push  $_test-output-buffered-file->buffer/imm32
3394     # . . call
3395     e8/call  clear-stream/disp32
3396     # . . discard args
3397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3398     # initialize input
3399     # . write(_test-input-stream, "ab: # cd")
3400     # . . push args
3401     68/push  "ab: # cd"/imm32
3402     68/push  _test-input-stream/imm32
3403     # . . call
3404     e8/call  write/disp32
3405     # . . discard args
3406     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3407     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3408     # . . push args
3409     68/push  _test-output-buffered-file/imm32
3410     68/push  _test-input-stream/imm32
3411     # . . call
3412     e8/call  convert-instruction/disp32
3413     # . . discard args
3414     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3415     # check that the line just passed through
3416     # . flush(_test-output-buffered-file)
3417     # . . push args
3418     68/push  _test-output-buffered-file/imm32
3419     # . . call
3420     e8/call  flush/disp32
3421     # . . discard args
3422     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3423     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
3424     # . . push args
3425     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
3426     68/push  "ab: # cd"/imm32
3427     68/push  _test-output-stream/imm32
3428     # . . call
3429     e8/call  check-stream-equal/disp32
3430     # . . discard args
3431     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3432     # . epilogue
3433     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3434     5d/pop-to-ebp
3435     c3/return
3436 
3437 test-convert-instruction-handles-single-opcode:
3438     # if the instruction consists of a single opcode, strip its metadata and pass it along
3439     # . prologue
3440     55/push-ebp
3441     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3442     # setup
3443     # . clear-stream(_test-input-stream)
3444     # . . push args
3445     68/push  _test-input-stream/imm32
3446     # . . call
3447     e8/call  clear-stream/disp32
3448     # . . discard args
3449     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3450     # . clear-stream(_test-output-stream)
3451     # . . push args
3452     68/push  _test-output-stream/imm32
3453     # . . call
3454     e8/call  clear-stream/disp32
3455     # . . discard args
3456     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3457     # . clear-stream($_test-output-buffered-file->buffer)
3458     # . . push args
3459     68/push  $_test-output-buffered-file->buffer/imm32
3460     # . . call
3461     e8/call  clear-stream/disp32
3462     # . . discard args
3463     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3464     # initialize input
3465     # . write(_test-input-stream, "ab/cd # comment")
3466     # . . push args
3467     68/push  "ab/cd # comment"/imm32
3468     68/push  _test-input-stream/imm32
3469     # . . call
3470     e8/call  write/disp32
3471     # . . discard args
3472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3473     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3474     # . . push args
3475     68/push  _test-output-buffered-file/imm32
3476     68/push  _test-input-stream/imm32
3477     # . . call
3478     e8/call  convert-instruction/disp32
3479     # . . discard args
3480     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3481     # check output
3482     # . flush(_test-output-buffered-file)
3483     # . . push args
3484     68/push  _test-output-buffered-file/imm32
3485     # . . call
3486     e8/call  flush/disp32
3487     # . . discard args
3488     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3489 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3515     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
3516     # . . push args
3517     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
3518     68/push  "ab  # ab/cd # comment"/imm32
3519     68/push  _test-output-stream/imm32
3520     # . . call
3521     e8/call  check-stream-equal/disp32
3522     # . . discard args
3523     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3524     # . epilogue
3525     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3526     5d/pop-to-ebp
3527     c3/return
3528 
3529 test-convert-instruction-handles-0f-opcode:
3530     # if the instruction starts with 0f opcode, include a second opcode
3531     # . prologue
3532     55/push-ebp
3533     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3534     # setup
3535     # . clear-stream(_test-input-stream)
3536     # . . push args
3537     68/push  _test-input-stream/imm32
3538     # . . call
3539     e8/call  clear-stream/disp32
3540     # . . discard args
3541     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3542     # . clear-stream(_test-output-stream)
3543     # . . push args
3544     68/push  _test-output-stream/imm32
3545     # . . call
3546     e8/call  clear-stream/disp32
3547     # . . discard args
3548     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3549     # . clear-stream($_test-output-buffered-file->buffer)
3550     # . . push args
3551     68/push  $_test-output-buffered-file->buffer/imm32
3552     # . . call
3553     e8/call  clear-stream/disp32
3554     # . . discard args
3555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3556     # initialize input
3557     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
3558     # . . push args
3559     68/push  "0f/m1 ab/m2 # comment"/imm32
3560     68/push  _test-input-stream/imm32
3561     # . . call
3562     e8/call  write/disp32
3563     # . . discard args
3564     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3565     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3566     # . . push args
3567     68/push  _test-output-buffered-file/imm32
3568     68/push  _test-input-stream/imm32
3569     # . . call
3570     e8/call  convert-instruction/disp32
3571     # . . discard args
3572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3573     # check output
3574     # . flush(_test-output-buffered-file)
3575     # . . push args
3576     68/push  _test-output-buffered-file/imm32
3577     # . . call
3578     e8/call  flush/disp32
3579     # . . discard args
3580     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3581 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3607     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
3608     # . . push args
3609     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
3610     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
3611     68/push  _test-output-stream/imm32
3612     # . . call
3613     e8/call  check-stream-equal/disp32
3614     # . . discard args
3615     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3616     # . epilogue
3617     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3618     5d/pop-to-ebp
3619     c3/return
3620 
3621 test-convert-instruction-handles-f2-opcode:
3622     # if the instruction starts with f2 opcode, include a second opcode
3623     # . prologue
3624     55/push-ebp
3625     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3626     # setup
3627     # . clear-stream(_test-input-stream)
3628     # . . push args
3629     68/push  _test-input-stream/imm32
3630     # . . call
3631     e8/call  clear-stream/disp32
3632     # . . discard args
3633     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3634     # . clear-stream(_test-output-stream)
3635     # . . push args
3636     68/push  _test-output-stream/imm32
3637     # . . call
3638     e8/call  clear-stream/disp32
3639     # . . discard args
3640     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3641     # . clear-stream($_test-output-buffered-file->buffer)
3642     # . . push args
3643     68/push  $_test-output-buffered-file->buffer/imm32
3644     # . . call
3645     e8/call  clear-stream/disp32
3646     # . . discard args
3647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3648     # initialize input
3649     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
3650     # . . push args
3651     68/push  "f2/m1 ab/m2 # comment"/imm32
3652     68/push  _test-input-stream/imm32
3653     # . . call
3654     e8/call  write/disp32
3655     # . . discard args
3656     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3657     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3658     # . . push args
3659     68/push  _test-output-buffered-file/imm32
3660     68/push  _test-input-stream/imm32
3661     # . . call
3662     e8/call  convert-instruction/disp32
3663     # . . discard args
3664     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3665     # check output
3666     # . flush(_test-output-buffered-file)
3667     # . . push args
3668     68/push  _test-output-buffered-file/imm32
3669     # . . call
3670     e8/call  flush/disp32
3671     # . . discard args
3672     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3673 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3699     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
3700     # . . push args
3701     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
3702     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
3703     68/push  _test-output-stream/imm32
3704     # . . call
3705     e8/call  check-stream-equal/disp32
3706     # . . discard args
3707     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3708     # . epilogue
3709     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3710     5d/pop-to-ebp
3711     c3/return
3712 
3713 test-convert-instruction-handles-f3-opcode:
3714     # if the instruction starts with f3 opcode, include a second opcode
3715     # . prologue
3716     55/push-ebp
3717     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3718     # setup
3719     # . clear-stream(_test-input-stream)
3720     # . . push args
3721     68/push  _test-input-stream/imm32
3722     # . . call
3723     e8/call  clear-stream/disp32
3724     # . . discard args
3725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3726     # . clear-stream(_test-output-stream)
3727     # . . push args
3728     68/push  _test-output-stream/imm32
3729     # . . call
3730     e8/call  clear-stream/disp32
3731     # . . discard args
3732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3733     # . clear-stream($_test-output-buffered-file->buffer)
3734     # . . push args
3735     68/push  $_test-output-buffered-file->buffer/imm32
3736     # . . call
3737     e8/call  clear-stream/disp32
3738     # . . discard args
3739     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3740     # initialize input
3741     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
3742     # . . push args
3743     68/push  "f3/m1 ab/m2 # comment"/imm32
3744     68/push  _test-input-stream/imm32
3745     # . . call
3746     e8/call  write/disp32
3747     # . . discard args
3748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3749     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3750     # . . push args
3751     68/push  _test-output-buffered-file/imm32
3752     68/push  _test-input-stream/imm32
3753     # . . call
3754     e8/call  convert-instruction/disp32
3755     # . . discard args
3756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3757     # check output
3758     # . flush(_test-output-buffered-file)
3759     # . . push args
3760     68/push  _test-output-buffered-file/imm32
3761     # . . call
3762     e8/call  flush/disp32
3763     # . . discard args
3764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3765 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3791     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
3792     # . . push args
3793     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
3794     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
3795     68/push  _test-output-stream/imm32
3796     # . . call
3797     e8/call  check-stream-equal/disp32
3798     # . . discard args
3799     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3800     # . epilogue
3801     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3802     5d/pop-to-ebp
3803     c3/return
3804 
3805 test-convert-instruction-handles-f2-0f-opcode:
3806     # if the instruction starts with f2 0f opcode, include a second opcode
3807     # . prologue
3808     55/push-ebp
3809     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3810     # setup
3811     # . clear-stream(_test-input-stream)
3812     # . . push args
3813     68/push  _test-input-stream/imm32
3814     # . . call
3815     e8/call  clear-stream/disp32
3816     # . . discard args
3817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3818     # . clear-stream(_test-output-stream)
3819     # . . push args
3820     68/push  _test-output-stream/imm32
3821     # . . call
3822     e8/call  clear-stream/disp32
3823     # . . discard args
3824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3825     # . clear-stream($_test-output-buffered-file->buffer)
3826     # . . push args
3827     68/push  $_test-output-buffered-file->buffer/imm32
3828     # . . call
3829     e8/call  clear-stream/disp32
3830     # . . discard args
3831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3832     # initialize input
3833     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
3834     # . . push args
3835     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
3836     68/push  _test-input-stream/imm32
3837     # . . call
3838     e8/call  write/disp32
3839     # . . discard args
3840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3841     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3842     # . . push args
3843     68/push  _test-output-buffered-file/imm32
3844     68/push  _test-input-stream/imm32
3845     # . . call
3846     e8/call  convert-instruction/disp32
3847     # . . discard args
3848     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3849     # check output
3850     # . flush(_test-output-buffered-file)
3851     # . . push args
3852     68/push  _test-output-buffered-file/imm32
3853     # . . call
3854     e8/call  flush/disp32
3855     # . . discard args
3856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3857 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3883     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
3884     # . . push args
3885     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
3886     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
3887     68/push  _test-output-stream/imm32
3888     # . . call
3889     e8/call  check-stream-equal/disp32
3890     # . . discard args
3891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3892     # . epilogue
3893     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3894     5d/pop-to-ebp
3895     c3/return
3896 
3897 test-convert-instruction-handles-f3-0f-opcode:
3898     # if the instruction starts with f3 0f opcode, include a second opcode
3899     # . prologue
3900     55/push-ebp
3901     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3902     # setup
3903     # . clear-stream(_test-input-stream)
3904     # . . push args
3905     68/push  _test-input-stream/imm32
3906     # . . call
3907     e8/call  clear-stream/disp32
3908     # . . discard args
3909     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3910     # . clear-stream(_test-output-stream)
3911     # . . push args
3912     68/push  _test-output-stream/imm32
3913     # . . call
3914     e8/call  clear-stream/disp32
3915     # . . discard args
3916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3917     # . clear-stream($_test-output-buffered-file->buffer)
3918     # . . push args
3919     68/push  $_test-output-buffered-file->buffer/imm32
3920     # . . call
3921     e8/call  clear-stream/disp32
3922     # . . discard args
3923     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3924     # initialize input
3925     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
3926     # . . push args
3927     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
3928     68/push  _test-input-stream/imm32
3929     # . . call
3930     e8/call  write/disp32
3931     # . . discard args
3932     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3933     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3934     # . . push args
3935     68/push  _test-output-buffered-file/imm32
3936     68/push  _test-input-stream/imm32
3937     # . . call
3938     e8/call  convert-instruction/disp32
3939     # . . discard args
3940     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3941     # check output
3942     # . flush(_test-output-buffered-file)
3943     # . . push args
3944     68/push  _test-output-buffered-file/imm32
3945     # . . call
3946     e8/call  flush/disp32
3947     # . . discard args
3948     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3949 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3975     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
3976     # . . push args
3977     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
3978     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
3979     68/push  _test-output-stream/imm32
3980     # . . call
3981     e8/call  check-stream-equal/disp32
3982     # . . discard args
3983     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3984     # . epilogue
3985     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3986     5d/pop-to-ebp
3987     c3/return
3988 
3989 test-convert-instruction-handles-unused-opcodes:
3990     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
3991     # . prologue
3992     55/push-ebp
3993     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3994     # setup
3995     # . clear-stream(_test-input-stream)
3996     # . . push args
3997     68/push  _test-input-stream/imm32
3998     # . . call
3999     e8/call  clear-stream/disp32
4000     # . . discard args
4001     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4002     # . clear-stream(_test-output-stream)
4003     # . . push args
4004     68/push  _test-output-stream/imm32
4005     # . . call
4006     e8/call  clear-stream/disp32
4007     # . . discard args
4008     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4009     # . clear-stream($_test-output-buffered-file->buffer)
4010     # . . push args
4011     68/push  $_test-output-buffered-file->buffer/imm32
4012     # . . call
4013     e8/call  clear-stream/disp32
4014     # . . discard args
4015     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4016     # initialize input
4017     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
4018     # . . push args
4019     68/push  "ab/m1 cd/m2 # comment"/imm32
4020     68/push  _test-input-stream/imm32
4021     # . . call
4022     e8/call  write/disp32
4023     # . . discard args
4024     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4025     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4026     # . . push args
4027     68/push  _test-output-buffered-file/imm32
4028     68/push  _test-input-stream/imm32
4029     # . . call
4030     e8/call  convert-instruction/disp32
4031     # . . discard args
4032     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4033     # check output
4034     # . flush(_test-output-buffered-file)
4035     # . . push args
4036     68/push  _test-output-buffered-file/imm32
4037     # . . call
4038     e8/call  flush/disp32
4039     # . . discard args
4040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4041 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4067     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4068     # . . push args
4069     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
4070     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
4071     68/push  _test-output-stream/imm32
4072     # . . call
4073     e8/call  check-stream-equal/disp32
4074     # . . discard args
4075     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4076     # . epilogue
4077     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4078     5d/pop-to-ebp
4079     c3/return
4080 
4081 test-convert-instruction-handles-unused-second-opcodes:
4082     # if the second opcode isn't 0f, don't include further opcodes
4083     # . prologue
4084     55/push-ebp
4085     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4086     # setup
4087     # . clear-stream(_test-input-stream)
4088     # . . push args
4089     68/push  _test-input-stream/imm32
4090     # . . call
4091     e8/call  clear-stream/disp32
4092     # . . discard args
4093     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4094     # . clear-stream(_test-output-stream)
4095     # . . push args
4096     68/push  _test-output-stream/imm32
4097     # . . call
4098     e8/call  clear-stream/disp32
4099     # . . discard args
4100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4101     # . clear-stream($_test-output-buffered-file->buffer)
4102     # . . push args
4103     68/push  $_test-output-buffered-file->buffer/imm32
4104     # . . call
4105     e8/call  clear-stream/disp32
4106     # . . discard args
4107     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4108     # initialize input
4109     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
4110     # . . push args
4111     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
4112     68/push  _test-input-stream/imm32
4113     # . . call
4114     e8/call  write/disp32
4115     # . . discard args
4116     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4117     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4118     # . . push args
4119     68/push  _test-output-buffered-file/imm32
4120     68/push  _test-input-stream/imm32
4121     # . . call
4122     e8/call  convert-instruction/disp32
4123     # . . discard args
4124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4125     # check output
4126     # . flush(_test-output-buffered-file)
4127     # . . push args
4128     68/push  _test-output-buffered-file/imm32
4129     # . . call
4130     e8/call  flush/disp32
4131     # . . discard args
4132     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4133 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4159     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
4160     # . . push args
4161     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4162     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
4163     68/push  _test-output-stream/imm32
4164     # . . call
4165     e8/call  check-stream-equal/disp32
4166     # . . discard args
4167     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4168     # . epilogue
4169     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4170     5d/pop-to-ebp
4171     c3/return
4172 
4173 test-convert-instruction-handles-unused-second-opcodes-2:
4174     # if the second opcode isn't 0f, don't include further opcodes
4175     # . prologue
4176     55/push-ebp
4177     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4178     # setup
4179     # . clear-stream(_test-input-stream)
4180     # . . push args
4181     68/push  _test-input-stream/imm32
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     # . clear-stream(_test-output-stream)
4187     # . . push args
4188     68/push  _test-output-stream/imm32
4189     # . . call
4190     e8/call  clear-stream/disp32
4191     # . . discard args
4192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4193     # . clear-stream($_test-output-buffered-file->buffer)
4194     # . . push args
4195     68/push  $_test-output-buffered-file->buffer/imm32
4196     # . . call
4197     e8/call  clear-stream/disp32
4198     # . . discard args
4199     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4200     # initialize input
4201     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
4202     # . . push args
4203     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
4204     68/push  _test-input-stream/imm32
4205     # . . call
4206     e8/call  write/disp32
4207     # . . discard args
4208     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4209     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4210     # . . push args
4211     68/push  _test-output-buffered-file/imm32
4212     68/push  _test-input-stream/imm32
4213     # . . call
4214     e8/call  convert-instruction/disp32
4215     # . . discard args
4216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4217     # check output
4218     # . flush(_test-output-buffered-file)
4219     # . . push args
4220     68/push  _test-output-buffered-file/imm32
4221     # . . call
4222     e8/call  flush/disp32
4223     # . . discard args
4224     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4225 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4251     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
4252     # . . push args
4253     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4254     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
4255     68/push  _test-output-stream/imm32
4256     # . . call
4257     e8/call  check-stream-equal/disp32
4258     # . . discard args
4259     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4260     # . epilogue
4261     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4262     5d/pop-to-ebp
4263     c3/return
4264 
4265 test-convert-instruction-emits-modrm-byte:
4266     # pack mod, rm32 and r32 operands into ModR/M byte
4267     # . prologue
4268     55/push-ebp
4269     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4270     # setup
4271     # . clear-stream(_test-input-stream)
4272     # . . push args
4273     68/push  _test-input-stream/imm32
4274     # . . call
4275     e8/call  clear-stream/disp32
4276     # . . discard args
4277     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4278     # . clear-stream(_test-output-stream)
4279     # . . push args
4280     68/push  _test-output-stream/imm32
4281     # . . call
4282     e8/call  clear-stream/disp32
4283     # . . discard args
4284     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4285     # . clear-stream($_test-output-buffered-file->buffer)
4286     # . . push args
4287     68/push  $_test-output-buffered-file->buffer/imm32
4288     # . . call
4289     e8/call  clear-stream/disp32
4290     # . . discard args
4291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4292     # initialize input
4293     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
4294     # . . push args
4295     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
4296     68/push  _test-input-stream/imm32
4297     # . . call
4298     e8/call  write/disp32
4299     # . . discard args
4300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4301     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4302     # . . push args
4303     68/push  _test-output-buffered-file/imm32
4304     68/push  _test-input-stream/imm32
4305     # . . call
4306     e8/call  convert-instruction/disp32
4307     # . . discard args
4308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4309     # check output
4310     # . flush(_test-output-buffered-file)
4311     # . . push args
4312     68/push  _test-output-buffered-file/imm32
4313     # . . call
4314     e8/call  flush/disp32
4315     # . . discard args
4316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4317 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4343     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
4344     # . . push args
4345     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
4346     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
4347     68/push  _test-output-stream/imm32
4348     # . . call
4349     e8/call  check-stream-equal/disp32
4350     # . . discard args
4351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4352     # . epilogue
4353     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4354     5d/pop-to-ebp
4355     c3/return
4356 
4357 test-convert-instruction-emits-modrm-byte-with-non-zero-mod:
4358     # . prologue
4359     55/push-ebp
4360     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4361     # setup
4362     # . clear-stream(_test-input-stream)
4363     # . . push args
4364     68/push  _test-input-stream/imm32
4365     # . . call
4366     e8/call  clear-stream/disp32
4367     # . . discard args
4368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4369     # . clear-stream(_test-output-stream)
4370     # . . push args
4371     68/push  _test-output-stream/imm32
4372     # . . call
4373     e8/call  clear-stream/disp32
4374     # . . discard args
4375     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4376     # . clear-stream($_test-output-buffered-file->buffer)
4377     # . . push args
4378     68/push  $_test-output-buffered-file->buffer/imm32
4379     # . . call
4380     e8/call  clear-stream/disp32
4381     # . . discard args
4382     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4383     # initialize input
4384     # . write(_test-input-stream, "01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx")
4385     # . . push args
4386     68/push  "01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx"/imm32
4387     68/push  _test-input-stream/imm32
4388     # . . call
4389     e8/call  write/disp32
4390     # . . discard args
4391     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4392     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4393     # . . push args
4394     68/push  _test-output-buffered-file/imm32
4395     68/push  _test-input-stream/imm32
4396     # . . call
4397     e8/call  convert-instruction/disp32
4398     # . . discard args
4399     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4400     # . flush(_test-output-buffered-file)
4401     # . . push args
4402     68/push  _test-output-buffered-file/imm32
4403     # . . call
4404     e8/call  flush/disp32
4405     # . . discard args
4406     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4407 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4433     # check output
4434     # . check-stream-equal(_test-output-stream, "# abcd", msg)
4435     # . . push args
4436     68/push  "F - test-convert-instruction-foo"/imm32
4437     68/push  "01 cb  # 01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx"/imm32
4438     68/push  _test-output-stream/imm32
4439     # . . call
4440     e8/call  check-stream-equal/disp32
4441     # . . discard args
4442     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4443     # . epilogue
4444     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4445     5d/pop-to-ebp
4446     c3/return
4447 
4448 test-convert-instruction-emits-modrm-byte-from-subop:
4449     # pack mod, rm32 and subop operands into ModR/M byte
4450     # . prologue
4451     55/push-ebp
4452     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4453     # setup
4454     # . clear-stream(_test-input-stream)
4455     # . . push args
4456     68/push  _test-input-stream/imm32
4457     # . . call
4458     e8/call  clear-stream/disp32
4459     # . . discard args
4460     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4461     # . clear-stream(_test-output-stream)
4462     # . . push args
4463     68/push  _test-output-stream/imm32
4464     # . . call
4465     e8/call  clear-stream/disp32
4466     # . . discard args
4467     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4468     # . clear-stream($_test-output-buffered-file->buffer)
4469     # . . push args
4470     68/push  $_test-output-buffered-file->buffer/imm32
4471     # . . call
4472     e8/call  clear-stream/disp32
4473     # . . discard args
4474     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4475     # initialize input
4476     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
4477     # . . push args
4478     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
4479     68/push  _test-input-stream/imm32
4480     # . . call
4481     e8/call  write/disp32
4482     # . . discard args
4483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4484     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4485     # . . push args
4486     68/push  _test-output-buffered-file/imm32
4487     68/push  _test-input-stream/imm32
4488     # . . call
4489     e8/call  convert-instruction/disp32
4490     # . . discard args
4491     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4492     # check output
4493     # . flush(_test-output-buffered-file)
4494     # . . push args
4495     68/push  _test-output-buffered-file/imm32
4496     # . . call
4497     e8/call  flush/disp32
4498     # . . discard args
4499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4500 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4526     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
4527     # . . push args
4528     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
4529     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
4530     68/push  _test-output-stream/imm32
4531     # . . call
4532     e8/call  check-stream-equal/disp32
4533     # . . discard args
4534     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4535     # . epilogue
4536     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4537     5d/pop-to-ebp
4538     c3/return
4539 
4540 test-convert-instruction-emits-modrm-byte-with-missing-mod:
4541     # pack rm32 and r32 operands into ModR/M byte
4542     # . prologue
4543     55/push-ebp
4544     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4545     # setup
4546     # . clear-stream(_test-input-stream)
4547     # . . push args
4548     68/push  _test-input-stream/imm32
4549     # . . call
4550     e8/call  clear-stream/disp32
4551     # . . discard args
4552     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4553     # . clear-stream(_test-output-stream)
4554     # . . push args
4555     68/push  _test-output-stream/imm32
4556     # . . call
4557     e8/call  clear-stream/disp32
4558     # . . discard args
4559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4560     # . clear-stream($_test-output-buffered-file->buffer)
4561     # . . push args
4562     68/push  $_test-output-buffered-file->buffer/imm32
4563     # . . call
4564     e8/call  clear-stream/disp32
4565     # . . discard args
4566     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4567     # initialize input
4568     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
4569     # . . push args
4570     68/push  "8b/copy 0/rm32 1/r32"/imm32
4571     68/push  _test-input-stream/imm32
4572     # . . call
4573     e8/call  write/disp32
4574     # . . discard args
4575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4576     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4577     # . . push args
4578     68/push  _test-output-buffered-file/imm32
4579     68/push  _test-input-stream/imm32
4580     # . . call
4581     e8/call  convert-instruction/disp32
4582     # . . discard args
4583     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4584     # check output
4585     # . flush(_test-output-buffered-file)
4586     # . . push args
4587     68/push  _test-output-buffered-file/imm32
4588     # . . call
4589     e8/call  flush/disp32
4590     # . . discard args
4591     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4592 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4618     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
4619     # . . push args
4620     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
4621     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
4622     68/push  _test-output-stream/imm32
4623     # . . call
4624     e8/call  check-stream-equal/disp32
4625     # . . discard args
4626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4627     # . epilogue
4628     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4629     5d/pop-to-ebp
4630     c3/return
4631 
4632 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
4633     # pack mod and r32 operands into ModR/M byte
4634     # . prologue
4635     55/push-ebp
4636     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4637     # setup
4638     # . clear-stream(_test-input-stream)
4639     # . . push args
4640     68/push  _test-input-stream/imm32
4641     # . . call
4642     e8/call  clear-stream/disp32
4643     # . . discard args
4644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4645     # . clear-stream(_test-output-stream)
4646     # . . push args
4647     68/push  _test-output-stream/imm32
4648     # . . call
4649     e8/call  clear-stream/disp32
4650     # . . discard args
4651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4652     # . clear-stream($_test-output-buffered-file->buffer)
4653     # . . push args
4654     68/push  $_test-output-buffered-file->buffer/imm32
4655     # . . call
4656     e8/call  clear-stream/disp32
4657     # . . discard args
4658     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4659     # initialize input
4660     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
4661     # . . push args
4662     68/push  "8b/copy 0/mod 1/r32"/imm32
4663     68/push  _test-input-stream/imm32
4664     # . . call
4665     e8/call  write/disp32
4666     # . . discard args
4667     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4668     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4669     # . . push args
4670     68/push  _test-output-buffered-file/imm32
4671     68/push  _test-input-stream/imm32
4672     # . . call
4673     e8/call  convert-instruction/disp32
4674     # . . discard args
4675     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4676     # check output
4677     # . flush(_test-output-buffered-file)
4678     # . . push args
4679     68/push  _test-output-buffered-file/imm32
4680     # . . call
4681     e8/call  flush/disp32
4682     # . . discard args
4683     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4684 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4710     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
4711     # . . push args
4712     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
4713     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
4714     68/push  _test-output-stream/imm32
4715     # . . call
4716     e8/call  check-stream-equal/disp32
4717     # . . discard args
4718     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4719     # . epilogue
4720     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4721     5d/pop-to-ebp
4722     c3/return
4723 
4724 test-convert-instruction-emits-modrm-byte-with-missing-r32:
4725     # pack mod and rm32 operands into ModR/M byte
4726     # . prologue
4727     55/push-ebp
4728     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4729     # setup
4730     # . clear-stream(_test-input-stream)
4731     # . . push args
4732     68/push  _test-input-stream/imm32
4733     # . . call
4734     e8/call  clear-stream/disp32
4735     # . . discard args
4736     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4737     # . clear-stream(_test-output-stream)
4738     # . . push args
4739     68/push  _test-output-stream/imm32
4740     # . . call
4741     e8/call  clear-stream/disp32
4742     # . . discard args
4743     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4744     # . clear-stream($_test-output-buffered-file->buffer)
4745     # . . push args
4746     68/push  $_test-output-buffered-file->buffer/imm32
4747     # . . call
4748     e8/call  clear-stream/disp32
4749     # . . discard args
4750     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4751     # initialize input
4752     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
4753     # . . push args
4754     68/push  "8b/copy 0/mod 0/rm32"/imm32
4755     68/push  _test-input-stream/imm32
4756     # . . call
4757     e8/call  write/disp32
4758     # . . discard args
4759     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4760     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4761     # . . push args
4762     68/push  _test-output-buffered-file/imm32
4763     68/push  _test-input-stream/imm32
4764     # . . call
4765     e8/call  convert-instruction/disp32
4766     # . . discard args
4767     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4768     # check output
4769     # . flush(_test-output-buffered-file)
4770     # . . push args
4771     68/push  _test-output-buffered-file/imm32
4772     # . . call
4773     e8/call  flush/disp32
4774     # . . discard args
4775     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4776 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4802     # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
4803     # . . push args
4804     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
4805     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
4806     68/push  _test-output-stream/imm32
4807     # . . call
4808     e8/call  check-stream-equal/disp32
4809     # . . discard args
4810     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4811     # . epilogue
4812     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4813     5d/pop-to-ebp
4814     c3/return
4815 
4816 test-convert-instruction-emits-sib-byte:
4817     # pack base, index and scale operands into SIB byte
4818     # . prologue
4819     55/push-ebp
4820     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4821     # setup
4822     # . clear-stream(_test-input-stream)
4823     # . . push args
4824     68/push  _test-input-stream/imm32
4825     # . . call
4826     e8/call  clear-stream/disp32
4827     # . . discard args
4828     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4829     # . clear-stream(_test-output-stream)
4830     # . . push args
4831     68/push  _test-output-stream/imm32
4832     # . . call
4833     e8/call  clear-stream/disp32
4834     # . . discard args
4835     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4836     # . clear-stream($_test-output-buffered-file->buffer)
4837     # . . push args
4838     68/push  $_test-output-buffered-file->buffer/imm32
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 4/rm32 1/r32 0/base 1/index 0/scale")
4845     # . . push args
4846     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/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 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
4895     # . . push args
4896     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
4897     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/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     # . epilogue
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-scale:
4909     # pack base, index and scale operands into SIB byte
4910     # . prologue
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->buffer)
4929     # . . push args
4930     68/push  $_test-output-buffered-file->buffer/imm32
4931     # . . call
4932     e8/call  clear-stream/disp32
4933     # . . discard args
4934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4935     # initialize input
4936     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/scale")
4937     # . . push args
4938     68/push  "8b/copy 0/mod 4/rm32 1/scale"/imm32
4939     68/push  _test-input-stream/imm32
4940     # . . call
4941     e8/call  write/disp32
4942     # . . discard args
4943     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4944     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4945     # . . push args
4946     68/push  _test-output-buffered-file/imm32
4947     68/push  _test-input-stream/imm32
4948     # . . call
4949     e8/call  convert-instruction/disp32
4950     # . . discard args
4951     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4952     # check output
4953     # . flush(_test-output-buffered-file)
4954     # . . push args
4955     68/push  _test-output-buffered-file/imm32
4956     # . . call
4957     e8/call  flush/disp32
4958     # . . discard args
4959     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4960 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4986     # . check-stream-equal(_test-output-stream, "8b 04 40  # 8b/copy 0/mod 4/rm32 1/scale", msg)
4987     # . . push args
4988     68/push  "F - test-convert-instruction-emits-scale"/imm32
4989     68/push  "8b 04 40  # 8b/copy 0/mod 4/rm32 1/scale"/imm32
4990     68/push  _test-output-stream/imm32
4991     # . . call
4992     e8/call  check-stream-equal/disp32
4993     # . . discard args
4994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4995     # . epilogue
4996     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4997     5d/pop-to-ebp
4998     c3/return
4999 
5000 test-convert-instruction-emits-sib-byte-with-missing-base:
5001     # pack index and scale operands into SIB byte
5002     # . prologue
5003     55/push-ebp
5004     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5005     # setup
5006     # . clear-stream(_test-input-stream)
5007     # . . push args
5008     68/push  _test-input-stream/imm32
5009     # . . call
5010     e8/call  clear-stream/disp32
5011     # . . discard args
5012     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5013     # . clear-stream(_test-output-stream)
5014     # . . push args
5015     68/push  _test-output-stream/imm32
5016     # . . call
5017     e8/call  clear-stream/disp32
5018     # . . discard args
5019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5020     # . clear-stream($_test-output-buffered-file->buffer)
5021     # . . push args
5022     68/push  $_test-output-buffered-file->buffer/imm32
5023     # . . call
5024     e8/call  clear-stream/disp32
5025     # . . discard args
5026     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5027     # initialize input
5028     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
5029     # . . push args
5030     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5031     68/push  _test-input-stream/imm32
5032     # . . call
5033     e8/call  write/disp32
5034     # . . discard args
5035     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5036     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5037     # . . push args
5038     68/push  _test-output-buffered-file/imm32
5039     68/push  _test-input-stream/imm32
5040     # . . call
5041     e8/call  convert-instruction/disp32
5042     # . . discard args
5043     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5044     # check output
5045     # . flush(_test-output-buffered-file)
5046     # . . push args
5047     68/push  _test-output-buffered-file/imm32
5048     # . . call
5049     e8/call  flush/disp32
5050     # . . discard args
5051     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5052 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5078     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
5079     # . . push args
5080     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
5081     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5082     68/push  _test-output-stream/imm32
5083     # . . call
5084     e8/call  check-stream-equal/disp32
5085     # . . discard args
5086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5087     # . epilogue
5088     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5089     5d/pop-to-ebp
5090     c3/return
5091 
5092 test-convert-instruction-emits-sib-byte-with-missing-index:
5093     # pack base and scale operands into SIB byte
5094     # . prologue
5095     55/push-ebp
5096     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5097     # setup
5098     # . clear-stream(_test-input-stream)
5099     # . . push args
5100     68/push  _test-input-stream/imm32
5101     # . . call
5102     e8/call  clear-stream/disp32
5103     # . . discard args
5104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5105     # . clear-stream(_test-output-stream)
5106     # . . push args
5107     68/push  _test-output-stream/imm32
5108     # . . call
5109     e8/call  clear-stream/disp32
5110     # . . discard args
5111     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5112     # . clear-stream($_test-output-buffered-file->buffer)
5113     # . . push args
5114     68/push  $_test-output-buffered-file->buffer/imm32
5115     # . . call
5116     e8/call  clear-stream/disp32
5117     # . . discard args
5118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5119     # initialize input
5120     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
5121     # . . push args
5122     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5123     68/push  _test-input-stream/imm32
5124     # . . call
5125     e8/call  write/disp32
5126     # . . discard args
5127     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5128     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5129     # . . push args
5130     68/push  _test-output-buffered-file/imm32
5131     68/push  _test-input-stream/imm32
5132     # . . call
5133     e8/call  convert-instruction/disp32
5134     # . . discard args
5135     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5136     # check output
5137     # . flush(_test-output-buffered-file)
5138     # . . push args
5139     68/push  _test-output-buffered-file/imm32
5140     # . . call
5141     e8/call  flush/disp32
5142     # . . discard args
5143     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5144 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5170     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
5171     # . . push args
5172     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
5173     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5174     68/push  _test-output-stream/imm32
5175     # . . call
5176     e8/call  check-stream-equal/disp32
5177     # . . discard args
5178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5179     # . epilogue
5180     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5181     5d/pop-to-ebp
5182     c3/return
5183 
5184 test-convert-instruction-emits-sib-byte-with-missing-scale:
5185     # pack base and index operands into SIB byte
5186     # . prologue
5187     55/push-ebp
5188     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5189     # setup
5190     # . clear-stream(_test-input-stream)
5191     # . . push args
5192     68/push  _test-input-stream/imm32
5193     # . . call
5194     e8/call  clear-stream/disp32
5195     # . . discard args
5196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5197     # . clear-stream(_test-output-stream)
5198     # . . push args
5199     68/push  _test-output-stream/imm32
5200     # . . call
5201     e8/call  clear-stream/disp32
5202     # . . discard args
5203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5204     # . clear-stream($_test-output-buffered-file->buffer)
5205     # . . push args
5206     68/push  $_test-output-buffered-file->buffer/imm32
5207     # . . call
5208     e8/call  clear-stream/disp32
5209     # . . discard args
5210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5211     # initialize input
5212     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
5213     # . . push args
5214     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5215     68/push  _test-input-stream/imm32
5216     # . . call
5217     e8/call  write/disp32
5218     # . . discard args
5219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5220     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5221     # . . push args
5222     68/push  _test-output-buffered-file/imm32
5223     68/push  _test-input-stream/imm32
5224     # . . call
5225     e8/call  convert-instruction/disp32
5226     # . . discard args
5227     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5228     # check output
5229     # . flush(_test-output-buffered-file)
5230     # . . push args
5231     68/push  _test-output-buffered-file/imm32
5232     # . . call
5233     e8/call  flush/disp32
5234     # . . discard args
5235     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5236 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5262     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
5263     # . . push args
5264     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
5265     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5266     68/push  _test-output-stream/imm32
5267     # . . call
5268     e8/call  check-stream-equal/disp32
5269     # . . discard args
5270     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5271     # . epilogue
5272     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5273     5d/pop-to-ebp
5274     c3/return
5275 
5276 test-convert-instruction-handles-disp32-operand:
5277     # expand /disp32 operand into 4 bytes
5278     # . prologue
5279     55/push-ebp
5280     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5281     # setup
5282     # . clear-stream(_test-input-stream)
5283     # . . push args
5284     68/push  _test-input-stream/imm32
5285     # . . call
5286     e8/call  clear-stream/disp32
5287     # . . discard args
5288     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5289     # . clear-stream(_test-output-stream)
5290     # . . push args
5291     68/push  _test-output-stream/imm32
5292     # . . call
5293     e8/call  clear-stream/disp32
5294     # . . discard args
5295     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5296     # . clear-stream($_test-output-buffered-file->buffer)
5297     # . . push args
5298     68/push  $_test-output-buffered-file->buffer/imm32
5299     # . . call
5300     e8/call  clear-stream/disp32
5301     # . . discard args
5302     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5303     # initialize input
5304     # . write(_test-input-stream, "e8/call 20/disp32")
5305     # . . push args
5306     68/push  "e8/call 20/disp32"/imm32
5307     68/push  _test-input-stream/imm32
5308     # . . call
5309     e8/call  write/disp32
5310     # . . discard args
5311     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5312     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5313     # . . push args
5314     68/push  _test-output-buffered-file/imm32
5315     68/push  _test-input-stream/imm32
5316     # . . call
5317     e8/call  convert-instruction/disp32
5318     # . . discard args
5319     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5320     # check output
5321     # . flush(_test-output-buffered-file)
5322     # . . push args
5323     68/push  _test-output-buffered-file/imm32
5324     # . . call
5325     e8/call  flush/disp32
5326     # . . discard args
5327     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5328 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5354     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
5355     # . . push args
5356     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
5357     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
5358     68/push  _test-output-stream/imm32
5359     # . . call
5360     e8/call  check-stream-equal/disp32
5361     # . . discard args
5362     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5363     # . epilogue
5364     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5365     5d/pop-to-ebp
5366     c3/return
5367 
5368 test-convert-instruction-handles-disp16-operand:
5369     # expand /disp16 operand into 2 bytes
5370     # . prologue
5371     55/push-ebp
5372     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5373     # setup
5374     # . clear-stream(_test-input-stream)
5375     # . . push args
5376     68/push  _test-input-stream/imm32
5377     # . . call
5378     e8/call  clear-stream/disp32
5379     # . . discard args
5380     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5381     # . clear-stream(_test-output-stream)
5382     # . . push args
5383     68/push  _test-output-stream/imm32
5384     # . . call
5385     e8/call  clear-stream/disp32
5386     # . . discard args
5387     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5388     # . clear-stream($_test-output-buffered-file->buffer)
5389     # . . push args
5390     68/push  $_test-output-buffered-file->buffer/imm32
5391     # . . call
5392     e8/call  clear-stream/disp32
5393     # . . discard args
5394     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5395     # initialize input
5396     # . write(_test-input-stream, "e8/call 20/disp16")
5397     # . . push args
5398     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
5399     68/push  _test-input-stream/imm32
5400     # . . call
5401     e8/call  write/disp32
5402     # . . discard args
5403     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5404     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5405     # . . push args
5406     68/push  _test-output-buffered-file/imm32
5407     68/push  _test-input-stream/imm32
5408     # . . call
5409     e8/call  convert-instruction/disp32
5410     # . . discard args
5411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5412     # check output
5413     # . flush(_test-output-buffered-file)
5414     # . . push args
5415     68/push  _test-output-buffered-file/imm32
5416     # . . call
5417     e8/call  flush/disp32
5418     # . . discard args
5419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5420 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5446     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
5447     # . . push args
5448     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
5449     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
5450     68/push  _test-output-stream/imm32
5451     # . . call
5452     e8/call  check-stream-equal/disp32
5453     # . . discard args
5454     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5455     # . epilogue
5456     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5457     5d/pop-to-ebp
5458     c3/return
5459 
5460 test-convert-instruction-handles-disp8-operand:
5461     # expand /disp8 operand into 1 byte
5462     # . prologue
5463     55/push-ebp
5464     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5465     # setup
5466     # . clear-stream(_test-input-stream)
5467     # . . push args
5468     68/push  _test-input-stream/imm32
5469     # . . call
5470     e8/call  clear-stream/disp32
5471     # . . discard args
5472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5473     # . clear-stream(_test-output-stream)
5474     # . . push args
5475     68/push  _test-output-stream/imm32
5476     # . . call
5477     e8/call  clear-stream/disp32
5478     # . . discard args
5479     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5480     # . clear-stream($_test-output-buffered-file->buffer)
5481     # . . push args
5482     68/push  $_test-output-buffered-file->buffer/imm32
5483     # . . call
5484     e8/call  clear-stream/disp32
5485     # . . discard args
5486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5487     # initialize input
5488     # . write(_test-input-stream, "eb/jump 20/disp8")
5489     # . . push args
5490     68/push  "eb/jump 20/disp8"/imm32
5491     68/push  _test-input-stream/imm32
5492     # . . call
5493     e8/call  write/disp32
5494     # . . discard args
5495     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5496     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5497     # . . push args
5498     68/push  _test-output-buffered-file/imm32
5499     68/push  _test-input-stream/imm32
5500     # . . call
5501     e8/call  convert-instruction/disp32
5502     # . . discard args
5503     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5504     # check output
5505     # . flush(_test-output-buffered-file)
5506     # . . push args
5507     68/push  _test-output-buffered-file/imm32
5508     # . . call
5509     e8/call  flush/disp32
5510     # . . discard args
5511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5512 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5538     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
5539     # . . push args
5540     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
5541     68/push  "eb 20  # eb/jump 20/disp8"/imm32
5542     68/push  _test-output-stream/imm32
5543     # . . call
5544     e8/call  check-stream-equal/disp32
5545     # . . discard args
5546     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5547     # . epilogue
5548     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5549     5d/pop-to-ebp
5550     c3/return
5551 
5552 test-convert-instruction-handles-disp8-name:
5553     # pass /disp8 name directly through
5554     # . prologue
5555     55/push-ebp
5556     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5557     # setup
5558     # . clear-stream(_test-input-stream)
5559     # . . push args
5560     68/push  _test-input-stream/imm32
5561     # . . call
5562     e8/call  clear-stream/disp32
5563     # . . discard args
5564     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5565     # . clear-stream(_test-output-stream)
5566     # . . push args
5567     68/push  _test-output-stream/imm32
5568     # . . call
5569     e8/call  clear-stream/disp32
5570     # . . discard args
5571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5572     # . clear-stream($_test-output-buffered-file->buffer)
5573     # . . push args
5574     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5580     # . write(_test-input-stream, "eb/jump xyz/disp8")
5581     # . . push args
5582     68/push  "eb/jump xyz/disp8"/imm32
5583     68/push  _test-input-stream/imm32
5584     # . . call
5585     e8/call  write/disp32
5586     # . . discard args
5587     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5588     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5589     # . . push args
5590     68/push  _test-output-buffered-file/imm32
5591     68/push  _test-input-stream/imm32
5592     # . . call
5593     e8/call  convert-instruction/disp32
5594     # . . discard args
5595     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5596     # check output
5597     # . flush(_test-output-buffered-file)
5598     # . . push args
5599     68/push  _test-output-buffered-file/imm32
5600     # . . call
5601     e8/call  flush/disp32
5602     # . . discard args
5603     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5604 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5630     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
5631     # . . push args
5632     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
5633     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
5634     68/push  _test-output-stream/imm32
5635     # . . call
5636     e8/call  check-stream-equal/disp32
5637     # . . discard args
5638     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5639     # . epilogue
5640     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5641     5d/pop-to-ebp
5642     c3/return
5643 
5644 test-convert-instruction-handles-imm32-operand:
5645     # expand /imm32 operand into 4 bytes
5646     # . prologue
5647     55/push-ebp
5648     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5649     # setup
5650     # . clear-stream(_test-input-stream)
5651     # . . push args
5652     68/push  _test-input-stream/imm32
5653     # . . call
5654     e8/call  clear-stream/disp32
5655     # . . discard args
5656     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5657     # . clear-stream(_test-output-stream)
5658     # . . push args
5659     68/push  _test-output-stream/imm32
5660     # . . call
5661     e8/call  clear-stream/disp32
5662     # . . discard args
5663     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5664     # . clear-stream($_test-output-buffered-file->buffer)
5665     # . . push args
5666     68/push  $_test-output-buffered-file->buffer/imm32
5667     # . . call
5668     e8/call  clear-stream/disp32
5669     # . . discard args
5670     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5671     # initialize input
5672     # . write(_test-input-stream, "68/push 0x20/imm32")
5673     # . . push args
5674     68/push  "68/push 0x20/imm32"/imm32
5675     68/push  _test-input-stream/imm32
5676     # . . call
5677     e8/call  write/disp32
5678     # . . discard args
5679     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5680     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5681     # . . push args
5682     68/push  _test-output-buffered-file/imm32
5683     68/push  _test-input-stream/imm32
5684     # . . call
5685     e8/call  convert-instruction/disp32
5686     # . . discard args
5687     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5688     # check output
5689     # . flush(_test-output-buffered-file)
5690     # . . push args
5691     68/push  _test-output-buffered-file/imm32
5692     # . . call
5693     e8/call  flush/disp32
5694     # . . discard args
5695     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5696 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5722     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
5723     # . . push args
5724     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
5725     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
5726     68/push  _test-output-stream/imm32
5727     # . . call
5728     e8/call  check-stream-equal/disp32
5729     # . . discard args
5730     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5731     # . epilogue
5732     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5733     5d/pop-to-ebp
5734     c3/return
5735 
5736 test-convert-instruction-handles-imm16-operand:
5737     # expand /imm16 operand into 2 bytes
5738     # we don't have one of these at the moment, so this expands to an invalid instruction
5739     # . prologue
5740     55/push-ebp
5741     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5742     # setup
5743     # . clear-stream(_test-input-stream)
5744     # . . push args
5745     68/push  _test-input-stream/imm32
5746     # . . call
5747     e8/call  clear-stream/disp32
5748     # . . discard args
5749     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5750     # . clear-stream(_test-output-stream)
5751     # . . push args
5752     68/push  _test-output-stream/imm32
5753     # . . call
5754     e8/call  clear-stream/disp32
5755     # . . discard args
5756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5757     # . clear-stream($_test-output-buffered-file->buffer)
5758     # . . push args
5759     68/push  $_test-output-buffered-file->buffer/imm32
5760     # . . call
5761     e8/call  clear-stream/disp32
5762     # . . discard args
5763     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5764     # initialize input
5765     # . write(_test-input-stream, "68/push 0x20/imm16")
5766     # . . push args
5767     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
5768     68/push  _test-input-stream/imm32
5769     # . . call
5770     e8/call  write/disp32
5771     # . . discard args
5772     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5773     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5774     # . . push args
5775     68/push  _test-output-buffered-file/imm32
5776     68/push  _test-input-stream/imm32
5777     # . . call
5778     e8/call  convert-instruction/disp32
5779     # . . discard args
5780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5781     # check output
5782     # . flush(_test-output-buffered-file)
5783     # . . push args
5784     68/push  _test-output-buffered-file/imm32
5785     # . . call
5786     e8/call  flush/disp32
5787     # . . discard args
5788     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5789 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5815     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
5816     # . . push args
5817     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
5818     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
5819     68/push  _test-output-stream/imm32
5820     # . . call
5821     e8/call  check-stream-equal/disp32
5822     # . . discard args
5823     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5824     # . epilogue
5825     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5826     5d/pop-to-ebp
5827     c3/return
5828 
5829 test-convert-instruction-handles-imm8-operand:
5830     # expand /imm8 operand into 1 byte
5831     # we don't have one of these at the moment, so this expands to an invalid instruction
5832     # . prologue
5833     55/push-ebp
5834     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5835     # setup
5836     # . clear-stream(_test-input-stream)
5837     # . . push args
5838     68/push  _test-input-stream/imm32
5839     # . . call
5840     e8/call  clear-stream/disp32
5841     # . . discard args
5842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5843     # . clear-stream(_test-output-stream)
5844     # . . push args
5845     68/push  _test-output-stream/imm32
5846     # . . call
5847     e8/call  clear-stream/disp32
5848     # . . discard args
5849     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5850     # . clear-stream($_test-output-buffered-file->buffer)
5851     # . . push args
5852     68/push  $_test-output-buffered-file->buffer/imm32
5853     # . . call
5854     e8/call  clear-stream/disp32
5855     # . . discard args
5856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5857     # initialize input
5858     # . write(_test-input-stream, "68/push 0x20/imm8")
5859     # . . push args
5860     68/push  "68/push 0x20/imm8"/imm32
5861     68/push  _test-input-stream/imm32
5862     # . . call
5863     e8/call  write/disp32
5864     # . . discard args
5865     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5866     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5867     # . . push args
5868     68/push  _test-output-buffered-file/imm32
5869     68/push  _test-input-stream/imm32
5870     # . . call
5871     e8/call  convert-instruction/disp32
5872     # . . discard args
5873     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5874     # check output
5875     # . flush(_test-output-buffered-file)
5876     # . . push args
5877     68/push  _test-output-buffered-file/imm32
5878     # . . call
5879     e8/call  flush/disp32
5880     # . . discard args
5881     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5882 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5908     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
5909     # . . push args
5910     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
5911     68/push  "68 20  # 68/push 0x20/imm8"/imm32
5912     68/push  _test-output-stream/imm32
5913     # . . call
5914     e8/call  check-stream-equal/disp32
5915     # . . discard args
5916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5917     # . epilogue
5918     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5919     5d/pop-to-ebp
5920     c3/return
5921 
5922 # shortcut for parse-hex-int-from-slice(next-token-from-slice(word->start, word->end, '/'))
5923 parse-datum-of-word:  # word: (addr slice) -> value/eax: int
5924     # . prologue
5925     55/push-ebp
5926     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5927     # . save registers
5928     51/push-ecx
5929     56/push-esi
5930     # esi = word
5931     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
5932     # var slice/ecx: slice
5933     68/push  0/imm32/end
5934     68/push  0/imm32/start
5935     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
5936     # slice = next-token-from-slice(word->start, word->end, '/')
5937     # . . push args
5938     51/push-ecx
5939     68/push  0x2f/imm32/slash
5940     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
5941     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
5942     # . . call
5943     e8/call  next-token-from-slice/disp32
5944     # . . discard args
5945     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
5946     # return parse-hex-int-from-slice(slice)
5947     # . . push args
5948     51/push-ecx
5949     # . . call
5950     e8/call  parse-hex-int-from-slice/disp32
5951     # . . discard args
5952     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5953 $parse-datum-of-word:end:
5954     # . reclaim locals
5955     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5956     # . restore registers
5957     5e/pop-to-esi
5958     59/pop-to-ecx
5959     # . epilogue
5960     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5961     5d/pop-to-ebp
5962     c3/return
5963 
5964 # . . vim:nowrap:textwidth=0