https://github.com/akkartik/mu/blob/master/apps/pack.subx
   1 # Read a text file of SubX instructions from stdin, and convert it into a list
   2 # of whitespace-separated ascii hex bytes on stdout. Label definitions and
   3 # uses are left untouched.
   4 #
   5 # To run:
   6 #   $ ./bootstrap translate init.linux 0*.subx apps/subx-params.subx apps/pack.subx  -o apps/pack
   7 #   $ echo '05/add-to-eax 0x20/imm32'  |./bootstrap run apps/pack
   8 # Expected output:
   9 #   05 20 00 00 00  # 05/add-to-eax 0x20/imm32
  10 # The original instruction gets included as a comment at the end of each
  11 # converted line.
  12 #
  13 # There's zero error-checking. For now we assume the input program is valid.
  14 # We'll continue to rely on the C++ version for error messages.
  15 
  16 == code
  17 #   instruction                     effective address                                                   register    displacement    immediate
  18 # . op          subop               mod             rm32          base        index         scale       r32
  19 # . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes
  20 
  21 Entry:  # run tests if necessary, convert stdin if not
  22     # . prologue
  23     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  24 
  25     # initialize heap
  26     # . Heap = new-segment(Heap-size)
  27     # . . push args
  28     68/push  Heap/imm32
  29     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  30     # . . call
  31     e8/call  new-segment/disp32
  32     # . . discard args
  33     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  34 
  35     # - if argc > 1 and argv[1] == "test", then return run_tests()
  36     # if (argc <= 1) goto interactive
  37     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  38     7e/jump-if-<=  $subx-pack-main:interactive/disp8
  39     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  40     # . eax = kernel-string-equal?(argv[1], "test")
  41     # . . push args
  42     68/push  "test"/imm32
  43     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  44     # . . call
  45     e8/call  kernel-string-equal?/disp32
  46     # . . discard args
  47     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  48     # . if (eax == false) goto interactive
  49     3d/compare-eax-and  0/imm32/false
  50     74/jump-if-=  $subx-pack-main:interactive/disp8
  51     # run-tests()
  52     e8/call  run-tests/disp32
  53     # syscall(exit, *Num-test-failures)
  54     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  55     eb/jump  $subx-pack-main:end/disp8
  56 $subx-pack-main:interactive:
  57     # - otherwise convert stdin
  58     # subx-pack(Stdin, Stdout)
  59     # . . push args
  60     68/push  Stdout/imm32
  61     68/push  Stdin/imm32
  62     # . . call
  63     e8/call  subx-pack/disp32
  64     # . . discard args
  65     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x8/imm32         # add to esp
  66     # syscall(exit, 0)
  67     bb/copy-to-ebx  0/imm32
  68 $subx-pack-main:end:
  69     e8/call  syscall_exit/disp32
  70 
  71 # - big picture
  72 # We'll operate on each line/instruction in isolation. That way we only need to
  73 # allocate memory for converting a single instruction.
  74 #
  75 # To pack an entire file, convert every segment in it
  76 # To convert a code segment, convert every instruction (line) until the next segment header
  77 # To convert a non-data segment, convert every word until the next segment header
  78 #
  79 # primary state: line
  80 #   stream of 512 bytes; abort if it ever overflows
  81 
  82 # conceptual hierarchy within a line:
  83 #   line = words separated by ' ', maybe followed by comment starting with '#'
  84 #   word = datum until '/', then 0 or more metadata separated by '/'
  85 #
  86 # we won't bother saving the internal structure of lines; reparsing should be cheap using three primitives:
  87 #   next-token(stream, delim char) -> slice (start, end pointers)
  88 #   next-token-from-slice(start, end, delim char) -> slice
  89 #   slice-equal?(slice, string)
  90 
  91 subx-pack:  # in: (addr buffered-file), out: (addr buffered-file)
  92     # pseudocode:
  93     #   var line: (stream byte 512)
  94     #   var in-code? = false
  95     #   while true
  96     #     clear-stream(line)
  97     #     read-line-buffered(in, line)
  98     #     if (line->write == 0) break             # end of file
  99     #     var word-slice = next-word(line)
 100     #     if slice-empty?(word-slice)             # whitespace
 101     #       write-stream-data(out, line)
 102     #     else if (slice-equal?(word-slice, "=="))
 103     #       word-slice = next-word(line)
 104     #       in-code? = slice-equal?(word-slice, "code")
 105     #       write-stream-data(out, line)
 106     #     else if (in-code?)
 107     #       rewind-stream(line)
 108     #       convert-instruction(line, out)
 109     #     else
 110     #       rewind-stream(line)
 111     #       convert-data(line, out)
 112     #   flush(out)
 113     #
 114     # . prologue
 115     55/push-ebp
 116     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 117     # . save registers
 118     50/push-eax
 119     51/push-ecx
 120     52/push-edx
 121     53/push-ebx
 122     # var line/ecx: (stream byte 512)
 123     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 124     68/push  0x200/imm32/length
 125     68/push  0/imm32/read
 126     68/push  0/imm32/write
 127     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 128     # var word-slice/edx: slice
 129     68/push  0/imm32/end
 130     68/push  0/imm32/start
 131     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 132     # var in-code?/ebx: boolean = false
 133     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 134 $subx-pack:loop:
 135     # clear-stream(line)
 136     # . . push args
 137     51/push-ecx
 138     # . . call
 139     e8/call  clear-stream/disp32
 140     # . . discard args
 141     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 142     # read-line-buffered(in, line)
 143     # . . push args
 144     51/push-ecx
 145     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 146     # . . call
 147     e8/call  read-line-buffered/disp32
 148     # . . discard args
 149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 150 $subx-pack:check0:
 151     # if (line->write == 0) break
 152     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
 153     0f 84/jump-if-=  $subx-pack:break/disp32
 154 +-- 26 lines: #?     # dump line ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 180     # next-word(line, word-slice)
 181     # . . push args
 182     52/push-edx
 183     51/push-ecx
 184     # . . call
 185     e8/call  next-word/disp32
 186     # . . discard args
 187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 188 $subx-pack:check1:
 189     # if (slice-empty?(word-slice)) write-stream-data(out, line)
 190     # . eax = slice-empty?(word-slice)
 191     # . . push args
 192     52/push-edx
 193     # . . call
 194     e8/call  slice-empty?/disp32
 195     # . . discard args
 196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 197     # . if (eax != false) write-stream-data(out, line)
 198     3d/compare-eax-and  0/imm32/false
 199     0f 85/jump-if-!=  $subx-pack:pass-through/disp32
 200 $subx-pack:check2:
 201 +-- 40 lines: #?     # dump word-slice ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 241     # if (!slice-equal?(word-slice, "==")) goto next check
 242     # . eax = slice-equal?(word-slice, "==")
 243     # . . push args
 244     68/push  "=="/imm32
 245     52/push-edx
 246     # . . call
 247     e8/call  slice-equal?/disp32
 248     # . . discard args
 249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 250     # . if (eax == false) goto check3
 251     3d/compare-eax-and  0/imm32/false
 252     0f 84/jump-if-=  $subx-pack:check3/disp32
 253     # word-slice = next-word(line)
 254     # . . push args
 255     52/push-edx
 256     51/push-ecx
 257     # . . call
 258     e8/call  next-word/disp32
 259     # . . discard args
 260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 261 +-- 40 lines: #?     # dump segment name -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 301     # in-code? = slice-equal?(word-slice, "code")
 302     # . . push args
 303     68/push  "code"/imm32
 304     52/push-edx
 305     # . . call
 306     e8/call  slice-equal?/disp32
 307     # . . discard args
 308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 309     # . . in-code? = eax
 310     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
 311     # write-stream-data(out, line)
 312     eb/jump  $subx-pack:pass-through/disp8
 313 $subx-pack:check3:
 314     # else rewind-stream(line)
 315     # . rewind-stream(line)
 316     # . . push args
 317     51/push-ecx
 318     # . . call
 319     e8/call  rewind-stream/disp32
 320     # . . discard args
 321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 322     # if (in-code? != false) convert-instruction(line, out)
 323     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32/false     # compare ebx
 324     74/jump-if-=  $subx-pack:data/disp8
 325 $subx-pack:code:
 326     # . convert-instruction(line, out)
 327     # . . push args
 328     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 329     51/push-ecx
 330     # . . call
 331     e8/call  convert-instruction/disp32
 332     # . . discard args
 333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 334     # . loop
 335     e9/jump  $subx-pack:loop/disp32
 336 $subx-pack:data:
 337     # else convert-data(line, out)
 338     # . . push args
 339     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 340     51/push-ecx
 341     # . . call
 342     e8/call  convert-data/disp32
 343     # . . discard args
 344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 345     # . loop
 346     e9/jump  $subx-pack:loop/disp32
 347 $subx-pack:pass-through:
 348     # write-stream-data(out, line)
 349     # . . push args
 350     51/push-ecx
 351     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 352     # . . call
 353     e8/call  write-stream-data/disp32
 354     # . . discard args
 355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 356     # . loop
 357     e9/jump  $subx-pack:loop/disp32
 358 $subx-pack:break:
 359     # flush(out)
 360     # . . push args
 361     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 362     # . . call
 363     e8/call  flush/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 366 $subx-pack:end:
 367     # . reclaim locals
 368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 369     # . restore registers
 370     5b/pop-to-ebx
 371     5a/pop-to-edx
 372     59/pop-to-ecx
 373     58/pop-to-eax
 374     # . epilogue
 375     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 376     5d/pop-to-ebp
 377     c3/return
 378 
 379 test-subx-pack-passes-empty-lines-through:
 380     # if a line is empty, pass it along unchanged
 381     # . prologue
 382     55/push-ebp
 383     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 384     # setup
 385     # . clear-stream(_test-input-stream)
 386     # . . push args
 387     68/push  _test-input-stream/imm32
 388     # . . call
 389     e8/call  clear-stream/disp32
 390     # . . discard args
 391     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 392     # . clear-stream($_test-input-buffered-file->buffer)
 393     # . . push args
 394     68/push  $_test-input-buffered-file->buffer/imm32
 395     # . . call
 396     e8/call  clear-stream/disp32
 397     # . . discard args
 398     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 399     # . clear-stream(_test-output-stream)
 400     # . . push args
 401     68/push  _test-output-stream/imm32
 402     # . . call
 403     e8/call  clear-stream/disp32
 404     # . . discard args
 405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 406     # . clear-stream($_test-output-buffered-file->buffer)
 407     # . . push args
 408     68/push  $_test-output-buffered-file->buffer/imm32
 409     # . . call
 410     e8/call  clear-stream/disp32
 411     # . . discard args
 412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 413     # write nothing to input
 414     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 415     # . . push args
 416     68/push  _test-output-buffered-file/imm32
 417     68/push  _test-input-buffered-file/imm32
 418     # . . call
 419     e8/call  subx-pack/disp32
 420     # . . discard args
 421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 422     # check that the line just passed through
 423     # . flush(_test-output-buffered-file)
 424     # . . push args
 425     68/push  _test-output-buffered-file/imm32
 426     # . . call
 427     e8/call  flush/disp32
 428     # . . discard args
 429     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 430     # . check-stream-equal(_test-output-stream, "", msg)
 431     # . . push args
 432     68/push  "F - test-subx-pack-passes-empty-lines-through"/imm32
 433     68/push  ""/imm32
 434     68/push  _test-output-stream/imm32
 435     # . . call
 436     e8/call  check-stream-equal/disp32
 437     # . . discard args
 438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 439     # . epilogue
 440     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 441     5d/pop-to-ebp
 442     c3/return
 443 
 444 test-subx-pack-passes-lines-with-just-whitespace-through:
 445     # if a line is empty, pass it along unchanged
 446     # . prologue
 447     55/push-ebp
 448     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 449     # setup
 450     # . clear-stream(_test-input-stream)
 451     # . . push args
 452     68/push  _test-input-stream/imm32
 453     # . . call
 454     e8/call  clear-stream/disp32
 455     # . . discard args
 456     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 457     # . clear-stream($_test-input-buffered-file->buffer)
 458     # . . push args
 459     68/push  $_test-input-buffered-file->buffer/imm32
 460     # . . call
 461     e8/call  clear-stream/disp32
 462     # . . discard args
 463     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 464     # . clear-stream(_test-output-stream)
 465     # . . push args
 466     68/push  _test-output-stream/imm32
 467     # . . call
 468     e8/call  clear-stream/disp32
 469     # . . discard args
 470     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 471     # . clear-stream($_test-output-buffered-file->buffer)
 472     # . . push args
 473     68/push  $_test-output-buffered-file->buffer/imm32
 474     # . . call
 475     e8/call  clear-stream/disp32
 476     # . . discard args
 477     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 478     # initialize input
 479     # . write(_test-input-stream, "    ")
 480     # . . push args
 481     68/push  "    "/imm32
 482     68/push  _test-input-stream/imm32
 483     # . . call
 484     e8/call  write/disp32
 485     # . . discard args
 486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 487     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 488     # . . push args
 489     68/push  _test-output-buffered-file/imm32
 490     68/push  _test-input-buffered-file/imm32
 491     # . . call
 492     e8/call  subx-pack/disp32
 493     # . . discard args
 494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 495     # check that the line just passed through
 496     # . flush(_test-output-buffered-file)
 497     # . . push args
 498     68/push  _test-output-buffered-file/imm32
 499     # . . call
 500     e8/call  flush/disp32
 501     # . . discard args
 502     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 503     # . check-next-stream-line-equal(_test-output-stream, "    ", msg)
 504     # . . push args
 505     68/push  "F - test-subx-pack-passes-with-just-whitespace-through"/imm32
 506     68/push  "    "/imm32
 507     68/push  _test-output-stream/imm32
 508     # . . call
 509     e8/call  check-next-stream-line-equal/disp32
 510     # . . discard args
 511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 512     # . epilogue
 513     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 514     5d/pop-to-ebp
 515     c3/return
 516 
 517 test-subx-pack-passes-segment-headers-through:
 518     # if a line starts with '==', pass it along unchanged
 519     # . prologue
 520     55/push-ebp
 521     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 522     # setup
 523     # . clear-stream(_test-input-stream)
 524     # . . push args
 525     68/push  _test-input-stream/imm32
 526     # . . call
 527     e8/call  clear-stream/disp32
 528     # . . discard args
 529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 530     # . clear-stream($_test-input-buffered-file->buffer)
 531     # . . push args
 532     68/push  $_test-input-buffered-file->buffer/imm32
 533     # . . call
 534     e8/call  clear-stream/disp32
 535     # . . discard args
 536     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 537     # . clear-stream(_test-output-stream)
 538     # . . push args
 539     68/push  _test-output-stream/imm32
 540     # . . call
 541     e8/call  clear-stream/disp32
 542     # . . discard args
 543     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 544     # . clear-stream($_test-output-buffered-file->buffer)
 545     # . . push args
 546     68/push  $_test-output-buffered-file->buffer/imm32
 547     # . . call
 548     e8/call  clear-stream/disp32
 549     # . . discard args
 550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 551     # initialize input
 552     # . write(_test-input-stream, "== abcd 0x1")
 553     # . . push args
 554     68/push  "== abcd 0x1"/imm32
 555     68/push  _test-input-stream/imm32
 556     # . . call
 557     e8/call  write/disp32
 558     # . . discard args
 559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 560     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 561     # . . push args
 562     68/push  _test-output-buffered-file/imm32
 563     68/push  _test-input-buffered-file/imm32
 564     # . . call
 565     e8/call  subx-pack/disp32
 566     # . . discard args
 567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 568     # check that the line just passed through
 569     # . flush(_test-output-buffered-file)
 570     # . . push args
 571     68/push  _test-output-buffered-file/imm32
 572     # . . call
 573     e8/call  flush/disp32
 574     # . . discard args
 575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 576     # . check-stream-equal(_test-output-stream, "== abcd 0x1", msg)
 577     # . . push args
 578     68/push  "F - test-subx-pack-passes-segment-headers-through"/imm32
 579     68/push  "== abcd 0x1"/imm32
 580     68/push  _test-output-stream/imm32
 581     # . . call
 582     e8/call  check-stream-equal/disp32
 583     # . . discard args
 584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 585     # . epilogue
 586     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 587     5d/pop-to-ebp
 588     c3/return
 589 
 590 test-subx-pack-in-data-segment:
 591     # correctly process lines in the data segment
 592     # . prologue
 593     55/push-ebp
 594     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 595     # setup
 596     # . clear-stream(_test-input-stream)
 597     # . . push args
 598     68/push  _test-input-stream/imm32
 599     # . . call
 600     e8/call  clear-stream/disp32
 601     # . . discard args
 602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 603     # . clear-stream($_test-input-buffered-file->buffer)
 604     # . . push args
 605     68/push  $_test-input-buffered-file->buffer/imm32
 606     # . . call
 607     e8/call  clear-stream/disp32
 608     # . . discard args
 609     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 610     # . clear-stream(_test-output-stream)
 611     # . . push args
 612     68/push  _test-output-stream/imm32
 613     # . . call
 614     e8/call  clear-stream/disp32
 615     # . . discard args
 616     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 617     # . clear-stream($_test-output-buffered-file->buffer)
 618     # . . push args
 619     68/push  $_test-output-buffered-file->buffer/imm32
 620     # . . call
 621     e8/call  clear-stream/disp32
 622     # . . discard args
 623     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 624     # initialize input
 625     #   == code 0x1
 626     #   == data 0x2
 627     #   3 4/imm32
 628     # . write(_test-input-stream, "== code 0x1")
 629     # . . push args
 630     68/push  "== code 0x1\n"/imm32
 631     68/push  _test-input-stream/imm32
 632     # . . call
 633     e8/call  write/disp32
 634     # . . discard args
 635     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 636     # . write(_test-input-stream, "== data 0x2\n")
 637     # . . push args
 638     68/push  "== data 0x2\n"/imm32
 639     68/push  _test-input-stream/imm32
 640     # . . call
 641     e8/call  write/disp32
 642     # . . discard args
 643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 644     # . write(_test-input-stream, "3 4/imm32\n")
 645     # . . push args
 646     68/push  "3 4/imm32\n"/imm32
 647     68/push  _test-input-stream/imm32
 648     # . . call
 649     e8/call  write/disp32
 650     # . . discard args
 651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 652     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 653     # . . push args
 654     68/push  _test-output-buffered-file/imm32
 655     68/push  _test-input-buffered-file/imm32
 656     # . . call
 657     e8/call  subx-pack/disp32
 658     # . . discard args
 659     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 660     # check output
 661 +-- 26 lines: #?     # debug print -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 687     # . flush(_test-output-buffered-file)
 688     # . . push args
 689     68/push  _test-output-buffered-file/imm32
 690     # . . call
 691     e8/call  flush/disp32
 692     # . . discard args
 693     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 694     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
 695     # . . push args
 696     68/push  "F - test-subx-pack-in-data-segment/0"/imm32
 697     68/push  "== code 0x1"/imm32
 698     68/push  _test-output-stream/imm32
 699     # . . call
 700     e8/call  check-next-stream-line-equal/disp32
 701     # . . discard args
 702     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 703     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
 704     # . . push args
 705     68/push  "F - test-subx-pack-in-data-segment/1"/imm32
 706     68/push  "== data 0x2"/imm32
 707     68/push  _test-output-stream/imm32
 708     # . . call
 709     e8/call  check-next-stream-line-equal/disp32
 710     # . . discard args
 711     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 712     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 713     # . . push args
 714     68/push  "F - test-subx-pack-in-data-segment/2"/imm32
 715     68/push  "03 04 00 00 00 "/imm32
 716     68/push  _test-output-stream/imm32
 717     # . . call
 718     e8/call  check-next-stream-line-equal/disp32
 719     # . . discard args
 720     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 721     # . epilogue
 722     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 723     5d/pop-to-ebp
 724     c3/return
 725 
 726 test-subx-pack-code-and-data-segments:
 727     # correctly process lines in both code and data segments
 728     # . prologue
 729     55/push-ebp
 730     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 731     # setup
 732     # . clear-stream(_test-input-stream)
 733     # . . push args
 734     68/push  _test-input-stream/imm32
 735     # . . call
 736     e8/call  clear-stream/disp32
 737     # . . discard args
 738     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 739     # . clear-stream($_test-input-buffered-file->buffer)
 740     # . . push args
 741     68/push  $_test-input-buffered-file->buffer/imm32
 742     # . . call
 743     e8/call  clear-stream/disp32
 744     # . . discard args
 745     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 746     # . clear-stream(_test-output-stream)
 747     # . . push args
 748     68/push  _test-output-stream/imm32
 749     # . . call
 750     e8/call  clear-stream/disp32
 751     # . . discard args
 752     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 753     # . clear-stream($_test-output-buffered-file->buffer)
 754     # . . push args
 755     68/push  $_test-output-buffered-file->buffer/imm32
 756     # . . call
 757     e8/call  clear-stream/disp32
 758     # . . discard args
 759     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 760     # initialize input
 761     #   == code 0x1
 762     #   e8/call 20/disp32
 763     #   68/push 0x20/imm8
 764     #   == data 0x2
 765     #   3 4/imm32
 766     # . write(_test-input-stream, "== code 0x1\n")
 767     # . . push args
 768     68/push  "== code 0x1\n"/imm32
 769     68/push  _test-input-stream/imm32
 770     # . . call
 771     e8/call  write/disp32
 772     # . . discard args
 773     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 774     # . write(_test-input-stream, "e8/call 20/disp32\n")
 775     # . . push args
 776     68/push  "e8/call 20/disp32\n"/imm32
 777     68/push  _test-input-stream/imm32
 778     # . . call
 779     e8/call  write/disp32
 780     # . . discard args
 781     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 782     # . write(_test-input-stream, "68/push 0x20/imm8\n")
 783     # . . push args
 784     68/push  "68/push 0x20/imm8\n"/imm32
 785     68/push  _test-input-stream/imm32
 786     # . . call
 787     e8/call  write/disp32
 788     # . . discard args
 789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 790     # . write(_test-input-stream, "== data 0x2\n")
 791     # . . push args
 792     68/push  "== data 0x2\n"/imm32
 793     68/push  _test-input-stream/imm32
 794     # . . call
 795     e8/call  write/disp32
 796     # . . discard args
 797     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 798     # . write(_test-input-stream, "3 4/imm32\n")
 799     # . . push args
 800     68/push  "3 4/imm32\n"/imm32
 801     68/push  _test-input-stream/imm32
 802     # . . call
 803     e8/call  write/disp32
 804     # . . discard args
 805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 806     # subx-pack(_test-input-buffered-file, _test-output-buffered-file)
 807     # . . push args
 808     68/push  _test-output-buffered-file/imm32
 809     68/push  _test-input-buffered-file/imm32
 810     # . . call
 811     e8/call  subx-pack/disp32
 812     # . . discard args
 813     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 814     # check output
 815     #   == code 0x1
 816     #   e8 20 00 00 00  # e8/call 20/disp32
 817     #   68 20  # 68/push 0x20/imm8
 818     #   == data 0x2
 819     #   03 04 00 00 00
 820 +-- 26 lines: #?     # debug print -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 846     # . flush(_test-output-buffered-file)
 847     # . . push args
 848     68/push  _test-output-buffered-file/imm32
 849     # . . call
 850     e8/call  flush/disp32
 851     # . . discard args
 852     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 853     # . check-next-stream-line-equal(_test-output-stream, "== code 0x1", msg)
 854     # . . push args
 855     68/push  "F - test-subx-pack-code-and-data-segments/0"/imm32
 856     68/push  "== code 0x1"/imm32
 857     68/push  _test-output-stream/imm32
 858     # . . call
 859     e8/call  check-next-stream-line-equal/disp32
 860     # . . discard args
 861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 862     # . check-next-stream-line-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
 863     # . . push args
 864     68/push  "F - test-subx-pack-code-and-data-segments/1"/imm32
 865     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
 866     68/push  _test-output-stream/imm32
 867     # . . call
 868     e8/call  check-next-stream-line-equal/disp32
 869     # . . discard args
 870     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 871     # . check-next-stream-line-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
 872     # . . push args
 873     68/push  "F - test-subx-pack-code-and-data-segments/2"/imm32
 874     68/push  "68 20  # 68/push 0x20/imm8"/imm32
 875     68/push  _test-output-stream/imm32
 876     # . . call
 877     e8/call  check-next-stream-line-equal/disp32
 878     # . . discard args
 879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 880     # . check-next-stream-line-equal(_test-output-stream, "== data 0x2", msg)
 881     # . . push args
 882     68/push  "F - test-subx-pack-code-and-data-segments/3"/imm32
 883     68/push  "== data 0x2"/imm32
 884     68/push  _test-output-stream/imm32
 885     # . . call
 886     e8/call  check-next-stream-line-equal/disp32
 887     # . . discard args
 888     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 889     # . check-next-stream-line-equal(_test-output-stream, "03 04 00 00 00 ", msg)
 890     # . . push args
 891     68/push  "F - test-subx-pack-code-and-data-segments/4"/imm32
 892     68/push  "03 04 00 00 00 "/imm32
 893     68/push  _test-output-stream/imm32
 894     # . . call
 895     e8/call  check-next-stream-line-equal/disp32
 896     # . . discard args
 897     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 898     # . epilogue
 899     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 900     5d/pop-to-ebp
 901     c3/return
 902 
 903 convert-data:  # line: (addr stream byte), out: (addr buffered-file)
 904     # pseudocode:
 905     #   var word-slice: slice
 906     #   while true
 907     #     word-slice = next-word(line)
 908     #     if slice-empty?(word-slice)                 # end of file (maybe including trailing whitespace)
 909     #       break  # skip emitting some whitespace
 910     #     if slice-starts-with?(word-slice, "#")      # comment
 911     #       write-slice-buffered(out, word-slice)
 912     #       return
 913     #     if slice-ends-with?(word-slice, ":")        # label
 914     #       write-stream-data(out, line)
 915     #       return
 916     #     if has-metadata?(word-slice, "imm32")
 917     #       emit(out, word-slice, 4)
 918     #     # disp32 is not permitted in data segments, and anything else is only a byte long
 919     #     else
 920     #       emit(out, word-slice, 1)
 921     #   write-buffered(out, "\n")
 922     #
 923     # . prologue
 924     55/push-ebp
 925     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 926     # . save registers
 927     50/push-eax
 928     51/push-ecx
 929     52/push-edx
 930     # var word-slice/ecx: slice
 931     68/push  0/imm32/end
 932     68/push  0/imm32/start
 933     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 934 +-- 26 lines: #?     # dump line ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 960 $convert-data:loop:
 961     # next-word(line, word-slice)
 962     # . . push args
 963     51/push-ecx
 964     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 965     # . . call
 966     e8/call  next-word/disp32
 967     # . . discard args
 968     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 969 +-- 40 lines: #?     # dump word-slice ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1009 $convert-data:check0:
