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