https://github.com/akkartik/mu/blob/main/linux/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/bootstrap translate [01]*.subx subx-params.subx pack.subx  -o pack
   7 #   $ echo '05/add-to-eax 0x20/imm32'  |bootstrap/bootstrap run 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     #     else if has-metadata?(word-slice, "imm16")
 919     #       emit(out, word-slice, 2)
 920     #     else
 921     #       emit(out, word-slice, 1)
 922     #   write-buffered(out, "\n")
 923     #
 924     # . prologue
 925     55/push-ebp
 926     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 927     # . save registers
 928     50/push-eax
 929     51/push-ecx
 930     52/push-edx
 931     # var word-slice/ecx: slice
 932     68/push  0/imm32/end
 933     68/push  0/imm32/start
 934     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 935 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
 961 $convert-data:loop:
 962     # next-word(line, word-slice)
 963     # . . push args
 964     51/push-ecx
 965     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 966     # . . call
 967     e8/call  next-word/disp32
 968     # . . discard args
 969     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 970 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
1010 $convert-data:check0:
1011     # if (slice-empty?(word-slice)) break
1012     # . eax = slice-empty?(word-slice)
1013     # . . push args
1014     51/push-ecx
1015     # . . call
1016     e8/call  slice-empty?/disp32
1017     # . . discard args
1018     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1019     # . if (eax != false) break
1020     3d/compare-eax-and  0/imm32/false
1021     0f 85/jump-if-!=  $convert-data:break/disp32
1022 $convert-data:check-for-comment:
1023     # if (slice-starts-with?(word-slice, "#"))
1024     # . var start/edx: (addr byte) = word-slice->start
1025     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
1026     # . var c/eax: byte = *start
1027     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1028     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
1029     # . if (c != '#') goto next check
1030     3d/compare-eax-and  0x23/imm32/hash
1031     75/jump-if-!=  $convert-data:check-for-label/disp8
1032 $convert-data:comment:
1033     # write-slice-buffered(out, word-slice)
1034     # . . push args
1035     51/push-ecx
1036     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1037     # . . call
1038     e8/call  write-slice-buffered/disp32
1039     # . . discard args
1040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1041     # return
1042     0f 85/jump-if-!=  $convert-data:end/disp32
1043 $convert-data:check-for-label:
1044     # if (slice-ends-with?(word-slice, ":"))
1045     # . var end/edx: (addr byte) = word-slice->end
1046     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1047     # . var c/eax: byte = *(end-1)
1048     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1049     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
1050     # . if (c != ':') goto next check
1051     3d/compare-eax-and  0x3a/imm32/colon
1052     75/jump-if-!=  $convert-data:check-for-imm32/disp8
1053 $convert-data:label:
1054     # write-stream-data(out, line)
1055     # . . push args
1056     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1057     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1058     # . . call
1059     e8/call  write-stream-data/disp32
1060     # . . discard args
1061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1062     # return
1063     0f 85/jump-if-!=  $convert-data:end/disp32
1064 $convert-data:check-for-imm32:
1065     # if (has-metadata?(word-slice, "imm32"))
1066     # . eax = has-metadata?(ecx, "imm32")
1067     # . . push args
1068     68/push  "imm32"/imm32
1069     51/push-ecx
1070     # . . call
1071     e8/call  has-metadata?/disp32
1072     # . . discard args
1073     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1074     # . if (eax == false) process as a single byte
1075     3d/compare-eax-and  0/imm32/false
1076     74/jump-if-=  $convert-data:check-for-imm16/disp8
1077 $convert-data:imm32:
1078     # emit(out, word-slice, 4)
1079     # . . push args
1080     68/push  4/imm32
1081     51/push-ecx
1082     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1083     # . . call
1084     e8/call  emit/disp32
1085     # . . discard args
1086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1087     e9/jump  $convert-data:loop/disp32
1088 $convert-data:check-for-imm16:
1089     # if (has-metadata?(word-slice, "imm16"))
1090     # . eax = has-metadata?(ecx, "imm16")
1091     # . . push args
1092     68/push  "imm16"/imm32
1093     51/push-ecx
1094     # . . call
1095     e8/call  has-metadata?/disp32
1096     # . . discard args
1097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1098     # . if (eax == false) process as a single byte
1099     3d/compare-eax-and  0/imm32/false
1100     74/jump-if-=  $convert-data:single-byte/disp8
1101 $convert-data:imm16:
1102     # emit(out, word-slice, 2)
1103     # . . push args
1104     68/push  2/imm32
1105     51/push-ecx
1106     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1107     # . . call
1108     e8/call  emit/disp32
1109     # . . discard args
1110     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1111     e9/jump  $convert-data:loop/disp32
1112 $convert-data:single-byte:
1113     # emit(out, word-slice, 1)
1114     # . . push args
1115     68/push  1/imm32
1116     51/push-ecx
1117     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1118     # . . call
1119     e8/call  emit/disp32
1120     # . . discard args
1121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1122     e9/jump  $convert-data:loop/disp32
1123 $convert-data:break:
1124     # write-buffered(out, "\n")
1125     # . . push args
1126     68/push  Newline/imm32
1127     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1128     # . . call
1129     e8/call  write-buffered/disp32
1130     # . . discard args
1131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1132 $convert-data:end:
1133     # . reclaim locals
1134     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1135     # . restore registers
1136     5a/pop-to-edx
1137     59/pop-to-ecx
1138     58/pop-to-eax
1139     # . epilogue
1140     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1141     5d/pop-to-ebp
1142     c3/return
1143 
1144 test-convert-data-passes-comments-through:
1145     # if a line starts with '#', pass it along unchanged
1146     # . prologue
1147     55/push-ebp
1148     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1149     # setup
1150     # . clear-stream(_test-input-stream)
1151     # . . push args
1152     68/push  _test-input-stream/imm32
1153     # . . call
1154     e8/call  clear-stream/disp32
1155     # . . discard args
1156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1157     # . clear-stream(_test-output-stream)
1158     # . . push args
1159     68/push  _test-output-stream/imm32
1160     # . . call
1161     e8/call  clear-stream/disp32
1162     # . . discard args
1163     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1164     # . clear-stream($_test-output-buffered-file->buffer)
1165     # . . push args
1166     68/push  $_test-output-buffered-file->buffer/imm32
1167     # . . call
1168     e8/call  clear-stream/disp32
1169     # . . discard args
1170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1171     # initialize input
1172     # . write(_test-input-stream, "# abcd")
1173     # . . push args
1174     68/push  "# abcd"/imm32
1175     68/push  _test-input-stream/imm32
1176     # . . call
1177     e8/call  write/disp32
1178     # . . discard args
1179     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1180     # convert-data(_test-input-stream, _test-output-buffered-file)
1181     # . . push args
1182     68/push  _test-output-buffered-file/imm32
1183     68/push  _test-input-stream/imm32
1184     # . . call
1185     e8/call  convert-data/disp32
1186     # . . discard args
1187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1188     # check that the line just passed through
1189     # . flush(_test-output-buffered-file)
1190     # . . push args
1191     68/push  _test-output-buffered-file/imm32
1192     # . . call
1193     e8/call  flush/disp32
1194     # . . discard args
1195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1196 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
1222     # . check-stream-equal(_test-output-stream, "# abcd", msg)
1223     # . . push args
1224     68/push  "F - test-convert-data-passes-comments-through"/imm32
1225     68/push  "# abcd"/imm32
1226     68/push  _test-output-stream/imm32
1227     # . . call
1228     e8/call  check-stream-equal/disp32
1229     # . . discard args
1230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1231     # . epilogue
1232     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1233     5d/pop-to-ebp
1234     c3/return
1235 
1236 test-convert-data-passes-labels-through:
1237     # if the first word ends with ':', pass along the entire line unchanged
1238     # . prologue
1239     55/push-ebp
1240     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1241     # setup
1242     # . clear-stream(_test-input-stream)
1243     # . . push args
1244     68/push  _test-input-stream/imm32
1245     # . . call
1246     e8/call  clear-stream/disp32
1247     # . . discard args
1248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1249     # . clear-stream(_test-output-stream)
1250     # . . push args
1251     68/push  _test-output-stream/imm32
1252     # . . call
1253     e8/call  clear-stream/disp32
1254     # . . discard args
1255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1256     # . clear-stream($_test-output-buffered-file->buffer)
1257     # . . push args
1258     68/push  $_test-output-buffered-file->buffer/imm32
1259     # . . call
1260     e8/call  clear-stream/disp32
1261     # . . discard args
1262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1263     # initialize input
1264     # . write(_test-input-stream, "ab: # cd")
1265     # . . push args
1266     68/push  "ab: # cd"/imm32
1267     68/push  _test-input-stream/imm32
1268     # . . call
1269     e8/call  write/disp32
1270     # . . discard args
1271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1272     # convert-data(_test-input-stream, _test-output-buffered-file)
1273     # . . push args
1274     68/push  _test-output-buffered-file/imm32
1275     68/push  _test-input-stream/imm32
1276     # . . call
1277     e8/call  convert-data/disp32
1278     # . . discard args
1279     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1280     # check that the line just passed through
1281     # . flush(_test-output-buffered-file)
1282     # . . push args
1283     68/push  _test-output-buffered-file/imm32
1284     # . . call
1285     e8/call  flush/disp32
1286     # . . discard args
1287     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1288     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
1289     # . . push args
1290     68/push  "F - test-convert-data-passes-labels-through"/imm32
1291     68/push  "ab: # cd"/imm32
1292     68/push  _test-output-stream/imm32
1293     # . . call
1294     e8/call  check-stream-equal/disp32
1295     # . . discard args
1296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1297     # . epilogue
1298     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1299     5d/pop-to-ebp
1300     c3/return
1301 
1302 test-convert-data-passes-names-through:
1303     # If a word is a valid name, just emit it unchanged.
1304     # Later phases will deal with it.
1305     # . prologue
1306     55/push-ebp
1307     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1308     # setup
1309     # . clear-stream(_test-input-stream)
1310     # . . push args
1311     68/push  _test-input-stream/imm32
1312     # . . call
1313     e8/call  clear-stream/disp32
1314     # . . discard args
1315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1316     # . clear-stream(_test-output-stream)
1317     # . . push args
1318     68/push  _test-output-stream/imm32
1319     # . . call
1320     e8/call  clear-stream/disp32
1321     # . . discard args
1322     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1323     # . clear-stream($_test-output-buffered-file->buffer)
1324     # . . push args
1325     68/push  $_test-output-buffered-file->buffer/imm32
1326     # . . call
1327     e8/call  clear-stream/disp32
1328     # . . discard args
1329     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1330     # initialize input
1331     # . write(_test-input-stream, "abcd/imm32")
1332     # . . push args
1333     68/push  "abcd/imm32"/imm32
1334     68/push  _test-input-stream/imm32
1335     # . . call
1336     e8/call  write/disp32
1337     # . . discard args
1338     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1339     # convert-data(_test-input-stream, _test-output-buffered-file)
1340     # . . push args
1341     68/push  _test-output-buffered-file/imm32
1342     68/push  _test-input-stream/imm32
1343     # . . call
1344     e8/call  convert-data/disp32
1345     # . . discard args
1346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1347     # check that the line just passed through
1348     # . flush(_test-output-buffered-file)
1349     # . . push args
1350     68/push  _test-output-buffered-file/imm32
1351     # . . call
1352     e8/call  flush/disp32
1353     # . . discard args
1354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1355     # . check-stream-equal(_test-output-stream, "abcd/imm32 \n", msg)
1356     # . . push args
1357     68/push  "F - test-convert-data-passes-names-through"/imm32
1358     68/push  "abcd/imm32 \n"/imm32
1359     68/push  _test-output-stream/imm32
1360     # . . call
1361     e8/call  check-stream-equal/disp32
1362     # . . discard args
1363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1364     # . epilogue
1365     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1366     5d/pop-to-ebp
1367     c3/return
1368 
1369 test-convert-data-handles-imm32:
1370     # If a word has the /imm32 metadata, emit it in 4 bytes.
1371     # . prologue
1372     55/push-ebp
1373     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1374     # setup
1375     # . clear-stream(_test-input-stream)
1376     # . . push args
1377     68/push  _test-input-stream/imm32
1378     # . . call
1379     e8/call  clear-stream/disp32
1380     # . . discard args
1381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1382     # . clear-stream(_test-output-stream)
1383     # . . push args
1384     68/push  _test-output-stream/imm32
1385     # . . call
1386     e8/call  clear-stream/disp32
1387     # . . discard args
1388     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1389     # . clear-stream($_test-output-buffered-file->buffer)
1390     # . . push args
1391     68/push  $_test-output-buffered-file->buffer/imm32
1392     # . . call
1393     e8/call  clear-stream/disp32
1394     # . . discard args
1395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1396     # initialize input
1397     # . write(_test-input-stream, "30/imm32")
1398     # . . push args
1399     68/push  "30/imm32"/imm32
1400     68/push  _test-input-stream/imm32
1401     # . . call
1402     e8/call  write/disp32
1403     # . . discard args
1404     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1405     # convert-data(_test-input-stream, _test-output-buffered-file)
1406     # . . push args
1407     68/push  _test-output-buffered-file/imm32
1408     68/push  _test-input-stream/imm32
1409     # . . call
1410     e8/call  convert-data/disp32
1411     # . . discard args
1412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1413     # check that 4 bytes were written
1414     # . flush(_test-output-buffered-file)
1415     # . . push args
1416     68/push  _test-output-buffered-file/imm32
1417     # . . call
1418     e8/call  flush/disp32
1419     # . . discard args
1420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1421     # . check-stream-equal(_test-output-stream, "30 00 00 00 \n", msg)
1422     # . . push args
1423     68/push  "F - test-convert-data-handles-imm32"/imm32
1424     68/push  "30 00 00 00 \n"/imm32
1425     68/push  _test-output-stream/imm32
1426     # . . call
1427     e8/call  check-stream-equal/disp32
1428     # . . discard args
1429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1430     # . epilogue
1431     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1432     5d/pop-to-ebp
1433     c3/return
1434 
1435 test-convert-data-handles-imm16:
1436     # . prologue
1437     55/push-ebp
1438     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1439     # setup
1440     # . clear-stream(_test-input-stream)
1441     # . . push args
1442     68/push  _test-input-stream/imm32
1443     # . . call
1444     e8/call  clear-stream/disp32
1445     # . . discard args
1446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1447     # . clear-stream(_test-output-stream)
1448     # . . push args
1449     68/push  _test-output-stream/imm32
1450     # . . call
1451     e8/call  clear-stream/disp32
1452     # . . discard args
1453     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1454     # . clear-stream($_test-output-buffered-file->buffer)
1455     # . . push args
1456     68/push  $_test-output-buffered-file->buffer/imm32
1457     # . . call
1458     e8/call  clear-stream/disp32
1459     # . . discard args
1460     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1461     # initialize input
1462     # . write(_test-input-stream, "30/imm16")
1463     # . . push args
1464     68/push  "30/imm16"/imm32
1465     68/push  _test-input-stream/imm32
1466     # . . call
1467     e8/call  write/disp32
1468     # . . discard args
1469     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1470     # convert-data(_test-input-stream, _test-output-buffered-file)
1471     # . . push args
1472     68/push  _test-output-buffered-file/imm32
1473     68/push  _test-input-stream/imm32
1474     # . . call
1475     e8/call  convert-data/disp32
1476     # . . discard args
1477     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1478     # check that a single byte was written
1479     # . flush(_test-output-buffered-file)
1480     # . . push args
1481     68/push  _test-output-buffered-file/imm32
1482     # . . call
1483     e8/call  flush/disp32
1484     # . . discard args
1485     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1486     # . check-stream-equal(_test-output-stream, "30 00 \n", msg)
1487     # . . push args
1488     68/push  "F - test-convert-data-handles-imm16"/imm32
1489     68/push  "30 00 \n"/imm32
1490     68/push  _test-output-stream/imm32
1491     # . . call
1492     e8/call  check-stream-equal/disp32
1493     # . . discard args
1494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1495     # . epilogue
1496     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1497     5d/pop-to-ebp
1498     c3/return
1499 
1500 test-convert-data-handles-imm8:
1501     # . prologue
1502     55/push-ebp
1503     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1504     # setup
1505     # . clear-stream(_test-input-stream)
1506     # . . push args
1507     68/push  _test-input-stream/imm32
1508     # . . call
1509     e8/call  clear-stream/disp32
1510     # . . discard args
1511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1512     # . clear-stream(_test-output-stream)
1513     # . . push args
1514     68/push  _test-output-stream/imm32
1515     # . . call
1516     e8/call  clear-stream/disp32
1517     # . . discard args
1518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1519     # . clear-stream($_test-output-buffered-file->buffer)
1520     # . . push args
1521     68/push  $_test-output-buffered-file->buffer/imm32
1522     # . . call
1523     e8/call  clear-stream/disp32
1524     # . . discard args
1525     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1526     # initialize input
1527     # . write(_test-input-stream, "30/imm8")
1528     # . . push args
1529     68/push  "30/imm8"/imm32
1530     68/push  _test-input-stream/imm32
1531     # . . call
1532     e8/call  write/disp32
1533     # . . discard args
1534     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1535     # convert-data(_test-input-stream, _test-output-buffered-file)
1536     # . . push args
1537     68/push  _test-output-buffered-file/imm32
1538     68/push  _test-input-stream/imm32
1539     # . . call
1540     e8/call  convert-data/disp32
1541     # . . discard args
1542     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1543     # check that a single byte was written
1544     # . flush(_test-output-buffered-file)
1545     # . . push args
1546     68/push  _test-output-buffered-file/imm32
1547     # . . call
1548     e8/call  flush/disp32
1549     # . . discard args
1550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1551     # . check-stream-equal(_test-output-stream, "30 \n", msg)
1552     # . . push args
1553     68/push  "F - test-convert-data-handles-imm8"/imm32
1554     68/push  "30 \n"/imm32
1555     68/push  _test-output-stream/imm32
1556     # . . call
1557     e8/call  check-stream-equal/disp32
1558     # . . discard args
1559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1560     # . epilogue
1561     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1562     5d/pop-to-ebp
1563     c3/return
1564 
1565 test-convert-data-multiple-bytes:
1566     # Multiple single-byte words in input stream get processed one by one.
1567     # . prologue
1568     55/push-ebp
1569     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1570     # setup
1571     # . clear-stream(_test-input-stream)
1572     # . . push args
1573     68/push  _test-input-stream/imm32
1574     # . . call
1575     e8/call  clear-stream/disp32
1576     # . . discard args
1577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1578     # . clear-stream(_test-output-stream)
1579     # . . push args
1580     68/push  _test-output-stream/imm32
1581     # . . call
1582     e8/call  clear-stream/disp32
1583     # . . discard args
1584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1585     # . clear-stream($_test-output-buffered-file->buffer)
1586     # . . push args
1587     68/push  $_test-output-buffered-file->buffer/imm32
1588     # . . call
1589     e8/call  clear-stream/disp32
1590     # . . discard args
1591     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1592     # initialize input
1593     # . write(_test-input-stream, "1 2")
1594     # . . push args
1595     68/push  "1 2"/imm32
1596     68/push  _test-input-stream/imm32
1597     # . . call
1598     e8/call  write/disp32
1599     # . . discard args
1600     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1601     # convert-data(_test-input-stream, _test-output-buffered-file)
1602     # . . push args
1603     68/push  _test-output-buffered-file/imm32
1604     68/push  _test-input-stream/imm32
1605     # . . call
1606     e8/call  convert-data/disp32
1607     # . . discard args
1608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1609     # check output
1610     # . flush(_test-output-buffered-file)
1611     # . . push args
1612     68/push  _test-output-buffered-file/imm32
1613     # . . call
1614     e8/call  flush/disp32
1615     # . . discard args
1616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1617     # . check-stream-equal(_test-output-stream, "01 02 \n", msg)
1618     # . . push args
1619     68/push  "F - test-convert-data-multiple-bytes"/imm32
1620     68/push  "01 02 \n"/imm32
1621     68/push  _test-output-stream/imm32
1622     # . . call
1623     e8/call  check-stream-equal/disp32
1624     # . . discard args
1625     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1626     # . epilogue
1627     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1628     5d/pop-to-ebp
1629     c3/return
1630 
1631 test-convert-data-byte-then-name:
1632     # Single-byte word followed by valid name get processed one by one.
1633     # . prologue
1634     55/push-ebp
1635     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1636     # setup
1637     # . clear-stream(_test-input-stream)
1638     # . . push args
1639     68/push  _test-input-stream/imm32
1640     # . . call
1641     e8/call  clear-stream/disp32
1642     # . . discard args
1643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1644     # . clear-stream(_test-output-stream)
1645     # . . push args
1646     68/push  _test-output-stream/imm32
1647     # . . call
1648     e8/call  clear-stream/disp32
1649     # . . discard args
1650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1651     # . clear-stream($_test-output-buffered-file->buffer)
1652     # . . push args
1653     68/push  $_test-output-buffered-file->buffer/imm32
1654     # . . call
1655     e8/call  clear-stream/disp32
1656     # . . discard args
1657     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1658     # initialize input
1659     # . write(_test-input-stream, "30 abcd/o")
1660     # . . push args
1661     68/push  "30 abcd/o"/imm32
1662     68/push  _test-input-stream/imm32
1663     # . . call
1664     e8/call  write/disp32
1665     # . . discard args
1666     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1667     # convert-data(_test-input-stream, _test-output-buffered-file)
1668     # . . push args
1669     68/push  _test-output-buffered-file/imm32
1670     68/push  _test-input-stream/imm32
1671     # . . call
1672     e8/call  convert-data/disp32
1673     # . . discard args
1674     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1675     # check output
1676     # . flush(_test-output-buffered-file)
1677     # . . push args
1678     68/push  _test-output-buffered-file/imm32
1679     # . . call
1680     e8/call  flush/disp32
1681     # . . discard args
1682     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1683     # . check-stream-equal(_test-output-stream, "30 abcd/o \n", msg)
1684     # . . push args
1685     68/push  "F - test-convert-data-byte-then-name"/imm32
1686     68/push  "30 abcd/o \n"/imm32
1687     68/push  _test-output-stream/imm32
1688     # . . call
1689     e8/call  check-stream-equal/disp32
1690     # . . discard args
1691     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1692     # . epilogue
1693     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1694     5d/pop-to-ebp
1695     c3/return
1696 
1697 test-convert-data-multiple-words:
1698     # Multiple words in input stream get processed one by one.
1699     # . prologue
1700     55/push-ebp
1701     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1702     # setup
1703     # . clear-stream(_test-input-stream)
1704     # . . push args
1705     68/push  _test-input-stream/imm32
1706     # . . call
1707     e8/call  clear-stream/disp32
1708     # . . discard args
1709     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1710     # . clear-stream(_test-output-stream)
1711     # . . push args
1712     68/push  _test-output-stream/imm32
1713     # . . call
1714     e8/call  clear-stream/disp32
1715     # . . discard args
1716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1717     # . clear-stream($_test-output-buffered-file->buffer)
1718     # . . push args
1719     68/push  $_test-output-buffered-file->buffer/imm32
1720     # . . call
1721     e8/call  clear-stream/disp32
1722     # . . discard args
1723     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1724     # initialize input
1725     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
1726     # . . push args
1727     68/push  "30 abcd/o 42e1/imm32"/imm32
1728     68/push  _test-input-stream/imm32
1729     # . . call
1730     e8/call  write/disp32
1731     # . . discard args
1732     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1733     # convert-data(_test-input-stream, _test-output-buffered-file)
1734     # . . push args
1735     68/push  _test-output-buffered-file/imm32
1736     68/push  _test-input-stream/imm32
1737     # . . call
1738     e8/call  convert-data/disp32
1739     # . . discard args
1740     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1741     # check output
1742     # . flush(_test-output-buffered-file)
1743     # . . push args
1744     68/push  _test-output-buffered-file/imm32
1745     # . . call
1746     e8/call  flush/disp32
1747     # . . discard args
1748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1749 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
1775     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 \n", msg)
1776     # . . push args
1777     68/push  "F - test-convert-data-multiple-words"/imm32
1778     68/push  "30 abcd/o e1 42 00 00 \n"/imm32
1779     68/push  _test-output-stream/imm32
1780     # . . call
1781     e8/call  check-stream-equal/disp32
1782     # . . discard args
1783     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1784     # . epilogue
1785     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1786     5d/pop-to-ebp
1787     c3/return
1788 
1789 test-convert-data-trailing-comment:
1790     # Trailing comments in data segment get appropriately ignored.
1791     # . prologue
1792     55/push-ebp
1793     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1794     # setup
1795     # . clear-stream(_test-input-stream)
1796     # . . push args
1797     68/push  _test-input-stream/imm32
1798     # . . call
1799     e8/call  clear-stream/disp32
1800     # . . discard args
1801     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1802     # . clear-stream(_test-output-stream)
1803     # . . push args
1804     68/push  _test-output-stream/imm32
1805     # . . call
1806     e8/call  clear-stream/disp32
1807     # . . discard args
1808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1809     # . clear-stream($_test-output-buffered-file->buffer)
1810     # . . push args
1811     68/push  $_test-output-buffered-file->buffer/imm32
1812     # . . call
1813     e8/call  clear-stream/disp32
1814     # . . discard args
1815     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1816     # initialize input
1817     # . write(_test-input-stream, "30/imm32 # comment")
1818     # . . push args
1819     68/push  "30/imm32 # comment"/imm32
1820     68/push  _test-input-stream/imm32
1821     # . . call
1822     e8/call  write/disp32
1823     # . . discard args
1824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1825     # convert-data(_test-input-stream, _test-output-buffered-file)
1826     # . . push args
1827     68/push  _test-output-buffered-file/imm32
1828     68/push  _test-input-stream/imm32
1829     # . . call
1830     e8/call  convert-data/disp32
1831     # . . discard args
1832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1833     # check output
1834     # . flush(_test-output-buffered-file)
1835     # . . push args
1836     68/push  _test-output-buffered-file/imm32
1837     # . . call
1838     e8/call  flush/disp32
1839     # . . discard args
1840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1841 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
1867     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
1868     # . . push args
1869     68/push  "F - test-convert-data-trailing-comment"/imm32
1870     68/push  "30 00 00 00 # comment"/imm32
1871     68/push  _test-output-stream/imm32
1872     # . . call
1873     e8/call  check-stream-equal/disp32
1874     # . . discard args
1875     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1876     # . epilogue
1877     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1878     5d/pop-to-ebp
1879     c3/return
1880 
1881 # pack an instruction, following the C++ version
1882 #
1883 # zero error handling at the moment (continuing to rely on the C++ version for that):
1884 #   missing fields are always 0-filled
1885 #   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.
1886 #   may pick up any of duplicate operands in an instruction
1887 #   silently drop extraneous operands
1888 #   unceremoniously abort on non-numeric operands except disp or imm
1889 #   opcodes must be lowercase and zero padded
1890 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
1891 convert-instruction:  # line: (addr stream byte), out: (addr buffered-file)
1892     # pseudocode:
1893     #   # some early exits
1894     #   var word-slice = next-word(line)
1895     #   if slice-empty?(word-slice)
1896     #     write-stream-data(out, line)
1897     #     return
1898     #   if slice-starts-with?(word-slice, "#")
1899     #     write-stream-data(out, line)
1900     #     return
1901     #   if slice-ends-with?(word-slice, ":")
1902     #     write-stream-data(out, line)
1903     #     return
1904     #   # really convert
1905     #   emit-opcodes(line, out)
1906     #   emit-modrm(line, out)
1907     #   emit-sib(line, out)
1908     #   emit-disp(line, out)
1909     #   emit-imm(line, out)
1910     #   emit-line-in-comment(line, out)
1911     #
1912     # . prologue
1913     55/push-ebp
1914     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1915     # . save registers
1916     50/push-eax
1917     51/push-ecx
1918     52/push-edx
1919     # var word-slice/ecx: slice
1920     68/push  0/imm32/end
1921     68/push  0/imm32/start
1922     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1923     # next-word(line, word-slice)
1924     # . . push args
1925     51/push-ecx
1926     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1927     # . . call
1928     e8/call  next-word/disp32
1929     # . . discard args
1930     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1931 $convert-instruction:check0:
1932     # if (slice-empty?(word-slice)) break
1933     # . eax = slice-empty?(word-slice)
1934     # . . push args
1935     51/push-ecx
1936     # . . call
1937     e8/call  slice-empty?/disp32
1938     # . . discard args
1939     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1940     # . if (eax != false) pass through
1941     3d/compare-eax-and  0/imm32/false
1942     75/jump-if-!=  $convert-instruction:pass-through/disp8
1943 $convert-instruction:check1:
1944     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
1945     # . var start/edx: (addr byte) = word-slice->start
1946     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
1947     # . var c/eax: byte = *start
1948     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1949     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
1950     # . if (c == '#') pass through
1951     3d/compare-eax-and  0x23/imm32/hash
1952     74/jump-if-=  $convert-instruction:pass-through/disp8
1953 $convert-instruction:check2:
1954     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
1955     # . var end/edx: (addr byte) = word-slice->end
1956     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1957     # . var c/eax: byte = *(end-1)
1958     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1959     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
1960     # . if (c == ':') pass through
1961     3d/compare-eax-and  0x3a/imm32/colon
1962     75/jump-if-!=  $convert-instruction:really-convert/disp8
1963 $convert-instruction:pass-through:
1964     # write-stream-data(out, line)
1965     # . . push args
1966     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1967     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1968     # . . call
1969     e8/call  write-stream-data/disp32
1970     # . . discard args
1971     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1972     # return
1973     eb/jump  $convert-instruction:end/disp8
1974 $convert-instruction:really-convert:
1975     # emit-opcodes(line, out)
1976     # . . push args
1977     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1978     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1979     # . . call
1980     e8/call  emit-opcodes/disp32
1981     # . . discard args
1982     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1983     # emit-modrm(line, out)
1984     # . . push args
1985     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1986     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1987     # . . call
1988     e8/call  emit-modrm/disp32
1989     # . . discard args
1990     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1991     # emit-sib(line, out)
1992     # . . push args
1993     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1994     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1995     # . . call
1996     e8/call  emit-sib/disp32
1997     # . . discard args
1998     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1999     # emit-disp(line, out)
2000     # . . push args
2001     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2002     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2003     # . . call
2004     e8/call  emit-disp/disp32
2005     # . . discard args
2006     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2007     # emit-imm(line, out)
2008     # . . push args
2009     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2010     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2011     # . . call
2012     e8/call  emit-imm/disp32
2013     # . . discard args
2014     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2015     # emit-line-in-comment(line, out)
2016     # . . push args
2017     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2018     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2019     # . . call
2020     e8/call  emit-line-in-comment/disp32
2021     # . . discard args
2022     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2023 $convert-instruction:end:
2024     # . restore locals
2025     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2026     # . restore registers
2027     5a/pop-to-edx
2028     59/pop-to-ecx
2029     58/pop-to-eax
2030     # . epilogue
2031     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2032     5d/pop-to-ebp
2033     c3/return
2034 
2035 emit-opcodes:  # line: (addr stream byte), out: (addr buffered-file)
2036     # opcodes occupy 1-3 bytes:
2037     #   xx
2038     #   0f xx
2039     #   f2 xx
2040     #   f3 xx
2041     #   f2 0f xx
2042     #   f3 0f xx
2043     #   66 xx (hacky support for boot.subx)
2044     #   db xx (hacky support for boot.subx)
2045     #
2046     # pseudocode:
2047     #   rewind-stream(line)
2048     #
2049     #   var op1 = next-word(line)
2050     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
2051     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
2052     #   write-slice-buffered(out, op1)
2053     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3") && !slice-equal?(op1, "66") && !slice-equal?(op1, "db")
2054     #     return
2055     #
2056     #   var op2 = next-word(line)
2057     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
2058     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
2059     #   write-slice-buffered(out, op2)
2060     #   if slice-equal?(op1, "0f")
2061     #     return
2062     #   if !slice-equal?(op2, "0f")
2063     #     return
2064     #
2065     #   var op3 = next-word(line)
2066     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
2067     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
2068     #   write-slice-buffered(out, op3)
2069     #
2070     # . prologue
2071     55/push-ebp
2072     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2073     # . save registers
2074     50/push-eax
2075     51/push-ecx
2076     52/push-edx
2077     53/push-ebx
2078     # var op1/ecx: slice
2079     68/push  0/imm32/end
2080     68/push  0/imm32/start
2081     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2082     # var op2/edx: slice
2083     68/push  0/imm32/end
2084     68/push  0/imm32/start
2085     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
2086     # rewind-stream(line)
2087     # . . push args
2088     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2089     # . . call
2090     e8/call  rewind-stream/disp32
2091     # . . discard args
2092     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2093 $emit-opcodes:op1:
2094     # next-word(line, op1)
2095     # . . push args
2096     51/push-ecx
2097     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2098     # . . call
2099     e8/call  next-word/disp32
2100     # . . discard args
2101     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2102     # if (slice-empty?(op1)) return
2103     # . eax = slice-empty?(op1)
2104     # . . push args
2105     51/push-ecx
2106     # . . call
2107     e8/call  slice-empty?/disp32
2108     # . . discard args
2109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2110     # . if (eax != false) return
2111     3d/compare-eax-and  0/imm32/false
2112     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2113     # if (slice-starts-with?(op1, "#")) return
2114     # . var start/ebx: (addr byte) = op1->start
2115     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # copy *ecx to ebx
2116     # . var c/eax: byte = *start
2117     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2118     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2119     # . if (c == '#') return
2120     3d/compare-eax-and  0x23/imm32/hash
2121     0f 84/jump-if-=  $emit-opcodes:end/disp32
2122     # op1 = next-token-from-slice(op1->start, op1->end, '/')
2123     # . . push args
2124     51/push-ecx
2125     68/push  0x2f/imm32/slash
2126     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
2127     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
2128     # . . call
2129     e8/call  next-token-from-slice/disp32
2130     # . . discard args
2131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2132     # write-slice-buffered(out, op1)
2133     # . . push args
2134     51/push-ecx
2135     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2136     # . . call
2137     e8/call  write-slice-buffered/disp32
2138     # . . discard args
2139     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2140     # write-buffered(out, " ")
2141     # . . push args
2142     68/push  Space/imm32
2143     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2144     # . . call
2145     e8/call  write-buffered/disp32
2146     # . . discard args
2147     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2148     # if (slice-equal?(op1, "0f")) goto op2
2149     # . eax = slice-equal?(op1, "0f")
2150     # . . push args
2151     68/push  "0f"/imm32
2152     51/push-ecx
2153     # . . call
2154     e8/call  slice-equal?/disp32
2155     # . . discard args
2156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2157     # . if (eax != false) goto op2
2158     3d/compare-eax-and  0/imm32/false
2159     75/jump-if-!=  $emit-opcodes:op2/disp8
2160     # if (slice-equal?(op1, "f2")) goto op2
2161     # . eax = slice-equal?(op1, "f2")
2162     # . . push args
2163     68/push  "f2"/imm32
2164     51/push-ecx
2165     # . . call
2166     e8/call  slice-equal?/disp32
2167     # . . discard args
2168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2169     # . if (eax != false) goto op2
2170     3d/compare-eax-and  0/imm32/false
2171     75/jump-if-!=  $emit-opcodes:op2/disp8
2172     # if (slice-equal?(op1, "f3")) goto op2
2173     # . eax = slice-equal?(op1, "f3")
2174     # . . push args
2175     68/push  "f3"/imm32
2176     51/push-ecx
2177     # . . call
2178     e8/call  slice-equal?/disp32
2179     # . . discard args
2180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2181     # . if (eax != false) goto op2
2182     3d/compare-eax-and  0/imm32/false
2183     75/jump-if-!=  $emit-opcodes:op2/disp8
2184     # if (slice-equal?(op1, "66")) goto op2
2185     # . eax = slice-equal?(op1, "66")
2186     # . . push args
2187     68/push  "66"/imm32
2188     51/push-ecx
2189     # . . call
2190     e8/call  slice-equal?/disp32
2191     # . . discard args
2192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2193     # . if (eax != false) goto op2
2194     3d/compare-eax-and  0/imm32/false
2195     75/jump-if-!=  $emit-opcodes:op2/disp8
2196     # if (slice-equal?(op1, "db")) goto op2
2197     # . eax = slice-equal?(op1, "db")
2198     # . . push args
2199     68/push  "db"/imm32
2200     51/push-ecx
2201     # . . call
2202     e8/call  slice-equal?/disp32
2203     # . . discard args
2204     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2205     # . if (eax != false) goto op2
2206     3d/compare-eax-and  0/imm32/false
2207     75/jump-if-!=  $emit-opcodes:op2/disp8
2208     # otherwise return
2209     e9/jump  $emit-opcodes:end/disp32
2210 $emit-opcodes:op2:
2211     # next-word(line, op2)
2212     # . . push args
2213     52/push-edx
2214     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2215     # . . call
2216     e8/call  next-word/disp32
2217     # . . discard args
2218     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2219     # if (slice-empty?(op2)) return
2220     # . eax = slice-empty?(op2)
2221     # . . push args
2222     52/push-edx
2223     # . . call
2224     e8/call  slice-empty?/disp32
2225     # . . discard args
2226     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2227     # . if (eax != false) return
2228     3d/compare-eax-and  0/imm32/false
2229     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2230     # if (slice-starts-with?(op2, "#")) return
2231     # . var start/ebx: (addr byte) = op2->start
2232     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
2233     # . var c/eax: byte = *start
2234     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2235     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2236     # . if (c == '#') return
2237     3d/compare-eax-and  0x23/imm32/hash
2238     0f 84/jump-if-=  $emit-opcodes:end/disp32
2239     # op2 = next-token-from-slice(op2->start, op2->end, '/')
2240     # . . push args
2241     52/push-edx
2242     68/push  0x2f/imm32/slash
2243     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2244     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2245     # . . call
2246     e8/call  next-token-from-slice/disp32
2247     # . . discard args
2248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2249     # write-slice-buffered(out, op2)
2250     # . . push args
2251     52/push-edx
2252     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2253     # . . call
2254     e8/call  write-slice-buffered/disp32
2255     # . . discard args
2256     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2257     # write-buffered(out, " ")
2258     # . . push args
2259     68/push  Space/imm32
2260     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2261     # . . call
2262     e8/call  write-buffered/disp32
2263     # . . discard args
2264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2265     # if (slice-equal?(op1, "0f")) return
2266     # . eax = slice-equal?(op1, "0f")
2267     # . . push args
2268     68/push  "0f"/imm32
2269     51/push-ecx
2270     # . . call
2271     e8/call  slice-equal?/disp32
2272     # . . discard args
2273     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2274     # . if (eax != false) return
2275     3d/compare-eax-and  0/imm32/false
2276     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2277     # if (!slice-equal?(op2, "0f")) return
2278     # . eax = slice-equal?(op2, "0f")
2279     # . . push args
2280     68/push  "0f"/imm32
2281     52/push-edx
2282     # . . call
2283     e8/call  slice-equal?/disp32
2284     # . . discard args
2285     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2286     # . if (eax == false) return
2287     3d/compare-eax-and  0/imm32/false
2288     0f 84/jump-if-=  $emit-opcodes:end/disp32
2289 $emit-opcodes:op3:
2290     # next-word(line, op3)  # reuse op2/edx
2291     # . . push args
2292     52/push-edx
2293     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2294     # . . call
2295     e8/call  next-word/disp32
2296     # . . discard args
2297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2298     # if (slice-empty?(op3)) return
2299     # . eax = slice-empty?(op3)
2300     # . . push args
2301     52/push-edx
2302     # . . call
2303     e8/call  slice-empty?/disp32
2304     # . . discard args
2305     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2306     # . if (eax != false) return
2307     3d/compare-eax-and  0/imm32/false
2308     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2309     # if (slice-starts-with?(op3, "#")) return
2310     # . var start/ebx: (addr byte) = op2->start
2311     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
2312     # . var c/eax: byte = *start
2313     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2314     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2315     # . if (c == '#') return
2316     3d/compare-eax-and  0x23/imm32/hash
2317     0f 84/jump-if-=  $emit-opcodes:end/disp32
2318     # op3 = next-token-from-slice(op3->start, op3->end, '/')
2319     # . . push args
2320     52/push-edx
2321     68/push  0x2f/imm32/slash
2322     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2323     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2324     # . . call
2325     e8/call  next-token-from-slice/disp32
2326     # . . discard args
2327     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2328     # write-slice-buffered(out, op3)
2329     # . . push args
2330     52/push-edx
2331     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2332     # . . call
2333     e8/call  write-slice-buffered/disp32
2334     # . . discard args
2335     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2336     # write-buffered(out, " ")
2337     # . . push args
2338     68/push  Space/imm32
2339     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2340     # . . call
2341     e8/call  write-buffered/disp32
2342     # . . discard args
2343     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2344 $emit-opcodes:end:
2345     # . restore locals
2346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2347     # . restore registers
2348     5b/pop-to-ebx
2349     5a/pop-to-edx
2350     59/pop-to-ecx
2351     58/pop-to-eax
2352     # . epilogue
2353     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2354     5d/pop-to-ebp
2355     c3/return
2356 
2357 emit-modrm:  # line: (addr stream byte), out: (addr buffered-file)
2358     # pseudocode:
2359     #   rewind-stream(line)
2360     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
2361     #   var word-slice: slice
2362     #   while true
2363     #     word-slice = next-word(line)
2364     #     if (slice-empty?(word-slice)) break
2365     #     if (slice-starts-with?(word-slice, "#")) break
2366     #     if (has-metadata?(word-slice, "mod"))
2367     #       mod = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2368     #       has-modrm? = true
2369     #     else if has-metadata?(word-slice, "rm32") or has-metadata?(word-slice, "xm32")
2370     #       rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2371     #       has-modrm? = true
2372     #     else if has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "x32") or has-metadata?(word-slice, "subop")
2373     #       r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2374     #       has-modrm? = true
2375     #   if has-modrm?
2376     #     var modrm = mod & 0b11
2377     #     modrm <<= 3
2378     #     modrm |= r32 & 0b111
2379     #     modrm <<= 3
2380     #     modrm |= rm32 & 0b111
2381     #     emit-hex(out, modrm, 1)
2382     #
2383     # . prologue
2384     55/push-ebp
2385     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2386     # . save registers
2387     50/push-eax
2388     51/push-ecx
2389     52/push-edx
2390     53/push-ebx
2391     56/push-esi
2392     57/push-edi
2393     # var word-slice/ecx: slice
2394     68/push  0/imm32/end
2395     68/push  0/imm32/start
2396     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2397     # var has-modrm?/edx: boolean = false
2398     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
2399     # var mod/ebx: byte = 0
2400     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2401     # var rm32/esi: byte = 0
2402     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
2403     # var r32/edi: byte = 0
2404     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
2405     # rewind-stream(line)
2406     # . . push args
2407     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2408     # . . call
2409     e8/call  rewind-stream/disp32
2410     # . . discard args
2411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2412 +-- 33 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
2445 $emit-modrm:loop:
2446     # next-word(line, word-slice)
2447     # . . push args
2448     51/push-ecx
2449     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2450     # . . call
2451     e8/call  next-word/disp32
2452     # . . discard args
2453     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2454 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
2494 $emit-modrm:check0:
2495     # if (slice-empty?(word-slice)) break
2496     # . eax = slice-empty?(word-slice)
2497     # . . push args
2498     51/push-ecx
2499     # . . call
2500     e8/call  slice-empty?/disp32
2501     # . . discard args
2502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2503     # . if (eax != false) pass through
2504     3d/compare-eax-and  0/imm32/false
2505     0f 85/jump-if-!=  $emit-modrm:break/disp32
2506 $emit-modrm:check1:
2507     # if (slice-starts-with?(word-slice, "#")) break
2508     # . spill edx
2509     52/push-edx
2510     # . var start/edx: (addr byte) = word-slice->start
2511     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2512     # . var c/eax: byte = *start
2513     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2514     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
2515     # . restore edx
2516     5a/pop-to-edx
2517     # . if (c == '#') pass through
2518     3d/compare-eax-and  0x23/imm32/hash
2519     0f 84/jump-if-=  $emit-modrm:break/disp32
2520 $emit-modrm:check-for-mod:
2521     # if (has-metadata?(word-slice, "mod"))
2522     # . eax = has-metadata?(ecx, "mod")
2523     # . . push args
2524     68/push  "mod"/imm32
2525     51/push-ecx
2526     # . . call
2527     e8/call  has-metadata?/disp32
2528     # . . discard args
2529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2530     # . if (eax == false) goto next check
2531     3d/compare-eax-and  0/imm32/false
2532     74/jump-if-=  $emit-modrm:check-for-rm32/disp8
2533 $emit-modrm:mod:
2534     # mod = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2535     # . eax = parse-datum-of-word(word-slice)
2536     # . . push args
2537     51/push-ecx
2538     # . . call
2539     e8/call  parse-datum-of-word/disp32
2540     # . . discard args
2541     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2542     # . mod = eax
2543     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2544     # has-modrm? = true
2545     ba/copy-to-edx  1/imm32/true
2546     # continue
2547     e9/jump  $emit-modrm:loop/disp32
2548 $emit-modrm:check-for-rm32:
2549     # if (has-metadata?(word-slice, "rm32"))
2550     # . eax = has-metadata?(ecx, "rm32")
2551     # . . push args
2552     68/push  "rm32"/imm32
2553     51/push-ecx
2554     # . . call
2555     e8/call  has-metadata?/disp32
2556     # . . discard args
2557     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2558     # . if (eax == false) goto next check
2559     3d/compare-eax-and  0/imm32/false
2560     74/jump-if-=  $emit-modrm:check-for-xm32/disp8
2561 $emit-modrm:rm32:
2562     # rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2563     # . eax = parse-datum-of-word(word-slice)
2564     # . . push args
2565     51/push-ecx
2566     # . . call
2567     e8/call  parse-datum-of-word/disp32
2568     # . . discard args
2569     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2570     # . rm32 = eax
2571     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2572     # has-modrm? = true
2573     ba/copy-to-edx  1/imm32/true
2574     # continue
2575     e9/jump  $emit-modrm:loop/disp32
2576 $emit-modrm:check-for-xm32:
2577     # if (has-metadata?(word-slice, "xm32"))
2578     # . eax = has-metadata?(ecx, "xm32")
2579     # . . push args
2580     68/push  "xm32"/imm32
2581     51/push-ecx
2582     # . . call
2583     e8/call  has-metadata?/disp32
2584     # . . discard args
2585     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2586     # . if (eax == false) goto next check
2587     3d/compare-eax-and  0/imm32/false
2588     74/jump-if-=  $emit-modrm:check-for-r32/disp8
2589 $emit-modrm:xm32:
2590     # rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2591     # . eax = parse-datum-of-word(word-slice)
2592     # . . push args
2593     51/push-ecx
2594     # . . call
2595     e8/call  parse-datum-of-word/disp32
2596     # . . discard args
2597     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2598     # . rm32 = eax
2599     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2600     # has-modrm? = true
2601     ba/copy-to-edx  1/imm32/true
2602     # continue
2603     e9/jump  $emit-modrm:loop/disp32
2604 $emit-modrm:check-for-r32:
2605     # if (has-metadata?(word-slice, "r32"))
2606     # . eax = has-metadata?(ecx, "r32")
2607     # . . push args
2608     68/push  "r32"/imm32
2609     51/push-ecx
2610     # . . call
2611     e8/call  has-metadata?/disp32
2612     # . . discard args
2613     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2614     # . if (eax == false) goto next check
2615     3d/compare-eax-and  0/imm32/false
2616     74/jump-if-=  $emit-modrm:check-for-x32/disp8
2617 $emit-modrm:r32:
2618     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2619     # . eax = parse-datum-of-word(word-slice)
2620     # . . push args
2621     51/push-ecx
2622     # . . call
2623     e8/call  parse-datum-of-word/disp32
2624     # . . discard args
2625     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2626     # . r32 = eax
2627     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2628     # has-modrm? = true
2629     ba/copy-to-edx  1/imm32/true
2630     # continue
2631     e9/jump  $emit-modrm:loop/disp32
2632 $emit-modrm:check-for-x32:
2633     # if (has-metadata?(word-slice, "x32"))
2634     # . eax = has-metadata?(ecx, "x32")
2635     # . . push args
2636     68/push  "x32"/imm32
2637     51/push-ecx
2638     # . . call
2639     e8/call  has-metadata?/disp32
2640     # . . discard args
2641     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2642     # . if (eax == false) goto next check
2643     3d/compare-eax-and  0/imm32/false
2644     74/jump-if-=  $emit-modrm:check-for-subop/disp8
2645 $emit-modrm:x32:
2646     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2647     # . eax = parse-datum-of-word(word-slice)
2648     # . . push args
2649     51/push-ecx
2650     # . . call
2651     e8/call  parse-datum-of-word/disp32
2652     # . . discard args
2653     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2654     # . r32 = eax
2655     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2656     # has-modrm? = true
2657     ba/copy-to-edx  1/imm32/true
2658     # continue
2659     e9/jump  $emit-modrm:loop/disp32
2660 $emit-modrm:check-for-subop:
2661     # if (has-metadata?(word-slice, "subop"))
2662     # . eax = has-metadata?(ecx, "subop")
2663     # . . push args
2664     68/push  "subop"/imm32
2665     51/push-ecx
2666     # . . call
2667     e8/call  has-metadata?/disp32
2668     # . . discard args
2669     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2670     # . if (eax == false) loop
2671     3d/compare-eax-and  0/imm32/false
2672     0f 84/jump-if-=  $emit-modrm:loop/disp32
2673 $emit-modrm:subop:
2674     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2675     # . eax = parse-datum-of-word(word-slice)
2676     # . . push args
2677     51/push-ecx
2678     # . . call
2679     e8/call  parse-datum-of-word/disp32
2680     # . . discard args
2681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2682     # . r32 = eax
2683     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2684     # has-modrm? = true
2685     ba/copy-to-edx  1/imm32/true
2686     # continue
2687     e9/jump  $emit-modrm:loop/disp32
2688 $emit-modrm:break:
2689     # if (!has-modrm?) return
2690     81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
2691     74/jump-if-=  $emit-modrm:end/disp8
2692 $emit-modrm:calculate:
2693     # var modrm/ebx: byte = mod & 0b11
2694     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
2695     # modrm <<= 3
2696     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2697     # modrm |= r32 & 0b111
2698     81          4/subop/and         3/mod/direct    7/rm32/edi    .           .             .           .           .               7/imm32/0b111     # bitwise and of edi
2699     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           7/r32/edi   .               .                 # ebx = bitwise OR with edi
2700     # modrm <<= 3
2701     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2702     # modrm |= rm32 & 0b111
2703     81          4/subop/and         3/mod/direct    6/rm32/esi    .           .             .           .           .               7/imm32/0b111     # bitwise and of esi
2704     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           6/r32/esi   .               .                 # ebx = bitwise OR with esi
2705 $emit-modrm:emit:
2706     # emit-hex(out, modrm, 1)
2707     # . . push args
2708     68/push  1/imm32
2709     53/push-ebx
2710     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2711     # . . call
2712     e8/call  emit-hex/disp32
2713     # . . discard args
2714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2715 $emit-modrm:end:
2716     # . restore locals
2717     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2718     # . restore registers
2719     5f/pop-to-edi
2720     5e/pop-to-esi
2721     5b/pop-to-ebx
2722     5a/pop-to-edx
2723     59/pop-to-ecx
2724     58/pop-to-eax
2725     # . epilogue
2726     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2727     5d/pop-to-ebp
2728     c3/return
2729 
2730 emit-sib:  # line: (addr stream byte), out: (addr buffered-file)
2731     # pseudocode:
2732     #   var has-sib? = false, base = 0, index = 0, scale = 0
2733     #   var word-slice: slice
2734     #   while true
2735     #     word-slice = next-word(line)
2736     #     if (slice-empty?(word-slice)) break
2737     #     if (slice-starts-with?(word-slice, "#")) break
2738     #     if (has-metadata?(word-slice, "base")
2739     #       base = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2740     #       has-sib? = true
2741     #     else if (has-metadata?(word-slice, "index")
2742     #       index = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2743     #       has-sib? = true
2744     #     else if (has-metadata?(word-slice, "scale")
2745     #       scale = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2746     #       has-sib? = true
2747     #   if has-sib?
2748     #     var sib = scale & 0b11
2749     #     sib <<= 3
2750     #     sib |= index & 0b111
2751     #     sib <<= 3
2752     #     sib |= base & 0b111
2753     #     emit-hex(out, sib, 1)
2754     #
2755     # . prologue
2756     55/push-ebp
2757     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2758     # . save registers
2759     50/push-eax
2760     51/push-ecx
2761     52/push-edx
2762     53/push-ebx
2763     56/push-esi
2764     57/push-edi
2765     # var word-slice/ecx: slice
2766     68/push  0/imm32/end
2767     68/push  0/imm32/start
2768     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2769     # var has-sib?/edx: boolean = false
2770     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
2771     # var scale/ebx: byte = 0
2772     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2773     # var base/esi: byte = 0
2774     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
2775     # var index/edi: byte = 0
2776     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
2777     # rewind-stream(line)
2778     # . . push args
2779     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2780     # . . call
2781     e8/call  rewind-stream/disp32
2782     # . . discard args
2783     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2784 $emit-sib:loop:
2785 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
2811     # next-word(line, word-slice)
2812     # . . push args
2813     51/push-ecx
2814     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2815     # . . call
2816     e8/call  next-word/disp32
2817     # . . discard args
2818     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2819 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
2859 $emit-sib:check0:
2860     # if (slice-empty?(word-slice)) break
2861     # . eax = slice-empty?(word-slice)
2862     # . . push args
2863     51/push-ecx
2864     # . . call
2865     e8/call  slice-empty?/disp32
2866     # . . discard args
2867     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2868     # . if (eax != false) pass through
2869     3d/compare-eax-and  0/imm32/false
2870     0f 85/jump-if-!=  $emit-sib:break/disp32
2871 $emit-sib:check1:
2872     # if (slice-starts-with?(word-slice, "#")) break
2873     # . spill edx
2874     52/push-edx
2875     # . var start/edx: (addr byte) = word-slice->start
2876     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2877     # . var c/eax: byte = *start
2878     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2879     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
2880     # . restore edx
2881     5a/pop-to-edx
2882     # . if (c == '#') pass through
2883     3d/compare-eax-and  0x23/imm32/hash
2884     0f 84/jump-if-=  $emit-sib:break/disp32
2885 $emit-sib:check-for-scale:
2886     # if (has-metadata?(word-slice, "scale"))
2887     # . eax = has-metadata?(ecx, "scale")
2888     # . . push args
2889     68/push  "scale"/imm32
2890     51/push-ecx
2891     # . . call
2892     e8/call  has-metadata?/disp32
2893     # . . discard args
2894     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2895     # . if (eax == false) goto next check
2896     3d/compare-eax-and  0/imm32/false
2897     74/jump-if-=  $emit-sib:check-for-base/disp8
2898 $emit-sib:scale:
2899     # scale = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2900     # . eax = parse-datum-of-word(word-slice)
2901     # . . push args
2902     51/push-ecx
2903     # . . call
2904     e8/call  parse-datum-of-word/disp32
2905     # . . discard args
2906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2907     # . scale = eax
2908     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2909     # has-sib? = true
2910     ba/copy-to-edx  1/imm32/true
2911     # continue
2912     e9/jump  $emit-sib:loop/disp32
2913 $emit-sib:check-for-base:
2914     # if (has-metadata?(word-slice, "base"))
2915     # . eax = has-metadata?(ecx, "base")
2916     # . . push args
2917     68/push  "base"/imm32
2918     51/push-ecx
2919     # . . call
2920     e8/call  has-metadata?/disp32
2921     # . . discard args
2922     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2923     # . if (eax == false) goto next check
2924     3d/compare-eax-and  0/imm32/false
2925     74/jump-if-=  $emit-sib:check-for-index/disp8
2926 $emit-sib:base:
2927     # base = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2928     # . eax = parse-datum-of-word(word-slice)
2929     # . . push args
2930     51/push-ecx
2931     # . . call
2932     e8/call  parse-datum-of-word/disp32
2933     # . . discard args
2934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2935     # . base = eax
2936     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2937     # has-sib? = true
2938     ba/copy-to-edx  1/imm32/true
2939     # continue
2940     e9/jump  $emit-sib:loop/disp32
2941 $emit-sib:check-for-index:
2942     # if (has-metadata?(word-slice, "index"))
2943     # . eax = has-metadata?(ecx, "index")
2944     # . . push args
2945     68/push  "index"/imm32
2946     51/push-ecx
2947     # . . call
2948     e8/call  has-metadata?/disp32
2949     # . . discard args
2950     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2951     # . if (eax == false) loop
2952     3d/compare-eax-and  0/imm32/false
2953     0f 84/jump-if-=  $emit-sib:loop/disp32
2954 $emit-sib:index:
2955     # index = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2956     # . eax = parse-datum-of-word(word-slice)
2957     # . . push args
2958     51/push-ecx
2959     # . . call
2960     e8/call  parse-datum-of-word/disp32
2961     # . . discard args
2962     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2963     # . index = eax
2964     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2965     # has-sib? = true
2966     ba/copy-to-edx  1/imm32/true
2967     # continue
2968     e9/jump  $emit-sib:loop/disp32
2969 $emit-sib:break:
2970     # if (!has-sib?) return
2971     81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
2972     74/jump-if-=  $emit-sib:end/disp8
2973 $emit-sib:calculate:
2974     # var sib/ebx: byte = scale & 0b11
2975     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
2976     # sib <<= 2
2977     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2978     # sib |= index & 0b111
2979     81          4/subop/and         3/mod/direct    7/rm32/edi    .           .             .           .           .               7/imm32/0b111     # bitwise and of edi
2980     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           7/r32/edi   .               .                 # ebx = bitwise OR with edi
2981     # sib <<= 3
2982     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2983     # sib |= base & 0b111
2984     81          4/subop/and         3/mod/direct    6/rm32/esi    .           .             .           .           .               7/imm32/0b111     # bitwise and of esi
2985     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           6/r32/esi   .               .                 # ebx = bitwise OR with esi
2986 $emit-sib:emit:
2987     # emit-hex(out, sib, 1)
2988     # . . push args
2989     68/push  1/imm32
2990     53/push-ebx
2991     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2992     # . . call
2993     e8/call  emit-hex/disp32
2994     # . . discard args
2995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2996 $emit-sib:end:
2997     # . restore locals
2998     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2999     # . restore registers
3000     5f/pop-to-edi
3001     5e/pop-to-esi
3002     5b/pop-to-ebx
3003     5a/pop-to-edx
3004     59/pop-to-ecx
3005     58/pop-to-eax
3006     # . epilogue
3007     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3008     5d/pop-to-ebp
3009     c3/return
3010 
3011 emit-disp:  # line: (addr stream byte), out: (addr buffered-file)
3012     # pseudocode:
3013     #   rewind-stream(line)
3014     #   var word-slice: slice
3015     #   while true
3016     #     word-slice = next-word(line)
3017     #     if (slice-empty?(word-slice)) break
3018     #     if (slice-starts-with?(word-slice, "#")) break
3019     #     if has-metadata?(word-slice, "disp32")
3020     #       emit(out, word-slice, 4)
3021     #       break
3022     #     if has-metadata?(word-slice, "disp16")
3023     #       emit(out, word-slice, 2)
3024     #       break
3025     #     if has-metadata?(word-slice, "disp8")
3026     #       emit(out, word-slice, 1)
3027     #       break
3028     #
3029     # . prologue
3030     55/push-ebp
3031     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3032     # . save registers
3033     50/push-eax
3034     51/push-ecx
3035     52/push-edx
3036     # var word-slice/ecx: slice
3037     68/push  0/imm32/end
3038     68/push  0/imm32/start
3039     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3040     # rewind-stream(line)
3041     # . . push args
3042     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3043     # . . call
3044     e8/call  rewind-stream/disp32
3045     # . . discard args
3046     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3047 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
3073 $emit-disp:loop:
3074     # next-word(line, word-slice)
3075     # . . push args
3076     51/push-ecx
3077     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3078     # . . call
3079     e8/call  next-word/disp32
3080     # . . discard args
3081     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3082 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
3122 $emit-disp:check0:
3123     # if (slice-empty?(word-slice)) break
3124     # . eax = slice-empty?(word-slice)
3125     # . . push args
3126     51/push-ecx
3127     # . . call
3128     e8/call  slice-empty?/disp32
3129     # . . discard args
3130     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3131     # . if (eax != false) pass through
3132     3d/compare-eax-and  0/imm32/false
3133     0f 85/jump-if-!=  $emit-disp:break/disp32
3134 $emit-disp:check1:
3135     # if (slice-starts-with?(word-slice, "#")) break
3136     # . var start/edx: (addr byte) = word-slice->start
3137     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
3138     # . var c/eax: byte = *start
3139     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
3140     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
3141     # . if (c == '#') break
3142     3d/compare-eax-and  0x23/imm32/hash
3143     0f 84/jump-if-=  $emit-disp:break/disp32
3144 $emit-disp:check-for-disp32:
3145     # if (has-metadata?(word-slice, "disp32"))
3146     # . eax = has-metadata?(ecx, "disp32")
3147     # . . push args
3148     68/push  "disp32"/imm32
3149     51/push-ecx
3150     # . . call
3151     e8/call  has-metadata?/disp32
3152     # . . discard args
3153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3154     # . if (eax == false) goto next check
3155     3d/compare-eax-and  0/imm32/false
3156     74/jump-if-=  $emit-disp:check-for-disp16/disp8
3157 $emit-disp:disp32:
3158     # emit(out, word-slice, 4)
3159     # . . push args
3160     68/push  4/imm32
3161     51/push-ecx
3162     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3163     # . . call
3164     e8/call  emit/disp32
3165     # . . discard args
3166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3167     # break
3168     e9/jump  $emit-disp:break/disp32
3169 $emit-disp:check-for-disp16:
3170     # else if (has-metadata?(word-slice, "disp16"))
3171     # . eax = has-metadata?(ecx, "disp16")
3172     # . . push args
3173     68/push  "disp16"/imm32
3174     51/push-ecx
3175     # . . call
3176     e8/call  has-metadata?/disp32
3177     # . . discard args
3178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3179     # . if (eax == false) goto next check
3180     3d/compare-eax-and  0/imm32/false
3181     74/jump-if-=  $emit-disp:check-for-disp8/disp8
3182 $emit-disp:disp16:
3183     # emit(out, word-slice, 2)
3184     # . . push args
3185     68/push  2/imm32
3186     51/push-ecx
3187     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3188     # . . call
3189     e8/call  emit/disp32
3190     # . . discard args
3191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3192     # break
3193     e9/jump  $emit-disp:break/disp32
3194 $emit-disp:check-for-disp8:
3195     # if (has-metadata?(word-slice, "disp8"))
3196     # . eax = has-metadata?(ecx, "disp8")
3197     # . . push args
3198     68/push  "disp8"/imm32
3199     51/push-ecx
3200     # . . call
3201     e8/call  has-metadata?/disp32
3202     # . . discard args
3203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3204     # . if (eax == false) loop
3205     3d/compare-eax-and  0/imm32/false
3206     0f 84/jump-if-=  $emit-disp:loop/disp32
3207 $emit-disp:disp8:
3208     # emit(out, word-slice, 1)
3209     # . . push args
3210     68/push  1/imm32
3211     51/push-ecx
3212     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3213     # . . call
3214     e8/call  emit/disp32
3215     # . . discard args
3216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3217     # break
3218 $emit-disp:break:
3219     # . restore locals
3220     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3221     # . restore registers
3222     5a/pop-to-edx
3223     59/pop-to-ecx
3224     58/pop-to-eax
3225     # . epilogue
3226     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3227     5d/pop-to-ebp
3228     c3/return
3229 
3230 emit-imm:  # line: (addr stream byte), out: (addr buffered-file)
3231     # pseudocode:
3232     #   rewind-stream(line)
3233     #   var word-slice: slice
3234     #   while true
3235     #     word-slice = next-word(line)
3236     #     if (slice-empty?(word-slice)) break
3237     #     if (slice-starts-with?(word-slice, "#")) break
3238     #     if has-metadata?(word-slice, "imm32")
3239     #       emit(out, word-slice, 4)
3240     #       break
3241     #     if has-metadata?(word-slice, "imm16")
3242     #       emit(out, word-slice, 2)
3243     #       break
3244     #     if has-metadata?(word-slice, "imm8")
3245     #       emit(out, word-slice, 1)
3246     #       break
3247     #
3248     # . prologue
3249     55/push-ebp
3250     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3251     # . save registers
3252     50/push-eax
3253     51/push-ecx
3254     52/push-edx
3255     # var word-slice/ecx: slice
3256     68/push  0/imm32/end
3257     68/push  0/imm32/start
3258     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3259     # rewind-stream(line)
3260     # . . push args
3261     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3262     # . . call
3263     e8/call  rewind-stream/disp32
3264     # . . discard args
3265     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3266 +-- 26 lines: #?     # dump line -------------------------------------------------------------------------------------------------------------------------------------------
3292 $emit-imm:loop:
3293     # next-word(line, word-slice)
3294     # . . push args
3295     51/push-ecx
3296     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3297     # . . call
3298     e8/call  next-word/disp32
3299     # . . discard args
3300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3301 +-- 40 lines: #?     # dump word-slice -------------------------------------------------------------------------------------------------------------------------------------
3341 $emit-imm:check0:
3342     # if (slice-empty?(word-slice)) break
3343     # . eax = slice-empty?(word-slice)
3344     # . . push args
3345     51/push-ecx
3346     # . . call
3347     e8/call  slice-empty?/disp32
3348     # . . discard args
3349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3350     # . if (eax != false) pass through
3351     3d/compare-eax-and  0/imm32/false
3352     0f 85/jump-if-!=  $emit-imm:break/disp32
3353 $emit-imm:check1:
3354     # if (slice-starts-with?(word-slice, "#")) break
3355     # . var start/edx: (addr byte) = slice->start
3356     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
3357     # . var c/eax: byte = *start
3358     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
3359     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
3360     # . if (c == '#') break
3361     3d/compare-eax-and  0x23/imm32/hash
3362     0f 84/jump-if-=  $emit-imm:break/disp32
3363 $emit-imm:check-for-imm32:
3364     # if (has-metadata?(word-slice, "imm32"))
3365     # . eax = has-metadata?(ecx, "imm32")
3366     # . . push args
3367     68/push  "imm32"/imm32
3368     51/push-ecx
3369     # . . call
3370     e8/call  has-metadata?/disp32
3371     # . . discard args
3372     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3373     # . if (eax == false) goto next check
3374     3d/compare-eax-and  0/imm32/false
3375     74/jump-if-=  $emit-imm:check-for-imm16/disp8
3376 $emit-imm:imm32:
3377     # emit(out, word-slice, 4)
3378     # . . push args
3379     68/push  4/imm32
3380     51/push-ecx
3381     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3382     # . . call
3383     e8/call  emit/disp32
3384     # . . discard args
3385     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3386     # break
3387     e9/jump  $emit-imm:break/disp32
3388 $emit-imm:check-for-imm16:
3389     # if (has-metadata?(word-slice, "imm16"))
3390     # . eax = has-metadata?(ecx, "imm16")
3391     # . . push args
3392     68/push  "imm16"/imm32
3393     51/push-ecx
3394     # . . call
3395     e8/call  has-metadata?/disp32
3396     # . . discard args
3397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3398     # . if (eax == false) goto next check
3399     3d/compare-eax-and  0/imm32/false
3400     74/jump-if-=  $emit-imm:check-for-imm8/disp8
3401 $emit-imm:imm16:
3402     # emit(out, word-slice, 2)
3403     # . . push args
3404     68/push  2/imm32
3405     51/push-ecx
3406     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3407     # . . call
3408     e8/call  emit/disp32
3409     # . . discard args
3410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3411     # break
3412     e9/jump  $emit-imm:break/disp32
3413 $emit-imm:check-for-imm8:
3414     # if (has-metadata?(word-slice, "imm8"))
3415     # . eax = has-metadata?(ecx, "imm8")
3416     # . . push args
3417     68/push  "imm8"/imm32
3418     51/push-ecx
3419     # . . call
3420     e8/call  has-metadata?/disp32
3421     # . . discard args
3422     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3423     # . if (eax == false) loop
3424     3d/compare-eax-and  0/imm32/false
3425     0f 84/jump-if-=  $emit-imm:loop/disp32
3426 $emit-imm:imm8:
3427     # emit(out, word-slice, 1)
3428     # . . push args
3429     68/push  1/imm32
3430     51/push-ecx
3431     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3432     # . . call
3433     e8/call  emit/disp32
3434     # . . discard args
3435     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3436     # break
3437 $emit-imm:break:
3438     # . restore locals
3439     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3440     # . restore registers
3441     5a/pop-to-edx
3442     59/pop-to-ecx
3443     58/pop-to-eax
3444     # . epilogue
3445     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3446     5d/pop-to-ebp
3447     c3/return
3448 
3449 emit-line-in-comment:  # line: (addr stream byte), out: (addr buffered-file)
3450     # . prologue
3451     55/push-ebp
3452     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3453     # write-buffered(out, " # ")
3454     # . . push args
3455     68/push  " # "/imm32
3456     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3457     # . . call
3458     e8/call  write-buffered/disp32
3459     # . . discard args
3460     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3461     # write-stream-data(out, line)
3462     # . . push args
3463     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3464     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3465     # . . call
3466     e8/call  write-stream-data/disp32
3467     # . . discard args
3468     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3469 $emit-line-in-comment:end:
3470     # . epilogue
3471     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3472     5d/pop-to-ebp
3473     c3/return
3474 
3475 test-convert-instruction-passes-comments-through:
3476     # if a line starts with '#', pass it along unchanged
3477     # . prologue
3478     55/push-ebp
3479     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3480     # setup
3481     # . clear-stream(_test-input-stream)
3482     # . . push args
3483     68/push  _test-input-stream/imm32
3484     # . . call
3485     e8/call  clear-stream/disp32
3486     # . . discard args
3487     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3488     # . clear-stream(_test-output-stream)
3489     # . . push args
3490     68/push  _test-output-stream/imm32
3491     # . . call
3492     e8/call  clear-stream/disp32
3493     # . . discard args
3494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3495     # . clear-stream($_test-output-buffered-file->buffer)
3496     # . . push args
3497     68/push  $_test-output-buffered-file->buffer/imm32
3498     # . . call
3499     e8/call  clear-stream/disp32
3500     # . . discard args
3501     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3502     # initialize input
3503     # . write(_test-input-stream, "# abcd")
3504     # . . push args
3505     68/push  "# abcd"/imm32
3506     68/push  _test-input-stream/imm32
3507     # . . call
3508     e8/call  write/disp32
3509     # . . discard args
3510     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3511     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3512     # . . push args
3513     68/push  _test-output-buffered-file/imm32
3514     68/push  _test-input-stream/imm32
3515     # . . call
3516     e8/call  convert-instruction/disp32
3517     # . . discard args
3518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3519     # check that the line just passed through
3520     # . flush(_test-output-buffered-file)
3521     # . . push args
3522     68/push  _test-output-buffered-file/imm32
3523     # . . call
3524     e8/call  flush/disp32
3525     # . . discard args
3526     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3527     # . check-stream-equal(_test-output-stream, "# abcd", msg)
3528     # . . push args
3529     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
3530     68/push  "# abcd"/imm32
3531     68/push  _test-output-stream/imm32
3532     # . . call
3533     e8/call  check-stream-equal/disp32
3534     # . . discard args
3535     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3536     # . epilogue
3537     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3538     5d/pop-to-ebp
3539     c3/return
3540 
3541 test-convert-instruction-passes-labels-through:
3542     # if the first word ends with ':', pass along the entire line unchanged
3543     # . prologue
3544     55/push-ebp
3545     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3546     # setup
3547     # . clear-stream(_test-input-stream)
3548     # . . push args
3549     68/push  _test-input-stream/imm32
3550     # . . call
3551     e8/call  clear-stream/disp32
3552     # . . discard args
3553     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3554     # . clear-stream(_test-output-stream)
3555     # . . push args
3556     68/push  _test-output-stream/imm32
3557     # . . call
3558     e8/call  clear-stream/disp32
3559     # . . discard args
3560     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3561     # . clear-stream($_test-output-buffered-file->buffer)
3562     # . . push args
3563     68/push  $_test-output-buffered-file->buffer/imm32
3564     # . . call
3565     e8/call  clear-stream/disp32
3566     # . . discard args
3567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3568     # initialize input
3569     # . write(_test-input-stream, "ab: # cd")
3570     # . . push args
3571     68/push  "ab: # cd"/imm32
3572     68/push  _test-input-stream/imm32
3573     # . . call
3574     e8/call  write/disp32
3575     # . . discard args
3576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3577     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3578     # . . push args
3579     68/push  _test-output-buffered-file/imm32
3580     68/push  _test-input-stream/imm32
3581     # . . call
3582     e8/call  convert-instruction/disp32
3583     # . . discard args
3584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3585     # check that the line just passed through
3586     # . flush(_test-output-buffered-file)
3587     # . . push args
3588     68/push  _test-output-buffered-file/imm32
3589     # . . call
3590     e8/call  flush/disp32
3591     # . . discard args
3592     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3593     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
3594     # . . push args
3595     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
3596     68/push  "ab: # cd"/imm32
3597     68/push  _test-output-stream/imm32
3598     # . . call
3599     e8/call  check-stream-equal/disp32
3600     # . . discard args
3601     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3602     # . epilogue
3603     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3604     5d/pop-to-ebp
3605     c3/return
3606 
3607 test-convert-instruction-handles-single-opcode:
3608     # if the instruction consists of a single opcode, strip its metadata and pass it along
3609     # . prologue
3610     55/push-ebp
3611     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3612     # setup
3613     # . clear-stream(_test-input-stream)
3614     # . . push args
3615     68/push  _test-input-stream/imm32
3616     # . . call
3617     e8/call  clear-stream/disp32
3618     # . . discard args
3619     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3620     # . clear-stream(_test-output-stream)
3621     # . . push args
3622     68/push  _test-output-stream/imm32
3623     # . . call
3624     e8/call  clear-stream/disp32
3625     # . . discard args
3626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3627     # . clear-stream($_test-output-buffered-file->buffer)
3628     # . . push args
3629     68/push  $_test-output-buffered-file->buffer/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     # initialize input
3635     # . write(_test-input-stream, "ab/cd # comment")
3636     # . . push args
3637     68/push  "ab/cd # comment"/imm32
3638     68/push  _test-input-stream/imm32
3639     # . . call
3640     e8/call  write/disp32
3641     # . . discard args
3642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3643     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3644     # . . push args
3645     68/push  _test-output-buffered-file/imm32
3646     68/push  _test-input-stream/imm32
3647     # . . call
3648     e8/call  convert-instruction/disp32
3649     # . . discard args
3650     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3651     # check output
3652     # . flush(_test-output-buffered-file)
3653     # . . push args
3654     68/push  _test-output-buffered-file/imm32
3655     # . . call
3656     e8/call  flush/disp32
3657     # . . discard args
3658     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3659 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
3685     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
3686     # . . push args
3687     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
3688     68/push  "ab  # ab/cd # comment"/imm32
3689     68/push  _test-output-stream/imm32
3690     # . . call
3691     e8/call  check-stream-equal/disp32
3692     # . . discard args
3693     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3694     # . epilogue
3695     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3696     5d/pop-to-ebp
3697     c3/return
3698 
3699 test-convert-instruction-handles-0f-opcode:
3700     # if the instruction starts with 0f opcode, include a second opcode
3701     # . prologue
3702     55/push-ebp
3703     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3704     # setup
3705     # . clear-stream(_test-input-stream)
3706     # . . push args
3707     68/push  _test-input-stream/imm32
3708     # . . call
3709     e8/call  clear-stream/disp32
3710     # . . discard args
3711     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3712     # . clear-stream(_test-output-stream)
3713     # . . push args
3714     68/push  _test-output-stream/imm32
3715     # . . call
3716     e8/call  clear-stream/disp32
3717     # . . discard args
3718     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3719     # . clear-stream($_test-output-buffered-file->buffer)
3720     # . . push args
3721     68/push  $_test-output-buffered-file->buffer/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     # initialize input
3727     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
3728     # . . push args
3729     68/push  "0f/m1 ab/m2 # comment"/imm32
3730     68/push  _test-input-stream/imm32
3731     # . . call
3732     e8/call  write/disp32
3733     # . . discard args
3734     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3735     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3736     # . . push args
3737     68/push  _test-output-buffered-file/imm32
3738     68/push  _test-input-stream/imm32
3739     # . . call
3740     e8/call  convert-instruction/disp32
3741     # . . discard args
3742     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3743     # check output
3744     # . flush(_test-output-buffered-file)
3745     # . . push args
3746     68/push  _test-output-buffered-file/imm32
3747     # . . call
3748     e8/call  flush/disp32
3749     # . . discard args
3750     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3751 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
3777     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
3778     # . . push args
3779     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
3780     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
3781     68/push  _test-output-stream/imm32
3782     # . . call
3783     e8/call  check-stream-equal/disp32
3784     # . . discard args
3785     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3786     # . epilogue
3787     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3788     5d/pop-to-ebp
3789     c3/return
3790 
3791 test-convert-instruction-handles-f2-opcode:
3792     # if the instruction starts with f2 opcode, include a second opcode
3793     # . prologue
3794     55/push-ebp
3795     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3796     # setup
3797     # . clear-stream(_test-input-stream)
3798     # . . push args
3799     68/push  _test-input-stream/imm32
3800     # . . call
3801     e8/call  clear-stream/disp32
3802     # . . discard args
3803     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3804     # . clear-stream(_test-output-stream)
3805     # . . push args
3806     68/push  _test-output-stream/imm32
3807     # . . call
3808     e8/call  clear-stream/disp32
3809     # . . discard args
3810     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3811     # . clear-stream($_test-output-buffered-file->buffer)
3812     # . . push args
3813     68/push  $_test-output-buffered-file->buffer/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     # initialize input
3819     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
3820     # . . push args
3821     68/push  "f2/m1 ab/m2 # comment"/imm32
3822     68/push  _test-input-stream/imm32
3823     # . . call
3824     e8/call  write/disp32
3825     # . . discard args
3826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3827     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3828     # . . push args
3829     68/push  _test-output-buffered-file/imm32
3830     68/push  _test-input-stream/imm32
3831     # . . call
3832     e8/call  convert-instruction/disp32
3833     # . . discard args
3834     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3835     # check output
3836     # . flush(_test-output-buffered-file)
3837     # . . push args
3838     68/push  _test-output-buffered-file/imm32
3839     # . . call
3840     e8/call  flush/disp32
3841     # . . discard args
3842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3843 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
3869     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
3870     # . . push args
3871     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
3872     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
3873     68/push  _test-output-stream/imm32
3874     # . . call
3875     e8/call  check-stream-equal/disp32
3876     # . . discard args
3877     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3878     # . epilogue
3879     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3880     5d/pop-to-ebp
3881     c3/return
3882 
3883 test-convert-instruction-handles-f3-opcode:
3884     # if the instruction starts with f3 opcode, include a second opcode
3885     # . prologue
3886     55/push-ebp
3887     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3888     # setup
3889     # . clear-stream(_test-input-stream)
3890     # . . push args
3891     68/push  _test-input-stream/imm32
3892     # . . call
3893     e8/call  clear-stream/disp32
3894     # . . discard args
3895     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3896     # . clear-stream(_test-output-stream)
3897     # . . push args
3898     68/push  _test-output-stream/imm32
3899     # . . call
3900     e8/call  clear-stream/disp32
3901     # . . discard args
3902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3903     # . clear-stream($_test-output-buffered-file->buffer)
3904     # . . push args
3905     68/push  $_test-output-buffered-file->buffer/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     # initialize input
3911     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
3912     # . . push args
3913     68/push  "f3/m1 ab/m2 # comment"/imm32
3914     68/push  _test-input-stream/imm32
3915     # . . call
3916     e8/call  write/disp32
3917     # . . discard args
3918     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3919     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3920     # . . push args
3921     68/push  _test-output-buffered-file/imm32
3922     68/push  _test-input-stream/imm32
3923     # . . call
3924     e8/call  convert-instruction/disp32
3925     # . . discard args
3926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3927     # check output
3928     # . flush(_test-output-buffered-file)
3929     # . . push args
3930     68/push  _test-output-buffered-file/imm32
3931     # . . call
3932     e8/call  flush/disp32
3933     # . . discard args
3934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3935 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
3961     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
3962     # . . push args
3963     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
3964     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
3965     68/push  _test-output-stream/imm32
3966     # . . call
3967     e8/call  check-stream-equal/disp32
3968     # . . discard args
3969     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3970     # . epilogue
3971     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3972     5d/pop-to-ebp
3973     c3/return
3974 
3975 test-convert-instruction-handles-f2-0f-opcode:
3976     # if the instruction starts with f2 0f opcode, include a second opcode
3977     # . prologue
3978     55/push-ebp
3979     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3980     # setup
3981     # . clear-stream(_test-input-stream)
3982     # . . push args
3983     68/push  _test-input-stream/imm32
3984     # . . call
3985     e8/call  clear-stream/disp32
3986     # . . discard args
3987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3988     # . clear-stream(_test-output-stream)
3989     # . . push args
3990     68/push  _test-output-stream/imm32
3991     # . . call
3992     e8/call  clear-stream/disp32
3993     # . . discard args
3994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3995     # . clear-stream($_test-output-buffered-file->buffer)
3996     # . . push args
3997     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4003     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
4004     # . . push args
4005     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
4006     68/push  _test-input-stream/imm32
4007     # . . call
4008     e8/call  write/disp32
4009     # . . discard args
4010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4011     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4012     # . . push args
4013     68/push  _test-output-buffered-file/imm32
4014     68/push  _test-input-stream/imm32
4015     # . . call
4016     e8/call  convert-instruction/disp32
4017     # . . discard args
4018     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4019     # check output
4020     # . flush(_test-output-buffered-file)
4021     # . . push args
4022     68/push  _test-output-buffered-file/imm32
4023     # . . call
4024     e8/call  flush/disp32
4025     # . . discard args
4026     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4027 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4053     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
4054     # . . push args
4055     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
4056     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
4057     68/push  _test-output-stream/imm32
4058     # . . call
4059     e8/call  check-stream-equal/disp32
4060     # . . discard args
4061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4062     # . epilogue
4063     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4064     5d/pop-to-ebp
4065     c3/return
4066 
4067 test-convert-instruction-handles-f3-0f-opcode:
4068     # if the instruction starts with f3 0f opcode, include a second opcode
4069     # . prologue
4070     55/push-ebp
4071     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4072     # setup
4073     # . clear-stream(_test-input-stream)
4074     # . . push args
4075     68/push  _test-input-stream/imm32
4076     # . . call
4077     e8/call  clear-stream/disp32
4078     # . . discard args
4079     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4080     # . clear-stream(_test-output-stream)
4081     # . . push args
4082     68/push  _test-output-stream/imm32
4083     # . . call
4084     e8/call  clear-stream/disp32
4085     # . . discard args
4086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4087     # . clear-stream($_test-output-buffered-file->buffer)
4088     # . . push args
4089     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4095     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
4096     # . . push args
4097     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
4098     68/push  _test-input-stream/imm32
4099     # . . call
4100     e8/call  write/disp32
4101     # . . discard args
4102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4103     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4104     # . . push args
4105     68/push  _test-output-buffered-file/imm32
4106     68/push  _test-input-stream/imm32
4107     # . . call
4108     e8/call  convert-instruction/disp32
4109     # . . discard args
4110     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4111     # check output
4112     # . flush(_test-output-buffered-file)
4113     # . . push args
4114     68/push  _test-output-buffered-file/imm32
4115     # . . call
4116     e8/call  flush/disp32
4117     # . . discard args
4118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4119 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4145     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4146     # . . push args
4147     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
4148     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
4149     68/push  _test-output-stream/imm32
4150     # . . call
4151     e8/call  check-stream-equal/disp32
4152     # . . discard args
4153     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4154     # . epilogue
4155     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4156     5d/pop-to-ebp
4157     c3/return
4158 
4159 test-convert-instruction-handles-unused-opcodes:
4160     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
4161     # . prologue
4162     55/push-ebp
4163     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4164     # setup
4165     # . clear-stream(_test-input-stream)
4166     # . . push args
4167     68/push  _test-input-stream/imm32
4168     # . . call
4169     e8/call  clear-stream/disp32
4170     # . . discard args
4171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4172     # . clear-stream(_test-output-stream)
4173     # . . push args
4174     68/push  _test-output-stream/imm32
4175     # . . call
4176     e8/call  clear-stream/disp32
4177     # . . discard args
4178     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4179     # . clear-stream($_test-output-buffered-file->buffer)
4180     # . . push args
4181     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4187     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
4188     # . . push args
4189     68/push  "ab/m1 cd/m2 # comment"/imm32
4190     68/push  _test-input-stream/imm32
4191     # . . call
4192     e8/call  write/disp32
4193     # . . discard args
4194     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4195     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4196     # . . push args
4197     68/push  _test-output-buffered-file/imm32
4198     68/push  _test-input-stream/imm32
4199     # . . call
4200     e8/call  convert-instruction/disp32
4201     # . . discard args
4202     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4203     # check output
4204     # . flush(_test-output-buffered-file)
4205     # . . push args
4206     68/push  _test-output-buffered-file/imm32
4207     # . . call
4208     e8/call  flush/disp32
4209     # . . discard args
4210     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4211 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4237     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4238     # . . push args
4239     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
4240     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
4241     68/push  _test-output-stream/imm32
4242     # . . call
4243     e8/call  check-stream-equal/disp32
4244     # . . discard args
4245     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4246     # . epilogue
4247     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4248     5d/pop-to-ebp
4249     c3/return
4250 
4251 test-convert-instruction-handles-unused-second-opcodes:
4252     # if the second opcode isn't 0f, don't include further opcodes
4253     # . prologue
4254     55/push-ebp
4255     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4256     # setup
4257     # . clear-stream(_test-input-stream)
4258     # . . push args
4259     68/push  _test-input-stream/imm32
4260     # . . call
4261     e8/call  clear-stream/disp32
4262     # . . discard args
4263     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4264     # . clear-stream(_test-output-stream)
4265     # . . push args
4266     68/push  _test-output-stream/imm32
4267     # . . call
4268     e8/call  clear-stream/disp32
4269     # . . discard args
4270     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4271     # . clear-stream($_test-output-buffered-file->buffer)
4272     # . . push args
4273     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4279     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
4280     # . . push args
4281     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
4282     68/push  _test-input-stream/imm32
4283     # . . call
4284     e8/call  write/disp32
4285     # . . discard args
4286     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4287     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4288     # . . push args
4289     68/push  _test-output-buffered-file/imm32
4290     68/push  _test-input-stream/imm32
4291     # . . call
4292     e8/call  convert-instruction/disp32
4293     # . . discard args
4294     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4295     # check output
4296     # . flush(_test-output-buffered-file)
4297     # . . push args
4298     68/push  _test-output-buffered-file/imm32
4299     # . . call
4300     e8/call  flush/disp32
4301     # . . discard args
4302     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4303 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4329     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
4330     # . . push args
4331     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4332     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
4333     68/push  _test-output-stream/imm32
4334     # . . call
4335     e8/call  check-stream-equal/disp32
4336     # . . discard args
4337     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4338     # . epilogue
4339     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4340     5d/pop-to-ebp
4341     c3/return
4342 
4343 test-convert-instruction-handles-unused-second-opcodes-2:
4344     # if the second opcode isn't 0f, don't include further opcodes
4345     # . prologue
4346     55/push-ebp
4347     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4348     # setup
4349     # . clear-stream(_test-input-stream)
4350     # . . push args
4351     68/push  _test-input-stream/imm32
4352     # . . call
4353     e8/call  clear-stream/disp32
4354     # . . discard args
4355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4356     # . clear-stream(_test-output-stream)
4357     # . . push args
4358     68/push  _test-output-stream/imm32
4359     # . . call
4360     e8/call  clear-stream/disp32
4361     # . . discard args
4362     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4363     # . clear-stream($_test-output-buffered-file->buffer)
4364     # . . push args
4365     68/push  $_test-output-buffered-file->buffer/imm32
4366     # . . call
4367     e8/call  clear-stream/disp32
4368     # . . discard args
4369     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4370     # initialize input
4371     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
4372     # . . push args
4373     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
4374     68/push  _test-input-stream/imm32
4375     # . . call
4376     e8/call  write/disp32
4377     # . . discard args
4378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4379     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4380     # . . push args
4381     68/push  _test-output-buffered-file/imm32
4382     68/push  _test-input-stream/imm32
4383     # . . call
4384     e8/call  convert-instruction/disp32
4385     # . . discard args
4386     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4387     # check output
4388     # . flush(_test-output-buffered-file)
4389     # . . push args
4390     68/push  _test-output-buffered-file/imm32
4391     # . . call
4392     e8/call  flush/disp32
4393     # . . discard args
4394     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4395 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4421     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
4422     # . . push args
4423     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4424     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
4425     68/push  _test-output-stream/imm32
4426     # . . call
4427     e8/call  check-stream-equal/disp32
4428     # . . discard args
4429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4430     # . epilogue
4431     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4432     5d/pop-to-ebp
4433     c3/return
4434 
4435 test-convert-instruction-emits-modrm-byte:
4436     # pack mod, rm32 and r32 operands into ModR/M byte
4437     # . prologue
4438     55/push-ebp
4439     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4440     # setup
4441     # . clear-stream(_test-input-stream)
4442     # . . push args
4443     68/push  _test-input-stream/imm32
4444     # . . call
4445     e8/call  clear-stream/disp32
4446     # . . discard args
4447     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4448     # . clear-stream(_test-output-stream)
4449     # . . push args
4450     68/push  _test-output-stream/imm32
4451     # . . call
4452     e8/call  clear-stream/disp32
4453     # . . discard args
4454     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4455     # . clear-stream($_test-output-buffered-file->buffer)
4456     # . . push args
4457     68/push  $_test-output-buffered-file->buffer/imm32
4458     # . . call
4459     e8/call  clear-stream/disp32
4460     # . . discard args
4461     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4462     # initialize input
4463     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
4464     # . . push args
4465     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
4466     68/push  _test-input-stream/imm32
4467     # . . call
4468     e8/call  write/disp32
4469     # . . discard args
4470     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4471     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4472     # . . push args
4473     68/push  _test-output-buffered-file/imm32
4474     68/push  _test-input-stream/imm32
4475     # . . call
4476     e8/call  convert-instruction/disp32
4477     # . . discard args
4478     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4479     # check output
4480     # . flush(_test-output-buffered-file)
4481     # . . push args
4482     68/push  _test-output-buffered-file/imm32
4483     # . . call
4484     e8/call  flush/disp32
4485     # . . discard args
4486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4487 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4513     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
4514     # . . push args
4515     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
4516     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
4517     68/push  _test-output-stream/imm32
4518     # . . call
4519     e8/call  check-stream-equal/disp32
4520     # . . discard args
4521     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4522     # . epilogue
4523     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4524     5d/pop-to-ebp
4525     c3/return
4526 
4527 test-convert-instruction-emits-modrm-byte-with-non-zero-mod:
4528     # . prologue
4529     55/push-ebp
4530     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4531     # setup
4532     # . clear-stream(_test-input-stream)
4533     # . . push args
4534     68/push  _test-input-stream/imm32
4535     # . . call
4536     e8/call  clear-stream/disp32
4537     # . . discard args
4538     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4539     # . clear-stream(_test-output-stream)
4540     # . . push args
4541     68/push  _test-output-stream/imm32
4542     # . . call
4543     e8/call  clear-stream/disp32
4544     # . . discard args
4545     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4546     # . clear-stream($_test-output-buffered-file->buffer)
4547     # . . push args
4548     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4554     # . write(_test-input-stream, "01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx")
4555     # . . push args
4556     68/push  "01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx"/imm32
4557     68/push  _test-input-stream/imm32
4558     # . . call
4559     e8/call  write/disp32
4560     # . . discard args
4561     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4562     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4563     # . . push args
4564     68/push  _test-output-buffered-file/imm32
4565     68/push  _test-input-stream/imm32
4566     # . . call
4567     e8/call  convert-instruction/disp32
4568     # . . discard args
4569     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4570     # . flush(_test-output-buffered-file)
4571     # . . push args
4572     68/push  _test-output-buffered-file/imm32
4573     # . . call
4574     e8/call  flush/disp32
4575     # . . discard args
4576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4577 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4603     # check output
4604     # . check-stream-equal(_test-output-stream, "# abcd", msg)
4605     # . . push args
4606     68/push  "F - test-convert-instruction-foo"/imm32
4607     68/push  "01 cb  # 01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx"/imm32
4608     68/push  _test-output-stream/imm32
4609     # . . call
4610     e8/call  check-stream-equal/disp32
4611     # . . discard args
4612     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4613     # . epilogue
4614     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4615     5d/pop-to-ebp
4616     c3/return
4617 
4618 test-convert-instruction-emits-modrm-byte-from-subop:
4619     # pack mod, rm32 and subop operands into ModR/M byte
4620     # . prologue
4621     55/push-ebp
4622     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4623     # setup
4624     # . clear-stream(_test-input-stream)
4625     # . . push args
4626     68/push  _test-input-stream/imm32
4627     # . . call
4628     e8/call  clear-stream/disp32
4629     # . . discard args
4630     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4631     # . clear-stream(_test-output-stream)
4632     # . . push args
4633     68/push  _test-output-stream/imm32
4634     # . . call
4635     e8/call  clear-stream/disp32
4636     # . . discard args
4637     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4638     # . clear-stream($_test-output-buffered-file->buffer)
4639     # . . push args
4640     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4646     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
4647     # . . push args
4648     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
4649     68/push  _test-input-stream/imm32
4650     # . . call
4651     e8/call  write/disp32
4652     # . . discard args
4653     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4654     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4655     # . . push args
4656     68/push  _test-output-buffered-file/imm32
4657     68/push  _test-input-stream/imm32
4658     # . . call
4659     e8/call  convert-instruction/disp32
4660     # . . discard args
4661     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4662     # check output
4663     # . flush(_test-output-buffered-file)
4664     # . . push args
4665     68/push  _test-output-buffered-file/imm32
4666     # . . call
4667     e8/call  flush/disp32
4668     # . . discard args
4669     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4670 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4696     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
4697     # . . push args
4698     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
4699     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
4700     68/push  _test-output-stream/imm32
4701     # . . call
4702     e8/call  check-stream-equal/disp32
4703     # . . discard args
4704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4705     # . epilogue
4706     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4707     5d/pop-to-ebp
4708     c3/return
4709 
4710 test-convert-instruction-emits-modrm-byte-with-missing-mod:
4711     # pack rm32 and r32 operands into ModR/M byte
4712     # . prologue
4713     55/push-ebp
4714     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4715     # setup
4716     # . clear-stream(_test-input-stream)
4717     # . . push args
4718     68/push  _test-input-stream/imm32
4719     # . . call
4720     e8/call  clear-stream/disp32
4721     # . . discard args
4722     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4723     # . clear-stream(_test-output-stream)
4724     # . . push args
4725     68/push  _test-output-stream/imm32
4726     # . . call
4727     e8/call  clear-stream/disp32
4728     # . . discard args
4729     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4730     # . clear-stream($_test-output-buffered-file->buffer)
4731     # . . push args
4732     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4738     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
4739     # . . push args
4740     68/push  "8b/copy 0/rm32 1/r32"/imm32
4741     68/push  _test-input-stream/imm32
4742     # . . call
4743     e8/call  write/disp32
4744     # . . discard args
4745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4746     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4747     # . . push args
4748     68/push  _test-output-buffered-file/imm32
4749     68/push  _test-input-stream/imm32
4750     # . . call
4751     e8/call  convert-instruction/disp32
4752     # . . discard args
4753     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4754     # check output
4755     # . flush(_test-output-buffered-file)
4756     # . . push args
4757     68/push  _test-output-buffered-file/imm32
4758     # . . call
4759     e8/call  flush/disp32
4760     # . . discard args
4761     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4762 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4788     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
4789     # . . push args
4790     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
4791     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
4792     68/push  _test-output-stream/imm32
4793     # . . call
4794     e8/call  check-stream-equal/disp32
4795     # . . discard args
4796     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4797     # . epilogue
4798     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4799     5d/pop-to-ebp
4800     c3/return
4801 
4802 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
4803     # pack mod and r32 operands into ModR/M byte
4804     # . prologue
4805     55/push-ebp
4806     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4807     # setup
4808     # . clear-stream(_test-input-stream)
4809     # . . push args
4810     68/push  _test-input-stream/imm32
4811     # . . call
4812     e8/call  clear-stream/disp32
4813     # . . discard args
4814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4815     # . clear-stream(_test-output-stream)
4816     # . . push args
4817     68/push  _test-output-stream/imm32
4818     # . . call
4819     e8/call  clear-stream/disp32
4820     # . . discard args
4821     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4822     # . clear-stream($_test-output-buffered-file->buffer)
4823     # . . push args
4824     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4830     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
4831     # . . push args
4832     68/push  "8b/copy 0/mod 1/r32"/imm32
4833     68/push  _test-input-stream/imm32
4834     # . . call
4835     e8/call  write/disp32
4836     # . . discard args
4837     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4838     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4839     # . . push args
4840     68/push  _test-output-buffered-file/imm32
4841     68/push  _test-input-stream/imm32
4842     # . . call
4843     e8/call  convert-instruction/disp32
4844     # . . discard args
4845     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4846     # check output
4847     # . flush(_test-output-buffered-file)
4848     # . . push args
4849     68/push  _test-output-buffered-file/imm32
4850     # . . call
4851     e8/call  flush/disp32
4852     # . . discard args
4853     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4854 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4880     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
4881     # . . push args
4882     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
4883     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
4884     68/push  _test-output-stream/imm32
4885     # . . call
4886     e8/call  check-stream-equal/disp32
4887     # . . discard args
4888     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4889     # . epilogue
4890     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4891     5d/pop-to-ebp
4892     c3/return
4893 
4894 test-convert-instruction-emits-modrm-byte-with-missing-r32:
4895     # pack mod and rm32 operands into ModR/M byte
4896     # . prologue
4897     55/push-ebp
4898     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4899     # setup
4900     # . clear-stream(_test-input-stream)
4901     # . . push args
4902     68/push  _test-input-stream/imm32
4903     # . . call
4904     e8/call  clear-stream/disp32
4905     # . . discard args
4906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4907     # . clear-stream(_test-output-stream)
4908     # . . push args
4909     68/push  _test-output-stream/imm32
4910     # . . call
4911     e8/call  clear-stream/disp32
4912     # . . discard args
4913     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4914     # . clear-stream($_test-output-buffered-file->buffer)
4915     # . . push args
4916     68/push  $_test-output-buffered-file->buffer/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     # initialize input
4922     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
4923     # . . push args
4924     68/push  "8b/copy 0/mod 0/rm32"/imm32
4925     68/push  _test-input-stream/imm32
4926     # . . call
4927     e8/call  write/disp32
4928     # . . discard args
4929     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4930     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4931     # . . push args
4932     68/push  _test-output-buffered-file/imm32
4933     68/push  _test-input-stream/imm32
4934     # . . call
4935     e8/call  convert-instruction/disp32
4936     # . . discard args
4937     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4938     # check output
4939     # . flush(_test-output-buffered-file)
4940     # . . push args
4941     68/push  _test-output-buffered-file/imm32
4942     # . . call
4943     e8/call  flush/disp32
4944     # . . discard args
4945     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4946 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
4972     # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
4973     # . . push args
4974     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
4975     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
4976     68/push  _test-output-stream/imm32
4977     # . . call
4978     e8/call  check-stream-equal/disp32
4979     # . . discard args
4980     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4981     # . epilogue
4982     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4983     5d/pop-to-ebp
4984     c3/return
4985 
4986 test-convert-instruction-emits-sib-byte:
4987     # pack base, index and scale operands into SIB byte
4988     # . prologue
4989     55/push-ebp
4990     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4991     # setup
4992     # . clear-stream(_test-input-stream)
4993     # . . push args
4994     68/push  _test-input-stream/imm32
4995     # . . call
4996     e8/call  clear-stream/disp32
4997     # . . discard args
4998     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4999     # . clear-stream(_test-output-stream)
5000     # . . push args
5001     68/push  _test-output-stream/imm32
5002     # . . call
5003     e8/call  clear-stream/disp32
5004     # . . discard args
5005     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5006     # . clear-stream($_test-output-buffered-file->buffer)
5007     # . . push args
5008     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5014     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
5015     # . . push args
5016     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
5017     68/push  _test-input-stream/imm32
5018     # . . call
5019     e8/call  write/disp32
5020     # . . discard args
5021     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5022     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5023     # . . push args
5024     68/push  _test-output-buffered-file/imm32
5025     68/push  _test-input-stream/imm32
5026     # . . call
5027     e8/call  convert-instruction/disp32
5028     # . . discard args
5029     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5030     # check output
5031     # . flush(_test-output-buffered-file)
5032     # . . push args
5033     68/push  _test-output-buffered-file/imm32
5034     # . . call
5035     e8/call  flush/disp32
5036     # . . discard args
5037     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5038 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5064     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
5065     # . . push args
5066     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
5067     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
5068     68/push  _test-output-stream/imm32
5069     # . . call
5070     e8/call  check-stream-equal/disp32
5071     # . . discard args
5072     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5073     # . epilogue
5074     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5075     5d/pop-to-ebp
5076     c3/return
5077 
5078 test-convert-instruction-emits-scale:
5079     # pack base, index and scale operands into SIB byte
5080     # . prologue
5081     55/push-ebp
5082     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5083     # setup
5084     # . clear-stream(_test-input-stream)
5085     # . . push args
5086     68/push  _test-input-stream/imm32
5087     # . . call
5088     e8/call  clear-stream/disp32
5089     # . . discard args
5090     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5091     # . clear-stream(_test-output-stream)
5092     # . . push args
5093     68/push  _test-output-stream/imm32
5094     # . . call
5095     e8/call  clear-stream/disp32
5096     # . . discard args
5097     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5098     # . clear-stream($_test-output-buffered-file->buffer)
5099     # . . push args
5100     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5106     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/scale")
5107     # . . push args
5108     68/push  "8b/copy 0/mod 4/rm32 1/scale"/imm32
5109     68/push  _test-input-stream/imm32
5110     # . . call
5111     e8/call  write/disp32
5112     # . . discard args
5113     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5114     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5115     # . . push args
5116     68/push  _test-output-buffered-file/imm32
5117     68/push  _test-input-stream/imm32
5118     # . . call
5119     e8/call  convert-instruction/disp32
5120     # . . discard args
5121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5122     # check output
5123     # . flush(_test-output-buffered-file)
5124     # . . push args
5125     68/push  _test-output-buffered-file/imm32
5126     # . . call
5127     e8/call  flush/disp32
5128     # . . discard args
5129     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5130 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5156     # . check-stream-equal(_test-output-stream, "8b 04 40  # 8b/copy 0/mod 4/rm32 1/scale", msg)
5157     # . . push args
5158     68/push  "F - test-convert-instruction-emits-scale"/imm32
5159     68/push  "8b 04 40  # 8b/copy 0/mod 4/rm32 1/scale"/imm32
5160     68/push  _test-output-stream/imm32
5161     # . . call
5162     e8/call  check-stream-equal/disp32
5163     # . . discard args
5164     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5165     # . epilogue
5166     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5167     5d/pop-to-ebp
5168     c3/return
5169 
5170 test-convert-instruction-emits-sib-byte-with-missing-base:
5171     # pack index and scale operands into SIB byte
5172     # . prologue
5173     55/push-ebp
5174     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5175     # setup
5176     # . clear-stream(_test-input-stream)
5177     # . . push args
5178     68/push  _test-input-stream/imm32
5179     # . . call
5180     e8/call  clear-stream/disp32
5181     # . . discard args
5182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5183     # . clear-stream(_test-output-stream)
5184     # . . push args
5185     68/push  _test-output-stream/imm32
5186     # . . call
5187     e8/call  clear-stream/disp32
5188     # . . discard args
5189     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5190     # . clear-stream($_test-output-buffered-file->buffer)
5191     # . . push args
5192     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5198     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
5199     # . . push args
5200     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5201     68/push  _test-input-stream/imm32
5202     # . . call
5203     e8/call  write/disp32
5204     # . . discard args
5205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5206     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5207     # . . push args
5208     68/push  _test-output-buffered-file/imm32
5209     68/push  _test-input-stream/imm32
5210     # . . call
5211     e8/call  convert-instruction/disp32
5212     # . . discard args
5213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5214     # check output
5215     # . flush(_test-output-buffered-file)
5216     # . . push args
5217     68/push  _test-output-buffered-file/imm32
5218     # . . call
5219     e8/call  flush/disp32
5220     # . . discard args
5221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5222 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5248     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
5249     # . . push args
5250     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
5251     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5252     68/push  _test-output-stream/imm32
5253     # . . call
5254     e8/call  check-stream-equal/disp32
5255     # . . discard args
5256     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5257     # . epilogue
5258     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5259     5d/pop-to-ebp
5260     c3/return
5261 
5262 test-convert-instruction-emits-sib-byte-with-missing-index:
5263     # pack base and scale operands into SIB byte
5264     # . prologue
5265     55/push-ebp
5266     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5267     # setup
5268     # . clear-stream(_test-input-stream)
5269     # . . push args
5270     68/push  _test-input-stream/imm32
5271     # . . call
5272     e8/call  clear-stream/disp32
5273     # . . discard args
5274     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5275     # . clear-stream(_test-output-stream)
5276     # . . push args
5277     68/push  _test-output-stream/imm32
5278     # . . call
5279     e8/call  clear-stream/disp32
5280     # . . discard args
5281     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5282     # . clear-stream($_test-output-buffered-file->buffer)
5283     # . . push args
5284     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5290     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
5291     # . . push args
5292     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5293     68/push  _test-input-stream/imm32
5294     # . . call
5295     e8/call  write/disp32
5296     # . . discard args
5297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5298     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5299     # . . push args
5300     68/push  _test-output-buffered-file/imm32
5301     68/push  _test-input-stream/imm32
5302     # . . call
5303     e8/call  convert-instruction/disp32
5304     # . . discard args
5305     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5306     # check output
5307     # . flush(_test-output-buffered-file)
5308     # . . push args
5309     68/push  _test-output-buffered-file/imm32
5310     # . . call
5311     e8/call  flush/disp32
5312     # . . discard args
5313     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5314 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5340     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
5341     # . . push args
5342     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
5343     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5344     68/push  _test-output-stream/imm32
5345     # . . call
5346     e8/call  check-stream-equal/disp32
5347     # . . discard args
5348     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5349     # . epilogue
5350     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5351     5d/pop-to-ebp
5352     c3/return
5353 
5354 test-convert-instruction-emits-sib-byte-with-missing-scale:
5355     # pack base and index operands into SIB byte
5356     # . prologue
5357     55/push-ebp
5358     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5359     # setup
5360     # . clear-stream(_test-input-stream)
5361     # . . push args
5362     68/push  _test-input-stream/imm32
5363     # . . call
5364     e8/call  clear-stream/disp32
5365     # . . discard args
5366     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5367     # . clear-stream(_test-output-stream)
5368     # . . push args
5369     68/push  _test-output-stream/imm32
5370     # . . call
5371     e8/call  clear-stream/disp32
5372     # . . discard args
5373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5374     # . clear-stream($_test-output-buffered-file->buffer)
5375     # . . push args
5376     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5382     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
5383     # . . push args
5384     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5385     68/push  _test-input-stream/imm32
5386     # . . call
5387     e8/call  write/disp32
5388     # . . discard args
5389     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5390     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5391     # . . push args
5392     68/push  _test-output-buffered-file/imm32
5393     68/push  _test-input-stream/imm32
5394     # . . call
5395     e8/call  convert-instruction/disp32
5396     # . . discard args
5397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5398     # check output
5399     # . flush(_test-output-buffered-file)
5400     # . . push args
5401     68/push  _test-output-buffered-file/imm32
5402     # . . call
5403     e8/call  flush/disp32
5404     # . . discard args
5405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5406 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5432     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
5433     # . . push args
5434     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
5435     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5436     68/push  _test-output-stream/imm32
5437     # . . call
5438     e8/call  check-stream-equal/disp32
5439     # . . discard args
5440     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5441     # . epilogue
5442     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5443     5d/pop-to-ebp
5444     c3/return
5445 
5446 test-convert-instruction-handles-disp32-operand:
5447     # expand /disp32 operand into 4 bytes
5448     # . prologue
5449     55/push-ebp
5450     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5451     # setup
5452     # . clear-stream(_test-input-stream)
5453     # . . push args
5454     68/push  _test-input-stream/imm32
5455     # . . call
5456     e8/call  clear-stream/disp32
5457     # . . discard args
5458     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5459     # . clear-stream(_test-output-stream)
5460     # . . push args
5461     68/push  _test-output-stream/imm32
5462     # . . call
5463     e8/call  clear-stream/disp32
5464     # . . discard args
5465     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5466     # . clear-stream($_test-output-buffered-file->buffer)
5467     # . . push args
5468     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5474     # . write(_test-input-stream, "e8/call 20/disp32")
5475     # . . push args
5476     68/push  "e8/call 20/disp32"/imm32
5477     68/push  _test-input-stream/imm32
5478     # . . call
5479     e8/call  write/disp32
5480     # . . discard args
5481     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5482     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5483     # . . push args
5484     68/push  _test-output-buffered-file/imm32
5485     68/push  _test-input-stream/imm32
5486     # . . call
5487     e8/call  convert-instruction/disp32
5488     # . . discard args
5489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5490     # check output
5491     # . flush(_test-output-buffered-file)
5492     # . . push args
5493     68/push  _test-output-buffered-file/imm32
5494     # . . call
5495     e8/call  flush/disp32
5496     # . . discard args
5497     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5498 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5524     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
5525     # . . push args
5526     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
5527     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
5528     68/push  _test-output-stream/imm32
5529     # . . call
5530     e8/call  check-stream-equal/disp32
5531     # . . discard args
5532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5533     # . epilogue
5534     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5535     5d/pop-to-ebp
5536     c3/return
5537 
5538 test-convert-instruction-handles-disp16-operand:
5539     # expand /disp16 operand into 2 bytes
5540     # . prologue
5541     55/push-ebp
5542     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5543     # setup
5544     # . clear-stream(_test-input-stream)
5545     # . . push args
5546     68/push  _test-input-stream/imm32
5547     # . . call
5548     e8/call  clear-stream/disp32
5549     # . . discard args
5550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5551     # . clear-stream(_test-output-stream)
5552     # . . push args
5553     68/push  _test-output-stream/imm32
5554     # . . call
5555     e8/call  clear-stream/disp32
5556     # . . discard args
5557     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5558     # . clear-stream($_test-output-buffered-file->buffer)
5559     # . . push args
5560     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5566     # . write(_test-input-stream, "e8/call 20/disp16")
5567     # . . push args
5568     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
5569     68/push  _test-input-stream/imm32
5570     # . . call
5571     e8/call  write/disp32
5572     # . . discard args
5573     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5574     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5575     # . . push args
5576     68/push  _test-output-buffered-file/imm32
5577     68/push  _test-input-stream/imm32
5578     # . . call
5579     e8/call  convert-instruction/disp32
5580     # . . discard args
5581     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5582     # check output
5583     # . flush(_test-output-buffered-file)
5584     # . . push args
5585     68/push  _test-output-buffered-file/imm32
5586     # . . call
5587     e8/call  flush/disp32
5588     # . . discard args
5589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5590 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5616     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
5617     # . . push args
5618     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
5619     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
5620     68/push  _test-output-stream/imm32
5621     # . . call
5622     e8/call  check-stream-equal/disp32
5623     # . . discard args
5624     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5625     # . epilogue
5626     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5627     5d/pop-to-ebp
5628     c3/return
5629 
5630 test-convert-instruction-handles-disp8-operand:
5631     # expand /disp8 operand into 1 byte
5632     # . prologue
5633     55/push-ebp
5634     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5635     # setup
5636     # . clear-stream(_test-input-stream)
5637     # . . push args
5638     68/push  _test-input-stream/imm32
5639     # . . call
5640     e8/call  clear-stream/disp32
5641     # . . discard args
5642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5643     # . clear-stream(_test-output-stream)
5644     # . . push args
5645     68/push  _test-output-stream/imm32
5646     # . . call
5647     e8/call  clear-stream/disp32
5648     # . . discard args
5649     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5650     # . clear-stream($_test-output-buffered-file->buffer)
5651     # . . push args
5652     68/push  $_test-output-buffered-file->buffer/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     # initialize input
5658     # . write(_test-input-stream, "eb/jump 20/disp8")
5659     # . . push args
5660     68/push  "eb/jump 20/disp8"/imm32
5661     68/push  _test-input-stream/imm32
5662     # . . call
5663     e8/call  write/disp32
5664     # . . discard args
5665     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5666     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5667     # . . push args
5668     68/push  _test-output-buffered-file/imm32
5669     68/push  _test-input-stream/imm32
5670     # . . call
5671     e8/call  convert-instruction/disp32
5672     # . . discard args
5673     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5674     # check output
5675     # . flush(_test-output-buffered-file)
5676     # . . push args
5677     68/push  _test-output-buffered-file/imm32
5678     # . . call
5679     e8/call  flush/disp32
5680     # . . discard args
5681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5682 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5708     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
5709     # . . push args
5710     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
5711     68/push  "eb 20  # eb/jump 20/disp8"/imm32
5712     68/push  _test-output-stream/imm32
5713     # . . call
5714     e8/call  check-stream-equal/disp32
5715     # . . discard args
5716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5717     # . epilogue
5718     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5719     5d/pop-to-ebp
5720     c3/return
5721 
5722 test-convert-instruction-handles-disp8-name:
5723     # pass /disp8 name directly through
5724     # . prologue
5725     55/push-ebp
5726     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5727     # setup
5728     # . clear-stream(_test-input-stream)
5729     # . . push args
5730     68/push  _test-input-stream/imm32
5731     # . . call
5732     e8/call  clear-stream/disp32
5733     # . . discard args
5734     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5735     # . clear-stream(_test-output-stream)
5736     # . . push args
5737     68/push  _test-output-stream/imm32
5738     # . . call
5739     e8/call  clear-stream/disp32
5740     # . . discard args
5741     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5742     # . clear-stream($_test-output-buffered-file->buffer)
5743     # . . push args
5744     68/push  $_test-output-buffered-file->buffer/imm32
5745     # . . call
5746     e8/call  clear-stream/disp32
5747     # . . discard args
5748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5749     # initialize input
5750     # . write(_test-input-stream, "eb/jump xyz/disp8")
5751     # . . push args
5752     68/push  "eb/jump xyz/disp8"/imm32
5753     68/push  _test-input-stream/imm32
5754     # . . call
5755     e8/call  write/disp32
5756     # . . discard args
5757     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5758     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5759     # . . push args
5760     68/push  _test-output-buffered-file/imm32
5761     68/push  _test-input-stream/imm32
5762     # . . call
5763     e8/call  convert-instruction/disp32
5764     # . . discard args
5765     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5766     # check output
5767     # . flush(_test-output-buffered-file)
5768     # . . push args
5769     68/push  _test-output-buffered-file/imm32
5770     # . . call
5771     e8/call  flush/disp32
5772     # . . discard args
5773     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5774 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5800     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
5801     # . . push args
5802     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
5803     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
5804     68/push  _test-output-stream/imm32
5805     # . . call
5806     e8/call  check-stream-equal/disp32
5807     # . . discard args
5808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5809     # . epilogue
5810     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5811     5d/pop-to-ebp
5812     c3/return
5813 
5814 test-convert-instruction-handles-imm32-operand:
5815     # expand /imm32 operand into 4 bytes
5816     # . prologue
5817     55/push-ebp
5818     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5819     # setup
5820     # . clear-stream(_test-input-stream)
5821     # . . push args
5822     68/push  _test-input-stream/imm32
5823     # . . call
5824     e8/call  clear-stream/disp32
5825     # . . discard args
5826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5827     # . clear-stream(_test-output-stream)
5828     # . . push args
5829     68/push  _test-output-stream/imm32
5830     # . . call
5831     e8/call  clear-stream/disp32
5832     # . . discard args
5833     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5834     # . clear-stream($_test-output-buffered-file->buffer)
5835     # . . push args
5836     68/push  $_test-output-buffered-file->buffer/imm32
5837     # . . call
5838     e8/call  clear-stream/disp32
5839     # . . discard args
5840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5841     # initialize input
5842     # . write(_test-input-stream, "68/push 0x20/imm32")
5843     # . . push args
5844     68/push  "68/push 0x20/imm32"/imm32
5845     68/push  _test-input-stream/imm32
5846     # . . call
5847     e8/call  write/disp32
5848     # . . discard args
5849     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5850     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5851     # . . push args
5852     68/push  _test-output-buffered-file/imm32
5853     68/push  _test-input-stream/imm32
5854     # . . call
5855     e8/call  convert-instruction/disp32
5856     # . . discard args
5857     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5858     # check output
5859     # . flush(_test-output-buffered-file)
5860     # . . push args
5861     68/push  _test-output-buffered-file/imm32
5862     # . . call
5863     e8/call  flush/disp32
5864     # . . discard args
5865     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5866 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5892     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
5893     # . . push args
5894     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
5895     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
5896     68/push  _test-output-stream/imm32
5897     # . . call
5898     e8/call  check-stream-equal/disp32
5899     # . . discard args
5900     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5901     # . epilogue
5902     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5903     5d/pop-to-ebp
5904     c3/return
5905 
5906 test-convert-instruction-handles-imm16-operand:
5907     # expand /imm16 operand into 2 bytes
5908     # we don't have one of these at the moment, so this expands to an invalid instruction
5909     # . prologue
5910     55/push-ebp
5911     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5912     # setup
5913     # . clear-stream(_test-input-stream)
5914     # . . push args
5915     68/push  _test-input-stream/imm32
5916     # . . call
5917     e8/call  clear-stream/disp32
5918     # . . discard args
5919     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5920     # . clear-stream(_test-output-stream)
5921     # . . push args
5922     68/push  _test-output-stream/imm32
5923     # . . call
5924     e8/call  clear-stream/disp32
5925     # . . discard args
5926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5927     # . clear-stream($_test-output-buffered-file->buffer)
5928     # . . push args
5929     68/push  $_test-output-buffered-file->buffer/imm32
5930     # . . call
5931     e8/call  clear-stream/disp32
5932     # . . discard args
5933     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5934     # initialize input
5935     # . write(_test-input-stream, "68/push 0x20/imm16")
5936     # . . push args
5937     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
5938     68/push  _test-input-stream/imm32
5939     # . . call
5940     e8/call  write/disp32
5941     # . . discard args
5942     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5943     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5944     # . . push args
5945     68/push  _test-output-buffered-file/imm32
5946     68/push  _test-input-stream/imm32
5947     # . . call
5948     e8/call  convert-instruction/disp32
5949     # . . discard args
5950     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5951     # check output
5952     # . flush(_test-output-buffered-file)
5953     # . . push args
5954     68/push  _test-output-buffered-file/imm32
5955     # . . call
5956     e8/call  flush/disp32
5957     # . . discard args
5958     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5959 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
5985     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
5986     # . . push args
5987     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
5988     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
5989     68/push  _test-output-stream/imm32
5990     # . . call
5991     e8/call  check-stream-equal/disp32
5992     # . . discard args
5993     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5994     # . epilogue
5995     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5996     5d/pop-to-ebp
5997     c3/return
5998 
5999 test-convert-instruction-handles-imm8-operand:
6000     # expand /imm8 operand into 1 byte
6001     # we don't have one of these at the moment, so this expands to an invalid instruction
6002     # . prologue
6003     55/push-ebp
6004     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
6005     # setup
6006     # . clear-stream(_test-input-stream)
6007     # . . push args
6008     68/push  _test-input-stream/imm32
6009     # . . call
6010     e8/call  clear-stream/disp32
6011     # . . discard args
6012     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
6013     # . clear-stream(_test-output-stream)
6014     # . . push args
6015     68/push  _test-output-stream/imm32
6016     # . . call
6017     e8/call  clear-stream/disp32
6018     # . . discard args
6019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
6020     # . clear-stream($_test-output-buffered-file->buffer)
6021     # . . push args
6022     68/push  $_test-output-buffered-file->buffer/imm32
6023     # . . call
6024     e8/call  clear-stream/disp32
6025     # . . discard args
6026     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
6027     # initialize input
6028     # . write(_test-input-stream, "68/push 0x20/imm8")
6029     # . . push args
6030     68/push  "68/push 0x20/imm8"/imm32
6031     68/push  _test-input-stream/imm32
6032     # . . call
6033     e8/call  write/disp32
6034     # . . discard args
6035     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
6036     # convert-instruction(_test-input-stream, _test-output-buffered-file)
6037     # . . push args
6038     68/push  _test-output-buffered-file/imm32
6039     68/push  _test-input-stream/imm32
6040     # . . call
6041     e8/call  convert-instruction/disp32
6042     # . . discard args
6043     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
6044     # check output
6045     # . flush(_test-output-buffered-file)
6046     # . . push args
6047     68/push  _test-output-buffered-file/imm32
6048     # . . call
6049     e8/call  flush/disp32
6050     # . . discard args
6051     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
6052 +-- 26 lines: #?     # dump output -----------------------------------------------------------------------------------------------------------------------------------------
6078     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
6079     # . . push args
6080     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
6081     68/push  "68 20  # 68/push 0x20/imm8"/imm32
6082     68/push  _test-output-stream/imm32
6083     # . . call
6084     e8/call  check-stream-equal/disp32
6085     # . . discard args
6086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
6087     # . epilogue
6088     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
6089     5d/pop-to-ebp
6090     c3/return
6091 
6092 # shortcut for parse-hex-int-from-slice(next-token-from-slice(word->start, word->end, '/'))
6093 parse-datum-of-word:  # word: (addr slice) -> value/eax: int
6094     # . prologue
6095     55/push-ebp
6096     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
6097     # . save registers
6098     51/push-ecx
6099     56/push-esi
6100     # esi = word
6101     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
6102     # var slice/ecx: slice
6103     68/push  0/imm32/end
6104     68/push  0/imm32/start
6105     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
6106     # slice = next-token-from-slice(word->start, word->end, '/')
6107     # . . push args
6108     51/push-ecx
6109     68/push  0x2f/imm32/slash
6110     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
6111     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
6112     # . . call
6113     e8/call  next-token-from-slice/disp32
6114     # . . discard args
6115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
6116     # return parse-hex-int-from-slice(slice)
6117     # . . push args
6118     51/push-ecx
6119     # . . call
6120     e8/call  parse-hex-int-from-slice/disp32
6121     # . . discard args
6122     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
6123 $parse-datum-of-word:end:
6124     # . reclaim locals
6125     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
6126     # . restore registers
6127     5e/pop-to-esi
6128     59/pop-to-ecx
6129     # . epilogue
6130     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
6131     5d/pop-to-ebp
6132     c3/return
6133 
6134 # . . vim:nowrap:textwidth=0