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     # pseudocode:
1654     #   skip-chars-matching(line, ' ')
1655     #   if line->read >= line->write              # end of line
1656     #     out = {0, 0}
1657     #     return
1658     #   out->start = &line->data[line->read]
1659     #   if line->data[line->read] == '#'          # comment
1660     #     out.end = &line->data[line->write]
1661     #     return
1662     #   if line->data[line->read] == '"'          # string literal
1663     #     skip-string(line)
1664     #   else if line->data[line->read] == '*'     # expression
1665     #     if line->data[line->read + 1] == ' '
1666     #       abort
1667     #     if line->data[line->read + 1] == '('
1668     #       skip-until-close-paren(line)
1669     #       if (line->data[line->read] != ')'
1670     #         abort
1671     #       ++line->data[line->read] to skip ')'
1672     #   skip-chars-not-matching-whitespace(line)
1673     #   out->end = &line->data[line->read]
1674     #
1675     # registers:
1676     #   ecx: often line->read
1677     #   eax: often line->data[line->read]
1678     #
1679     # . prolog
1680     55/push-ebp
1681     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1682     # . save registers
1683     50/push-eax
1684     51/push-ecx
1685     56/push-esi
1686     57/push-edi
1687     # esi = line
1688     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
1689     # edi = out
1690     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
1691     # skip-chars-matching(line, ' ')
1692     # . . push args
1693     68/push  0x20/imm32/space
1694     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1695     # . . call
1696     e8/call  skip-chars-matching/disp32
1697     # . . discard args
1698     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1699 $next-word-or-expression:check0:
1700     # if (line->read >= line->write) clear out and return
1701     # . ecx = line->read
1702     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1703     # . if (ecx < line->write) goto next check
1704     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare ecx with *esi
1705     7c/jump-if-lesser  $next-word-or-expression:check-for-comment/disp8
1706     # . return out = {0, 0}
1707     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
1708     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
1709     e9/jump  $next-word-or-expression:end/disp32
1710 $next-word-or-expression:check-for-comment:
1711     # out->start = &line->data[line->read]
1712     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
1713     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
1714     # if (line->data[line->read] != '#') goto next check
1715     # . eax = line->data[line->read]
1716     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1717     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
1718     # . if (eax != '#') goto next check
1719     3d/compare-eax-and  0x23/imm32/pound
1720     75/jump-if-not-equal  $next-word-or-expression:check-for-string-literal/disp8
1721 $next-word-or-expression:comment:
1722     # out->end = &line->data[line->write]
1723     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1724     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
1725     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1726     # line->read = line->write  # skip rest of line
1727     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1728     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(esi+4)
1729     # return
1730     eb/jump  $next-word-or-expression:end/disp8
1731 $next-word-or-expression:check-for-string-literal:
1732     # if (line->data[line->read] != '"') goto next check
1733     3d/compare-eax-and  0x22/imm32/dquote
1734     75/jump-if-not-equal  $next-word-or-expression:check-for-expression/disp8
1735 $next-word-or-expression:string-literal:
1736     # skip-string(line)
1737     # . . push args
1738     56/push-esi
1739     # . . call
1740     e8/call  skip-string/disp32
1741     # . . discard args
1742     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1743     # skip rest of word
1744     eb/jump  $next-word-or-expression:regular-word/disp8
1745 $next-word-or-expression:check-for-expression:
1746     # if (line->data[line->read] != '*') goto next check
1747     3d/compare-eax-and  0x2a/imm32/asterisk
1748     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
1749     # if (line->data[line->read + 1] == ' ') goto error1
1750     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
1751     3d/compare-eax-and  0x20/imm32/space
1752     74/jump-if-equal  $next-word-or-expression:error1/disp8
1753     # if (line->data[line->read + 1] != '(') goto regular word
1754     3d/compare-eax-and  0x28/imm32/open-paren
1755     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
1756 $next-word-or-expression:paren:
1757     # skip-until-close-paren(line)
1758     # . . push args
1759     56/push-esi
1760     # . . call
1761     e8/call  skip-until-close-paren/disp32
1762     # . . discard args
1763     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1764     # if (line->data[line->read] != ')') goto error2
1765     # . eax = line->data[line->read]
1766     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1767     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
1768     # . if (eax != ')') goto error2
1769     3d/compare-eax-and  0x29/imm32/close-paren
1770     75/jump-if-not-equal  $next-word-or-expression:error2/disp8
1771     # skip ')'
1772     ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # increment *(esi+4)
1773     # fall through
1774 $next-word-or-expression:regular-word:
1775     # skip-chars-not-matching-whitespace(line)  # including trailing newline
1776     # . . push args
1777     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1778     # . . call
1779     e8/call  skip-chars-not-matching-whitespace/disp32
1780     # . . discard args
1781     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1782     # out->end = &line->data[line->read]
1783     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1784     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
1785     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1786 $next-word-or-expression:end:
1787     # . restore registers
1788     5f/pop-to-edi
1789     5e/pop-to-esi
1790     59/pop-to-ecx
1791     58/pop-to-eax
1792     # . epilog
1793     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1794     5d/pop-to-ebp
1795     c3/return
1796 
1797 $next-word-or-expression:error1:
1798     # print(stderr, "error: no space allowed after '*' in '" line "'")
1799     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
1800     # . . push args
1801     68/push  "error: no space allowed after '*' in '"/imm32
1802     68/push  Stderr/imm32
1803     # . . call
1804     e8/call  write-buffered/disp32
1805     # . . discard args
1806     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1807     # . write-stream-data(Stderr, line)
1808     # . . push args
1809     56/push-esi
1810     68/push  Stderr/imm32
1811     # . . call
1812     e8/call  write-stream-data/disp32
1813     # . . discard args
1814     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1815     # . write-buffered(Stderr, "'")
1816     # . . push args
1817     68/push  "'"/imm32
1818     68/push  Stderr/imm32
1819     # . . call
1820     e8/call  write-buffered/disp32
1821     # . . discard args
1822     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1823     # . flush(Stderr)
1824     # . . push args
1825     68/push  Stderr/imm32
1826     # . . call
1827     e8/call  flush/disp32
1828     # . . discard args
1829     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1830     # . syscall(exit, 1)
1831     bb/copy-to-ebx  1/imm32
1832     b8/copy-to-eax  1/imm32/exit
1833     cd/syscall  0x80/imm8
1834     # never gets here
1835 
1836 $next-word-or-expression:error2:
1837     # print(stderr, "error: *(...) expression must be all on a single line in '" line "'")
1838     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
1839     # . . push args
1840     68/push  "error: *(...) expression must be all on a single line in '"/imm32
1841     68/push  Stderr/imm32
1842     # . . call
1843     e8/call  write-buffered/disp32
1844     # . . discard args
1845     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1846     # . write-stream-data(Stderr, line)
1847     # . . push args
1848     56/push-esi
1849     68/push  Stderr/imm32
1850     # . . call
1851     e8/call  write-stream-data/disp32
1852     # . . discard args
1853     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1854     # . write-buffered(Stderr, "'")
1855     # . . push args
1856     68/push  "'"/imm32
1857     68/push  Stderr/imm32
1858     # . . call
1859     e8/call  write-buffered/disp32
1860     # . . discard args
1861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1862     # . flush(Stderr)
1863     # . . push args
1864     68/push  Stderr/imm32
1865     # . . call
1866     e8/call  flush/disp32
1867     # . . discard args
1868     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1869     # . syscall(exit, 1)
1870     bb/copy-to-ebx  1/imm32
1871     b8/copy-to-eax  1/imm32/exit
1872     cd/syscall  0x80/imm8
1873     # never gets here
1874 
1875 test-next-word-or-expression:
1876     # . prolog
1877     55/push-ebp
1878     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1879     # setup
1880     # . clear-stream(_test-input-stream)
1881     # . . push args
1882     68/push  _test-input-stream/imm32
1883     # . . call
1884     e8/call  clear-stream/disp32
1885     # . . discard args
1886     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1887     # var slice/ecx = {0, 0}
1888     68/push  0/imm32/end
1889     68/push  0/imm32/start
1890     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1891     # write(_test-input-stream, "  ab")
1892     # . . push args
1893     68/push  "  ab"/imm32
1894     68/push  _test-input-stream/imm32
1895     # . . call
1896     e8/call  write/disp32
1897     # . . discard args
1898     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1899     # next-word-or-expression(_test-input-stream, slice)
1900     # . . push args
1901     51/push-ecx
1902     68/push  _test-input-stream/imm32
1903     # . . call
1904     e8/call  next-word-or-expression/disp32
1905     # . . discard args
1906     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1907     # check-ints-equal(_test-input-stream->read, 4, msg)
1908     # . . push args
1909     68/push  "F - test-next-word-or-expression/updates-stream-read-correctly"/imm32
1910     68/push  4/imm32
1911     b8/copy-to-eax  _test-input-stream/imm32
1912     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1913     # . . call
1914     e8/call  check-ints-equal/disp32
1915     # . . discard args
1916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1917     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1918     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1919     # . . push args
1920     68/push  "F - test-next-word-or-expression: start"/imm32
1921     68/push  0xe/imm32
1922     # . . push slice->start - _test-input-stream
1923     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1924     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1925     50/push-eax
1926     # . . call
1927     e8/call  check-ints-equal/disp32
1928     # . . discard args
1929     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1930     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1931     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1932     # . . push args
1933     68/push  "F - test-next-word-or-expression: end"/imm32
1934     68/push  0x10/imm32
1935     # . . push slice->end - _test-input-stream
1936     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
1937     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1938     50/push-eax
1939     # . . call
1940     e8/call  check-ints-equal/disp32
1941     # . . discard args
1942     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1943     # . epilog
1944     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1945     5d/pop-to-ebp
1946     c3/return
1947 
1948 test-next-word-or-expression-returns-whole-comment:
1949     # . prolog
1950     55/push-ebp
1951     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1952     # setup
1953     # . clear-stream(_test-input-stream)
1954     # . . push args
1955     68/push  _test-input-stream/imm32
1956     # . . call
1957     e8/call  clear-stream/disp32
1958     # . . discard args
1959     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1960     # var slice/ecx = {0, 0}
1961     68/push  0/imm32/end
1962     68/push  0/imm32/start
1963     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1964     # write(_test-input-stream, "  # a")
1965     # . . push args
1966     68/push  "  # a"/imm32
1967     68/push  _test-input-stream/imm32
1968     # . . call
1969     e8/call  write/disp32
1970     # . . discard args
1971     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1972     # next-word-or-expression(_test-input-stream, slice)
1973     # . . push args
1974     51/push-ecx
1975     68/push  _test-input-stream/imm32
1976     # . . call
1977     e8/call  next-word-or-expression/disp32
1978     # . . discard args
1979     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1980     # check-ints-equal(_test-input-stream->read, 5, msg)
1981     # . . push args
1982     68/push  "F - test-next-word-or-expression-returns-whole-comment/updates-stream-read-correctly"/imm32
1983     68/push  5/imm32
1984     b8/copy-to-eax  _test-input-stream/imm32
1985     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1986     # . . call
1987     e8/call  check-ints-equal/disp32
1988     # . . discard args
1989     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1990     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1991     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1992     # . . push args
1993     68/push  "F - test-next-word-or-expression-returns-whole-comment: start"/imm32
1994     68/push  0xe/imm32
1995     # . . push slice->start - _test-input-stream
1996     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1997     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1998     50/push-eax
1999     # . . call
2000     e8/call  check-ints-equal/disp32
2001     # . . discard args
2002     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2003     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
2004     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
2005     # . . push args
2006     68/push  "F - test-next-word-or-expression-returns-whole-comment: end"/imm32
2007     68/push  0x11/imm32
2008     # . . push slice->end - _test-input-stream
2009     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2010     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2011     50/push-eax
2012     # . . call
2013     e8/call  check-ints-equal/disp32
2014     # . . discard args
2015     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2016     # . epilog
2017     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2018     5d/pop-to-ebp
2019     c3/return
2020 
2021 test-next-word-or-expression-returns-empty-slice-on-eof:
2022     # . prolog
2023     55/push-ebp
2024     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2025     # setup
2026     # . clear-stream(_test-input-stream)
2027     # . . push args
2028     68/push  _test-input-stream/imm32
2029     # . . call
2030     e8/call  clear-stream/disp32
2031     # . . discard args
2032     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2033     # var slice/ecx = {0, 0}
2034     68/push  0/imm32/end
2035     68/push  0/imm32/start
2036     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2037     # write nothing to _test-input-stream
2038     # next-word-or-expression(_test-input-stream, slice)
2039     # . . push args
2040     51/push-ecx
2041     68/push  _test-input-stream/imm32
2042     # . . call
2043     e8/call  next-word-or-expression/disp32
2044     # . . discard args
2045     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2046     # check-ints-equal(slice->end - slice->start, 0, msg)
2047     # . . push args
2048     68/push  "F - test-next-word-or-expression-returns-empty-expression-on-eof"/imm32
2049     68/push  0/imm32
2050     # . . push slice->end - slice->start
2051     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2052     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
2053     50/push-eax
2054     # . . call
2055     e8/call  check-ints-equal/disp32
2056     # . . discard args
2057     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2058     # . epilog
2059     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2060     5d/pop-to-ebp
2061     c3/return
2062 
2063 test-next-word-or-expression-returns-string-literal:
2064     # . prolog
2065     55/push-ebp
2066     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2067     # setup
2068     # . clear-stream(_test-input-stream)
2069     # . . push args
2070     68/push  _test-input-stream/imm32
2071     # . . call
2072     e8/call  clear-stream/disp32
2073     # . . discard args
2074     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2075     # var slice/ecx = {0, 0}
2076     68/push  0/imm32/end
2077     68/push  0/imm32/start
2078     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2079     # write(_test-input-stream, " \"a b\"/imm32 ")
2080     # . . push args
2081     68/push  " \"a b\"/imm32 "/imm32
2082     68/push  _test-input-stream/imm32
2083     # . . call
2084     e8/call  write/disp32
2085     # . . discard args
2086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2087     # next-word-or-expression(_test-input-stream, slice)
2088     # . . push args
2089     51/push-ecx
2090     68/push  _test-input-stream/imm32
2091     # . . call
2092     e8/call  next-word-or-expression/disp32
2093     # . . discard args
2094     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2095     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2096     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2097     # . . push args
2098     68/push  "F - test-next-word-or-expression-returns-string-literal: start"/imm32
2099     68/push  0xd/imm32
2100     # . . push slice->start - _test-input-stream
2101     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2102     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2103     50/push-eax
2104     # . . call
2105     e8/call  check-ints-equal/disp32
2106     # . . discard args
2107     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2108     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
2109     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
2110     # . . push args
2111     68/push  "F - test-next-word-or-expression-returns-string-literal: end"/imm32
2112     68/push  0x18/imm32
2113     # . . push slice->end - _test-input-stream
2114     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2115     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2116     50/push-eax
2117     # . . call
2118     e8/call  check-ints-equal/disp32
2119     # . . discard args
2120     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2121     # . epilog
2122     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2123     5d/pop-to-ebp
2124     c3/return
2125 
2126 test-next-word-or-expression-returns-string-with-escapes:
2127     # . prolog
2128     55/push-ebp
2129     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2130     # setup
2131     # . clear-stream(_test-input-stream)
2132     # . . push args
2133     68/push  _test-input-stream/imm32
2134     # . . call
2135     e8/call  clear-stream/disp32
2136     # . . discard args
2137     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2138     # var slice/ecx = {0, 0}
2139     68/push  0/imm32/end
2140     68/push  0/imm32/start
2141     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2142     # write(_test-input-stream, " \"a\\\"b\"/x")
2143     # . . push args
2144     68/push  " \"a\\\"b\"/x"/imm32
2145     68/push  _test-input-stream/imm32
2146     # . . call
2147     e8/call  write/disp32
2148     # . . discard args
2149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2150     # next-word-or-expression(_test-input-stream, slice)
2151     # . . push args
2152     51/push-ecx
2153     68/push  _test-input-stream/imm32
2154     # . . call
2155     e8/call  next-word-or-expression/disp32
2156     # . . discard args
2157     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2158     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2159     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2160     # . . push args
2161     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: start"/imm32
2162     68/push  0xd/imm32
2163     # . . push slice->start - _test-input-stream
2164     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2165     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2166     50/push-eax
2167     # . . call
2168     e8/call  check-ints-equal/disp32
2169     # . . discard args
2170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2171     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
2172     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
2173     # . . push args
2174     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: end"/imm32
2175     68/push  0x15/imm32
2176     # . . push slice->end - _test-input-stream
2177     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2178     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2179     50/push-eax
2180     # . . call
2181     e8/call  check-ints-equal/disp32
2182     # . . discard args
2183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2184     # . epilog
2185     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2186     5d/pop-to-ebp
2187     c3/return
2188 
2189 test-next-word-or-expression-returns-whole-expression:
2190     # . prolog
2191     55/push-ebp
2192     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2193     # setup
2194     # . clear-stream(_test-input-stream)
2195     # . . push args
2196     68/push  _test-input-stream/imm32
2197     # . . call
2198     e8/call  clear-stream/disp32
2199     # . . discard args
2200     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2201     # var slice/ecx = {0, 0}
2202     68/push  0/imm32/end
2203     68/push  0/imm32/start
2204     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2205     # write(_test-input-stream, " *(a b)/imm32 ")
2206     # . . push args
2207     68/push  " *(a b)/imm32 "/imm32
2208     68/push  _test-input-stream/imm32
2209     # . . call
2210     e8/call  write/disp32
2211     # . . discard args
2212     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2213     # next-word-or-expression(_test-input-stream, slice)
2214     # . . push args
2215     51/push-ecx
2216     68/push  _test-input-stream/imm32
2217     # . . call
2218     e8/call  next-word-or-expression/disp32
2219     # . . discard args
2220     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2221     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2222     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2223     # . . push args
2224     68/push  "F - test-next-word-or-expression-returns-whole-expression: start"/imm32
2225     68/push  0xd/imm32
2226     # . . push slice->start - _test-input-stream
2227     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2228     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2229     50/push-eax
2230     # . . call
2231     e8/call  check-ints-equal/disp32
2232     # . . discard args
2233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2234     # check-ints-equal(slice->end - _test-input-stream->data, 13, msg)
2235     # . check-ints-equal(slice->end - _test-input-stream, 25, msg)
2236     # . . push args
2237     68/push  "F - test-next-word-or-expression-returns-whole-expression: end"/imm32
2238     68/push  0x19/imm32
2239     # . . push slice->end - _test-input-stream
2240     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2241     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2242     50/push-eax
2243     # . . call
2244     e8/call  check-ints-equal/disp32
2245     # . . discard args
2246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2247     # . epilog
2248     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2249     5d/pop-to-ebp
2250     c3/return
2251 
2252 # Grammar:
2253 #   *reg                    -> 0/mod reg/rm32
2254 #   *(reg)                  -> 0/mod reg/rm32
2255 #   *(reg+disp)             -> 2/mod reg/rm32 disp/disp32
2256 #   *(reg1+reg2<<s)         -> 2/mod 4/rm32 reg1/base reg2/index s/scale 0/disp32
2257 #   *(reg1+reg2<<s+disp)    -> 2/mod 4/rm32 reg1/base reg2/index s/scale disp/disp32
2258 # Intermediate structure: base, index, scale, disp
2259 # Default values: base: 0, index: 4 (none), scale: 0, disp: 0
2260 parse-effective-address:  # word-slice : (address slice) -> base/eax, index/ecx, scale/edx, disp/ebx
2261     # pseudocode:
2262     #   var local-slice = {word-slice->start, word-slice->end}
2263     #   ++local-slice->start to skip '*'
2264     #   initialize defaults: base=0, index=4, scale=0, disp=0
2265     #   if (*local-slice->start != '(') {
2266     #     local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
2267     #     base = get-slice(Registers, local-slice, row-size=8)
2268     #     return
2269     #   }
2270     #   # compound expressions
2271     #   skip whitespace
2272     #   read register into base
2273     #   skip whitespace
2274     #   if (*local-slice->start == ')') goto end
2275     #   if (*local-slice->start == '-') goto displacement
2276     #   if (*local-slice->start != '+') goto error1
2277     #   ++local-slice->start to skip '+'
2278     #   skip whitespace
2279     #   if next 3 characters don't make a register, goto displacement
2280     #   read register into index
2281     #   skip whitespace
2282     #   if (*local-slice->start == ')') goto end
2283     #   if (*local-slice->start == '<') {
2284     #     ++local-slice->start to skip '<'
2285     #     if (*local-slice->start != '<') goto error2
2286     #     ++local-slice->start to skip '<'
2287     #     skip whitespace
2288     #     read integer into scale
2289     #     skip whitespace
2290     #     if (*local-slice->start == ')') goto end
2291     #   }
2292     #   if (*local-slice->start not in '+' '-') goto error3
2293     # displacement:
2294     #   read integer into disp
2295     #   skip whitespace
2296     #   if (*local-slice->start != ')') goto error4
2297     # . prolog
2298     55/push-ebp
2299     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2300     # . save registers
2301     56/push-esi
2302     57/push-edi
2303     # var local-slice/esi : (address slice) = {word-slice->start, word-slice->end}
2304     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2305     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2306     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2307     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
2308     # ++local-slice->start to skip '*'
2309     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2310     # initialize defaults
2311     # base is in edi; we'll move it to eax just before we return
2312     bf/copy-to-edi  0/imm32
2313     b9/copy-to-ecx  4/imm32/no-index
2314     ba/copy-to-edx  0/imm32/.scale
2315     bb/copy-to-ebx  0/imm32/disp
2316 $parse-effective-address:check-for-simple-register:
2317     # if (*local-slice->start == '(') goto compound expression
2318     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2319     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2320     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2321     3d/compare-eax-and  0x28/imm32/open-paren
2322     74/jump-if-equal  $parse-effective-address:compound-expression/disp8
2323 $parse-effective-address:simple-register:
2324     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
2325     # . . push args
2326     56/push-esi
2327     68/push  0x2f/imm32/slash
2328     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2329     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2330     # . . call
2331     e8/call  next-token-from-slice/disp32
2332     # . . discard args
2333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2334     # base = get-slice(Registers, local-slice, row-size=8)
2335     # . eax = get-slice(Registers, local-slice, row-size=8)
2336     # . . push args
2337     68/push  "Registers"/imm32
2338     68/push  8/imm32/row-size
2339     56/push-esi
2340     68/push  Registers/imm32
2341     # . . call
2342     e8/call  get-slice/disp32
2343     # . . discard args
2344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2345     # . base = *eax
2346     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy *eax to edi
2347     # return
2348     e9/jump  $parse-effective-address:end/disp32
2349 $parse-effective-address:compound-expression:
2350     # ++local-slice->start to skip '('
2351     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2352     # skip whitespace
2353     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2354     # . . push args
2355     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2356     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2357     # . . call
2358     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2359     # . . discard args
2360     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2361     # . local-slice->start = eax
2362     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2363     # read register into base
2364     # . eax = next-register(local-slice)
2365     # . . push args
2366     56/push-esi
2367     # . . call
2368     e8/call  next-register/disp32
2369     # . . discard args
2370     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2371     # . edi = *eax
2372     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy *eax to edi
2373     # skip whitespace
2374     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2375     # . . push args
2376     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2377     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2378     # . . call
2379     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2380     # . . discard args
2381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2382     # . local-slice->start = eax
2383     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2384     # if (*local-slice->start == ')') goto end
2385     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2386     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2387     3d/compare-eax-and  0x29/imm32/close-paren
2388     0f 84/jump-if-equal  $parse-effective-address:end/disp32
2389     # if (*local-slice->start == '-') goto displacement
2390     3d/compare-eax-and  0x2d/imm32/minus
2391     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
2392     # if (*local-slice->start != '+') goto error1
2393     3d/compare-eax-and  0x2b/imm32/plus
2394     0f 85/jump-if-not-equal  $parse-effective-address:error1/disp32
2395 $parse-effective-address:check-for-index:
2396     # ++local-slice->start to skip '+'
2397     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2398     # skip whitespace
2399     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2400     # . . push args
2401     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2402     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2403     # . . call
2404     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2405     # . . discard args
2406     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2407     # . local-slice->start = eax
2408     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2409 $parse-effective-address:resolve-ambiguity:
2410     # if next 3 characters don't make a register, goto displacement
2411     # . spill ecx
2412     51/push-ecx
2413     # . var tmp/ecx = {local-slice->start, local-slice->start+3}
2414     # . . ecx = local-slice->start
2415     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
2416     # . . eax = local-slice->start+3
2417     05/add-to-eax  3/imm32
2418     # . . push
2419     50/push-eax
2420     51/push-ecx
2421     # . . copy esp to ecx
2422     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2423     # . eax = maybe-get-slice(Register, tmp, row-size=8)
2424     # . . push args
2425     68/push  8/imm32/row-size
2426     51/push-ecx
2427     68/push  Registers/imm32
2428     # . . call
2429     e8/call  maybe-get-slice/disp32
2430     # . . discard args
2431     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2432     # . reclaim tmp
2433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2434     # . restore ecx
2435     59/pop-to-ecx
2436     # . if (eax == 0) goto displacement
2437     3d/compare-eax-and  0/imm32
2438     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
2439 $parse-effective-address:index:
2440     # read register into index
2441     # . eax = next-register(local-slice)
2442     # . . push args
2443     56/push-esi
2444     # . . call
2445     e8/call  next-register/disp32
2446     # . . discard args
2447     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2448     # . ecx = *eax
2449     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2450     # skip whitespace
2451     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2452     # . . push args
2453     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2454     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2455     # . . call
2456     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2457     # . . discard args
2458     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2459     # . local-slice->start = eax
2460     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2461     # if (*local-slice->start == ')') goto end
2462     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2463     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2464     3d/compare-eax-and  0x29/imm32/close-paren
2465     0f 84/jump-if-equal  $parse-effective-address:end/disp32
2466 $parse-effective-address:check-for-scale:
2467     # if (*local-slice->start != '<') goto next check
2468     3d/compare-eax-and  0x3c/imm32/less-than
2469     75/jump-if-not-equal  $parse-effective-address:check-for-displacement/disp8
2470     # ++local-slice->start to skip '<'
2471     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2472     # if (*local-slice->start != '<') goto error2
2473     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2474     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2475     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2476     3d/compare-eax-and  0x3c/imm32/less-than
2477     0f 85/jump-if-not-equal  $parse-effective-address:error2/disp32
2478     # ++local-slice->start to skip '<'
2479     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2480     # skip whitespace
2481     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2482     # . . push args
2483     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2484     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2485     # . . call
2486     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2487     # . . discard args
2488     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2489     # . local-slice->start = eax
2490     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2491 $parse-effective-address:scale:
2492     # read positive integer into scale
2493     # . eax = next-positive-hex-int(local-slice)
2494     # . . push args
2495     56/push-esi
2496     # . . call
2497     e8/call  next-positive-hex-int/disp32
2498     # . . discard args
2499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2500     # . edx = eax
2501     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
2502     # skip whitespace
2503     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2504     # . . push args
2505     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2506     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2507     # . . call
2508     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2509     # . . discard args
2510     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2511     # . local-slice->start = eax
2512     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2513     # if (*local-slice->start == ')') goto end
2514     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2515     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2516     3d/compare-eax-and  0x29/imm32/close-paren
2517     74/jump-if-equal  $parse-effective-address:end/disp8
2518 $parse-effective-address:check-for-displacement:
2519     # if (*local-slice->start not in '+' '-') goto error3
2520     3d/compare-eax-and  0x2b/imm32/plus
2521     74/jump-if-equal  $parse-effective-address:displacement/disp8
2522     3d/compare-eax-and  0x2d/imm32/minus
2523     74/jump-if-equal  $parse-effective-address:displacement/disp8
2524     e9/jump  $parse-effective-address:error3/disp32
2525 $parse-effective-address:displacement:
2526     # read integer into disp
2527     # . eax = next-hex-int(local-slice)
2528     # . . push args
2529     56/push-esi
2530     # . . call
2531     e8/call  next-hex-int/disp32
2532     # . . discard args
2533     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2534     # . ebx = eax
2535     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2536     # skip whitespace
2537     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2538     # . . push args
2539     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2540     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2541     # . . call
2542     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2543     # . . discard args
2544     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2545     # . local-slice->start = eax
2546     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2547     # if (*local-slice->start != ')') goto error4
2548     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2549     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2550     3d/compare-eax-and  0x29/imm32/close-paren
2551     0f 85/jump-if-not-equal  $parse-effective-address:error4/disp32
2552 $parse-effective-address:end:
2553     # return base in eax
2554     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
2555     # . reclaim locals
2556     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2557     # . restore registers
2558     5f/pop-to-edi
2559     5e/pop-to-esi
2560     # . epilog
2561     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2562     5d/pop-to-ebp
2563     c3/return
2564 
2565 $parse-effective-address:error1:
2566     # print(stderr, "error: unexpected character: " eax "\n")
2567     # . write-buffered(Stderr, "error: unexpected character: ")
2568     # . . push args
2569     68/push  "error: unexpected character: "/imm32
2570     68/push  Stderr/imm32
2571     # . . call
2572     e8/call  write-buffered/disp32
2573     # . . discard args
2574     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2575     # . print-int32-buffered(out, eax)
2576     # . . push args
2577     50/push-eax
2578     68/push  Stderr/imm32
2579     # . . call
2580     e8/call  print-int32-buffered/disp32
2581     # . . discard args
2582     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2583     # . write-buffered(Stderr, "\n")
2584     # . . push args
2585     68/push  "\n"/imm32
2586     68/push  Stderr/imm32
2587     # . . call
2588     e8/call  write-buffered/disp32
2589     # . . discard args
2590     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2591     # . flush(Stderr)
2592     # . . push args
2593     68/push  Stderr/imm32
2594     # . . call
2595     e8/call  flush/disp32
2596     # . . discard args
2597     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2598     # . syscall(exit, 1)
2599     bb/copy-to-ebx  1/imm32
2600     b8/copy-to-eax  1/imm32/exit
2601     cd/syscall  0x80/imm8
2602     # never gets here
2603 
2604 $parse-effective-address:error2:
2605     # print(stderr, "error: '<' can only be followed by '<' but got: " eax "\n")
2606     # . write-buffered(Stderr, "error: '<' can only be followed by '<' but got: ")
2607     # . . push args
2608     68/push  "error: '<' can only be followed by '<' but got: "/imm32
2609     68/push  Stderr/imm32
2610     # . . call
2611     e8/call  write-buffered/disp32
2612     # . . discard args
2613     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2614     # . print-int32-buffered(out, eax)
2615     # . . push args
2616     50/push-eax
2617     68/push  Stderr/imm32
2618     # . . call
2619     e8/call  print-int32-buffered/disp32
2620     # . . discard args
2621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2622     # . write-buffered(Stderr, "\n")
2623     # . . push args
2624     68/push  "\n"/imm32
2625     68/push  Stderr/imm32
2626     # . . call
2627     e8/call  write-buffered/disp32
2628     # . . discard args
2629     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2630     # . flush(Stderr)
2631     # . . push args
2632     68/push  Stderr/imm32
2633     # . . call
2634     e8/call  flush/disp32
2635     # . . discard args
2636     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2637     # . syscall(exit, 1)
2638     bb/copy-to-ebx  1/imm32
2639     b8/copy-to-eax  1/imm32/exit
2640     cd/syscall  0x80/imm8
2641     # never gets here
2642 
2643 $parse-effective-address:error3:
2644     # print(stderr, "error: unexpected character before displacement: " eax "\n")
2645     # . write-buffered(Stderr, "error: unexpected character before displacement: ")
2646     # . . push args
2647     68/push  "error: unexpected character before displacement: "/imm32
2648     68/push  Stderr/imm32
2649     # . . call
2650     e8/call  write-buffered/disp32
2651     # . . discard args
2652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2653     # . print-int32-buffered(out, eax)
2654     # . . push args
2655     50/push-eax
2656     68/push  Stderr/imm32
2657     # . . call
2658     e8/call  print-int32-buffered/disp32
2659     # . . discard args
2660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2661     # . write-buffered(Stderr, "\n")
2662     # . . push args
2663     68/push  "\n"/imm32
2664     68/push  Stderr/imm32
2665     # . . call
2666     e8/call  write-buffered/disp32
2667     # . . discard args
2668     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2669     # . flush(Stderr)
2670     # . . push args
2671     68/push  Stderr/imm32
2672     # . . call
2673     e8/call  flush/disp32
2674     # . . discard args
2675     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2676     # . syscall(exit, 1)
2677     bb/copy-to-ebx  1/imm32
2678     b8/copy-to-eax  1/imm32/exit
2679     cd/syscall  0x80/imm8
2680     # never gets here
2681 
2682 $parse-effective-address:error4:
2683     # print(stderr, "error: unexpected character after displacement: " eax "; expected ')' to wrap up\n")
2684     # . write-buffered(Stderr, "error: unexpected character after displacement: ")
2685     # . . push args
2686     68/push  "error: unexpected character after displacement: "/imm32
2687     68/push  Stderr/imm32
2688     # . . call
2689     e8/call  write-buffered/disp32
2690     # . . discard args
2691     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2692     # . print-int32-buffered(out, eax)
2693     # . . push args
2694     50/push-eax
2695     68/push  Stderr/imm32
2696     # . . call
2697     e8/call  print-int32-buffered/disp32
2698     # . . discard args
2699     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2700     # . write-buffered(Stderr, "; expected ')' to wrap up\n")
2701     # . . push args
2702     68/push  "; expected ')' to wrap up\n"/imm32
2703     68/push  Stderr/imm32
2704     # . . call
2705     e8/call  write-buffered/disp32
2706     # . . discard args
2707     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2708     # . flush(Stderr)
2709     # . . push args
2710     68/push  Stderr/imm32
2711     # . . call
2712     e8/call  flush/disp32
2713     # . . discard args
2714     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2715     # . syscall(exit, 1)
2716     bb/copy-to-ebx  1/imm32
2717     b8/copy-to-eax  1/imm32/exit
2718     cd/syscall  0x80/imm8
2719     # never gets here
2720 
2721 # assumes 'in' starts with a register name, and returns pointer to its code
2722 # side-effect: modifies 'in' to scan past the initial register name
2723 next-register:  # in : (address slice) -> reg/eax : int
2724     # . prolog
2725     55/push-ebp
2726     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2727     # . save registers
2728     51/push-ecx
2729     56/push-esi
2730     # esi = in
2731     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2732     # var reg-slice/ecx : (address slice) = {in->start, in->start + 3}
2733     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2734     05/add-to-eax  3/imm32
2735     50/push-eax
2736     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2737     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2738     # in->start += 3
2739     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               3/imm32           # add to *esi
2740     # eax = get-slice(Registers, reg-slice, row-size=8)
2741     # . . push args
2742     68/push  "next-register"/imm32
2743     68/push  8/imm32/row-size
2744     51/push-ecx
2745     68/push  Registers/imm32
2746     # . . call
2747     e8/call  get-slice/disp32
2748     # . . discard args
2749     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2750 $next-register:end:
2751     # . reclaim locals
2752     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2753     # . restore registers
2754     5e/pop-to-esi
2755     59/pop-to-ecx
2756     # . epilog
2757     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2758     5d/pop-to-ebp
2759     c3/return
2760 
2761 test-parse-effective-address-simple:
2762     # . prolog
2763     55/push-ebp
2764     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2765     # var slice/ecx = "*esi"
2766     b8/copy-to-eax  "*esi"/imm32
2767     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2768     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
2769     05/add-to-eax  4/imm32
2770     # . ecx = {eax, ecx}
2771     51/push-ecx
2772     50/push-eax
2773     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2774     # eax, ecx, edx, ebx = parse-effective-address(slice)
2775     # . . push args
2776     51/push-ecx
2777     # . . call
2778     e8/call  parse-effective-address/disp32
2779     # . . discard args
2780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2781     # slice clobbered beyond this point
2782     # check-ints-equal(eax, 6, msg)
2783     # . . push args
2784     68/push  "F - test-parse-effective-address-simple/base"/imm32
2785     68/push  6/imm32/esi
2786     50/push-eax
2787     # . . call
2788     e8/call  check-ints-equal/disp32
2789     # . . discard args
2790     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2791     # check-ints-equal(ecx, 4, msg)
2792     # . . push args
2793     68/push  "F - test-parse-effective-address-simple/index"/imm32
2794     68/push  4/imm32/none
2795     51/push-ecx
2796     # . . call
2797     e8/call  check-ints-equal/disp32
2798     # . . discard args
2799     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2800     # check-ints-equal(edx, 0, msg)
2801     # . . push args
2802     68/push  "F - test-parse-effective-address-simple/scale"/imm32
2803     68/push  0/imm32/none
2804     52/push-edx
2805     # . . call
2806     e8/call  check-ints-equal/disp32
2807     # . . discard args
2808     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2809     # check-ints-equal(ebx, 0, msg)
2810     # . . push args
2811     68/push  "F - test-parse-effective-address-simple/displacement"/imm32
2812     68/push  0/imm32/none
2813     53/push-ebx
2814     # . . call
2815     e8/call  check-ints-equal/disp32
2816     # . . discard args
2817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2818     # . epilog
2819     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2820     5d/pop-to-ebp
2821     c3/return
2822 
2823 test-parse-effective-address-base:
2824     # . prolog
2825     55/push-ebp
2826     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2827     # var slice/ecx = "*(esi  )"
2828     b8/copy-to-eax  "*(esi  )"/imm32
2829     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2830     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
2831     05/add-to-eax  4/imm32
2832     # . ecx = {eax, ecx}
2833     51/push-ecx
2834     50/push-eax
2835     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2836     # eax, ecx, edx, ebx = parse-effective-address(slice)
2837     # . . push args
2838     51/push-ecx
2839     # . . call
2840     e8/call  parse-effective-address/disp32
2841     # . . discard args
2842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2843     # slice clobbered beyond this point
2844     # check-ints-equal(eax, 6, msg)
2845     # . . push args
2846     68/push  "F - test-parse-effective-address-base/base"/imm32
2847     68/push  6/imm32/esi
2848     50/push-eax
2849     # . . call
2850     e8/call  check-ints-equal/disp32
2851     # . . discard args
2852     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2853     # check-ints-equal(ecx, 4, msg)
2854     # . . push args
2855     68/push  "F - test-parse-effective-address-base/index"/imm32
2856     68/push  4/imm32/none
2857     51/push-ecx
2858     # . . call
2859     e8/call  check-ints-equal/disp32
2860     # . . discard args
2861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2862     # check-ints-equal(edx, 0, msg)
2863     # . . push args
2864     68/push  "F - test-parse-effective-address-base/scale"/imm32
2865     68/push  0/imm32/none
2866     52/push-edx
2867     # . . call
2868     e8/call  check-ints-equal/disp32
2869     # . . discard args
2870     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2871     # check-ints-equal(ebx, 0, msg)
2872     # . . push args
2873     68/push  "F - test-parse-effective-address-base/displacement"/imm32
2874     68/push  0/imm32/none
2875     53/push-ebx
2876     # . . call
2877     e8/call  check-ints-equal/disp32
2878     # . . discard args
2879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2880     # . epilog
2881     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2882     5d/pop-to-ebp
2883     c3/return
2884 
2885 test-parse-effective-address-base-displacement:
2886     # . prolog
2887     55/push-ebp
2888     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2889     # var slice/ecx = "*(esi+3)"
2890     b8/copy-to-eax  "*(esi+3)"/imm32
2891     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2892     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
2893     05/add-to-eax  4/imm32
2894     # . ecx = {eax, ecx}
2895     51/push-ecx
2896     50/push-eax
2897     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2898     # eax, ecx, edx, ebx = parse-effective-address(slice)
2899     # . . push args
2900     51/push-ecx
2901     # . . call
2902     e8/call  parse-effective-address/disp32
2903     # . . discard args
2904     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2905     # slice clobbered beyond this point
2906     # check-ints-equal(eax, 6, msg)
2907     # . . push args
2908     68/push  "F - test-parse-effective-address-base-displacement/base"/imm32
2909     68/push  6/imm32/esi
2910     50/push-eax
2911     # . . call
2912     e8/call  check-ints-equal/disp32
2913     # . . discard args
2914     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2915     # check-ints-equal(ecx, 4, msg)
2916     # . . push args
2917     68/push  "F - test-parse-effective-address-base-displacement/index"/imm32
2918     68/push  4/imm32/none
2919     51/push-ecx
2920     # . . call
2921     e8/call  check-ints-equal/disp32
2922     # . . discard args
2923     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2924     # check-ints-equal(edx, 0, msg)
2925     # . . push args
2926     68/push  "F - test-parse-effective-address-base-displacement/scale"/imm32
2927     68/push  0/imm32/none
2928     52/push-edx
2929     # . . call
2930     e8/call  check-ints-equal/disp32
2931     # . . discard args
2932     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2933     # check-ints-equal(ebx, 3, msg)
2934     # . . push args
2935     68/push  "F - test-parse-effective-address-base-displacement/displacement"/imm32
2936     68/push  3/imm32
2937     53/push-ebx
2938     # . . call
2939     e8/call  check-ints-equal/disp32
2940     # . . discard args
2941     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2942     # . epilog
2943     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2944     5d/pop-to-ebp
2945     c3/return
2946 
2947 test-parse-effective-address-base-negative-displacement:
2948     # . prolog
2949     55/push-ebp
2950     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2951     # var slice/ecx = "*(esi-3)"
2952     b8/copy-to-eax  "*(esi-3)"/imm32
2953     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2954     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
2955     05/add-to-eax  4/imm32
2956     # . ecx = {eax, ecx}
2957     51/push-ecx
2958     50/push-eax
2959     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2960     # eax, ecx, edx, ebx = parse-effective-address(slice)
2961     # . . push args
2962     51/push-ecx
2963     # . . call
2964     e8/call  parse-effective-address/disp32
2965     # . . discard args
2966     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2967     # slice clobbered beyond this point
2968     # check-ints-equal(eax, 6, msg)
2969     # . . push args
2970     68/push  "F - test-parse-effective-address-base-negative-displacement/base"/imm32
2971     68/push  6/imm32/esi
2972     50/push-eax
2973     # . . call
2974     e8/call  check-ints-equal/disp32
2975     # . . discard args
2976     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2977     # check-ints-equal(ecx, 4, msg)
2978     # . . push args
2979     68/push  "F - test-parse-effective-address-base-negative-displacement/index"/imm32
2980     68/push  4/imm32/none
2981     51/push-ecx
2982     # . . call
2983     e8/call  check-ints-equal/disp32
2984     # . . discard args
2985     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2986     # check-ints-equal(edx, 0, msg)
2987     # . . push args
2988     68/push  "F - test-parse-effective-address-base-negative-displacement/scale"/imm32
2989     68/push  0/imm32/none
2990     52/push-edx
2991     # . . call
2992     e8/call  check-ints-equal/disp32
2993     # . . discard args
2994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2995     # check-ints-equal(ebx, -3, msg)
2996     # . . push args
2997     68/push  "F - test-parse-effective-address-base-negative-displacement/displacement"/imm32
2998     68/push  -3/imm32
2999     53/push-ebx
3000     # . . call
3001     e8/call  check-ints-equal/disp32
3002     # . . discard args
3003     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3004     # . epilog
3005     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3006     5d/pop-to-ebp
3007     c3/return
3008 
3009 test-parse-effective-address-base-index:
3010     # . prolog
3011     55/push-ebp
3012     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3013     # var slice/ecx = "*(esi+ecx)"
3014     b8/copy-to-eax  "*(esi+ecx)"/imm32
3015     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3016     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
3017     05/add-to-eax  4/imm32
3018     # . ecx = {eax, ecx}
3019     51/push-ecx
3020     50/push-eax
3021     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3022     # eax, ecx, edx, ebx = parse-effective-address(slice)
3023     # . . push args
3024     51/push-ecx
3025     # . . call
3026     e8/call  parse-effective-address/disp32
3027     # . . discard args
3028     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3029     # slice clobbered beyond this point
3030     # check-ints-equal(eax, 6, msg)
3031     # . . push args
3032     68/push  "F - test-parse-effective-address-base-index/base"/imm32
3033     68/push  6/imm32/esi
3034     50/push-eax
3035     # . . call
3036     e8/call  check-ints-equal/disp32
3037     # . . discard args
3038     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3039     # check-ints-equal(ecx, 1, msg)
3040     # . . push args
3041     68/push  "F - test-parse-effective-address-base-index/index"/imm32
3042     68/push  1/imm32/none
3043     51/push-ecx
3044     # . . call
3045     e8/call  check-ints-equal/disp32
3046     # . . discard args
3047     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3048     # check-ints-equal(edx, 0, msg)
3049     # . . push args
3050     68/push  "F - test-parse-effective-address-base-index/scale"/imm32
3051     68/push  0/imm32/none
3052     52/push-edx
3053     # . . call
3054     e8/call  check-ints-equal/disp32
3055     # . . discard args
3056     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3057     # check-ints-equal(ebx, 0, msg)
3058     # . . push args
3059     68/push  "F - test-parse-effective-address-base-index/displacement"/imm32
3060     68/push  0/imm32
3061     53/push-ebx
3062     # . . call
3063     e8/call  check-ints-equal/disp32
3064     # . . discard args
3065     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3066     # . epilog
3067     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3068     5d/pop-to-ebp
3069     c3/return
3070 
3071 test-parse-effective-address-base-index-scale:
3072     # . prolog
3073     55/push-ebp
3074     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3075     # var slice/ecx = "*(esi+ecx<<2)"
3076     b8/copy-to-eax  "*(esi+ecx<<2)"/imm32
3077     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3078     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
3079     05/add-to-eax  4/imm32
3080     # . ecx = {eax, ecx}
3081     51/push-ecx
3082     50/push-eax
3083     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3084     # eax, ecx, edx, ebx = parse-effective-address(slice)
3085     # . . push args
3086     51/push-ecx
3087     # . . call
3088     e8/call  parse-effective-address/disp32
3089     # . . discard args
3090     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3091     # slice clobbered beyond this point
3092     # check-ints-equal(eax, 6, msg)
3093     # . . push args
3094     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
3095     68/push  6/imm32/esi
3096     50/push-eax
3097     # . . call
3098     e8/call  check-ints-equal/disp32
3099     # . . discard args
3100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3101     # check-ints-equal(ecx, 1, msg)
3102     # . . push args
3103     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
3104     68/push  1/imm32/none
3105     51/push-ecx
3106     # . . call
3107     e8/call  check-ints-equal/disp32
3108     # . . discard args
3109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3110     # check-ints-equal(edx, 2, msg)
3111     # . . push args
3112     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
3113     68/push  2/imm32
3114     52/push-edx
3115     # . . call
3116     e8/call  check-ints-equal/disp32
3117     # . . discard args
3118     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3119     # check-ints-equal(ebx, 0, msg)
3120     # . . push args
3121     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
3122     68/push  0/imm32
3123     53/push-ebx
3124     # . . call
3125     e8/call  check-ints-equal/disp32
3126     # . . discard args
3127     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3128     # . epilog
3129     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3130     5d/pop-to-ebp
3131     c3/return
3132 
3133 test-parse-effective-address-base-index-scale-displacement:
3134     # . prolog
3135     55/push-ebp
3136     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3137     # var slice/ecx = "*(esi + ecx<<2 - 0x34)"
3138     b8/copy-to-eax  "*(esi + ecx<<2 - 0x34)"/imm32
3139     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3140     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
3141     05/add-to-eax  4/imm32
3142     # . ecx = {eax, ecx}
3143     51/push-ecx
3144     50/push-eax
3145     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3146     # eax, ecx, edx, ebx = parse-effective-address(slice)
3147     # . . push args
3148     51/push-ecx
3149     # . . call
3150     e8/call  parse-effective-address/disp32
3151     # . . discard args
3152     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3153     # slice clobbered beyond this point
3154     # check-ints-equal(eax, 6, msg)
3155     # . . push args
3156     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
3157     68/push  6/imm32/esi
3158     50/push-eax
3159     # . . call
3160     e8/call  check-ints-equal/disp32
3161     # . . discard args
3162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3163     # check-ints-equal(ecx, 1, msg)
3164     # . . push args
3165     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
3166     68/push  1/imm32/none
3167     51/push-ecx
3168     # . . call
3169     e8/call  check-ints-equal/disp32
3170     # . . discard args
3171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3172     # check-ints-equal(edx, 2, msg)
3173     # . . push args
3174     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
3175     68/push  2/imm32
3176     52/push-edx
3177     # . . call
3178     e8/call  check-ints-equal/disp32
3179     # . . discard args
3180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3181     # check-ints-equal(ebx, -0x34, msg)
3182     # . . push args
3183     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
3184     68/push  -0x34/imm32
3185     53/push-ebx
3186     # . . call
3187     e8/call  check-ints-equal/disp32
3188     # . . discard args
3189     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3190     # . epilog
3191     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3192     5d/pop-to-ebp
3193     c3/return
3194 
3195 # Code generation:
3196 #   if base is esp, then goto emit-sib
3197 #   if base is ebp, then goto emit-sib
3198 #   if index is none and disp is 0, then mod = 0 and rm32 = base
3199 #   if index is none, then mod = 2 and rm32 = base and disp32 = disp
3200 # emit-sib:
3201 #   if index is not none, then mod = 2 and rm32 = 4 and base = base and index = index and disp32 = disp
3202 emit-indirect-mode:  # out : (address buffered-file), base : int, index : int, scale : int, disp : int
3203     # . prolog
3204     55/push-ebp
3205     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3206 $emit-indirect-mode:check-for-ebp:
3207     # if (base == 5) goto emit-sib
3208     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       5/imm32           # compare *(ebp+12)
3209     74/jump-if-equal  $emit-indirect-mode:emit-sib/disp8
3210 $emit-indirect-mode:check-for-esp:
3211     # if (base == 4) goto emit-sib
3212     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       4/imm32           # compare *(ebp+12)
3213     74/jump-if-equal  $emit-indirect-mode:emit-sib/disp8
3214 $emit-indirect-mode:check-for-sib:
3215     # if (index == 4/none) goto next check
3216     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      4/imm32           # compare *(ebp+16)
3217     0f 84/jump-if-equal  $emit-indirect-mode:check-for-disp/disp32
3218 $emit-indirect-mode:emit-sib:
3219     # emit(out, "2/mod/indirect 4/rm32/sib " base "/base " index "/index " scale "/scale " disp "/disp32")
3220     # . write-buffered(out, "2/mod/*+disp32 4/rm32/sib ")
3221     # . . push args
3222     68/push  "2/mod/*+disp32 4/rm32/sib "/imm32
3223     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3224     # . . call
3225     e8/call  write-buffered/disp32
3226     # . . discard args
3227     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3228     # . print-int32-buffered(out, base)
3229     # . . push args
3230     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3231     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3232     # . . call
3233     e8/call  print-int32-buffered/disp32
3234     # . . discard args
3235     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3236     # . write-buffered(out, "/base ")
3237     # . . push args
3238     68/push  "/base "/imm32
3239     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3240     # . . call
3241     e8/call  write-buffered/disp32
3242     # . . discard args
3243     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3244     # . print-int32-buffered(out, index)
3245     # . . push args
3246     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3247     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3248     # . . call
3249     e8/call  print-int32-buffered/disp32
3250     # . . discard args
3251     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3252     # . write-buffered(out, "/index ")
3253     # . . push args
3254     68/push  "/index "/imm32
3255     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3256     # . . call
3257     e8/call  write-buffered/disp32
3258     # . . discard args
3259     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3260     # . print-int32-buffered(out, scale)
3261     # . . push args
3262     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
3263     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3264     # . . call
3265     e8/call  print-int32-buffered/disp32
3266     # . . discard args
3267     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3268     # . write-buffered(out, "/scale ")
3269     # . . push args
3270     68/push  "/scale "/imm32
3271     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3272     # . . call
3273     e8/call  write-buffered/disp32
3274     # . . discard args
3275     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3276     # . print-int32-buffered(out, disp)
3277     # . . push args
3278     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3279     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3280     # . . call
3281     e8/call  print-int32-buffered/disp32
3282     # . . discard args
3283     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3284     # . write-buffered(out, "/disp32")
3285     # . . push args
3286     68/push  "/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     e9/jump  $emit-indirect-mode:end/disp32
3293 $emit-indirect-mode:check-for-disp:
3294     # if (disp == 0) goto next check
3295     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      0/imm32           # compare *(ebp+24)
3296     74/jump-if-equal  $emit-indirect-mode:emit-indirect/disp8
3297 $emit-indirect-mode:emit-disp:
3298     # emit(out, "2/mod/*+disp32 " base "/rm32 " disp "/disp32")
3299     # . write-buffered(out, "2/mod/*+disp32 ")
3300     # . . push args
3301     68/push  "2/mod/*+disp32 "/imm32
3302     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3303     # . . call
3304     e8/call  write-buffered/disp32
3305     # . . discard args
3306     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3307     # . print-int32-buffered(out, base)
3308     # . . push args
3309     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3310     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3311     # . . call
3312     e8/call  print-int32-buffered/disp32
3313     # . . discard args
3314     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3315     # . write-buffered(out, "/rm32 ")
3316     # . . push args
3317     68/push  "/rm32 "/imm32
3318     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3319     # . . call
3320     e8/call  write-buffered/disp32
3321     # . . discard args
3322     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3323     # . print-int32-buffered(out, disp)
3324     # . . push args
3325     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3326     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3327     # . . call
3328     e8/call  print-int32-buffered/disp32
3329     # . . discard args
3330     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3331     # . write-buffered(out, "/disp32")
3332     # . . push args
3333     68/push  "/disp32"/imm32
3334     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3335     # . . call
3336     e8/call  write-buffered/disp32
3337     # . . discard args
3338     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3339     eb/jump  $emit-indirect-mode:end/disp8
3340 $emit-indirect-mode:emit-indirect:
3341     # emit(out, "0/mod/indirect " base "/rm32")
3342     # . write-buffered(out, "0/mod/indirect ")
3343     # . . push args
3344     68/push  "0/mod/indirect "/imm32
3345     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3346     # . . call
3347     e8/call  write-buffered/disp32
3348     # . . discard args
3349     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3350     # . print-int32-buffered(out, base)
3351     # . . push args
3352     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3353     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3354     # . . call
3355     e8/call  print-int32-buffered/disp32
3356     # . . discard args
3357     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3358     # . write-buffered(out, "/rm32")
3359     # . . push args
3360     68/push  "/rm32"/imm32
3361     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3362     # . . call
3363     e8/call  write-buffered/disp32
3364     # . . discard args
3365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3366 $emit-indirect-mode:end:
3367     # . epilog
3368     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3369     5d/pop-to-ebp
3370     c3/return
3371 
3372 test-emit-indirect-mode:
3373     # . prolog
3374     55/push-ebp
3375     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3376     # setup
3377     # . clear-stream(_test-output-stream)
3378     # . . push args
3379     68/push  _test-output-stream/imm32
3380     # . . call
3381     e8/call  clear-stream/disp32
3382     # . . discard args
3383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3384     # . clear-stream(_test-output-buffered-file+4)
3385     # . . push args
3386     b8/copy-to-eax  _test-output-buffered-file/imm32
3387     05/add-to-eax  4/imm32
3388     50/push-eax
3389     # . . call
3390     e8/call  clear-stream/disp32
3391     # . . discard args
3392     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3393     # emit-indirect-mode(_test-output-buffered-file, 0, 4/none, 0, 0)
3394     # . . write args
3395     68/push  0/imm32/.disp
3396     68/push  0/imm32/.scale
3397     68/push  4/imm32/.index/none
3398     68/push  0/imm32/.base
3399     68/push  _test-output-buffered-file/imm32
3400     # . . call
3401     e8/call  emit-indirect-mode/disp32
3402     # . . discard args
3403     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3404     # . flush(_test-output-buffered-file)
3405     # . . push args
3406     68/push  _test-output-buffered-file/imm32
3407     # . . call
3408     e8/call  flush/disp32
3409     # . . discard args
3410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3411 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3437     # check-stream-equal(_test-output-stream, "0/mod/indirect 0/rm32", msg)
3438     # . . push args
3439     68/push  "F - test-emit-indirect-mode"/imm32
3440     68/push  "0/mod/indirect 0x00000000/rm32"/imm32
3441     68/push  _test-output-stream/imm32
3442     # . . call
3443     e8/call  check-stream-equal/disp32
3444     # . . discard args
3445     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3446     # . epilog
3447     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3448     5d/pop-to-ebp
3449     c3/return
3450 
3451 test-emit-indirect-mode-2:
3452     # . prolog
3453     55/push-ebp
3454     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3455     # setup
3456     # . clear-stream(_test-output-stream)
3457     # . . push args
3458     68/push  _test-output-stream/imm32
3459     # . . call
3460     e8/call  clear-stream/disp32
3461     # . . discard args
3462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3463     # . clear-stream(_test-output-buffered-file+4)
3464     # . . push args
3465     b8/copy-to-eax  _test-output-buffered-file/imm32
3466     05/add-to-eax  4/imm32
3467     50/push-eax
3468     # . . call
3469     e8/call  clear-stream/disp32
3470     # . . discard args
3471     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3472     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 0)
3473     # . . write args
3474     68/push  0/imm32/.disp
3475     68/push  0/imm32/.scale
3476     68/push  4/imm32/.index/none
3477     68/push  7/imm32/.base
3478     68/push  _test-output-buffered-file/imm32
3479     # . . call
3480     e8/call  emit-indirect-mode/disp32
3481     # . . discard args
3482     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3483     # . flush(_test-output-buffered-file)
3484     # . . push args
3485     68/push  _test-output-buffered-file/imm32
3486     # . . call
3487     e8/call  flush/disp32
3488     # . . discard args
3489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3490 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3516     # check-stream-equal(_test-output-stream, "0/mod/indirect 7/rm32", msg)
3517     # . . push args
3518     68/push  "F - test-emit-indirect-mode-2"/imm32
3519     68/push  "0/mod/indirect 0x00000007/rm32"/imm32
3520     68/push  _test-output-stream/imm32
3521     # . . call
3522     e8/call  check-stream-equal/disp32
3523     # . . discard args
3524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3525     # . epilog
3526     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3527     5d/pop-to-ebp
3528     c3/return
3529 
3530 test-emit-indirect-mode-with-disp:
3531     # . prolog
3532     55/push-ebp
3533     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3534     # setup
3535     # . clear-stream(_test-output-stream)
3536     # . . push args
3537     68/push  _test-output-stream/imm32
3538     # . . call
3539     e8/call  clear-stream/disp32
3540     # . . discard args
3541     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3542     # . clear-stream(_test-output-buffered-file+4)
3543     # . . push args
3544     b8/copy-to-eax  _test-output-buffered-file/imm32
3545     05/add-to-eax  4/imm32
3546     50/push-eax
3547     # . . call
3548     e8/call  clear-stream/disp32
3549     # . . discard args
3550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3551     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 4)
3552     # . . write args
3553     68/push  4/imm32/.disp
3554     68/push  0/imm32/.scale
3555     68/push  4/imm32/.index/none
3556     68/push  6/imm32/.base
3557     68/push  _test-output-buffered-file/imm32
3558     # . . call
3559     e8/call  emit-indirect-mode/disp32
3560     # . . discard args
3561     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3562     # . flush(_test-output-buffered-file)
3563     # . . push args
3564     68/push  _test-output-buffered-file/imm32
3565     # . . call
3566     e8/call  flush/disp32
3567     # . . discard args
3568     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3569 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3595     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 4/disp32", msg)
3596     # . . push args
3597     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
3598     68/push  "2/mod/*+disp32 0x00000006/rm32 0x00000004/disp32"/imm32
3599     68/push  _test-output-stream/imm32
3600     # . . call
3601     e8/call  check-stream-equal/disp32
3602     # . . discard args
3603     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3604     # . epilog
3605     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3606     5d/pop-to-ebp
3607     c3/return
3608 
3609 test-emit-indirect-mode-with-disp-negative:
3610     # . prolog
3611     55/push-ebp
3612     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3613     # setup
3614     # . clear-stream(_test-output-stream)
3615     # . . push args
3616     68/push  _test-output-stream/imm32
3617     # . . call
3618     e8/call  clear-stream/disp32
3619     # . . discard args
3620     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3621     # . clear-stream(_test-output-buffered-file+4)
3622     # . . push args
3623     b8/copy-to-eax  _test-output-buffered-file/imm32
3624     05/add-to-eax  4/imm32
3625     50/push-eax
3626     # . . call
3627     e8/call  clear-stream/disp32
3628     # . . discard args
3629     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3630     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, -4)
3631     # . . write args
3632     68/push  -4/imm32/.disp
3633     68/push  0/imm32/.scale
3634     68/push  4/imm32/.index/none
3635     68/push  6/imm32/.base
3636     68/push  _test-output-buffered-file/imm32
3637     # . . call
3638     e8/call  emit-indirect-mode/disp32
3639     # . . discard args
3640     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3641     # . flush(_test-output-buffered-file)
3642     # . . push args
3643     68/push  _test-output-buffered-file/imm32
3644     # . . call
3645     e8/call  flush/disp32
3646     # . . discard args
3647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3648 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3674     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 -4/disp32", msg)
3675     # . . push args
3676     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
3677     68/push  "2/mod/*+disp32 0x00000006/rm32 0xfffffffc/disp32"/imm32
3678     68/push  _test-output-stream/imm32
3679     # . . call
3680     e8/call  check-stream-equal/disp32
3681     # . . discard args
3682     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3683     # . epilog
3684     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3685     5d/pop-to-ebp
3686     c3/return
3687 
3688 test-emit-indirect-mode-with-sib:
3689     # . prolog
3690     55/push-ebp
3691     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3692     # setup
3693     # . clear-stream(_test-output-stream)
3694     # . . push args
3695     68/push  _test-output-stream/imm32
3696     # . . call
3697     e8/call  clear-stream/disp32
3698     # . . discard args
3699     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3700     # . clear-stream(_test-output-buffered-file+4)
3701     # . . push args
3702     b8/copy-to-eax  _test-output-buffered-file/imm32
3703     05/add-to-eax  4/imm32
3704     50/push-eax
3705     # . . call
3706     e8/call  clear-stream/disp32
3707     # . . discard args
3708     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3709     # emit-indirect-mode(_test-output-buffered-file, 6/base, 1/index, 2/scale, 4/disp)
3710     # . . write args
3711     68/push  4/imm32/.disp
3712     68/push  2/imm32/.scale
3713     68/push  1/imm32/.index
3714     68/push  6/imm32/.base
3715     68/push  _test-output-buffered-file/imm32
3716     # . . call
3717     e8/call  emit-indirect-mode/disp32
3718     # . . discard args
3719     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3720     # . flush(_test-output-buffered-file)
3721     # . . push args
3722     68/push  _test-output-buffered-file/imm32
3723     # . . call
3724     e8/call  flush/disp32
3725     # . . discard args
3726     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3727 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3753     # check-stream-equal(_test-output-stream, "2/mod/indirect 4/rm32/sib 6/base 1/index 2/scale 4/disp", msg)
3754     # . . push args
3755     68/push  "F - test-emit-indirect-mode-with-sib"/imm32
3756     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000006/base 0x00000001/index 0x00000002/scale 0x00000004/disp32"/imm32
3757     68/push  _test-output-stream/imm32
3758     # . . call
3759     e8/call  check-stream-equal/disp32
3760     # . . discard args
3761     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3762     # . epilog
3763     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3764     5d/pop-to-ebp
3765     c3/return
3766 
3767 test-emit-indirect-mode-ebp:
3768     # . prolog
3769     55/push-ebp
3770     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3771     # setup
3772     # . clear-stream(_test-output-stream)
3773     # . . push args
3774     68/push  _test-output-stream/imm32
3775     # . . call
3776     e8/call  clear-stream/disp32
3777     # . . discard args
3778     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3779     # . clear-stream(_test-output-buffered-file+4)
3780     # . . push args
3781     b8/copy-to-eax  _test-output-buffered-file/imm32
3782     05/add-to-eax  4/imm32
3783     50/push-eax
3784     # . . call
3785     e8/call  clear-stream/disp32
3786     # . . discard args
3787     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3788     # emit-indirect-mode(_test-output-buffered-file, 5/base, 0/index, 0/scale, 0/disp)
3789     # . . write args
3790     68/push  0/imm32/.disp
3791     68/push  0/imm32/.scale
3792     68/push  0/imm32/.index
3793     68/push  5/imm32/.base/ebp
3794     68/push  _test-output-buffered-file/imm32
3795     # . . call
3796     e8/call  emit-indirect-mode/disp32
3797     # . . discard args
3798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3799     # . flush(_test-output-buffered-file)
3800     # . . push args
3801     68/push  _test-output-buffered-file/imm32
3802     # . . call
3803     e8/call  flush/disp32
3804     # . . discard args
3805     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3806 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3832     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 4/rm32/sib 5/base/ebp 0/index 0/scale 0/disp32", msg)
3833     # . . push args
3834     68/push  "F - test-emit-indirect-mode-ebp"/imm32
3835     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000005/base 0x00000000/index 0x00000000/scale 0x00000000/disp32"/imm32
3836     68/push  _test-output-stream/imm32
3837     # . . call
3838     e8/call  check-stream-equal/disp32
3839     # . . discard args
3840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3841     # . epilog
3842     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3843     5d/pop-to-ebp
3844     c3/return
3845 
3846 test-emit-indirect-mode-esp:
3847     # . prolog
3848     55/push-ebp
3849     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3850     # setup
3851     # . clear-stream(_test-output-stream)
3852     # . . push args
3853     68/push  _test-output-stream/imm32
3854     # . . call
3855     e8/call  clear-stream/disp32
3856     # . . discard args
3857     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3858     # . clear-stream(_test-output-buffered-file+4)
3859     # . . push args
3860     b8/copy-to-eax  _test-output-buffered-file/imm32
3861     05/add-to-eax  4/imm32
3862     50/push-eax
3863     # . . call
3864     e8/call  clear-stream/disp32
3865     # . . discard args
3866     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3867     # emit-indirect-mode(_test-output-buffered-file, 4/base, 0/index, 0/scale, 0/disp)
3868     # . . write args
3869     68/push  0/imm32/.disp
3870     68/push  0/imm32/.scale
3871     68/push  0/imm32/.index
3872     68/push  4/imm32/.base/esp
3873     68/push  _test-output-buffered-file/imm32
3874     # . . call
3875     e8/call  emit-indirect-mode/disp32
3876     # . . discard args
3877     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3878     # . flush(_test-output-buffered-file)
3879     # . . push args
3880     68/push  _test-output-buffered-file/imm32
3881     # . . call
3882     e8/call  flush/disp32
3883     # . . discard args
3884     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3885 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3911     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 4/rm32/sib 4/base/ebp 0/index 0/scale 0/disp32", msg)
3912     # . . push args
3913     68/push  "F - test-emit-indirect-mode-esp"/imm32
3914     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000004/base 0x00000000/index 0x00000000/scale 0x00000000/disp32"/imm32
3915     68/push  _test-output-stream/imm32
3916     # . . call
3917     e8/call  check-stream-equal/disp32
3918     # . . discard args
3919     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3920     # . epilog
3921     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3922     5d/pop-to-ebp
3923     c3/return
3924 
3925 disp32-mode?:  # in : (address slice) -> reg/eax : boolean
3926     # . prolog
3927     55/push-ebp
3928     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3929     # . save registers
3930     56/push-esi
3931     57/push-edi
3932     # var local-slice/esi : (address slice) = {in->start, in->end}
3933     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
3934     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3935     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3936     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
3937     # ++local-slice->start to skip '*'
3938     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
3939     # if (*local-slice->start == '(') return false
3940     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
3941     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
3942     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
3943     3d/compare-eax-and  0x28/imm32/open-paren
3944     74/jump-if-equal  $disp32-mode?:false/disp8
3945 $disp32-mode?:check-for-register:
3946     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
3947     # . . push args
3948     56/push-esi
3949     68/push  0x2f/imm32/slash
3950     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3951     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3952     # . . call
3953     e8/call  next-token-from-slice/disp32
3954     # . . discard args
3955     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
3956     # reg-num/eax = maybe-get-slice(Registers, local-slice, row-size=8)
3957     # . . push args
3958     68/push  8/imm32/row-size
3959     56/push-esi
3960     68/push  Registers/imm32
3961     # . . cal
3962     e8/call  maybe-get-slice/disp32
3963     # . . discard args
3964     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3965     # if (eax != 0) return false
3966     3d/compare-eax-and  0/imm32
3967     75/jump-if-not-equal  $disp32-mode?:false/disp8
3968     # return true
3969     b8/copy-to-eax  1/imm32/true
3970     eb/jump  $disp32-mode?:end/disp8
3971 $disp32-mode?:false:
3972     b8/copy-to-eax  0/imm32/false
3973 $disp32-mode?:end:
3974     # . reclaim locals
3975     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3976     # . restore registers
3977     5f/pop-to-edi
3978     5e/pop-to-esi
3979     # . epilog
3980     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3981     5d/pop-to-ebp
3982     c3/return
3983 
3984 emit-indirect-disp32:  # out : (address buffered-file), word-slice : (address slice)
3985     # . prolog
3986     55/push-ebp
3987     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3988     # . save registers
3989     56/push-esi
3990     # var local-slice/esi : (address slice) = {in->start, in->end}
3991     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
3992     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3993     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3994     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
3995     # ++local-slice->start to skip '*'
3996     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
3997     # write-buffered(out, "0/mod/indirect 5/rm32/.disp32 ")
3998     # . . push args
3999     68/push  "0/mod/indirect 5/rm32/.disp32 "/imm32
4000     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
4001     # . . call
4002     e8/call  write-buffered/disp32
4003     # . . discard args
4004     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4005     # write-slice-buffered(out, local-slice)
4006     # . . push args
4007     56/push-esi
4008     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
4009     # . . call
4010     e8/call  write-slice-buffered/disp32
4011     # . . discard args
4012     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4013     # write-buffered(out, "/disp32")
4014     # . . push args
4015     68/push  "/disp32"/imm32
4016     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
4017     # . . call
4018     e8/call  write-buffered/disp32
4019     # . . discard args
4020     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4021 $emit-indirect-disp32:end:
4022     # . reclaim locals
4023     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4024     # . restore registers
4025     5e/pop-to-esi
4026     # . epilog
4027     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4028     5d/pop-to-ebp
4029     c3/return
4030 
4031 # assumes 'in' starts with optional '+' or '-', optional whitespace, and an unsigned integer
4032 # returns the value of the integer
4033 # side-effect: modifies 'in' to skip past the integer
4034 next-hex-int:  # in : (address slice) -> result/eax
4035     # . prolog
4036     55/push-ebp
4037     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4038     # . save registers
4039     51/push-ecx
4040     52/push-edx
4041     53/push-ebx
4042     56/push-esi
4043     57/push-edi
4044     # result/edi = 0
4045     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
4046     # esi = in
4047     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
4048     # edx = in->end
4049     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
4050     # curr/ecx = in->start
4051     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
4052     # negate?/ebx = false
4053     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4054     # eax = *curr
4055     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4056     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4057 $next-hex-int:positive:
4058     # if (*curr == '+') ++curr
4059     3d/compare-eax-and  0x2b/imm32/+
4060     75/jump-if-not-equal  $next-hex-int:negative/disp8
4061     # . ++curr
4062     41/increment-ecx
4063     eb/jump  $next-hex-int:skip-whitespace/disp8
4064 $next-hex-int:negative:
4065     # else if (*curr == '-') ++curr, negate = true
4066     3d/compare-eax-and  0x2d/imm32/-
4067     75/jump-if-not-equal  $next-hex-int:skip-whitespace/disp8
4068 $next-hex-int:need-to-negate:
4069     # . ++curr
4070     41/increment-ecx
4071     # . negate = true
4072     bb/copy-to-ebx  1/imm32/true
4073     # fall through
4074 $next-hex-int:skip-whitespace:
4075     # spill eax
4076     50/push-eax
4077     # eax = skip-chars-matching-whitespace-in-slice(word-slice->start, word-slice->end)
4078     # . . push args
4079     52/push-edx
4080     51/push-ecx
4081     # . . call
4082     e8/call  skip-chars-matching-whitespace-in-slice/disp32
4083     # . . discard args
4084     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4085     # ecx = eax
4086     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
4087     # restore eax
4088     58/pop-to-eax
4089 $next-hex-int:initial-0:
4090     # skip past leading '0x'
4091     # . if (*curr != '0') jump to loop
4092     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4093     3d/compare-eax-and  0x30/imm32/0
4094     75/jump-if-not-equal  $next-hex-int:loop/disp8
4095     # . ++curr
4096     41/increment-ecx
4097 $next-hex-int:initial-0x:
4098     # . if (curr >= in->end) return result
4099     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4100     73/jump-if-greater-or-equal-unsigned  $next-hex-int:end/disp8
4101     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
4102     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4103     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4104     3d/compare-eax-and  0x78/imm32/x
4105     75/jump-if-not-equal  $next-hex-int:loop/disp8
4106     # . ++curr
4107     41/increment-ecx
4108 $next-hex-int:loop:
4109     # if (curr >= in->end) break
4110     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4111     73/jump-if-greater-or-equal-unsigned  $next-hex-int:break/disp8
4112     # if (!is-hex-digit?(*curr)) break
4113     # . eax = *curr
4114     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4115     # . eax = is-hex-digit?(*curr)
4116     # . . push args
4117     50/push-eax
4118     # . . call
4119     e8/call  is-hex-digit?/disp32
4120     # . . discard args
4121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4122     # . if (eax == 0) break
4123     3d/compare-eax-and  0/imm32
4124     74/jump-if-equal  $next-hex-int:break/disp8
4125     # eax = from-hex-char(*curr)
4126     # . . copy arg to eax
4127     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4128     # . . call
4129     e8/call  from-hex-char/disp32
4130     # result = result * 16 + eax
4131     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi left by 4 bits
4132     01/add                          3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to edi
4133     # ++curr
4134     41/increment-ecx
4135     # loop
4136     eb/jump  $next-hex-int:loop/disp8
4137 $next-hex-int:break:
4138     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
4139     74/jump-if-equal  $next-hex-int:end/disp8
4140 $next-hex-int:negate:
4141     f7          3/subop/negate      3/mod/direct    7/rm32/edi    .           .             .           .           .               .                 # negate edi
4142 $next-hex-int:end:
4143     # word-slice->start = curr
4144     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy ecx to *esi
4145     # return edi
4146     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
4147     # . restore registers
4148     5f/pop-to-edi
4149     5e/pop-to-esi
4150     5b/pop-to-ebx
4151     5a/pop-to-edx
4152     59/pop-to-ecx
4153     # . epilog
4154     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4155     5d/pop-to-ebp
4156     c3/return
4157 
4158 $next-hex-int:abort:
4159     # . _write(2/stderr, error)
4160     # . . push args
4161     68/push  "next-hex-int: invalid hex char: "/imm32
4162     68/push  2/imm32/stderr
4163     # . . call
4164     e8/call  _write/disp32
4165     # . . discard args
4166     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4167     # . clear-stream(Stderr+4)
4168     # . . save eax
4169     50/push-eax
4170     # . . push args
4171     b8/copy-to-eax  Stderr/imm32
4172     05/add-to-eax  4/imm32
4173     50/push-eax
4174     # . . call
4175     e8/call  clear-stream/disp32
4176     # . . discard args
4177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4178     # . . restore eax
4179     58/pop-to-eax
4180     # . print-int32-buffered(Stderr, eax)
4181     # . . push args
4182     50/push-eax
4183     68/push  Stderr/imm32
4184     # . . call
4185     e8/call  print-int32-buffered/disp32
4186     # . . discard args
4187     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4188     # . flush(Stderr)
4189     # . . push args
4190     68/push  Stderr/imm32
4191     # . . call
4192     e8/call  flush/disp32
4193     # . . discard args
4194     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4195     # . _write(2/stderr, "\n")
4196     # . . push args
4197     68/push  "\n"/imm32
4198     68/push  2/imm32/stderr
4199     # . . call
4200     e8/call  _write/disp32
4201     # . . discard args
4202     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4203     # . syscall(exit, 1)
4204     bb/copy-to-ebx  1/imm32
4205     b8/copy-to-eax  1/imm32/exit
4206     cd/syscall  0x80/imm8
4207     # never gets here
4208 
4209 test-next-hex-int-single-digit:
4210     # . prolog
4211     55/push-ebp
4212     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4213     # (eax..ecx) = "+a)"
4214     b8/copy-to-eax  "+a)"/imm32
4215     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4216     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
4217     05/add-to-eax  4/imm32
4218     # var slice/ecx = {eax, ecx}
4219     51/push-ecx
4220     50/push-eax
4221     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4222     # eax = next-hex-int(slice)
4223     # . . push args
4224     51/push-ecx
4225     # . . call
4226     e8/call  next-hex-int/disp32
4227     # . . discard args
4228     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4229     # check-ints-equal(eax, 0xa, msg)
4230     # . . push args
4231     68/push  "F - test-next-hex-int-single-digit"/imm32
4232     68/push  0xa/imm32
4233     50/push-eax
4234     # . . call
4235     e8/call  check-ints-equal/disp32
4236     # . . discard args
4237     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4238     # . epilog
4239     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4240     5d/pop-to-ebp
4241     c3/return
4242 
4243 test-next-hex-int-multi-digit:
4244     # . prolog
4245     55/push-ebp
4246     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4247     # (eax..ecx) = "+ 34a)"
4248     b8/copy-to-eax  "+ 34a)"/imm32
4249     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4250     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
4251     05/add-to-eax  4/imm32
4252     # var slice/ecx = {eax, ecx}
4253     51/push-ecx
4254     50/push-eax
4255     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4256     # eax = next-hex-int(slice)
4257     # . . push args
4258     51/push-ecx
4259     # . . call
4260     e8/call  next-hex-int/disp32
4261     # . . discard args
4262     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4263     # check-ints-equal(eax, 0x34a, msg)
4264     # . . push args
4265     68/push  "F - test-next-hex-int-multi-digit"/imm32
4266     68/push  0x34a/imm32
4267     50/push-eax
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-next-hex-int-0x-prefix:
4278     # . prolog
4279     55/push-ebp
4280     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4281     # (eax..ecx) = "+0x34)"
4282     b8/copy-to-eax  "+0x34)"/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     # var slice/ecx = {eax, ecx}
4287     51/push-ecx
4288     50/push-eax
4289     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4290     # eax = next-hex-int(slice)
4291     # . . push args
4292     51/push-ecx
4293     # . . call
4294     e8/call  next-hex-int/disp32
4295     # . . discard args
4296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4297     # check-ints-equal(eax, 0x34, msg)
4298     # . . push args
4299     68/push  "F - test-next-hex-int-0x-prefix"/imm32
4300     68/push  0x34/imm32
4301     50/push-eax
4302     # . . call
4303     e8/call  check-ints-equal/disp32
4304     # . . discard args
4305     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4306     # . epilog
4307     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4308     5d/pop-to-ebp
4309     c3/return
4310 
4311 test-next-hex-int-zero:
4312     # . prolog
4313     55/push-ebp
4314     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4315     # (eax..ecx) = "+0)"
4316     b8/copy-to-eax  "+0)"/imm32
4317     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4318     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
4319     05/add-to-eax  4/imm32
4320     # var slice/ecx = {eax, ecx}
4321     51/push-ecx
4322     50/push-eax
4323     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4324     # eax = next-hex-int(slice)
4325     # . . push args
4326     51/push-ecx
4327     # . . call
4328     e8/call  next-hex-int/disp32
4329     # . . discard args
4330     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4331     # check-ints-equal(eax, 0, msg)
4332     # . . push args
4333     68/push  "F - test-next-hex-int-zero"/imm32
4334     68/push  0/imm32
4335     50/push-eax
4336     # . . call
4337     e8/call  check-ints-equal/disp32
4338     # . . discard args
4339     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4340     # . epilog
4341     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4342     5d/pop-to-ebp
4343     c3/return
4344 
4345 test-next-hex-int-0-prefix:
4346     # . prolog
4347     55/push-ebp
4348     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4349     # (eax..ecx) = "+ 03)"
4350     b8/copy-to-eax  "+ 03)"/imm32
4351     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4352     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
4353     05/add-to-eax  4/imm32
4354     # var slice/ecx = {eax, ecx}
4355     51/push-ecx
4356     50/push-eax
4357     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4358     # eax = next-hex-int(slice)
4359     # . . push args
4360     51/push-ecx
4361     # . . call
4362     e8/call  next-hex-int/disp32
4363     # . . discard args
4364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4365     # check-ints-equal(eax, 3, msg)
4366     # . . push args
4367     68/push  "F - test-next-hex-int-0-prefix"/imm32
4368     68/push  3/imm32
4369     50/push-eax
4370     # . . call
4371     e8/call  check-ints-equal/disp32
4372     # . . discard args
4373     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4374     # . epilog
4375     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4376     5d/pop-to-ebp
4377     c3/return
4378 
4379 test-next-hex-int-negative:
4380     # . prolog
4381     55/push-ebp
4382     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4383     # (eax..ecx) = "-03)"
4384     b8/copy-to-eax  "-03)"/imm32
4385     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4386     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
4387     05/add-to-eax  4/imm32
4388     # var slice/ecx = {eax, ecx}
4389     51/push-ecx
4390     50/push-eax
4391     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4392     # eax = next-hex-int(slice)
4393     # . . push args
4394     51/push-ecx
4395     # . . call
4396     e8/call  next-hex-int/disp32
4397     # . . discard args
4398     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4399     # check-ints-equal(eax, -3, msg)
4400     # . . push args
4401     68/push  "F - test-next-hex-int-negative"/imm32
4402     68/push  -3/imm32
4403     50/push-eax
4404     # . . call
4405     e8/call  check-ints-equal/disp32
4406     # . . discard args
4407     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4408     # . epilog
4409     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4410     5d/pop-to-ebp
4411     c3/return
4412 
4413 test-next-hex-int-negative-with-space:
4414     # . prolog
4415     55/push-ebp
4416     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4417     # (eax..ecx) = "- 03)"
4418     b8/copy-to-eax  "- 03)"/imm32
4419     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4420     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
4421     05/add-to-eax  4/imm32
4422     # var slice/ecx = {eax, ecx}
4423     51/push-ecx
4424     50/push-eax
4425     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4426     # eax = next-hex-int(slice)
4427     # . . push args
4428     51/push-ecx
4429     # . . call
4430     e8/call  next-hex-int/disp32
4431     # . . discard args
4432     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4433     # check-ints-equal(eax, -3, msg)
4434     # . . push args
4435     68/push  "F - test-next-hex-int-negative-with-space"/imm32
4436     68/push  -3/imm32
4437     50/push-eax
4438     # . . call
4439     e8/call  check-ints-equal/disp32
4440     # . . discard args
4441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4442     # . epilog
4443     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4444     5d/pop-to-ebp
4445     c3/return
4446 
4447 # assumes 'in' starts a positive unsigned integer
4448 # returns the value of the integer
4449 # side-effect: modifies 'in' to skip past the integer
4450 next-positive-hex-int:  # in : (address slice) -> result/eax
4451     # . prolog
4452     55/push-ebp
4453     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4454     # . save registers
4455     51/push-ecx
4456     52/push-edx
4457     53/push-ebx
4458     56/push-esi
4459     57/push-edi
4460     # result/edi = 0
4461     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
4462     # esi = in
4463     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
4464     # edx = in->end
4465     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
4466     # curr/ecx = in->start
4467     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
4468     # negate?/ebx = false
4469     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4470     # eax = *curr
4471     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4472     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4473 $next-positive-hex-int:initial-0:
4474     # skip past leading '0x'
4475     # . if (*curr != '0') jump to loop
4476     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4477     3d/compare-eax-and  0x30/imm32/0
4478     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
4479     # . ++curr
4480     41/increment-ecx
4481 $next-positive-hex-int:initial-0x:
4482     # . if (curr >= in->end) return result
4483     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4484     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
4485     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
4486     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4487     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4488     3d/compare-eax-and  0x78/imm32/x
4489     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
4490     # . ++curr
4491     41/increment-ecx
4492 $next-positive-hex-int:loop:
4493     # if (curr >= in->end) break
4494     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4495     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
4496     # if (!is-hex-digit?(*curr)) break
4497     # . eax = *curr
4498     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4499     # . eax = is-hex-digit?(*curr)
4500     # . . push args
4501     50/push-eax
4502     # . . call
4503     e8/call  is-hex-digit?/disp32
4504     # . . discard args
4505     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4506     # . if (eax == 0) break
4507     3d/compare-eax-and  0/imm32
4508     74/jump-if-equal  $next-positive-hex-int:end/disp8
4509     # eax = from-hex-char(*curr)
4510     # . . copy arg to eax
4511     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4512     # . . call
4513     e8/call  from-hex-char/disp32
4514     # result = result * 16 + eax
4515     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi left by 4 bits
4516     01/add                          3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to edi
4517     # ++curr
4518     41/increment-ecx
4519     # loop
4520     eb/jump  $next-positive-hex-int:loop/disp8
4521 $next-positive-hex-int:end:
4522     # word-slice->start = curr
4523     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy ecx to *esi
4524     # return edi
4525     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
4526     # . restore registers
4527     5f/pop-to-edi
4528     5e/pop-to-esi
4529     5b/pop-to-ebx
4530     5a/pop-to-edx
4531     59/pop-to-ecx
4532     # . epilog
4533     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4534     5d/pop-to-ebp
4535     c3/return
4536 
4537 test-next-positive-hex-int-single-digit:
4538     # . prolog
4539     55/push-ebp
4540     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4541     # (eax..ecx) = "a)"
4542     b8/copy-to-eax  "a)"/imm32
4543     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4544     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
4545     05/add-to-eax  4/imm32
4546     # var slice/ecx = {eax, ecx}
4547     51/push-ecx
4548     50/push-eax
4549     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4550     # eax = next-positive-hex-int(slice)
4551     # . . push args
4552     51/push-ecx
4553     # . . call
4554     e8/call  next-positive-hex-int/disp32
4555     # . . discard args
4556     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4557     # check-ints-equal(eax, 0xa, msg)
4558     # . . push args
4559     68/push  "F - test-next-positive-hex-int-single-digit"/imm32
4560     68/push  0xa/imm32
4561     50/push-eax
4562     # . . call
4563     e8/call  check-ints-equal/disp32
4564     # . . discard args
4565     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4566     # . epilog
4567     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4568     5d/pop-to-ebp
4569     c3/return
4570 
4571 test-next-positive-hex-int-multi-digit:
4572     # . prolog
4573     55/push-ebp
4574     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4575     # (eax..ecx) = "34a)"
4576     b8/copy-to-eax  "34a)"/imm32
4577     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4578     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
4579     05/add-to-eax  4/imm32
4580     # var slice/ecx = {eax, ecx}
4581     51/push-ecx
4582     50/push-eax
4583     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4584     # eax = next-positive-hex-int(slice)
4585     # . . push args
4586     51/push-ecx
4587     # . . call
4588     e8/call  next-positive-hex-int/disp32
4589     # . . discard args
4590     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4591     # check-ints-equal(eax, 0x34a, msg)
4592     # . . push args
4593     68/push  "F - test-next-positive-hex-int-multi-digit"/imm32
4594     68/push  0x34a/imm32
4595     50/push-eax
4596     # . . call
4597     e8/call  check-ints-equal/disp32
4598     # . . discard args
4599     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4600     # . epilog
4601     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4602     5d/pop-to-ebp
4603     c3/return
4604 
4605 test-next-positive-hex-int-0x-prefix:
4606     # . prolog
4607     55/push-ebp
4608     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4609     # (eax..ecx) = "0x34)"
4610     b8/copy-to-eax  "0x34)"/imm32
4611     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4612     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
4613     05/add-to-eax  4/imm32
4614     # var slice/ecx = {eax, ecx}
4615     51/push-ecx
4616     50/push-eax
4617     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4618     # eax = next-positive-hex-int(slice)
4619     # . . push args
4620     51/push-ecx
4621     # . . call
4622     e8/call  next-positive-hex-int/disp32
4623     # . . discard args
4624     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4625     # check-ints-equal(eax, 0x34, msg)
4626     # . . push args
4627     68/push  "F - test-next-positive-hex-int-0x-prefix"/imm32
4628     68/push  0x34/imm32
4629     50/push-eax
4630     # . . call
4631     e8/call  check-ints-equal/disp32
4632     # . . discard args
4633     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4634     # . epilog
4635     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4636     5d/pop-to-ebp
4637     c3/return
4638 
4639 test-next-positive-hex-int-zero:
4640     # . prolog
4641     55/push-ebp
4642     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4643     # (eax..ecx) = "0"
4644     b8/copy-to-eax  "0"/imm32
4645     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4646     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
4647     05/add-to-eax  4/imm32
4648     # var slice/ecx = {eax, ecx}
4649     51/push-ecx
4650     50/push-eax
4651     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4652     # eax = next-positive-hex-int(slice)
4653     # . . push args
4654     51/push-ecx
4655     # . . call
4656     e8/call  next-positive-hex-int/disp32
4657     # . . discard args
4658     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4659     # check-ints-equal(eax, 0, msg)
4660     # . . push args
4661     68/push  "F - test-next-positive-hex-int-zero"/imm32
4662     68/push  0/imm32
4663     50/push-eax
4664     # . . call
4665     e8/call  check-ints-equal/disp32
4666     # . . discard args
4667     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4668     # . epilog
4669     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4670     5d/pop-to-ebp
4671     c3/return
4672 
4673 test-next-positive-hex-int-0-prefix:
4674     # . prolog
4675     55/push-ebp
4676     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4677     # (eax..ecx) = "03)"
4678     b8/copy-to-eax  "03)"/imm32
4679     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4680     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
4681     05/add-to-eax  4/imm32
4682     # var slice/ecx = {eax, ecx}
4683     51/push-ecx
4684     50/push-eax
4685     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4686     # eax = next-positive-hex-int(slice)
4687     # . . push args
4688     51/push-ecx
4689     # . . call
4690     e8/call  next-positive-hex-int/disp32
4691     # . . discard args
4692     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4693     # check-ints-equal(eax, 3, msg)
4694     # . . push args
4695     68/push  "F - test-next-positive-hex-int-0-prefix"/imm32
4696     68/push  3/imm32
4697     50/push-eax
4698     # . . call
4699     e8/call  check-ints-equal/disp32
4700     # . . discard args
4701     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4702     # . epilog
4703     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4704     5d/pop-to-ebp
4705     c3/return
4706 
4707 == data
4708 Registers:  # (table string int)
4709   # a table is a stream
4710   0x40/imm32/write
4711   0/imm32/read
4712   0x40/imm32/length
4713   # data
4714   "eax"/imm32  0/imm32
4715   "ecx"/imm32  1/imm32
4716   "edx"/imm32  2/imm32
4717   "ebx"/imm32  3/imm32
4718   "esp"/imm32  4/imm32
4719   "ebp"/imm32  5/imm32
4720   "esi"/imm32  6/imm32
4721   "edi"/imm32  7/imm32
4722 
4723 # . . vim:nowrap:textwidth=0