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 init.linux 0*.subx apps/subx-params.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     # . prologue
  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 interactive
  70     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  71     7e/jump-if-lesser-or-equal  $subx-sigils-main:interactive/disp8
  72     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  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 interactive
  82     3d/compare-eax-and  0/imm32
  83     74/jump-if-equal  $subx-sigils-main:interactive/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  $subx-sigils-main:end/disp8
  89 $subx-sigils-main:interactive:
  90     # - otherwise convert stdin
  91     # subx-sigils(Stdin, Stdout)
  92     # . . push args
  93     68/push  Stdout/imm32
  94     68/push  Stdin/imm32
  95     # . . call
  96     e8/call  subx-sigils/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 $subx-sigils-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 subx-sigils:  # in : (address buffered-file), out : (address buffered-file)
 108     # pseudocode:
 109     #   var line : (stream byte 512)
 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     # . prologue
 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 : (stream byte 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 $subx-sigils: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 $subx-sigils: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  $subx-sigils:break/disp32
 174 $subx-sigils: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 $subx-sigils: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  $subx-sigils:next-line/disp32
 195 $subx-sigils: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  $subx-sigils:word-loop/disp8
 205 $subx-sigils: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  $subx-sigils:check-for-indirect-mode/disp8
 209 $subx-sigils:direct-mode:
 210 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
 250     # emit-direct-mode(out, word-slice)
 251     # . . push args
 252     52/push-edx
 253     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 254     # . . call
 255     e8/call  emit-direct-mode/disp32
 256     # . . discard args
 257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 258     # continue
 259     e9/jump  $subx-sigils:next-word/disp32
 260 $subx-sigils:check-for-indirect-mode:
 261     # if (!slice-starts-with?(word-slice, "*")) goto next check
 262     3d/compare-eax-and  0x2a/imm32/asterisk
 263     75/jump-if-not-equal  $subx-sigils:check-for-invalid-addition/disp8
 264     # if (!disp32-mode?(word-slice)) goto indirect mode
 265     # . eax = disp32-mode?(word-slice)
 266     # . . push args
 267     52/push-edx
 268     # . . call
 269     e8/call  disp32-mode?/disp32
 270     # . . discard args
 271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 272     # . if (eax == 0) goto indirect mode
 273     3d/compare-eax-and  0/imm32
 274     74/jump-if-equal  $subx-sigils:indirect-mode/disp8
 275 $subx-sigils:disp32-mode:
 276     # emit-indirect-mode(out, word-slice)
 277     # . . push args
 278     52/push-edx
 279     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 280     # . . call
 281     e8/call  emit-indirect-disp32/disp32
 282     # . . discard args
 283     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 284     # continue
 285     e9/jump  $subx-sigils:next-word/disp32
 286 $subx-sigils:indirect-mode:
 287     # spill registers
 288     50/push-eax
 289     51/push-ecx
 290     52/push-edx
 291     53/push-ebx
 292     # base/eax, index/ecx, scale/edx, disp/ebx = parse-effective-address(word-slice)
 293     # . . push args
 294     52/push-edx
 295     # . . call
 296     e8/call  parse-effective-address/disp32
 297     # . . discard args
 298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 299     # emit-indirect-mode(out, base, index, scale, disp)
 300     # . . push args
 301     53/push-ebx
 302     52/push-edx
 303     51/push-ecx
 304     50/push-eax
 305     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 306     # . . call
 307     e8/call  emit-indirect-mode/disp32
 308     # . . discard args
 309     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
 310     # restore registers
 311     5b/pop-to-ebx
 312     5a/pop-to-edx
 313     59/pop-to-ecx
 314     58/pop-to-eax
 315     # continue
 316     e9/jump  $subx-sigils:next-word/disp32
 317 $subx-sigils:check-for-invalid-addition:
 318     # if (slice-starts-with?(word-slice, "+")) goto error1
 319     3d/compare-eax-and  0x2b/imm32/plus
 320     74/jump-if-equal  $subx-sigils:error1/disp8
 321 $subx-sigils:check-for-invalid-left-shift:
 322     # if (slice-starts-with?(word-slice, "<")) goto error1
 323     3d/compare-eax-and  0x3c/imm32/less-than
 324     74/jump-if-equal  $subx-sigils:error1/disp8
 325 $subx-sigils:regular-word:
 326     # write-slice-buffered(out, word-slice)
 327     # . . push args
 328     52/push-edx
 329     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 330     # . . call
 331     e8/call  write-slice-buffered/disp32
 332     # . . discard args
 333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 334     # fall through
 335 $subx-sigils:next-word:
 336     # write-buffered(out, " ")
 337     # . . push args
 338     68/push  Space/imm32
 339     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 340     # . . call
 341     e8/call  write-buffered/disp32
 342     # . . discard args
 343     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 344     # loop
 345     e9/jump  $subx-sigils:word-loop/disp32
 346 $subx-sigils:next-line:
 347     # write-buffered(out, "\n")
 348     # . . push args
 349     68/push  Newline/imm32
 350     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 351     # . . call
 352     e8/call  write-buffered/disp32
 353     # . . discard args
 354     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 355     # loop
 356     e9/jump  $subx-sigils:line-loop/disp32
 357 $subx-sigils:break:
 358     # flush(out)
 359     # . . push args
 360     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 361     # . . call
 362     e8/call  flush/disp32
 363     # . . discard args
 364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 365 $subx-sigils:end:
 366     # . reclaim locals
 367     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 368     # . restore registers
 369     5b/pop-to-ebx
 370     5a/pop-to-edx
 371     59/pop-to-ecx
 372     58/pop-to-eax
 373     # . epilogue
 374     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 375     5d/pop-to-ebp
 376     c3/return
 377 
 378 $subx-sigils:error1:
 379     # print(stderr, "error: '" eax "' only permitted within '*(...)' in '" line "'")
 380     # . write-buffered(Stderr, "error: '")
 381     # . . push args
 382     68/push  "error: '"/imm32
 383     68/push  Stderr/imm32
 384     # . . call
 385     e8/call  write-buffered/disp32
 386     # . . discard args
 387     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 388     # . write-byte-buffered(Stderr, eax)
 389     # . . push args
 390     50/push-eax
 391     68/push  Stderr/imm32
 392     # . . call
 393     e8/call  write-byte-buffered/disp32
 394     # . . discard args
 395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 396     # . write-buffered(Stderr, "' only permitted within '*(...)' in '")
 397     # . . push args
 398     68/push  "' only permitted within '*(...)' in '"/imm32
 399     68/push  Stderr/imm32
 400     # . . call
 401     e8/call  write-buffered/disp32
 402     # . . discard args
 403     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 404     # . write-stream-data(Stderr, line)
 405     # . . push args
 406     51/push-ecx
 407     68/push  Stderr/imm32
 408     # . . call
 409     e8/call  write-stream-data/disp32
 410     # . . discard args
 411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 412     # . write-buffered(Stderr, "'")
 413     # . . push args
 414     68/push  "'"/imm32
 415     68/push  Stderr/imm32
 416     # . . call
 417     e8/call  write-buffered/disp32
 418     # . . discard args
 419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 420     # . flush(Stderr)
 421     # . . push args
 422     68/push  Stderr/imm32
 423     # . . call
 424     e8/call  flush/disp32
 425     # . . discard args
 426     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 427     # . syscall(exit, 1)
 428     bb/copy-to-ebx  1/imm32
 429     b8/copy-to-eax  1/imm32/exit
 430     cd/syscall  0x80/imm8
 431     # never gets here
 432 
 433 test-subx-sigils-passes-most-words-through:
 434     # . prologue
 435     55/push-ebp
 436     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 437     # setup
 438     # . clear-stream(_test-input-stream)
 439     # . . push args
 440     68/push  _test-input-stream/imm32
 441     # . . call
 442     e8/call  clear-stream/disp32
 443     # . . discard args
 444     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 445     # . clear-stream(_test-input-buffered-file->buffer)
 446     # . . push args
 447     68/push  _test-input-buffered-file->buffer/imm32
 448     # . . call
 449     e8/call  clear-stream/disp32
 450     # . . discard args
 451     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 452     # . clear-stream(_test-output-stream)
 453     # . . push args
 454     68/push  _test-output-stream/imm32
 455     # . . call
 456     e8/call  clear-stream/disp32
 457     # . . discard args
 458     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 459     # . clear-stream(_test-output-buffered-file->buffer)
 460     # . . push args
 461     68/push  _test-output-buffered-file->buffer/imm32
 462     # . . call
 463     e8/call  clear-stream/disp32
 464     # . . discard args
 465     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 466     # initialize input
 467     # . write(_test-input-stream, "== abcd 0x1")
 468     # . . push args
 469     68/push  "== abcd 0x1"/imm32
 470     68/push  _test-input-stream/imm32
 471     # . . call
 472     e8/call  write/disp32
 473     # . . discard args
 474     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 475     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
 476     # . . push args
 477     68/push  _test-output-buffered-file/imm32
 478     68/push  _test-input-buffered-file/imm32
 479     # . . call
 480     e8/call  subx-sigils/disp32
 481     # . . discard args
 482     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 483     # check that the line just passed through
 484     # . flush(_test-output-buffered-file)
 485     # . . push args
 486     68/push  _test-output-buffered-file/imm32
 487     # . . call
 488     e8/call  flush/disp32
 489     # . . discard args
 490     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 491 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 517     # . check-stream-equal(_test-output-stream, "== abcd 0x1 \n", msg)
 518     # . . push args
 519     68/push  "F - test-subx-sigils-passes-most-words-through"/imm32
 520     68/push  "== abcd 0x1 \n"/imm32
 521     68/push  _test-output-stream/imm32
 522     # . . call
 523     e8/call  check-stream-equal/disp32
 524     # . . discard args
 525     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 526     # . epilogue
 527     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 528     5d/pop-to-ebp
 529     c3/return
 530 
 531 test-subx-sigils-direct-mode:
 532     # . prologue
 533     55/push-ebp
 534     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 535     # setup
 536     # . clear-stream(_test-input-stream)
 537     # . . push args
 538     68/push  _test-input-stream/imm32
 539     # . . call
 540     e8/call  clear-stream/disp32
 541     # . . discard args
 542     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 543     # . clear-stream(_test-input-buffered-file->buffer)
 544     # . . push args
 545     68/push  _test-input-buffered-file->buffer/imm32
 546     # . . call
 547     e8/call  clear-stream/disp32
 548     # . . discard args
 549     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 550     # . clear-stream(_test-output-stream)
 551     # . . push args
 552     68/push  _test-output-stream/imm32
 553     # . . call
 554     e8/call  clear-stream/disp32
 555     # . . discard args
 556     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 557     # . clear-stream(_test-output-buffered-file->buffer)
 558     # . . push args
 559     68/push  _test-output-buffered-file->buffer/imm32
 560     # . . call
 561     e8/call  clear-stream/disp32
 562     # . . discard args
 563     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 564     # initialize input
 565     # . write(_test-input-stream, "ab %ecx")
 566     # . . push args
 567     68/push  "ab %ecx"/imm32
 568     68/push  _test-input-stream/imm32
 569     # . . call
 570     e8/call  write/disp32
 571     # . . discard args
 572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 573     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
 574     # . . push args
 575     68/push  _test-output-buffered-file/imm32
 576     68/push  _test-input-buffered-file/imm32
 577     # . . call
 578     e8/call  subx-sigils/disp32
 579     # . . discard args
 580     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 581     # check that the line just passed through
 582     # . flush(_test-output-buffered-file)
 583     # . . push args
 584     68/push  _test-output-buffered-file/imm32
 585     # . . call
 586     e8/call  flush/disp32
 587     # . . discard args
 588     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 589 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 615     # . check-stream-equal(_test-output-stream, "ab 3/mod/direct 0x00000001/rm32 \n", msg)
 616     # . . push args
 617     68/push  "F - test-subx-sigils-direct-mode"/imm32
 618     68/push  "ab 3/mod/direct 0x00000001/rm32 \n"/imm32
 619     68/push  _test-output-stream/imm32
 620     # . . call
 621     e8/call  check-stream-equal/disp32
 622     # . . discard args
 623     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 624     # . epilogue
 625     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 626     5d/pop-to-ebp
 627     c3/return
 628 
 629 test-subx-sigils-direct-mode-with-metadata:
 630     # . prologue
 631     55/push-ebp
 632     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 633     # setup
 634     # . clear-stream(_test-input-stream)
 635     # . . push args
 636     68/push  _test-input-stream/imm32
 637     # . . call
 638     e8/call  clear-stream/disp32
 639     # . . discard args
 640     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 641     # . clear-stream(_test-input-buffered-file->buffer)
 642     # . . push args
 643     68/push  _test-input-buffered-file->buffer/imm32
 644     # . . call
 645     e8/call  clear-stream/disp32
 646     # . . discard args
 647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 648     # . clear-stream(_test-output-stream)
 649     # . . push args
 650     68/push  _test-output-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-output-buffered-file->buffer)
 656     # . . push args
 657     68/push  _test-output-buffered-file->buffer/imm32
 658     # . . call
 659     e8/call  clear-stream/disp32
 660     # . . discard args
 661     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 662     # initialize input
 663     # . write(_test-input-stream, "ab %ecx/foo")
 664     # . . push args
 665     68/push  "ab %ecx/foo"/imm32
 666     68/push  _test-input-stream/imm32
 667     # . . call
 668     e8/call  write/disp32
 669     # . . discard args
 670     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 671     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
 672     # . . push args
 673     68/push  _test-output-buffered-file/imm32
 674     68/push  _test-input-buffered-file/imm32
 675     # . . call
 676     e8/call  subx-sigils/disp32
 677     # . . discard args
 678     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 679     # check that the line just passed through
 680     # . flush(_test-output-buffered-file)
 681     # . . push args
 682     68/push  _test-output-buffered-file/imm32
 683     # . . call
 684     e8/call  flush/disp32
 685     # . . discard args
 686     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 687     # . check-stream-equal(_test-output-stream, "ab 3/mod/direct 0x00000001/rm32 \n", msg)
 688     # . . push args
 689     68/push  "F - test-subx-sigils-direct-mode-with-metadata"/imm32
 690     68/push  "ab 3/mod/direct 0x00000001/rm32 \n"/imm32
 691     68/push  _test-output-stream/imm32
 692     # . . call
 693     e8/call  check-stream-equal/disp32
 694     # . . discard args
 695     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 696     # . epilogue
 697     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 698     5d/pop-to-ebp
 699     c3/return
 700 
 701 test-subx-sigils-register-indirect-mode:
 702     # . prologue
 703     55/push-ebp
 704     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 705     # setup
 706     # . clear-stream(_test-input-stream)
 707     # . . push args
 708     68/push  _test-input-stream/imm32
 709     # . . call
 710     e8/call  clear-stream/disp32
 711     # . . discard args
 712     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 713     # . clear-stream(_test-input-buffered-file->buffer)
 714     # . . push args
 715     68/push  _test-input-buffered-file->buffer/imm32
 716     # . . call
 717     e8/call  clear-stream/disp32
 718     # . . discard args
 719     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 720     # . clear-stream(_test-output-stream)
 721     # . . push args
 722     68/push  _test-output-stream/imm32
 723     # . . call
 724     e8/call  clear-stream/disp32
 725     # . . discard args
 726     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 727     # . clear-stream(_test-output-buffered-file->buffer)
 728     # . . push args
 729     68/push  _test-output-buffered-file->buffer/imm32
 730     # . . call
 731     e8/call  clear-stream/disp32
 732     # . . discard args
 733     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 734     # initialize input
 735     # . write(_test-input-stream, "ab *ecx")
 736     # . . push args
 737     68/push  "ab *ecx"/imm32
 738     68/push  _test-input-stream/imm32
 739     # . . call
 740     e8/call  write/disp32
 741     # . . discard args
 742     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 743     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
 744     # . . push args
 745     68/push  _test-output-buffered-file/imm32
 746     68/push  _test-input-buffered-file/imm32
 747     # . . call
 748     e8/call  subx-sigils/disp32
 749     # . . discard args
 750     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 751     # check that the line just passed through
 752     # . flush(_test-output-buffered-file)
 753     # . . push args
 754     68/push  _test-output-buffered-file/imm32
 755     # . . call
 756     e8/call  flush/disp32
 757     # . . discard args
 758     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 759 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 785     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 0x00000001/rm32 \n", msg)
 786     # . . push args
 787     68/push  "F - test-subx-sigils-register-indirect-mode"/imm32
 788     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
 789     68/push  _test-output-stream/imm32
 790     # . . call
 791     e8/call  check-stream-equal/disp32
 792     # . . discard args
 793     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 794     # . epilogue
 795     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 796     5d/pop-to-ebp
 797     c3/return
 798 
 799 test-subx-sigils-register-indirect-mode-with-metadata:
 800     # . prologue
 801     55/push-ebp
 802     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 803     # setup
 804     # . clear-stream(_test-input-stream)
 805     # . . push args
 806     68/push  _test-input-stream/imm32
 807     # . . call
 808     e8/call  clear-stream/disp32
 809     # . . discard args
 810     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 811     # . clear-stream(_test-input-buffered-file->buffer)
 812     # . . push args
 813     68/push  _test-input-buffered-file->buffer/imm32
 814     # . . call
 815     e8/call  clear-stream/disp32
 816     # . . discard args
 817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 818     # . clear-stream(_test-output-stream)
 819     # . . push args
 820     68/push  _test-output-stream/imm32
 821     # . . call
 822     e8/call  clear-stream/disp32
 823     # . . discard args
 824     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 825     # . clear-stream(_test-output-buffered-file->buffer)
 826     # . . push args
 827     68/push  _test-output-buffered-file->buffer/imm32
 828     # . . call
 829     e8/call  clear-stream/disp32
 830     # . . discard args
 831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 832     # initialize input
 833     # . write(_test-input-stream, "ab *ecx/foo")
 834     # . . push args
 835     68/push  "ab *ecx/foo"/imm32
 836     68/push  _test-input-stream/imm32
 837     # . . call
 838     e8/call  write/disp32
 839     # . . discard args
 840     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 841     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
 842     # . . push args
 843     68/push  _test-output-buffered-file/imm32
 844     68/push  _test-input-buffered-file/imm32
 845     # . . call
 846     e8/call  subx-sigils/disp32
 847     # . . discard args
 848     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 849     # check that the line just passed through
 850     # . flush(_test-output-buffered-file)
 851     # . . push args
 852     68/push  _test-output-buffered-file/imm32
 853     # . . call
 854     e8/call  flush/disp32
 855     # . . discard args
 856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 857     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 0x00000001/rm32 \n", msg)
 858     # . . push args
 859     68/push  "F - test-subx-sigils-register-indirect-mode-with-metadata"/imm32
 860     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
 861     68/push  _test-output-stream/imm32
 862     # . . call
 863     e8/call  check-stream-equal/disp32
 864     # . . discard args
 865     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 866     # . epilogue
 867     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 868     5d/pop-to-ebp
 869     c3/return
 870 
 871 test-subx-sigils-register-indirect-mode-without-displacement:
 872     # . prologue
 873     55/push-ebp
 874     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 875     # setup
 876     # . clear-stream(_test-input-stream)
 877     # . . push args
 878     68/push  _test-input-stream/imm32
 879     # . . call
 880     e8/call  clear-stream/disp32
 881     # . . discard args
 882     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 883     # . clear-stream(_test-input-buffered-file->buffer)
 884     # . . push args
 885     68/push  _test-input-buffered-file->buffer/imm32
 886     # . . call
 887     e8/call  clear-stream/disp32
 888     # . . discard args
 889     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 890     # . clear-stream(_test-output-stream)
 891     # . . push args
 892     68/push  _test-output-stream/imm32
 893     # . . call
 894     e8/call  clear-stream/disp32
 895     # . . discard args
 896     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 897     # . clear-stream(_test-output-buffered-file->buffer)
 898     # . . push args
 899     68/push  _test-output-buffered-file->buffer/imm32
 900     # . . call
 901     e8/call  clear-stream/disp32
 902     # . . discard args
 903     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 904     # initialize input
 905     # . write(_test-input-stream, "ab *(ecx)")
 906     # . . push args
 907     68/push  "ab *(ecx)"/imm32
 908     68/push  _test-input-stream/imm32
 909     # . . call
 910     e8/call  write/disp32
 911     # . . discard args
 912     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 913     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
 914     # . . push args
 915     68/push  _test-output-buffered-file/imm32
 916     68/push  _test-input-buffered-file/imm32
 917     # . . call
 918     e8/call  subx-sigils/disp32
 919     # . . discard args
 920     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 921     # check that the line just passed through
 922     # . flush(_test-output-buffered-file)
 923     # . . push args
 924     68/push  _test-output-buffered-file/imm32
 925     # . . call
 926     e8/call  flush/disp32
 927     # . . discard args
 928     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 929 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
 955     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 1/rm32 \n", msg)
 956     # . . push args
 957     68/push  "F - test-subx-sigils-register-indirect-mode-without-displacement"/imm32
 958     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
 959     68/push  _test-output-stream/imm32
 960     # . . call
 961     e8/call  check-stream-equal/disp32
 962     # . . discard args
 963     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
 964     # . epilogue
 965     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 966     5d/pop-to-ebp
 967     c3/return
 968 
 969 test-subx-sigils-register-indirect-mode-with-displacement:
 970     # . prologue
 971     55/push-ebp
 972     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 973     # setup
 974     # . clear-stream(_test-input-stream)
 975     # . . push args
 976     68/push  _test-input-stream/imm32
 977     # . . call
 978     e8/call  clear-stream/disp32
 979     # . . discard args
 980     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 981     # . clear-stream(_test-input-buffered-file->buffer)
 982     # . . push args
 983     68/push  _test-input-buffered-file->buffer/imm32
 984     # . . call
 985     e8/call  clear-stream/disp32
 986     # . . discard args
 987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 988     # . clear-stream(_test-output-stream)
 989     # . . push args
 990     68/push  _test-output-stream/imm32
 991     # . . call
 992     e8/call  clear-stream/disp32
 993     # . . discard args
 994     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 995     # . clear-stream(_test-output-buffered-file->buffer)
 996     # . . push args
 997     68/push  _test-output-buffered-file->buffer/imm32
 998     # . . call
 999     e8/call  clear-stream/disp32
