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