1010     # if (slice-empty?(word-slice)) break
1011     # . eax = slice-empty?(word-slice)
1012     # . . push args
1013     51/push-ecx
1014     # . . call
1015     e8/call  slice-empty?/disp32
1016     # . . discard args
1017     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1018     # . if (eax != false) break
1019     3d/compare-eax-and  0/imm32/false
1020     0f 85/jump-if-!=  $convert-data:break/disp32
1021 $convert-data:check-for-comment:
1022     # if (slice-starts-with?(word-slice, "#"))
1023     # . var start/edx: (addr byte) = word-slice->start
1024     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
1025     # . var c/eax: byte = *start
1026     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1027     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
1028     # . if (c != '#') goto next check
1029     3d/compare-eax-and  0x23/imm32/hash
1030     75/jump-if-!=  $convert-data:check-for-label/disp8
1031 $convert-data:comment:
1032     # write-slice-buffered(out, word-slice)
1033     # . . push args
1034     51/push-ecx
1035     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1036     # . . call
1037     e8/call  write-slice-buffered/disp32
1038     # . . discard args
1039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1040     # return
1041     0f 85/jump-if-!=  $convert-data:end/disp32
1042 $convert-data:check-for-label:
1043     # if (slice-ends-with?(word-slice, ":"))
1044     # . var end/edx: (addr byte) = word-slice->end
1045     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1046     # . var c/eax: byte = *(end-1)
1047     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1048     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
1049     # . if (c != ':') goto next check
1050     3d/compare-eax-and  0x3a/imm32/colon
1051     75/jump-if-!=  $convert-data:check-for-imm32/disp8
1052 $convert-data:label:
1053     # write-stream-data(out, line)
1054     # . . push args
1055     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1056     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1057     # . . call
1058     e8/call  write-stream-data/disp32
1059     # . . discard args
1060     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1061     # return
1062     75/jump-if-!=  $convert-data:end/disp8
1063 $convert-data:check-for-imm32:
1064     # if (has-metadata?(word-slice, "imm32"))
1065     # . eax = has-metadata?(ecx, "imm32")
1066     # . . push args
1067     68/push  "imm32"/imm32
1068     51/push-ecx
1069     # . . call
1070     e8/call  has-metadata?/disp32
1071     # . . discard args
1072     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1073     # . if (eax == false) process as a single byte
1074     3d/compare-eax-and  0/imm32/false
1075     74/jump-if-=  $convert-data:single-byte/disp8
1076 $convert-data:imm32:
1077     # emit(out, word-slice, 4)
1078     # . . push args
1079     68/push  4/imm32
1080     51/push-ecx
1081     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1082     # . . call
1083     e8/call  emit/disp32
1084     # . . discard args
1085     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1086     e9/jump  $convert-data:loop/disp32
1087 $convert-data:single-byte:
1088     # emit(out, word-slice, 1)
1089     # . . push args
1090     68/push  1/imm32
1091     51/push-ecx
1092     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1093     # . . call
1094     e8/call  emit/disp32
1095     # . . discard args
1096     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1097     e9/jump  $convert-data:loop/disp32
1098 $convert-data:break:
1099     # write-buffered(out, "\n")
1100     # . . push args
1101     68/push  Newline/imm32
1102     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1103     # . . call
1104     e8/call  write-buffered/disp32
1105     # . . discard args
1106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1107 $convert-data:end:
1108     # . reclaim locals
1109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1110     # . restore registers
1111     5a/pop-to-edx
1112     59/pop-to-ecx
1113     58/pop-to-eax
1114     # . epilogue
1115     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1116     5d/pop-to-ebp
1117     c3/return
1118 
1119 test-convert-data-passes-comments-through:
1120     # if a line starts with '#', pass it along unchanged
1121     # . prologue
1122     55/push-ebp
1123     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1124     # setup
1125     # . clear-stream(_test-input-stream)
1126     # . . push args
1127     68/push  _test-input-stream/imm32
1128     # . . call
1129     e8/call  clear-stream/disp32
1130     # . . discard args
1131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1132     # . clear-stream(_test-output-stream)
1133     # . . push args
1134     68/push  _test-output-stream/imm32
1135     # . . call
1136     e8/call  clear-stream/disp32
1137     # . . discard args
1138     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1139     # . clear-stream($_test-output-buffered-file->buffer)
1140     # . . push args
1141     68/push  $_test-output-buffered-file->buffer/imm32
1142     # . . call
1143     e8/call  clear-stream/disp32
1144     # . . discard args
1145     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1146     # initialize input
1147     # . write(_test-input-stream, "# abcd")
1148     # . . push args
1149     68/push  "# abcd"/imm32
1150     68/push  _test-input-stream/imm32
1151     # . . call
1152     e8/call  write/disp32
1153     # . . discard args
1154     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1155     # convert-data(_test-input-stream, _test-output-buffered-file)
1156     # . . push args
1157     68/push  _test-output-buffered-file/imm32
1158     68/push  _test-input-stream/imm32
1159     # . . call
1160     e8/call  convert-data/disp32
1161     # . . discard args
1162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1163     # check that the line just passed through
1164     # . flush(_test-output-buffered-file)
1165     # . . push args
1166     68/push  _test-output-buffered-file/imm32
1167     # . . call
1168     e8/call  flush/disp32
1169     # . . discard args
1170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1171 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1197     # . check-stream-equal(_test-output-stream, "# abcd", msg)
1198     # . . push args
1199     68/push  "F - test-convert-data-passes-comments-through"/imm32
1200     68/push  "# abcd"/imm32
1201     68/push  _test-output-stream/imm32
1202     # . . call
1203     e8/call  check-stream-equal/disp32
1204     # . . discard args
1205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1206     # . epilogue
1207     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1208     5d/pop-to-ebp
1209     c3/return
1210 
1211 test-convert-data-passes-labels-through:
1212     # if the first word ends with ':', pass along the entire line unchanged
1213     # . prologue
1214     55/push-ebp
1215     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1216     # setup
1217     # . clear-stream(_test-input-stream)
1218     # . . push args
1219     68/push  _test-input-stream/imm32
1220     # . . call
1221     e8/call  clear-stream/disp32
1222     # . . discard args
1223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1224     # . clear-stream(_test-output-stream)
1225     # . . push args
1226     68/push  _test-output-stream/imm32
1227     # . . call
1228     e8/call  clear-stream/disp32
1229     # . . discard args
1230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1231     # . clear-stream($_test-output-buffered-file->buffer)
1232     # . . push args
1233     68/push  $_test-output-buffered-file->buffer/imm32
1234     # . . call
1235     e8/call  clear-stream/disp32
1236     # . . discard args
1237     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1238     # initialize input
1239     # . write(_test-input-stream, "ab: # cd")
1240     # . . push args
1241     68/push  "ab: # cd"/imm32
1242     68/push  _test-input-stream/imm32
1243     # . . call
1244     e8/call  write/disp32
1245     # . . discard args
1246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1247     # convert-data(_test-input-stream, _test-output-buffered-file)
1248     # . . push args
1249     68/push  _test-output-buffered-file/imm32
1250     68/push  _test-input-stream/imm32
1251     # . . call
1252     e8/call  convert-data/disp32
1253     # . . discard args
1254     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1255     # check that the line just passed through
1256     # . flush(_test-output-buffered-file)
1257     # . . push args
1258     68/push  _test-output-buffered-file/imm32
1259     # . . call
1260     e8/call  flush/disp32
1261     # . . discard args
1262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1263     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
1264     # . . push args
1265     68/push  "F - test-convert-data-passes-labels-through"/imm32
1266     68/push  "ab: # cd"/imm32
1267     68/push  _test-output-stream/imm32
1268     # . . call
1269     e8/call  check-stream-equal/disp32
1270     # . . discard args
1271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1272     # . epilogue
1273     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1274     5d/pop-to-ebp
1275     c3/return
1276 
1277 test-convert-data-passes-names-through:
1278     # If a word is a valid name, just emit it unchanged.
1279     # Later phases will deal with it.
1280     # . prologue
1281     55/push-ebp
1282     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1283     # setup
1284     # . clear-stream(_test-input-stream)
1285     # . . push args
1286     68/push  _test-input-stream/imm32
1287     # . . call
1288     e8/call  clear-stream/disp32
1289     # . . discard args
1290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1291     # . clear-stream(_test-output-stream)
1292     # . . push args
1293     68/push  _test-output-stream/imm32
1294     # . . call
1295     e8/call  clear-stream/disp32
1296     # . . discard args
1297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1298     # . clear-stream($_test-output-buffered-file->buffer)
1299     # . . push args
1300     68/push  $_test-output-buffered-file->buffer/imm32
1301     # . . call
1302     e8/call  clear-stream/disp32
1303     # . . discard args
1304     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1305     # initialize input
1306     # . write(_test-input-stream, "abcd/imm32")
1307     # . . push args
1308     68/push  "abcd/imm32"/imm32
1309     68/push  _test-input-stream/imm32
1310     # . . call
1311     e8/call  write/disp32
1312     # . . discard args
1313     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1314     # convert-data(_test-input-stream, _test-output-buffered-file)
1315     # . . push args
1316     68/push  _test-output-buffered-file/imm32
1317     68/push  _test-input-stream/imm32
1318     # . . call
1319     e8/call  convert-data/disp32
1320     # . . discard args
1321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1322     # check that the line just passed through
1323     # . flush(_test-output-buffered-file)
1324     # . . push args
1325     68/push  _test-output-buffered-file/imm32
1326     # . . call
1327     e8/call  flush/disp32
1328     # . . discard args
1329     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1330     # . check-stream-equal(_test-output-stream, "abcd/imm32 \n", msg)
1331     # . . push args
1332     68/push  "F - test-convert-data-passes-names-through"/imm32
1333     68/push  "abcd/imm32 \n"/imm32
1334     68/push  _test-output-stream/imm32
1335     # . . call
1336     e8/call  check-stream-equal/disp32
1337     # . . discard args
1338     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1339     # . epilogue
1340     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1341     5d/pop-to-ebp
1342     c3/return
1343 
1344 test-convert-data-handles-imm32:
1345     # If a word has the /imm32 metadata, emit it in 4 bytes.
1346     # . prologue
1347     55/push-ebp
1348     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1349     # setup
1350     # . clear-stream(_test-input-stream)
1351     # . . push args
1352     68/push  _test-input-stream/imm32
1353     # . . call
1354     e8/call  clear-stream/disp32
1355     # . . discard args
1356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1357     # . clear-stream(_test-output-stream)
1358     # . . push args
1359     68/push  _test-output-stream/imm32
1360     # . . call
1361     e8/call  clear-stream/disp32
1362     # . . discard args
1363     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1364     # . clear-stream($_test-output-buffered-file->buffer)
1365     # . . push args
1366     68/push  $_test-output-buffered-file->buffer/imm32
1367     # . . call
1368     e8/call  clear-stream/disp32
1369     # . . discard args
1370     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1371     # initialize input
1372     # . write(_test-input-stream, "30/imm32")
1373     # . . push args
1374     68/push  "30/imm32"/imm32
1375     68/push  _test-input-stream/imm32
1376     # . . call
1377     e8/call  write/disp32
1378     # . . discard args
1379     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1380     # convert-data(_test-input-stream, _test-output-buffered-file)
1381     # . . push args
1382     68/push  _test-output-buffered-file/imm32
1383     68/push  _test-input-stream/imm32
1384     # . . call
1385     e8/call  convert-data/disp32
1386     # . . discard args
1387     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1388     # check that 4 bytes were written
1389     # . flush(_test-output-buffered-file)
1390     # . . push args
1391     68/push  _test-output-buffered-file/imm32
1392     # . . call
1393     e8/call  flush/disp32
1394     # . . discard args
1395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1396     # . check-stream-equal(_test-output-stream, "30 00 00 00 \n", msg)
1397     # . . push args
1398     68/push  "F - test-convert-data-handles-imm32"/imm32
1399     68/push  "30 00 00 00 \n"/imm32
1400     68/push  _test-output-stream/imm32
1401     # . . call
1402     e8/call  check-stream-equal/disp32
1403     # . . discard args
1404     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1405     # . epilogue
1406     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1407     5d/pop-to-ebp
1408     c3/return
1409 
1410 test-convert-data-handles-single-byte:
1411     # Any metadata but /imm32 will emit a single byte.
1412     # Data segments can't have /disp32, and SubX doesn't support 16-bit operands.
1413     # . prologue
1414     55/push-ebp
1415     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1416     # setup
1417     # . clear-stream(_test-input-stream)
1418     # . . push args
1419     68/push  _test-input-stream/imm32
1420     # . . call
1421     e8/call  clear-stream/disp32
1422     # . . discard args
1423     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1424     # . clear-stream(_test-output-stream)
1425     # . . push args
1426     68/push  _test-output-stream/imm32
1427     # . . call
1428     e8/call  clear-stream/disp32
1429     # . . discard args
1430     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1431     # . clear-stream($_test-output-buffered-file->buffer)
1432     # . . push args
1433     68/push  $_test-output-buffered-file->buffer/imm32
1434     # . . call
1435     e8/call  clear-stream/disp32
1436     # . . discard args
1437     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1438     # initialize input
1439     # . write(_test-input-stream, "30/imm16")
1440     # . . push args
1441     68/push  "30/imm16"/imm32
1442     68/push  _test-input-stream/imm32
1443     # . . call
1444     e8/call  write/disp32
1445     # . . discard args
1446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1447     # convert-data(_test-input-stream, _test-output-buffered-file)
1448     # . . push args
1449     68/push  _test-output-buffered-file/imm32
1450     68/push  _test-input-stream/imm32
1451     # . . call
1452     e8/call  convert-data/disp32
1453     # . . discard args
1454     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1455     # check that a single byte was written (imm16 is not a valid operand type)
1456     # . flush(_test-output-buffered-file)
1457     # . . push args
1458     68/push  _test-output-buffered-file/imm32
1459     # . . call
1460     e8/call  flush/disp32
1461     # . . discard args
1462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1463     # . check-stream-equal(_test-output-stream, "30 \n", msg)
1464     # . . push args
1465     68/push  "F - test-convert-data-handles-single-byte"/imm32
1466     68/push  "30 \n"/imm32
1467     68/push  _test-output-stream/imm32
1468     # . . call
1469     e8/call  check-stream-equal/disp32
1470     # . . discard args
1471     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1472     # . epilogue
1473     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1474     5d/pop-to-ebp
1475     c3/return
1476 
1477 test-convert-data-multiple-bytes:
1478     # Multiple single-byte words in input stream get processed one by one.
1479     # . prologue
1480     55/push-ebp
1481     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1482     # setup
1483     # . clear-stream(_test-input-stream)
1484     # . . push args
1485     68/push  _test-input-stream/imm32
1486     # . . call
1487     e8/call  clear-stream/disp32
1488     # . . discard args
1489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1490     # . clear-stream(_test-output-stream)
1491     # . . push args
1492     68/push  _test-output-stream/imm32
1493     # . . call
1494     e8/call  clear-stream/disp32
1495     # . . discard args
1496     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1497     # . clear-stream($_test-output-buffered-file->buffer)
1498     # . . push args
1499     68/push  $_test-output-buffered-file->buffer/imm32
1500     # . . call
1501     e8/call  clear-stream/disp32
1502     # . . discard args
1503     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1504     # initialize input
1505     # . write(_test-input-stream, "1 2")
1506     # . . push args
1507     68/push  "1 2"/imm32
1508     68/push  _test-input-stream/imm32
1509     # . . call
1510     e8/call  write/disp32
1511     # . . discard args
1512     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1513     # convert-data(_test-input-stream, _test-output-buffered-file)
1514     # . . push args
1515     68/push  _test-output-buffered-file/imm32
1516     68/push  _test-input-stream/imm32
1517     # . . call
1518     e8/call  convert-data/disp32
1519     # . . discard args
1520     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1521     # check output
1522     # . flush(_test-output-buffered-file)
1523     # . . push args
1524     68/push  _test-output-buffered-file/imm32
1525     # . . call
1526     e8/call  flush/disp32
1527     # . . discard args
1528     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1529     # . check-stream-equal(_test-output-stream, "01 02 \n", msg)
1530     # . . push args
1531     68/push  "F - test-convert-data-multiple-bytes"/imm32
1532     68/push  "01 02 \n"/imm32
1533     68/push  _test-output-stream/imm32
1534     # . . call
1535     e8/call  check-stream-equal/disp32
1536     # . . discard args
1537     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1538     # . epilogue
1539     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1540     5d/pop-to-ebp
1541     c3/return
1542 
1543 test-convert-data-byte-then-name:
1544     # Single-byte word followed by valid name get processed one by one.
1545     # . prologue
1546     55/push-ebp
1547     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1548     # setup
1549     # . clear-stream(_test-input-stream)
1550     # . . push args
1551     68/push  _test-input-stream/imm32
1552     # . . call
1553     e8/call  clear-stream/disp32
1554     # . . discard args
1555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1556     # . clear-stream(_test-output-stream)
1557     # . . push args
1558     68/push  _test-output-stream/imm32
1559     # . . call
1560     e8/call  clear-stream/disp32
1561     # . . discard args
1562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1563     # . clear-stream($_test-output-buffered-file->buffer)
1564     # . . push args
1565     68/push  $_test-output-buffered-file->buffer/imm32
1566     # . . call
1567     e8/call  clear-stream/disp32
1568     # . . discard args
1569     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1570     # initialize input
1571     # . write(_test-input-stream, "30 abcd/o")
1572     # . . push args
1573     68/push  "30 abcd/o"/imm32
1574     68/push  _test-input-stream/imm32
1575     # . . call
1576     e8/call  write/disp32
1577     # . . discard args
1578     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1579     # convert-data(_test-input-stream, _test-output-buffered-file)
1580     # . . push args
1581     68/push  _test-output-buffered-file/imm32
1582     68/push  _test-input-stream/imm32
1583     # . . call
1584     e8/call  convert-data/disp32
1585     # . . discard args
1586     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1587     # check output
1588     # . flush(_test-output-buffered-file)
1589     # . . push args
1590     68/push  _test-output-buffered-file/imm32
1591     # . . call
1592     e8/call  flush/disp32
1593     # . . discard args
1594     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1595     # . check-stream-equal(_test-output-stream, "30 abcd/o \n", msg)
1596     # . . push args
1597     68/push  "F - test-convert-data-byte-then-name"/imm32
1598     68/push  "30 abcd/o \n"/imm32
1599     68/push  _test-output-stream/imm32
1600     # . . call
1601     e8/call  check-stream-equal/disp32
1602     # . . discard args
1603     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1604     # . epilogue
1605     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1606     5d/pop-to-ebp
1607     c3/return
1608 
1609 test-convert-data-multiple-words:
1610     # Multiple words in input stream get processed one by one.
1611     # . prologue
1612     55/push-ebp
1613     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1614     # setup
1615     # . clear-stream(_test-input-stream)
1616     # . . push args
1617     68/push  _test-input-stream/imm32
1618     # . . call
1619     e8/call  clear-stream/disp32
1620     # . . discard args
1621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1622     # . clear-stream(_test-output-stream)
1623     # . . push args
1624     68/push  _test-output-stream/imm32
1625     # . . call
1626     e8/call  clear-stream/disp32
1627     # . . discard args
1628     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1629     # . clear-stream($_test-output-buffered-file->buffer)
1630     # . . push args
1631     68/push  $_test-output-buffered-file->buffer/imm32
1632     # . . call
1633     e8/call  clear-stream/disp32
1634     # . . discard args
1635     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1636     # initialize input
1637     # . write(_test-input-stream, "30 abcd/o 42e1/imm32")
1638     # . . push args
1639     68/push  "30 abcd/o 42e1/imm32"/imm32
1640     68/push  _test-input-stream/imm32
1641     # . . call
1642     e8/call  write/disp32
1643     # . . discard args
1644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1645     # convert-data(_test-input-stream, _test-output-buffered-file)
1646     # . . push args
1647     68/push  _test-output-buffered-file/imm32
1648     68/push  _test-input-stream/imm32
1649     # . . call
1650     e8/call  convert-data/disp32
1651     # . . discard args
1652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1653     # check output
1654     # . flush(_test-output-buffered-file)
1655     # . . push args
1656     68/push  _test-output-buffered-file/imm32
1657     # . . call
1658     e8/call  flush/disp32
1659     # . . discard args
1660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1661 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1687     # . check-stream-equal(_test-output-stream, "30 abcd/o 42 e1 00 00 \n", msg)
1688     # . . push args
1689     68/push  "F - test-convert-data-multiple-words"/imm32
1690     68/push  "30 abcd/o e1 42 00 00 \n"/imm32
1691     68/push  _test-output-stream/imm32
1692     # . . call
1693     e8/call  check-stream-equal/disp32
1694     # . . discard args
1695     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1696     # . epilogue
1697     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1698     5d/pop-to-ebp
1699     c3/return
1700 
1701 test-convert-data-trailing-comment:
1702     # Trailing comments in data segment get appropriately ignored.
1703     # . prologue
1704     55/push-ebp
1705     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1706     # setup
1707     # . clear-stream(_test-input-stream)
1708     # . . push args
1709     68/push  _test-input-stream/imm32
1710     # . . call
1711     e8/call  clear-stream/disp32
1712     # . . discard args
1713     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1714     # . clear-stream(_test-output-stream)
1715     # . . push args
1716     68/push  _test-output-stream/imm32
1717     # . . call
1718     e8/call  clear-stream/disp32
1719     # . . discard args
1720     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1721     # . clear-stream($_test-output-buffered-file->buffer)
1722     # . . push args
1723     68/push  $_test-output-buffered-file->buffer/imm32
1724     # . . call
1725     e8/call  clear-stream/disp32
1726     # . . discard args
1727     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1728     # initialize input
1729     # . write(_test-input-stream, "30/imm32 # comment")
1730     # . . push args
1731     68/push  "30/imm32 # comment"/imm32
1732     68/push  _test-input-stream/imm32
1733     # . . call
1734     e8/call  write/disp32
1735     # . . discard args
1736     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1737     # convert-data(_test-input-stream, _test-output-buffered-file)
1738     # . . push args
1739     68/push  _test-output-buffered-file/imm32
1740     68/push  _test-input-stream/imm32
1741     # . . call
1742     e8/call  convert-data/disp32
1743     # . . discard args
1744     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1745     # check output
1746     # . flush(_test-output-buffered-file)
1747     # . . push args
1748     68/push  _test-output-buffered-file/imm32
1749     # . . call
1750     e8/call  flush/disp32
1751     # . . discard args
1752     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1753 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1779     # . check-stream-equal(_test-output-stream, "30 00 00 00 # comment", msg)
1780     # . . push args
1781     68/push  "F - test-convert-data-trailing-comment"/imm32
1782     68/push  "30 00 00 00 # comment"/imm32
1783     68/push  _test-output-stream/imm32
1784     # . . call
1785     e8/call  check-stream-equal/disp32
1786     # . . discard args
1787     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1788     # . epilogue
1789     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1790     5d/pop-to-ebp
1791     c3/return
1792 
1793 # pack an instruction, following the C++ version
1794 #
1795 # zero error handling at the moment (continuing to rely on the C++ version for that):
1796 #   missing fields are always 0-filled
1797 #   bytes never mentioned are silently dropped; if you don't provide /mod, /rm32 or /r32 you don't get a 0 ModR/M byte. You get *no* ModR/M byte.
1798 #   may pick up any of duplicate operands in an instruction
1799 #   silently drop extraneous operands
1800 #   unceremoniously abort on non-numeric operands except disp or imm
1801 #   opcodes must be lowercase and zero padded
1802 #   opcodes with misleading operand metadata may get duplicated as operands as well. don't rely on this.
1803 convert-instruction:  # line: (addr stream byte), out: (addr buffered-file)
1804     # pseudocode:
1805     #   # some early exits
1806     #   var word-slice = next-word(line)
1807     #   if slice-empty?(word-slice)
1808     #     write-stream-data(out, line)
1809     #     return
1810     #   if slice-starts-with?(word-slice, "#")
1811     #     write-stream-data(out, line)
1812     #     return
1813     #   if slice-ends-with?(word-slice, ":")
1814     #     write-stream-data(out, line)
1815     #     return
1816     #   # really convert
1817     #   emit-opcodes(line, out)
1818     #   emit-modrm(line, out)
1819     #   emit-sib(line, out)
1820     #   emit-disp(line, out)
1821     #   emit-imm(line, out)
1822     #   emit-line-in-comment(line, out)
1823     #
1824     # . prologue
1825     55/push-ebp
1826     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1827     # . save registers
1828     50/push-eax
1829     51/push-ecx
1830     52/push-edx
1831     # var word-slice/ecx: slice
1832     68/push  0/imm32/end
1833     68/push  0/imm32/start
1834     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1835     # next-word(line, word-slice)
1836     # . . push args
1837     51/push-ecx
1838     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1839     # . . call
1840     e8/call  next-word/disp32
1841     # . . discard args
1842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1843 $convert-instruction:check0:
1844     # if (slice-empty?(word-slice)) break
1845     # . eax = slice-empty?(word-slice)
1846     # . . push args
1847     51/push-ecx
1848     # . . call
1849     e8/call  slice-empty?/disp32
1850     # . . discard args
1851     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1852     # . if (eax != false) pass through
1853     3d/compare-eax-and  0/imm32/false
1854     75/jump-if-!=  $convert-instruction:pass-through/disp8
1855 $convert-instruction:check1:
1856     # if (slice-starts-with?(word-slice, "#")) write-stream-data(out, line)
1857     # . var start/edx: (addr byte) = word-slice->start
1858     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
1859     # . var c/eax: byte = *start
1860     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1861     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
1862     # . if (c == '#') pass through
1863     3d/compare-eax-and  0x23/imm32/hash
1864     74/jump-if-=  $convert-instruction:pass-through/disp8
1865 $convert-instruction:check2:
1866     # if (slice-ends-with?(word-slice, ":")) write-stream-data(out, line)
1867     # . var end/edx: (addr byte) = word-slice->end
1868     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1869     # . var c/eax: byte = *(end-1)
1870     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1871     8a/copy-byte                    1/mod/*+disp8   2/rm32/edx    .           .             .           0/r32/AL    -1/disp8        .                 # copy byte at *ecx to AL
1872     # . if (c == ':') pass through
1873     3d/compare-eax-and  0x3a/imm32/colon
1874     75/jump-if-!=  $convert-instruction:really-convert/disp8
1875 $convert-instruction:pass-through:
1876     # write-stream-data(out, line)
1877     # . . push args
1878     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1879     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1880     # . . call
1881     e8/call  write-stream-data/disp32
1882     # . . discard args
1883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1884     # return
1885     eb/jump  $convert-instruction:end/disp8
1886 $convert-instruction:really-convert:
1887     # emit-opcodes(line, out)
1888     # . . push args
1889     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1890     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1891     # . . call
1892     e8/call  emit-opcodes/disp32
1893     # . . discard args
1894     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1895     # emit-modrm(line, out)
1896     # . . push args
1897     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1898     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1899     # . . call
1900     e8/call  emit-modrm/disp32
1901     # . . discard args
1902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1903     # emit-sib(line, out)
1904     # . . push args
1905     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1906     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1907     # . . call
1908     e8/call  emit-sib/disp32
1909     # . . discard args
1910     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1911     # emit-disp(line, out)
1912     # . . push args
1913     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1914     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1915     # . . call
1916     e8/call  emit-disp/disp32
1917     # . . discard args
1918     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1919     # emit-imm(line, out)
1920     # . . push args
1921     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1922     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1923     # . . call
1924     e8/call  emit-imm/disp32
1925     # . . discard args
1926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1927     # emit-line-in-comment(line, out)
1928     # . . push args
1929     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
1930     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1931     # . . call
1932     e8/call  emit-line-in-comment/disp32
1933     # . . discard args
1934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1935 $convert-instruction:end:
1936     # . restore locals
1937     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1938     # . restore registers
1939     5a/pop-to-edx
1940     59/pop-to-ecx
1941     58/pop-to-eax
1942     # . epilogue
1943     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1944     5d/pop-to-ebp
1945     c3/return
1946 
1947 emit-opcodes:  # line: (addr stream byte), out: (addr buffered-file)
1948     # opcodes occupy 1-3 bytes:
1949     #   xx
1950     #   0f xx
1951     #   f2 xx
1952     #   f3 xx
1953     #   f2 0f xx
1954     #   f3 0f xx
1955     #
1956     # pseudocode:
1957     #   rewind-stream(line)
1958     #
1959     #   var op1 = next-word(line)
1960     #   if (slice-empty?(op1) || slice-starts-with?(op1, "#")) return
1961     #   op1 = next-token-from-slice(op1->start, op1->end, "/")
1962     #   write-slice-buffered(out, op1)
1963     #   if !slice-equal?(op1, "0f") && !slice-equal?(op1, "f2") && !slice-equal?(op1, "f3")
1964     #     return
1965     #
1966     #   var op2 = next-word(line)
1967     #   if (slice-empty?(op2) || slice-starts-with?(op2, "#")) return
1968     #   op2 = next-token-from-slice(op2->start, op2->end, "/")
1969     #   write-slice-buffered(out, op2)
1970     #   if slice-equal?(op1, "0f")
1971     #     return
1972     #   if !slice-equal?(op2, "0f")
1973     #     return
1974     #
1975     #   var op3 = next-word(line)
1976     #   if (slice-empty?(op3) || slice-starts-with?(op3, "#")) return
1977     #   op3 = next-token-from-slice(op3->start, op3->end, "/")
1978     #   write-slice-buffered(out, op3)
1979     #
1980     # . prologue
1981     55/push-ebp
1982     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1983     # . save registers
1984     50/push-eax
1985     51/push-ecx
1986     52/push-edx
1987     53/push-ebx
1988     # var op1/ecx: slice
1989     68/push  0/imm32/end
1990     68/push  0/imm32/start
1991     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1992     # var op2/edx: slice
1993     68/push  0/imm32/end
1994     68/push  0/imm32/start
1995     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
1996     # rewind-stream(line)
1997     # . . push args
1998     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1999     # . . call
2000     e8/call  rewind-stream/disp32
2001     # . . discard args
2002     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2003 $emit-opcodes:op1:
2004     # next-word(line, op1)
2005     # . . push args
2006     51/push-ecx
2007     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2008     # . . call
2009     e8/call  next-word/disp32
2010     # . . discard args
2011     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2012     # if (slice-empty?(op1)) return
2013     # . eax = slice-empty?(op1)
2014     # . . push args
2015     51/push-ecx
2016     # . . call
2017     e8/call  slice-empty?/disp32
2018     # . . discard args
2019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2020     # . if (eax != false) return
2021     3d/compare-eax-and  0/imm32/false
2022     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2023     # if (slice-starts-with?(op1, "#")) return
2024     # . var start/ebx: (addr byte) = op1->start
2025     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # copy *ecx to ebx
2026     # . var c/eax: byte = *start
2027     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2028     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2029     # . if (c == '#') return
2030     3d/compare-eax-and  0x23/imm32/hash
2031     0f 84/jump-if-=  $emit-opcodes:end/disp32
2032     # op1 = next-token-from-slice(op1->start, op1->end, '/')
2033     # . . push args
2034     51/push-ecx
2035     68/push  0x2f/imm32/slash
2036     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
2037     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
2038     # . . call
2039     e8/call  next-token-from-slice/disp32
2040     # . . discard args
2041     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2042     # write-slice-buffered(out, op1)
2043     # . . push args
2044     51/push-ecx
2045     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2046     # . . call
2047     e8/call  write-slice-buffered/disp32
2048     # . . discard args
2049     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2050     # write-buffered(out, " ")
2051     # . . push args
2052     68/push  Space/imm32
2053     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2054     # . . call
2055     e8/call  write-buffered/disp32
2056     # . . discard args
2057     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2058     # if (slice-equal?(op1, "0f")) goto op2
2059     # . eax = slice-equal?(op1, "0f")
2060     # . . push args
2061     68/push  "0f"/imm32
2062     51/push-ecx
2063     # . . call
2064     e8/call  slice-equal?/disp32
2065     # . . discard args
2066     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2067     # . if (eax != false) goto op2
2068     3d/compare-eax-and  0/imm32/false
2069     75/jump-if-!=  $emit-opcodes:op2/disp8
2070     # if (slice-equal?(op1, "f2")) goto op2
2071     # . eax = slice-equal?(op1, "f2")
2072     # . . push args
2073     68/push  "f2"/imm32
2074     51/push-ecx
2075     # . . call
2076     e8/call  slice-equal?/disp32
2077     # . . discard args
2078     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2079     # . if (eax != false) goto op2
2080     3d/compare-eax-and  0/imm32/false
2081     75/jump-if-!=  $emit-opcodes:op2/disp8
2082     # if (slice-equal?(op1, "f3")) goto op2
2083     # . eax = slice-equal?(op1, "f3")
2084     # . . push args
2085     68/push  "f3"/imm32
2086     51/push-ecx
2087     # . . call
2088     e8/call  slice-equal?/disp32
2089     # . . discard args
2090     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2091     # . if (eax != false) goto op2
2092     3d/compare-eax-and  0/imm32/false
2093     75/jump-if-!=  $emit-opcodes:op2/disp8
2094     # otherwise return
2095     e9/jump  $emit-opcodes:end/disp32
2096 $emit-opcodes:op2:
2097     # next-word(line, op2)
2098     # . . push args
2099     52/push-edx
2100     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2101     # . . call
2102     e8/call  next-word/disp32
2103     # . . discard args
2104     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2105     # if (slice-empty?(op2)) return
2106     # . eax = slice-empty?(op2)
2107     # . . push args
2108     52/push-edx
2109     # . . call
2110     e8/call  slice-empty?/disp32
2111     # . . discard args
2112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2113     # . if (eax != false) return
2114     3d/compare-eax-and  0/imm32/false
2115     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2116     # if (slice-starts-with?(op2, "#")) return
2117     # . var start/ebx: (addr byte) = op2->start
2118     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
2119     # . var c/eax: byte = *start
2120     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2121     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2122     # . if (c == '#') return
2123     3d/compare-eax-and  0x23/imm32/hash
2124     0f 84/jump-if-=  $emit-opcodes:end/disp32
2125     # op2 = next-token-from-slice(op2->start, op2->end, '/')
2126     # . . push args
2127     52/push-edx
2128     68/push  0x2f/imm32/slash
2129     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2130     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2131     # . . call
2132     e8/call  next-token-from-slice/disp32
2133     # . . discard args
2134     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2135     # write-slice-buffered(out, op2)
2136     # . . push args
2137     52/push-edx
2138     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2139     # . . call
2140     e8/call  write-slice-buffered/disp32
2141     # . . discard args
2142     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2143     # write-buffered(out, " ")
2144     # . . push args
2145     68/push  Space/imm32
2146     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2147     # . . call
2148     e8/call  write-buffered/disp32
2149     # . . discard args
2150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2151     # if (slice-equal?(op1, "0f")) return
2152     # . eax = slice-equal?(op1, "0f")
2153     # . . push args
2154     68/push  "0f"/imm32
2155     51/push-ecx
2156     # . . call
2157     e8/call  slice-equal?/disp32
2158     # . . discard args
2159     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2160     # . if (eax != false) return
2161     3d/compare-eax-and  0/imm32/false
2162     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2163     # if (!slice-equal?(op2, "0f")) return
2164     # . eax = slice-equal?(op2, "0f")
2165     # . . push args
2166     68/push  "0f"/imm32
2167     52/push-edx
2168     # . . call
2169     e8/call  slice-equal?/disp32
2170     # . . discard args
2171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2172     # . if (eax == false) return
2173     3d/compare-eax-and  0/imm32/false
2174     0f 84/jump-if-=  $emit-opcodes:end/disp32
2175 $emit-opcodes:op3:
2176     # next-word(line, op3)  # reuse op2/edx
2177     # . . push args
2178     52/push-edx
2179     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2180     # . . call
2181     e8/call  next-word/disp32
2182     # . . discard args
2183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2184     # if (slice-empty?(op3)) return
2185     # . eax = slice-empty?(op3)
2186     # . . push args
2187     52/push-edx
2188     # . . call
2189     e8/call  slice-empty?/disp32
2190     # . . discard args
2191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2192     # . if (eax != false) return
2193     3d/compare-eax-and  0/imm32/false
2194     0f 85/jump-if-!=  $emit-opcodes:end/disp32
2195     # if (slice-starts-with?(op3, "#")) return
2196     # . var start/ebx: (addr byte) = op2->start
2197     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
2198     # . var c/eax: byte = *start
2199     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2200     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
2201     # . if (c == '#') return
2202     3d/compare-eax-and  0x23/imm32/hash
2203     0f 84/jump-if-=  $emit-opcodes:end/disp32
2204     # op3 = next-token-from-slice(op3->start, op3->end, '/')
2205     # . . push args
2206     52/push-edx
2207     68/push  0x2f/imm32/slash
2208     ff          6/subop/push        1/mod/*+disp8   2/rm32/edx    .           .             .           .           4/disp8         .                 # push *(edx+4)
2209     ff          6/subop/push        0/mod/indirect  2/rm32/edx    .           .             .           .           .               .                 # push *edx
2210     # . . call
2211     e8/call  next-token-from-slice/disp32
2212     # . . discard args
2213     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2214     # write-slice-buffered(out, op3)
2215     # . . push args
2216     52/push-edx
2217     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2218     # . . call
2219     e8/call  write-slice-buffered/disp32
2220     # . . discard args
2221     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2222     # write-buffered(out, " ")
2223     # . . push args
2224     68/push  Space/imm32
2225     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2226     # . . call
2227     e8/call  write-buffered/disp32
2228     # . . discard args
2229     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2230 $emit-opcodes:end:
2231     # . restore locals
2232     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2233     # . restore registers
2234     5b/pop-to-ebx
2235     5a/pop-to-edx
2236     59/pop-to-ecx
2237     58/pop-to-eax
2238     # . epilogue
2239     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2240     5d/pop-to-ebp
2241     c3/return
2242 
2243 emit-modrm:  # line: (addr stream byte), out: (addr buffered-file)
2244     # pseudocode:
2245     #   rewind-stream(line)
2246     #   var has-modrm? = false, mod = 0, rm32 = 0, r32 = 0
2247     #   var word-slice: slice
2248     #   while true
2249     #     word-slice = next-word(line)
2250     #     if (slice-empty?(word-slice)) break
2251     #     if (slice-starts-with?(word-slice, "#")) break
2252     #     if (has-metadata?(word-slice, "mod"))
2253     #       mod = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2254     #       has-modrm? = true
2255     #     else if has-metadata?(word-slice, "rm32") or has-metadata?(word-slice, "xm32")
2256     #       rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2257     #       has-modrm? = true
2258     #     else if has-metadata?(word-slice, "r32") or has-metadata?(word-slice, "x32") or has-metadata?(word-slice, "subop")
2259     #       r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2260     #       has-modrm? = true
2261     #   if has-modrm?
2262     #     var modrm = mod & 0b11
2263     #     modrm <<= 3
2264     #     modrm |= r32 & 0b111
2265     #     modrm <<= 3
2266     #     modrm |= rm32 & 0b111
2267     #     emit-hex(out, modrm, 1)
2268     #
2269     # . prologue
2270     55/push-ebp
2271     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2272     # . save registers
2273     50/push-eax
2274     51/push-ecx
2275     52/push-edx
2276     53/push-ebx
2277     56/push-esi
2278     57/push-edi
2279     # var word-slice/ecx: slice
2280     68/push  0/imm32/end
2281     68/push  0/imm32/start
2282     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2283     # var has-modrm?/edx: boolean = false
2284     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
2285     # var mod/ebx: byte = 0
2286     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2287     # var rm32/esi: byte = 0
2288     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
2289     # var r32/edi: byte = 0
2290     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
2291     # rewind-stream(line)
2292     # . . push args
2293     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2294     # . . call
2295     e8/call  rewind-stream/disp32
2296     # . . discard args
2297     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2298 +-- 33 lines: #?     # dump line ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2331 $emit-modrm:loop:
2332     # next-word(line, word-slice)
2333     # . . push args
2334     51/push-ecx
2335     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2336     # . . call
2337     e8/call  next-word/disp32
2338     # . . discard args
2339     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2340 +-- 40 lines: #?     # dump word-slice ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2380 $emit-modrm:check0:
2381     # if (slice-empty?(word-slice)) break
2382     # . eax = slice-empty?(word-slice)
2383     # . . push args
2384     51/push-ecx
2385     # . . call
2386     e8/call  slice-empty?/disp32
2387     # . . discard args
2388     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2389     # . if (eax != false) pass through
2390     3d/compare-eax-and  0/imm32/false
2391     0f 85/jump-if-!=  $emit-modrm:break/disp32
2392 $emit-modrm:check1:
2393     # if (slice-starts-with?(word-slice, "#")) break
2394     # . spill edx
2395     52/push-edx
2396     # . var start/edx: (addr byte) = word-slice->start
2397     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2398     # . var c/eax: byte = *start
2399     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2400     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
2401     # . restore edx
2402     5a/pop-to-edx
2403     # . if (c == '#') pass through
2404     3d/compare-eax-and  0x23/imm32/hash
2405     0f 84/jump-if-=  $emit-modrm:break/disp32
2406 $emit-modrm:check-for-mod:
2407     # if (has-metadata?(word-slice, "mod"))
2408     # . eax = has-metadata?(ecx, "mod")
2409     # . . push args
2410     68/push  "mod"/imm32
2411     51/push-ecx
2412     # . . call
2413     e8/call  has-metadata?/disp32
2414     # . . discard args
2415     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2416     # . if (eax == false) goto next check
2417     3d/compare-eax-and  0/imm32/false
2418     74/jump-if-=  $emit-modrm:check-for-rm32/disp8
2419 $emit-modrm:mod:
2420     # mod = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2421     # . eax = parse-datum-of-word(word-slice)
2422     # . . push args
2423     51/push-ecx
2424     # . . call
2425     e8/call  parse-datum-of-word/disp32
2426     # . . discard args
2427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2428     # . mod = eax
2429     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2430     # has-modrm? = true
2431     ba/copy-to-edx  1/imm32/true
2432     # continue
2433     e9/jump  $emit-modrm:loop/disp32
2434 $emit-modrm:check-for-rm32:
2435     # if (has-metadata?(word-slice, "rm32"))
2436     # . eax = has-metadata?(ecx, "rm32")
2437     # . . push args
2438     68/push  "rm32"/imm32
2439     51/push-ecx
2440     # . . call
2441     e8/call  has-metadata?/disp32
2442     # . . discard args
2443     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2444     # . if (eax == false) goto next check
2445     3d/compare-eax-and  0/imm32/false
2446     74/jump-if-=  $emit-modrm:check-for-xm32/disp8
2447 $emit-modrm:rm32:
2448     # rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2449     # . eax = parse-datum-of-word(word-slice)
2450     # . . push args
2451     51/push-ecx
2452     # . . call
2453     e8/call  parse-datum-of-word/disp32
2454     # . . discard args
2455     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2456     # . rm32 = eax
2457     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2458     # has-modrm? = true
2459     ba/copy-to-edx  1/imm32/true
2460     # continue
2461     e9/jump  $emit-modrm:loop/disp32
2462 $emit-modrm:check-for-xm32:
2463     # if (has-metadata?(word-slice, "xm32"))
2464     # . eax = has-metadata?(ecx, "xm32")
2465     # . . push args
2466     68/push  "xm32"/imm32
2467     51/push-ecx
2468     # . . call
2469     e8/call  has-metadata?/disp32
2470     # . . discard args
2471     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2472     # . if (eax == false) goto next check
2473     3d/compare-eax-and  0/imm32/false
2474     74/jump-if-=  $emit-modrm:check-for-r32/disp8
2475 $emit-modrm:xm32:
2476     # rm32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2477     # . eax = parse-datum-of-word(word-slice)
2478     # . . push args
2479     51/push-ecx
2480     # . . call
2481     e8/call  parse-datum-of-word/disp32
2482     # . . discard args
2483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2484     # . rm32 = eax
2485     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2486     # has-modrm? = true
2487     ba/copy-to-edx  1/imm32/true
2488     # continue
2489     e9/jump  $emit-modrm:loop/disp32
2490 $emit-modrm:check-for-r32:
2491     # if (has-metadata?(word-slice, "r32"))
2492     # . eax = has-metadata?(ecx, "r32")
2493     # . . push args
2494     68/push  "r32"/imm32
2495     51/push-ecx
2496     # . . call
2497     e8/call  has-metadata?/disp32
2498     # . . discard args
2499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2500     # . if (eax == false) goto next check
2501     3d/compare-eax-and  0/imm32/false
2502     74/jump-if-=  $emit-modrm:check-for-x32/disp8
2503 $emit-modrm:r32:
2504     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2505     # . eax = parse-datum-of-word(word-slice)
2506     # . . push args
2507     51/push-ecx
2508     # . . call
2509     e8/call  parse-datum-of-word/disp32
2510     # . . discard args
2511     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2512     # . r32 = eax
2513     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2514     # has-modrm? = true
2515     ba/copy-to-edx  1/imm32/true
2516     # continue
2517     e9/jump  $emit-modrm:loop/disp32
2518 $emit-modrm:check-for-x32:
2519     # if (has-metadata?(word-slice, "x32"))
2520     # . eax = has-metadata?(ecx, "x32")
2521     # . . push args
2522     68/push  "x32"/imm32
2523     51/push-ecx
2524     # . . call
2525     e8/call  has-metadata?/disp32
2526     # . . discard args
2527     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2528     # . if (eax == false) goto next check
2529     3d/compare-eax-and  0/imm32/false
2530     74/jump-if-=  $emit-modrm:check-for-subop/disp8
2531 $emit-modrm:x32:
2532     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2533     # . eax = parse-datum-of-word(word-slice)
2534     # . . push args
2535     51/push-ecx
2536     # . . call
2537     e8/call  parse-datum-of-word/disp32
2538     # . . discard args
2539     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2540     # . r32 = eax
2541     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2542     # has-modrm? = true
2543     ba/copy-to-edx  1/imm32/true
2544     # continue
2545     e9/jump  $emit-modrm:loop/disp32
2546 $emit-modrm:check-for-subop:
2547     # if (has-metadata?(word-slice, "subop"))
2548     # . eax = has-metadata?(ecx, "subop")
2549     # . . push args
2550     68/push  "subop"/imm32
2551     51/push-ecx
2552     # . . call
2553     e8/call  has-metadata?/disp32
2554     # . . discard args
2555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2556     # . if (eax == false) loop
2557     3d/compare-eax-and  0/imm32/false
2558     0f 84/jump-if-=  $emit-modrm:loop/disp32
2559 $emit-modrm:subop:
2560     # r32 = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2561     # . eax = parse-datum-of-word(word-slice)
2562     # . . push args
2563     51/push-ecx
2564     # . . call
2565     e8/call  parse-datum-of-word/disp32
2566     # . . discard args
2567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2568     # . r32 = eax
2569     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2570     # has-modrm? = true
2571     ba/copy-to-edx  1/imm32/true
2572     # continue
2573     e9/jump  $emit-modrm:loop/disp32
2574 $emit-modrm:break:
2575     # if (!has-modrm?) return
2576     81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
2577     74/jump-if-=  $emit-modrm:end/disp8
2578 $emit-modrm:calculate:
2579     # var modrm/ebx: byte = mod & 0b11
2580     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
2581     # modrm <<= 3
2582     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2583     # modrm |= r32 & 0b111
2584     81          4/subop/and         3/mod/direct    7/rm32/edi    .           .             .           .           .               7/imm32/0b111     # bitwise and of edi
2585     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           7/r32/edi   .               .                 # ebx = bitwise OR with edi
2586     # modrm <<= 3
2587     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2588     # modrm |= rm32 & 0b111
2589     81          4/subop/and         3/mod/direct    6/rm32/esi    .           .             .           .           .               7/imm32/0b111     # bitwise and of esi
2590     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           6/r32/esi   .               .                 # ebx = bitwise OR with esi
2591 $emit-modrm:emit:
2592     # emit-hex(out, modrm, 1)
2593     # . . push args
2594     68/push  1/imm32
2595     53/push-ebx
2596     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2597     # . . call
2598     e8/call  emit-hex/disp32
2599     # . . discard args
2600     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2601 $emit-modrm:end:
2602     # . restore locals
2603     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2604     # . restore registers
2605     5f/pop-to-edi
2606     5e/pop-to-esi
2607     5b/pop-to-ebx
2608     5a/pop-to-edx
2609     59/pop-to-ecx
2610     58/pop-to-eax
2611     # . epilogue
2612     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2613     5d/pop-to-ebp
2614     c3/return
2615 
2616 emit-sib:  # line: (addr stream byte), out: (addr buffered-file)
2617     # pseudocode:
2618     #   var has-sib? = false, base = 0, index = 0, scale = 0
2619     #   var word-slice: slice
2620     #   while true
2621     #     word-slice = next-word(line)
2622     #     if (slice-empty?(word-slice)) break
2623     #     if (slice-starts-with?(word-slice, "#")) break
2624     #     if (has-metadata?(word-slice, "base")
2625     #       base = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2626     #       has-sib? = true
2627     #     else if (has-metadata?(word-slice, "index")
2628     #       index = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2629     #       has-sib? = true
2630     #     else if (has-metadata?(word-slice, "scale")
2631     #       scale = parse-hex-int-from-slice(next-token-from-slice(word-slice, "/"))
2632     #       has-sib? = true
2633     #   if has-sib?
2634     #     var sib = scale & 0b11
2635     #     sib <<= 3
2636     #     sib |= index & 0b111
2637     #     sib <<= 3
2638     #     sib |= base & 0b111
2639     #     emit-hex(out, sib, 1)
2640     #
2641     # . prologue
2642     55/push-ebp
2643     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2644     # . save registers
2645     50/push-eax
2646     51/push-ecx
2647     52/push-edx
2648     53/push-ebx
2649     56/push-esi
2650     57/push-edi
2651     # var word-slice/ecx: slice
2652     68/push  0/imm32/end
2653     68/push  0/imm32/start
2654     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2655     # var has-sib?/edx: boolean = false
2656     31/xor                          3/mod/direct    2/rm32/edx    .           .             .           2/r32/edx   .               .                 # clear edx
2657     # var scale/ebx: byte = 0
2658     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
2659     # var base/esi: byte = 0
2660     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
2661     # var index/edi: byte = 0
2662     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
2663     # rewind-stream(line)
2664     # . . push args
2665     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2666     # . . call
2667     e8/call  rewind-stream/disp32
2668     # . . discard args
2669     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2670 $emit-sib:loop:
2671 +-- 26 lines: #?     # dump line ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2697     # next-word(line, word-slice)
2698     # . . push args
2699     51/push-ecx
2700     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2701     # . . call
2702     e8/call  next-word/disp32
2703     # . . discard args
2704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2705 +-- 40 lines: #?     # dump word-slice ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2745 $emit-sib:check0:
2746     # if (slice-empty?(word-slice)) break
2747     # . eax = slice-empty?(word-slice)
2748     # . . push args
2749     51/push-ecx
2750     # . . call
2751     e8/call  slice-empty?/disp32
2752     # . . discard args
2753     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2754     # . if (eax != false) pass through
2755     3d/compare-eax-and  0/imm32/false
2756     0f 85/jump-if-!=  $emit-sib:break/disp32
2757 $emit-sib:check1:
2758     # if (slice-starts-with?(word-slice, "#")) break
2759     # . spill edx
2760     52/push-edx
2761     # . var start/edx: (addr byte) = word-slice->start
2762     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
2763     # . var c/eax: byte = *start
2764     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
2765     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
2766     # . restore edx
2767     5a/pop-to-edx
2768     # . if (c == '#') pass through
2769     3d/compare-eax-and  0x23/imm32/hash
2770     0f 84/jump-if-=  $emit-sib:break/disp32
2771 $emit-sib:check-for-scale:
2772     # if (has-metadata?(word-slice, "scale"))
2773     # . eax = has-metadata?(ecx, "scale")
2774     # . . push args
2775     68/push  "scale"/imm32
2776     51/push-ecx
2777     # . . call
2778     e8/call  has-metadata?/disp32
2779     # . . discard args
2780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2781     # . if (eax == false) goto next check
2782     3d/compare-eax-and  0/imm32/false
2783     74/jump-if-=  $emit-sib:check-for-base/disp8
2784 $emit-sib:scale:
2785     # scale = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2786     # . eax = parse-datum-of-word(word-slice)
2787     # . . push args
2788     51/push-ecx
2789     # . . call
2790     e8/call  parse-datum-of-word/disp32
2791     # . . discard args
2792     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2793     # . scale = eax
2794     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2795     # has-sib? = true
2796     ba/copy-to-edx  1/imm32/true
2797     # continue
2798     e9/jump  $emit-sib:loop/disp32
2799 $emit-sib:check-for-base:
2800     # if (has-metadata?(word-slice, "base"))
2801     # . eax = has-metadata?(ecx, "base")
2802     # . . push args
2803     68/push  "base"/imm32
2804     51/push-ecx
2805     # . . call
2806     e8/call  has-metadata?/disp32
2807     # . . discard args
2808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2809     # . if (eax == false) goto next check
2810     3d/compare-eax-and  0/imm32/false
2811     74/jump-if-=  $emit-sib:check-for-index/disp8
2812 $emit-sib:base:
2813     # base = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2814     # . eax = parse-datum-of-word(word-slice)
2815     # . . push args
2816     51/push-ecx
2817     # . . call
2818     e8/call  parse-datum-of-word/disp32
2819     # . . discard args
2820     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2821     # . base = eax
2822     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to esi
2823     # has-sib? = true
2824     ba/copy-to-edx  1/imm32/true
2825     # continue
2826     e9/jump  $emit-sib:loop/disp32
2827 $emit-sib:check-for-index:
2828     # if (has-metadata?(word-slice, "index"))
2829     # . eax = has-metadata?(ecx, "index")
2830     # . . push args
2831     68/push  "index"/imm32
2832     51/push-ecx
2833     # . . call
2834     e8/call  has-metadata?/disp32
2835     # . . discard args
2836     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2837     # . if (eax == false) loop
2838     3d/compare-eax-and  0/imm32/false
2839     0f 84/jump-if-=  $emit-sib:loop/disp32
2840 $emit-sib:index:
2841     # index = parse-hex-int-from-slice(next-token-from-slice(word-slice->start, word-slice->end, '/'))
2842     # . eax = parse-datum-of-word(word-slice)
2843     # . . push args
2844     51/push-ecx
2845     # . . call
2846     e8/call  parse-datum-of-word/disp32
2847     # . . discard args
2848     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2849     # . index = eax
2850     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to edi
2851     # has-sib? = true
2852     ba/copy-to-edx  1/imm32/true
2853     # continue
2854     e9/jump  $emit-sib:loop/disp32
2855 $emit-sib:break:
2856     # if (!has-sib?) return
2857     81          7/subop/compare     3/mod/direct    2/rm32/edx    .           .             .           .           .               0/imm32/false     # compare edx
2858     74/jump-if-=  $emit-sib:end/disp8
2859 $emit-sib:calculate:
2860     # var sib/ebx: byte = scale & 0b11
2861     81          4/subop/and         3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm32/0b11      # bitwise and of ebx
2862     # sib <<= 2
2863     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2864     # sib |= index & 0b111
2865     81          4/subop/and         3/mod/direct    7/rm32/edi    .           .             .           .           .               7/imm32/0b111     # bitwise and of edi
2866     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           7/r32/edi   .               .                 # ebx = bitwise OR with edi
2867     # sib <<= 3
2868     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               3/imm8            # shift ebx left by 3 bits
2869     # sib |= base & 0b111
2870     81          4/subop/and         3/mod/direct    6/rm32/esi    .           .             .           .           .               7/imm32/0b111     # bitwise and of esi
2871     09/or                           3/mod/direct    3/rm32/ebx    .           .             .           6/r32/esi   .               .                 # ebx = bitwise OR with esi
2872 $emit-sib:emit:
2873     # emit-hex(out, sib, 1)
2874     # . . push args
2875     68/push  1/imm32
2876     53/push-ebx
2877     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
2878     # . . call
2879     e8/call  emit-hex/disp32
2880     # . . discard args
2881     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2882 $emit-sib:end:
2883     # . restore locals
2884     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2885     # . restore registers
2886     5f/pop-to-edi
2887     5e/pop-to-esi
2888     5b/pop-to-ebx
2889     5a/pop-to-edx
2890     59/pop-to-ecx
2891     58/pop-to-eax
2892     # . epilogue
2893     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2894     5d/pop-to-ebp
2895     c3/return
2896 
2897 emit-disp:  # line: (addr stream byte), out: (addr buffered-file)
2898     # pseudocode:
2899     #   rewind-stream(line)
2900     #   var word-slice: slice
2901     #   while true
2902     #     word-slice = next-word(line)
2903     #     if (slice-empty?(word-slice)) break
2904     #     if (slice-starts-with?(word-slice, "#")) break
2905     #     if has-metadata?(word-slice, "disp32")
2906     #       emit(out, word-slice, 4)
2907     #       break
2908     #     if has-metadata?(word-slice, "disp16")
2909     #       emit(out, word-slice, 2)
2910     #       break
2911     #     if has-metadata?(word-slice, "disp8")
2912     #       emit(out, word-slice, 1)
2913     #       break
2914     #
2915     # . prologue
2916     55/push-ebp
2917     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2918     # . save registers
2919     50/push-eax
2920     51/push-ecx
2921     52/push-edx
2922     # var word-slice/ecx: slice
2923     68/push  0/imm32/end
2924     68/push  0/imm32/start
2925     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2926     # rewind-stream(line)
2927     # . . push args
2928     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2929     # . . call
2930     e8/call  rewind-stream/disp32
2931     # . . discard args
2932     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2933 +-- 26 lines: #?     # dump line ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2959 $emit-disp:loop:
2960     # next-word(line, word-slice)
2961     # . . push args
2962     51/push-ecx
2963     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
2964     # . . call
2965     e8/call  next-word/disp32
2966     # . . discard args
2967     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2968 +-- 40 lines: #?     # dump word-slice ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3008 $emit-disp:check0:
3009     # if (slice-empty?(word-slice)) break
3010     # . eax = slice-empty?(word-slice)
3011     # . . push args
3012     51/push-ecx
3013     # . . call
3014     e8/call  slice-empty?/disp32
3015     # . . discard args
3016     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3017     # . if (eax != false) pass through
3018     3d/compare-eax-and  0/imm32/false
3019     0f 85/jump-if-!=  $emit-disp:break/disp32
3020 $emit-disp:check1:
3021     # if (slice-starts-with?(word-slice, "#")) break
3022     # . var start/edx: (addr byte) = word-slice->start
3023     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
3024     # . var c/eax: byte = *start
3025     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
3026     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
3027     # . if (c == '#') break
3028     3d/compare-eax-and  0x23/imm32/hash
3029     0f 84/jump-if-=  $emit-disp:break/disp32
3030 $emit-disp:check-for-disp32:
3031     # if (has-metadata?(word-slice, "disp32"))
3032     # . eax = has-metadata?(ecx, "disp32")
3033     # . . push args
3034     68/push  "disp32"/imm32
3035     51/push-ecx
3036     # . . call
3037     e8/call  has-metadata?/disp32
3038     # . . discard args
3039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3040     # . if (eax == false) goto next check
3041     3d/compare-eax-and  0/imm32/false
3042     74/jump-if-=  $emit-disp:check-for-disp16/disp8
3043 $emit-disp:disp32:
3044     # emit(out, word-slice, 4)
3045     # . . push args
3046     68/push  4/imm32
3047     51/push-ecx
3048     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3049     # . . call
3050     e8/call  emit/disp32
3051     # . . discard args
3052     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3053     # break
3054     e9/jump  $emit-disp:break/disp32
3055 $emit-disp:check-for-disp16:
3056     # else if (has-metadata?(word-slice, "disp16"))
3057     # . eax = has-metadata?(ecx, "disp16")
3058     # . . push args
3059     68/push  "disp16"/imm32
3060     51/push-ecx
3061     # . . call
3062     e8/call  has-metadata?/disp32
3063     # . . discard args
3064     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3065     # . if (eax == false) goto next check
3066     3d/compare-eax-and  0/imm32/false
3067     74/jump-if-=  $emit-disp:check-for-disp8/disp8
3068 $emit-disp:disp16:
3069     # emit(out, word-slice, 2)
3070     # . . push args
3071     68/push  2/imm32
3072     51/push-ecx
3073     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3074     # . . call
3075     e8/call  emit/disp32
3076     # . . discard args
3077     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3078     # break
3079     e9/jump  $emit-disp:break/disp32
3080 $emit-disp:check-for-disp8:
3081     # if (has-metadata?(word-slice, "disp8"))
3082     # . eax = has-metadata?(ecx, "disp8")
3083     # . . push args
3084     68/push  "disp8"/imm32
3085     51/push-ecx
3086     # . . call
3087     e8/call  has-metadata?/disp32
3088     # . . discard args
3089     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3090     # . if (eax == false) loop
3091     3d/compare-eax-and  0/imm32/false
3092     0f 84/jump-if-=  $emit-disp:loop/disp32
3093 $emit-disp:disp8:
3094     # emit(out, word-slice, 1)
3095     # . . push args
3096     68/push  1/imm32
3097     51/push-ecx
3098     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3099     # . . call
3100     e8/call  emit/disp32
3101     # . . discard args
3102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3103     # break
3104 $emit-disp:break:
3105     # . restore locals
3106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3107     # . restore registers
3108     5a/pop-to-edx
3109     59/pop-to-ecx
3110     58/pop-to-eax
3111     # . epilogue
3112     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3113     5d/pop-to-ebp
3114     c3/return
3115 
3116 emit-imm:  # line: (addr stream byte), out: (addr buffered-file)
3117     # pseudocode:
3118     #   rewind-stream(line)
3119     #   var word-slice: slice
3120     #   while true
3121     #     word-slice = next-word(line)
3122     #     if (slice-empty?(word-slice)) break
3123     #     if (slice-starts-with?(word-slice, "#")) break
3124     #     if has-metadata?(word-slice, "imm32")
3125     #       emit(out, word-slice, 4)
3126     #       break
3127     #     if has-metadata?(word-slice, "imm16")
3128     #       emit(out, word-slice, 2)
3129     #       break
3130     #     if has-metadata?(word-slice, "imm8")
3131     #       emit(out, word-slice, 1)
3132     #       break
3133     #
3134     # . prologue
3135     55/push-ebp
3136     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3137     # . save registers
3138     50/push-eax
3139     51/push-ecx
3140     52/push-edx
3141     # var word-slice/ecx: slice
3142     68/push  0/imm32/end
3143     68/push  0/imm32/start
3144     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3145     # rewind-stream(line)
3146     # . . push args
3147     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3148     # . . call
3149     e8/call  rewind-stream/disp32
3150     # . . discard args
3151     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3152 +-- 26 lines: #?     # dump line ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3178 $emit-imm:loop:
3179     # next-word(line, word-slice)
3180     # . . push args
3181     51/push-ecx
3182     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3183     # . . call
3184     e8/call  next-word/disp32
3185     # . . discard args
3186     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3187 +-- 40 lines: #?     # dump word-slice ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3227 $emit-imm:check0:
3228     # if (slice-empty?(word-slice)) break
3229     # . eax = slice-empty?(word-slice)
3230     # . . push args
3231     51/push-ecx
3232     # . . call
3233     e8/call  slice-empty?/disp32
3234     # . . discard args
3235     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3236     # . if (eax != false) pass through
3237     3d/compare-eax-and  0/imm32/false
3238     0f 85/jump-if-!=  $emit-imm:break/disp32
3239 $emit-imm:check1:
3240     # if (slice-starts-with?(word-slice, "#")) break
3241     # . var start/edx: (addr byte) = slice->start
3242     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # copy *ecx to edx
3243     # . var c/eax: byte = *start
3244     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
3245     8a/copy-byte                    0/mod/indirect  2/rm32/edx    .           .             .           0/r32/AL    .               .                 # copy byte at *edx to AL
3246     # . if (c == '#') break
3247     3d/compare-eax-and  0x23/imm32/hash
3248     0f 84/jump-if-=  $emit-imm:break/disp32
3249 $emit-imm:check-for-imm32:
3250     # if (has-metadata?(word-slice, "imm32"))
3251     # . eax = has-metadata?(ecx, "imm32")
3252     # . . push args
3253     68/push  "imm32"/imm32
3254     51/push-ecx
3255     # . . call
3256     e8/call  has-metadata?/disp32
3257     # . . discard args
3258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3259     # . if (eax == false) goto next check
3260     3d/compare-eax-and  0/imm32/false
3261     74/jump-if-=  $emit-imm:check-for-imm16/disp8
3262 $emit-imm:imm32:
3263     # emit(out, word-slice, 4)
3264     # . . push args
3265     68/push  4/imm32
3266     51/push-ecx
3267     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3268     # . . call
3269     e8/call  emit/disp32
3270     # . . discard args
3271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3272     # break
3273     e9/jump  $emit-imm:break/disp32
3274 $emit-imm:check-for-imm16:
3275     # if (has-metadata?(word-slice, "imm16"))
3276     # . eax = has-metadata?(ecx, "imm16")
3277     # . . push args
3278     68/push  "imm16"/imm32
3279     51/push-ecx
3280     # . . call
3281     e8/call  has-metadata?/disp32
3282     # . . discard args
3283     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3284     # . if (eax == false) goto next check
3285     3d/compare-eax-and  0/imm32/false
3286     74/jump-if-=  $emit-imm:check-for-imm8/disp8
3287 $emit-imm:imm16:
3288     # emit(out, word-slice, 2)
3289     # . . push args
3290     68/push  2/imm32
3291     51/push-ecx
3292     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3293     # . . call
3294     e8/call  emit/disp32
3295     # . . discard args
3296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3297     # break
3298     e9/jump  $emit-imm:break/disp32
3299 $emit-imm:check-for-imm8:
3300     # if (has-metadata?(word-slice, "imm8"))
3301     # . eax = has-metadata?(ecx, "imm8")
3302     # . . push args
3303     68/push  "imm8"/imm32
3304     51/push-ecx
3305     # . . call
3306     e8/call  has-metadata?/disp32
3307     # . . discard args
3308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3309     # . if (eax == false) loop
3310     3d/compare-eax-and  0/imm32/false
3311     0f 84/jump-if-=  $emit-imm:loop/disp32
3312 $emit-imm:imm8:
3313     # emit(out, word-slice, 1)
3314     # . . push args
3315     68/push  1/imm32
3316     51/push-ecx
3317     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3318     # . . call
3319     e8/call  emit/disp32
3320     # . . discard args
3321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3322     # break
3323 $emit-imm:break:
3324     # . restore locals
3325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3326     # . restore registers
3327     5a/pop-to-edx
3328     59/pop-to-ecx
3329     58/pop-to-eax
3330     # . epilogue
3331     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3332     5d/pop-to-ebp
3333     c3/return
3334 
3335 emit-line-in-comment:  # line: (addr stream byte), out: (addr buffered-file)
3336     # . prologue
3337     55/push-ebp
3338     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3339     # write-buffered(out, " # ")
3340     # . . push args
3341     68/push  " # "/imm32
3342     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3343     # . . call
3344     e8/call  write-buffered/disp32
3345     # . . discard args
3346     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3347     # write-stream-data(out, line)
3348     # . . push args
3349     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3350     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3351     # . . call
3352     e8/call  write-stream-data/disp32
3353     # . . discard args
3354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3355 $emit-line-in-comment:end:
3356     # . epilogue
3357     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3358     5d/pop-to-ebp
3359     c3/return
3360 
3361 test-convert-instruction-passes-comments-through:
3362     # if a line starts with '#', pass it along unchanged
3363     # . prologue
3364     55/push-ebp
3365     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3366     # setup
3367     # . clear-stream(_test-input-stream)
3368     # . . push args
3369     68/push  _test-input-stream/imm32
3370     # . . call
3371     e8/call  clear-stream/disp32
3372     # . . discard args
3373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3374     # . clear-stream(_test-output-stream)
3375     # . . push args
3376     68/push  _test-output-stream/imm32
3377     # . . call
3378     e8/call  clear-stream/disp32
3379     # . . discard args
3380     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3381     # . clear-stream($_test-output-buffered-file->buffer)
3382     # . . push args
3383     68/push  $_test-output-buffered-file->buffer/imm32
3384     # . . call
3385     e8/call  clear-stream/disp32
3386     # . . discard args
3387     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3388     # initialize input
3389     # . write(_test-input-stream, "# abcd")
3390     # . . push args
3391     68/push  "# abcd"/imm32
3392     68/push  _test-input-stream/imm32
3393     # . . call
3394     e8/call  write/disp32
3395     # . . discard args
3396     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3397     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3398     # . . push args
3399     68/push  _test-output-buffered-file/imm32
3400     68/push  _test-input-stream/imm32
3401     # . . call
3402     e8/call  convert-instruction/disp32
3403     # . . discard args
3404     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3405     # check that the line just passed through
3406     # . flush(_test-output-buffered-file)
3407     # . . push args
3408     68/push  _test-output-buffered-file/imm32
3409     # . . call
3410     e8/call  flush/disp32
3411     # . . discard args
3412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3413     # . check-stream-equal(_test-output-stream, "# abcd", msg)
3414     # . . push args
3415     68/push  "F - test-convert-instruction-passes-comments-through"/imm32
3416     68/push  "# abcd"/imm32
3417     68/push  _test-output-stream/imm32
3418     # . . call
3419     e8/call  check-stream-equal/disp32
3420     # . . discard args
3421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3422     # . epilogue
3423     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3424     5d/pop-to-ebp
3425     c3/return
3426 
3427 test-convert-instruction-passes-labels-through:
3428     # if the first word ends with ':', pass along the entire line unchanged
3429     # . prologue
3430     55/push-ebp
3431     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3432     # setup
3433     # . clear-stream(_test-input-stream)
3434     # . . push args
3435     68/push  _test-input-stream/imm32
3436     # . . call
3437     e8/call  clear-stream/disp32
3438     # . . discard args
3439     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3440     # . clear-stream(_test-output-stream)
3441     # . . push args
3442     68/push  _test-output-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-buffered-file->buffer)
3448     # . . push args
3449     68/push  $_test-output-buffered-file->buffer/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     # initialize input
3455     # . write(_test-input-stream, "ab: # cd")
3456     # . . push args
3457     68/push  "ab: # cd"/imm32
3458     68/push  _test-input-stream/imm32
3459     # . . call
3460     e8/call  write/disp32
3461     # . . discard args
3462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3463     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3464     # . . push args
3465     68/push  _test-output-buffered-file/imm32
3466     68/push  _test-input-stream/imm32
3467     # . . call
3468     e8/call  convert-instruction/disp32
3469     # . . discard args
3470     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3471     # check that the line just passed through
3472     # . flush(_test-output-buffered-file)
3473     # . . push args
3474     68/push  _test-output-buffered-file/imm32
3475     # . . call
3476     e8/call  flush/disp32
3477     # . . discard args
3478     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3479     # . check-stream-equal(_test-output-stream, "ab: # cd", msg)
3480     # . . push args
3481     68/push  "F - test-convert-instruction-passes-labels-through"/imm32
3482     68/push  "ab: # cd"/imm32
3483     68/push  _test-output-stream/imm32
3484     # . . call
3485     e8/call  check-stream-equal/disp32
3486     # . . discard args
3487     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3488     # . epilogue
3489     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3490     5d/pop-to-ebp
3491     c3/return
3492 
3493 test-convert-instruction-handles-single-opcode:
3494     # if the instruction consists of a single opcode, strip its metadata and pass it along
3495     # . prologue
3496     55/push-ebp
3497     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3498     # setup
3499     # . clear-stream(_test-input-stream)
3500     # . . push args
3501     68/push  _test-input-stream/imm32
3502     # . . call
3503     e8/call  clear-stream/disp32
3504     # . . discard args
3505     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3506     # . clear-stream(_test-output-stream)
3507     # . . push args
3508     68/push  _test-output-stream/imm32
3509     # . . call
3510     e8/call  clear-stream/disp32
3511     # . . discard args
3512     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3513     # . clear-stream($_test-output-buffered-file->buffer)
3514     # . . push args
3515     68/push  $_test-output-buffered-file->buffer/imm32
3516     # . . call
3517     e8/call  clear-stream/disp32
3518     # . . discard args
3519     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3520     # initialize input
3521     # . write(_test-input-stream, "ab/cd # comment")
3522     # . . push args
3523     68/push  "ab/cd # comment"/imm32
3524     68/push  _test-input-stream/imm32
3525     # . . call
3526     e8/call  write/disp32
3527     # . . discard args
3528     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3529     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3530     # . . push args
3531     68/push  _test-output-buffered-file/imm32
3532     68/push  _test-input-stream/imm32
3533     # . . call
3534     e8/call  convert-instruction/disp32
3535     # . . discard args
3536     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3537     # check output
3538     # . flush(_test-output-buffered-file)
3539     # . . push args
3540     68/push  _test-output-buffered-file/imm32
3541     # . . call
3542     e8/call  flush/disp32
3543     # . . discard args
3544     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3545 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3571     # . check-stream-equal(_test-output-stream, "ab  # ab/cd # comment", msg)
3572     # . . push args
3573     68/push  "F - test-convert-instruction-handles-single-opcode"/imm32
3574     68/push  "ab  # ab/cd # comment"/imm32
3575     68/push  _test-output-stream/imm32
3576     # . . call
3577     e8/call  check-stream-equal/disp32
3578     # . . discard args
3579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3580     # . epilogue
3581     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3582     5d/pop-to-ebp
3583     c3/return
3584 
3585 test-convert-instruction-handles-0f-opcode:
3586     # if the instruction starts with 0f opcode, include a second opcode
3587     # . prologue
3588     55/push-ebp
3589     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3590     # setup
3591     # . clear-stream(_test-input-stream)
3592     # . . push args
3593     68/push  _test-input-stream/imm32
3594     # . . call
3595     e8/call  clear-stream/disp32
3596     # . . discard args
3597     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3598     # . clear-stream(_test-output-stream)
3599     # . . push args
3600     68/push  _test-output-stream/imm32
3601     # . . call
3602     e8/call  clear-stream/disp32
3603     # . . discard args
3604     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3605     # . clear-stream($_test-output-buffered-file->buffer)
3606     # . . push args
3607     68/push  $_test-output-buffered-file->buffer/imm32
3608     # . . call
3609     e8/call  clear-stream/disp32
3610     # . . discard args
3611     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3612     # initialize input
3613     # . write(_test-input-stream, "0f/m1 ab/m2 # comment")
3614     # . . push args
3615     68/push  "0f/m1 ab/m2 # comment"/imm32
3616     68/push  _test-input-stream/imm32
3617     # . . call
3618     e8/call  write/disp32
3619     # . . discard args
3620     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3621     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3622     # . . push args
3623     68/push  _test-output-buffered-file/imm32
3624     68/push  _test-input-stream/imm32
3625     # . . call
3626     e8/call  convert-instruction/disp32
3627     # . . discard args
3628     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3629     # check output
3630     # . flush(_test-output-buffered-file)
3631     # . . push args
3632     68/push  _test-output-buffered-file/imm32
3633     # . . call
3634     e8/call  flush/disp32
3635     # . . discard args
3636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3637 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3663     # . check-stream-equal(_test-output-stream, "0f ab  # 0f/m1 ab/m2 # comment", msg)
3664     # . . push args
3665     68/push  "F - test-convert-instruction-handles-0f-opcode"/imm32
3666     68/push  "0f ab  # 0f/m1 ab/m2 # comment"/imm32
3667     68/push  _test-output-stream/imm32
3668     # . . call
3669     e8/call  check-stream-equal/disp32
3670     # . . discard args
3671     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3672     # . epilogue
3673     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3674     5d/pop-to-ebp
3675     c3/return
3676 
3677 test-convert-instruction-handles-f2-opcode:
3678     # if the instruction starts with f2 opcode, include a second opcode
3679     # . prologue
3680     55/push-ebp
3681     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3682     # setup
3683     # . clear-stream(_test-input-stream)
3684     # . . push args
3685     68/push  _test-input-stream/imm32
3686     # . . call
3687     e8/call  clear-stream/disp32
3688     # . . discard args
3689     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3690     # . clear-stream(_test-output-stream)
3691     # . . push args
3692     68/push  _test-output-stream/imm32
3693     # . . call
3694     e8/call  clear-stream/disp32
3695     # . . discard args
3696     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3697     # . clear-stream($_test-output-buffered-file->buffer)
3698     # . . push args
3699     68/push  $_test-output-buffered-file->buffer/imm32
3700     # . . call
3701     e8/call  clear-stream/disp32
3702     # . . discard args
3703     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3704     # initialize input
3705     # . write(_test-input-stream, "f2/m1 ab/m2 # comment")
3706     # . . push args
3707     68/push  "f2/m1 ab/m2 # comment"/imm32
3708     68/push  _test-input-stream/imm32
3709     # . . call
3710     e8/call  write/disp32
3711     # . . discard args
3712     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3713     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3714     # . . push args
3715     68/push  _test-output-buffered-file/imm32
3716     68/push  _test-input-stream/imm32
3717     # . . call
3718     e8/call  convert-instruction/disp32
3719     # . . discard args
3720     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3721     # check output
3722     # . flush(_test-output-buffered-file)
3723     # . . push args
3724     68/push  _test-output-buffered-file/imm32
3725     # . . call
3726     e8/call  flush/disp32
3727     # . . discard args
3728     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3729 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3755     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 # comment", msg)
3756     # . . push args
3757     68/push  "F - test-convert-instruction-handles-f2-opcode"/imm32
3758     68/push  "f2 ab  # f2/m1 ab/m2 # comment"/imm32
3759     68/push  _test-output-stream/imm32
3760     # . . call
3761     e8/call  check-stream-equal/disp32
3762     # . . discard args
3763     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3764     # . epilogue
3765     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3766     5d/pop-to-ebp
3767     c3/return
3768 
3769 test-convert-instruction-handles-f3-opcode:
3770     # if the instruction starts with f3 opcode, include a second opcode
3771     # . prologue
3772     55/push-ebp
3773     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3774     # setup
3775     # . clear-stream(_test-input-stream)
3776     # . . push args
3777     68/push  _test-input-stream/imm32
3778     # . . call
3779     e8/call  clear-stream/disp32
3780     # . . discard args
3781     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3782     # . clear-stream(_test-output-stream)
3783     # . . push args
3784     68/push  _test-output-stream/imm32
3785     # . . call
3786     e8/call  clear-stream/disp32
3787     # . . discard args
3788     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3789     # . clear-stream($_test-output-buffered-file->buffer)
3790     # . . push args
3791     68/push  $_test-output-buffered-file->buffer/imm32
3792     # . . call
3793     e8/call  clear-stream/disp32
3794     # . . discard args
3795     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3796     # initialize input
3797     # . write(_test-input-stream, "f3/m1 ab/m2 # comment")
3798     # . . push args
3799     68/push  "f3/m1 ab/m2 # comment"/imm32
3800     68/push  _test-input-stream/imm32
3801     # . . call
3802     e8/call  write/disp32
3803     # . . discard args
3804     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3805     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3806     # . . push args
3807     68/push  _test-output-buffered-file/imm32
3808     68/push  _test-input-stream/imm32
3809     # . . call
3810     e8/call  convert-instruction/disp32
3811     # . . discard args
3812     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3813     # check output
3814     # . flush(_test-output-buffered-file)
3815     # . . push args
3816     68/push  _test-output-buffered-file/imm32
3817     # . . call
3818     e8/call  flush/disp32
3819     # . . discard args
3820     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3821 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3847     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 # comment", msg)
3848     # . . push args
3849     68/push  "F - test-convert-instruction-handles-f3-opcode"/imm32
3850     68/push  "f3 ab  # f3/m1 ab/m2 # comment"/imm32
3851     68/push  _test-output-stream/imm32
3852     # . . call
3853     e8/call  check-stream-equal/disp32
3854     # . . discard args
3855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3856     # . epilogue
3857     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3858     5d/pop-to-ebp
3859     c3/return
3860 
3861 test-convert-instruction-handles-f2-0f-opcode:
3862     # if the instruction starts with f2 0f opcode, include a second opcode
3863     # . prologue
3864     55/push-ebp
3865     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3866     # setup
3867     # . clear-stream(_test-input-stream)
3868     # . . push args
3869     68/push  _test-input-stream/imm32
3870     # . . call
3871     e8/call  clear-stream/disp32
3872     # . . discard args
3873     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3874     # . clear-stream(_test-output-stream)
3875     # . . push args
3876     68/push  _test-output-stream/imm32
3877     # . . call
3878     e8/call  clear-stream/disp32
3879     # . . discard args
3880     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3881     # . clear-stream($_test-output-buffered-file->buffer)
3882     # . . push args
3883     68/push  $_test-output-buffered-file->buffer/imm32
3884     # . . call
3885     e8/call  clear-stream/disp32
3886     # . . discard args
3887     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3888     # initialize input
3889     # . write(_test-input-stream, "f2/m1 0f/m2 ab/m3 # comment")
3890     # . . push args
3891     68/push  "f2/m1 0f/m2 ab/m3 # comment"/imm32
3892     68/push  _test-input-stream/imm32
3893     # . . call
3894     e8/call  write/disp32
3895     # . . discard args
3896     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3897     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3898     # . . push args
3899     68/push  _test-output-buffered-file/imm32
3900     68/push  _test-input-stream/imm32
3901     # . . call
3902     e8/call  convert-instruction/disp32
3903     # . . discard args
3904     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3905     # check output
3906     # . flush(_test-output-buffered-file)
3907     # . . push args
3908     68/push  _test-output-buffered-file/imm32
3909     # . . call
3910     e8/call  flush/disp32
3911     # . . discard args
3912     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3913 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3939     # . check-stream-equal(_test-output-stream, "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment", msg)
3940     # . . push args
3941     68/push  "F - test-convert-instruction-handles-f2-0f-opcode"/imm32
3942     68/push  "f2 0f ab  # f2/m1 0f/m2 ab/m3 # comment"/imm32
3943     68/push  _test-output-stream/imm32
3944     # . . call
3945     e8/call  check-stream-equal/disp32
3946     # . . discard args
3947     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3948     # . epilogue
3949     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3950     5d/pop-to-ebp
3951     c3/return
3952 
3953 test-convert-instruction-handles-f3-0f-opcode:
3954     # if the instruction starts with f3 0f opcode, include a second opcode
3955     # . prologue
3956     55/push-ebp
3957     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3958     # setup
3959     # . clear-stream(_test-input-stream)
3960     # . . push args
3961     68/push  _test-input-stream/imm32
3962     # . . call
3963     e8/call  clear-stream/disp32
3964     # . . discard args
3965     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3966     # . clear-stream(_test-output-stream)
3967     # . . push args
3968     68/push  _test-output-stream/imm32
3969     # . . call
3970     e8/call  clear-stream/disp32
3971     # . . discard args
3972     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3973     # . clear-stream($_test-output-buffered-file->buffer)
3974     # . . push args
3975     68/push  $_test-output-buffered-file->buffer/imm32
3976     # . . call
3977     e8/call  clear-stream/disp32
3978     # . . discard args
3979     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3980     # initialize input
3981     # . write(_test-input-stream, "f3/m1 0f/m2 ab/m3 # comment")
3982     # . . push args
3983     68/push  "f3/m1 0f/m2 ab/m3 # comment"/imm32
3984     68/push  _test-input-stream/imm32
3985     # . . call
3986     e8/call  write/disp32
3987     # . . discard args
3988     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3989     # convert-instruction(_test-input-stream, _test-output-buffered-file)
3990     # . . push args
3991     68/push  _test-output-buffered-file/imm32
3992     68/push  _test-input-stream/imm32
3993     # . . call
3994     e8/call  convert-instruction/disp32
3995     # . . discard args
3996     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3997     # check output
3998     # . flush(_test-output-buffered-file)
3999     # . . push args
4000     68/push  _test-output-buffered-file/imm32
4001     # . . call
4002     e8/call  flush/disp32
4003     # . . discard args
4004     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4005 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4031     # . check-stream-equal(_test-output-stream, "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4032     # . . push args
4033     68/push  "F - test-convert-instruction-handles-f3-0f-opcode"/imm32
4034     68/push  "f3 0f ab  # f3/m1 0f/m2 ab/m3 # comment"/imm32
4035     68/push  _test-output-stream/imm32
4036     # . . call
4037     e8/call  check-stream-equal/disp32
4038     # . . discard args
4039     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4040     # . epilogue
4041     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4042     5d/pop-to-ebp
4043     c3/return
4044 
4045 test-convert-instruction-handles-unused-opcodes:
4046     # if the instruction doesn't start with f2, f3 or 0f, don't include other opcodes
4047     # . prologue
4048     55/push-ebp
4049     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4050     # setup
4051     # . clear-stream(_test-input-stream)
4052     # . . push args
4053     68/push  _test-input-stream/imm32
4054     # . . call
4055     e8/call  clear-stream/disp32
4056     # . . discard args
4057     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4058     # . clear-stream(_test-output-stream)
4059     # . . push args
4060     68/push  _test-output-stream/imm32
4061     # . . call
4062     e8/call  clear-stream/disp32
4063     # . . discard args
4064     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4065     # . clear-stream($_test-output-buffered-file->buffer)
4066     # . . push args
4067     68/push  $_test-output-buffered-file->buffer/imm32
4068     # . . call
4069     e8/call  clear-stream/disp32
4070     # . . discard args
4071     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4072     # initialize input
4073     # . write(_test-input-stream, "ab/m1 cd/m2 # comment")
4074     # . . push args
4075     68/push  "ab/m1 cd/m2 # comment"/imm32
4076     68/push  _test-input-stream/imm32
4077     # . . call
4078     e8/call  write/disp32
4079     # . . discard args
4080     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4081     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4082     # . . push args
4083     68/push  _test-output-buffered-file/imm32
4084     68/push  _test-input-stream/imm32
4085     # . . call
4086     e8/call  convert-instruction/disp32
4087     # . . discard args
4088     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4089     # check output
4090     # . flush(_test-output-buffered-file)
4091     # . . push args
4092     68/push  _test-output-buffered-file/imm32
4093     # . . call
4094     e8/call  flush/disp32
4095     # . . discard args
4096     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4097 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4123     # . check-stream-equal(_test-output-stream, "ab  # f3/m1 0f/m2 ab/m3 # comment", msg)
4124     # . . push args
4125     68/push  "F - test-convert-instruction-handles-unused-opcodes"/imm32
4126     68/push  "ab  # ab/m1 cd/m2 # comment"/imm32
4127     68/push  _test-output-stream/imm32
4128     # . . call
4129     e8/call  check-stream-equal/disp32
4130     # . . discard args
4131     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4132     # . epilogue
4133     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4134     5d/pop-to-ebp
4135     c3/return
4136 
4137 test-convert-instruction-handles-unused-second-opcodes:
4138     # if the second opcode isn't 0f, don't include further opcodes
4139     # . prologue
4140     55/push-ebp
4141     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4142     # setup
4143     # . clear-stream(_test-input-stream)
4144     # . . push args
4145     68/push  _test-input-stream/imm32
4146     # . . call
4147     e8/call  clear-stream/disp32
4148     # . . discard args
4149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4150     # . clear-stream(_test-output-stream)
4151     # . . push args
4152     68/push  _test-output-stream/imm32
4153     # . . call
4154     e8/call  clear-stream/disp32
4155     # . . discard args
4156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4157     # . clear-stream($_test-output-buffered-file->buffer)
4158     # . . push args
4159     68/push  $_test-output-buffered-file->buffer/imm32
4160     # . . call
4161     e8/call  clear-stream/disp32
4162     # . . discard args
4163     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4164     # initialize input
4165     # . write(_test-input-stream, "f2/m1 ab/m2 cd/m3 # comment")
4166     # . . push args
4167     68/push  "f2/m1 ab/m2 cd/m3 # comment"/imm32
4168     68/push  _test-input-stream/imm32
4169     # . . call
4170     e8/call  write/disp32
4171     # . . discard args
4172     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4173     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4174     # . . push args
4175     68/push  _test-output-buffered-file/imm32
4176     68/push  _test-input-stream/imm32
4177     # . . call
4178     e8/call  convert-instruction/disp32
4179     # . . discard args
4180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4181     # check output
4182     # . flush(_test-output-buffered-file)
4183     # . . push args
4184     68/push  _test-output-buffered-file/imm32
4185     # . . call
4186     e8/call  flush/disp32
4187     # . . discard args
4188     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4189 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4215     # . check-stream-equal(_test-output-stream, "f2 ab  # f2/m1 ab/m2 cd/m3 # comment", msg)
4216     # . . push args
4217     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4218     68/push  "f2 ab  # f2/m1 ab/m2 cd/m3 # comment"/imm32
4219     68/push  _test-output-stream/imm32
4220     # . . call
4221     e8/call  check-stream-equal/disp32
4222     # . . discard args
4223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4224     # . epilogue
4225     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4226     5d/pop-to-ebp
4227     c3/return
4228 
4229 test-convert-instruction-handles-unused-second-opcodes-2:
4230     # if the second opcode isn't 0f, don't include further opcodes
4231     # . prologue
4232     55/push-ebp
4233     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4234     # setup
4235     # . clear-stream(_test-input-stream)
4236     # . . push args
4237     68/push  _test-input-stream/imm32
4238     # . . call
4239     e8/call  clear-stream/disp32
4240     # . . discard args
4241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4242     # . clear-stream(_test-output-stream)
4243     # . . push args
4244     68/push  _test-output-stream/imm32
4245     # . . call
4246     e8/call  clear-stream/disp32
4247     # . . discard args
4248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4249     # . clear-stream($_test-output-buffered-file->buffer)
4250     # . . push args
4251     68/push  $_test-output-buffered-file->buffer/imm32
4252     # . . call
4253     e8/call  clear-stream/disp32
4254     # . . discard args
4255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4256     # initialize input
4257     # . write(_test-input-stream, "f3/m1 ab/m2 cd/m3 # comment")
4258     # . . push args
4259     68/push  "f3/m1 ab/m2 cd/m3 # comment"/imm32
4260     68/push  _test-input-stream/imm32
4261     # . . call
4262     e8/call  write/disp32
4263     # . . discard args
4264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4265     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4266     # . . push args
4267     68/push  _test-output-buffered-file/imm32
4268     68/push  _test-input-stream/imm32
4269     # . . call
4270     e8/call  convert-instruction/disp32
4271     # . . discard args
4272     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4273     # check output
4274     # . flush(_test-output-buffered-file)
4275     # . . push args
4276     68/push  _test-output-buffered-file/imm32
4277     # . . call
4278     e8/call  flush/disp32
4279     # . . discard args
4280     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4281 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4307     # . check-stream-equal(_test-output-stream, "f3 ab  # f3/m1 ab/m2 cd/m3 # comment", msg)
4308     # . . push args
4309     68/push  "F - test-convert-instruction-handles-unused-second-opcodes"/imm32
4310     68/push  "f3 ab  # f3/m1 ab/m2 cd/m3 # comment"/imm32
4311     68/push  _test-output-stream/imm32
4312     # . . call
4313     e8/call  check-stream-equal/disp32
4314     # . . discard args
4315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4316     # . epilogue
4317     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4318     5d/pop-to-ebp
4319     c3/return
4320 
4321 test-convert-instruction-emits-modrm-byte:
4322     # pack mod, rm32 and r32 operands into ModR/M byte
4323     # . prologue
4324     55/push-ebp
4325     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4326     # setup
4327     # . clear-stream(_test-input-stream)
4328     # . . push args
4329     68/push  _test-input-stream/imm32
4330     # . . call
4331     e8/call  clear-stream/disp32
4332     # . . discard args
4333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4334     # . clear-stream(_test-output-stream)
4335     # . . push args
4336     68/push  _test-output-stream/imm32
4337     # . . call
4338     e8/call  clear-stream/disp32
4339     # . . discard args
4340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4341     # . clear-stream($_test-output-buffered-file->buffer)
4342     # . . push args
4343     68/push  $_test-output-buffered-file->buffer/imm32
4344     # . . call
4345     e8/call  clear-stream/disp32
4346     # . . discard args
4347     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4348     # initialize input
4349     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32 1/r32")
4350     # . . push args
4351     68/push  "8b/copy 0/mod 0/rm32 1/r32"/imm32
4352     68/push  _test-input-stream/imm32
4353     # . . call
4354     e8/call  write/disp32
4355     # . . discard args
4356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4357     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4358     # . . push args
4359     68/push  _test-output-buffered-file/imm32
4360     68/push  _test-input-stream/imm32
4361     # . . call
4362     e8/call  convert-instruction/disp32
4363     # . . discard args
4364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4365     # check output
4366     # . flush(_test-output-buffered-file)
4367     # . . push args
4368     68/push  _test-output-buffered-file/imm32
4369     # . . call
4370     e8/call  flush/disp32
4371     # . . discard args
4372     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4373 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4399     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 0/rm32 1/r32", msg)
4400     # . . push args
4401     68/push  "F - test-convert-instruction-emits-modrm-byte"/imm32
4402     68/push  "8b 08  # 8b/copy 0/mod 0/rm32 1/r32"/imm32
4403     68/push  _test-output-stream/imm32
4404     # . . call
4405     e8/call  check-stream-equal/disp32
4406     # . . discard args
4407     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4408     # . epilogue
4409     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4410     5d/pop-to-ebp
4411     c3/return
4412 
4413 test-convert-instruction-emits-modrm-byte-with-non-zero-mod:
4414     # . prologue
4415     55/push-ebp
4416     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4417     # setup
4418     # . clear-stream(_test-input-stream)
4419     # . . push args
4420     68/push  _test-input-stream/imm32
4421     # . . call
4422     e8/call  clear-stream/disp32
4423     # . . discard args
4424     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4425     # . clear-stream(_test-output-stream)
4426     # . . push args
4427     68/push  _test-output-stream/imm32
4428     # . . call
4429     e8/call  clear-stream/disp32
4430     # . . discard args
4431     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4432     # . clear-stream($_test-output-buffered-file->buffer)
4433     # . . push args
4434     68/push  $_test-output-buffered-file->buffer/imm32
4435     # . . call
4436     e8/call  clear-stream/disp32
4437     # . . discard args
4438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4439     # initialize input
4440     # . write(_test-input-stream, "01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx")
4441     # . . push args
4442     68/push  "01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx"/imm32
4443     68/push  _test-input-stream/imm32
4444     # . . call
4445     e8/call  write/disp32
4446     # . . discard args
4447     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4448     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4449     # . . push args
4450     68/push  _test-output-buffered-file/imm32
4451     68/push  _test-input-stream/imm32
4452     # . . call
4453     e8/call  convert-instruction/disp32
4454     # . . discard args
4455     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4456     # . flush(_test-output-buffered-file)
4457     # . . push args
4458     68/push  _test-output-buffered-file/imm32
4459     # . . call
4460     e8/call  flush/disp32
4461     # . . discard args
4462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4463 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4489     # check output
4490     # . check-stream-equal(_test-output-stream, "# abcd", msg)
4491     # . . push args
4492     68/push  "F - test-convert-instruction-foo"/imm32
4493     68/push  "01 cb  # 01/add 3/mod/direct 3/rm32/ebx 1/r32/ecx"/imm32
4494     68/push  _test-output-stream/imm32
4495     # . . call
4496     e8/call  check-stream-equal/disp32
4497     # . . discard args
4498     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4499     # . epilogue
4500     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4501     5d/pop-to-ebp
4502     c3/return
4503 
4504 test-convert-instruction-emits-modrm-byte-from-subop:
4505     # pack mod, rm32 and subop operands into ModR/M byte
4506     # . prologue
4507     55/push-ebp
4508     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4509     # setup
4510     # . clear-stream(_test-input-stream)
4511     # . . push args
4512     68/push  _test-input-stream/imm32
4513     # . . call
4514     e8/call  clear-stream/disp32
4515     # . . discard args
4516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4517     # . clear-stream(_test-output-stream)
4518     # . . push args
4519     68/push  _test-output-stream/imm32
4520     # . . call
4521     e8/call  clear-stream/disp32
4522     # . . discard args
4523     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4524     # . clear-stream($_test-output-buffered-file->buffer)
4525     # . . push args
4526     68/push  $_test-output-buffered-file->buffer/imm32
4527     # . . call
4528     e8/call  clear-stream/disp32
4529     # . . discard args
4530     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4531     # initialize input
4532     # . write(_test-input-stream, "ff 6/subop/push 0/mod 0/rm32")
4533     # . . push args
4534     68/push  "ff 6/subop/push 0/mod 0/rm32"/imm32
4535     68/push  _test-input-stream/imm32
4536     # . . call
4537     e8/call  write/disp32
4538     # . . discard args
4539     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4540     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4541     # . . push args
4542     68/push  _test-output-buffered-file/imm32
4543     68/push  _test-input-stream/imm32
4544     # . . call
4545     e8/call  convert-instruction/disp32
4546     # . . discard args
4547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4548     # check output
4549     # . flush(_test-output-buffered-file)
4550     # . . push args
4551     68/push  _test-output-buffered-file/imm32
4552     # . . call
4553     e8/call  flush/disp32
4554     # . . discard args
4555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4556 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4582     # . check-stream-equal(_test-output-stream, "ff 30  # ff 6/subop/push 0/mod 0/rm32", msg)
4583     # . . push args
4584     68/push  "F - test-convert-instruction-emits-modrm-byte-from-subop"/imm32
4585     68/push  "ff 30  # ff 6/subop/push 0/mod 0/rm32"/imm32
4586     68/push  _test-output-stream/imm32
4587     # . . call
4588     e8/call  check-stream-equal/disp32
4589     # . . discard args
4590     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4591     # . epilogue
4592     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4593     5d/pop-to-ebp
4594     c3/return
4595 
4596 test-convert-instruction-emits-modrm-byte-with-missing-mod:
4597     # pack rm32 and r32 operands into ModR/M byte
4598     # . prologue
4599     55/push-ebp
4600     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4601     # setup
4602     # . clear-stream(_test-input-stream)
4603     # . . push args
4604     68/push  _test-input-stream/imm32
4605     # . . call
4606     e8/call  clear-stream/disp32
4607     # . . discard args
4608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4609     # . clear-stream(_test-output-stream)
4610     # . . push args
4611     68/push  _test-output-stream/imm32
4612     # . . call
4613     e8/call  clear-stream/disp32
4614     # . . discard args
4615     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4616     # . clear-stream($_test-output-buffered-file->buffer)
4617     # . . push args
4618     68/push  $_test-output-buffered-file->buffer/imm32
4619     # . . call
4620     e8/call  clear-stream/disp32
4621     # . . discard args
4622     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4623     # initialize input
4624     # . write(_test-input-stream, "8b/copy 0/rm32 1/r32")
4625     # . . push args
4626     68/push  "8b/copy 0/rm32 1/r32"/imm32
4627     68/push  _test-input-stream/imm32
4628     # . . call
4629     e8/call  write/disp32
4630     # . . discard args
4631     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4632     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4633     # . . push args
4634     68/push  _test-output-buffered-file/imm32
4635     68/push  _test-input-stream/imm32
4636     # . . call
4637     e8/call  convert-instruction/disp32
4638     # . . discard args
4639     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4640     # check output
4641     # . flush(_test-output-buffered-file)
4642     # . . push args
4643     68/push  _test-output-buffered-file/imm32
4644     # . . call
4645     e8/call  flush/disp32
4646     # . . discard args
4647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4648 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4674     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/rm32 1/r32", msg)
4675     # . . push args
4676     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-mod"/imm32
4677     68/push  "8b 08  # 8b/copy 0/rm32 1/r32"/imm32
4678     68/push  _test-output-stream/imm32
4679     # . . call
4680     e8/call  check-stream-equal/disp32
4681     # . . discard args
4682     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4683     # . epilogue
4684     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4685     5d/pop-to-ebp
4686     c3/return
4687 
4688 test-convert-instruction-emits-modrm-byte-with-missing-rm32:
4689     # pack mod and r32 operands into ModR/M byte
4690     # . prologue
4691     55/push-ebp
4692     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4693     # setup
4694     # . clear-stream(_test-input-stream)
4695     # . . push args
4696     68/push  _test-input-stream/imm32
4697     # . . call
4698     e8/call  clear-stream/disp32
4699     # . . discard args
4700     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4701     # . clear-stream(_test-output-stream)
4702     # . . push args
4703     68/push  _test-output-stream/imm32
4704     # . . call
4705     e8/call  clear-stream/disp32
4706     # . . discard args
4707     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4708     # . clear-stream($_test-output-buffered-file->buffer)
4709     # . . push args
4710     68/push  $_test-output-buffered-file->buffer/imm32
4711     # . . call
4712     e8/call  clear-stream/disp32
4713     # . . discard args
4714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4715     # initialize input
4716     # . write(_test-input-stream, "8b/copy 0/mod 1/r32")
4717     # . . push args
4718     68/push  "8b/copy 0/mod 1/r32"/imm32
4719     68/push  _test-input-stream/imm32
4720     # . . call
4721     e8/call  write/disp32
4722     # . . discard args
4723     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4724     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4725     # . . push args
4726     68/push  _test-output-buffered-file/imm32
4727     68/push  _test-input-stream/imm32
4728     # . . call
4729     e8/call  convert-instruction/disp32
4730     # . . discard args
4731     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4732     # check output
4733     # . flush(_test-output-buffered-file)
4734     # . . push args
4735     68/push  _test-output-buffered-file/imm32
4736     # . . call
4737     e8/call  flush/disp32
4738     # . . discard args
4739     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4740 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4766     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 1/r32", msg)
4767     # . . push args
4768     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-rm32"/imm32
4769     68/push  "8b 08  # 8b/copy 0/mod 1/r32"/imm32
4770     68/push  _test-output-stream/imm32
4771     # . . call
4772     e8/call  check-stream-equal/disp32
4773     # . . discard args
4774     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4775     # . epilogue
4776     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4777     5d/pop-to-ebp
4778     c3/return
4779 
4780 test-convert-instruction-emits-modrm-byte-with-missing-r32:
4781     # pack mod and rm32 operands into ModR/M byte
4782     # . prologue
4783     55/push-ebp
4784     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4785     # setup
4786     # . clear-stream(_test-input-stream)
4787     # . . push args
4788     68/push  _test-input-stream/imm32
4789     # . . call
4790     e8/call  clear-stream/disp32
4791     # . . discard args
4792     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4793     # . clear-stream(_test-output-stream)
4794     # . . push args
4795     68/push  _test-output-stream/imm32
4796     # . . call
4797     e8/call  clear-stream/disp32
4798     # . . discard args
4799     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4800     # . clear-stream($_test-output-buffered-file->buffer)
4801     # . . push args
4802     68/push  $_test-output-buffered-file->buffer/imm32
4803     # . . call
4804     e8/call  clear-stream/disp32
4805     # . . discard args
4806     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4807     # initialize input
4808     # . write(_test-input-stream, "8b/copy 0/mod 0/rm32")
4809     # . . push args
4810     68/push  "8b/copy 0/mod 0/rm32"/imm32
4811     68/push  _test-input-stream/imm32
4812     # . . call
4813     e8/call  write/disp32
4814     # . . discard args
4815     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4816     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4817     # . . push args
4818     68/push  _test-output-buffered-file/imm32
4819     68/push  _test-input-stream/imm32
4820     # . . call
4821     e8/call  convert-instruction/disp32
4822     # . . discard args
4823     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4824     # check output
4825     # . flush(_test-output-buffered-file)
4826     # . . push args
4827     68/push  _test-output-buffered-file/imm32
4828     # . . call
4829     e8/call  flush/disp32
4830     # . . discard args
4831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4832 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4858     # . check-stream-equal(_test-output-stream, "8b 00  # 8b/copy 0/mod 0/rm32", msg)
4859     # . . push args
4860     68/push  "F - test-convert-instruction-emits-modrm-byte-with-missing-r32"/imm32
4861     68/push  "8b 00  # 8b/copy 0/mod 0/rm32"/imm32
4862     68/push  _test-output-stream/imm32
4863     # . . call
4864     e8/call  check-stream-equal/disp32
4865     # . . discard args
4866     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4867     # . epilogue
4868     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4869     5d/pop-to-ebp
4870     c3/return
4871 
4872 test-convert-instruction-emits-sib-byte:
4873     # pack base, index and scale operands into SIB byte
4874     # . prologue
4875     55/push-ebp
4876     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4877     # setup
4878     # . clear-stream(_test-input-stream)
4879     # . . push args
4880     68/push  _test-input-stream/imm32
4881     # . . call
4882     e8/call  clear-stream/disp32
4883     # . . discard args
4884     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4885     # . clear-stream(_test-output-stream)
4886     # . . push args
4887     68/push  _test-output-stream/imm32
4888     # . . call
4889     e8/call  clear-stream/disp32
4890     # . . discard args
4891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4892     # . clear-stream($_test-output-buffered-file->buffer)
4893     # . . push args
4894     68/push  $_test-output-buffered-file->buffer/imm32
4895     # . . call
4896     e8/call  clear-stream/disp32
4897     # . . discard args
4898     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4899     # initialize input
4900     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale")
4901     # . . push args
4902     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
4903     68/push  _test-input-stream/imm32
4904     # . . call
4905     e8/call  write/disp32
4906     # . . discard args
4907     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4908     # convert-instruction(_test-input-stream, _test-output-buffered-file)
4909     # . . push args
4910     68/push  _test-output-buffered-file/imm32
4911     68/push  _test-input-stream/imm32
4912     # . . call
4913     e8/call  convert-instruction/disp32
4914     # . . discard args
4915     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4916     # check output
4917     # . flush(_test-output-buffered-file)
4918     # . . push args
4919     68/push  _test-output-buffered-file/imm32
4920     # . . call
4921     e8/call  flush/disp32
4922     # . . discard args
4923     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4924 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4950     # . check-stream-equal(_test-output-stream, "8b 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale", msg)
4951     # . . push args
4952     68/push  "F - test-convert-instruction-emits-sib-byte"/imm32
4953     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index 0/scale"/imm32
4954     68/push  _test-output-stream/imm32
4955     # . . call
4956     e8/call  check-stream-equal/disp32
4957     # . . discard args
4958     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4959     # . epilogue
4960     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4961     5d/pop-to-ebp
4962     c3/return
4963 
4964 test-convert-instruction-emits-scale:
4965     # pack base, index and scale operands into SIB byte
4966     # . prologue
4967     55/push-ebp
4968     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4969     # setup
4970     # . clear-stream(_test-input-stream)
4971     # . . push args
4972     68/push  _test-input-stream/imm32
4973     # . . call
4974     e8/call  clear-stream/disp32
4975     # . . discard args
4976     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4977     # . clear-stream(_test-output-stream)
4978     # . . push args
4979     68/push  _test-output-stream/imm32
4980     # . . call
4981     e8/call  clear-stream/disp32
4982     # . . discard args
4983     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4984     # . clear-stream($_test-output-buffered-file->buffer)
4985     # . . push args
4986     68/push  $_test-output-buffered-file->buffer/imm32
4987     # . . call
4988     e8/call  clear-stream/disp32
4989     # . . discard args
4990     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4991     # initialize input
4992     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/scale")
4993     # . . push args
4994     68/push  "8b/copy 0/mod 4/rm32 1/scale"/imm32
4995     68/push  _test-input-stream/imm32
4996     # . . call
4997     e8/call  write/disp32
4998     # . . discard args
4999     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5000     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5001     # . . push args
5002     68/push  _test-output-buffered-file/imm32
5003     68/push  _test-input-stream/imm32
5004     # . . call
5005     e8/call  convert-instruction/disp32
5006     # . . discard args
5007     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5008     # check output
5009     # . flush(_test-output-buffered-file)
5010     # . . push args
5011     68/push  _test-output-buffered-file/imm32
5012     # . . call
5013     e8/call  flush/disp32
5014     # . . discard args
5015     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5016 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5042     # . check-stream-equal(_test-output-stream, "8b 04 40  # 8b/copy 0/mod 4/rm32 1/scale", msg)
5043     # . . push args
5044     68/push  "F - test-convert-instruction-emits-scale"/imm32
5045     68/push  "8b 04 40  # 8b/copy 0/mod 4/rm32 1/scale"/imm32
5046     68/push  _test-output-stream/imm32
5047     # . . call
5048     e8/call  check-stream-equal/disp32
5049     # . . discard args
5050     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5051     # . epilogue
5052     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5053     5d/pop-to-ebp
5054     c3/return
5055 
5056 test-convert-instruction-emits-sib-byte-with-missing-base:
5057     # pack index and scale operands into SIB byte
5058     # . prologue
5059     55/push-ebp
5060     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5061     # setup
5062     # . clear-stream(_test-input-stream)
5063     # . . push args
5064     68/push  _test-input-stream/imm32
5065     # . . call
5066     e8/call  clear-stream/disp32
5067     # . . discard args
5068     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5069     # . clear-stream(_test-output-stream)
5070     # . . push args
5071     68/push  _test-output-stream/imm32
5072     # . . call
5073     e8/call  clear-stream/disp32
5074     # . . discard args
5075     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5076     # . clear-stream($_test-output-buffered-file->buffer)
5077     # . . push args
5078     68/push  $_test-output-buffered-file->buffer/imm32
5079     # . . call
5080     e8/call  clear-stream/disp32
5081     # . . discard args
5082     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5083     # initialize input
5084     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale")
5085     # . . push args
5086     68/push  "8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5087     68/push  _test-input-stream/imm32
5088     # . . call
5089     e8/call  write/disp32
5090     # . . discard args
5091     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5092     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5093     # . . push args
5094     68/push  _test-output-buffered-file/imm32
5095     68/push  _test-input-stream/imm32
5096     # . . call
5097     e8/call  convert-instruction/disp32
5098     # . . discard args
5099     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5100     # check output
5101     # . flush(_test-output-buffered-file)
5102     # . . push args
5103     68/push  _test-output-buffered-file/imm32
5104     # . . call
5105     e8/call  flush/disp32
5106     # . . discard args
5107     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5108 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5134     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale", msg)
5135     # . . push args
5136     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-base"/imm32
5137     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 1/index 0/scale"/imm32
5138     68/push  _test-output-stream/imm32
5139     # . . call
5140     e8/call  check-stream-equal/disp32
5141     # . . discard args
5142     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5143     # . epilogue
5144     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5145     5d/pop-to-ebp
5146     c3/return
5147 
5148 test-convert-instruction-emits-sib-byte-with-missing-index:
5149     # pack base and scale operands into SIB byte
5150     # . prologue
5151     55/push-ebp
5152     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5153     # setup
5154     # . clear-stream(_test-input-stream)
5155     # . . push args
5156     68/push  _test-input-stream/imm32
5157     # . . call
5158     e8/call  clear-stream/disp32
5159     # . . discard args
5160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5161     # . clear-stream(_test-output-stream)
5162     # . . push args
5163     68/push  _test-output-stream/imm32
5164     # . . call
5165     e8/call  clear-stream/disp32
5166     # . . discard args
5167     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5168     # . clear-stream($_test-output-buffered-file->buffer)
5169     # . . push args
5170     68/push  $_test-output-buffered-file->buffer/imm32
5171     # . . call
5172     e8/call  clear-stream/disp32
5173     # . . discard args
5174     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5175     # initialize input
5176     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale")
5177     # . . push args
5178     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5179     68/push  _test-input-stream/imm32
5180     # . . call
5181     e8/call  write/disp32
5182     # . . discard args
5183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5184     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5185     # . . push args
5186     68/push  _test-output-buffered-file/imm32
5187     68/push  _test-input-stream/imm32
5188     # . . call
5189     e8/call  convert-instruction/disp32
5190     # . . discard args
5191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5192     # check output
5193     # . flush(_test-output-buffered-file)
5194     # . . push args
5195     68/push  _test-output-buffered-file/imm32
5196     # . . call
5197     e8/call  flush/disp32
5198     # . . discard args
5199     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5200 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5226     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale", msg)
5227     # . . push args
5228     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-index"/imm32
5229     68/push  "8b 0c 00  # 8b/copy 0/mod 4/rm32 1/r32 0/base 0/scale"/imm32
5230     68/push  _test-output-stream/imm32
5231     # . . call
5232     e8/call  check-stream-equal/disp32
5233     # . . discard args
5234     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5235     # . epilogue
5236     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5237     5d/pop-to-ebp
5238     c3/return
5239 
5240 test-convert-instruction-emits-sib-byte-with-missing-scale:
5241     # pack base and index operands into SIB byte
5242     # . prologue
5243     55/push-ebp
5244     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5245     # setup
5246     # . clear-stream(_test-input-stream)
5247     # . . push args
5248     68/push  _test-input-stream/imm32
5249     # . . call
5250     e8/call  clear-stream/disp32
5251     # . . discard args
5252     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5253     # . clear-stream(_test-output-stream)
5254     # . . push args
5255     68/push  _test-output-stream/imm32
5256     # . . call
5257     e8/call  clear-stream/disp32
5258     # . . discard args
5259     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5260     # . clear-stream($_test-output-buffered-file->buffer)
5261     # . . push args
5262     68/push  $_test-output-buffered-file->buffer/imm32
5263     # . . call
5264     e8/call  clear-stream/disp32
5265     # . . discard args
5266     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5267     # initialize input
5268     # . write(_test-input-stream, "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index")
5269     # . . push args
5270     68/push  "8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5271     68/push  _test-input-stream/imm32
5272     # . . call
5273     e8/call  write/disp32
5274     # . . discard args
5275     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5276     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5277     # . . push args
5278     68/push  _test-output-buffered-file/imm32
5279     68/push  _test-input-stream/imm32
5280     # . . call
5281     e8/call  convert-instruction/disp32
5282     # . . discard args
5283     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5284     # check output
5285     # . flush(_test-output-buffered-file)
5286     # . . push args
5287     68/push  _test-output-buffered-file/imm32
5288     # . . call
5289     e8/call  flush/disp32
5290     # . . discard args
5291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5292 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5318     # . check-stream-equal(_test-output-stream, "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index", msg)
5319     # . . push args
5320     68/push  "F - test-convert-instruction-emits-sib-byte-with-missing-scale"/imm32
5321     68/push  "8b 0c 08  # 8b/copy 0/mod 4/rm32 1/r32 0/base 1/index"/imm32
5322     68/push  _test-output-stream/imm32
5323     # . . call
5324     e8/call  check-stream-equal/disp32
5325     # . . discard args
5326     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5327     # . epilogue
5328     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5329     5d/pop-to-ebp
5330     c3/return
5331 
5332 test-convert-instruction-handles-disp32-operand:
5333     # expand /disp32 operand into 4 bytes
5334     # . prologue
5335     55/push-ebp
5336     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5337     # setup
5338     # . clear-stream(_test-input-stream)
5339     # . . push args
5340     68/push  _test-input-stream/imm32
5341     # . . call
5342     e8/call  clear-stream/disp32
5343     # . . discard args
5344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5345     # . clear-stream(_test-output-stream)
5346     # . . push args
5347     68/push  _test-output-stream/imm32
5348     # . . call
5349     e8/call  clear-stream/disp32
5350     # . . discard args
5351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5352     # . clear-stream($_test-output-buffered-file->buffer)
5353     # . . push args
5354     68/push  $_test-output-buffered-file->buffer/imm32
5355     # . . call
5356     e8/call  clear-stream/disp32
5357     # . . discard args
5358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5359     # initialize input
5360     # . write(_test-input-stream, "e8/call 20/disp32")
5361     # . . push args
5362     68/push  "e8/call 20/disp32"/imm32
5363     68/push  _test-input-stream/imm32
5364     # . . call
5365     e8/call  write/disp32
5366     # . . discard args
5367     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5368     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5369     # . . push args
5370     68/push  _test-output-buffered-file/imm32
5371     68/push  _test-input-stream/imm32
5372     # . . call
5373     e8/call  convert-instruction/disp32
5374     # . . discard args
5375     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5376     # check output
5377     # . flush(_test-output-buffered-file)
5378     # . . push args
5379     68/push  _test-output-buffered-file/imm32
5380     # . . call
5381     e8/call  flush/disp32
5382     # . . discard args
5383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5384 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5410     # . check-stream-equal(_test-output-stream, "e8 20 00 00 00  # e8/call 20/disp32", msg)
5411     # . . push args
5412     68/push  "F - test-convert-instruction-handles-disp32-operand"/imm32
5413     68/push  "e8 20 00 00 00  # e8/call 20/disp32"/imm32
5414     68/push  _test-output-stream/imm32
5415     # . . call
5416     e8/call  check-stream-equal/disp32
5417     # . . discard args
5418     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5419     # . epilogue
5420     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5421     5d/pop-to-ebp
5422     c3/return
5423 
5424 test-convert-instruction-handles-disp16-operand:
5425     # expand /disp16 operand into 2 bytes
5426     # . prologue
5427     55/push-ebp
5428     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5429     # setup
5430     # . clear-stream(_test-input-stream)
5431     # . . push args
5432     68/push  _test-input-stream/imm32
5433     # . . call
5434     e8/call  clear-stream/disp32
5435     # . . discard args
5436     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5437     # . clear-stream(_test-output-stream)
5438     # . . push args
5439     68/push  _test-output-stream/imm32
5440     # . . call
5441     e8/call  clear-stream/disp32
5442     # . . discard args
5443     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5444     # . clear-stream($_test-output-buffered-file->buffer)
5445     # . . push args
5446     68/push  $_test-output-buffered-file->buffer/imm32
5447     # . . call
5448     e8/call  clear-stream/disp32
5449     # . . discard args
5450     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5451     # initialize input
5452     # . write(_test-input-stream, "e8/call 20/disp16")
5453     # . . push args
5454     68/push  "e8/call 20/disp16"/imm32  # not a valid instruction
5455     68/push  _test-input-stream/imm32
5456     # . . call
5457     e8/call  write/disp32
5458     # . . discard args
5459     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5460     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5461     # . . push args
5462     68/push  _test-output-buffered-file/imm32
5463     68/push  _test-input-stream/imm32
5464     # . . call
5465     e8/call  convert-instruction/disp32
5466     # . . discard args
5467     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5468     # check output
5469     # . flush(_test-output-buffered-file)
5470     # . . push args
5471     68/push  _test-output-buffered-file/imm32
5472     # . . call
5473     e8/call  flush/disp32
5474     # . . discard args
5475     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5476 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5502     # . check-stream-equal(_test-output-stream, "e8 20 00  # e8/call 20/disp16", msg)
5503     # . . push args
5504     68/push  "F - test-convert-instruction-handles-disp16-operand"/imm32
5505     68/push  "e8 20 00  # e8/call 20/disp16"/imm32
5506     68/push  _test-output-stream/imm32
5507     # . . call
5508     e8/call  check-stream-equal/disp32
5509     # . . discard args
5510     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5511     # . epilogue
5512     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5513     5d/pop-to-ebp
5514     c3/return
5515 
5516 test-convert-instruction-handles-disp8-operand:
5517     # expand /disp8 operand into 1 byte
5518     # . prologue
5519     55/push-ebp
5520     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5521     # setup
5522     # . clear-stream(_test-input-stream)
5523     # . . push args
5524     68/push  _test-input-stream/imm32
5525     # . . call
5526     e8/call  clear-stream/disp32
5527     # . . discard args
5528     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5529     # . clear-stream(_test-output-stream)
5530     # . . push args
5531     68/push  _test-output-stream/imm32
5532     # . . call
5533     e8/call  clear-stream/disp32
5534     # . . discard args
5535     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5536     # . clear-stream($_test-output-buffered-file->buffer)
5537     # . . push args
5538     68/push  $_test-output-buffered-file->buffer/imm32
5539     # . . call
5540     e8/call  clear-stream/disp32
5541     # . . discard args
5542     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5543     # initialize input
5544     # . write(_test-input-stream, "eb/jump 20/disp8")
5545     # . . push args
5546     68/push  "eb/jump 20/disp8"/imm32
5547     68/push  _test-input-stream/imm32
5548     # . . call
5549     e8/call  write/disp32
5550     # . . discard args
5551     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5552     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5553     # . . push args
5554     68/push  _test-output-buffered-file/imm32
5555     68/push  _test-input-stream/imm32
5556     # . . call
5557     e8/call  convert-instruction/disp32
5558     # . . discard args
5559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5560     # check output
5561     # . flush(_test-output-buffered-file)
5562     # . . push args
5563     68/push  _test-output-buffered-file/imm32
5564     # . . call
5565     e8/call  flush/disp32
5566     # . . discard args
5567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5568 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5594     # . check-stream-equal(_test-output-stream, "eb 20  # eb/jump 20/disp8", msg)
5595     # . . push args
5596     68/push  "F - test-convert-instruction-handles-disp8-operand"/imm32
5597     68/push  "eb 20  # eb/jump 20/disp8"/imm32
5598     68/push  _test-output-stream/imm32
5599     # . . call
5600     e8/call  check-stream-equal/disp32
5601     # . . discard args
5602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5603     # . epilogue
5604     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5605     5d/pop-to-ebp
5606     c3/return
5607 
5608 test-convert-instruction-handles-disp8-name:
5609     # pass /disp8 name directly through
5610     # . prologue
5611     55/push-ebp
5612     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5613     # setup
5614     # . clear-stream(_test-input-stream)
5615     # . . push args
5616     68/push  _test-input-stream/imm32
5617     # . . call
5618     e8/call  clear-stream/disp32
5619     # . . discard args
5620     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5621     # . clear-stream(_test-output-stream)
5622     # . . push args
5623     68/push  _test-output-stream/imm32
5624     # . . call
5625     e8/call  clear-stream/disp32
5626     # . . discard args
5627     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5628     # . clear-stream($_test-output-buffered-file->buffer)
5629     # . . push args
5630     68/push  $_test-output-buffered-file->buffer/imm32
5631     # . . call
5632     e8/call  clear-stream/disp32
5633     # . . discard args
5634     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5635     # initialize input
5636     # . write(_test-input-stream, "eb/jump xyz/disp8")
5637     # . . push args
5638     68/push  "eb/jump xyz/disp8"/imm32
5639     68/push  _test-input-stream/imm32
5640     # . . call
5641     e8/call  write/disp32
5642     # . . discard args
5643     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5644     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5645     # . . push args
5646     68/push  _test-output-buffered-file/imm32
5647     68/push  _test-input-stream/imm32
5648     # . . call
5649     e8/call  convert-instruction/disp32
5650     # . . discard args
5651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5652     # check output
5653     # . flush(_test-output-buffered-file)
5654     # . . push args
5655     68/push  _test-output-buffered-file/imm32
5656     # . . call
5657     e8/call  flush/disp32
5658     # . . discard args
5659     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5660 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5686     # . check-stream-equal(_test-output-stream, "eb xyz/disp8  # eb/jump xyz/disp8", msg)
5687     # . . push args
5688     68/push  "F - test-convert-instruction-handles-disp8-name"/imm32
5689     68/push  "eb xyz/disp8  # eb/jump xyz/disp8"/imm32
5690     68/push  _test-output-stream/imm32
5691     # . . call
5692     e8/call  check-stream-equal/disp32
5693     # . . discard args
5694     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5695     # . epilogue
5696     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5697     5d/pop-to-ebp
5698     c3/return
5699 
5700 test-convert-instruction-handles-imm32-operand:
5701     # expand /imm32 operand into 4 bytes
5702     # . prologue
5703     55/push-ebp
5704     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5705     # setup
5706     # . clear-stream(_test-input-stream)
5707     # . . push args
5708     68/push  _test-input-stream/imm32
5709     # . . call
5710     e8/call  clear-stream/disp32
5711     # . . discard args
5712     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5713     # . clear-stream(_test-output-stream)
5714     # . . push args
5715     68/push  _test-output-stream/imm32
5716     # . . call
5717     e8/call  clear-stream/disp32
5718     # . . discard args
5719     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5720     # . clear-stream($_test-output-buffered-file->buffer)
5721     # . . push args
5722     68/push  $_test-output-buffered-file->buffer/imm32
5723     # . . call
5724     e8/call  clear-stream/disp32
5725     # . . discard args
5726     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5727     # initialize input
5728     # . write(_test-input-stream, "68/push 0x20/imm32")
5729     # . . push args
5730     68/push  "68/push 0x20/imm32"/imm32
5731     68/push  _test-input-stream/imm32
5732     # . . call
5733     e8/call  write/disp32
5734     # . . discard args
5735     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5736     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5737     # . . push args
5738     68/push  _test-output-buffered-file/imm32
5739     68/push  _test-input-stream/imm32
5740     # . . call
5741     e8/call  convert-instruction/disp32
5742     # . . discard args
5743     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5744     # check output
5745     # . flush(_test-output-buffered-file)
5746     # . . push args
5747     68/push  _test-output-buffered-file/imm32
5748     # . . call
5749     e8/call  flush/disp32
5750     # . . discard args
5751     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5752 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5778     # . check-stream-equal(_test-output-stream, "68 20 00 00 00  # 68/push 0x20/imm32", msg)
5779     # . . push args
5780     68/push  "F - test-convert-instruction-handles-imm32-operand"/imm32
5781     68/push  "68 20 00 00 00  # 68/push 0x20/imm32"/imm32
5782     68/push  _test-output-stream/imm32
5783     # . . call
5784     e8/call  check-stream-equal/disp32
5785     # . . discard args
5786     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5787     # . epilogue
5788     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5789     5d/pop-to-ebp
5790     c3/return
5791 
5792 test-convert-instruction-handles-imm16-operand:
5793     # expand /imm16 operand into 2 bytes
5794     # we don't have one of these at the moment, so this expands to an invalid instruction
5795     # . prologue
5796     55/push-ebp
5797     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5798     # setup
5799     # . clear-stream(_test-input-stream)
5800     # . . push args
5801     68/push  _test-input-stream/imm32
5802     # . . call
5803     e8/call  clear-stream/disp32
5804     # . . discard args
5805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5806     # . clear-stream(_test-output-stream)
5807     # . . push args
5808     68/push  _test-output-stream/imm32
5809     # . . call
5810     e8/call  clear-stream/disp32
5811     # . . discard args
5812     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5813     # . clear-stream($_test-output-buffered-file->buffer)
5814     # . . push args
5815     68/push  $_test-output-buffered-file->buffer/imm32
5816     # . . call
5817     e8/call  clear-stream/disp32
5818     # . . discard args
5819     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5820     # initialize input
5821     # . write(_test-input-stream, "68/push 0x20/imm16")
5822     # . . push args
5823     68/push  "68/push 0x20/imm16"/imm32  # not a valid instruction
5824     68/push  _test-input-stream/imm32
5825     # . . call
5826     e8/call  write/disp32
5827     # . . discard args
5828     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5829     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5830     # . . push args
5831     68/push  _test-output-buffered-file/imm32
5832     68/push  _test-input-stream/imm32
5833     # . . call
5834     e8/call  convert-instruction/disp32
5835     # . . discard args
5836     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5837     # check output
5838     # . flush(_test-output-buffered-file)
5839     # . . push args
5840     68/push  _test-output-buffered-file/imm32
5841     # . . call
5842     e8/call  flush/disp32
5843     # . . discard args
5844     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5845 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5871     # . check-stream-equal(_test-output-stream, "68 20 00  # 68/push 0x20/imm16", msg)
5872     # . . push args
5873     68/push  "F - test-convert-instruction-handles-imm16-operand"/imm32
5874     68/push  "68 20 00  # 68/push 0x20/imm16"/imm32
5875     68/push  _test-output-stream/imm32
5876     # . . call
5877     e8/call  check-stream-equal/disp32
5878     # . . discard args
5879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5880     # . epilogue
5881     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5882     5d/pop-to-ebp
5883     c3/return
5884 
5885 test-convert-instruction-handles-imm8-operand:
5886     # expand /imm8 operand into 1 byte
5887     # we don't have one of these at the moment, so this expands to an invalid instruction
5888     # . prologue
5889     55/push-ebp
5890     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5891     # setup
5892     # . clear-stream(_test-input-stream)
5893     # . . push args
5894     68/push  _test-input-stream/imm32
5895     # . . call
5896     e8/call  clear-stream/disp32
5897     # . . discard args
5898     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5899     # . clear-stream(_test-output-stream)
5900     # . . push args
5901     68/push  _test-output-stream/imm32
5902     # . . call
5903     e8/call  clear-stream/disp32
5904     # . . discard args
5905     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5906     # . clear-stream($_test-output-buffered-file->buffer)
5907     # . . push args
5908     68/push  $_test-output-buffered-file->buffer/imm32
5909     # . . call
5910     e8/call  clear-stream/disp32
5911     # . . discard args
5912     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5913     # initialize input
5914     # . write(_test-input-stream, "68/push 0x20/imm8")
5915     # . . push args
5916     68/push  "68/push 0x20/imm8"/imm32
5917     68/push  _test-input-stream/imm32
5918     # . . call
5919     e8/call  write/disp32
5920     # . . discard args
5921     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5922     # convert-instruction(_test-input-stream, _test-output-buffered-file)
5923     # . . push args
5924     68/push  _test-output-buffered-file/imm32
5925     68/push  _test-input-stream/imm32
5926     # . . call
5927     e8/call  convert-instruction/disp32
5928     # . . discard args
5929     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
5930     # check output
5931     # . flush(_test-output-buffered-file)
5932     # . . push args
5933     68/push  _test-output-buffered-file/imm32
5934     # . . call
5935     e8/call  flush/disp32
5936     # . . discard args
5937     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5938 +-- 26 lines: #?     # dump output -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
5964     # . check-stream-equal(_test-output-stream, "68 20  # 68/push 0x20/imm8", msg)
5965     # . . push args
5966     68/push  "F - test-convert-instruction-handles-imm8-operand"/imm32
5967     68/push  "68 20  # 68/push 0x20/imm8"/imm32
5968     68/push  _test-output-stream/imm32
5969     # . . call
5970     e8/call  check-stream-equal/disp32
5971     # . . discard args
5972     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5973     # . epilogue
5974     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5975     5d/pop-to-ebp
5976     c3/return
5977 
5978 # shortcut for parse-hex-int-from-slice(next-token-from-slice(word->start, word->end, '/'))
5979 parse-datum-of-word:  # word: (addr slice) -> value/eax: int
5980     # . prologue
5981     55/push-ebp
5982     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
5983     # . save registers
5984     51/push-ecx
5985     56/push-esi
5986     # esi = word
5987     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
5988     # var slice/ecx: slice
5989     68/push  0/imm32/end
5990     68/push  0/imm32/start
5991     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
5992     # slice = next-token-from-slice(word->start, word->end, '/')
5993     # . . push args
5994     51/push-ecx
5995     68/push  0x2f/imm32/slash
5996     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
5997     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
5998     # . . call
5999     e8/call  next-token-from-slice/disp32
6000     # . . discard args
6001     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
6002     # return parse-hex-int-from-slice(slice)
6003     # . . push args
6004     51/push-ecx
6005     # . . call
6006     e8/call  parse-hex-int-from-slice/disp32
6007     # . . discard args
6008     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
6009 $parse-datum-of-word:end:
6010     # . reclaim locals
6011     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
6012     # . restore registers
6013     5e/pop-to-esi
6014     59/pop-to-ecx
6015     # . epilogue
6016     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
6017     5d/pop-to-ebp
6018     c3/return
6019 
6020 # . . vim:nowrap:textwidth=0