https://github.com/akkartik/mu/blob/master/apps/sigils.subx
   1 # Experimental syntax sugar for addressing modes that expand into /rm32 and
   2 # other related arguments.
   3 #
   4 # To run:
   5 #   $ ./subx translate 0*.subx apps/subx-common.subx apps/sigils.subx  -o apps/sigils
   6 #
   7 # We currently support the following notations:
   8 #
   9 # 1.
  10 #   $ echo '%eax'  |  ./subx run apps/sigils
  11 #   3/mod 0/rm32
  12 #
  13 # 2.
  14 #   $ echo '*eax'  |  ./subx run apps/sigils
  15 #   0/mod 0/rm32
  16 #
  17 # 3.
  18 #   $ echo '*(eax+4)'  |  ./subx run apps/sigils
  19 #   2/mod 0/rm32 4/disp32
  20 #
  21 # 4.
  22 #   $ echo '*(eax+ecx)'  |  ./subx run apps/sigils
  23 #   0/mod 4/rm32 0/base 1/index 0/scale
  24 #
  25 # 5.
  26 #   $ echo '*(eax+ecx+4)'  |  ./subx run apps/sigils
  27 #   2/mod 4/rm32 0/base 1/index 0/scale 4/disp32
  28 #
  29 # 6.
  30 #   $ echo '*(eax+ecx<<2+4)'  |  ./subx run apps/sigils
  31 #   2/mod 4/rm32 0/base 1/index 2/scale 4/disp32
  32 #
  33 # 7.
  34 #   $ echo '*Foo'  |  ./subx run apps/sigils
  35 #   0/mod 5/rm32/.disp32 Foo/disp32
  36 #
  37 # Addition isn't commutative here. Template must always be (base+index<<scale+disp),
  38 # though some components are optional as described above.
  39 # In particular, global variables like 'Foo' above don't support displacement
  40 # or index or scale.
  41 #
  42 # No metadata allowed inside '*(...)'.
  43 # Whitespace inside '*(...)' is ok. But not immediately after the '*' or '%'.
  44 #
  45 # The code generated is sub-optimal:
  46 #   - displacements are always disp32, even when disp8 will do
  47 #   - *(...esp...) always uses SIB arguments even when redundant
  48 
  49 == code
  50 #   instruction                     effective address                                                   register    displacement    immediate
  51 # . op          subop               mod             rm32          base        index         scale       r32
  52 # . 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
  53 
  54 Entry:  # run tests if necessary, convert stdin if not
  55     # . prolog
  56     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  57 
  58     # initialize heap
  59     # . Heap = new-segment(Heap-size)
  60     # . . push args
  61     68/push  Heap/imm32
  62     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  63     # . . call
  64     e8/call  new-segment/disp32
  65     # . . discard args
  66     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  67 
  68     # - if argc > 1 and argv[1] == "test", then return run_tests()
  69     # if (argc <= 1) goto run-main
  70     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  71     7e/jump-if-lesser-or-equal  $run-main/disp8
  72     # if (!kernel-string-equal?(argv[1], "test")) goto run-main
  73     # . eax = kernel-string-equal?(argv[1], "test")
  74     # . . push args
  75     68/push  "test"/imm32
  76     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  77     # . . call
  78     e8/call  kernel-string-equal?/disp32
  79     # . . discard args
  80     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  81     # . if (eax == 0) goto run-main
  82     3d/compare-eax-and  0/imm32
  83     74/jump-if-equal  $run-main/disp8
  84     # run-tests()
  85     e8/call  run-tests/disp32
  86     # syscall(exit, *Num-test-failures)
  87     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  88     eb/jump  $main:end/disp8
  89 $run-main:
  90     # - otherwise convert stdin
  91     # convert(Stdin, Stdout)
  92     # . . push args
  93     68/push  Stdout/imm32
  94     68/push  Stdin/imm32
  95     # . . call
  96     e8/call  convert/disp32
  97     # . . discard args
  98     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  99     # syscall(exit, 0)
 100     bb/copy-to-ebx  0/imm32
 101 $main:end:
 102     b8/copy-to-eax  1/imm32/exit
 103     cd/syscall  0x80/imm8
 104 
 105 # error messages considered:
 106 #   *x + 34                 -> error: base+disp addressing must be within '()'
 107 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
 108     # pseudocode:
 109     #   var line = new-stream(512, 1)
 110     #   while true
 111     #     clear-stream(line)
 112     #     read-line-buffered(in, line)
 113     #     if (line->write == 0) break                     # end of file
 114     #     while true
 115     #       var word-slice = next-word-or-expression(line)
 116     #       if slice-empty?(word-slice)                   # end of line
 117     #         break
 118     #       if slice-starts-with?(word-slice, "#")        # comment
 119     #         continue
 120     #       if slice-starts-with?(word-slice, '%')        # direct mode
 121     #         emit-direct-mode(out, word-slice)
 122     #       else if slice-starts-with?(word-slice, '*')   # indirect mode
 123     #         if disp32-mode?(word-slice)
 124     #           emit-indirect-disp32(out, word-slice)
 125     #         else
 126     #           base, index, scale, disp = parse-effective-address(word-slice)
 127     #           emit-indirect-mode(out, base, index, scale, disp)
 128     #       else if slice-starts-with?(word-slice, '+')
 129     #         abort("'+' only permitted within '*(...)'")
 130     #       else
 131     #         write-slice-buffered(out, word-slice)
 132     #       write(out, " ")
 133     #     write(out, "\n")
 134     #   flush(out)
 135     #
 136     # . prolog
 137     55/push-ebp
 138     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 139     # . save registers
 140     50/push-eax
 141     51/push-ecx
 142     52/push-edx
 143     53/push-ebx
 144     # var line/ecx : (address stream byte) = stream(512)
 145     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 146     68/push  0x200/imm32/length
 147     68/push  0/imm32/read
 148     68/push  0/imm32/write
 149     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 150     # var word-slice/edx = {0, 0}
 151     68/push  0/imm32/end
 152     68/push  0/imm32/start
 153     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 154 $convert:line-loop:
 155     # clear-stream(line)
 156     # . . push args
 157     51/push-ecx
 158     # . . call
 159     e8/call  clear-stream/disp32
 160     # . . discard args
 161     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 162     # read-line-buffered(in, line)
 163     # . . push args
 164     51/push-ecx
 165     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 166     # . . call
 167     e8/call  read-line-buffered/disp32
 168     # . . discard args
 169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 170 $convert:check0:
 171     # if (line->write == 0) break
 172     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
 173     0f 84/jump-if-equal  $convert:break/disp32
 174 $convert:word-loop:
 175     # next-word-or-expression(line, word-slice)
 176     # . . push args
 177     52/push-edx
 178     51/push-ecx
 179     # . . call
 180     e8/call  next-word-or-expression/disp32
 181     # . . discard args
 182     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 183 $convert:check1:
 184     # if (slice-empty?(word-slice)) break
 185     # . eax = slice-empty?(word-slice)
 186     # . . push args
 187     52/push-edx
 188     # . . call
 189     e8/call  slice-empty?/disp32
 190     # . . discard args
 191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 192     # . if (eax != 0) break
 193     3d/compare-eax-and  0/imm32
 194     0f 85/jump-if-not-equal  $convert:next-line/disp32
 195 $convert:check-for-comment:
 196     # if (slice-starts-with?(word-slice, "#")) continue
 197     # . start/ebx = word-slice->start
 198     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
 199     # . c/eax = *start
 200     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 201     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
 202     # . if (eax == '#') continue
 203     3d/compare-eax-and  0x23/imm32/hash
 204     74/jump-if-equal  $convert:word-loop/disp8
 205 $convert:check-for-direct-mode:
 206     # if (!slice-starts-with?(word-slice, "%")) goto next check
 207     3d/compare-eax-and  0x25/imm32/percent
 208     75/jump-if-not-equal  $convert:check-for-indirect-mode/disp8
 209 $convert:direct-mode:
 210 +-- 46 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
 256     # emit-direct-mode(out, word-slice)
 257     # . . push args
 258     52/push-edx
 259     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 260     # . . call
 261     e8/call  emit-direct-mode/disp32
 262     # . . discard args
 263     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 264     # continue
 265     e9/jump  $convert:next-word/disp32
 266 $convert:check-for-indirect-mode:
 267     # if (!slice-starts-with?(word-slice, "*")) goto next check
 268     3d/compare-eax-and  0x2a/imm32/asterisk
 269     75/jump-if-not-equal  $convert:check-for-invalid-addition/disp8
 270     # if (!disp32-mode?(word-slice)) goto indirect mode
 271     # . eax = disp32-mode?(word-slice)
 272     # . . push args
 273     52/push-edx
 274     # . . call
 275     e8/call  disp32-mode?/disp32
 276     # . . discard args
 277     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 278     # . if (eax == 0) goto indirect mode
 279     3d/compare-eax-and  0/imm32
 280     74/jump-if-equal  $convert:indirect-mode/disp8
 281 $convert:disp32-mode:
 282     # emit-indirect-mode(out, word-slice)
 283     # . . push args
 284     52/push-edx
 285     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 286     # . . call
 287     e8/call  emit-indirect-disp32/disp32
 288     # . . discard args
 289     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 290     # continue
 291     e9/jump  $convert:next-word/disp32
 292 $convert:indirect-mode:
 293     # spill registers
 294     50/push-eax
 295     51/push-ecx
 296     52/push-edx
 297     53/push-ebx
 298     # base/eax, index/ecx, scale/edx, disp/ebx = parse-effective-address(word-slice)
 299     # . . push args
 300     52/push-edx
 301     # . . call
 302     e8/call  parse-effective-address/disp32
 303     # . . discard args
 304     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 305     # emit-indirect-mode(out, base, index, scale, disp)
 306     # . . push args
 307     53/push-ebx
 308     52/push-edx
 309     51/push-ecx
 310     50/push-eax
 311     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 312     # . . call
 313     e8/call  emit-indirect-mode/disp32
 314     # . . discard args
 315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
 316     # restore registers
 317     5b/pop-to-ebx
 318     5a/pop-to-edx
 319     59/pop-to-ecx
 320     58/pop-to-eax
 321     # continue
 322     e9/jump  $convert:next-word/disp32
 323 $convert:check-for-invalid-addition:
 324     # if (slice-starts-with?(word-slice, "+")) goto error1
 325     3d/compare-eax-and  0x2b/imm32/plus
 326     74/jump-if-equal  $convert:error1/disp8
 327 $convert:check-for-invalid-left-shift:
 328     # if (slice-starts-with?(word-slice, "<")) goto error1
 329     3d/compare-eax-and  0x3c/imm32/less-than
 330     74/jump-if-equal  $convert:error1/disp8
 331 $convert:regular-word:
 332     # write-slice-buffered(out, word-slice)
 333     # . . push args
 334     52/push-edx
 335     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 336     # . . call
 337     e8/call  write-slice-buffered/disp32
 338     # . . discard args
 339     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 340     # fall through
 341 $convert:next-word:
 342     # write-buffered(out, " ")
 343     # . . push args
 344     68/push  " "/imm32
 345     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 346     # . . call
 347     e8/call  write-buffered/disp32
 348     # . . discard args
 349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 350     # loop
 351     e9/jump  $convert:word-loop/disp32
 352 $convert:next-line:
 353     # write-buffered(out, "\n")
 354     # . . push args
 355     68/push  Newline/imm32
 356     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 357     # . . call
 358     e8/call  write-buffered/disp32
 359     # . . discard args
 360     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 361     # loop
 362     e9/jump  $convert:line-loop/disp32
 363 $convert:break:
 364     # flush(out)
 365     # . . push args
 366     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 367     # . . call
 368     e8/call  flush/disp32
 369     # . . discard args
 370     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 371 $convert:end:
 372     # . reclaim locals
 373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 374     # . restore registers
 375     5b/pop-to-ebx
 376     5a/pop-to-edx
 377     59/pop-to-ecx
 378     58/pop-to-eax
 379     # . epilog
 380     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 381     5d/pop-to-ebp
 382     c3/return
 383 
 384 $convert:error1:
 385     # print(stderr, "error: '" eax "' only permitted within '*(...)' in '" line "'")
 386     # . write-buffered(Stderr, "error: '")
 387     # . . push args
 388     68/push  "error: '"/imm32
 389     68/push  Stderr/imm32
 390     # . . call
 391     e8/call  write-buffered/disp32
 392     # . . discard args
 393     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 394     # . write-byte-buffered(Stderr, eax)
 395     # . . push args
 396     50/push-eax
 397     68/push  Stderr/imm32
 398     # . . call
 399     e8/call  write-byte-buffered/disp32
 400     # . . discard args
 401     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 402     # . write-buffered(Stderr, "' only permitted within '*(...)' in '")
 403     # . . push args
 404     68/push  "' only permitted within '*(...)' in '"/imm32
 405     68/push  Stderr/imm32
 406     # . . call
 407     e8/call  write-buffered/disp32
 408     # . . discard args
 409     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 410     # . write-stream-data(Stderr, line)
 411     # . . push args
 412     51/push-ecx
 413     68/push  Stderr/imm32
 414     # . . call
 415     e8/call  write-stream-data/disp32
 416     # . . discard args
 417     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 418     # . write-buffered(Stderr, "'")
 419     # . . push args
 420     68/push  "'"/imm32
 421     68/push  Stderr/imm32
 422     # . . call
 423     e8/call  write-buffered/disp32
 424     # . . discard args
 425     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 426     # . flush(Stderr)
 427     # . . push args
 428     68/push  Stderr/imm32
 429     # . . call
 430     e8/call  flush/disp32
 431     # . . discard args
 432     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 433     # . syscall(exit, 1)
 434     bb/copy-to-ebx  1/imm32
 435     b8/copy-to-eax  1/imm32/exit
 436     cd/syscall  0x80/imm8
 437     # never gets here
 438 
 439 test-convert-passes-most-words-through:
 440     # . prolog
 441     55/push-ebp
 442     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 443     # setup
 444     # . clear-stream(_test-input-stream)
 445     # . . push args
 446     68/push  _test-input-stream/imm32
 447     # . . call
 448     e8/call  clear-stream/disp32
 449     # . . discard args
 450     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 451     # . clear-stream(_test-input-buffered-file+4)
 452     # . . push args
 453     b8/copy-to-eax  _test-input-buffered-file/imm32
 454     05/add-to-eax  4/imm32
 455     50/push-eax
 456     # . . call
 457     e8/call  clear-stream/disp32
 458     # . . discard args
 459     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 460     # . clear-stream(_test-output-stream)
 461     # . . push args
 462     68/push  _test-output-stream/imm32
 463     # . . call
 464     e8/call  clear-stream/disp32
 465     # . . discard args
 466     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 467     # . clear-stream(_test-output-buffered-file+4)
 468     # . . push args
 469     b8/copy-to-eax  _test-output-buffered-file/imm32
 470     05/add-to-eax  4/imm32
 471     50/push-eax
 472     # . . call
 473     e8/call  clear-stream/disp32
 474     # . . discard args
 475     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 476     # initialize input
 477     # . write(_test-input-stream, "== abcd 0x1")
 478     # . . push args
 479     68/push  "== abcd 0x1"/imm32
 480     68/push  _test-input-stream/imm32
 481     # . . call
 482     e8/call  write/disp32
 483     # . . discard args
 484     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 485     # convert(_test-input-buffered-file, _test-output-buffered-file)
 486     # . . push args
 487     68/push  _test-output-buffered-file/imm32
 488     68/push  _test-input-buffered-file/imm32
 489     # . . call
 490     e8/call  convert/disp32
 491     # . . discard args
 492     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 493     # check that the line just passed through
 494     # . flush(_test-output-buffered-file)
 495     # . . push args
 496     68/push  _test-output-buffered-file/imm32
 497     # . . call
 498     e8/call  flush/disp32
 499     # . . discard args
 500     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 501 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 527     # . check-stream-equal(_test-output-stream, "== abcd 0x1 \n", msg)
 528     # . . push args
 529     68/push  "F - test-convert-passes-most-words-through"/imm32
 530     68/push  "== abcd 0x1 \n"/imm32
 531     68/push  _test-output-stream/imm32
 532     # . . call
 533     e8/call  check-stream-equal/disp32
 534     # . . discard args
 535     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 536     # . epilog
 537     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 538     5d/pop-to-ebp
 539     c3/return
 540 
 541 test-convert-direct-mode:
 542     # . prolog
 543     55/push-ebp
 544     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 545     # setup
 546     # . clear-stream(_test-input-stream)
 547     # . . push args
 548     68/push  _test-input-stream/imm32
 549     # . . call
 550     e8/call  clear-stream/disp32
 551     # . . discard args
 552     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 553     # . clear-stream(_test-input-buffered-file+4)
 554     # . . push args
 555     b8/copy-to-eax  _test-input-buffered-file/imm32
 556     05/add-to-eax  4/imm32
 557     50/push-eax
 558     # . . call
 559     e8/call  clear-stream/disp32
 560     # . . discard args
 561     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 562     # . clear-stream(_test-output-stream)
 563     # . . push args
 564     68/push  _test-output-stream/imm32
 565     # . . call
 566     e8/call  clear-stream/disp32
 567     # . . discard args
 568     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 569     # . clear-stream(_test-output-buffered-file+4)
 570     # . . push args
 571     b8/copy-to-eax  _test-output-buffered-file/imm32
 572     05/add-to-eax  4/imm32
 573     50/push-eax
 574     # . . call
 575     e8/call  clear-stream/disp32
 576     # . . discard args
 577     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 578     # initialize input
 579     # . write(_test-input-stream, "ab %ecx")
 580     # . . push args
 581     68/push  "ab %ecx"/imm32
 582     68/push  _test-input-stream/imm32
 583     # . . call
 584     e8/call  write/disp32
 585     # . . discard args
 586     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 587     # convert(_test-input-buffered-file, _test-output-buffered-file)
 588     # . . push args
 589     68/push  _test-output-buffered-file/imm32
 590     68/push  _test-input-buffered-file/imm32
 591     # . . call
 592     e8/call  convert/disp32
 593     # . . discard args
 594     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 595     # check that the line just passed through
 596     # . flush(_test-output-buffered-file)
 597     # . . push args
 598     68/push  _test-output-buffered-file/imm32
 599     # . . call
 600     e8/call  flush/disp32
 601     # . . discard args
 602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 603 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 629     # . check-stream-equal(_test-output-stream, "ab 3/mod/direct 0x00000001/rm32 \n", msg)
 630     # . . push args
 631     68/push  "F - test-convert-direct-mode"/imm32
 632     68/push  "ab 3/mod/direct 0x00000001/rm32 \n"/imm32
 633     68/push  _test-output-stream/imm32
 634     # . . call
 635     e8/call  check-stream-equal/disp32
 636     # . . discard args
 637     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 638     # . epilog
 639     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 640     5d/pop-to-ebp
 641     c3/return
 642 
 643 test-convert-direct-mode-with-metadata:
 644     # . prolog
 645     55/push-ebp
 646     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 647     # setup
 648     # . clear-stream(_test-input-stream)
 649     # . . push args
 650     68/push  _test-input-stream/imm32
 651     # . . call
 652     e8/call  clear-stream/disp32
 653     # . . discard args
 654     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 655     # . clear-stream(_test-input-buffered-file+4)
 656     # . . push args
 657     b8/copy-to-eax  _test-input-buffered-file/imm32
 658     05/add-to-eax  4/imm32
 659     50/push-eax
 660     # . . call
 661     e8/call  clear-stream/disp32
 662     # . . discard args
 663     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 664     # . clear-stream(_test-output-stream)
 665     # . . push args
 666     68/push  _test-output-stream/imm32
 667     # . . call
 668     e8/call  clear-stream/disp32
 669     # . . discard args
 670     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 671     # . clear-stream(_test-output-buffered-file+4)
 672     # . . push args
 673     b8/copy-to-eax  _test-output-buffered-file/imm32
 674     05/add-to-eax  4/imm32
 675     50/push-eax
 676     # . . call
 677     e8/call  clear-stream/disp32
 678     # . . discard args
 679     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 680     # initialize input
 681     # . write(_test-input-stream, "ab %ecx/foo")
 682     # . . push args
 683     68/push  "ab %ecx/foo"/imm32
 684     68/push  _test-input-stream/imm32
 685     # . . call
 686     e8/call  write/disp32
 687     # . . discard args
 688     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 689     # convert(_test-input-buffered-file, _test-output-buffered-file)
 690     # . . push args
 691     68/push  _test-output-buffered-file/imm32
 692     68/push  _test-input-buffered-file/imm32
 693     # . . call
 694     e8/call  convert/disp32
 695     # . . discard args
 696     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 697     # check that the line just passed through
 698     # . flush(_test-output-buffered-file)
 699     # . . push args
 700     68/push  _test-output-buffered-file/imm32
 701     # . . call
 702     e8/call  flush/disp32
 703     # . . discard args
 704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 705     # . check-stream-equal(_test-output-stream, "ab 3/mod/direct 0x00000001/rm32 \n", msg)
 706     # . . push args
 707     68/push  "F - test-convert-direct-mode-with-metadata"/imm32
 708     68/push  "ab 3/mod/direct 0x00000001/rm32 \n"/imm32
 709     68/push  _test-output-stream/imm32
 710     # . . call
 711     e8/call  check-stream-equal/disp32
 712     # . . discard args
 713     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 714     # . epilog
 715     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 716     5d/pop-to-ebp
 717     c3/return
 718 
 719 test-convert-register-indirect-mode:
 720     # . prolog
 721     55/push-ebp
 722     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 723     # setup
 724     # . clear-stream(_test-input-stream)
 725     # . . push args
 726     68/push  _test-input-stream/imm32
 727     # . . call
 728     e8/call  clear-stream/disp32
 729     # . . discard args
 730     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 731     # . clear-stream(_test-input-buffered-file+4)
 732     # . . push args
 733     b8/copy-to-eax  _test-input-buffered-file/imm32
 734     05/add-to-eax  4/imm32
 735     50/push-eax
 736     # . . call
 737     e8/call  clear-stream/disp32
 738     # . . discard args
 739     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 740     # . clear-stream(_test-output-stream)
 741     # . . push args
 742     68/push  _test-output-stream/imm32
 743     # . . call
 744     e8/call  clear-stream/disp32
 745     # . . discard args
 746     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 747     # . clear-stream(_test-output-buffered-file+4)
 748     # . . push args
 749     b8/copy-to-eax  _test-output-buffered-file/imm32
 750     05/add-to-eax  4/imm32
 751     50/push-eax
 752     # . . call
 753     e8/call  clear-stream/disp32
 754     # . . discard args
 755     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 756     # initialize input
 757     # . write(_test-input-stream, "ab *ecx")
 758     # . . push args
 759     68/push  "ab *ecx"/imm32
 760     68/push  _test-input-stream/imm32
 761     # . . call
 762     e8/call  write/disp32
 763     # . . discard args
 764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 765     # convert(_test-input-buffered-file, _test-output-buffered-file)
 766     # . . push args
 767     68/push  _test-output-buffered-file/imm32
 768     68/push  _test-input-buffered-file/imm32
 769     # . . call
 770     e8/call  convert/disp32
 771     # . . discard args
 772     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 773     # check that the line just passed through
 774     # . flush(_test-output-buffered-file)
 775     # . . push args
 776     68/push  _test-output-buffered-file/imm32
 777     # . . call
 778     e8/call  flush/disp32
 779     # . . discard args
 780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 781 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 807     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 0x00000001/rm32 \n", msg)
 808     # . . push args
 809     68/push  "F - test-convert-register-indirect-mode"/imm32
 810     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
 811     68/push  _test-output-stream/imm32
 812     # . . call
 813     e8/call  check-stream-equal/disp32
 814     # . . discard args
 815     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 816     # . epilog
 817     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 818     5d/pop-to-ebp
 819     c3/return
 820 
 821 test-convert-register-indirect-mode-with-metadata:
 822     # . prolog
 823     55/push-ebp
 824     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 825     # setup
 826     # . clear-stream(_test-input-stream)
 827     # . . push args
 828     68/push  _test-input-stream/imm32
 829     # . . call
 830     e8/call  clear-stream/disp32
 831     # . . discard args
 832     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 833     # . clear-stream(_test-input-buffered-file+4)
 834     # . . push args
 835     b8/copy-to-eax  _test-input-buffered-file/imm32
 836     05/add-to-eax  4/imm32
 837     50/push-eax
 838     # . . call
 839     e8/call  clear-stream/disp32
 840     # . . discard args
 841     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 842     # . clear-stream(_test-output-stream)
 843     # . . push args
 844     68/push  _test-output-stream/imm32
 845     # . . call
 846     e8/call  clear-stream/disp32
 847     # . . discard args
 848     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 849     # . clear-stream(_test-output-buffered-file+4)
 850     # . . push args
 851     b8/copy-to-eax  _test-output-buffered-file/imm32
 852     05/add-to-eax  4/imm32
 853     50/push-eax
 854     # . . call
 855     e8/call  clear-stream/disp32
 856     # . . discard args
 857     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 858     # initialize input
 859     # . write(_test-input-stream, "ab *ecx/foo")
 860     # . . push args
 861     68/push  "ab *ecx/foo"/imm32
 862     68/push  _test-input-stream/imm32
 863     # . . call
 864     e8/call  write/disp32
 865     # . . discard args
 866     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 867     # convert(_test-input-buffered-file, _test-output-buffered-file)
 868     # . . push args
 869     68/push  _test-output-buffered-file/imm32
 870     68/push  _test-input-buffered-file/imm32
 871     # . . call
 872     e8/call  convert/disp32
 873     # . . discard args
 874     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 875     # check that the line just passed through
 876     # . flush(_test-output-buffered-file)
 877     # . . push args
 878     68/push  _test-output-buffered-file/imm32
 879     # . . call
 880     e8/call  flush/disp32
 881     # . . discard args
 882     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 883     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 0x00000001/rm32 \n", msg)
 884     # . . push args
 885     68/push  "F - test-convert-register-indirect-mode-with-metadata"/imm32
 886     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
 887     68/push  _test-output-stream/imm32
 888     # . . call
 889     e8/call  check-stream-equal/disp32
 890     # . . discard args
 891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 892     # . epilog
 893     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 894     5d/pop-to-ebp
 895     c3/return
 896 
 897 test-convert-register-indirect-mode-without-displacement:
 898     # . prolog
 899     55/push-ebp
 900     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 901     # setup
 902     # . clear-stream(_test-input-stream)
 903     # . . push args
 904     68/push  _test-input-stream/imm32
 905     # . . call
 906     e8/call  clear-stream/disp32
 907     # . . discard args
 908     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 909     # . clear-stream(_test-input-buffered-file+4)
 910     # . . push args
 911     b8/copy-to-eax  _test-input-buffered-file/imm32
 912     05/add-to-eax  4/imm32
 913     50/push-eax
 914     # . . call
 915     e8/call  clear-stream/disp32
 916     # . . discard args
 917     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 918     # . clear-stream(_test-output-stream)
 919     # . . push args
 920     68/push  _test-output-stream/imm32
 921     # . . call
 922     e8/call  clear-stream/disp32
 923     # . . discard args
 924     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 925     # . clear-stream(_test-output-buffered-file+4)
 926     # . . push args
 927     b8/copy-to-eax  _test-output-buffered-file/imm32
 928     05/add-to-eax  4/imm32
 929     50/push-eax
 930     # . . call
 931     e8/call  clear-stream/disp32
 932     # . . discard args
 933     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 934     # initialize input
 935     # . write(_test-input-stream, "ab *(ecx)")
 936     # . . push args
 937     68/push  "ab *(ecx)"/imm32
 938     68/push  _test-input-stream/imm32
 939     # . . call
 940     e8/call  write/disp32
 941     # . . discard args
 942     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 943     # convert(_test-input-buffered-file, _test-output-buffered-file)
 944     # . . push args
 945     68/push  _test-output-buffered-file/imm32
 946     68/push  _test-input-buffered-file/imm32
 947     # . . call
 948     e8/call  convert/disp32
 949     # . . discard args
 950     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 951     # check that the line just passed through
 952     # . flush(_test-output-buffered-file)
 953     # . . push args
 954     68/push  _test-output-buffered-file/imm32
 955     # . . call
 956     e8/call  flush/disp32
 957     # . . discard args
 958     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 959 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 985     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 1/rm32 \n", msg)
 986     # . . push args
 987     68/push  "F - test-convert-register-indirect-mode-without-displacement"/imm32
 988     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
 989     68/push  _test-output-stream/imm32
 990     # . . call
 991     e8/call  check-stream-equal/disp32
 992     # . . discard args
 993     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 994     # . epilog
 995     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 996     5d/pop-to-ebp
 997     c3/return
 998 
 999 test-convert-register-indirect-mode-with-displacement:
1000     # . prolog
1001     55/push-ebp
1002     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1003     # setup
1004     # . clear-stream(_test-input-stream)
1005     # . . push args
1006     68/push  _test-input-stream/imm32
1007     # . . call
1008     e8/call  clear-stream/disp32
1009     # . . discard args
1010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1011     # . clear-stream(_test-input-buffered-file+4)
1012     # . . push args
1013     b8/copy-to-eax  _test-input-buffered-file/imm32
1014     05/add-to-eax  4/imm32
1015     50/push-eax
1016     # . . call
1017     e8/call  clear-stream/disp32
1018     # . . discard args
1019     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1020     # . clear-stream(_test-output-stream)
1021     # . . push args
1022     68/push  _test-output-stream/imm32
1023     # . . call
1024     e8/call  clear-stream/disp32
1025     # . . discard args
1026     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1027     # . clear-stream(_test-output-buffered-file+4)
1028     # . . push args
1029     b8/copy-to-eax  _test-output-buffered-file/imm32
1030     05/add-to-eax  4/imm32
1031     50/push-eax
1032     # . . call
1033     e8/call  clear-stream/disp32
1034     # . . discard args
1035     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1036     # initialize input
1037     # . write(_test-input-stream, "ab *(ecx+4)")
1038     # . . push args
1039     68/push  "ab *(ecx+4)"/imm32
1040     68/push  _test-input-stream/imm32
1041     # . . call
1042     e8/call  write/disp32
1043     # . . discard args
1044     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1045     # convert(_test-input-buffered-file, _test-output-buffered-file)
1046     # . . push args
1047     68/push  _test-output-buffered-file/imm32
1048     68/push  _test-input-buffered-file/imm32
1049     # . . call
1050     e8/call  convert/disp32
1051     # . . discard args
1052     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1053     # check that the line just passed through
1054     # . flush(_test-output-buffered-file)
1055     # . . push args
1056     68/push  _test-output-buffered-file/imm32
1057     # . . call
1058     e8/call  flush/disp32
1059     # . . discard args
1060     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1061 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1087     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 1/rm32 4/disp32 \n", msg)
1088     # . . push args
1089     68/push  "F - test-convert-register-indirect-mode-with-displacement"/imm32
1090     68/push  "ab 2/mod/*+disp32 0x00000001/rm32 0x00000004/disp32 \n"/imm32
1091     68/push  _test-output-stream/imm32
1092     # . . call
1093     e8/call  check-stream-equal/disp32
1094     # . . discard args
1095     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1096     # . epilog
1097     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1098     5d/pop-to-ebp
1099     c3/return
1100 
1101 # boss level
1102 test-convert-register-indirect-mode-with-sib-byte:
1103     # . prolog
1104     55/push-ebp
1105     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1106     # setup
1107     # . clear-stream(_test-input-stream)
1108     # . . push args
1109     68/push  _test-input-stream/imm32
1110     # . . call
1111     e8/call  clear-stream/disp32
1112     # . . discard args
1113     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1114     # . clear-stream(_test-input-buffered-file+4)
1115     # . . push args
1116     b8/copy-to-eax  _test-input-buffered-file/imm32
1117     05/add-to-eax  4/imm32
1118     50/push-eax
1119     # . . call
1120     e8/call  clear-stream/disp32
1121     # . . discard args
1122     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1123     # . clear-stream(_test-output-stream)
1124     # . . push args
1125     68/push  _test-output-stream/imm32
1126     # . . call
1127     e8/call  clear-stream/disp32
1128     # . . discard args
1129     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1130     # . clear-stream(_test-output-buffered-file+4)
1131     # . . push args
1132     b8/copy-to-eax  _test-output-buffered-file/imm32
1133     05/add-to-eax  4/imm32
1134     50/push-eax
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     # initialize input
1140     # . write(_test-input-stream, "ab *(ecx + edx<<3 + 4)")
1141     # . . push args
1142     68/push  "ab *(ecx + edx<<3 + 4)"/imm32
1143     68/push  _test-input-stream/imm32
1144     # . . call
1145     e8/call  write/disp32
1146     # . . discard args
1147     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1148     # convert(_test-input-buffered-file, _test-output-buffered-file)
1149     # . . push args
1150     68/push  _test-output-buffered-file/imm32
1151     68/push  _test-input-buffered-file/imm32
1152     # . . call
1153     e8/call  convert/disp32
1154     # . . discard args
1155     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1156     # check that the line just passed through
1157     # . flush(_test-output-buffered-file)
1158     # . . push args
1159     68/push  _test-output-buffered-file/imm32
1160     # . . call
1161     e8/call  flush/disp32
1162     # . . discard args
1163     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1164 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1190     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale 4/disp32 \n", msg)
1191     # . . push args
1192     68/push  "F - test-convert-register-indirect-mode-with-sib-byte"/imm32
1193     68/push  "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0x00000004/disp32 \n"/imm32
1194     68/push  _test-output-stream/imm32
1195     # . . call
1196     e8/call  check-stream-equal/disp32
1197     # . . discard args
1198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1199     # . epilog
1200     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1201     5d/pop-to-ebp
1202     c3/return
1203 
1204 test-convert-register-indirect-mode-with-sib-byte-negative-displacement:
1205     # . prolog
1206     55/push-ebp
1207     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1208     # setup
1209     # . clear-stream(_test-input-stream)
1210     # . . push args
1211     68/push  _test-input-stream/imm32
1212     # . . call
1213     e8/call  clear-stream/disp32
1214     # . . discard args
1215     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1216     # . clear-stream(_test-input-buffered-file+4)
1217     # . . push args
1218     b8/copy-to-eax  _test-input-buffered-file/imm32
1219     05/add-to-eax  4/imm32
1220     50/push-eax
1221     # . . call
1222     e8/call  clear-stream/disp32
1223     # . . discard args
1224     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1225     # . clear-stream(_test-output-stream)
1226     # . . push args
1227     68/push  _test-output-stream/imm32
1228     # . . call
1229     e8/call  clear-stream/disp32
1230     # . . discard args
1231     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1232     # . clear-stream(_test-output-buffered-file+4)
1233     # . . push args
1234     b8/copy-to-eax  _test-output-buffered-file/imm32
1235     05/add-to-eax  4/imm32
1236     50/push-eax
1237     # . . call
1238     e8/call  clear-stream/disp32
1239     # . . discard args
1240     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1241     # initialize input
1242     # . write(_test-input-stream, "ab *(ecx + edx<<3 - 4)")
1243     # . . push args
1244     68/push  "ab *(ecx + edx<<3 - 4)"/imm32
1245     68/push  _test-input-stream/imm32
1246     # . . call
1247     e8/call  write/disp32
1248     # . . discard args
1249     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1250     # convert(_test-input-buffered-file, _test-output-buffered-file)
1251     # . . push args
1252     68/push  _test-output-buffered-file/imm32
1253     68/push  _test-input-buffered-file/imm32
1254     # . . call
1255     e8/call  convert/disp32
1256     # . . discard args
1257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1258     # check that the line just passed through
1259     # . flush(_test-output-buffered-file)
1260     # . . push args
1261     68/push  _test-output-buffered-file/imm32
1262     # . . call
1263     e8/call  flush/disp32
1264     # . . discard args
1265     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1266 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1292     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale -4/disp32 \n", msg)
1293     # . . push args
1294     68/push  "F - test-convert-register-indirect-mode-with-sib-byte-negative-displacement"/imm32
1295     68/push  "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0xfffffffc/disp32 \n"/imm32
1296     68/push  _test-output-stream/imm32
1297     # . . call
1298     e8/call  check-stream-equal/disp32
1299     # . . discard args
1300     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1301     # . epilog
1302     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1303     5d/pop-to-ebp
1304     c3/return
1305 
1306 test-convert-indirect-mode-without-register:
1307     # . prolog
1308     55/push-ebp
1309     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1310     # setup
1311     # . clear-stream(_test-input-stream)
1312     # . . push args
1313     68/push  _test-input-stream/imm32
1314     # . . call
1315     e8/call  clear-stream/disp32
1316     # . . discard args
1317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1318     # . clear-stream(_test-input-buffered-file+4)
1319     # . . push args
1320     b8/copy-to-eax  _test-input-buffered-file/imm32
1321     05/add-to-eax  4/imm32
1322     50/push-eax
1323     # . . call
1324     e8/call  clear-stream/disp32
1325     # . . discard args
1326     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1327     # . clear-stream(_test-output-stream)
1328     # . . push args
1329     68/push  _test-output-stream/imm32
1330     # . . call
1331     e8/call  clear-stream/disp32
1332     # . . discard args
1333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1334     # . clear-stream(_test-output-buffered-file+4)
1335     # . . push args
1336     b8/copy-to-eax  _test-output-buffered-file/imm32
1337     05/add-to-eax  4/imm32
1338     50/push-eax
1339     # . . call
1340     e8/call  clear-stream/disp32
1341     # . . discard args
1342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1343     # initialize input
1344     # . write(_test-input-stream, "ab *Foo")
1345     # . . push args
1346     68/push  "ab *Foo"/imm32
1347     68/push  _test-input-stream/imm32
1348     # . . call
1349     e8/call  write/disp32
1350     # . . discard args
1351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1352     # convert(_test-input-buffered-file, _test-output-buffered-file)
1353     # . . push args
1354     68/push  _test-output-buffered-file/imm32
1355     68/push  _test-input-buffered-file/imm32
1356     # . . call
1357     e8/call  convert/disp32
1358     # . . discard args
1359     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1360     # check that the line just passed through
1361     # . flush(_test-output-buffered-file)
1362     # . . push args
1363     68/push  _test-output-buffered-file/imm32
1364     # . . call
1365     e8/call  flush/disp32
1366     # . . discard args
1367     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1368 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1394     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 5/rm32/.disp32 Foo/disp32 \n", msg)
1395     # . . push args
1396     68/push  "F - test-convert-indirect-mode-without-register"/imm32
1397     68/push  "ab 0/mod/indirect 5/rm32/.disp32 Foo/disp32 \n"/imm32
1398     68/push  _test-output-stream/imm32
1399     # . . call
1400     e8/call  check-stream-equal/disp32
1401     # . . discard args
1402     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1403     # . epilog
1404     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1405     5d/pop-to-ebp
1406     c3/return
1407 
1408 emit-direct-mode:  # out : (address buffered-file), word-slice : (address slice)
1409     # . prolog
1410     55/push-ebp
1411     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1412     # . save registers
1413     50/push-eax
1414     # var local-slice/eax : (address slice) = {word-slice->start, word-slice->end}
1415     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
1416     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1417     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1418     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
1419     # ++local-slice->start to skip '%'
1420     # . ++(*eax)
1421     ff          0/subop/increment   0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # increment *eax
1422     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
1423     # . . push args
1424     50/push-eax
1425     68/push  0x2f/imm32/slash
1426     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1427     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1428     # . . call
1429     e8/call  next-token-from-slice/disp32
1430     # . . discard args
1431     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1432     # reg-num/eax = get-slice(Registers, local-slice, row-size=8)
1433     # . . push args
1434     68/push  "Registers"/imm32
1435     68/push  8/imm32/row-size
1436     50/push-eax
1437     68/push  Registers/imm32
1438     # . . call
1439     e8/call  get-slice/disp32
1440     # . . discard args
1441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1442     # write-buffered(out, "3/mod/direct ")
1443     # . . push args
1444     68/push  "3/mod/direct "/imm32
1445     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1446     # . . call
1447     e8/call  write-buffered/disp32
1448     # . . discard args
1449     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1450     # print-int32-buffered(out, *reg-num)
1451     # . . push args
1452     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1453     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1454     # . . call
1455     e8/call  print-int32-buffered/disp32
1456     # . . discard args
1457     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1458     # write-buffered(out, "/rm32")
1459     # . . push args
1460     68/push  "/rm32"/imm32
1461     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1462     # . . call
1463     e8/call  write-buffered/disp32
1464     # . . discard args
1465     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1466 $emit-direct-mode:end:
1467     # reclaim locals
1468     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1469     # . restore registers
1470     58/pop-to-eax
1471     # . epilog
1472     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1473     5d/pop-to-ebp
1474     c3/return
1475 
1476 test-emit-direct-mode:
1477     # . prolog
1478     55/push-ebp
1479     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1480     # setup
1481     # . clear-stream(_test-output-stream)
1482     # . . push args
1483     68/push  _test-output-stream/imm32
1484     # . . call
1485     e8/call  clear-stream/disp32
1486     # . . discard args
1487     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1488     # . clear-stream(_test-output-buffered-file+4)
1489     # . . push args
1490     b8/copy-to-eax  _test-output-buffered-file/imm32
1491     05/add-to-eax  4/imm32
1492     50/push-eax
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     # var slice/ecx = "%eax"
1498     b8/copy-to-eax  "%eax"/imm32
1499     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1500     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1501     05/add-to-eax  4/imm32
1502     # . ecx = {eax, ecx}
1503     51/push-ecx
1504     50/push-eax
1505     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1506     # emit-direct-mode(_test-output-buffered-file, str)
1507     # . . push args
1508     51/push-ecx
1509     68/push  _test-output-buffered-file/imm32
1510     # . . call
1511     e8/call  emit-direct-mode/disp32
1512     # . . discard args
1513     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32         # add to esp
1514     # . flush(_test-output-buffered-file)
1515     # . . push args
1516     68/push  _test-output-buffered-file/imm32
1517     # . . call
1518     e8/call  flush/disp32
1519     # . . discard args
1520     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1521 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1547     # check-stream-equal(_test-output-stream, "3/mod/direct 0/rm32", msg)
1548     # . . push args
1549     68/push  "F - test-emit-direct-mode/0"/imm32
1550     68/push  "3/mod/direct 0x00000000/rm32"/imm32
1551     68/push  _test-output-stream/imm32
1552     # . . call
1553     e8/call  check-stream-equal/disp32
1554     # . . discard args
1555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1556     # . epilog
1557     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1558     5d/pop-to-ebp
1559     c3/return
1560 
1561 test-emit-direct-mode-2:
1562     # . prolog
1563     55/push-ebp
1564     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1565     # setup
1566     # . clear-stream(_test-output-stream)
1567     # . . push args
1568     68/push  _test-output-stream/imm32
1569     # . . call
1570     e8/call  clear-stream/disp32
1571     # . . discard args
1572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1573     # . clear-stream(_test-output-buffered-file+4)
1574     # . . push args
1575     b8/copy-to-eax  _test-output-buffered-file/imm32
1576     05/add-to-eax  4/imm32
1577     50/push-eax
1578     # . . call
1579     e8/call  clear-stream/disp32
1580     # . . discard args
1581     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1582     # var slice/ecx = "%edi"
1583     b8/copy-to-eax  "%edi"/imm32
1584     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1585     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1586     05/add-to-eax  4/imm32
1587     # . ecx = {eax, ecx}
1588     51/push-ecx
1589     50/push-eax
1590     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1591     # emit-direct-mode(_test-output-buffered-file, str/ecx)
1592     # . . push args
1593     51/push-ecx
1594     68/push  _test-output-buffered-file/imm32
1595     # . . call
1596     e8/call  emit-direct-mode/disp32
1597     # . . discard args
1598     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32         # add to esp
1599     # . flush(_test-output-buffered-file)
1600     # . . push args
1601     68/push  _test-output-buffered-file/imm32
1602     # . . call
1603     e8/call  flush/disp32
1604     # . . discard args
1605     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1606 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1632     # check-stream-equal(_test-output-stream, "3/mod/direct 7/rm32", msg)
1633     # . . push args
1634     68/push  "F - test-emit-direct-mode/1"/imm32
1635     68/push  "3/mod/direct 0x00000007/rm32"/imm32
1636     68/push  _test-output-stream/imm32
1637     # . . call
1638     e8/call  check-stream-equal/disp32
1639     # . . discard args
1640     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1641     # . epilog
1642     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1643     5d/pop-to-ebp
1644     c3/return
1645 
1646 # (re)compute the bounds of the next word or parenthetical expression in the line
1647 # return empty string on reaching end of file
1648 #
1649 # error messages considered:
1650 #   * ...                   -> error: no space after '*'
1651 #   *(...                   -> error: *(...) expression must be all on a single line
1652 next-word-or-expression:  # line : (address stream byte), out : (address slice)
1653     # . prolog
1654     55/push-ebp
1655     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1656     # . save registers
1657     50/push-eax
1658     51/push-ecx
1659     56/push-esi
1660     57/push-edi
1661     # esi = line
1662     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
1663     # edi = out
1664     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
1665     # skip-chars-matching(line, ' ')
1666     # . . push args
1667     68/push  0x20/imm32/space
1668     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1669     # . . call
1670     e8/call  skip-chars-matching/disp32
1671     # . . discard args
1672     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1673 $next-word-or-expression:check0:
1674     # if (line->read >= line->write) clear out and return
1675     # . eax = line->read
1676     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
1677     # . if (eax < line->write) goto next check
1678     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
1679     7c/jump-if-lesser  $next-word-or-expression:check-for-comment/disp8
1680     # . return out = {0, 0}
1681     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
1682     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
1683     e9/jump  $next-word-or-expression:end/disp32
1684 $next-word-or-expression:check-for-comment:
1685     # out->start = &line->data[line->read]
1686     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1687     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
1688     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
1689     # if (line->data[line->read] != '#') goto next check
1690     # . eax = line->data[line->read]
1691     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1692     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
1693     # . compare
1694     3d/compare-eax-and  0x23/imm32/pound
1695     75/jump-if-not-equal  $next-word-or-expression:check-for-string-literal/disp8
1696 $next-word-or-expression:comment:
1697     # out->end = &line->data[line->write]
1698     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1699     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           0/r32/eax   0xc/disp8       .                 # copy esi+eax+12 to eax
1700     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1701     # line->read = line->write  # skip rest of line
1702     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1703     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(esi+4)
1704     # return
1705     eb/jump  $next-word-or-expression:end/disp8
1706 $next-word-or-expression:check-for-string-literal:
1707     # if (line->data[line->read] != '"') goto next check
1708     # . eax = line->data[line->read]
1709     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1710     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
1711     # . compare
1712     3d/compare-eax-and  0x22/imm32/dquote
1713     75/jump-if-not-equal  $next-word-or-expression:check-for-paren/disp8
1714 $next-word-or-expression:string-literal:
1715     # skip-string(line)
1716     # . . push args
1717     56/push-esi
1718     # . . call
1719     e8/call  skip-string/disp32
1720     # . . discard args
1721     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1722     # skip rest of word
1723     eb/jump  $next-word-or-expression:regular-word/disp8
1724 $next-word-or-expression:check-for-paren:
1725     # if (line->data[line->read] != '*') goto next check
1726     # . eax = line->data[line->read]
1727     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1728     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
1729     # . compare
1730     3d/compare-eax-and  0x2a/imm32/asterisk
1731     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
1732     # if (line->data[line->read] == ' ') goto error1
1733     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xd/disp8       .                 # copy byte at *(esi+ecx+12+1) to AL
1734     # . compare
1735     3d/compare-eax-and  0x20/imm32/space
1736     74/jump-if-equal  $next-word-or-expression:error1/disp8
1737     # if (line->data[line->read] != '(') goto regular word
1738     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xd/disp8       .                 # copy byte at *(esi+ecx+12+1) to AL
1739     # . compare
1740     3d/compare-eax-and  0x28/imm32/open-paren
1741     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
1742 $next-word-or-expression:paren:
1743     # skip-until-close-paren(line)
1744     # . . push args
1745     56/push-esi
1746     # . . call
1747     e8/call  skip-until-close-paren/disp32
1748     # . . discard args
1749     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1750     # if (line->data[line->read] != ')') goto error2
1751     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1752     8a/copy-byte                    1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/AL    0xc/disp8       .                 # copy byte at *(esi+ecx+12) to AL
1753     # . compare
1754     3d/compare-eax-and  0x29/imm32/close-paren
1755     75/jump-if-not-equal  $next-word-or-expression:error2/disp8
1756     # skip ')'
1757     ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # increment *(esi+4)
1758     # fall through
1759 $next-word-or-expression:regular-word:
1760     # skip-chars-not-matching-whitespace(line)  # including trailing newline
1761     # . . push args
1762     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1763     # . . call
1764     e8/call  skip-chars-not-matching-whitespace/disp32
1765     # . . discard args
1766     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1767     # out->end = &line->data[line->read]
1768     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1769     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  1/index/ecx   .           0/r32/eax   0xc/disp8       .                 # copy esi+ecx+12 to eax
1770     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1771 $next-word-or-expression:end:
1772     # . restore registers
1773     5f/pop-to-edi
1774     5e/pop-to-esi
1775     59/pop-to-ecx
1776     58/pop-to-eax
1777     # . epilog
1778     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1779     5d/pop-to-ebp
1780     c3/return
1781 
1782 $next-word-or-expression:error1:
1783     # print(stderr, "error: no space allowed after '*' in '" line "'")
1784     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
1785     # . . push args
1786     68/push  "error: no space allowed after '*' in '"/imm32
1787     68/push  Stderr/imm32
1788     # . . call
1789     e8/call  write-buffered/disp32
1790     # . . discard args
1791     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1792     # . write-stream-data(Stderr, line)
1793     # . . push args
1794     56/push-esi
1795     68/push  Stderr/imm32
1796     # . . call
1797     e8/call  write-stream-data/disp32
1798     # . . discard args
1799     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1800     # . write-buffered(Stderr, "'")
1801     # . . push args
1802     68/push  "'"/imm32
1803     68/push  Stderr/imm32
1804     # . . call
1805     e8/call  write-buffered/disp32
1806     # . . discard args
1807     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1808     # . flush(Stderr)
1809     # . . push args
1810     68/push  Stderr/imm32
1811     # . . call
1812     e8/call  flush/disp32
1813     # . . discard args
1814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1815     # . syscall(exit, 1)
1816     bb/copy-to-ebx  1/imm32
1817     b8/copy-to-eax  1/imm32/exit
1818     cd/syscall  0x80/imm8
1819     # never gets here
1820 
1821 $next-word-or-expression:error2:
1822     # print(stderr, "error: no space allowed after '*' in '" line "'")
1823     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
1824     # . . push args
1825     68/push  "error: *(...) expression must be all on a single line in '"/imm32
1826     68/push  Stderr/imm32
1827     # . . call
1828     e8/call  write-buffered/disp32
1829     # . . discard args
1830     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1831     # . write-stream-data(Stderr, line)
1832     # . . push args
1833     56/push-esi
1834     68/push  Stderr/imm32
1835     # . . call
1836     e8/call  write-stream-data/disp32
1837     # . . discard args
1838     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1839     # . write-buffered(Stderr, "'")
1840     # . . push args
1841     68/push  "'"/imm32
1842     68/push  Stderr/imm32
1843     # . . call
1844     e8/call  write-buffered/disp32
1845     # . . discard args
1846     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1847     # . flush(Stderr)
1848     # . . push args
1849     68/push  Stderr/imm32
1850     # . . call
1851     e8/call  flush/disp32
1852     # . . discard args
1853     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1854     # . syscall(exit, 1)
1855     bb/copy-to-ebx  1/imm32
1856     b8/copy-to-eax  1/imm32/exit
1857     cd/syscall  0x80/imm8
1858     # never gets here
1859 
1860 test-next-word-or-expression:
1861     # . prolog
1862     55/push-ebp
1863     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1864     # setup
1865     # . clear-stream(_test-input-stream)
1866     # . . push args
1867     68/push  _test-input-stream/imm32
1868     # . . call
1869     e8/call  clear-stream/disp32
1870     # . . discard args
1871     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1872     # var slice/ecx = {0, 0}
1873     68/push  0/imm32/end
1874     68/push  0/imm32/start
1875     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1876     # write(_test-input-stream, "  ab")
1877     # . . push args
1878     68/push  "  ab"/imm32
1879     68/push  _test-input-stream/imm32
1880     # . . call
1881     e8/call  write/disp32
1882     # . . discard args
1883     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1884     # next-word-or-expression(_test-input-stream, slice)
1885     # . . push args
1886     51/push-ecx
1887     68/push  _test-input-stream/imm32
1888     # . . call
1889     e8/call  next-word-or-expression/disp32
1890     # . . discard args
1891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1892     # check-ints-equal(_test-input-stream->read, 4, msg)
1893     # . . push args
1894     68/push  "F - test-next-word-or-expression/updates-stream-read-correctly"/imm32
1895     68/push  4/imm32
1896     b8/copy-to-eax  _test-input-stream/imm32
1897     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1898     # . . call
1899     e8/call  check-ints-equal/disp32
1900     # . . discard args
1901     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1902     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1903     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1904     # . . push args
1905     68/push  "F - test-next-word-or-expression: start"/imm32
1906     68/push  0xe/imm32
1907     # . . push slice->start - _test-input-stream
1908     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1909     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1910     50/push-eax
1911     # . . call
1912     e8/call  check-ints-equal/disp32
1913     # . . discard args
1914     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1915     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1916     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1917     # . . push args
1918     68/push  "F - test-next-word-or-expression: end"/imm32
1919     68/push  0x10/imm32
1920     # . . push slice->end - _test-input-stream
1921     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
1922     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1923     50/push-eax
1924     # . . call
1925     e8/call  check-ints-equal/disp32
1926     # . . discard args
1927     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1928     # . epilog
1929     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1930     5d/pop-to-ebp
1931     c3/return
1932 
1933 test-next-word-or-expression-returns-whole-comment:
1934     # . prolog
1935     55/push-ebp
1936     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1937     # setup
1938     # . clear-stream(_test-input-stream)
1939     # . . push args
1940     68/push  _test-input-stream/imm32
1941     # . . call
1942     e8/call  clear-stream/disp32
1943     # . . discard args
1944     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1945     # var slice/ecx = {0, 0}
1946     68/push  0/imm32/end
1947     68/push  0/imm32/start
1948     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1949     # write(_test-input-stream, "  # a")
1950     # . . push args
1951     68/push  "  # a"/imm32
1952     68/push  _test-input-stream/imm32
1953     # . . call
1954     e8/call  write/disp32
1955     # . . discard args
1956     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1957     # next-word-or-expression(_test-input-stream, slice)
1958     # . . push args
1959     51/push-ecx
1960     68/push  _test-input-stream/imm32
1961     # . . call
1962     e8/call  next-word-or-expression/disp32
1963     # . . discard args
1964     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1965     # check-ints-equal(_test-input-stream->read, 5, msg)
1966     # . . push args
1967     68/push  "F - test-next-word-or-expression-returns-whole-comment/updates-stream-read-correctly"/imm32
1968     68/push  5/imm32
1969     b8/copy-to-eax  _test-input-stream/imm32
1970     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1971     # . . call
1972     e8/call  check-ints-equal/disp32
1973     # . . discard args
1974     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1975     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1976     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1977     # . . push args
1978     68/push  "F - test-next-word-or-expression-returns-whole-comment: start"/imm32
1979     68/push  0xe/imm32
1980     # . . push slice->start - _test-input-stream
1981     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1982     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1983     50/push-eax
1984     # . . call
1985     e8/call  check-ints-equal/disp32
1986     # . . discard args
1987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1988     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
1989     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
1990     # . . push args
1991     68/push  "F - test-next-word-or-expression-returns-whole-comment: end"/imm32
1992     68/push  0x11/imm32
1993     # . . push slice->end - _test-input-stream
1994     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
1995     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1996     50/push-eax
1997     # . . call
1998     e8/call  check-ints-equal/disp32
1999     # . . discard args
2000     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2001     # . epilog
2002     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2003     5d/pop-to-ebp
2004     c3/return
2005 
2006 test-next-word-or-expression-returns-empty-slice-on-eof:
2007     # . prolog
2008     55/push-ebp
2009     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2010     # setup
2011     # . clear-stream(_test-input-stream)
2012     # . . push args
2013     68/push  _test-input-stream/imm32
2014     # . . call
2015     e8/call  clear-stream/disp32
2016     # . . discard args
2017     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2018     # var slice/ecx = {0, 0}
2019     68/push  0/imm32/end
2020     68/push  0/imm32/start
2021     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2022     # write nothing to _test-input-stream
2023     # next-word-or-expression(_test-input-stream, slice)
2024     # . . push args
2025     51/push-ecx
2026     68/push  _test-input-stream/imm32
2027     # . . call
2028     e8/call  next-word-or-expression/disp32
2029     # . . discard args
2030     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2031     # check-ints-equal(slice->end - slice->start, 0, msg)
2032     # . . push args
2033     68/push  "F - test-next-word-or-expression-returns-empty-expression-on-eof"/imm32
2034     68/push  0/imm32
2035     # . . push slice->end - slice->start
2036     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2037     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
2038     50/push-eax
2039     # . . call
2040     e8/call  check-ints-equal/disp32
2041     # . . discard args
2042     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2043     # . epilog
2044     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2045     5d/pop-to-ebp
2046     c3/return
2047 
2048 test-next-word-or-expression-returns-string-literal:
2049     # . prolog
2050     55/push-ebp
2051     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2052     # setup
2053     # . clear-stream(_test-input-stream)
2054     # . . push args
2055     68/push  _test-input-stream/imm32
2056     # . . call
2057     e8/call  clear-stream/disp32
2058     # . . discard args
2059     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2060     # var slice/ecx = {0, 0}
2061     68/push  0/imm32/end
2062     68/push  0/imm32/start
2063     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2064     # write(_test-input-stream, " \"a b\"/imm32 ")
2065     # . . push args
2066     68/push  " \"a b\"/imm32 "/imm32
2067     68/push  _test-input-stream/imm32
2068     # . . call
2069     e8/call  write/disp32
2070     # . . discard args
2071     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2072     # next-word-or-expression(_test-input-stream, slice)
2073     # . . push args
2074     51/push-ecx
2075     68/push  _test-input-stream/imm32
2076     # . . call
2077     e8/call  next-word-or-expression/disp32
2078     # . . discard args
2079     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2080     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2081     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2082     # . . push args
2083     68/push  "F - test-next-word-or-expression-returns-string-literal: start"/imm32
2084     68/push  0xd/imm32
2085     # . . push slice->start - _test-input-stream
2086     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2087     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2088     50/push-eax
2089     # . . call
2090     e8/call  check-ints-equal/disp32
2091     # . . discard args
2092     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2093     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
2094     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
2095     # . . push args
2096     68/push  "F - test-next-word-or-expression-returns-string-literal: end"/imm32
2097     68/push  0x18/imm32
2098     # . . push slice->end - _test-input-stream
2099     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2100     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2101     50/push-eax
2102     # . . call
2103     e8/call  check-ints-equal/disp32
2104     # . . discard args
2105     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2106     # . epilog
2107     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2108     5d/pop-to-ebp
2109     c3/return
2110 
2111 test-next-word-or-expression-returns-string-with-escapes:
2112     # . prolog
2113     55/push-ebp
2114     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2115     # setup
2116     # . clear-stream(_test-input-stream)
2117     # . . push args
2118     68/push  _test-input-stream/imm32
2119     # . . call
2120     e8/call  clear-stream/disp32
2121     # . . discard args
2122     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2123     # var slice/ecx = {0, 0}
2124     68/push  0/imm32/end
2125     68/push  0/imm32/start
2126     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2127     # write(_test-input-stream, " \"a\\\"b\"/x")
2128     # . . push args
2129     68/push  " \"a\\\"b\"/x"/imm32
2130     68/push  _test-input-stream/imm32
2131     # . . call
2132     e8/call  write/disp32
2133     # . . discard args
2134     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2135     # next-word-or-expression(_test-input-stream, slice)
2136     # . . push args
2137     51/push-ecx
2138     68/push  _test-input-stream/imm32
2139     # . . call
2140     e8/call  next-word-or-expression/disp32
2141     # . . discard args
2142     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2143     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2144     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2145     # . . push args
2146     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: start"/imm32
2147     68/push  0xd/imm32
2148     # . . push slice->start - _test-input-stream
2149     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2150     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2151     50/push-eax
2152     # . . call
2153     e8/call  check-ints-equal/disp32
2154     # . . discard args
2155     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2156     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
2157     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
2158     # . . push args
2159     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: end"/imm32
2160     68/push  0x15/imm32
2161     # . . push slice->end - _test-input-stream
2162     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2163     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2164     50/push-eax
2165     # . . call
2166     e8/call  check-ints-equal/disp32
2167     # . . discard args
2168     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2169     # . epilog
2170     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2171     5d/pop-to-ebp
2172     c3/return
2173 
2174 test-next-word-or-expression-returns-whole-expression:
2175     # . prolog
2176     55/push-ebp
2177     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2178     # setup
2179     # . clear-stream(_test-input-stream)
2180     # . . push args
2181     68/push  _test-input-stream/imm32
2182     # . . call
2183     e8/call  clear-stream/disp32
2184     # . . discard args
2185     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2186     # var slice/ecx = {0, 0}
2187     68/push  0/imm32/end
2188     68/push  0/imm32/start
2189     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2190     # write(_test-input-stream, " *(a b)/imm32 ")
2191     # . . push args
2192     68/push  " *(a b)/imm32 "/imm32
2193     68/push  _test-input-stream/imm32
2194     # . . call
2195     e8/call  write/disp32
2196     # . . discard args
2197     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2198     # next-word-or-expression(_test-input-stream, slice)
2199     # . . push args
2200     51/push-ecx
2201     68/push  _test-input-stream/imm32
2202     # . . call
2203     e8/call  next-word-or-expression/disp32
2204     # . . discard args
2205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2206     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2207     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2208     # . . push args
2209     68/push  "F - test-next-word-or-expression-returns-whole-expression: start"/imm32
2210     68/push  0xd/imm32
2211     # . . push slice->start - _test-input-stream
2212     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2213     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2214     50/push-eax
2215     # . . call
2216     e8/call  check-ints-equal/disp32
2217     # . . discard args
2218     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2219     # check-ints-equal(slice->end - _test-input-stream->data, 13, msg)
2220     # . check-ints-equal(slice->end - _test-input-stream, 25, msg)
2221     # . . push args
2222     68/push  "F - test-next-word-or-expression-returns-whole-expression: end"/imm32
2223     68/push  0x19/imm32
2224     # . . push slice->end - _test-input-stream
2225     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2226     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2227     50/push-eax
2228     # . . call
2229     e8/call  check-ints-equal/disp32
2230     # . . discard args
2231     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2232     # . epilog
2233     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2234     5d/pop-to-ebp
2235     c3/return
2236 
2237 # Grammar:
2238 #   *reg                    -> 0/mod reg/rm32
2239 #   *(reg)                  -> 0/mod reg/rm32
2240 #   *(reg+disp)             -> 2/mod reg/rm32 disp/disp32
2241 #   *(reg1+reg2<<s)         -> 2/mod 4/rm32 reg1/base reg2/index s/scale 0/disp32
2242 #   *(reg1+reg2<<s+disp)    -> 2/mod 4/rm32 reg1/base reg2/index s/scale disp/disp32
2243 # Intermediate structure: base, index, scale, disp
2244 # Default values: base: 0, index: 4 (none), scale: 0, disp: 0
2245 parse-effective-address:  # word-slice : (address slice) -> base/eax, index/ecx, scale/edx, disp/ebx
2246     # pseudocode:
2247     #   var local-slice = {word-slice->start, word-slice->end}
2248     #   ++local-slice->start to skip '*'
2249     #   initialize defaults: base=0, index=4, scale=0, disp=0
2250     #   if (*local-slice->start != '(') {
2251     #     local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
2252     #     base = get-slice(Registers, local-slice, row-size=8)
2253     #     return
2254     #   }
2255     #   # compound expressions
2256     #   skip whitespace
2257     #   read register into base
2258     #   skip whitespace
2259     #   if (*local-slice->start == ')') goto end
2260     #   if (*local-slice->start == '-') goto displacement
2261     #   if (*local-slice->start != '+') goto error1
2262     #   ++local-slice->start to skip '+'
2263     #   skip whitespace
2264     #   if next 3 characters don't make a register, goto displacement
2265     #   read register into index
2266     #   skip whitespace
2267     #   if (*local-slice->start == ')') goto end
2268     #   if (*local-slice->start == '<') {
2269     #     ++local-slice->start to skip '<'
2270     #     if (*local-slice->start != '<') goto error2
2271     #     ++local-slice->start to skip '<'
2272     #     skip whitespace
2273     #     read integer into scale
2274     #     skip whitespace
2275     #     if (*local-slice->start == ')') goto end
2276     #   }
2277     #   if (*local-slice->start not in '+' '-') goto error3
2278     # displacement:
2279     #   read integer into disp
2280     #   skip whitespace
2281     #   if (*local-slice->start != ')') goto error4
2282     # . prolog
2283     55/push-ebp
2284     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2285     # . save registers
2286     56/push-esi
2287     57/push-edi
2288     # var local-slice/esi : (address slice) = {word-slice->start, word-slice->end}
2289     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2290     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2291     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2292     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
2293     # ++local-slice->start to skip '*'
2294     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2295     # initialize defaults
2296     # base is in edi; we'll move it to eax just before we return
2297     bf/copy-to-edi  0/imm32
2298     b9/copy-to-ecx  4/imm32/no-index
2299     ba/copy-to-edx  0/imm32/.scale
2300     bb/copy-to-ebx  0/imm32/disp
2301 $parse-effective-address:check-for-simple-register:
2302     # if (*local-slice->start == '(') goto compound expression
2303     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2304     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2305     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2306     3d/compare-eax-and  0x28/imm32/open-paren
2307     74/jump-if-equal  $parse-effective-address:compound-expression/disp8
2308 $parse-effective-address:simple-register:
2309     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
2310     # . . push args
2311     56/push-esi
2312     68/push  0x2f/imm32/slash
2313     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2314     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2315     # . . call
2316     e8/call  next-token-from-slice/disp32
2317     # . . discard args
2318     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2319     # base = get-slice(Registers, local-slice, row-size=8)
2320     # . eax = get-slice(Registers, local-slice, row-size=8)
2321     # . . push args
2322     68/push  "Registers"/imm32
2323     68/push  8/imm32/row-size
2324     56/push-esi
2325     68/push  Registers/imm32
2326     # . . call
2327     e8/call  get-slice/disp32
2328     # . . discard args
2329     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2330     # . base = *eax
2331     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy *eax to edi
2332     # return
2333     e9/jump  $parse-effective-address:end/disp32
2334 $parse-effective-address:compound-expression:
2335     # ++local-slice->start to skip '('
2336     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2337     # skip whitespace
2338     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2339     # . . push args
2340     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2341     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2342     # . . call
2343     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2344     # . . discard args
2345     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2346     # . local-slice->start = eax
2347     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2348     # read register into base
2349     # . eax = next-register(local-slice)
2350     # . . push args
2351     56/push-esi
2352     # . . call
2353     e8/call  next-register/disp32
2354     # . . discard args
2355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2356     # . edi = *eax
2357     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy *eax to edi
2358     # skip whitespace
2359     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2360     # . . push args
2361     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2362     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2363     # . . call
2364     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2365     # . . discard args
2366     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2367     # . local-slice->start = eax
2368     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2369     # if (*local-slice->start == ')') goto end
2370     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2371     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2372     3d/compare-eax-and  0x29/imm32/close-paren
2373     0f 84/jump-if-equal  $parse-effective-address:end/disp32
2374     # if (*local-slice->start == '-') goto displacement
2375     3d/compare-eax-and  0x2d/imm32/minus
2376     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
2377     # if (*local-slice->start != '+') goto error1
2378     3d/compare-eax-and  0x2b/imm32/plus
2379     0f 85/jump-if-not-equal  $parse-effective-address:error1/disp32
2380 $parse-effective-address:check-for-index:
2381     # ++local-slice->start to skip '+'
2382     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2383     # skip whitespace
2384     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2385     # . . push args
2386     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2387     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2388     # . . call
2389     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2390     # . . discard args
2391     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2392     # . local-slice->start = eax
2393     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2394 $parse-effective-address:resolve-ambiguity:
2395     # if next 3 characters don't make a register, goto displacement
2396     # . spill ecx
2397     51/push-ecx
2398     # . var tmp/ecx = {local-slice->start, local-slice->start+3}
2399     # . . ecx = local-slice->start
2400     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
2401     # . . eax = local-slice->start+3
2402     05/add-to-eax  3/imm32
2403     # . . push
2404     50/push-eax
2405     51/push-ecx
2406     # . . copy esp to ecx
2407     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2408     # . eax = maybe-get-slice(Register, tmp, row-size=8)
2409     # . . push args
2410     68/push  8/imm32/row-size
2411     51/push-ecx
2412     68/push  Registers/imm32
2413     # . . call
2414     e8/call  maybe-get-slice/disp32
2415     # . . discard args
2416     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2417     # . reclaim tmp
2418     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2419     # . restore ecx
2420     59/pop-to-ecx
2421     # . if (eax == 0) goto displacement
2422     3d/compare-eax-and  0/imm32
2423     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
2424 $parse-effective-address:index:
2425     # read register into index
2426     # . eax = next-register(local-slice)
2427     # . . push args
2428     56/push-esi
2429     # . . call
2430     e8/call  next-register/disp32
2431     # . . discard args
2432     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2433     # . ecx = *eax
2434     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2435     # skip whitespace
2436     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2437     # . . push args
2438     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2439     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2440     # . . call
2441     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2442     # . . discard args
2443     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2444     # . local-slice->start = eax
2445     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2446     # if (*local-slice->start == ')') goto end
2447     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2448     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2449     3d/compare-eax-and  0x29/imm32/close-paren
2450     0f 84/jump-if-equal  $parse-effective-address:end/disp32
2451 $parse-effective-address:check-for-scale:
2452     # if (*local-slice->start != '<') goto next check
2453     3d/compare-eax-and  0x3c/imm32/less-than
2454     75/jump-if-not-equal  $parse-effective-address:check-for-displacement/disp8
2455     # ++local-slice->start to skip '<'
2456     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2457     # if (*local-slice->start != '<') goto error2
2458     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2459     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2460     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2461     3d/compare-eax-and  0x3c/imm32/less-than
2462     0f 85/jump-if-not-equal  $parse-effective-address:error2/disp32
2463     # ++local-slice->start to skip '<'
2464     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2465     # skip whitespace
2466     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2467     # . . push args
2468     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2469     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2470     # . . call
2471     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2472     # . . discard args
2473     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2474     # . local-slice->start = eax
2475     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2476 $parse-effective-address:scale:
2477     # read positive integer into scale
2478     # . eax = next-positive-hex-int(local-slice)
2479     # . . push args
2480     56/push-esi
2481     # . . call
2482     e8/call  next-positive-hex-int/disp32
2483     # . . discard args
2484     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2485     # . edx = eax
2486     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
2487     # skip whitespace
2488     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2489     # . . push args
2490     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2491     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2492     # . . call
2493     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2494     # . . discard args
2495     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2496     # . local-slice->start = eax
2497     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2498     # if (*local-slice->start == ')') goto end
2499     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2500     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2501     3d/compare-eax-and  0x29/imm32/close-paren
2502     74/jump-if-equal  $parse-effective-address:end/disp8
2503 $parse-effective-address:check-for-displacement:
2504     # if (*local-slice->start not in '+' '-') goto error3
2505     3d/compare-eax-and  0x2b/imm32/plus
2506     74/jump-if-equal  $parse-effective-address:displacement/disp8
2507     3d/compare-eax-and  0x2d/imm32/minus
2508     74/jump-if-equal  $parse-effective-address:displacement/disp8
2509     e9/jump  $parse-effective-address:error3/disp32
2510 $parse-effective-address:displacement:
2511     # read integer into disp
2512     # . eax = next-hex-int(local-slice)
2513     # . . push args
2514     56/push-esi
2515     # . . call
2516     e8/call  next-hex-int/disp32
2517     # . . discard args
2518     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2519     # . ebx = eax
2520     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2521     # skip whitespace
2522     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2523     # . . push args
2524     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2525     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2526     # . . call
2527     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2528     # . . discard args
2529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2530     # . local-slice->start = eax
2531     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2532     # if (*local-slice->start != ')') goto error4
2533     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2534     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2535     3d/compare-eax-and  0x29/imm32/close-paren
2536     0f 85/jump-if-not-equal  $parse-effective-address:error4/disp32
2537 $parse-effective-address:end:
2538     # return base in eax
2539     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
2540     # reclaim locals
2541     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2542     # . restore registers
2543     5f/pop-to-edi
2544     5e/pop-to-esi
2545     # . epilog
2546     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2547     5d/pop-to-ebp
2548     c3/return
2549 
2550 $parse-effective-address:error1:
2551     # print(stderr, "error: unexpected character: " eax "\n")
2552     # . write-buffered(Stderr, "error: unexpected character: ")
2553     # . . push args
2554     68/push  "error: unexpected character: "/imm32
2555     68/push  Stderr/imm32
2556     # . . call
2557     e8/call  write-buffered/disp32
2558     # . . discard args
2559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2560     # . print-int32-buffered(out, eax)
2561     # . . push args
2562     50/push-eax
2563     68/push  Stderr/imm32
2564     # . . call
2565     e8/call  print-int32-buffered/disp32
2566     # . . discard args
2567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2568     # . write-buffered(Stderr, "\n")
2569     # . . push args
2570     68/push  "\n"/imm32
2571     68/push  Stderr/imm32
2572     # . . call
2573     e8/call  write-buffered/disp32
2574     # . . discard args
2575     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2576     # . flush(Stderr)
2577     # . . push args
2578     68/push  Stderr/imm32
2579     # . . call
2580     e8/call  flush/disp32
2581     # . . discard args
2582     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2583     # . syscall(exit, 1)
2584     bb/copy-to-ebx  1/imm32
2585     b8/copy-to-eax  1/imm32/exit
2586     cd/syscall  0x80/imm8
2587     # never gets here
2588 
2589 $parse-effective-address:error2:
2590     # print(stderr, "error: '<' can only be followed by '<' but got: " eax "\n")
2591     # . write-buffered(Stderr, "error: '<' can only be followed by '<' but got: ")
2592     # . . push args
2593     68/push  "error: '<' can only be followed by '<' but got: "/imm32
2594     68/push  Stderr/imm32
2595     # . . call
2596     e8/call  write-buffered/disp32
2597     # . . discard args
2598     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2599     # . print-int32-buffered(out, eax)
2600     # . . push args
2601     50/push-eax
2602     68/push  Stderr/imm32
2603     # . . call
2604     e8/call  print-int32-buffered/disp32
2605     # . . discard args
2606     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2607     # . write-buffered(Stderr, "\n")
2608     # . . push args
2609     68/push  "\n"/imm32
2610     68/push  Stderr/imm32
2611     # . . call
2612     e8/call  write-buffered/disp32
2613     # . . discard args
2614     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2615     # . flush(Stderr)
2616     # . . push args
2617     68/push  Stderr/imm32
2618     # . . call
2619     e8/call  flush/disp32
2620     # . . discard args
2621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2622     # . syscall(exit, 1)
2623     bb/copy-to-ebx  1/imm32
2624     b8/copy-to-eax  1/imm32/exit
2625     cd/syscall  0x80/imm8
2626     # never gets here
2627 
2628 $parse-effective-address:error3:
2629     # print(stderr, "error: unexpected character before displacement: " eax "\n")
2630     # . write-buffered(Stderr, "error: unexpected character before displacement: ")
2631     # . . push args
2632     68/push  "error: unexpected character before displacement: "/imm32
2633     68/push  Stderr/imm32
2634     # . . call
2635     e8/call  write-buffered/disp32
2636     # . . discard args
2637     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2638     # . print-int32-buffered(out, eax)
2639     # . . push args
2640     50/push-eax
2641     68/push  Stderr/imm32
2642     # . . call
2643     e8/call  print-int32-buffered/disp32
2644     # . . discard args
2645     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2646     # . write-buffered(Stderr, "\n")
2647     # . . push args
2648     68/push  "\n"/imm32
2649     68/push  Stderr/imm32
2650     # . . call
2651     e8/call  write-buffered/disp32
2652     # . . discard args
2653     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2654     # . flush(Stderr)
2655     # . . push args
2656     68/push  Stderr/imm32
2657     # . . call
2658     e8/call  flush/disp32
2659     # . . discard args
2660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2661     # . syscall(exit, 1)
2662     bb/copy-to-ebx  1/imm32
2663     b8/copy-to-eax  1/imm32/exit
2664     cd/syscall  0x80/imm8
2665     # never gets here
2666 
2667 $parse-effective-address:error4:
2668     # print(stderr, "error: unexpected character after displacement: " eax "; expected ')' to wrap up\n")
2669     # . write-buffered(Stderr, "error: unexpected character after displacement: ")
2670     # . . push args
2671     68/push  "error: unexpected character after displacement: "/imm32
2672     68/push  Stderr/imm32
2673     # . . call
2674     e8/call  write-buffered/disp32
2675     # . . discard args
2676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2677     # . print-int32-buffered(out, eax)
2678     # . . push args
2679     50/push-eax
2680     68/push  Stderr/imm32
2681     # . . call
2682     e8/call  print-int32-buffered/disp32
2683     # . . discard args
2684     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2685     # . write-buffered(Stderr, "; expected ')' to wrap up\n")
2686     # . . push args
2687     68/push  "; expected ')' to wrap up\n"/imm32
2688     68/push  Stderr/imm32
2689     # . . call
2690     e8/call  write-buffered/disp32
2691     # . . discard args
2692     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2693     # . flush(Stderr)
2694     # . . push args
2695     68/push  Stderr/imm32
2696     # . . call
2697     e8/call  flush/disp32
2698     # . . discard args
2699     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2700     # . syscall(exit, 1)
2701     bb/copy-to-ebx  1/imm32
2702     b8/copy-to-eax  1/imm32/exit
2703     cd/syscall  0x80/imm8
2704     # never gets here
2705 
2706 # assumes 'in' starts with a register name, and returns pointer to its code
2707 # side-effect: modifies 'in' to scan past the initial register name
2708 next-register:  # in : (address slice) -> reg/eax : int
2709     # . prolog
2710     55/push-ebp
2711     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2712     # . save registers
2713     51/push-ecx
2714     56/push-esi
2715     # esi = in
2716     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2717     # var reg-slice/ecx : (address slice) = {in->start, in->start + 3}
2718     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2719     05/add-to-eax  3/imm32
2720     50/push-eax
2721     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2722     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2723     # in->start += 3
2724     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               3/imm32           # add to *esi
2725     # eax = get-slice(Registers, reg-slice, row-size=8)
2726     # . . push args
2727     68/push  "next-register"/imm32
2728     68/push  8/imm32/row-size
2729     51/push-ecx
2730     68/push  Registers/imm32
2731     # . . call
2732     e8/call  get-slice/disp32
2733     # . . discard args
2734     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2735 $next-register:end:
2736     # reclaim locals
2737     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2738     # . restore registers
2739     5e/pop-to-esi
2740     59/pop-to-ecx
2741     # . epilog
2742     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2743     5d/pop-to-ebp
2744     c3/return
2745 
2746 test-parse-effective-address-simple:
2747     # . prolog
2748     55/push-ebp
2749     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2750     # var slice/ecx = "*esi"
2751     b8/copy-to-eax  "*esi"/imm32
2752     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2753     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
2754     05/add-to-eax  4/imm32
2755     # . ecx = {eax, ecx}
2756     51/push-ecx
2757     50/push-eax
2758     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2759     # eax, ecx, edx, ebx = parse-effective-address(slice)
2760     # . . push args
2761     51/push-ecx
2762     # . . call
2763     e8/call  parse-effective-address/disp32
2764     # . . discard args
2765     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2766     # slice clobbered beyond this point
2767     # check-ints-equal(eax, 6, msg)
2768     # . . push args
2769     68/push  "F - test-parse-effective-address-simple/base"/imm32
2770     68/push  6/imm32/esi
2771     50/push-eax
2772     # . . call
2773     e8/call  check-ints-equal/disp32
2774     # . . discard args
2775     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2776     # check-ints-equal(ecx, 4, msg)
2777     # . . push args
2778     68/push  "F - test-parse-effective-address-simple/index"/imm32
2779     68/push  4/imm32/none
2780     51/push-ecx
2781     # . . call
2782     e8/call  check-ints-equal/disp32
2783     # . . discard args
2784     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2785     # check-ints-equal(edx, 0, msg)
2786     # . . push args
2787     68/push  "F - test-parse-effective-address-simple/scale"/imm32
2788     68/push  0/imm32/none
2789     52/push-edx
2790     # . . call
2791     e8/call  check-ints-equal/disp32
2792     # . . discard args
2793     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2794     # check-ints-equal(ebx, 0, msg)
2795     # . . push args
2796     68/push  "F - test-parse-effective-address-simple/displacement"/imm32
2797     68/push  0/imm32/none
2798     53/push-ebx
2799     # . . call
2800     e8/call  check-ints-equal/disp32
2801     # . . discard args
2802     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2803     # . epilog
2804     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2805     5d/pop-to-ebp
2806     c3/return
2807 
2808 test-parse-effective-address-base:
2809     # . prolog
2810     55/push-ebp
2811     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2812     # var slice/ecx = "*(esi  )"
2813     b8/copy-to-eax  "*(esi  )"/imm32
2814     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2815     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
2816     05/add-to-eax  4/imm32
2817     # . ecx = {eax, ecx}
2818     51/push-ecx
2819     50/push-eax
2820     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2821     # eax, ecx, edx, ebx = parse-effective-address(slice)
2822     # . . push args
2823     51/push-ecx
2824     # . . call
2825     e8/call  parse-effective-address/disp32
2826     # . . discard args
2827     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2828     # slice clobbered beyond this point
2829     # check-ints-equal(eax, 6, msg)
2830     # . . push args
2831     68/push  "F - test-parse-effective-address-base/base"/imm32
2832     68/push  6/imm32/esi
2833     50/push-eax
2834     # . . call
2835     e8/call  check-ints-equal/disp32
2836     # . . discard args
2837     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2838     # check-ints-equal(ecx, 4, msg)
2839     # . . push args
2840     68/push  "F - test-parse-effective-address-base/index"/imm32
2841     68/push  4/imm32/none
2842     51/push-ecx
2843     # . . call
2844     e8/call  check-ints-equal/disp32
2845     # . . discard args
2846     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2847     # check-ints-equal(edx, 0, msg)
2848     # . . push args
2849     68/push  "F - test-parse-effective-address-base/scale"/imm32
2850     68/push  0/imm32/none
2851     52/push-edx
2852     # . . call
2853     e8/call  check-ints-equal/disp32
2854     # . . discard args
2855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2856     # check-ints-equal(ebx, 0, msg)
2857     # . . push args
2858     68/push  "F - test-parse-effective-address-base/displacement"/imm32
2859     68/push  0/imm32/none
2860     53/push-ebx
2861     # . . call
2862     e8/call  check-ints-equal/disp32
2863     # . . discard args
2864     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2865     # . epilog
2866     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2867     5d/pop-to-ebp
2868     c3/return
2869 
2870 test-parse-effective-address-base-displacement:
2871     # . prolog
2872     55/push-ebp
2873     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2874     # var slice/ecx = "*(esi+3)"
2875     b8/copy-to-eax  "*(esi+3)"/imm32
2876     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2877     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
2878     05/add-to-eax  4/imm32
2879     # . ecx = {eax, ecx}
2880     51/push-ecx
2881     50/push-eax
2882     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2883     # eax, ecx, edx, ebx = parse-effective-address(slice)
2884     # . . push args
2885     51/push-ecx
2886     # . . call
2887     e8/call  parse-effective-address/disp32
2888     # . . discard args
2889     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2890     # slice clobbered beyond this point
2891     # check-ints-equal(eax, 6, msg)
2892     # . . push args
2893     68/push  "F - test-parse-effective-address-base-displacement/base"/imm32
2894     68/push  6/imm32/esi
2895     50/push-eax
2896     # . . call
2897     e8/call  check-ints-equal/disp32
2898     # . . discard args
2899     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2900     # check-ints-equal(ecx, 4, msg)
2901     # . . push args
2902     68/push  "F - test-parse-effective-address-base-displacement/index"/imm32
2903     68/push  4/imm32/none
2904     51/push-ecx
2905     # . . call
2906     e8/call  check-ints-equal/disp32
2907     # . . discard args
2908     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2909     # check-ints-equal(edx, 0, msg)
2910     # . . push args
2911     68/push  "F - test-parse-effective-address-base-displacement/scale"/imm32
2912     68/push  0/imm32/none
2913     52/push-edx
2914     # . . call
2915     e8/call  check-ints-equal/disp32
2916     # . . discard args
2917     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2918     # check-ints-equal(ebx, 3, msg)
2919     # . . push args
2920     68/push  "F - test-parse-effective-address-base-displacement/displacement"/imm32
2921     68/push  3/imm32
2922     53/push-ebx
2923     # . . call
2924     e8/call  check-ints-equal/disp32
2925     # . . discard args
2926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2927     # . epilog
2928     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2929     5d/pop-to-ebp
2930     c3/return
2931 
2932 test-parse-effective-address-base-negative-displacement:
2933     # . prolog
2934     55/push-ebp
2935     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2936     # var slice/ecx = "*(esi-3)"
2937     b8/copy-to-eax  "*(esi-3)"/imm32
2938     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2939     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
2940     05/add-to-eax  4/imm32
2941     # . ecx = {eax, ecx}
2942     51/push-ecx
2943     50/push-eax
2944     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2945     # eax, ecx, edx, ebx = parse-effective-address(slice)
2946     # . . push args
2947     51/push-ecx
2948     # . . call
2949     e8/call  parse-effective-address/disp32
2950     # . . discard args
2951     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2952     # slice clobbered beyond this point
2953     # check-ints-equal(eax, 6, msg)
2954     # . . push args
2955     68/push  "F - test-parse-effective-address-base-negative-displacement/base"/imm32
2956     68/push  6/imm32/esi
2957     50/push-eax
2958     # . . call
2959     e8/call  check-ints-equal/disp32
2960     # . . discard args
2961     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2962     # check-ints-equal(ecx, 4, msg)
2963     # . . push args
2964     68/push  "F - test-parse-effective-address-base-negative-displacement/index"/imm32
2965     68/push  4/imm32/none
2966     51/push-ecx
2967     # . . call
2968     e8/call  check-ints-equal/disp32
2969     # . . discard args
2970     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2971     # check-ints-equal(edx, 0, msg)
2972     # . . push args
2973     68/push  "F - test-parse-effective-address-base-negative-displacement/scale"/imm32
2974     68/push  0/imm32/none
2975     52/push-edx
2976     # . . call
2977     e8/call  check-ints-equal/disp32
2978     # . . discard args
2979     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2980     # check-ints-equal(ebx, -3, msg)
2981     # . . push args
2982     68/push  "F - test-parse-effective-address-base-negative-displacement/displacement"/imm32
2983     68/push  -3/imm32
2984     53/push-ebx
2985     # . . call
2986     e8/call  check-ints-equal/disp32
2987     # . . discard args
2988     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2989     # . epilog
2990     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2991     5d/pop-to-ebp
2992     c3/return
2993 
2994 test-parse-effective-address-base-index:
2995     # . prolog
2996     55/push-ebp
2997     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2998     # var slice/ecx = "*(esi+ecx)"
2999     b8/copy-to-eax  "*(esi+ecx)"/imm32
3000     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3001     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
3002     05/add-to-eax  4/imm32
3003     # . ecx = {eax, ecx}
3004     51/push-ecx
3005     50/push-eax
3006     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3007     # eax, ecx, edx, ebx = parse-effective-address(slice)
3008     # . . push args
3009     51/push-ecx
3010     # . . call
3011     e8/call  parse-effective-address/disp32
3012     # . . discard args
3013     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3014     # slice clobbered beyond this point
3015     # check-ints-equal(eax, 6, msg)
3016     # . . push args
3017     68/push  "F - test-parse-effective-address-base-index/base"/imm32
3018     68/push  6/imm32/esi
3019     50/push-eax
3020     # . . call
3021     e8/call  check-ints-equal/disp32
3022     # . . discard args
3023     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3024     # check-ints-equal(ecx, 1, msg)
3025     # . . push args
3026     68/push  "F - test-parse-effective-address-base-index/index"/imm32
3027     68/push  1/imm32/none
3028     51/push-ecx
3029     # . . call
3030     e8/call  check-ints-equal/disp32
3031     # . . discard args
3032     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3033     # check-ints-equal(edx, 0, msg)
3034     # . . push args
3035     68/push  "F - test-parse-effective-address-base-index/scale"/imm32
3036     68/push  0/imm32/none
3037     52/push-edx
3038     # . . call
3039     e8/call  check-ints-equal/disp32
3040     # . . discard args
3041     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3042     # check-ints-equal(ebx, 0, msg)
3043     # . . push args
3044     68/push  "F - test-parse-effective-address-base-index/displacement"/imm32
3045     68/push  0/imm32
3046     53/push-ebx
3047     # . . call
3048     e8/call  check-ints-equal/disp32
3049     # . . discard args
3050     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3051     # . epilog
3052     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3053     5d/pop-to-ebp
3054     c3/return
3055 
3056 test-parse-effective-address-base-index-scale:
3057     # . prolog
3058     55/push-ebp
3059     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3060     # var slice/ecx = "*(esi+ecx<<2)"
3061     b8/copy-to-eax  "*(esi+ecx<<2)"/imm32
3062     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3063     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
3064     05/add-to-eax  4/imm32
3065     # . ecx = {eax, ecx}
3066     51/push-ecx
3067     50/push-eax
3068     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3069     # eax, ecx, edx, ebx = parse-effective-address(slice)
3070     # . . push args
3071     51/push-ecx
3072     # . . call
3073     e8/call  parse-effective-address/disp32
3074     # . . discard args
3075     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3076     # slice clobbered beyond this point
3077     # check-ints-equal(eax, 6, msg)
3078     # . . push args
3079     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
3080     68/push  6/imm32/esi
3081     50/push-eax
3082     # . . call
3083     e8/call  check-ints-equal/disp32
3084     # . . discard args
3085     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3086     # check-ints-equal(ecx, 1, msg)
3087     # . . push args
3088     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
3089     68/push  1/imm32/none
3090     51/push-ecx
3091     # . . call
3092     e8/call  check-ints-equal/disp32
3093     # . . discard args
3094     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3095     # check-ints-equal(edx, 2, msg)
3096     # . . push args
3097     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
3098     68/push  2/imm32
3099     52/push-edx
3100     # . . call
3101     e8/call  check-ints-equal/disp32
3102     # . . discard args
3103     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3104     # check-ints-equal(ebx, 0, msg)
3105     # . . push args
3106     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
3107     68/push  0/imm32
3108     53/push-ebx
3109     # . . call
3110     e8/call  check-ints-equal/disp32
3111     # . . discard args
3112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3113     # . epilog
3114     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3115     5d/pop-to-ebp
3116     c3/return
3117 
3118 test-parse-effective-address-base-index-scale-displacement:
3119     # . prolog
3120     55/push-ebp
3121     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3122     # var slice/ecx = "*(esi + ecx<<2 - 0x34)"
3123     b8/copy-to-eax  "*(esi + ecx<<2 - 0x34)"/imm32
3124     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3125     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
3126     05/add-to-eax  4/imm32
3127     # . ecx = {eax, ecx}
3128     51/push-ecx
3129     50/push-eax
3130     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3131     # eax, ecx, edx, ebx = parse-effective-address(slice)
3132     # . . push args
3133     51/push-ecx
3134     # . . call
3135     e8/call  parse-effective-address/disp32
3136     # . . discard args
3137     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3138     # slice clobbered beyond this point
3139     # check-ints-equal(eax, 6, msg)
3140     # . . push args
3141     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
3142     68/push  6/imm32/esi
3143     50/push-eax
3144     # . . call
3145     e8/call  check-ints-equal/disp32
3146     # . . discard args
3147     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3148     # check-ints-equal(ecx, 1, msg)
3149     # . . push args
3150     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
3151     68/push  1/imm32/none
3152     51/push-ecx
3153     # . . call
3154     e8/call  check-ints-equal/disp32
3155     # . . discard args
3156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3157     # check-ints-equal(edx, 2, msg)
3158     # . . push args
3159     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
3160     68/push  2/imm32
3161     52/push-edx
3162     # . . call
3163     e8/call  check-ints-equal/disp32
3164     # . . discard args
3165     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3166     # check-ints-equal(ebx, -0x34, msg)
3167     # . . push args
3168     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
3169     68/push  -0x34/imm32
3170     53/push-ebx
3171     # . . call
3172     e8/call  check-ints-equal/disp32
3173     # . . discard args
3174     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3175     # . epilog
3176     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3177     5d/pop-to-ebp
3178     c3/return
3179 
3180 # Code generation:
3181 #   if base is esp, then goto emit-sib
3182 #   if base is ebp, then goto emit-sib
3183 #   if index is none and disp is 0, then mod = 0 and rm32 = base
3184 #   if index is none, then mod = 2 and rm32 = base and disp32 = disp
3185 # emit-sib:
3186 #   if index is not none, then mod = 2 and rm32 = 4 and base = base and index = index and disp32 = disp
3187 emit-indirect-mode:  # out : (address buffered-file), base : int, index : int, scale : int, disp : int
3188     # . prolog
3189     55/push-ebp
3190     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3191 $emit-indirect-mode:check-for-ebp:
3192     # if (base == 5) goto emit-sib
3193     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       5/imm32           # compare *(ebp+12)
3194     74/jump-if-equal  $emit-indirect-mode:emit-sib/disp8
3195 $emit-indirect-mode:check-for-esp:
3196     # if (base == 4) goto emit-sib
3197     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       4/imm32           # compare *(ebp+12)
3198     74/jump-if-equal  $emit-indirect-mode:emit-sib/disp8
3199 $emit-indirect-mode:check-for-sib:
3200     # if (index == 4/none) goto next check
3201     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      4/imm32           # compare *(ebp+16)
3202     0f 84/jump-if-equal  $emit-indirect-mode:check-for-disp/disp32
3203 $emit-indirect-mode:emit-sib:
3204     # emit(out, "2/mod/indirect 4/rm32/sib " base "/base " index "/index " scale "/scale " disp "/disp32")
3205     # . write-buffered(out, "2/mod/*+disp32 4/rm32/sib ")
3206     # . . push args
3207     68/push  "2/mod/*+disp32 4/rm32/sib "/imm32
3208     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3209     # . . call
3210     e8/call  write-buffered/disp32
3211     # . . discard args
3212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3213     # . print-int32-buffered(out, base)
3214     # . . push args
3215     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3216     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3217     # . . call
3218     e8/call  print-int32-buffered/disp32
3219     # . . discard args
3220     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3221     # . write-buffered(out, "/base ")
3222     # . . push args
3223     68/push  "/base "/imm32
3224     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3225     # . . call
3226     e8/call  write-buffered/disp32
3227     # . . discard args
3228     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3229     # . print-int32-buffered(out, index)
3230     # . . push args
3231     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3232     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3233     # . . call
3234     e8/call  print-int32-buffered/disp32
3235     # . . discard args
3236     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3237     # . write-buffered(out, "/index ")
3238     # . . push args
3239     68/push  "/index "/imm32
3240     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3241     # . . call
3242     e8/call  write-buffered/disp32
3243     # . . discard args
3244     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3245     # . print-int32-buffered(out, scale)
3246     # . . push args
3247     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
3248     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3249     # . . call
3250     e8/call  print-int32-buffered/disp32
3251     # . . discard args
3252     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3253     # . write-buffered(out, "/scale ")
3254     # . . push args
3255     68/push  "/scale "/imm32
3256     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3257     # . . call
3258     e8/call  write-buffered/disp32
3259     # . . discard args
3260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3261     # . print-int32-buffered(out, disp)
3262     # . . push args
3263     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3264     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3265     # . . call
3266     e8/call  print-int32-buffered/disp32
3267     # . . discard args
3268     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3269     # . write-buffered(out, "/disp32")
3270     # . . push args
3271     68/push  "/disp32"/imm32
3272     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3273     # . . call
3274     e8/call  write-buffered/disp32
3275     # . . discard args
3276     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3277     e9/jump  $emit-indirect-mode:end/disp32
3278 $emit-indirect-mode:check-for-disp:
3279     # if (disp == 0) goto next check
3280     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      0/imm32           # compare *(ebp+24)
3281     74/jump-if-equal  $emit-indirect-mode:emit-indirect/disp8
3282 $emit-indirect-mode:emit-disp:
3283     # emit(out, "2/mod/*+disp32 " base "/rm32 " disp "/disp32")
3284     # . write-buffered(out, "2/mod/*+disp32 ")
3285     # . . push args
3286     68/push  "2/mod/*+disp32 "/imm32
3287     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3288     # . . call
3289     e8/call  write-buffered/disp32
3290     # . . discard args
3291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3292     # . print-int32-buffered(out, base)
3293     # . . push args
3294     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3295     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3296     # . . call
3297     e8/call  print-int32-buffered/disp32
3298     # . . discard args
3299     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3300     # . write-buffered(out, "/rm32 ")
3301     # . . push args
3302     68/push  "/rm32 "/imm32
3303     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3304     # . . call
3305     e8/call  write-buffered/disp32
3306     # . . discard args
3307     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3308     # . print-int32-buffered(out, disp)
3309     # . . push args
3310     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3311     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3312     # . . call
3313     e8/call  print-int32-buffered/disp32
3314     # . . discard args
3315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3316     # . write-buffered(out, "/disp32")
3317     # . . push args
3318     68/push  "/disp32"/imm32
3319     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3320     # . . call
3321     e8/call  write-buffered/disp32
3322     # . . discard args
3323     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3324     eb/jump  $emit-indirect-mode:end/disp8
3325 $emit-indirect-mode:emit-indirect:
3326     # emit(out, "0/mod/indirect " base "/rm32")
3327     # . write-buffered(out, "0/mod/indirect ")
3328     # . . push args
3329     68/push  "0/mod/indirect "/imm32
3330     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3331     # . . call
3332     e8/call  write-buffered/disp32
3333     # . . discard args
3334     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3335     # . print-int32-buffered(out, base)
3336     # . . push args
3337     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3338     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3339     # . . call
3340     e8/call  print-int32-buffered/disp32
3341     # . . discard args
3342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3343     # . write-buffered(out, "/rm32")
3344     # . . push args
3345     68/push  "/rm32"/imm32
3346     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3347     # . . call
3348     e8/call  write-buffered/disp32
3349     # . . discard args
3350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3351 $emit-indirect-mode:end:
3352     # . epilog
3353     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3354     5d/pop-to-ebp
3355     c3/return
3356 
3357 test-emit-indirect-mode:
3358     # . prolog
3359     55/push-ebp
3360     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3361     # setup
3362     # . clear-stream(_test-output-stream)
3363     # . . push args
3364     68/push  _test-output-stream/imm32
3365     # . . call
3366     e8/call  clear-stream/disp32
3367     # . . discard args
3368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3369     # . clear-stream(_test-output-buffered-file+4)
3370     # . . push args
3371     b8/copy-to-eax  _test-output-buffered-file/imm32
3372     05/add-to-eax  4/imm32
3373     50/push-eax
3374     # . . call
3375     e8/call  clear-stream/disp32
3376     # . . discard args
3377     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3378     # emit-indirect-mode(_test-output-buffered-file, 0, 4/none, 0, 0)
3379     # . . write args
3380     68/push  0/imm32/.disp
3381     68/push  0/imm32/.scale
3382     68/push  4/imm32/.index/none
3383     68/push  0/imm32/.base
3384     68/push  _test-output-buffered-file/imm32
3385     # . . call
3386     e8/call  emit-indirect-mode/disp32
3387     # . . discard args
3388     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3389     # . flush(_test-output-buffered-file)
3390     # . . push args
3391     68/push  _test-output-buffered-file/imm32
3392     # . . call
3393     e8/call  flush/disp32
3394     # . . discard args
3395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3396 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3422     # check-stream-equal(_test-output-stream, "0/mod/indirect 0/rm32", msg)
3423     # . . push args
3424     68/push  "F - test-emit-indirect-mode"/imm32
3425     68/push  "0/mod/indirect 0x00000000/rm32"/imm32
3426     68/push  _test-output-stream/imm32
3427     # . . call
3428     e8/call  check-stream-equal/disp32
3429     # . . discard args
3430     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3431     # . epilog
3432     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3433     5d/pop-to-ebp
3434     c3/return
3435 
3436 test-emit-indirect-mode-2:
3437     # . prolog
3438     55/push-ebp
3439     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3440     # setup
3441     # . clear-stream(_test-output-stream)
3442     # . . push args
3443     68/push  _test-output-stream/imm32
3444     # . . call
3445     e8/call  clear-stream/disp32
3446     # . . discard args
3447     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3448     # . clear-stream(_test-output-buffered-file+4)
3449     # . . push args
3450     b8/copy-to-eax  _test-output-buffered-file/imm32
3451     05/add-to-eax  4/imm32
3452     50/push-eax
3453     # . . call
3454     e8/call  clear-stream/disp32
3455     # . . discard args
3456     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3457     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 0)
3458     # . . write args
3459     68/push  0/imm32/.disp
3460     68/push  0/imm32/.scale
3461     68/push  4/imm32/.index/none
3462     68/push  7/imm32/.base
3463     68/push  _test-output-buffered-file/imm32
3464     # . . call
3465     e8/call  emit-indirect-mode/disp32
3466     # . . discard args
3467     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3468     # . flush(_test-output-buffered-file)
3469     # . . push args
3470     68/push  _test-output-buffered-file/imm32
3471     # . . call
3472     e8/call  flush/disp32
3473     # . . discard args
3474     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3475 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3501     # check-stream-equal(_test-output-stream, "0/mod/indirect 7/rm32", msg)
3502     # . . push args
3503     68/push  "F - test-emit-indirect-mode-2"/imm32
3504     68/push  "0/mod/indirect 0x00000007/rm32"/imm32
3505     68/push  _test-output-stream/imm32
3506     # . . call
3507     e8/call  check-stream-equal/disp32
3508     # . . discard args
3509     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3510     # . epilog
3511     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3512     5d/pop-to-ebp
3513     c3/return
3514 
3515 test-emit-indirect-mode-with-disp:
3516     # . prolog
3517     55/push-ebp
3518     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3519     # setup
3520     # . clear-stream(_test-output-stream)
3521     # . . push args
3522     68/push  _test-output-stream/imm32
3523     # . . call
3524     e8/call  clear-stream/disp32
3525     # . . discard args
3526     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3527     # . clear-stream(_test-output-buffered-file+4)
3528     # . . push args
3529     b8/copy-to-eax  _test-output-buffered-file/imm32
3530     05/add-to-eax  4/imm32
3531     50/push-eax
3532     # . . call
3533     e8/call  clear-stream/disp32
3534     # . . discard args
3535     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3536     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 4)
3537     # . . write args
3538     68/push  4/imm32/.disp
3539     68/push  0/imm32/.scale
3540     68/push  4/imm32/.index/none
3541     68/push  6/imm32/.base
3542     68/push  _test-output-buffered-file/imm32
3543     # . . call
3544     e8/call  emit-indirect-mode/disp32
3545     # . . discard args
3546     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3547     # . flush(_test-output-buffered-file)
3548     # . . push args
3549     68/push  _test-output-buffered-file/imm32
3550     # . . call
3551     e8/call  flush/disp32
3552     # . . discard args
3553     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3554 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3580     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 4/disp32", msg)
3581     # . . push args
3582     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
3583     68/push  "2/mod/*+disp32 0x00000006/rm32 0x00000004/disp32"/imm32
3584     68/push  _test-output-stream/imm32
3585     # . . call
3586     e8/call  check-stream-equal/disp32
3587     # . . discard args
3588     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3589     # . epilog
3590     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3591     5d/pop-to-ebp
3592     c3/return
3593 
3594 test-emit-indirect-mode-with-disp-negative:
3595     # . prolog
3596     55/push-ebp
3597     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3598     # setup
3599     # . clear-stream(_test-output-stream)
3600     # . . push args
3601     68/push  _test-output-stream/imm32
3602     # . . call
3603     e8/call  clear-stream/disp32
3604     # . . discard args
3605     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3606     # . clear-stream(_test-output-buffered-file+4)
3607     # . . push args
3608     b8/copy-to-eax  _test-output-buffered-file/imm32
3609     05/add-to-eax  4/imm32
3610     50/push-eax
3611     # . . call
3612     e8/call  clear-stream/disp32
3613     # . . discard args
3614     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3615     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, -4)
3616     # . . write args
3617     68/push  -4/imm32/.disp
3618     68/push  0/imm32/.scale
3619     68/push  4/imm32/.index/none
3620     68/push  6/imm32/.base
3621     68/push  _test-output-buffered-file/imm32
3622     # . . call
3623     e8/call  emit-indirect-mode/disp32
3624     # . . discard args
3625     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3626     # . flush(_test-output-buffered-file)
3627     # . . push args
3628     68/push  _test-output-buffered-file/imm32
3629     # . . call
3630     e8/call  flush/disp32
3631     # . . discard args
3632     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3633 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3659     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 -4/disp32", msg)
3660     # . . push args
3661     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
3662     68/push  "2/mod/*+disp32 0x00000006/rm32 0xfffffffc/disp32"/imm32
3663     68/push  _test-output-stream/imm32
3664     # . . call
3665     e8/call  check-stream-equal/disp32
3666     # . . discard args
3667     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3668     # . epilog
3669     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3670     5d/pop-to-ebp
3671     c3/return
3672 
3673 test-emit-indirect-mode-with-sib:
3674     # . prolog
3675     55/push-ebp
3676     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3677     # setup
3678     # . clear-stream(_test-output-stream)
3679     # . . push args
3680     68/push  _test-output-stream/imm32
3681     # . . call
3682     e8/call  clear-stream/disp32
3683     # . . discard args
3684     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3685     # . clear-stream(_test-output-buffered-file+4)
3686     # . . push args
3687     b8/copy-to-eax  _test-output-buffered-file/imm32
3688     05/add-to-eax  4/imm32
3689     50/push-eax
3690     # . . call
3691     e8/call  clear-stream/disp32
3692     # . . discard args
3693     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3694     # emit-indirect-mode(_test-output-buffered-file, 6/base, 1/index, 2/scale, 4/disp)
3695     # . . write args
3696     68/push  4/imm32/.disp
3697     68/push  2/imm32/.scale
3698     68/push  1/imm32/.index
3699     68/push  6/imm32/.base
3700     68/push  _test-output-buffered-file/imm32
3701     # . . call
3702     e8/call  emit-indirect-mode/disp32
3703     # . . discard args
3704     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3705     # . flush(_test-output-buffered-file)
3706     # . . push args
3707     68/push  _test-output-buffered-file/imm32
3708     # . . call
3709     e8/call  flush/disp32
3710     # . . discard args
3711     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3712 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3738     # check-stream-equal(_test-output-stream, "2/mod/indirect 4/rm32/sib 6/base 1/index 2/scale 4/disp", msg)
3739     # . . push args
3740     68/push  "F - test-emit-indirect-mode-with-sib"/imm32
3741     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000006/base 0x00000001/index 0x00000002/scale 0x00000004/disp32"/imm32
3742     68/push  _test-output-stream/imm32
3743     # . . call
3744     e8/call  check-stream-equal/disp32
3745     # . . discard args
3746     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3747     # . epilog
3748     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3749     5d/pop-to-ebp
3750     c3/return
3751 
3752 test-emit-indirect-mode-ebp:
3753     # . prolog
3754     55/push-ebp
3755     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3756     # setup
3757     # . clear-stream(_test-output-stream)
3758     # . . push args
3759     68/push  _test-output-stream/imm32
3760     # . . call
3761     e8/call  clear-stream/disp32
3762     # . . discard args
3763     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3764     # . clear-stream(_test-output-buffered-file+4)
3765     # . . push args
3766     b8/copy-to-eax  _test-output-buffered-file/imm32
3767     05/add-to-eax  4/imm32
3768     50/push-eax
3769     # . . call
3770     e8/call  clear-stream/disp32
3771     # . . discard args
3772     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3773     # emit-indirect-mode(_test-output-buffered-file, 5/base, 0/index, 0/scale, 0/disp)
3774     # . . write args
3775     68/push  0/imm32/.disp
3776     68/push  0/imm32/.scale
3777     68/push  0/imm32/.index
3778     68/push  5/imm32/.base/ebp
3779     68/push  _test-output-buffered-file/imm32
3780     # . . call
3781     e8/call  emit-indirect-mode/disp32
3782     # . . discard args
3783     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3784     # . flush(_test-output-buffered-file)
3785     # . . push args
3786     68/push  _test-output-buffered-file/imm32
3787     # . . call
3788     e8/call  flush/disp32
3789     # . . discard args
3790     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3791 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3817     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 4/rm32/sib 5/base/ebp 0/index 0/scale 0/disp32", msg)
3818     # . . push args
3819     68/push  "F - test-emit-indirect-mode-ebp"/imm32
3820     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000005/base 0x00000000/index 0x00000000/scale 0x00000000/disp32"/imm32
3821     68/push  _test-output-stream/imm32
3822     # . . call
3823     e8/call  check-stream-equal/disp32
3824     # . . discard args
3825     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3826     # . epilog
3827     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3828     5d/pop-to-ebp
3829     c3/return
3830 
3831 test-emit-indirect-mode-esp:
3832     # . prolog
3833     55/push-ebp
3834     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3835     # setup
3836     # . clear-stream(_test-output-stream)
3837     # . . push args
3838     68/push  _test-output-stream/imm32
3839     # . . call
3840     e8/call  clear-stream/disp32
3841     # . . discard args
3842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3843     # . clear-stream(_test-output-buffered-file+4)
3844     # . . push args
3845     b8/copy-to-eax  _test-output-buffered-file/imm32
3846     05/add-to-eax  4/imm32
3847     50/push-eax
3848     # . . call
3849     e8/call  clear-stream/disp32
3850     # . . discard args
3851     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3852     # emit-indirect-mode(_test-output-buffered-file, 4/base, 0/index, 0/scale, 0/disp)
3853     # . . write args
3854     68/push  0/imm32/.disp
3855     68/push  0/imm32/.scale
3856     68/push  0/imm32/.index
3857     68/push  4/imm32/.base/esp
3858     68/push  _test-output-buffered-file/imm32
3859     # . . call
3860     e8/call  emit-indirect-mode/disp32
3861     # . . discard args
3862     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3863     # . flush(_test-output-buffered-file)
3864     # . . push args
3865     68/push  _test-output-buffered-file/imm32
3866     # . . call
3867     e8/call  flush/disp32
3868     # . . discard args
3869     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3870 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3896     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 4/rm32/sib 4/base/ebp 0/index 0/scale 0/disp32", msg)
3897     # . . push args
3898     68/push  "F - test-emit-indirect-mode-esp"/imm32
3899     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000004/base 0x00000000/index 0x00000000/scale 0x00000000/disp32"/imm32
3900     68/push  _test-output-stream/imm32
3901     # . . call
3902     e8/call  check-stream-equal/disp32
3903     # . . discard args
3904     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3905     # . epilog
3906     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3907     5d/pop-to-ebp
3908     c3/return
3909 
3910 disp32-mode?:  # in : (address slice) -> reg/eax : boolean
3911     # . prolog
3912     55/push-ebp
3913     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3914     # . save registers
3915     56/push-esi
3916     57/push-edi
3917     # var local-slice/esi : (address slice) = {in->start, in->end}
3918     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
3919     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3920     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3921     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
3922     # ++local-slice->start to skip '*'
3923     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
3924     # if (*local-slice->start == '(') return false
3925     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
3926     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
3927     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
3928     3d/compare-eax-and  0x28/imm32/open-paren
3929     74/jump-if-equal  $disp32-mode?:false/disp8
3930 $disp32-mode?:check-for-register:
3931     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
3932     # . . push args
3933     56/push-esi
3934     68/push  0x2f/imm32/slash
3935     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3936     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3937     # . . call
3938     e8/call  next-token-from-slice/disp32
3939     # . . discard args
3940     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
3941     # reg-num/eax = maybe-get-slice(Registers, local-slice, row-size=8)
3942     # . . push args
3943     68/push  8/imm32/row-size
3944     56/push-esi
3945     68/push  Registers/imm32
3946     # . . cal
3947     e8/call  maybe-get-slice/disp32
3948     # . . discard args
3949     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3950     # if (eax != 0) return false
3951     3d/compare-eax-and  0/imm32
3952     75/jump-if-not-equal  $disp32-mode?:false/disp8
3953     # return true
3954     b8/copy-to-eax  1/imm32/true
3955     eb/jump  $disp32-mode?:end/disp8
3956 $disp32-mode?:false:
3957     b8/copy-to-eax  0/imm32/false
3958 $disp32-mode?:end:
3959     # reclaim locals
3960     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3961     # . restore registers
3962     5f/pop-to-edi
3963     5e/pop-to-esi
3964     # . epilog
3965     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3966     5d/pop-to-ebp
3967     c3/return
3968 
3969 emit-indirect-disp32:  # out : (address buffered-file), word-slice : (address slice)
3970     # . prolog
3971     55/push-ebp
3972     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3973     # . save registers
3974     56/push-esi
3975     # var local-slice/esi : (address slice) = {in->start, in->end}
3976     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
3977     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3978     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3979     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
3980     # ++local-slice->start to skip '*'
3981     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
3982     # write-buffered(out, "0/mod/indirect 5/rm32/.disp32 ")
3983     # . . push args
3984     68/push  "0/mod/indirect 5/rm32/.disp32 "/imm32
3985     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3986     # . . call
3987     e8/call  write-buffered/disp32
3988     # . . discard args
3989     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3990     # write-slice-buffered(out, local-slice)
3991     # . . push args
3992     56/push-esi
3993     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3994     # . . call
3995     e8/call  write-slice-buffered/disp32
3996     # . . discard args
3997     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3998     # write-buffered(out, "/disp32")
3999     # . . push args
4000     68/push  "/disp32"/imm32
4001     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
4002     # . . call
4003     e8/call  write-buffered/disp32
4004     # . . discard args
4005     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4006 $emit-indirect-disp32:end:
4007     # reclaim locals
4008     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4009     # . restore registers
4010     5e/pop-to-esi
4011     # . epilog
4012     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4013     5d/pop-to-ebp
4014     c3/return
4015 
4016 # update line->read to ')'
4017 # line->read ends at ')'
4018 skip-until-close-paren:  # line : (address stream)
4019     # . prolog
4020     55/push-ebp
4021     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4022     # . save registers
4023     50/push-eax
4024     51/push-ecx
4025     52/push-edx
4026     # ecx = line
4027     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
4028     # eax = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write])
4029     # . . push &line->data[line->write]
4030     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   8/disp8         .                 # copy *(ecx+8) to edx
4031     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
4032     52/push-edx
4033     # . . push &line->data[line->read]
4034     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
4035     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
4036     52/push-edx
4037     # . . call
4038     e8/call  skip-until-close-paren-in-slice/disp32
4039     # . . discard args
4040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4041     # line->read = eax - line->data
4042     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
4043     2d/subtract-from-eax  0xc/imm32
4044     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         0/r32/eax   4/disp8         .                 # copy eax to *(ecx+4)
4045 $skip-until-close-paren:end:
4046     # . restore registers
4047     5a/pop-to-edx
4048     59/pop-to-ecx
4049     58/pop-to-eax
4050     # . epilog
4051     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4052     5d/pop-to-ebp
4053     c3/return
4054 
4055 test-skip-until-close-paren:
4056     # . prolog
4057     55/push-ebp
4058     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4059     # setup
4060     # . clear-stream(_test-input-stream)
4061     # . . push args
4062     68/push  _test-input-stream/imm32
4063     # . . call
4064     e8/call  clear-stream/disp32
4065     # . . discard args
4066     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4067     # . write(_test-input-stream, "*(abc) def")
4068     # .                   indices:  0123 45
4069     # . . push args
4070     68/push  "*(abc) def"/imm32
4071     68/push  _test-input-stream/imm32
4072     # . . call
4073     e8/call  write/disp32
4074     # . . discard args
4075     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4076     # precondition: line->read == 0
4077     # . . push args
4078     68/push  "F - test-skip-until-close-paren/precondition"/imm32
4079     68/push  0/imm32
4080     b8/copy-to-eax  _test-input-stream/imm32
4081     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
4082     # . . call
4083     e8/call  check-ints-equal/disp32
4084     # . . discard args
4085     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4086     # skip-until-close-paren(_test-input-stream)
4087     # . . push args
4088     68/push  _test-input-stream/imm32
4089     # . . call
4090     e8/call  skip-until-close-paren/disp32
4091     # . . discard args
4092     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4093     # check-ints-equal(line->read, 5, msg)
4094     # . . push args
4095     68/push  "F - test-skip-until-close-paren"/imm32
4096     68/push  5/imm32
4097     b8/copy-to-eax  _test-input-stream/imm32
4098     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
4099     # . . call
4100     e8/call  check-ints-equal/disp32
4101     # . . discard args
4102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4103     # . epilog
4104     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4105     5d/pop-to-ebp
4106     c3/return
4107 
4108 test-skip-until-close-paren-ignores-spaces:
4109     # . prolog
4110     55/push-ebp
4111     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4112     # setup
4113     # . clear-stream(_test-input-stream)
4114     # . . push args
4115     68/push  _test-input-stream/imm32
4116     # . . call
4117     e8/call  clear-stream/disp32
4118     # . . discard args
4119     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4120     # . write(_test-input-stream, "*(a b)/yz")
4121     # . . push args
4122     68/push  "*(a b)/yz"/imm32
4123     68/push  _test-input-stream/imm32
4124     # . . call
4125     e8/call  write/disp32
4126     # . . discard args
4127     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4128     # precondition: line->read == 0
4129     # . . push args
4130     68/push  "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32
4131     68/push  0/imm32
4132     b8/copy-to-eax  _test-input-stream/imm32
4133     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
4134     # . . call
4135     e8/call  check-ints-equal/disp32
4136     # . . discard args
4137     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4138     # skip-until-close-paren(_test-input-stream)
4139     # . . push args
4140     68/push  _test-input-stream/imm32
4141     # . . call
4142     e8/call  skip-until-close-paren/disp32
4143     # . . discard args
4144     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4145     # check-ints-equal(line->read, 5, msg)
4146     # . . push args
4147     68/push  "F - test-skip-until-close-paren-ignores-spaces"/imm32
4148     68/push  5/imm32
4149     b8/copy-to-eax  _test-input-stream/imm32
4150     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
4151     # . . call
4152     e8/call  check-ints-equal/disp32
4153     # . . discard args
4154     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4155     # . epilog
4156     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4157     5d/pop-to-ebp
4158     c3/return
4159 
4160 test-skip-until-close-paren-works-from-mid-stream:
4161     # . prolog
4162     55/push-ebp
4163     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4164     # setup
4165     # . clear-stream(_test-input-stream)
4166     # . . push args
4167     68/push  _test-input-stream/imm32
4168     # . . call
4169     e8/call  clear-stream/disp32
4170     # . . discard args
4171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4172     # . write(_test-input-stream, "0 *(a b)/yz")
4173     # . . push args
4174     68/push  "0 *(a b)/yz"/imm32
4175     68/push  _test-input-stream/imm32
4176     # . . call
4177     e8/call  write/disp32
4178     # . . discard args
4179     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4180     # precondition: _test-input-stream->read == 2
4181     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         2/imm32           # copy to *(eax+4)
4182     # skip-until-close-paren(_test-input-stream)
4183     # . . push args
4184     68/push  _test-input-stream/imm32
4185     # . . call
4186     e8/call  skip-until-close-paren/disp32
4187     # . . discard args
4188     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4189     # check-ints-equal(_test-input-stream->read, 7, msg)
4190     # . . push args
4191     68/push  "F - test-skip-until-close-paren-works-from-mid-stream"/imm32
4192     68/push  7/imm32
4193     b8/copy-to-eax  _test-input-stream/imm32
4194     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
4195     # . . call
4196     e8/call  check-ints-equal/disp32
4197     # . . discard args
4198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4199     # . epilog
4200     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4201     5d/pop-to-ebp
4202     c3/return
4203 
4204 skip-until-close-paren-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/eax
4205     # . prolog
4206     55/push-ebp
4207     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4208     # . save registers
4209     51/push-ecx
4210     52/push-edx
4211     # ecx = curr
4212     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
4213     # edx = end
4214     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
4215     # eax = 0
4216     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4217     # skip initial dquote
4218     41/increment-ecx
4219 $skip-until-close-paren-in-slice:loop:
4220     # if (curr >= end) break
4221     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4222     73/jump-if-greater-unsigned-or-equal  $skip-until-close-paren-in-slice:break/disp8
4223     # AL = *curr
4224     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4225 $skip-until-close-paren-in-slice:check-close:
4226     # if (eax == ')') break
4227     3d/compare-eax-and  0x29/imm32/close-paren
4228     74/jump-if-equal  $skip-until-close-paren-in-slice:break/disp8
4229     # ++curr
4230     41/increment-ecx
4231     eb/jump  $skip-until-close-paren-in-slice:loop/disp8
4232 $skip-until-close-paren-in-slice:break:
4233     # return curr
4234     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to eax
4235 $skip-until-close-paren-in-slice:end:
4236     # . restore registers
4237     5a/pop-to-edx
4238     59/pop-to-ecx
4239     # . epilog
4240     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4241     5d/pop-to-ebp
4242     c3/return
4243 
4244 test-skip-until-close-paren-in-slice:
4245     # . prolog
4246     55/push-ebp
4247     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4248     # setup: (eax..ecx) = "*(abc) def"
4249     b8/copy-to-eax  "*(abc) def"/imm32
4250     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4251     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4252     05/add-to-eax  4/imm32
4253     # eax = skip-until-close-paren-in-slice(eax, ecx)
4254     # . . push args
4255     51/push-ecx
4256     50/push-eax
4257     # . . call
4258     e8/call  skip-until-close-paren-in-slice/disp32
4259     # . . discard args
4260     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4261     # check-ints-equal(ecx-eax, 5, msg)  # eax is at the ')'
4262     # . . push args
4263     68/push  "F - test-skip-until-close-paren-in-slice"/imm32
4264     68/push  5/imm32
4265     # . . push ecx-eax
4266     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
4267     51/push-ecx
4268     # . . call
4269     e8/call  check-ints-equal/disp32
4270     # . . discard args
4271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4272     # . epilog
4273     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4274     5d/pop-to-ebp
4275     c3/return
4276 
4277 test-skip-until-close-paren-in-slice-ignores-spaces:
4278     # . prolog
4279     55/push-ebp
4280     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4281     # setup: (eax..ecx) = "*(a b)/yz"
4282     b8/copy-to-eax  "*(a b)/yz"/imm32
4283     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4284     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4285     05/add-to-eax  4/imm32
4286     # eax = skip-until-close-paren-in-slice(eax, ecx)
4287     # . . push args
4288     51/push-ecx
4289     50/push-eax
4290     # . . call
4291     e8/call  skip-until-close-paren-in-slice/disp32
4292     # . . discard args
4293     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4294     # check-ints-equal(ecx-eax, 4, msg)  # eax is at the ')'
4295     # . . push args
4296     68/push  "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32
4297     68/push  4/imm32
4298     # . . push ecx-eax
4299     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
4300     51/push-ecx
4301     # . . call
4302     e8/call  check-ints-equal/disp32
4303     # . . discard args
4304     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4305     # . epilog
4306     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4307     5d/pop-to-ebp
4308     c3/return
4309 
4310 test-skip-until-close-paren-in-slice-stops-at-end:
4311     # . prolog
4312     55/push-ebp
4313     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4314     # setup: (eax..ecx) = "*(abc"  # unbalanced dquote
4315     b8/copy-to-eax  "*(abc"/imm32
4316     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4317     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4318     05/add-to-eax  4/imm32
4319     # eax = skip-until-close-paren-in-slice(eax, ecx)
4320     # . . push args
4321     51/push-ecx
4322     50/push-eax
4323     # . . call
4324     e8/call  skip-until-close-paren-in-slice/disp32
4325     # . . discard args
4326     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4327     # check-ints-equal(ecx-eax, 0, msg)  # skipped to end of slice
4328     # . . push args
4329     68/push  "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32
4330     68/push  0/imm32
4331     # . . push ecx-eax
4332     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
4333     51/push-ecx
4334     # . . call
4335     e8/call  check-ints-equal/disp32
4336     # . . discard args
4337     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4338     # . epilog
4339     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4340     5d/pop-to-ebp
4341     c3/return
4342 
4343 # assumes 'in' starts with optional '+' or '-', optional whitespace, and an unsigned integer
4344 # returns the value of the integer
4345 # side-effect: modifies 'in' to skip past the integer
4346 next-hex-int:  # in : (address slice) -> result/eax
4347     # . prolog
4348     55/push-ebp
4349     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4350     # . save registers
4351     51/push-ecx
4352     52/push-edx
4353     53/push-ebx
4354     56/push-esi
4355     57/push-edi
4356     # result/edi = 0
4357     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
4358     # esi = in
4359     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
4360     # edx = in->end
4361     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
4362     # curr/ecx = in->start
4363     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
4364     # negate?/ebx = false
4365     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4366     # eax = *curr
4367     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4368     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4369 $next-hex-int:positive:
4370     # if (*curr == '+') ++curr
4371     3d/compare-eax-and  0x2b/imm32/+
4372     75/jump-if-not-equal  $next-hex-int:negative/disp8
4373     # . ++curr
4374     41/increment-ecx
4375     eb/jump  $next-hex-int:skip-whitespace/disp8
4376 $next-hex-int:negative:
4377     # else if (*curr == '-') ++curr, negate = true
4378     3d/compare-eax-and  0x2d/imm32/-
4379     75/jump-if-not-equal  $next-hex-int:skip-whitespace/disp8
4380 $next-hex-int:need-to-negate:
4381     # . ++curr
4382     41/increment-ecx
4383     # . negate = true
4384     bb/copy-to-ebx  1/imm32/true
4385     # fall through
4386 $next-hex-int:skip-whitespace:
4387     # spill eax
4388     50/push-eax
4389     # eax = skip-chars-matching-whitespace-in-slice(word-slice->start, word-slice->end)
4390     # . . push args
4391     52/push-edx
4392     51/push-ecx
4393     # . . call
4394     e8/call  skip-chars-matching-whitespace-in-slice/disp32
4395     # . . discard args
4396     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4397     # ecx = eax
4398     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
4399     # restore eax
4400     58/pop-to-eax
4401 $next-hex-int:initial-0:
4402     # skip past leading '0x'
4403     # . if (*curr != '0') jump to loop
4404     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4405     3d/compare-eax-and  0x30/imm32/0
4406     75/jump-if-not-equal  $next-hex-int:loop/disp8
4407     # . ++curr
4408     41/increment-ecx
4409 $next-hex-int:initial-0x:
4410     # . if (curr >= in->end) return result
4411     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4412     73/jump-if-greater-or-equal-unsigned  $next-hex-int:end/disp8
4413     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
4414     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4415     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4416     3d/compare-eax-and  0x78/imm32/x
4417     75/jump-if-not-equal  $next-hex-int:loop/disp8
4418     # . ++curr
4419     41/increment-ecx
4420 $next-hex-int:loop:
4421     # if (curr >= in->end) break
4422     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4423     73/jump-if-greater-or-equal-unsigned  $next-hex-int:break/disp8
4424     # if (!is-hex-digit?(*curr)) break
4425     # . eax = *curr
4426     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4427     # . eax = is-hex-digit?(*curr)
4428     # . . push args
4429     50/push-eax
4430     # . . call
4431     e8/call  is-hex-digit?/disp32
4432     # . . discard args
4433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4434     # . if (eax == 0) break
4435     3d/compare-eax-and  0/imm32
4436     74/jump-if-equal  $next-hex-int:break/disp8
4437     # eax = from-hex-char(*curr)
4438     # . . copy arg to eax
4439     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4440     # . . call
4441     e8/call  from-hex-char/disp32
4442     # result = result * 16 + eax
4443     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi left by 4 bits
4444     01/add                          3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to edi
4445     # ++curr
4446     41/increment-ecx
4447     # loop
4448     eb/jump  $next-hex-int:loop/disp8
4449 $next-hex-int:break:
4450     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
4451     74/jump-if-equal  $next-hex-int:end/disp8
4452 $next-hex-int:negate:
4453     f7          3/subop/negate      3/mod/direct    7/rm32/edi    .           .             .           .           .               .                 # negate edi
4454 $next-hex-int:end:
4455     # word-slice->start = curr
4456     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy ecx to *esi
4457     # return edi
4458     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
4459     # . restore registers
4460     5f/pop-to-edi
4461     5e/pop-to-esi
4462     5b/pop-to-ebx
4463     5a/pop-to-edx
4464     59/pop-to-ecx
4465     # . epilog
4466     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4467     5d/pop-to-ebp
4468     c3/return
4469 
4470 $next-hex-int:abort:
4471     # . _write(2/stderr, error)
4472     # . . push args
4473     68/push  "next-hex-int: invalid hex char: "/imm32
4474     68/push  2/imm32/stderr
4475     # . . call
4476     e8/call  _write/disp32
4477     # . . discard args
4478     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4479     # . clear-stream(Stderr+4)
4480     # . . save eax
4481     50/push-eax
4482     # . . push args
4483     b8/copy-to-eax  Stderr/imm32
4484     05/add-to-eax  4/imm32
4485     50/push-eax
4486     # . . call
4487     e8/call  clear-stream/disp32
4488     # . . discard args
4489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4490     # . . restore eax
4491     58/pop-to-eax
4492     # . print-int32-buffered(Stderr, eax)
4493     # . . push args
4494     50/push-eax
4495     68/push  Stderr/imm32
4496     # . . call
4497     e8/call  print-int32-buffered/disp32
4498     # . . discard args
4499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4500     # . flush(Stderr)
4501     # . . push args
4502     68/push  Stderr/imm32
4503     # . . call
4504     e8/call  flush/disp32
4505     # . . discard args
4506     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4507     # . _write(2/stderr, "\n")
4508     # . . push args
4509     68/push  "\n"/imm32
4510     68/push  2/imm32/stderr
4511     # . . call
4512     e8/call  _write/disp32
4513     # . . discard args
4514     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4515     # . syscall(exit, 1)
4516     bb/copy-to-ebx  1/imm32
4517     b8/copy-to-eax  1/imm32/exit
4518     cd/syscall  0x80/imm8
4519     # never gets here
4520 
4521 test-next-hex-int-single-digit:
4522     # . prolog
4523     55/push-ebp
4524     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4525     # (eax..ecx) = "+a)"
4526     b8/copy-to-eax  "+a)"/imm32
4527     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4528     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4529     05/add-to-eax  4/imm32
4530     # var slice/ecx = {eax, ecx}
4531     51/push-ecx
4532     50/push-eax
4533     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4534     # eax = next-hex-int(slice)
4535     # . . push args
4536     51/push-ecx
4537     # . . call
4538     e8/call  next-hex-int/disp32
4539     # . . discard args
4540     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4541     # check-ints-equal(eax, 0xa, msg)
4542     # . . push args
4543     68/push  "F - test-next-hex-int-single-digit"/imm32
4544     68/push  0xa/imm32
4545     50/push-eax
4546     # . . call
4547     e8/call  check-ints-equal/disp32
4548     # . . discard args
4549     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4550     # . epilog
4551     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4552     5d/pop-to-ebp
4553     c3/return
4554 
4555 test-next-hex-int-multi-digit:
4556     # . prolog
4557     55/push-ebp
4558     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4559     # (eax..ecx) = "+ 34a)"
4560     b8/copy-to-eax  "+ 34a)"/imm32
4561     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4562     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4563     05/add-to-eax  4/imm32
4564     # var slice/ecx = {eax, ecx}
4565     51/push-ecx
4566     50/push-eax
4567     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4568     # eax = next-hex-int(slice)
4569     # . . push args
4570     51/push-ecx
4571     # . . call
4572     e8/call  next-hex-int/disp32
4573     # . . discard args
4574     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4575     # check-ints-equal(eax, 0x34a, msg)
4576     # . . push args
4577     68/push  "F - test-next-hex-int-multi-digit"/imm32
4578     68/push  0x34a/imm32
4579     50/push-eax
4580     # . . call
4581     e8/call  check-ints-equal/disp32
4582     # . . discard args
4583     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4584     # . epilog
4585     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4586     5d/pop-to-ebp
4587     c3/return
4588 
4589 test-next-hex-int-0x-prefix:
4590     # . prolog
4591     55/push-ebp
4592     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4593     # (eax..ecx) = "+0x34)"
4594     b8/copy-to-eax  "+0x34)"/imm32
4595     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4596     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4597     05/add-to-eax  4/imm32
4598     # var slice/ecx = {eax, ecx}
4599     51/push-ecx
4600     50/push-eax
4601     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4602     # eax = next-hex-int(slice)
4603     # . . push args
4604     51/push-ecx
4605     # . . call
4606     e8/call  next-hex-int/disp32
4607     # . . discard args
4608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4609     # check-ints-equal(eax, 0x34, msg)
4610     # . . push args
4611     68/push  "F - test-next-hex-int-0x-prefix"/imm32
4612     68/push  0x34/imm32
4613     50/push-eax
4614     # . . call
4615     e8/call  check-ints-equal/disp32
4616     # . . discard args
4617     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4618     # . epilog
4619     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4620     5d/pop-to-ebp
4621     c3/return
4622 
4623 test-next-hex-int-zero:
4624     # . prolog
4625     55/push-ebp
4626     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4627     # (eax..ecx) = "+0)"
4628     b8/copy-to-eax  "+0)"/imm32
4629     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4630     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4631     05/add-to-eax  4/imm32
4632     # var slice/ecx = {eax, ecx}
4633     51/push-ecx
4634     50/push-eax
4635     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4636     # eax = next-hex-int(slice)
4637     # . . push args
4638     51/push-ecx
4639     # . . call
4640     e8/call  next-hex-int/disp32
4641     # . . discard args
4642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4643     # check-ints-equal(eax, 0, msg)
4644     # . . push args
4645     68/push  "F - test-next-hex-int-zero"/imm32
4646     68/push  0/imm32
4647     50/push-eax
4648     # . . call
4649     e8/call  check-ints-equal/disp32
4650     # . . discard args
4651     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4652     # . epilog
4653     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4654     5d/pop-to-ebp
4655     c3/return
4656 
4657 test-next-hex-int-0-prefix:
4658     # . prolog
4659     55/push-ebp
4660     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4661     # (eax..ecx) = "+ 03)"
4662     b8/copy-to-eax  "+ 03)"/imm32
4663     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4664     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4665     05/add-to-eax  4/imm32
4666     # var slice/ecx = {eax, ecx}
4667     51/push-ecx
4668     50/push-eax
4669     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4670     # eax = next-hex-int(slice)
4671     # . . push args
4672     51/push-ecx
4673     # . . call
4674     e8/call  next-hex-int/disp32
4675     # . . discard args
4676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4677     # check-ints-equal(eax, 3, msg)
4678     # . . push args
4679     68/push  "F - test-next-hex-int-0-prefix"/imm32
4680     68/push  3/imm32
4681     50/push-eax
4682     # . . call
4683     e8/call  check-ints-equal/disp32
4684     # . . discard args
4685     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4686     # . epilog
4687     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4688     5d/pop-to-ebp
4689     c3/return
4690 
4691 test-next-hex-int-negative:
4692     # . prolog
4693     55/push-ebp
4694     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4695     # (eax..ecx) = "-03)"
4696     b8/copy-to-eax  "-03)"/imm32
4697     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4698     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4699     05/add-to-eax  4/imm32
4700     # var slice/ecx = {eax, ecx}
4701     51/push-ecx
4702     50/push-eax
4703     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4704     # eax = next-hex-int(slice)
4705     # . . push args
4706     51/push-ecx
4707     # . . call
4708     e8/call  next-hex-int/disp32
4709     # . . discard args
4710     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4711     # check-ints-equal(eax, -3, msg)
4712     # . . push args
4713     68/push  "F - test-next-hex-int-negative"/imm32
4714     68/push  -3/imm32
4715     50/push-eax
4716     # . . call
4717     e8/call  check-ints-equal/disp32
4718     # . . discard args
4719     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4720     # . epilog
4721     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4722     5d/pop-to-ebp
4723     c3/return
4724 
4725 test-next-hex-int-negative-with-space:
4726     # . prolog
4727     55/push-ebp
4728     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4729     # (eax..ecx) = "- 03)"
4730     b8/copy-to-eax  "- 03)"/imm32
4731     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4732     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4733     05/add-to-eax  4/imm32
4734     # var slice/ecx = {eax, ecx}
4735     51/push-ecx
4736     50/push-eax
4737     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4738     # eax = next-hex-int(slice)
4739     # . . push args
4740     51/push-ecx
4741     # . . call
4742     e8/call  next-hex-int/disp32
4743     # . . discard args
4744     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4745     # check-ints-equal(eax, -3, msg)
4746     # . . push args
4747     68/push  "F - test-next-hex-int-negative-with-space"/imm32
4748     68/push  -3/imm32
4749     50/push-eax
4750     # . . call
4751     e8/call  check-ints-equal/disp32
4752     # . . discard args
4753     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4754     # . epilog
4755     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4756     5d/pop-to-ebp
4757     c3/return
4758 
4759 # assumes 'in' starts a positive unsigned integer
4760 # returns the value of the integer
4761 # side-effect: modifies 'in' to skip past the integer
4762 next-positive-hex-int:  # in : (address slice) -> result/eax
4763     # . prolog
4764     55/push-ebp
4765     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4766     # . save registers
4767     51/push-ecx
4768     52/push-edx
4769     53/push-ebx
4770     56/push-esi
4771     57/push-edi
4772     # result/edi = 0
4773     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
4774     # esi = in
4775     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
4776     # edx = in->end
4777     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
4778     # curr/ecx = in->start
4779     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
4780     # negate?/ebx = false
4781     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4782     # eax = *curr
4783     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4784     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4785 $next-positive-hex-int:initial-0:
4786     # skip past leading '0x'
4787     # . if (*curr != '0') jump to loop
4788     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4789     3d/compare-eax-and  0x30/imm32/0
4790     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
4791     # . ++curr
4792     41/increment-ecx
4793 $next-positive-hex-int:initial-0x:
4794     # . if (curr >= in->end) return result
4795     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4796     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
4797     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
4798     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4799     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4800     3d/compare-eax-and  0x78/imm32/x
4801     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
4802     # . ++curr
4803     41/increment-ecx
4804 $next-positive-hex-int:loop:
4805     # if (curr >= in->end) break
4806     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4807     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
4808     # if (!is-hex-digit?(*curr)) break
4809     # . eax = *curr
4810     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4811     # . eax = is-hex-digit?(*curr)
4812     # . . push args
4813     50/push-eax
4814     # . . call
4815     e8/call  is-hex-digit?/disp32
4816     # . . discard args
4817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4818     # . if (eax == 0) break
4819     3d/compare-eax-and  0/imm32
4820     74/jump-if-equal  $next-positive-hex-int:end/disp8
4821     # eax = from-hex-char(*curr)
4822     # . . copy arg to eax
4823     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4824     # . . call
4825     e8/call  from-hex-char/disp32
4826     # result = result * 16 + eax
4827     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi left by 4 bits
4828     01/add                          3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to edi
4829     # ++curr
4830     41/increment-ecx
4831     # loop
4832     eb/jump  $next-positive-hex-int:loop/disp8
4833 $next-positive-hex-int:end:
4834     # word-slice->start = curr
4835     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy ecx to *esi
4836     # return edi
4837     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
4838     # . restore registers
4839     5f/pop-to-edi
4840     5e/pop-to-esi
4841     5b/pop-to-ebx
4842     5a/pop-to-edx
4843     59/pop-to-ecx
4844     # . epilog
4845     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4846     5d/pop-to-ebp
4847     c3/return
4848 
4849 test-next-positive-hex-int-single-digit:
4850     # . prolog
4851     55/push-ebp
4852     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4853     # (eax..ecx) = "a)"
4854     b8/copy-to-eax  "a)"/imm32
4855     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4856     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4857     05/add-to-eax  4/imm32
4858     # var slice/ecx = {eax, ecx}
4859     51/push-ecx
4860     50/push-eax
4861     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4862     # eax = next-positive-hex-int(slice)
4863     # . . push args
4864     51/push-ecx
4865     # . . call
4866     e8/call  next-positive-hex-int/disp32
4867     # . . discard args
4868     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4869     # check-ints-equal(eax, 0xa, msg)
4870     # . . push args
4871     68/push  "F - test-next-positive-hex-int-single-digit"/imm32
4872     68/push  0xa/imm32
4873     50/push-eax
4874     # . . call
4875     e8/call  check-ints-equal/disp32
4876     # . . discard args
4877     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4878     # . epilog
4879     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4880     5d/pop-to-ebp
4881     c3/return
4882 
4883 test-next-positive-hex-int-multi-digit:
4884     # . prolog
4885     55/push-ebp
4886     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4887     # (eax..ecx) = "34a)"
4888     b8/copy-to-eax  "34a)"/imm32
4889     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4890     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4891     05/add-to-eax  4/imm32
4892     # var slice/ecx = {eax, ecx}
4893     51/push-ecx
4894     50/push-eax
4895     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4896     # eax = next-positive-hex-int(slice)
4897     # . . push args
4898     51/push-ecx
4899     # . . call
4900     e8/call  next-positive-hex-int/disp32
4901     # . . discard args
4902     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4903     # check-ints-equal(eax, 0x34a, msg)
4904     # . . push args
4905     68/push  "F - test-next-positive-hex-int-multi-digit"/imm32
4906     68/push  0x34a/imm32
4907     50/push-eax
4908     # . . call
4909     e8/call  check-ints-equal/disp32
4910     # . . discard args
4911     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4912     # . epilog
4913     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4914     5d/pop-to-ebp
4915     c3/return
4916 
4917 test-next-positive-hex-int-0x-prefix:
4918     # . prolog
4919     55/push-ebp
4920     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4921     # (eax..ecx) = "0x34)"
4922     b8/copy-to-eax  "0x34)"/imm32
4923     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4924     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4925     05/add-to-eax  4/imm32
4926     # var slice/ecx = {eax, ecx}
4927     51/push-ecx
4928     50/push-eax
4929     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4930     # eax = next-positive-hex-int(slice)
4931     # . . push args
4932     51/push-ecx
4933     # . . call
4934     e8/call  next-positive-hex-int/disp32
4935     # . . discard args
4936     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4937     # check-ints-equal(eax, 0x34, msg)
4938     # . . push args
4939     68/push  "F - test-next-positive-hex-int-0x-prefix"/imm32
4940     68/push  0x34/imm32
4941     50/push-eax
4942     # . . call
4943     e8/call  check-ints-equal/disp32
4944     # . . discard args
4945     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4946     # . epilog
4947     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4948     5d/pop-to-ebp
4949     c3/return
4950 
4951 test-next-positive-hex-int-zero:
4952     # . prolog
4953     55/push-ebp
4954     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4955     # (eax..ecx) = "0"
4956     b8/copy-to-eax  "0"/imm32
4957     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4958     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4959     05/add-to-eax  4/imm32
4960     # var slice/ecx = {eax, ecx}
4961     51/push-ecx
4962     50/push-eax
4963     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4964     # eax = next-positive-hex-int(slice)
4965     # . . push args
4966     51/push-ecx
4967     # . . call
4968     e8/call  next-positive-hex-int/disp32
4969     # . . discard args
4970     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4971     # check-ints-equal(eax, 0, msg)
4972     # . . push args
4973     68/push  "F - test-next-positive-hex-int-zero"/imm32
4974     68/push  0/imm32
4975     50/push-eax
4976     # . . call
4977     e8/call  check-ints-equal/disp32
4978     # . . discard args
4979     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4980     # . epilog
4981     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4982     5d/pop-to-ebp
4983     c3/return
4984 
4985 test-next-positive-hex-int-0-prefix:
4986     # . prolog
4987     55/push-ebp
4988     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4989     # (eax..ecx) = "03)"
4990     b8/copy-to-eax  "03)"/imm32
4991     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4992     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
4993     05/add-to-eax  4/imm32
4994     # var slice/ecx = {eax, ecx}
4995     51/push-ecx
4996     50/push-eax
4997     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4998     # eax = next-positive-hex-int(slice)
4999     # . . push args
5000     51/push-ecx
5001     # . . call
5002     e8/call  next-positive-hex-int/disp32
5003     # . . discard args
5004     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
5005     # check-ints-equal(eax, 3, msg)
5006     # . . push args
5007     68/push  "F - test-next-positive-hex-int-0-prefix"/imm32
5008     68/push  3/imm32
5009     50/push-eax
5010     # . . call
5011     e8/call  check-ints-equal/disp32
5012     # . . discard args
5013     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
5014     # . epilog
5015     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
5016     5d/pop-to-ebp
5017     c3/return
5018 
5019 == data
5020 Registers:  # (table string int)
5021   # a table is a stream
5022   0x40/imm32/write
5023   0/imm32/read
5024   0x40/imm32/length
5025   # data
5026   "eax"/imm32  0/imm32
5027   "ecx"/imm32  1/imm32
5028   "edx"/imm32  2/imm32
5029   "ebx"/imm32  3/imm32
5030   "esp"/imm32  4/imm32
5031   "ebp"/imm32  5/imm32
5032   "esi"/imm32  6/imm32
5033   "edi"/imm32  7/imm32
5034 
5035 # . . vim:nowrap:textwidth=0