1000     # . . discard args
1001     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1002     # initialize input
1003     # . write(_test-input-stream, "ab *(ecx+4)")
1004     # . . push args
1005     68/push  "ab *(ecx+4)"/imm32
1006     68/push  _test-input-stream/imm32
1007     # . . call
1008     e8/call  write/disp32
1009     # . . discard args
1010     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1011     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
1012     # . . push args
1013     68/push  _test-output-buffered-file/imm32
1014     68/push  _test-input-buffered-file/imm32
1015     # . . call
1016     e8/call  subx-sigils/disp32
1017     # . . discard args
1018     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1019     # check that the line just passed through
1020     # . flush(_test-output-buffered-file)
1021     # . . push args
1022     68/push  _test-output-buffered-file/imm32
1023     # . . call
1024     e8/call  flush/disp32
1025     # . . discard args
1026     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1027 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1053     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 1/rm32 4/disp32 \n", msg)
1054     # . . push args
1055     68/push  "F - test-subx-sigils-register-indirect-mode-with-displacement"/imm32
1056     68/push  "ab 2/mod/*+disp32 0x00000001/rm32 0x00000004/disp32 \n"/imm32
1057     68/push  _test-output-stream/imm32
1058     # . . call
1059     e8/call  check-stream-equal/disp32
1060     # . . discard args
1061     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1062     # . epilogue
1063     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1064     5d/pop-to-ebp
1065     c3/return
1066 
1067 # boss level
1068 test-subx-sigils-register-indirect-mode-with-sib-byte:
1069     # . prologue
1070     55/push-ebp
1071     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1072     # setup
1073     # . clear-stream(_test-input-stream)
1074     # . . push args
1075     68/push  _test-input-stream/imm32
1076     # . . call
1077     e8/call  clear-stream/disp32
1078     # . . discard args
1079     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1080     # . clear-stream(_test-input-buffered-file->buffer)
1081     # . . push args
1082     68/push  _test-input-buffered-file->buffer/imm32
1083     # . . call
1084     e8/call  clear-stream/disp32
1085     # . . discard args
1086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1087     # . clear-stream(_test-output-stream)
1088     # . . push args
1089     68/push  _test-output-stream/imm32
1090     # . . call
1091     e8/call  clear-stream/disp32
1092     # . . discard args
1093     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1094     # . clear-stream(_test-output-buffered-file->buffer)
1095     # . . push args
1096     68/push  _test-output-buffered-file->buffer/imm32
1097     # . . call
1098     e8/call  clear-stream/disp32
1099     # . . discard args
1100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1101     # initialize input
1102     # . write(_test-input-stream, "ab *(ecx + edx<<3 + 4)")
1103     # . . push args
1104     68/push  "ab *(ecx + edx<<3 + 4)"/imm32
1105     68/push  _test-input-stream/imm32
1106     # . . call
1107     e8/call  write/disp32
1108     # . . discard args
1109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1110     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
1111     # . . push args
1112     68/push  _test-output-buffered-file/imm32
1113     68/push  _test-input-buffered-file/imm32
1114     # . . call
1115     e8/call  subx-sigils/disp32
1116     # . . discard args
1117     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1118     # check that the line just passed through
1119     # . flush(_test-output-buffered-file)
1120     # . . push args
1121     68/push  _test-output-buffered-file/imm32
1122     # . . call
1123     e8/call  flush/disp32
1124     # . . discard args
1125     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1126 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1152     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale 4/disp32 \n", msg)
1153     # . . push args
1154     68/push  "F - test-subx-sigils-register-indirect-mode-with-sib-byte"/imm32
1155     68/push  "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0x00000004/disp32 \n"/imm32
1156     68/push  _test-output-stream/imm32
1157     # . . call
1158     e8/call  check-stream-equal/disp32
1159     # . . discard args
1160     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1161     # . epilogue
1162     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1163     5d/pop-to-ebp
1164     c3/return
1165 
1166 test-subx-sigils-register-indirect-mode-with-sib-byte-negative-displacement:
1167     # . prologue
1168     55/push-ebp
1169     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1170     # setup
1171     # . clear-stream(_test-input-stream)
1172     # . . push args
1173     68/push  _test-input-stream/imm32
1174     # . . call
1175     e8/call  clear-stream/disp32
1176     # . . discard args
1177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1178     # . clear-stream(_test-input-buffered-file->buffer)
1179     # . . push args
1180     68/push  _test-input-buffered-file->buffer/imm32
1181     # . . call
1182     e8/call  clear-stream/disp32
1183     # . . discard args
1184     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1185     # . clear-stream(_test-output-stream)
1186     # . . push args
1187     68/push  _test-output-stream/imm32
1188     # . . call
1189     e8/call  clear-stream/disp32
1190     # . . discard args
1191     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1192     # . clear-stream(_test-output-buffered-file->buffer)
1193     # . . push args
1194     68/push  _test-output-buffered-file->buffer/imm32
1195     # . . call
1196     e8/call  clear-stream/disp32
1197     # . . discard args
1198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1199     # initialize input
1200     # . write(_test-input-stream, "ab *(ecx + edx<<3 - 4)")
1201     # . . push args
1202     68/push  "ab *(ecx + edx<<3 - 4)"/imm32
1203     68/push  _test-input-stream/imm32
1204     # . . call
1205     e8/call  write/disp32
1206     # . . discard args
1207     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1208     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
1209     # . . push args
1210     68/push  _test-output-buffered-file/imm32
1211     68/push  _test-input-buffered-file/imm32
1212     # . . call
1213     e8/call  subx-sigils/disp32
1214     # . . discard args
1215     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1216     # check that the line just passed through
1217     # . flush(_test-output-buffered-file)
1218     # . . push args
1219     68/push  _test-output-buffered-file/imm32
1220     # . . call
1221     e8/call  flush/disp32
1222     # . . discard args
1223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1224 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1250     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale -4/disp32 \n", msg)
1251     # . . push args
1252     68/push  "F - test-subx-sigils-register-indirect-mode-with-sib-byte-negative-displacement"/imm32
1253     68/push  "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0xfffffffc/disp32 \n"/imm32
1254     68/push  _test-output-stream/imm32
1255     # . . call
1256     e8/call  check-stream-equal/disp32
1257     # . . discard args
1258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1259     # . epilogue
1260     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1261     5d/pop-to-ebp
1262     c3/return
1263 
1264 test-subx-sigils-indirect-mode-without-register:
1265     # . prologue
1266     55/push-ebp
1267     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1268     # setup
1269     # . clear-stream(_test-input-stream)
1270     # . . push args
1271     68/push  _test-input-stream/imm32
1272     # . . call
1273     e8/call  clear-stream/disp32
1274     # . . discard args
1275     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1276     # . clear-stream(_test-input-buffered-file->buffer)
1277     # . . push args
1278     68/push  _test-input-buffered-file->buffer/imm32
1279     # . . call
1280     e8/call  clear-stream/disp32
1281     # . . discard args
1282     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1283     # . clear-stream(_test-output-stream)
1284     # . . push args
1285     68/push  _test-output-stream/imm32
1286     # . . call
1287     e8/call  clear-stream/disp32
1288     # . . discard args
1289     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1290     # . clear-stream(_test-output-buffered-file->buffer)
1291     # . . push args
1292     68/push  _test-output-buffered-file->buffer/imm32
1293     # . . call
1294     e8/call  clear-stream/disp32
1295     # . . discard args
1296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1297     # initialize input
1298     # . write(_test-input-stream, "ab *Foo")
1299     # . . push args
1300     68/push  "ab *Foo"/imm32
1301     68/push  _test-input-stream/imm32
1302     # . . call
1303     e8/call  write/disp32
1304     # . . discard args
1305     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1306     # subx-sigils(_test-input-buffered-file, _test-output-buffered-file)
1307     # . . push args
1308     68/push  _test-output-buffered-file/imm32
1309     68/push  _test-input-buffered-file/imm32
1310     # . . call
1311     e8/call  subx-sigils/disp32
1312     # . . discard args
1313     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1314     # check that the line just passed through
1315     # . flush(_test-output-buffered-file)
1316     # . . push args
1317     68/push  _test-output-buffered-file/imm32
1318     # . . call
1319     e8/call  flush/disp32
1320     # . . discard args
1321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1322 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
1348     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 5/rm32/.disp32 Foo/disp32 \n", msg)
1349     # . . push args
1350     68/push  "F - test-subx-sigils-indirect-mode-without-register"/imm32
1351     68/push  "ab 0/mod/indirect 5/rm32/.disp32 Foo/disp32 \n"/imm32
1352     68/push  _test-output-stream/imm32
1353     # . . call
1354     e8/call  check-stream-equal/disp32
1355     # . . discard args
1356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1357     # . epilogue
1358     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1359     5d/pop-to-ebp
1360     c3/return
1361 
1362 emit-direct-mode:  # out : (address buffered-file), word-slice : (address slice)
1363     # . prologue
1364     55/push-ebp
1365     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1366     # . save registers
1367     50/push-eax
1368     # var local-slice/eax : (address slice) = {word-slice->start, word-slice->end}
1369     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   0xc/disp8       .                 # copy *(ebp+12) to eax
1370     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1371     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1372     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           4/r32/esp   .               .                 # copy esp to eax
1373     # ++local-slice->start to skip '%'
1374     # . ++(*eax)
1375     ff          0/subop/increment   0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # increment *eax
1376     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
1377     # . . push args
1378     50/push-eax
1379     68/push  0x2f/imm32/slash
1380     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1381     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1382     # . . call
1383     e8/call  next-token-from-slice/disp32
1384     # . . discard args
1385     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1386     # reg-num/eax = get-slice(Registers, local-slice, row-size=8)
1387     # . . push args
1388     68/push  "Registers"/imm32
1389     68/push  8/imm32/row-size
1390     50/push-eax
1391     68/push  Registers/imm32
1392     # . . call
1393     e8/call  get-slice/disp32
1394     # . . discard args
1395     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1396     # write-buffered(out, "3/mod/direct ")
1397     # . . push args
1398     68/push  "3/mod/direct "/imm32
1399     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1400     # . . call
1401     e8/call  write-buffered/disp32
1402     # . . discard args
1403     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1404     # print-int32-buffered(out, *reg-num)
1405     # . . push args
1406     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1407     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1408     # . . call
1409     e8/call  print-int32-buffered/disp32
1410     # . . discard args
1411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1412     # write-buffered(out, "/rm32")
1413     # . . push args
1414     68/push  "/rm32"/imm32
1415     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1416     # . . call
1417     e8/call  write-buffered/disp32
1418     # . . discard args
1419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1420 $emit-direct-mode:end:
1421     # . reclaim locals
1422     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1423     # . restore registers
1424     58/pop-to-eax
1425     # . epilogue
1426     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1427     5d/pop-to-ebp
1428     c3/return
1429 
1430 test-emit-direct-mode:
1431     # . prologue
1432     55/push-ebp
1433     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1434     # setup
1435     # . clear-stream(_test-output-stream)
1436     # . . push args
1437     68/push  _test-output-stream/imm32
1438     # . . call
1439     e8/call  clear-stream/disp32
1440     # . . discard args
1441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1442     # . clear-stream(_test-output-buffered-file->buffer)
1443     # . . push args
1444     68/push  _test-output-buffered-file->buffer/imm32
1445     # . . call
1446     e8/call  clear-stream/disp32
1447     # . . discard args
1448     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1449     # var slice/ecx = "%eax"
1450     b8/copy-to-eax  "%eax"/imm32
1451     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1452     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
1453     05/add-to-eax  4/imm32
1454     # . ecx = {eax, ecx}
1455     51/push-ecx
1456     50/push-eax
1457     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1458     # emit-direct-mode(_test-output-buffered-file, str)
1459     # . . push args
1460     51/push-ecx
1461     68/push  _test-output-buffered-file/imm32
1462     # . . call
1463     e8/call  emit-direct-mode/disp32
1464     # . . discard args
1465     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32         # add to esp
1466     # . flush(_test-output-buffered-file)
1467     # . . push args
1468     68/push  _test-output-buffered-file/imm32
1469     # . . call
1470     e8/call  flush/disp32
1471     # . . discard args
1472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1473 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1499     # check-stream-equal(_test-output-stream, "3/mod/direct 0/rm32", msg)
1500     # . . push args
1501     68/push  "F - test-emit-direct-mode/0"/imm32
1502     68/push  "3/mod/direct 0x00000000/rm32"/imm32
1503     68/push  _test-output-stream/imm32
1504     # . . call
1505     e8/call  check-stream-equal/disp32
1506     # . . discard args
1507     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1508     # . epilogue
1509     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1510     5d/pop-to-ebp
1511     c3/return
1512 
1513 test-emit-direct-mode-2:
1514     # . prologue
1515     55/push-ebp
1516     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1517     # setup
1518     # . clear-stream(_test-output-stream)
1519     # . . push args
1520     68/push  _test-output-stream/imm32
1521     # . . call
1522     e8/call  clear-stream/disp32
1523     # . . discard args
1524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1525     # . clear-stream(_test-output-buffered-file->buffer)
1526     # . . push args
1527     68/push  _test-output-buffered-file->buffer/imm32
1528     # . . call
1529     e8/call  clear-stream/disp32
1530     # . . discard args
1531     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1532     # var slice/ecx = "%edi"
1533     b8/copy-to-eax  "%edi"/imm32
1534     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1535     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
1536     05/add-to-eax  4/imm32
1537     # . ecx = {eax, ecx}
1538     51/push-ecx
1539     50/push-eax
1540     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1541     # emit-direct-mode(_test-output-buffered-file, str/ecx)
1542     # . . push args
1543     51/push-ecx
1544     68/push  _test-output-buffered-file/imm32
1545     # . . call
1546     e8/call  emit-direct-mode/disp32
1547     # . . discard args
1548     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32         # add to esp
1549     # . flush(_test-output-buffered-file)
1550     # . . push args
1551     68/push  _test-output-buffered-file/imm32
1552     # . . call
1553     e8/call  flush/disp32
1554     # . . discard args
1555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1556 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
1582     # check-stream-equal(_test-output-stream, "3/mod/direct 7/rm32", msg)
1583     # . . push args
1584     68/push  "F - test-emit-direct-mode/1"/imm32
1585     68/push  "3/mod/direct 0x00000007/rm32"/imm32
1586     68/push  _test-output-stream/imm32
1587     # . . call
1588     e8/call  check-stream-equal/disp32
1589     # . . discard args
1590     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1591     # . epilogue
1592     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1593     5d/pop-to-ebp
1594     c3/return
1595 
1596 # (re)compute the bounds of the next word or parenthetical expression in the line
1597 # return empty string on reaching end of file
1598 #
1599 # error messages considered:
1600 #   * ...                   -> error: no space after '*'
1601 #   *(...                   -> error: *(...) expression must be all on a single line
1602 next-word-or-expression:  # line : (address stream byte), out : (address slice)
1603     # pseudocode:
1604     #   skip-chars-matching(line, ' ')
1605     #   if line->read >= line->write              # end of line
1606     #     out = {0, 0}
1607     #     return
1608     #   out->start = &line->data[line->read]
1609     #   if line->data[line->read] == '#'          # comment
1610     #     out.end = &line->data[line->write]
1611     #     return
1612     #   if line->data[line->read] == '"'          # string literal
1613     #     skip-string(line)
1614     #   else if line->data[line->read] == '*'     # expression
1615     #     if line->data[line->read + 1] == ' '
1616     #       abort
1617     #     if line->data[line->read + 1] == '('
1618     #       skip-until-close-paren(line)
1619     #       if (line->data[line->read] != ')'
1620     #         abort
1621     #       ++line->data[line->read] to skip ')'
1622     #   skip-chars-not-matching-whitespace(line)
1623     #   out->end = &line->data[line->read]
1624     #
1625     # registers:
1626     #   ecx: often line->read
1627     #   eax: often line->data[line->read]
1628     #
1629     # . prologue
1630     55/push-ebp
1631     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1632     # . save registers
1633     50/push-eax
1634     51/push-ecx
1635     56/push-esi
1636     57/push-edi
1637     # esi = line
1638     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
1639     # edi = out
1640     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
1641     # skip-chars-matching(line, ' ')
1642     # . . push args
1643     68/push  0x20/imm32/space
1644     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1645     # . . call
1646     e8/call  skip-chars-matching/disp32
1647     # . . discard args
1648     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1649 $next-word-or-expression:check0:
1650     # if (line->read >= line->write) clear out and return
1651     # . ecx = line->read
1652     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1653     # . if (ecx < line->write) goto next check
1654     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare ecx with *esi
1655     7c/jump-if-lesser  $next-word-or-expression:check-for-comment/disp8
1656     # . return out = {0, 0}
1657     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
1658     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
1659     e9/jump  $next-word-or-expression:end/disp32
1660 $next-word-or-expression:check-for-comment:
1661     # out->start = &line->data[line->read]
1662     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
1663     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
1664     # if (line->data[line->read] != '#') goto next check
1665     # . eax = line->data[line->read]
1666     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1667     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
1668     # . if (eax != '#') goto next check
1669     3d/compare-eax-and  0x23/imm32/pound
1670     75/jump-if-not-equal  $next-word-or-expression:check-for-string-literal/disp8
1671 $next-word-or-expression:comment:
1672     # out->end = &line->data[line->write]
1673     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1674     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
1675     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1676     # line->read = line->write  # skip rest of line
1677     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1678     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(esi+4)
1679     # return
1680     eb/jump  $next-word-or-expression:end/disp8
1681 $next-word-or-expression:check-for-string-literal:
1682     # if (line->data[line->read] != '"') goto next check
1683     3d/compare-eax-and  0x22/imm32/dquote
1684     75/jump-if-not-equal  $next-word-or-expression:check-for-expression/disp8
1685 $next-word-or-expression:string-literal:
1686     # skip-string(line)
1687     # . . push args
1688     56/push-esi
1689     # . . call
1690     e8/call  skip-string/disp32
1691     # . . discard args
1692     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1693     # skip rest of word
1694     eb/jump  $next-word-or-expression:regular-word/disp8
1695 $next-word-or-expression:check-for-expression:
1696     # if (line->data[line->read] != '*') goto next check
1697     3d/compare-eax-and  0x2a/imm32/asterisk
1698     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
1699     # if (line->data[line->read + 1] == ' ') goto error1
1700     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
1701     3d/compare-eax-and  0x20/imm32/space
1702     74/jump-if-equal  $next-word-or-expression:error1/disp8
1703     # if (line->data[line->read + 1] != '(') goto regular word
1704     3d/compare-eax-and  0x28/imm32/open-paren
1705     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
1706 $next-word-or-expression:paren:
1707     # skip-until-close-paren(line)
1708     # . . push args
1709     56/push-esi
1710     # . . call
1711     e8/call  skip-until-close-paren/disp32
1712     # . . discard args
1713     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1714     # if (line->data[line->read] != ')') goto error2
1715     # . eax = line->data[line->read]
1716     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
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 error2
1719     3d/compare-eax-and  0x29/imm32/close-paren
1720     75/jump-if-not-equal  $next-word-or-expression:error2/disp8
1721     # skip ')'
1722     ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # increment *(esi+4)
1723     # fall through
1724 $next-word-or-expression:regular-word:
1725     # skip-chars-not-matching-whitespace(line)  # including trailing newline
1726     # . . push args
1727     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1728     # . . call
1729     e8/call  skip-chars-not-matching-whitespace/disp32
1730     # . . discard args
1731     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1732     # out->end = &line->data[line->read]
1733     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1734     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
1735     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1736 $next-word-or-expression:end:
1737     # . restore registers
1738     5f/pop-to-edi
1739     5e/pop-to-esi
1740     59/pop-to-ecx
1741     58/pop-to-eax
1742     # . epilogue
1743     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1744     5d/pop-to-ebp
1745     c3/return
1746 
1747 $next-word-or-expression:error1:
1748     # print(stderr, "error: no space allowed after '*' in '" line "'")
1749     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
1750     # . . push args
1751     68/push  "error: no space allowed after '*' in '"/imm32
1752     68/push  Stderr/imm32
1753     # . . call
1754     e8/call  write-buffered/disp32
1755     # . . discard args
1756     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1757     # . write-stream-data(Stderr, line)
1758     # . . push args
1759     56/push-esi
1760     68/push  Stderr/imm32
1761     # . . call
1762     e8/call  write-stream-data/disp32
1763     # . . discard args
1764     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1765     # . write-buffered(Stderr, "'")
1766     # . . push args
1767     68/push  "'"/imm32
1768     68/push  Stderr/imm32
1769     # . . call
1770     e8/call  write-buffered/disp32
1771     # . . discard args
1772     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1773     # . flush(Stderr)
1774     # . . push args
1775     68/push  Stderr/imm32
1776     # . . call
1777     e8/call  flush/disp32
1778     # . . discard args
1779     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1780     # . syscall(exit, 1)
1781     bb/copy-to-ebx  1/imm32
1782     b8/copy-to-eax  1/imm32/exit
1783     cd/syscall  0x80/imm8
1784     # never gets here
1785 
1786 $next-word-or-expression:error2:
1787     # print(stderr, "error: *(...) expression must be all on a single line in '" line "'")
1788     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
1789     # . . push args
1790     68/push  "error: *(...) expression must be all on a single line in '"/imm32
1791     68/push  Stderr/imm32
1792     # . . call
1793     e8/call  write-buffered/disp32
1794     # . . discard args
1795     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1796     # . write-stream-data(Stderr, line)
1797     # . . push args
1798     56/push-esi
1799     68/push  Stderr/imm32
1800     # . . call
1801     e8/call  write-stream-data/disp32
1802     # . . discard args
1803     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1804     # . write-buffered(Stderr, "'")
1805     # . . push args
1806     68/push  "'"/imm32
1807     68/push  Stderr/imm32
1808     # . . call
1809     e8/call  write-buffered/disp32
1810     # . . discard args
1811     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1812     # . flush(Stderr)
1813     # . . push args
1814     68/push  Stderr/imm32
1815     # . . call
1816     e8/call  flush/disp32
1817     # . . discard args
1818     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1819     # . syscall(exit, 1)
1820     bb/copy-to-ebx  1/imm32
1821     b8/copy-to-eax  1/imm32/exit
1822     cd/syscall  0x80/imm8
1823     # never gets here
1824 
1825 test-next-word-or-expression:
1826     # . prologue
1827     55/push-ebp
1828     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1829     # setup
1830     # . clear-stream(_test-input-stream)
1831     # . . push args
1832     68/push  _test-input-stream/imm32
1833     # . . call
1834     e8/call  clear-stream/disp32
1835     # . . discard args
1836     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1837     # var slice/ecx = {0, 0}
1838     68/push  0/imm32/end
1839     68/push  0/imm32/start
1840     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1841     # write(_test-input-stream, "  ab")
1842     # . . push args
1843     68/push  "  ab"/imm32
1844     68/push  _test-input-stream/imm32
1845     # . . call
1846     e8/call  write/disp32
1847     # . . discard args
1848     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1849     # next-word-or-expression(_test-input-stream, slice)
1850     # . . push args
1851     51/push-ecx
1852     68/push  _test-input-stream/imm32
1853     # . . call
1854     e8/call  next-word-or-expression/disp32
1855     # . . discard args
1856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1857     # check-ints-equal(_test-input-stream->read, 4, msg)
1858     # . . push args
1859     68/push  "F - test-next-word-or-expression/updates-stream-read-correctly"/imm32
1860     68/push  4/imm32
1861     b8/copy-to-eax  _test-input-stream/imm32
1862     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1863     # . . call
1864     e8/call  check-ints-equal/disp32
1865     # . . discard args
1866     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1867     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1868     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1869     # . . push args
1870     68/push  "F - test-next-word-or-expression: start"/imm32
1871     68/push  0xe/imm32
1872     # . . push slice->start - _test-input-stream
1873     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1874     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1875     50/push-eax
1876     # . . call
1877     e8/call  check-ints-equal/disp32
1878     # . . discard args
1879     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1880     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1881     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1882     # . . push args
1883     68/push  "F - test-next-word-or-expression: end"/imm32
1884     68/push  0x10/imm32
1885     # . . push slice->end - _test-input-stream
1886     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
1887     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1888     50/push-eax
1889     # . . call
1890     e8/call  check-ints-equal/disp32
1891     # . . discard args
1892     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1893     # . epilogue
1894     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1895     5d/pop-to-ebp
1896     c3/return
1897 
1898 test-next-word-or-expression-returns-whole-comment:
1899     # . prologue
1900     55/push-ebp
1901     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1902     # setup
1903     # . clear-stream(_test-input-stream)
1904     # . . push args
1905     68/push  _test-input-stream/imm32
1906     # . . call
1907     e8/call  clear-stream/disp32
1908     # . . discard args
1909     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1910     # var slice/ecx = {0, 0}
1911     68/push  0/imm32/end
1912     68/push  0/imm32/start
1913     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1914     # write(_test-input-stream, "  # a")
1915     # . . push args
1916     68/push  "  # a"/imm32
1917     68/push  _test-input-stream/imm32
1918     # . . call
1919     e8/call  write/disp32
1920     # . . discard args
1921     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1922     # next-word-or-expression(_test-input-stream, slice)
1923     # . . push args
1924     51/push-ecx
1925     68/push  _test-input-stream/imm32
1926     # . . call
1927     e8/call  next-word-or-expression/disp32
1928     # . . discard args
1929     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1930     # check-ints-equal(_test-input-stream->read, 5, msg)
1931     # . . push args
1932     68/push  "F - test-next-word-or-expression-returns-whole-comment/updates-stream-read-correctly"/imm32
1933     68/push  5/imm32
1934     b8/copy-to-eax  _test-input-stream/imm32
1935     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1936     # . . call
1937     e8/call  check-ints-equal/disp32
1938     # . . discard args
1939     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1940     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1941     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1942     # . . push args
1943     68/push  "F - test-next-word-or-expression-returns-whole-comment: start"/imm32
1944     68/push  0xe/imm32
1945     # . . push slice->start - _test-input-stream
1946     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1947     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1948     50/push-eax
1949     # . . call
1950     e8/call  check-ints-equal/disp32
1951     # . . discard args
1952     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1953     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
1954     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
1955     # . . push args
1956     68/push  "F - test-next-word-or-expression-returns-whole-comment: end"/imm32
1957     68/push  0x11/imm32
1958     # . . push slice->end - _test-input-stream
1959     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
1960     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1961     50/push-eax
1962     # . . call
1963     e8/call  check-ints-equal/disp32
1964     # . . discard args
1965     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1966     # . epilogue
1967     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1968     5d/pop-to-ebp
1969     c3/return
1970 
1971 test-next-word-or-expression-returns-empty-slice-on-eof:
1972     # . prologue
1973     55/push-ebp
1974     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1975     # setup
1976     # . clear-stream(_test-input-stream)
1977     # . . push args
1978     68/push  _test-input-stream/imm32
1979     # . . call
1980     e8/call  clear-stream/disp32
1981     # . . discard args
1982     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1983     # var slice/ecx = {0, 0}
1984     68/push  0/imm32/end
1985     68/push  0/imm32/start
1986     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1987     # write nothing to _test-input-stream
1988     # next-word-or-expression(_test-input-stream, slice)
1989     # . . push args
1990     51/push-ecx
1991     68/push  _test-input-stream/imm32
1992     # . . call
1993     e8/call  next-word-or-expression/disp32
1994     # . . discard args
1995     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1996     # check-ints-equal(slice->end - slice->start, 0, msg)
1997     # . . push args
1998     68/push  "F - test-next-word-or-expression-returns-empty-expression-on-eof"/imm32
1999     68/push  0/imm32
2000     # . . push slice->end - slice->start
2001     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2002     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
2003     50/push-eax
2004     # . . call
2005     e8/call  check-ints-equal/disp32
2006     # . . discard args
2007     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2008     # . epilogue
2009     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2010     5d/pop-to-ebp
2011     c3/return
2012 
2013 test-next-word-or-expression-returns-string-literal:
2014     # . prologue
2015     55/push-ebp
2016     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2017     # setup
2018     # . clear-stream(_test-input-stream)
2019     # . . push args
2020     68/push  _test-input-stream/imm32
2021     # . . call
2022     e8/call  clear-stream/disp32
2023     # . . discard args
2024     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2025     # var slice/ecx = {0, 0}
2026     68/push  0/imm32/end
2027     68/push  0/imm32/start
2028     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2029     # write(_test-input-stream, " \"a b\"/imm32 ")
2030     # . . push args
2031     68/push  " \"a b\"/imm32 "/imm32
2032     68/push  _test-input-stream/imm32
2033     # . . call
2034     e8/call  write/disp32
2035     # . . discard args
2036     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2037     # next-word-or-expression(_test-input-stream, slice)
2038     # . . push args
2039     51/push-ecx
2040     68/push  _test-input-stream/imm32
2041     # . . call
2042     e8/call  next-word-or-expression/disp32
2043     # . . discard args
2044     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2045     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2046     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2047     # . . push args
2048     68/push  "F - test-next-word-or-expression-returns-string-literal: start"/imm32
2049     68/push  0xd/imm32
2050     # . . push slice->start - _test-input-stream
2051     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2052     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract 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     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
2059     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
2060     # . . push args
2061     68/push  "F - test-next-word-or-expression-returns-string-literal: end"/imm32
2062     68/push  0x18/imm32
2063     # . . push slice->end - _test-input-stream
2064     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2065     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2066     50/push-eax
2067     # . . call
2068     e8/call  check-ints-equal/disp32
2069     # . . discard args
2070     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2071     # . epilogue
2072     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2073     5d/pop-to-ebp
2074     c3/return
2075 
2076 test-next-word-or-expression-returns-string-with-escapes:
2077     # . prologue
2078     55/push-ebp
2079     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2080     # setup
2081     # . clear-stream(_test-input-stream)
2082     # . . push args
2083     68/push  _test-input-stream/imm32
2084     # . . call
2085     e8/call  clear-stream/disp32
2086     # . . discard args
2087     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2088     # var slice/ecx = {0, 0}
2089     68/push  0/imm32/end
2090     68/push  0/imm32/start
2091     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2092     # write(_test-input-stream, " \"a\\\"b\"/x")
2093     # . . push args
2094     68/push  " \"a\\\"b\"/x"/imm32
2095     68/push  _test-input-stream/imm32
2096     # . . call
2097     e8/call  write/disp32
2098     # . . discard args
2099     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2100     # next-word-or-expression(_test-input-stream, slice)
2101     # . . push args
2102     51/push-ecx
2103     68/push  _test-input-stream/imm32
2104     # . . call
2105     e8/call  next-word-or-expression/disp32
2106     # . . discard args
2107     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2108     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2109     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2110     # . . push args
2111     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: start"/imm32
2112     68/push  0xd/imm32
2113     # . . push slice->start - _test-input-stream
2114     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx 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     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
2122     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
2123     # . . push args
2124     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: end"/imm32
2125     68/push  0x15/imm32
2126     # . . push slice->end - _test-input-stream
2127     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2128     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2129     50/push-eax
2130     # . . call
2131     e8/call  check-ints-equal/disp32
2132     # . . discard args
2133     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2134     # . epilogue
2135     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2136     5d/pop-to-ebp
2137     c3/return
2138 
2139 test-next-word-or-expression-returns-whole-expression:
2140     # . prologue
2141     55/push-ebp
2142     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2143     # setup
2144     # . clear-stream(_test-input-stream)
2145     # . . push args
2146     68/push  _test-input-stream/imm32
2147     # . . call
2148     e8/call  clear-stream/disp32
2149     # . . discard args
2150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2151     # var slice/ecx = {0, 0}
2152     68/push  0/imm32/end
2153     68/push  0/imm32/start
2154     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2155     # write(_test-input-stream, " *(a b)/imm32 ")
2156     # . . push args
2157     68/push  " *(a b)/imm32 "/imm32
2158     68/push  _test-input-stream/imm32
2159     # . . call
2160     e8/call  write/disp32
2161     # . . discard args
2162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2163     # next-word-or-expression(_test-input-stream, slice)
2164     # . . push args
2165     51/push-ecx
2166     68/push  _test-input-stream/imm32
2167     # . . call
2168     e8/call  next-word-or-expression/disp32
2169     # . . discard args
2170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2171     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2172     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2173     # . . push args
2174     68/push  "F - test-next-word-or-expression-returns-whole-expression: start"/imm32
2175     68/push  0xd/imm32
2176     # . . push slice->start - _test-input-stream
2177     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx 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     # check-ints-equal(slice->end - _test-input-stream->data, 13, msg)
2185     # . check-ints-equal(slice->end - _test-input-stream, 25, msg)
2186     # . . push args
2187     68/push  "F - test-next-word-or-expression-returns-whole-expression: end"/imm32
2188     68/push  0x19/imm32
2189     # . . push slice->end - _test-input-stream
2190     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2191     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2192     50/push-eax
2193     # . . call
2194     e8/call  check-ints-equal/disp32
2195     # . . discard args
2196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2197     # . epilogue
2198     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2199     5d/pop-to-ebp
2200     c3/return
2201 
2202 # Grammar:
2203 #   *reg                    -> 0/mod reg/rm32
2204 #   *(reg)                  -> 0/mod reg/rm32
2205 #   *(reg+disp)             -> 2/mod reg/rm32 disp/disp32
2206 #   *(reg1+reg2<<s)         -> 2/mod 4/rm32 reg1/base reg2/index s/scale 0/disp32
2207 #   *(reg1+reg2<<s+disp)    -> 2/mod 4/rm32 reg1/base reg2/index s/scale disp/disp32
2208 # Intermediate structure: base, index, scale, disp
2209 # Default values: base: 0, index: 4 (none), scale: 0, disp: 0
2210 parse-effective-address:  # word-slice : (address slice) -> base/eax, index/ecx, scale/edx, disp/ebx
2211     # pseudocode:
2212     #   var local-slice = {word-slice->start, word-slice->end}
2213     #   ++local-slice->start to skip '*'
2214     #   initialize defaults: base=0, index=4, scale=0, disp=0
2215     #   if (*local-slice->start != '(') {
2216     #     local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
2217     #     base = get-slice(Registers, local-slice, row-size=8)
2218     #     return
2219     #   }
2220     #   # compound expressions
2221     #   skip whitespace
2222     #   read register into base
2223     #   skip whitespace
2224     #   if (*local-slice->start == ')') goto end
2225     #   if (*local-slice->start == '-') goto displacement
2226     #   if (*local-slice->start != '+') goto error1
2227     #   ++local-slice->start to skip '+'
2228     #   skip whitespace
2229     #   if next 3 characters don't make a register, goto displacement
2230     #   read register into index
2231     #   skip whitespace
2232     #   if (*local-slice->start == ')') goto end
2233     #   if (*local-slice->start == '<') {
2234     #     ++local-slice->start to skip '<'
2235     #     if (*local-slice->start != '<') goto error2
2236     #     ++local-slice->start to skip '<'
2237     #     skip whitespace
2238     #     read integer into scale
2239     #     skip whitespace
2240     #     if (*local-slice->start == ')') goto end
2241     #   }
2242     #   if (*local-slice->start not in '+' '-') goto error3
2243     # displacement:
2244     #   read integer into disp
2245     #   skip whitespace
2246     #   if (*local-slice->start != ')') goto error4
2247     # . prologue
2248     55/push-ebp
2249     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2250     # . save registers
2251     56/push-esi
2252     57/push-edi
2253     # var local-slice/esi : (address slice) = {word-slice->start, word-slice->end}
2254     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2255     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2256     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2257     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
2258     # ++local-slice->start to skip '*'
2259     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2260     # initialize defaults
2261     # base is in edi; we'll move it to eax just before we return
2262     bf/copy-to-edi  0/imm32
2263     b9/copy-to-ecx  4/imm32/no-index
2264     ba/copy-to-edx  0/imm32/.scale
2265     bb/copy-to-ebx  0/imm32/disp
2266 $parse-effective-address:check-for-simple-register:
2267     # if (*local-slice->start == '(') goto compound expression
2268     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2269     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2270     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2271     3d/compare-eax-and  0x28/imm32/open-paren
2272     74/jump-if-equal  $parse-effective-address:compound-expression/disp8
2273 $parse-effective-address:simple-register:
2274     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
2275     # . . push args
2276     56/push-esi
2277     68/push  0x2f/imm32/slash
2278     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2279     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2280     # . . call
2281     e8/call  next-token-from-slice/disp32
2282     # . . discard args
2283     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2284     # base = get-slice(Registers, local-slice, row-size=8)
2285     # . eax = get-slice(Registers, local-slice, row-size=8)
2286     # . . push args
2287     68/push  "Registers"/imm32
2288     68/push  8/imm32/row-size
2289     56/push-esi
2290     68/push  Registers/imm32
2291     # . . call
2292     e8/call  get-slice/disp32
2293     # . . discard args
2294     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2295     # . base = *eax
2296     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy *eax to edi
2297     # return
2298     e9/jump  $parse-effective-address:end/disp32
2299 $parse-effective-address:compound-expression:
2300     # ++local-slice->start to skip '('
2301     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2302     # skip whitespace
2303     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2304     # . . push args
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     # . . call
2308     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2309     # . . discard args
2310     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2311     # . local-slice->start = eax
2312     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2313     # read register into base
2314     # . eax = next-register(local-slice)
2315     # . . push args
2316     56/push-esi
2317     # . . call
2318     e8/call  next-register/disp32
2319     # . . discard args
2320     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2321     # . edi = *eax
2322     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy *eax to edi
2323     # skip whitespace
2324     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2325     # . . push args
2326     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2327     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2328     # . . call
2329     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2330     # . . discard args
2331     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2332     # . local-slice->start = eax
2333     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2334     # if (*local-slice->start == ')') goto end
2335     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2336     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2337     3d/compare-eax-and  0x29/imm32/close-paren
2338     0f 84/jump-if-equal  $parse-effective-address:end/disp32
2339     # if (*local-slice->start == '-') goto displacement
2340     3d/compare-eax-and  0x2d/imm32/minus
2341     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
2342     # if (*local-slice->start != '+') goto error1
2343     3d/compare-eax-and  0x2b/imm32/plus
2344     0f 85/jump-if-not-equal  $parse-effective-address:error1/disp32
2345 $parse-effective-address:check-for-index:
2346     # ++local-slice->start to skip '+'
2347     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2348     # skip whitespace
2349     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2350     # . . push args
2351     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2352     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2353     # . . call
2354     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2355     # . . discard args
2356     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2357     # . local-slice->start = eax
2358     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2359 $parse-effective-address:resolve-ambiguity:
2360     # if next 3 characters don't make a register, goto displacement
2361     # . spill ecx
2362     51/push-ecx
2363     # . var tmp/ecx = {local-slice->start, local-slice->start+3}
2364     # . . ecx = local-slice->start
2365     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
2366     # . . eax = local-slice->start+3
2367     05/add-to-eax  3/imm32
2368     # . . push
2369     50/push-eax
2370     51/push-ecx
2371     # . . copy esp to ecx
2372     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2373     # . eax = maybe-get-slice(Register, tmp, row-size=8)
2374     # . . push args
2375     68/push  8/imm32/row-size
2376     51/push-ecx
2377     68/push  Registers/imm32
2378     # . . call
2379     e8/call  maybe-get-slice/disp32
2380     # . . discard args
2381     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2382     # . reclaim tmp
2383     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2384     # . restore ecx
2385     59/pop-to-ecx
2386     # . if (eax == 0) goto displacement
2387     3d/compare-eax-and  0/imm32
2388     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
2389 $parse-effective-address:index:
2390     # read register into index
2391     # . eax = next-register(local-slice)
2392     # . . push args
2393     56/push-esi
2394     # . . call
2395     e8/call  next-register/disp32
2396     # . . discard args
2397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2398     # . ecx = *eax
2399     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2400     # skip whitespace
2401     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2402     # . . push args
2403     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2404     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2405     # . . call
2406     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2407     # . . discard args
2408     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2409     # . local-slice->start = eax
2410     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2411     # if (*local-slice->start == ')') goto end
2412     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2413     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2414     3d/compare-eax-and  0x29/imm32/close-paren
2415     0f 84/jump-if-equal  $parse-effective-address:end/disp32
2416 $parse-effective-address:check-for-scale:
2417     # if (*local-slice->start != '<') goto next check
2418     3d/compare-eax-and  0x3c/imm32/less-than
2419     75/jump-if-not-equal  $parse-effective-address:check-for-displacement/disp8
2420     # ++local-slice->start to skip '<'
2421     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2422     # if (*local-slice->start != '<') goto error2
2423     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2424     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2425     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2426     3d/compare-eax-and  0x3c/imm32/less-than
2427     0f 85/jump-if-not-equal  $parse-effective-address:error2/disp32
2428     # ++local-slice->start to skip '<'
2429     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2430     # skip whitespace
2431     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2432     # . . push args
2433     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2434     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2435     # . . call
2436     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2437     # . . discard args
2438     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2439     # . local-slice->start = eax
2440     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2441 $parse-effective-address:scale:
2442     # read positive integer into scale
2443     # . eax = next-positive-hex-int(local-slice)
2444     # . . push args
2445     56/push-esi
2446     # . . call
2447     e8/call  next-positive-hex-int/disp32
2448     # . . discard args
2449     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2450     # . edx = eax
2451     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
2452     # skip whitespace
2453     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2454     # . . push args
2455     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2456     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2457     # . . call
2458     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2459     # . . discard args
2460     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2461     # . local-slice->start = eax
2462     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2463     # if (*local-slice->start == ')') goto end
2464     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2465     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2466     3d/compare-eax-and  0x29/imm32/close-paren
2467     74/jump-if-equal  $parse-effective-address:end/disp8
2468 $parse-effective-address:check-for-displacement:
2469     # if (*local-slice->start not in '+' '-') goto error3
2470     3d/compare-eax-and  0x2b/imm32/plus
2471     74/jump-if-equal  $parse-effective-address:displacement/disp8
2472     3d/compare-eax-and  0x2d/imm32/minus
2473     74/jump-if-equal  $parse-effective-address:displacement/disp8
2474     e9/jump  $parse-effective-address:error3/disp32
2475 $parse-effective-address:displacement:
2476     # read integer into disp
2477     # . eax = next-hex-int(local-slice)
2478     # . . push args
2479     56/push-esi
2480     # . . call
2481     e8/call  next-hex-int/disp32
2482     # . . discard args
2483     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2484     # . ebx = eax
2485     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2486     # skip whitespace
2487     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2488     # . . push args
2489     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2490     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2491     # . . call
2492     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2493     # . . discard args
2494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2495     # . local-slice->start = eax
2496     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2497     # if (*local-slice->start != ')') goto error4
2498     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2499     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2500     3d/compare-eax-and  0x29/imm32/close-paren
2501     0f 85/jump-if-not-equal  $parse-effective-address:error4/disp32
2502 $parse-effective-address:end:
2503     # return base in eax
2504     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
2505     # . reclaim locals
2506     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2507     # . restore registers
2508     5f/pop-to-edi
2509     5e/pop-to-esi
2510     # . epilogue
2511     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2512     5d/pop-to-ebp
2513     c3/return
2514 
2515 $parse-effective-address:error1:
2516     # print(stderr, "error: unexpected character: " eax "\n")
2517     # . write-buffered(Stderr, "error: unexpected character: ")
2518     # . . push args
2519     68/push  "error: unexpected character: "/imm32
2520     68/push  Stderr/imm32
2521     # . . call
2522     e8/call  write-buffered/disp32
2523     # . . discard args
2524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2525     # . print-int32-buffered(out, eax)
2526     # . . push args
2527     50/push-eax
2528     68/push  Stderr/imm32
2529     # . . call
2530     e8/call  print-int32-buffered/disp32
2531     # . . discard args
2532     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2533     # . write-buffered(Stderr, "\n")
2534     # . . push args
2535     68/push  Newline/imm32
2536     68/push  Stderr/imm32
2537     # . . call
2538     e8/call  write-buffered/disp32
2539     # . . discard args
2540     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2541     # . flush(Stderr)
2542     # . . push args
2543     68/push  Stderr/imm32
2544     # . . call
2545     e8/call  flush/disp32
2546     # . . discard args
2547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2548     # . syscall(exit, 1)
2549     bb/copy-to-ebx  1/imm32
2550     b8/copy-to-eax  1/imm32/exit
2551     cd/syscall  0x80/imm8
2552     # never gets here
2553 
2554 $parse-effective-address:error2:
2555     # print(stderr, "error: '<' can only be followed by '<' but got: " eax "\n")
2556     # . write-buffered(Stderr, "error: '<' can only be followed by '<' but got: ")
2557     # . . push args
2558     68/push  "error: '<' can only be followed by '<' but got: "/imm32
2559     68/push  Stderr/imm32
2560     # . . call
2561     e8/call  write-buffered/disp32
2562     # . . discard args
2563     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2564     # . print-int32-buffered(out, eax)
2565     # . . push args
2566     50/push-eax
2567     68/push  Stderr/imm32
2568     # . . call
2569     e8/call  print-int32-buffered/disp32
2570     # . . discard args
2571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2572     # . write-buffered(Stderr, "\n")
2573     # . . push args
2574     68/push  Newline/imm32
2575     68/push  Stderr/imm32
2576     # . . call
2577     e8/call  write-buffered/disp32
2578     # . . discard args
2579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2580     # . flush(Stderr)
2581     # . . push args
2582     68/push  Stderr/imm32
2583     # . . call
2584     e8/call  flush/disp32
2585     # . . discard args
2586     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2587     # . syscall(exit, 1)
2588     bb/copy-to-ebx  1/imm32
2589     b8/copy-to-eax  1/imm32/exit
2590     cd/syscall  0x80/imm8
2591     # never gets here
2592 
2593 $parse-effective-address:error3:
2594     # print(stderr, "error: unexpected character before displacement: " eax "\n")
2595     # . write-buffered(Stderr, "error: unexpected character before displacement: ")
2596     # . . push args
2597     68/push  "error: unexpected character before displacement: "/imm32
2598     68/push  Stderr/imm32
2599     # . . call
2600     e8/call  write-buffered/disp32
2601     # . . discard args
2602     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2603     # . print-int32-buffered(out, eax)
2604     # . . push args
2605     50/push-eax
2606     68/push  Stderr/imm32
2607     # . . call
2608     e8/call  print-int32-buffered/disp32
2609     # . . discard args
2610     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2611     # . write-buffered(Stderr, "\n")
2612     # . . push args
2613     68/push  Newline/imm32
2614     68/push  Stderr/imm32
2615     # . . call
2616     e8/call  write-buffered/disp32
2617     # . . discard args
2618     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2619     # . flush(Stderr)
2620     # . . push args
2621     68/push  Stderr/imm32
2622     # . . call
2623     e8/call  flush/disp32
2624     # . . discard args
2625     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2626     # . syscall(exit, 1)
2627     bb/copy-to-ebx  1/imm32
2628     b8/copy-to-eax  1/imm32/exit
2629     cd/syscall  0x80/imm8
2630     # never gets here
2631 
2632 $parse-effective-address:error4:
2633     # print(stderr, "error: unexpected character after displacement: " eax "; expected ')' to wrap up\n")
2634     # . write-buffered(Stderr, "error: unexpected character after displacement: ")
2635     # . . push args
2636     68/push  "error: unexpected character after displacement: "/imm32
2637     68/push  Stderr/imm32
2638     # . . call
2639     e8/call  write-buffered/disp32
2640     # . . discard args
2641     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2642     # . print-int32-buffered(out, eax)
2643     # . . push args
2644     50/push-eax
2645     68/push  Stderr/imm32
2646     # . . call
2647     e8/call  print-int32-buffered/disp32
2648     # . . discard args
2649     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2650     # . write-buffered(Stderr, "; expected ')' to wrap up\n")
2651     # . . push args
2652     68/push  "; expected ')' to wrap up\n"/imm32
2653     68/push  Stderr/imm32
2654     # . . call
2655     e8/call  write-buffered/disp32
2656     # . . discard args
2657     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2658     # . flush(Stderr)
2659     # . . push args
2660     68/push  Stderr/imm32
2661     # . . call
2662     e8/call  flush/disp32
2663     # . . discard args
2664     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2665     # . syscall(exit, 1)
2666     bb/copy-to-ebx  1/imm32
2667     b8/copy-to-eax  1/imm32/exit
2668     cd/syscall  0x80/imm8
2669     # never gets here
2670 
2671 # assumes 'in' starts with a register name, and returns pointer to its code
2672 # side-effect: modifies 'in' to scan past the initial register name
2673 next-register:  # in : (address slice) -> reg/eax : int
2674     # . prologue
2675     55/push-ebp
2676     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2677     # . save registers
2678     51/push-ecx
2679     56/push-esi
2680     # esi = in
2681     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2682     # var reg-slice/ecx : (address slice) = {in->start, in->start + 3}
2683     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2684     05/add-to-eax  3/imm32
2685     50/push-eax
2686     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2687     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2688     # in->start += 3
2689     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               3/imm32           # add to *esi
2690     # eax = get-slice(Registers, reg-slice, row-size=8)
2691     # . . push args
2692     68/push  "next-register"/imm32
2693     68/push  8/imm32/row-size
2694     51/push-ecx
2695     68/push  Registers/imm32
2696     # . . call
2697     e8/call  get-slice/disp32
2698     # . . discard args
2699     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2700 $next-register:end:
2701     # . reclaim locals
2702     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2703     # . restore registers
2704     5e/pop-to-esi
2705     59/pop-to-ecx
2706     # . epilogue
2707     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2708     5d/pop-to-ebp
2709     c3/return
2710 
2711 test-parse-effective-address-simple:
2712     # . prologue
2713     55/push-ebp
2714     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2715     # var slice/ecx = "*esi"
2716     b8/copy-to-eax  "*esi"/imm32
2717     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2718     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
2719     05/add-to-eax  4/imm32
2720     # . ecx = {eax, ecx}
2721     51/push-ecx
2722     50/push-eax
2723     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2724     # eax, ecx, edx, ebx = parse-effective-address(slice)
2725     # . . push args
2726     51/push-ecx
2727     # . . call
2728     e8/call  parse-effective-address/disp32
2729     # . . discard args
2730     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2731     # slice clobbered beyond this point
2732     # check-ints-equal(eax, 6, msg)
2733     # . . push args
2734     68/push  "F - test-parse-effective-address-simple/base"/imm32
2735     68/push  6/imm32/esi
2736     50/push-eax
2737     # . . call
2738     e8/call  check-ints-equal/disp32
2739     # . . discard args
2740     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2741     # check-ints-equal(ecx, 4, msg)
2742     # . . push args
2743     68/push  "F - test-parse-effective-address-simple/index"/imm32
2744     68/push  4/imm32/none
2745     51/push-ecx
2746     # . . call
2747     e8/call  check-ints-equal/disp32
2748     # . . discard args
2749     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2750     # check-ints-equal(edx, 0, msg)
2751     # . . push args
2752     68/push  "F - test-parse-effective-address-simple/scale"/imm32
2753     68/push  0/imm32/none
2754     52/push-edx
2755     # . . call
2756     e8/call  check-ints-equal/disp32
2757     # . . discard args
2758     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2759     # check-ints-equal(ebx, 0, msg)
2760     # . . push args
2761     68/push  "F - test-parse-effective-address-simple/displacement"/imm32
2762     68/push  0/imm32/none
2763     53/push-ebx
2764     # . . call
2765     e8/call  check-ints-equal/disp32
2766     # . . discard args
2767     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2768     # . epilogue
2769     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2770     5d/pop-to-ebp
2771     c3/return
2772 
2773 test-parse-effective-address-base:
2774     # . prologue
2775     55/push-ebp
2776     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2777     # var slice/ecx = "*(esi  )"
2778     b8/copy-to-eax  "*(esi  )"/imm32
2779     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2780     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
2781     05/add-to-eax  4/imm32
2782     # . ecx = {eax, ecx}
2783     51/push-ecx
2784     50/push-eax
2785     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2786     # eax, ecx, edx, ebx = parse-effective-address(slice)
2787     # . . push args
2788     51/push-ecx
2789     # . . call
2790     e8/call  parse-effective-address/disp32
2791     # . . discard args
2792     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2793     # slice clobbered beyond this point
2794     # check-ints-equal(eax, 6, msg)
2795     # . . push args
2796     68/push  "F - test-parse-effective-address-base/base"/imm32
2797     68/push  6/imm32/esi
2798     50/push-eax
2799     # . . call
2800     e8/call  check-ints-equal/disp32
2801     # . . discard args
2802     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2803     # check-ints-equal(ecx, 4, msg)
2804     # . . push args
2805     68/push  "F - test-parse-effective-address-base/index"/imm32
2806     68/push  4/imm32/none
2807     51/push-ecx
2808     # . . call
2809     e8/call  check-ints-equal/disp32
2810     # . . discard args
2811     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2812     # check-ints-equal(edx, 0, msg)
2813     # . . push args
2814     68/push  "F - test-parse-effective-address-base/scale"/imm32
2815     68/push  0/imm32/none
2816     52/push-edx
2817     # . . call
2818     e8/call  check-ints-equal/disp32
2819     # . . discard args
2820     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2821     # check-ints-equal(ebx, 0, msg)
2822     # . . push args
2823     68/push  "F - test-parse-effective-address-base/displacement"/imm32
2824     68/push  0/imm32/none
2825     53/push-ebx
2826     # . . call
2827     e8/call  check-ints-equal/disp32
2828     # . . discard args
2829     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2830     # . epilogue
2831     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2832     5d/pop-to-ebp
2833     c3/return
2834 
2835 test-parse-effective-address-base-displacement:
2836     # . prologue
2837     55/push-ebp
2838     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2839     # var slice/ecx = "*(esi+3)"
2840     b8/copy-to-eax  "*(esi+3)"/imm32
2841     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2842     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
2843     05/add-to-eax  4/imm32
2844     # . ecx = {eax, ecx}
2845     51/push-ecx
2846     50/push-eax
2847     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2848     # eax, ecx, edx, ebx = parse-effective-address(slice)
2849     # . . push args
2850     51/push-ecx
2851     # . . call
2852     e8/call  parse-effective-address/disp32
2853     # . . discard args
2854     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2855     # slice clobbered beyond this point
2856     # check-ints-equal(eax, 6, msg)
2857     # . . push args
2858     68/push  "F - test-parse-effective-address-base-displacement/base"/imm32
2859     68/push  6/imm32/esi
2860     50/push-eax
2861     # . . call
2862     e8/call  check-ints-equal/disp32
2863     # . . discard args
2864     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2865     # check-ints-equal(ecx, 4, msg)
2866     # . . push args
2867     68/push  "F - test-parse-effective-address-base-displacement/index"/imm32
2868     68/push  4/imm32/none
2869     51/push-ecx
2870     # . . call
2871     e8/call  check-ints-equal/disp32
2872     # . . discard args
2873     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2874     # check-ints-equal(edx, 0, msg)
2875     # . . push args
2876     68/push  "F - test-parse-effective-address-base-displacement/scale"/imm32
2877     68/push  0/imm32/none
2878     52/push-edx
2879     # . . call
2880     e8/call  check-ints-equal/disp32
2881     # . . discard args
2882     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2883     # check-ints-equal(ebx, 3, msg)
2884     # . . push args
2885     68/push  "F - test-parse-effective-address-base-displacement/displacement"/imm32
2886     68/push  3/imm32
2887     53/push-ebx
2888     # . . call
2889     e8/call  check-ints-equal/disp32
2890     # . . discard args
2891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2892     # . epilogue
2893     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2894     5d/pop-to-ebp
2895     c3/return
2896 
2897 test-parse-effective-address-base-negative-displacement:
2898     # . prologue
2899     55/push-ebp
2900     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2901     # var slice/ecx = "*(esi-3)"
2902     b8/copy-to-eax  "*(esi-3)"/imm32
2903     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2904     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
2905     05/add-to-eax  4/imm32
2906     # . ecx = {eax, ecx}
2907     51/push-ecx
2908     50/push-eax
2909     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2910     # eax, ecx, edx, ebx = parse-effective-address(slice)
2911     # . . push args
2912     51/push-ecx
2913     # . . call
2914     e8/call  parse-effective-address/disp32
2915     # . . discard args
2916     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2917     # slice clobbered beyond this point
2918     # check-ints-equal(eax, 6, msg)
2919     # . . push args
2920     68/push  "F - test-parse-effective-address-base-negative-displacement/base"/imm32
2921     68/push  6/imm32/esi
2922     50/push-eax
2923     # . . call
2924     e8/call  check-ints-equal/disp32
2925     # . . discard args
2926     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2927     # check-ints-equal(ecx, 4, msg)
2928     # . . push args
2929     68/push  "F - test-parse-effective-address-base-negative-displacement/index"/imm32
2930     68/push  4/imm32/none
2931     51/push-ecx
2932     # . . call
2933     e8/call  check-ints-equal/disp32
2934     # . . discard args
2935     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2936     # check-ints-equal(edx, 0, msg)
2937     # . . push args
2938     68/push  "F - test-parse-effective-address-base-negative-displacement/scale"/imm32
2939     68/push  0/imm32/none
2940     52/push-edx
2941     # . . call
2942     e8/call  check-ints-equal/disp32
2943     # . . discard args
2944     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2945     # check-ints-equal(ebx, -3, msg)
2946     # . . push args
2947     68/push  "F - test-parse-effective-address-base-negative-displacement/displacement"/imm32
2948     68/push  -3/imm32
2949     53/push-ebx
2950     # . . call
2951     e8/call  check-ints-equal/disp32
2952     # . . discard args
2953     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2954     # . epilogue
2955     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2956     5d/pop-to-ebp
2957     c3/return
2958 
2959 test-parse-effective-address-base-index:
2960     # . prologue
2961     55/push-ebp
2962     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2963     # var slice/ecx = "*(esi+ecx)"
2964     b8/copy-to-eax  "*(esi+ecx)"/imm32
2965     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2966     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
2967     05/add-to-eax  4/imm32
2968     # . ecx = {eax, ecx}
2969     51/push-ecx
2970     50/push-eax
2971     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2972     # eax, ecx, edx, ebx = parse-effective-address(slice)
2973     # . . push args
2974     51/push-ecx
2975     # . . call
2976     e8/call  parse-effective-address/disp32
2977     # . . discard args
2978     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2979     # slice clobbered beyond this point
2980     # check-ints-equal(eax, 6, msg)
2981     # . . push args
2982     68/push  "F - test-parse-effective-address-base-index/base"/imm32
2983     68/push  6/imm32/esi
2984     50/push-eax
2985     # . . call
2986     e8/call  check-ints-equal/disp32
2987     # . . discard args
2988     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2989     # check-ints-equal(ecx, 1, msg)
2990     # . . push args
2991     68/push  "F - test-parse-effective-address-base-index/index"/imm32
2992     68/push  1/imm32/none
2993     51/push-ecx
2994     # . . call
2995     e8/call  check-ints-equal/disp32
2996     # . . discard args
2997     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2998     # check-ints-equal(edx, 0, msg)
2999     # . . push args
3000     68/push  "F - test-parse-effective-address-base-index/scale"/imm32
3001     68/push  0/imm32/none
3002     52/push-edx
3003     # . . call
3004     e8/call  check-ints-equal/disp32
3005     # . . discard args
3006     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3007     # check-ints-equal(ebx, 0, msg)
3008     # . . push args
3009     68/push  "F - test-parse-effective-address-base-index/displacement"/imm32
3010     68/push  0/imm32
3011     53/push-ebx
3012     # . . call
3013     e8/call  check-ints-equal/disp32
3014     # . . discard args
3015     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3016     # . epilogue
3017     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3018     5d/pop-to-ebp
3019     c3/return
3020 
3021 test-parse-effective-address-base-index-scale:
3022     # . prologue
3023     55/push-ebp
3024     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3025     # var slice/ecx = "*(esi+ecx<<2)"
3026     b8/copy-to-eax  "*(esi+ecx<<2)"/imm32
3027     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3028     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
3029     05/add-to-eax  4/imm32
3030     # . ecx = {eax, ecx}
3031     51/push-ecx
3032     50/push-eax
3033     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3034     # eax, ecx, edx, ebx = parse-effective-address(slice)
3035     # . . push args
3036     51/push-ecx
3037     # . . call
3038     e8/call  parse-effective-address/disp32
3039     # . . discard args
3040     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3041     # slice clobbered beyond this point
3042     # check-ints-equal(eax, 6, msg)
3043     # . . push args
3044     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
3045     68/push  6/imm32/esi
3046     50/push-eax
3047     # . . call
3048     e8/call  check-ints-equal/disp32
3049     # . . discard args
3050     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3051     # check-ints-equal(ecx, 1, msg)
3052     # . . push args
3053     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
3054     68/push  1/imm32/none
3055     51/push-ecx
3056     # . . call
3057     e8/call  check-ints-equal/disp32
3058     # . . discard args
3059     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3060     # check-ints-equal(edx, 2, msg)
3061     # . . push args
3062     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
3063     68/push  2/imm32
3064     52/push-edx
3065     # . . call
3066     e8/call  check-ints-equal/disp32
3067     # . . discard args
3068     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3069     # check-ints-equal(ebx, 0, msg)
3070     # . . push args
3071     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
3072     68/push  0/imm32
3073     53/push-ebx
3074     # . . call
3075     e8/call  check-ints-equal/disp32
3076     # . . discard args
3077     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3078     # . epilogue
3079     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3080     5d/pop-to-ebp
3081     c3/return
3082 
3083 test-parse-effective-address-base-index-scale-displacement:
3084     # . prologue
3085     55/push-ebp
3086     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3087     # var slice/ecx = "*(esi + ecx<<2 - 0x34)"
3088     b8/copy-to-eax  "*(esi + ecx<<2 - 0x34)"/imm32
3089     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3090     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
3091     05/add-to-eax  4/imm32
3092     # . ecx = {eax, ecx}
3093     51/push-ecx
3094     50/push-eax
3095     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3096     # eax, ecx, edx, ebx = parse-effective-address(slice)
3097     # . . push args
3098     51/push-ecx
3099     # . . call
3100     e8/call  parse-effective-address/disp32
3101     # . . discard args
3102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3103     # slice clobbered beyond this point
3104     # check-ints-equal(eax, 6, msg)
3105     # . . push args
3106     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
3107     68/push  6/imm32/esi
3108     50/push-eax
3109     # . . call
3110     e8/call  check-ints-equal/disp32
3111     # . . discard args
3112     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3113     # check-ints-equal(ecx, 1, msg)
3114     # . . push args
3115     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
3116     68/push  1/imm32/none
3117     51/push-ecx
3118     # . . call
3119     e8/call  check-ints-equal/disp32
3120     # . . discard args
3121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3122     # check-ints-equal(edx, 2, msg)
3123     # . . push args
3124     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
3125     68/push  2/imm32
3126     52/push-edx
3127     # . . call
3128     e8/call  check-ints-equal/disp32
3129     # . . discard args
3130     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3131     # check-ints-equal(ebx, -0x34, msg)
3132     # . . push args
3133     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
3134     68/push  -0x34/imm32
3135     53/push-ebx
3136     # . . call
3137     e8/call  check-ints-equal/disp32
3138     # . . discard args
3139     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3140     # . epilogue
3141     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3142     5d/pop-to-ebp
3143     c3/return
3144 
3145 # Code generation:
3146 #   if base is esp, then goto emit-sib
3147 #   if base is ebp, then goto emit-sib
3148 #   if index is none and disp is 0, then mod = 0 and rm32 = base
3149 #   if index is none, then mod = 2 and rm32 = base and disp32 = disp
3150 # emit-sib:
3151 #   if index is not none, then mod = 2 and rm32 = 4 and base = base and index = index and disp32 = disp
3152 emit-indirect-mode:  # out : (address buffered-file), base : int, index : int, scale : int, disp : int
3153     # . prologue
3154     55/push-ebp
3155     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3156 $emit-indirect-mode:check-for-ebp:
3157     # if (base == 5) goto emit-sib
3158     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       5/imm32           # compare *(ebp+12)
3159     74/jump-if-equal  $emit-indirect-mode:emit-sib/disp8
3160 $emit-indirect-mode:check-for-esp:
3161     # if (base == 4) goto emit-sib
3162     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       4/imm32           # compare *(ebp+12)
3163     74/jump-if-equal  $emit-indirect-mode:emit-sib/disp8
3164 $emit-indirect-mode:check-for-sib:
3165     # if (index == 4/none) goto next check
3166     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      4/imm32           # compare *(ebp+16)
3167     0f 84/jump-if-equal  $emit-indirect-mode:check-for-disp/disp32
3168 $emit-indirect-mode:emit-sib:
3169     # emit(out, "2/mod/indirect 4/rm32/sib " base "/base " index "/index " scale "/scale " disp "/disp32")
3170     # . write-buffered(out, "2/mod/*+disp32 4/rm32/sib ")
3171     # . . push args
3172     68/push  "2/mod/*+disp32 4/rm32/sib "/imm32
3173     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3174     # . . call
3175     e8/call  write-buffered/disp32
3176     # . . discard args
3177     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3178     # . print-int32-buffered(out, base)
3179     # . . push args
3180     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3181     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3182     # . . call
3183     e8/call  print-int32-buffered/disp32
3184     # . . discard args
3185     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3186     # . write-buffered(out, "/base ")
3187     # . . push args
3188     68/push  "/base "/imm32
3189     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3190     # . . call
3191     e8/call  write-buffered/disp32
3192     # . . discard args
3193     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3194     # . print-int32-buffered(out, index)
3195     # . . push args
3196     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3197     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3198     # . . call
3199     e8/call  print-int32-buffered/disp32
3200     # . . discard args
3201     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3202     # . write-buffered(out, "/index ")
3203     # . . push args
3204     68/push  "/index "/imm32
3205     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3206     # . . call
3207     e8/call  write-buffered/disp32
3208     # . . discard args
3209     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3210     # . print-int32-buffered(out, scale)
3211     # . . push args
3212     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
3213     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3214     # . . call
3215     e8/call  print-int32-buffered/disp32
3216     # . . discard args
3217     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3218     # . write-buffered(out, "/scale ")
3219     # . . push args
3220     68/push  "/scale "/imm32
3221     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3222     # . . call
3223     e8/call  write-buffered/disp32
3224     # . . discard args
3225     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3226     # . print-int32-buffered(out, disp)
3227     # . . push args
3228     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3229     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3230     # . . call
3231     e8/call  print-int32-buffered/disp32
3232     # . . discard args
3233     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3234     # . write-buffered(out, "/disp32")
3235     # . . push args
3236     68/push  "/disp32"/imm32
3237     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3238     # . . call
3239     e8/call  write-buffered/disp32
3240     # . . discard args
3241     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3242     e9/jump  $emit-indirect-mode:end/disp32
3243 $emit-indirect-mode:check-for-disp:
3244     # if (disp == 0) goto next check
3245     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      0/imm32           # compare *(ebp+24)
3246     74/jump-if-equal  $emit-indirect-mode:emit-indirect/disp8
3247 $emit-indirect-mode:emit-disp:
3248     # emit(out, "2/mod/*+disp32 " base "/rm32 " disp "/disp32")
3249     # . write-buffered(out, "2/mod/*+disp32 ")
3250     # . . push args
3251     68/push  "2/mod/*+disp32 "/imm32
3252     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3253     # . . call
3254     e8/call  write-buffered/disp32
3255     # . . discard args
3256     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3257     # . print-int32-buffered(out, base)
3258     # . . push args
3259     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3260     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3261     # . . call
3262     e8/call  print-int32-buffered/disp32
3263     # . . discard args
3264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3265     # . write-buffered(out, "/rm32 ")
3266     # . . push args
3267     68/push  "/rm32 "/imm32
3268     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3269     # . . call
3270     e8/call  write-buffered/disp32
3271     # . . discard args
3272     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3273     # . print-int32-buffered(out, disp)
3274     # . . push args
3275     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3276     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3277     # . . call
3278     e8/call  print-int32-buffered/disp32
3279     # . . discard args
3280     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3281     # . write-buffered(out, "/disp32")
3282     # . . push args
3283     68/push  "/disp32"/imm32
3284     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3285     # . . call
3286     e8/call  write-buffered/disp32
3287     # . . discard args
3288     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3289     eb/jump  $emit-indirect-mode:end/disp8
3290 $emit-indirect-mode:emit-indirect:
3291     # emit(out, "0/mod/indirect " base "/rm32")
3292     # . write-buffered(out, "0/mod/indirect ")
3293     # . . push args
3294     68/push  "0/mod/indirect "/imm32
3295     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3296     # . . call
3297     e8/call  write-buffered/disp32
3298     # . . discard args
3299     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3300     # . print-int32-buffered(out, base)
3301     # . . push args
3302     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3303     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3304     # . . call
3305     e8/call  print-int32-buffered/disp32
3306     # . . discard args
3307     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3308     # . write-buffered(out, "/rm32")
3309     # . . push args
3310     68/push  "/rm32"/imm32
3311     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3312     # . . call
3313     e8/call  write-buffered/disp32
3314     # . . discard args
3315     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3316 $emit-indirect-mode:end:
3317     # . epilogue
3318     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3319     5d/pop-to-ebp
3320     c3/return
3321 
3322 test-emit-indirect-mode:
3323     # . prologue
3324     55/push-ebp
3325     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3326     # setup
3327     # . clear-stream(_test-output-stream)
3328     # . . push args
3329     68/push  _test-output-stream/imm32
3330     # . . call
3331     e8/call  clear-stream/disp32
3332     # . . discard args
3333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3334     # . clear-stream(_test-output-buffered-file->buffer)
3335     # . . push args
3336     68/push  _test-output-buffered-file->buffer/imm32
3337     # . . call
3338     e8/call  clear-stream/disp32
3339     # . . discard args
3340     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3341     # emit-indirect-mode(_test-output-buffered-file, 0, 4/none, 0, 0)
3342     # . . write args
3343     68/push  0/imm32/.disp
3344     68/push  0/imm32/.scale
3345     68/push  4/imm32/.index/none
3346     68/push  0/imm32/.base
3347     68/push  _test-output-buffered-file/imm32
3348     # . . call
3349     e8/call  emit-indirect-mode/disp32
3350     # . . discard args
3351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3352     # . flush(_test-output-buffered-file)
3353     # . . push args
3354     68/push  _test-output-buffered-file/imm32
3355     # . . call
3356     e8/call  flush/disp32
3357     # . . discard args
3358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3359 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3385     # check-stream-equal(_test-output-stream, "0/mod/indirect 0/rm32", msg)
3386     # . . push args
3387     68/push  "F - test-emit-indirect-mode"/imm32
3388     68/push  "0/mod/indirect 0x00000000/rm32"/imm32
3389     68/push  _test-output-stream/imm32
3390     # . . call
3391     e8/call  check-stream-equal/disp32
3392     # . . discard args
3393     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3394     # . epilogue
3395     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3396     5d/pop-to-ebp
3397     c3/return
3398 
3399 test-emit-indirect-mode-2:
3400     # . prologue
3401     55/push-ebp
3402     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3403     # setup
3404     # . clear-stream(_test-output-stream)
3405     # . . push args
3406     68/push  _test-output-stream/imm32
3407     # . . call
3408     e8/call  clear-stream/disp32
3409     # . . discard args
3410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3411     # . clear-stream(_test-output-buffered-file->buffer)
3412     # . . push args
3413     68/push  _test-output-buffered-file->buffer/imm32
3414     # . . call
3415     e8/call  clear-stream/disp32
3416     # . . discard args
3417     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3418     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 0)
3419     # . . write args
3420     68/push  0/imm32/.disp
3421     68/push  0/imm32/.scale
3422     68/push  4/imm32/.index/none
3423     68/push  7/imm32/.base
3424     68/push  _test-output-buffered-file/imm32
3425     # . . call
3426     e8/call  emit-indirect-mode/disp32
3427     # . . discard args
3428     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3429     # . flush(_test-output-buffered-file)
3430     # . . push args
3431     68/push  _test-output-buffered-file/imm32
3432     # . . call
3433     e8/call  flush/disp32
3434     # . . discard args
3435     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3436 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3462     # check-stream-equal(_test-output-stream, "0/mod/indirect 7/rm32", msg)
3463     # . . push args
3464     68/push  "F - test-emit-indirect-mode-2"/imm32
3465     68/push  "0/mod/indirect 0x00000007/rm32"/imm32
3466     68/push  _test-output-stream/imm32
3467     # . . call
3468     e8/call  check-stream-equal/disp32
3469     # . . discard args
3470     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3471     # . epilogue
3472     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3473     5d/pop-to-ebp
3474     c3/return
3475 
3476 test-emit-indirect-mode-with-disp:
3477     # . prologue
3478     55/push-ebp
3479     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3480     # setup
3481     # . clear-stream(_test-output-stream)
3482     # . . push args
3483     68/push  _test-output-stream/imm32
3484     # . . call
3485     e8/call  clear-stream/disp32
3486     # . . discard args
3487     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3488     # . clear-stream(_test-output-buffered-file->buffer)
3489     # . . push args
3490     68/push  _test-output-buffered-file->buffer/imm32
3491     # . . call
3492     e8/call  clear-stream/disp32
3493     # . . discard args
3494     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3495     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 4)
3496     # . . write args
3497     68/push  4/imm32/.disp
3498     68/push  0/imm32/.scale
3499     68/push  4/imm32/.index/none
3500     68/push  6/imm32/.base
3501     68/push  _test-output-buffered-file/imm32
3502     # . . call
3503     e8/call  emit-indirect-mode/disp32
3504     # . . discard args
3505     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3506     # . flush(_test-output-buffered-file)
3507     # . . push args
3508     68/push  _test-output-buffered-file/imm32
3509     # . . call
3510     e8/call  flush/disp32
3511     # . . discard args
3512     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3513 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3539     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 4/disp32", msg)
3540     # . . push args
3541     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
3542     68/push  "2/mod/*+disp32 0x00000006/rm32 0x00000004/disp32"/imm32
3543     68/push  _test-output-stream/imm32
3544     # . . call
3545     e8/call  check-stream-equal/disp32
3546     # . . discard args
3547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3548     # . epilogue
3549     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3550     5d/pop-to-ebp
3551     c3/return
3552 
3553 test-emit-indirect-mode-with-disp-negative:
3554     # . prologue
3555     55/push-ebp
3556     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3557     # setup
3558     # . clear-stream(_test-output-stream)
3559     # . . push args
3560     68/push  _test-output-stream/imm32
3561     # . . call
3562     e8/call  clear-stream/disp32
3563     # . . discard args
3564     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3565     # . clear-stream(_test-output-buffered-file->buffer)
3566     # . . push args
3567     68/push  _test-output-buffered-file->buffer/imm32
3568     # . . call
3569     e8/call  clear-stream/disp32
3570     # . . discard args
3571     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3572     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, -4)
3573     # . . write args
3574     68/push  -4/imm32/.disp
3575     68/push  0/imm32/.scale
3576     68/push  4/imm32/.index/none
3577     68/push  6/imm32/.base
3578     68/push  _test-output-buffered-file/imm32
3579     # . . call
3580     e8/call  emit-indirect-mode/disp32
3581     # . . discard args
3582     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3583     # . flush(_test-output-buffered-file)
3584     # . . push args
3585     68/push  _test-output-buffered-file/imm32
3586     # . . call
3587     e8/call  flush/disp32
3588     # . . discard args
3589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3590 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3616     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 -4/disp32", msg)
3617     # . . push args
3618     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
3619     68/push  "2/mod/*+disp32 0x00000006/rm32 0xfffffffc/disp32"/imm32
3620     68/push  _test-output-stream/imm32
3621     # . . call
3622     e8/call  check-stream-equal/disp32
3623     # . . discard args
3624     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3625     # . epilogue
3626     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3627     5d/pop-to-ebp
3628     c3/return
3629 
3630 test-emit-indirect-mode-with-sib:
3631     # . prologue
3632     55/push-ebp
3633     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3634     # setup
3635     # . clear-stream(_test-output-stream)
3636     # . . push args
3637     68/push  _test-output-stream/imm32
3638     # . . call
3639     e8/call  clear-stream/disp32
3640     # . . discard args
3641     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3642     # . clear-stream(_test-output-buffered-file->buffer)
3643     # . . push args
3644     68/push  _test-output-buffered-file->buffer/imm32
3645     # . . call
3646     e8/call  clear-stream/disp32
3647     # . . discard args
3648     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3649     # emit-indirect-mode(_test-output-buffered-file, 6/base, 1/index, 2/scale, 4/disp)
3650     # . . write args
3651     68/push  4/imm32/.disp
3652     68/push  2/imm32/.scale
3653     68/push  1/imm32/.index
3654     68/push  6/imm32/.base
3655     68/push  _test-output-buffered-file/imm32
3656     # . . call
3657     e8/call  emit-indirect-mode/disp32
3658     # . . discard args
3659     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3660     # . flush(_test-output-buffered-file)
3661     # . . push args
3662     68/push  _test-output-buffered-file/imm32
3663     # . . call
3664     e8/call  flush/disp32
3665     # . . discard args
3666     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3667 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3693     # check-stream-equal(_test-output-stream, "2/mod/indirect 4/rm32/sib 6/base 1/index 2/scale 4/disp", msg)
3694     # . . push args
3695     68/push  "F - test-emit-indirect-mode-with-sib"/imm32
3696     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000006/base 0x00000001/index 0x00000002/scale 0x00000004/disp32"/imm32
3697     68/push  _test-output-stream/imm32
3698     # . . call
3699     e8/call  check-stream-equal/disp32
3700     # . . discard args
3701     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3702     # . epilogue
3703     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3704     5d/pop-to-ebp
3705     c3/return
3706 
3707 test-emit-indirect-mode-ebp:
3708     # . prologue
3709     55/push-ebp
3710     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3711     # setup
3712     # . clear-stream(_test-output-stream)
3713     # . . push args
3714     68/push  _test-output-stream/imm32
3715     # . . call
3716     e8/call  clear-stream/disp32
3717     # . . discard args
3718     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3719     # . clear-stream(_test-output-buffered-file->buffer)
3720     # . . push args
3721     68/push  _test-output-buffered-file->buffer/imm32
3722     # . . call
3723     e8/call  clear-stream/disp32
3724     # . . discard args
3725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3726     # emit-indirect-mode(_test-output-buffered-file, 5/base, 0/index, 0/scale, 0/disp)
3727     # . . write args
3728     68/push  0/imm32/.disp
3729     68/push  0/imm32/.scale
3730     68/push  0/imm32/.index
3731     68/push  5/imm32/.base/ebp
3732     68/push  _test-output-buffered-file/imm32
3733     # . . call
3734     e8/call  emit-indirect-mode/disp32
3735     # . . discard args
3736     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3737     # . flush(_test-output-buffered-file)
3738     # . . push args
3739     68/push  _test-output-buffered-file/imm32
3740     # . . call
3741     e8/call  flush/disp32
3742     # . . discard args
3743     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3744 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3770     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 4/rm32/sib 5/base/ebp 0/index 0/scale 0/disp32", msg)
3771     # . . push args
3772     68/push  "F - test-emit-indirect-mode-ebp"/imm32
3773     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000005/base 0x00000000/index 0x00000000/scale 0x00000000/disp32"/imm32
3774     68/push  _test-output-stream/imm32
3775     # . . call
3776     e8/call  check-stream-equal/disp32
3777     # . . discard args
3778     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3779     # . epilogue
3780     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3781     5d/pop-to-ebp
3782     c3/return
3783 
3784 test-emit-indirect-mode-esp:
3785     # . prologue
3786     55/push-ebp
3787     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3788     # setup
3789     # . clear-stream(_test-output-stream)
3790     # . . push args
3791     68/push  _test-output-stream/imm32
3792     # . . call
3793     e8/call  clear-stream/disp32
3794     # . . discard args
3795     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3796     # . clear-stream(_test-output-buffered-file->buffer)
3797     # . . push args
3798     68/push  _test-output-buffered-file->buffer/imm32
3799     # . . call
3800     e8/call  clear-stream/disp32
3801     # . . discard args
3802     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3803     # emit-indirect-mode(_test-output-buffered-file, 4/base, 0/index, 0/scale, 0/disp)
3804     # . . write args
3805     68/push  0/imm32/.disp
3806     68/push  0/imm32/.scale
3807     68/push  0/imm32/.index
3808     68/push  4/imm32/.base/esp
3809     68/push  _test-output-buffered-file/imm32
3810     # . . call
3811     e8/call  emit-indirect-mode/disp32
3812     # . . discard args
3813     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3814     # . flush(_test-output-buffered-file)
3815     # . . push args
3816     68/push  _test-output-buffered-file/imm32
3817     # . . call
3818     e8/call  flush/disp32
3819     # . . discard args
3820     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3821 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
3847     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 4/rm32/sib 4/base/ebp 0/index 0/scale 0/disp32", msg)
3848     # . . push args
3849     68/push  "F - test-emit-indirect-mode-esp"/imm32
3850     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000004/base 0x00000000/index 0x00000000/scale 0x00000000/disp32"/imm32
3851     68/push  _test-output-stream/imm32
3852     # . . call
3853     e8/call  check-stream-equal/disp32
3854     # . . discard args
3855     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3856     # . epilogue
3857     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3858     5d/pop-to-ebp
3859     c3/return
3860 
3861 disp32-mode?:  # in : (address slice) -> reg/eax : boolean
3862     # . prologue
3863     55/push-ebp
3864     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3865     # . save registers
3866     56/push-esi
3867     57/push-edi
3868     # var local-slice/esi : (address slice) = {in->start, in->end}
3869     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
3870     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3871     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3872     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
3873     # ++local-slice->start to skip '*'
3874     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
3875     # if (*local-slice->start == '(') return false
3876     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
3877     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
3878     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
3879     3d/compare-eax-and  0x28/imm32/open-paren
3880     74/jump-if-equal  $disp32-mode?:false/disp8
3881 $disp32-mode?:check-for-register:
3882     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
3883     # . . push args
3884     56/push-esi
3885     68/push  0x2f/imm32/slash
3886     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3887     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3888     # . . call
3889     e8/call  next-token-from-slice/disp32
3890     # . . discard args
3891     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
3892     # reg-num/eax = maybe-get-slice(Registers, local-slice, row-size=8)
3893     # . . push args
3894     68/push  8/imm32/row-size
3895     56/push-esi
3896     68/push  Registers/imm32
3897     # . . cal
3898     e8/call  maybe-get-slice/disp32
3899     # . . discard args
3900     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3901     # if (eax != 0) return false
3902     3d/compare-eax-and  0/imm32
3903     75/jump-if-not-equal  $disp32-mode?:false/disp8
3904     # return true
3905     b8/copy-to-eax  1/imm32/true
3906     eb/jump  $disp32-mode?:end/disp8
3907 $disp32-mode?:false:
3908     b8/copy-to-eax  0/imm32/false
3909 $disp32-mode?:end:
3910     # . reclaim locals
3911     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3912     # . restore registers
3913     5f/pop-to-edi
3914     5e/pop-to-esi
3915     # . epilogue
3916     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3917     5d/pop-to-ebp
3918     c3/return
3919 
3920 emit-indirect-disp32:  # out : (address buffered-file), word-slice : (address slice)
3921     # . prologue
3922     55/push-ebp
3923     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3924     # . save registers
3925     56/push-esi
3926     # var local-slice/esi : (address slice) = {in->start, in->end}
3927     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
3928     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3929     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3930     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
3931     # ++local-slice->start to skip '*'
3932     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
3933     # write-buffered(out, "0/mod/indirect 5/rm32/.disp32 ")
3934     # . . push args
3935     68/push  "0/mod/indirect 5/rm32/.disp32 "/imm32
3936     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3937     # . . call
3938     e8/call  write-buffered/disp32
3939     # . . discard args
3940     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3941     # write-slice-buffered(out, local-slice)
3942     # . . push args
3943     56/push-esi
3944     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3945     # . . call
3946     e8/call  write-slice-buffered/disp32
3947     # . . discard args
3948     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3949     # write-buffered(out, "/disp32")
3950     # . . push args
3951     68/push  "/disp32"/imm32
3952     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3953     # . . call
3954     e8/call  write-buffered/disp32
3955     # . . discard args
3956     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3957 $emit-indirect-disp32:end:
3958     # . reclaim locals
3959     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3960     # . restore registers
3961     5e/pop-to-esi
3962     # . epilogue
3963     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3964     5d/pop-to-ebp
3965     c3/return
3966 
3967 # assumes 'in' starts with optional '+' or '-', optional whitespace, and an unsigned integer
3968 # returns the value of the integer
3969 # side-effect: modifies 'in' to skip past the integer
3970 next-hex-int:  # in : (address slice) -> result/eax
3971     # . prologue
3972     55/push-ebp
3973     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3974     # . save registers
3975     51/push-ecx
3976     52/push-edx
3977     53/push-ebx
3978     56/push-esi
3979     57/push-edi
3980     # result/edi = 0
3981     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
3982     # esi = in
3983     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
3984     # edx = in->end
3985     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
3986     # curr/ecx = in->start
3987     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
3988     # negate?/ebx = false
3989     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
3990     # eax = *curr
3991     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
3992     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
3993 $next-hex-int:positive:
3994     # if (*curr == '+') ++curr
3995     3d/compare-eax-and  0x2b/imm32/+
3996     75/jump-if-not-equal  $next-hex-int:negative/disp8
3997     # . ++curr
3998     41/increment-ecx
3999     eb/jump  $next-hex-int:skip-whitespace/disp8
4000 $next-hex-int:negative:
4001     # else if (*curr == '-') ++curr, negate = true
4002     3d/compare-eax-and  0x2d/imm32/-
4003     75/jump-if-not-equal  $next-hex-int:skip-whitespace/disp8
4004 $next-hex-int:need-to-negate:
4005     # . ++curr
4006     41/increment-ecx
4007     # . negate = true
4008     bb/copy-to-ebx  1/imm32/true
4009     # fall through
4010 $next-hex-int:skip-whitespace:
4011     # spill eax
4012     50/push-eax
4013     # eax = skip-chars-matching-whitespace-in-slice(word-slice->start, word-slice->end)
4014     # . . push args
4015     52/push-edx
4016     51/push-ecx
4017     # . . call
4018     e8/call  skip-chars-matching-whitespace-in-slice/disp32
4019     # . . discard args
4020     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4021     # ecx = eax
4022     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
4023     # restore eax
4024     58/pop-to-eax
4025 $next-hex-int:initial-0:
4026     # skip past leading '0x'
4027     # . if (*curr != '0') jump to loop
4028     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4029     3d/compare-eax-and  0x30/imm32/0
4030     75/jump-if-not-equal  $next-hex-int:loop/disp8
4031     # . ++curr
4032     41/increment-ecx
4033 $next-hex-int:initial-0x:
4034     # . if (curr >= in->end) return result
4035     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4036     73/jump-if-greater-or-equal-unsigned  $next-hex-int:end/disp8
4037     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
4038     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4039     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4040     3d/compare-eax-and  0x78/imm32/x
4041     75/jump-if-not-equal  $next-hex-int:loop/disp8
4042     # . ++curr
4043     41/increment-ecx
4044 $next-hex-int:loop:
4045     # if (curr >= in->end) break
4046     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4047     73/jump-if-greater-or-equal-unsigned  $next-hex-int:break/disp8
4048     # if (!is-hex-digit?(*curr)) break
4049     # . eax = *curr
4050     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4051     # . eax = is-hex-digit?(*curr)
4052     # . . push args
4053     50/push-eax
4054     # . . call
4055     e8/call  is-hex-digit?/disp32
4056     # . . discard args
4057     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4058     # . if (eax == 0) break
4059     3d/compare-eax-and  0/imm32
4060     74/jump-if-equal  $next-hex-int:break/disp8
4061     # eax = from-hex-char(*curr)
4062     # . . copy arg to eax
4063     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4064     # . . call
4065     e8/call  from-hex-char/disp32
4066     # result = result * 16 + eax
4067     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi left by 4 bits
4068     01/add                          3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to edi
4069     # ++curr
4070     41/increment-ecx
4071     # loop
4072     eb/jump  $next-hex-int:loop/disp8
4073 $next-hex-int:break:
4074     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32           # compare ebx
4075     74/jump-if-equal  $next-hex-int:end/disp8
4076 $next-hex-int:negate:
4077     f7          3/subop/negate      3/mod/direct    7/rm32/edi    .           .             .           .           .               .                 # negate edi
4078 $next-hex-int:end:
4079     # word-slice->start = curr
4080     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy ecx to *esi
4081     # return edi
4082     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
4083     # . restore registers
4084     5f/pop-to-edi
4085     5e/pop-to-esi
4086     5b/pop-to-ebx
4087     5a/pop-to-edx
4088     59/pop-to-ecx
4089     # . epilogue
4090     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4091     5d/pop-to-ebp
4092     c3/return
4093 
4094 $next-hex-int:abort:
4095     # . _write(2/stderr, error)
4096     # . . push args
4097     68/push  "next-hex-int: invalid hex char: "/imm32
4098     68/push  2/imm32/stderr
4099     # . . call
4100     e8/call  _write/disp32
4101     # . . discard args
4102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4103     # . clear-stream(Stderr->buffer)
4104     # . . save eax
4105     50/push-eax
4106     # . . push args
4107     68/push  Stderr->buffer/imm32
4108     # . . call
4109     e8/call  clear-stream/disp32
4110     # . . discard args
4111     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4112     # . . restore eax
4113     58/pop-to-eax
4114     # . print-int32-buffered(Stderr, eax)
4115     # . . push args
4116     50/push-eax
4117     68/push  Stderr/imm32
4118     # . . call
4119     e8/call  print-int32-buffered/disp32
4120     # . . discard args
4121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4122     # . flush(Stderr)
4123     # . . push args
4124     68/push  Stderr/imm32
4125     # . . call
4126     e8/call  flush/disp32
4127     # . . discard args
4128     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4129     # . _write(2/stderr, "\n")
4130     # . . push args
4131     68/push  Newline/imm32
4132     68/push  2/imm32/stderr
4133     # . . call
4134     e8/call  _write/disp32
4135     # . . discard args
4136     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4137     # . syscall(exit, 1)
4138     bb/copy-to-ebx  1/imm32
4139     b8/copy-to-eax  1/imm32/exit
4140     cd/syscall  0x80/imm8
4141     # never gets here
4142 
4143 test-next-hex-int-single-digit:
4144     # . prologue
4145     55/push-ebp
4146     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4147     # (eax..ecx) = "+a)"
4148     b8/copy-to-eax  "+a)"/imm32
4149     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4150     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
4151     05/add-to-eax  4/imm32
4152     # var slice/ecx = {eax, ecx}
4153     51/push-ecx
4154     50/push-eax
4155     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4156     # eax = next-hex-int(slice)
4157     # . . push args
4158     51/push-ecx
4159     # . . call
4160     e8/call  next-hex-int/disp32
4161     # . . discard args
4162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4163     # check-ints-equal(eax, 0xa, msg)
4164     # . . push args
4165     68/push  "F - test-next-hex-int-single-digit"/imm32
4166     68/push  0xa/imm32
4167     50/push-eax
4168     # . . call
4169     e8/call  check-ints-equal/disp32
4170     # . . discard args
4171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4172     # . epilogue
4173     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4174     5d/pop-to-ebp
4175     c3/return
4176 
4177 test-next-hex-int-multi-digit:
4178     # . prologue
4179     55/push-ebp
4180     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4181     # (eax..ecx) = "+ 34a)"
4182     b8/copy-to-eax  "+ 34a)"/imm32
4183     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4184     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
4185     05/add-to-eax  4/imm32
4186     # var slice/ecx = {eax, ecx}
4187     51/push-ecx
4188     50/push-eax
4189     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4190     # eax = next-hex-int(slice)
4191     # . . push args
4192     51/push-ecx
4193     # . . call
4194     e8/call  next-hex-int/disp32
4195     # . . discard args
4196     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4197     # check-ints-equal(eax, 0x34a, msg)
4198     # . . push args
4199     68/push  "F - test-next-hex-int-multi-digit"/imm32
4200     68/push  0x34a/imm32
4201     50/push-eax
4202     # . . call
4203     e8/call  check-ints-equal/disp32
4204     # . . discard args
4205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4206     # . epilogue
4207     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4208     5d/pop-to-ebp
4209     c3/return
4210 
4211 test-next-hex-int-0x-prefix:
4212     # . prologue
4213     55/push-ebp
4214     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4215     # (eax..ecx) = "+0x34)"
4216     b8/copy-to-eax  "+0x34)"/imm32
4217     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4218     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
4219     05/add-to-eax  4/imm32
4220     # var slice/ecx = {eax, ecx}
4221     51/push-ecx
4222     50/push-eax
4223     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4224     # eax = next-hex-int(slice)
4225     # . . push args
4226     51/push-ecx
4227     # . . call
4228     e8/call  next-hex-int/disp32
4229     # . . discard args
4230     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4231     # check-ints-equal(eax, 0x34, msg)
4232     # . . push args
4233     68/push  "F - test-next-hex-int-0x-prefix"/imm32
4234     68/push  0x34/imm32
4235     50/push-eax
4236     # . . call
4237     e8/call  check-ints-equal/disp32
4238     # . . discard args
4239     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4240     # . epilogue
4241     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4242     5d/pop-to-ebp
4243     c3/return
4244 
4245 test-next-hex-int-zero:
4246     # . prologue
4247     55/push-ebp
4248     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4249     # (eax..ecx) = "+0)"
4250     b8/copy-to-eax  "+0)"/imm32
4251     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4252     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
4253     05/add-to-eax  4/imm32
4254     # var slice/ecx = {eax, ecx}
4255     51/push-ecx
4256     50/push-eax
4257     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4258     # eax = next-hex-int(slice)
4259     # . . push args
4260     51/push-ecx
4261     # . . call
4262     e8/call  next-hex-int/disp32
4263     # . . discard args
4264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4265     # check-ints-equal(eax, 0, msg)
4266     # . . push args
4267     68/push  "F - test-next-hex-int-zero"/imm32
4268     68/push  0/imm32
4269     50/push-eax
4270     # . . call
4271     e8/call  check-ints-equal/disp32
4272     # . . discard args
4273     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4274     # . epilogue
4275     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4276     5d/pop-to-ebp
4277     c3/return
4278 
4279 test-next-hex-int-0-prefix:
4280     # . prologue
4281     55/push-ebp
4282     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4283     # (eax..ecx) = "+ 03)"
4284     b8/copy-to-eax  "+ 03)"/imm32
4285     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4286     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
4287     05/add-to-eax  4/imm32
4288     # var slice/ecx = {eax, ecx}
4289     51/push-ecx
4290     50/push-eax
4291     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4292     # eax = next-hex-int(slice)
4293     # . . push args
4294     51/push-ecx
4295     # . . call
4296     e8/call  next-hex-int/disp32
4297     # . . discard args
4298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4299     # check-ints-equal(eax, 3, msg)
4300     # . . push args
4301     68/push  "F - test-next-hex-int-0-prefix"/imm32
4302     68/push  3/imm32
4303     50/push-eax
4304     # . . call
4305     e8/call  check-ints-equal/disp32
4306     # . . discard args
4307     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4308     # . epilogue
4309     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4310     5d/pop-to-ebp
4311     c3/return
4312 
4313 test-next-hex-int-negative:
4314     # . prologue
4315     55/push-ebp
4316     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4317     # (eax..ecx) = "-03)"
4318     b8/copy-to-eax  "-03)"/imm32
4319     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4320     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
4321     05/add-to-eax  4/imm32
4322     # var slice/ecx = {eax, ecx}
4323     51/push-ecx
4324     50/push-eax
4325     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4326     # eax = next-hex-int(slice)
4327     # . . push args
4328     51/push-ecx
4329     # . . call
4330     e8/call  next-hex-int/disp32
4331     # . . discard args
4332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4333     # check-ints-equal(eax, -3, msg)
4334     # . . push args
4335     68/push  "F - test-next-hex-int-negative"/imm32
4336     68/push  -3/imm32
4337     50/push-eax
4338     # . . call
4339     e8/call  check-ints-equal/disp32
4340     # . . discard args
4341     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4342     # . epilogue
4343     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4344     5d/pop-to-ebp
4345     c3/return
4346 
4347 test-next-hex-int-negative-with-space:
4348     # . prologue
4349     55/push-ebp
4350     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4351     # (eax..ecx) = "- 03)"
4352     b8/copy-to-eax  "- 03)"/imm32
4353     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4354     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
4355     05/add-to-eax  4/imm32
4356     # var slice/ecx = {eax, ecx}
4357     51/push-ecx
4358     50/push-eax
4359     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4360     # eax = next-hex-int(slice)
4361     # . . push args
4362     51/push-ecx
4363     # . . call
4364     e8/call  next-hex-int/disp32
4365     # . . discard args
4366     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4367     # check-ints-equal(eax, -3, msg)
4368     # . . push args
4369     68/push  "F - test-next-hex-int-negative-with-space"/imm32
4370     68/push  -3/imm32
4371     50/push-eax
4372     # . . call
4373     e8/call  check-ints-equal/disp32
4374     # . . discard args
4375     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4376     # . epilogue
4377     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4378     5d/pop-to-ebp
4379     c3/return
4380 
4381 # assumes 'in' starts a positive unsigned integer
4382 # returns the value of the integer
4383 # side-effect: modifies 'in' to skip past the integer
4384 next-positive-hex-int:  # in : (address slice) -> result/eax
4385     # . prologue
4386     55/push-ebp
4387     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4388     # . save registers
4389     51/push-ecx
4390     52/push-edx
4391     53/push-ebx
4392     56/push-esi
4393     57/push-edi
4394     # result/edi = 0
4395     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
4396     # esi = in
4397     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
4398     # edx = in->end
4399     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
4400     # curr/ecx = in->start
4401     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
4402     # negate?/ebx = false
4403     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4404     # eax = *curr
4405     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4406     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4407 $next-positive-hex-int:initial-0:
4408     # skip past leading '0x'
4409     # . if (*curr != '0') jump to loop
4410     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4411     3d/compare-eax-and  0x30/imm32/0
4412     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
4413     # . ++curr
4414     41/increment-ecx
4415 $next-positive-hex-int:initial-0x:
4416     # . if (curr >= in->end) return result
4417     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4418     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
4419     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
4420     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4421     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4422     3d/compare-eax-and  0x78/imm32/x
4423     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
4424     # . ++curr
4425     41/increment-ecx
4426 $next-positive-hex-int:loop:
4427     # if (curr >= in->end) break
4428     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4429     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
4430     # if (!is-hex-digit?(*curr)) break
4431     # . eax = *curr
4432     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4433     # . eax = is-hex-digit?(*curr)
4434     # . . push args
4435     50/push-eax
4436     # . . call
4437     e8/call  is-hex-digit?/disp32
4438     # . . discard args
4439     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4440     # . if (eax == 0) break
4441     3d/compare-eax-and  0/imm32
4442     74/jump-if-equal  $next-positive-hex-int:end/disp8
4443     # eax = from-hex-char(*curr)
4444     # . . copy arg to eax
4445     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4446     # . . call
4447     e8/call  from-hex-char/disp32
4448     # result = result * 16 + eax
4449     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi left by 4 bits
4450     01/add                          3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to edi
4451     # ++curr
4452     41/increment-ecx
4453     # loop
4454     eb/jump  $next-positive-hex-int:loop/disp8
4455 $next-positive-hex-int:end:
4456     # word-slice->start = curr
4457     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy ecx to *esi
4458     # return edi
4459     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
4460     # . restore registers
4461     5f/pop-to-edi
4462     5e/pop-to-esi
4463     5b/pop-to-ebx
4464     5a/pop-to-edx
4465     59/pop-to-ecx
4466     # . epilogue
4467     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4468     5d/pop-to-ebp
4469     c3/return
4470 
4471 test-next-positive-hex-int-single-digit:
4472     # . prologue
4473     55/push-ebp
4474     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4475     # (eax..ecx) = "a)"
4476     b8/copy-to-eax  "a)"/imm32
4477     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4478     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
4479     05/add-to-eax  4/imm32
4480     # var slice/ecx = {eax, ecx}
4481     51/push-ecx
4482     50/push-eax
4483     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4484     # eax = next-positive-hex-int(slice)
4485     # . . push args
4486     51/push-ecx
4487     # . . call
4488     e8/call  next-positive-hex-int/disp32
4489     # . . discard args
4490     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4491     # check-ints-equal(eax, 0xa, msg)
4492     # . . push args
4493     68/push  "F - test-next-positive-hex-int-single-digit"/imm32
4494     68/push  0xa/imm32
4495     50/push-eax
4496     # . . call
4497     e8/call  check-ints-equal/disp32
4498     # . . discard args
4499     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4500     # . epilogue
4501     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4502     5d/pop-to-ebp
4503     c3/return
4504 
4505 test-next-positive-hex-int-multi-digit:
4506     # . prologue
4507     55/push-ebp
4508     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4509     # (eax..ecx) = "34a)"
4510     b8/copy-to-eax  "34a)"/imm32
4511     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4512     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
4513     05/add-to-eax  4/imm32
4514     # var slice/ecx = {eax, ecx}
4515     51/push-ecx
4516     50/push-eax
4517     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4518     # eax = next-positive-hex-int(slice)
4519     # . . push args
4520     51/push-ecx
4521     # . . call
4522     e8/call  next-positive-hex-int/disp32
4523     # . . discard args
4524     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4525     # check-ints-equal(eax, 0x34a, msg)
4526     # . . push args
4527     68/push  "F - test-next-positive-hex-int-multi-digit"/imm32
4528     68/push  0x34a/imm32
4529     50/push-eax
4530     # . . call
4531     e8/call  check-ints-equal/disp32
4532     # . . discard args
4533     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4534     # . epilogue
4535     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4536     5d/pop-to-ebp
4537     c3/return
4538 
4539 test-next-positive-hex-int-0x-prefix:
4540     # . prologue
4541     55/push-ebp
4542     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4543     # (eax..ecx) = "0x34)"
4544     b8/copy-to-eax  "0x34)"/imm32
4545     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4546     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
4547     05/add-to-eax  4/imm32
4548     # var slice/ecx = {eax, ecx}
4549     51/push-ecx
4550     50/push-eax
4551     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4552     # eax = next-positive-hex-int(slice)
4553     # . . push args
4554     51/push-ecx
4555     # . . call
4556     e8/call  next-positive-hex-int/disp32
4557     # . . discard args
4558     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4559     # check-ints-equal(eax, 0x34, msg)
4560     # . . push args
4561     68/push  "F - test-next-positive-hex-int-0x-prefix"/imm32
4562     68/push  0x34/imm32
4563     50/push-eax
4564     # . . call
4565     e8/call  check-ints-equal/disp32
4566     # . . discard args
4567     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4568     # . epilogue
4569     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4570     5d/pop-to-ebp
4571     c3/return
4572 
4573 test-next-positive-hex-int-zero:
4574     # . prologue
4575     55/push-ebp
4576     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4577     # (eax..ecx) = "0"
4578     b8/copy-to-eax  "0"/imm32
4579     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4580     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
4581     05/add-to-eax  4/imm32
4582     # var slice/ecx = {eax, ecx}
4583     51/push-ecx
4584     50/push-eax
4585     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4586     # eax = next-positive-hex-int(slice)
4587     # . . push args
4588     51/push-ecx
4589     # . . call
4590     e8/call  next-positive-hex-int/disp32
4591     # . . discard args
4592     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4593     # check-ints-equal(eax, 0, msg)
4594     # . . push args
4595     68/push  "F - test-next-positive-hex-int-zero"/imm32
4596     68/push  0/imm32
4597     50/push-eax
4598     # . . call
4599     e8/call  check-ints-equal/disp32
4600     # . . discard args
4601     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4602     # . epilogue
4603     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4604     5d/pop-to-ebp
4605     c3/return
4606 
4607 test-next-positive-hex-int-0-prefix:
4608     # . prologue
4609     55/push-ebp
4610     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4611     # (eax..ecx) = "03)"
4612     b8/copy-to-eax  "03)"/imm32
4613     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4614     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
4615     05/add-to-eax  4/imm32
4616     # var slice/ecx = {eax, ecx}
4617     51/push-ecx
4618     50/push-eax
4619     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4620     # eax = next-positive-hex-int(slice)
4621     # . . push args
4622     51/push-ecx
4623     # . . call
4624     e8/call  next-positive-hex-int/disp32
4625     # . . discard args
4626     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4627     # check-ints-equal(eax, 3, msg)
4628     # . . push args
4629     68/push  "F - test-next-positive-hex-int-0-prefix"/imm32
4630     68/push  3/imm32
4631     50/push-eax
4632     # . . call
4633     e8/call  check-ints-equal/disp32
4634     # . . discard args
4635     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4636     # . epilogue
4637     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4638     5d/pop-to-ebp
4639     c3/return
4640 
4641 == data
4642 Registers:  # (table string int)
4643   # a table is a stream
4644   0x40/imm32/write
4645   0/imm32/read
4646   0x40/imm32/length
4647   # data
4648   "eax"/imm32  0/imm32
4649   "ecx"/imm32  1/imm32
4650   "edx"/imm32  2/imm32
4651   "ebx"/imm32  3/imm32
4652   "esp"/imm32  4/imm32
4653   "ebp"/imm32  5/imm32
4654   "esi"/imm32  6/imm32
4655   "edi"/imm32  7/imm32
4656 
4657 # . . vim:nowrap:textwidth=0