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 #   $ ./bootstrap 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'  |  ./bootstrap run apps/sigils
  11 #   3/mod 0/rm32
  12 #
  13 # 2.
  14 #   $ echo '*eax'  |  ./bootstrap run apps/sigils
  15 #   0/mod 0/rm32
  16 #
  17 # 3.
  18 #   $ echo '*(eax+4)'  |  ./bootstrap run apps/sigils
  19 #   2/mod 0/rm32 4/disp32
  20 #
  21 # 4.
  22 #   $ echo '*(eax+ecx)'  |  ./bootstrap run apps/sigils
  23 #   0/mod 4/rm32 0/base 1/index 0/scale
  24 #
  25 # 5.
  26 #   $ echo '*(eax+ecx+4)'  |  ./bootstrap 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)'  |  ./bootstrap run apps/sigils
  31 #   2/mod 4/rm32 0/base 1/index 2/scale 4/disp32
  32 #
  33 # 7.
  34 #   $ echo '*Foo'  |  ./bootstrap 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 # We don't support conversions to floating-point registers. In particular, so
  45 # far there's no %xmm1.
  46 #
  47 # The code generated is sub-optimal:
  48 #   - displacements are always disp32, even when disp8 will do
  49 #   - *(...esp...) always uses SIB arguments even when redundant
  50 
  51 == code
  52 #   instruction                     effective address                                                   register    displacement    immediate
  53 # . op          subop               mod             rm32          base        index         scale       r32
  54 # . 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
  55 
  56 Entry:  # run tests if necessary, convert stdin if not
  57     # . prologue
  58     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
  59 
  60     # initialize heap
  61     # . Heap = new-segment(Heap-size)
  62     # . . push args
  63     68/push  Heap/imm32
  64     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
  65     # . . call
  66     e8/call  new-segment/disp32
  67     # . . discard args
  68     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  69 
  70     # - if argc > 1 and argv[1] == "test", then return run_tests()
  71     # if (argc <= 1) goto interactive
  72     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0/disp8         1/imm32           # compare *ebp
  73     7e/jump-if-<=  $subx-sigils-main:interactive/disp8
  74     # if (!kernel-string-equal?(argv[1], "test")) goto interactive
  75     # . eax = kernel-string-equal?(argv[1], "test")
  76     # . . push args
  77     68/push  "test"/imm32
  78     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
  79     # . . call
  80     e8/call  kernel-string-equal?/disp32
  81     # . . discard args
  82     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
  83     # . if (eax == false) goto interactive
  84     3d/compare-eax-and  0/imm32/false
  85     74/jump-if-=  $subx-sigils-main:interactive/disp8
  86     # run-tests()
  87     e8/call  run-tests/disp32
  88     # syscall(exit, *Num-test-failures)
  89     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/ebx   Num-test-failures/disp32          # copy *Num-test-failures to ebx
  90     eb/jump  $subx-sigils-main:end/disp8
  91 $subx-sigils-main:interactive:
  92     # - otherwise convert stdin
  93     # subx-sigils(Stdin, Stdout)
  94     # . . push args
  95     68/push  Stdout/imm32
  96     68/push  Stdin/imm32
  97     # . . call
  98     e8/call  subx-sigils/disp32
  99     # . . discard args
 100     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 101     # syscall(exit, 0)
 102     bb/copy-to-ebx  0/imm32
 103 $subx-sigils-main:end:
 104     e8/call  syscall_exit/disp32
 105 
 106 # error messages considered:
 107 #   *x + 34                 -> error: base+disp addressing must be within '()'
 108 subx-sigils:  # in: (addr buffered-file), out: (addr buffered-file)
 109     # pseudocode:
 110     #   var line: (stream byte 512)
 111     #   while true
 112     #     clear-stream(line)
 113     #     read-line-buffered(in, line)
 114     #     if (line->write == 0) break                     # end of file
 115     #     while true
 116     #       var word-slice = next-word-or-expression(line)
 117     #       if slice-empty?(word-slice)                   # end of line
 118     #         break
 119     #       if slice-starts-with?(word-slice, "#")        # comment
 120     #         continue
 121     #       if slice-starts-with?(word-slice, '%')        # direct mode
 122     #         emit-direct-mode(out, word-slice)
 123     #       else if slice-starts-with?(word-slice, '*')   # indirect mode
 124     #         if disp32-mode?(word-slice)
 125     #           emit-indirect-disp32(out, word-slice)
 126     #         else
 127     #           base, index, scale, disp = parse-effective-address(word-slice)
 128     #           emit-indirect-mode(out, base, index, scale, disp)
 129     #       else if slice-starts-with?(word-slice, '+')
 130     #         abort("'+' only permitted within '*(...)'")
 131     #       else
 132     #         write-slice-buffered(out, word-slice)
 133     #       write(out, " ")
 134     #     write(out, "\n")
 135     #   flush(out)
 136     #
 137     # . prologue
 138     55/push-ebp
 139     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 140     # . save registers
 141     50/push-eax
 142     51/push-ecx
 143     52/push-edx
 144     53/push-ebx
 145     # var line/ecx: (stream byte 512)
 146     81          5/subop/subtract    3/mod/direct    4/rm32/esp    .           .             .           .           .               0x200/imm32       # subtract from esp
 147     68/push  0x200/imm32/length
 148     68/push  0/imm32/read
 149     68/push  0/imm32/write
 150     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
 151     # var word-slice/edx: slice
 152     68/push  0/imm32/end
 153     68/push  0/imm32/start
 154     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           4/r32/esp   .               .                 # copy esp to edx
 155 $subx-sigils:line-loop:
 156     # clear-stream(line)
 157     # . . push args
 158     51/push-ecx
 159     # . . call
 160     e8/call  clear-stream/disp32
 161     # . . discard args
 162     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 163     # read-line-buffered(in, line)
 164     # . . push args
 165     51/push-ecx
 166     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 167     # . . call
 168     e8/call  read-line-buffered/disp32
 169     # . . discard args
 170     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 171 $subx-sigils:check0:
 172     # if (line->write == 0) break
 173     81          7/subop/compare     0/mod/indirect  1/rm32/ecx    .           .             .           .           .               0/imm32           # compare *ecx
 174     0f 84/jump-if-=  $subx-sigils:break/disp32
 175 $subx-sigils:word-loop:
 176     # next-word-or-expression(line, word-slice)
 177     # . . push args
 178     52/push-edx
 179     51/push-ecx
 180     # . . call
 181     e8/call  next-word-or-expression/disp32
 182     # . . discard args
 183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 184 $subx-sigils:check1:
 185     # if (slice-empty?(word-slice)) break
 186     # . eax = slice-empty?(word-slice)
 187     # . . push args
 188     52/push-edx
 189     # . . call
 190     e8/call  slice-empty?/disp32
 191     # . . discard args
 192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 193     # . if (eax != false) break
 194     3d/compare-eax-and  0/imm32/false
 195     0f 85/jump-if-!=  $subx-sigils:next-line/disp32
 196 $subx-sigils:check-for-comment:
 197     # if (slice-starts-with?(word-slice, "#")) continue
 198     # . start/ebx = word-slice->start
 199     8b/copy                         0/mod/indirect  2/rm32/edx    .           .             .           3/r32/ebx   .               .                 # copy *edx to ebx
 200     # . c/eax = *start
 201     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 202     8a/copy-byte                    0/mod/indirect  3/rm32/ebx    .           .             .           0/r32/AL    .               .                 # copy byte at *ebx to AL
 203     # . if (eax == '#') continue
 204     3d/compare-eax-and  0x23/imm32/hash
 205     74/jump-if-=  $subx-sigils:word-loop/disp8
 206 $subx-sigils:check-for-direct-mode:
 207     # if (!slice-starts-with?(word-slice, "%")) goto next check
 208     3d/compare-eax-and  0x25/imm32/percent
 209     75/jump-if-!=  $subx-sigils:check-for-indirect-mode/disp8
 210 $subx-sigils:direct-mode:
 211 +-- 40 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 251     # emit-direct-mode(out, word-slice)
 252     # . . push args
 253     52/push-edx
 254     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 255     # . . call
 256     e8/call  emit-direct-mode/disp32
 257     # . . discard args
 258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 259     # continue
 260     e9/jump  $subx-sigils:next-word/disp32
 261 $subx-sigils:check-for-indirect-mode:
 262     # if (!slice-starts-with?(word-slice, "*")) goto next check
 263     3d/compare-eax-and  0x2a/imm32/asterisk
 264     75/jump-if-!=  $subx-sigils:check-for-invalid-addition/disp8
 265     # if (!disp32-mode?(word-slice)) goto indirect mode
 266     # . eax = disp32-mode?(word-slice)
 267     # . . push args
 268     52/push-edx
 269     # . . call
 270     e8/call  disp32-mode?/disp32
 271     # . . discard args
 272     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 273     # . if (eax == false) goto indirect mode
 274     3d/compare-eax-and  0/imm32/false
 275     74/jump-if-=  $subx-sigils:indirect-mode/disp8
 276 $subx-sigils:disp32-mode:
 277     # emit-indirect-mode(out, word-slice)
 278     # . . push args
 279     52/push-edx
 280     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 281     # . . call
 282     e8/call  emit-indirect-disp32/disp32
 283     # . . discard args
 284     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 285     # continue
 286     e9/jump  $subx-sigils:next-word/disp32
 287 $subx-sigils:indirect-mode:
 288     # spill registers
 289     50/push-eax
 290     51/push-ecx
 291     52/push-edx
 292     53/push-ebx
 293     # base/eax, index/ecx, scale/edx, disp/ebx = parse-effective-address(word-slice)
 294     # . . push args
 295     52/push-edx
 296     # . . call
 297     e8/call  parse-effective-address/disp32
 298     # . . discard args
 299     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 300     # emit-indirect-mode(out, base, index, scale, disp)
 301     # . . push args
 302     53/push-ebx
 303     52/push-edx
 304     51/push-ecx
 305     50/push-eax
 306     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 307     # . . call
 308     e8/call  emit-indirect-mode/disp32
 309     # . . discard args
 310     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
 311     # restore registers
 312     5b/pop-to-ebx
 313     5a/pop-to-edx
 314     59/pop-to-ecx
 315     58/pop-to-eax
 316     # continue
 317     e9/jump  $subx-sigils:next-word/disp32
 318 $subx-sigils:check-for-invalid-addition:
 319     # if (slice-starts-with?(word-slice, "+")) goto error1
 320     3d/compare-eax-and  0x2b/imm32/plus
 321     74/jump-if-=  $subx-sigils:error1/disp8
 322 $subx-sigils:check-for-invalid-left-shift:
 323     # if (slice-starts-with?(word-slice, "<")) goto error1
 324     3d/compare-eax-and  0x3c/imm32/less-than
 325     74/jump-if-=  $subx-sigils:error1/disp8
 326 $subx-sigils:regular-word:
 327     # write-slice-buffered(out, word-slice)
 328     # . . push args
 329     52/push-edx
 330     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 331     # . . call
 332     e8/call  write-slice-buffered/disp32
 333     # . . discard args
 334     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 335     # fall through
 336 $subx-sigils:next-word:
 337     # write-buffered(out, " ")
 338     # . . push args
 339     68/push  Space/imm32
 340     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 341     # . . call
 342     e8/call  write-buffered/disp32
 343     # . . discard args
 344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 345     # loop
 346     e9/jump  $subx-sigils:word-loop/disp32
 347 $subx-sigils:next-line:
 348     # write-buffered(out, "\n")
 349     # . . push args
 350     68/push  Newline/imm32
 351     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 352     # . . call
 353     e8/call  write-buffered/disp32
 354     # . . discard args
 355     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 356     # loop
 357     e9/jump  $subx-sigils:line-loop/disp32
 358 $subx-sigils:break:
 359     # flush(out)
 360     # . . push args
 361     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 362     # . . call
 363     e8/call  flush/disp32
 364     # . . discard args
 365     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 366 $subx-sigils:end:
 367     # . reclaim locals
 368     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x214/imm32       # add to esp
 369     # . restore registers
 370     5b/pop-to-ebx
 371     5a/pop-to-edx
 372     59/pop-to-ecx
 373     58/pop-to-eax
 374     # . epilogue
 375     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 376     5d/pop-to-ebp
 377     c3/return
 378 
 379 $subx-sigils:error1:
 380     # print(stderr, "error: '" eax "' only permitted within '*(...)' in '" line "'")
 381     # . write-buffered(Stderr, "error: '")
 382     # . . push args
 383     68/push  "error: '"/imm32
 384     68/push  Stderr/imm32
 385     # . . call
 386     e8/call  write-buffered/disp32
 387     # . . discard args
 388     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 389     # . write-byte-buffered(Stderr, eax)
 390     # . . push args
 391     50/push-eax
 392     68/push  Stderr/imm32
 393     # . . call
 394     e8/call  write-byte-buffered/disp32
 395     # . . discard args
 396     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 397     # . write-buffered(Stderr, "' only permitted within '*(...)' in '")
 398     # . . push args
 399     68/push  "' only permitted within '*(...)' in '"/imm32
 400     68/push  Stderr/imm32
 401     # . . call
 402     e8/call  write-buffered/disp32
 403     # . . discard args
 404     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 405     # . write-stream-data(Stderr, line)
 406     # . . push args
 407     51/push-ecx
 408     68/push  Stderr/imm32
 409     # . . call
 410     e8/call  write-stream-data/disp32
 411     # . . discard args
 412     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 413     # . write-buffered(Stderr, "'")
 414     # . . push args
 415     68/push  "'"/imm32
 416     68/push  Stderr/imm32
 417     # . . call
 418     e8/call  write-buffered/disp32
 419     # . . discard args
 420     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 421     # . flush(Stderr)
 422     # . . push args
 423     68/push  Stderr/imm32
 424     # . . call
 425     e8/call  flush/disp32
 426     # . . discard args
 427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 428     # . syscall(exit, 1)
 429     bb/copy-to-ebx  1/imm32
 430     e8/call  syscall_exit/disp32
 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: (addr buffered-file), word-slice: (addr 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: 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 +-- 24 lines: #?     # write-slice-buffered(Stderr, word-slice) ----------------------------------------------------------------------------------------------------------------------------------------------------
1400     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
1401     # . . push args
1402     50/push-eax
1403     68/push  0x2f/imm32/slash
1404     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1405     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1406     # . . call
1407     e8/call  next-token-from-slice/disp32
1408     # . . discard args
1409     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1410     # reg-num/eax = get-slice(Registers, local-slice, row-size=12)
1411     # . . push args
1412     68/push  "Registers"/imm32
1413     68/push  0xc/imm32/row-size
1414     50/push-eax
1415     68/push  Registers/imm32
1416     # . . call
1417     e8/call  get-slice/disp32
1418     # . . discard args
1419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
1420     # write-buffered(out, "3/mod/direct ")
1421     # . . push args
1422     68/push  "3/mod/direct "/imm32
1423     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1424     # . . call
1425     e8/call  write-buffered/disp32
1426     # . . discard args
1427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1428     # write-int32-hex-buffered(out, *reg-num)
1429     # . . push args
1430     ff          6/subop/push        0/mod/indirect  0/rm32/eax    .           .             .           .           .               .                 # push *eax
1431     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1432     # . . call
1433     e8/call  write-int32-hex-buffered/disp32
1434     # . . discard args
1435     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1436     # write-buffered(out, "/rm32")
1437     # . . push args
1438     68/push  "/rm32"/imm32
1439     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1440     # . . call
1441     e8/call  write-buffered/disp32
1442     # . . discard args
1443     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1444 $emit-direct-mode:end:
1445     # . reclaim locals
1446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1447     # . restore registers
1448     58/pop-to-eax
1449     # . epilogue
1450     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1451     5d/pop-to-ebp
1452     c3/return
1453 
1454 test-emit-direct-mode:
1455     # . prologue
1456     55/push-ebp
1457     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1458     # setup
1459     # . clear-stream(_test-output-stream)
1460     # . . push args
1461     68/push  _test-output-stream/imm32
1462     # . . call
1463     e8/call  clear-stream/disp32
1464     # . . discard args
1465     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1466     # . clear-stream($_test-output-buffered-file->buffer)
1467     # . . push args
1468     68/push  $_test-output-buffered-file->buffer/imm32
1469     # . . call
1470     e8/call  clear-stream/disp32
1471     # . . discard args
1472     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1473     # var slice/ecx = "%eax"
1474     b8/copy-to-eax  "%eax"/imm32
1475     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1476     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
1477     05/add-to-eax  4/imm32
1478     # . ecx = {eax, ecx}
1479     51/push-ecx
1480     50/push-eax
1481     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1482     # emit-direct-mode(_test-output-buffered-file, str)
1483     # . . push args
1484     51/push-ecx
1485     68/push  _test-output-buffered-file/imm32
1486     # . . call
1487     e8/call  emit-direct-mode/disp32
1488     # . . discard args
1489     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32         # add to esp
1490     # . flush(_test-output-buffered-file)
1491     # . . push args
1492     68/push  _test-output-buffered-file/imm32
1493     # . . call
1494     e8/call  flush/disp32
1495     # . . discard args
1496     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1497 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1523     # check-stream-equal(_test-output-stream, "3/mod/direct 0/rm32", msg)
1524     # . . push args
1525     68/push  "F - test-emit-direct-mode/0"/imm32
1526     68/push  "3/mod/direct 0x00000000/rm32"/imm32
1527     68/push  _test-output-stream/imm32
1528     # . . call
1529     e8/call  check-stream-equal/disp32
1530     # . . discard args
1531     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1532     # . epilogue
1533     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1534     5d/pop-to-ebp
1535     c3/return
1536 
1537 test-emit-direct-mode-2:
1538     # . prologue
1539     55/push-ebp
1540     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1541     # setup
1542     # . clear-stream(_test-output-stream)
1543     # . . push args
1544     68/push  _test-output-stream/imm32
1545     # . . call
1546     e8/call  clear-stream/disp32
1547     # . . discard args
1548     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1549     # . clear-stream($_test-output-buffered-file->buffer)
1550     # . . push args
1551     68/push  $_test-output-buffered-file->buffer/imm32
1552     # . . call
1553     e8/call  clear-stream/disp32
1554     # . . discard args
1555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1556     # var slice/ecx = "%edi"
1557     b8/copy-to-eax  "%edi"/imm32
1558     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1559     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
1560     05/add-to-eax  4/imm32
1561     # . ecx = {eax, ecx}
1562     51/push-ecx
1563     50/push-eax
1564     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1565     # emit-direct-mode(_test-output-buffered-file, str/ecx)
1566     # . . push args
1567     51/push-ecx
1568     68/push  _test-output-buffered-file/imm32
1569     # . . call
1570     e8/call  emit-direct-mode/disp32
1571     # . . discard args
1572     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32         # add to esp
1573     # . flush(_test-output-buffered-file)
1574     # . . push args
1575     68/push  _test-output-buffered-file/imm32
1576     # . . call
1577     e8/call  flush/disp32
1578     # . . discard args
1579     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1580 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1606     # check-stream-equal(_test-output-stream, "3/mod/direct 7/rm32", msg)
1607     # . . push args
1608     68/push  "F - test-emit-direct-mode/1"/imm32
1609     68/push  "3/mod/direct 0x00000007/rm32"/imm32
1610     68/push  _test-output-stream/imm32
1611     # . . call
1612     e8/call  check-stream-equal/disp32
1613     # . . discard args
1614     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1615     # . epilogue
1616     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1617     5d/pop-to-ebp
1618     c3/return
1619 
1620 # (re)compute the bounds of the next word or parenthetical expression in the line
1621 # return empty string on reaching end of file
1622 #
1623 # error messages considered:
1624 #   * ...                   -> error: no space after '*'
1625 #   *(...                   -> error: *(...) expression must be all on a single line
1626 next-word-or-expression:  # line: (addr stream byte), out: (addr slice)
1627     # pseudocode:
1628     #   skip-chars-matching(line, ' ')
1629     #   if line->read >= line->write              # end of line
1630     #     out = {0, 0}
1631     #     return
1632     #   out->start = &line->data[line->read]
1633     #   if line->data[line->read] == '#'          # comment
1634     #     out.end = &line->data[line->write]
1635     #     return
1636     #   if line->data[line->read] == '"'          # string literal
1637     #     skip-string(line)
1638     #   else if line->data[line->read] == '*'     # expression
1639     #     if line->data[line->read + 1] == ' '
1640     #       abort
1641     #     if line->data[line->read + 1] == '('
1642     #       skip-until-close-paren(line)
1643     #       if (line->data[line->read] != ')'
1644     #         abort
1645     #       ++line->data[line->read] to skip ')'
1646     #   skip-chars-not-matching-whitespace(line)
1647     #   out->end = &line->data[line->read]
1648     #
1649     # registers:
1650     #   ecx: often line->read
1651     #   eax: often line->data[line->read]
1652     #
1653     # . prologue
1654     55/push-ebp
1655     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1656     # . save registers
1657     50/push-eax
1658     51/push-ecx
1659     56/push-esi
1660     57/push-edi
1661     # esi = line
1662     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
1663     # edi = out
1664     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
1665     # skip-chars-matching(line, ' ')
1666     # . . push args
1667     68/push  0x20/imm32/space
1668     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1669     # . . call
1670     e8/call  skip-chars-matching/disp32
1671     # . . discard args
1672     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1673 $next-word-or-expression:check0:
1674     # if (line->read >= line->write) clear out and return
1675     # . ecx = line->read
1676     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1677     # . if (ecx < line->write) goto next check
1678     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # compare ecx with *esi
1679     7c/jump-if-<  $next-word-or-expression:check-for-comment/disp8
1680     # . return out = {0, 0}
1681     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
1682     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
1683     e9/jump  $next-word-or-expression:end/disp32
1684 $next-word-or-expression:check-for-comment:
1685     # out->start = &line->data[line->read]
1686     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
1687     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
1688     # if (line->data[line->read] != '#') goto next check
1689     # . eax = line->data[line->read]
1690     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1691     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
1692     # . if (eax != '#') goto next check
1693     3d/compare-eax-and  0x23/imm32/pound
1694     75/jump-if-!=  $next-word-or-expression:check-for-string-literal/disp8
1695 $next-word-or-expression:comment:
1696     # out->end = &line->data[line->write]
1697     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1698     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
1699     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1700     # line->read = line->write  # skip rest of line
1701     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
1702     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(esi+4)
1703     # return
1704     eb/jump  $next-word-or-expression:end/disp8
1705 $next-word-or-expression:check-for-string-literal:
1706     # if (line->data[line->read] != '"') goto next check
1707     3d/compare-eax-and  0x22/imm32/dquote
1708     75/jump-if-!=  $next-word-or-expression:check-for-expression/disp8
1709 $next-word-or-expression:string-literal:
1710     # skip-string(line)
1711     # . . push args
1712     56/push-esi
1713     # . . call
1714     e8/call  skip-string/disp32
1715     # . . discard args
1716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1717     # skip rest of word
1718     eb/jump  $next-word-or-expression:regular-word/disp8
1719 $next-word-or-expression:check-for-expression:
1720     # if (line->data[line->read] != '*') goto next check
1721     3d/compare-eax-and  0x2a/imm32/asterisk
1722     75/jump-if-!=  $next-word-or-expression:regular-word/disp8
1723     # if (line->data[line->read + 1] == ' ') goto error1
1724     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
1725     3d/compare-eax-and  0x20/imm32/space
1726     74/jump-if-=  $next-word-or-expression:error1/disp8
1727     # if (line->data[line->read + 1] != '(') goto regular word
1728     3d/compare-eax-and  0x28/imm32/open-paren
1729     75/jump-if-!=  $next-word-or-expression:regular-word/disp8
1730 $next-word-or-expression:paren:
1731     # skip-until-close-paren(line)
1732     # . . push args
1733     56/push-esi
1734     # . . call
1735     e8/call  skip-until-close-paren/disp32
1736     # . . discard args
1737     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1738     # if (line->data[line->read] != ')') goto error2
1739     # . eax = line->data[line->read]
1740     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1741     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
1742     # . if (eax != ')') goto error2
1743     3d/compare-eax-and  0x29/imm32/close-paren
1744     75/jump-if-!=  $next-word-or-expression:error2/disp8
1745     # skip ')'
1746     ff          0/subop/increment   1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # increment *(esi+4)
1747     # fall through
1748 $next-word-or-expression:regular-word:
1749     # skip-chars-not-matching-whitespace(line)  # including trailing newline
1750     # . . push args
1751     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
1752     # . . call
1753     e8/call  skip-chars-not-matching-whitespace/disp32
1754     # . . discard args
1755     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1756     # out->end = &line->data[line->read]
1757     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
1758     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
1759     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
1760 $next-word-or-expression:end:
1761     # . restore registers
1762     5f/pop-to-edi
1763     5e/pop-to-esi
1764     59/pop-to-ecx
1765     58/pop-to-eax
1766     # . epilogue
1767     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1768     5d/pop-to-ebp
1769     c3/return
1770 
1771 $next-word-or-expression:error1:
1772     # print(stderr, "error: no space allowed after '*' in '" line "'")
1773     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
1774     # . . push args
1775     68/push  "error: no space allowed after '*' in '"/imm32
1776     68/push  Stderr/imm32
1777     # . . call
1778     e8/call  write-buffered/disp32
1779     # . . discard args
1780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1781     # . write-stream-data(Stderr, line)
1782     # . . push args
1783     56/push-esi
1784     68/push  Stderr/imm32
1785     # . . call
1786     e8/call  write-stream-data/disp32
1787     # . . discard args
1788     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1789     # . write-buffered(Stderr, "'")
1790     # . . push args
1791     68/push  "'"/imm32
1792     68/push  Stderr/imm32
1793     # . . call
1794     e8/call  write-buffered/disp32
1795     # . . discard args
1796     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1797     # . flush(Stderr)
1798     # . . push args
1799     68/push  Stderr/imm32
1800     # . . call
1801     e8/call  flush/disp32
1802     # . . discard args
1803     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1804     # . syscall(exit, 1)
1805     bb/copy-to-ebx  1/imm32
1806     e8/call  syscall_exit/disp32
1807     # never gets here
1808 
1809 $next-word-or-expression:error2:
1810     # print(stderr, "error: *(...) expression must be all on a single line in '" line "'")
1811     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
1812     # . . push args
1813     68/push  "error: *(...) expression must be all on a single line in '"/imm32
1814     68/push  Stderr/imm32
1815     # . . call
1816     e8/call  write-buffered/disp32
1817     # . . discard args
1818     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1819     # . write-stream-data(Stderr, line)
1820     # . . push args
1821     56/push-esi
1822     68/push  Stderr/imm32
1823     # . . call
1824     e8/call  write-stream-data/disp32
1825     # . . discard args
1826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1827     # . write-buffered(Stderr, "'")
1828     # . . push args
1829     68/push  "'"/imm32
1830     68/push  Stderr/imm32
1831     # . . call
1832     e8/call  write-buffered/disp32
1833     # . . discard args
1834     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1835     # . flush(Stderr)
1836     # . . push args
1837     68/push  Stderr/imm32
1838     # . . call
1839     e8/call  flush/disp32
1840     # . . discard args
1841     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1842     # . syscall(exit, 1)
1843     bb/copy-to-ebx  1/imm32
1844     e8/call  syscall_exit/disp32
1845     # never gets here
1846 
1847 test-next-word-or-expression:
1848     # . prologue
1849     55/push-ebp
1850     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1851     # setup
1852     # . clear-stream(_test-input-stream)
1853     # . . push args
1854     68/push  _test-input-stream/imm32
1855     # . . call
1856     e8/call  clear-stream/disp32
1857     # . . discard args
1858     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1859     # var slice/ecx: slice
1860     68/push  0/imm32/end
1861     68/push  0/imm32/start
1862     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1863     # write(_test-input-stream, "  ab")
1864     # . . push args
1865     68/push  "  ab"/imm32
1866     68/push  _test-input-stream/imm32
1867     # . . call
1868     e8/call  write/disp32
1869     # . . discard args
1870     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1871     # next-word-or-expression(_test-input-stream, slice)
1872     # . . push args
1873     51/push-ecx
1874     68/push  _test-input-stream/imm32
1875     # . . call
1876     e8/call  next-word-or-expression/disp32
1877     # . . discard args
1878     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1879     # check-ints-equal(_test-input-stream->read, 4, msg)
1880     # . . push args
1881     68/push  "F - test-next-word-or-expression/updates-stream-read-correctly"/imm32
1882     68/push  4/imm32
1883     b8/copy-to-eax  _test-input-stream/imm32
1884     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1885     # . . call
1886     e8/call  check-ints-equal/disp32
1887     # . . discard args
1888     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1889     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1890     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1891     # . . push args
1892     68/push  "F - test-next-word-or-expression: start"/imm32
1893     68/push  0xe/imm32
1894     # . . push slice->start - _test-input-stream
1895     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1896     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1897     50/push-eax
1898     # . . call
1899     e8/call  check-ints-equal/disp32
1900     # . . discard args
1901     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1902     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
1903     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
1904     # . . push args
1905     68/push  "F - test-next-word-or-expression: end"/imm32
1906     68/push  0x10/imm32
1907     # . . push slice->end - _test-input-stream
1908     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
1909     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1910     50/push-eax
1911     # . . call
1912     e8/call  check-ints-equal/disp32
1913     # . . discard args
1914     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1915     # . epilogue
1916     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1917     5d/pop-to-ebp
1918     c3/return
1919 
1920 test-next-word-or-expression-returns-whole-comment:
1921     # . prologue
1922     55/push-ebp
1923     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1924     # setup
1925     # . clear-stream(_test-input-stream)
1926     # . . push args
1927     68/push  _test-input-stream/imm32
1928     # . . call
1929     e8/call  clear-stream/disp32
1930     # . . discard args
1931     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1932     # var slice/ecx: slice
1933     68/push  0/imm32/end
1934     68/push  0/imm32/start
1935     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
1936     # write(_test-input-stream, "  # a")
1937     # . . push args
1938     68/push  "  # a"/imm32
1939     68/push  _test-input-stream/imm32
1940     # . . call
1941     e8/call  write/disp32
1942     # . . discard args
1943     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1944     # next-word-or-expression(_test-input-stream, slice)
1945     # . . push args
1946     51/push-ecx
1947     68/push  _test-input-stream/imm32
1948     # . . call
1949     e8/call  next-word-or-expression/disp32
1950     # . . discard args
1951     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1952     # check-ints-equal(_test-input-stream->read, 5, msg)
1953     # . . push args
1954     68/push  "F - test-next-word-or-expression-returns-whole-comment/updates-stream-read-correctly"/imm32
1955     68/push  5/imm32
1956     b8/copy-to-eax  _test-input-stream/imm32
1957     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1958     # . . call
1959     e8/call  check-ints-equal/disp32
1960     # . . discard args
1961     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1962     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
1963     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
1964     # . . push args
1965     68/push  "F - test-next-word-or-expression-returns-whole-comment: start"/imm32
1966     68/push  0xe/imm32
1967     # . . push slice->start - _test-input-stream
1968     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
1969     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1970     50/push-eax
1971     # . . call
1972     e8/call  check-ints-equal/disp32
1973     # . . discard args
1974     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1975     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
1976     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
1977     # . . push args
1978     68/push  "F - test-next-word-or-expression-returns-whole-comment: end"/imm32
1979     68/push  0x11/imm32
1980     # . . push slice->end - _test-input-stream
1981     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
1982     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
1983     50/push-eax
1984     # . . call
1985     e8/call  check-ints-equal/disp32
1986     # . . discard args
1987     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1988     # . epilogue
1989     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1990     5d/pop-to-ebp
1991     c3/return
1992 
1993 test-next-word-or-expression-returns-empty-slice-on-eof:
1994     # . prologue
1995     55/push-ebp
1996     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1997     # setup
1998     # . clear-stream(_test-input-stream)
1999     # . . push args
2000     68/push  _test-input-stream/imm32
2001     # . . call
2002     e8/call  clear-stream/disp32
2003     # . . discard args
2004     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2005     # var slice/ecx: slice
2006     68/push  0/imm32/end
2007     68/push  0/imm32/start
2008     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2009     # write nothing to _test-input-stream
2010     # next-word-or-expression(_test-input-stream, slice)
2011     # . . push args
2012     51/push-ecx
2013     68/push  _test-input-stream/imm32
2014     # . . call
2015     e8/call  next-word-or-expression/disp32
2016     # . . discard args
2017     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2018     # check-ints-equal(slice->end - slice->start, 0, msg)
2019     # . . push args
2020     68/push  "F - test-next-word-or-expression-returns-empty-expression-on-eof"/imm32
2021     68/push  0/imm32
2022     # . . push slice->end - slice->start
2023     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2024     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
2025     50/push-eax
2026     # . . call
2027     e8/call  check-ints-equal/disp32
2028     # . . discard args
2029     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2030     # . epilogue
2031     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2032     5d/pop-to-ebp
2033     c3/return
2034 
2035 test-next-word-or-expression-returns-string-literal:
2036     # . prologue
2037     55/push-ebp
2038     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2039     # setup
2040     # . clear-stream(_test-input-stream)
2041     # . . push args
2042     68/push  _test-input-stream/imm32
2043     # . . call
2044     e8/call  clear-stream/disp32
2045     # . . discard args
2046     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2047     # var slice/ecx: slice
2048     68/push  0/imm32/end
2049     68/push  0/imm32/start
2050     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2051     # write(_test-input-stream, " \"a b\"/imm32 ")
2052     # . . push args
2053     68/push  " \"a b\"/imm32 "/imm32
2054     68/push  _test-input-stream/imm32
2055     # . . call
2056     e8/call  write/disp32
2057     # . . discard args
2058     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2059     # next-word-or-expression(_test-input-stream, slice)
2060     # . . push args
2061     51/push-ecx
2062     68/push  _test-input-stream/imm32
2063     # . . call
2064     e8/call  next-word-or-expression/disp32
2065     # . . discard args
2066     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2067     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2068     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2069     # . . push args
2070     68/push  "F - test-next-word-or-expression-returns-string-literal: start"/imm32
2071     68/push  0xd/imm32
2072     # . . push slice->start - _test-input-stream
2073     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2074     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2075     50/push-eax
2076     # . . call
2077     e8/call  check-ints-equal/disp32
2078     # . . discard args
2079     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2080     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
2081     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
2082     # . . push args
2083     68/push  "F - test-next-word-or-expression-returns-string-literal: end"/imm32
2084     68/push  0x18/imm32
2085     # . . push slice->end - _test-input-stream
2086     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2087     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2088     50/push-eax
2089     # . . call
2090     e8/call  check-ints-equal/disp32
2091     # . . discard args
2092     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2093     # . epilogue
2094     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2095     5d/pop-to-ebp
2096     c3/return
2097 
2098 test-next-word-or-expression-returns-string-with-escapes:
2099     # . prologue
2100     55/push-ebp
2101     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2102     # setup
2103     # . clear-stream(_test-input-stream)
2104     # . . push args
2105     68/push  _test-input-stream/imm32
2106     # . . call
2107     e8/call  clear-stream/disp32
2108     # . . discard args
2109     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2110     # var slice/ecx: slice
2111     68/push  0/imm32/end
2112     68/push  0/imm32/start
2113     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2114     # write(_test-input-stream, " \"a\\\"b\"/x")
2115     # . . push args
2116     68/push  " \"a\\\"b\"/x"/imm32
2117     68/push  _test-input-stream/imm32
2118     # . . call
2119     e8/call  write/disp32
2120     # . . discard args
2121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2122     # next-word-or-expression(_test-input-stream, slice)
2123     # . . push args
2124     51/push-ecx
2125     68/push  _test-input-stream/imm32
2126     # . . call
2127     e8/call  next-word-or-expression/disp32
2128     # . . discard args
2129     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2130     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2131     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2132     # . . push args
2133     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: start"/imm32
2134     68/push  0xd/imm32
2135     # . . push slice->start - _test-input-stream
2136     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2137     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2138     50/push-eax
2139     # . . call
2140     e8/call  check-ints-equal/disp32
2141     # . . discard args
2142     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2143     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
2144     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
2145     # . . push args
2146     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: end"/imm32
2147     68/push  0x15/imm32
2148     # . . push slice->end - _test-input-stream
2149     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2150     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2151     50/push-eax
2152     # . . call
2153     e8/call  check-ints-equal/disp32
2154     # . . discard args
2155     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2156     # . epilogue
2157     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2158     5d/pop-to-ebp
2159     c3/return
2160 
2161 test-next-word-or-expression-returns-whole-expression:
2162     # . prologue
2163     55/push-ebp
2164     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2165     # setup
2166     # . clear-stream(_test-input-stream)
2167     # . . push args
2168     68/push  _test-input-stream/imm32
2169     # . . call
2170     e8/call  clear-stream/disp32
2171     # . . discard args
2172     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2173     # var slice/ecx: slice
2174     68/push  0/imm32/end
2175     68/push  0/imm32/start
2176     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2177     # write(_test-input-stream, " *(a b)/imm32 ")
2178     # . . push args
2179     68/push  " *(a b)/imm32 "/imm32
2180     68/push  _test-input-stream/imm32
2181     # . . call
2182     e8/call  write/disp32
2183     # . . discard args
2184     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2185     # next-word-or-expression(_test-input-stream, slice)
2186     # . . push args
2187     51/push-ecx
2188     68/push  _test-input-stream/imm32
2189     # . . call
2190     e8/call  next-word-or-expression/disp32
2191     # . . discard args
2192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2193     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
2194     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
2195     # . . push args
2196     68/push  "F - test-next-word-or-expression-returns-whole-expression: start"/imm32
2197     68/push  0xd/imm32
2198     # . . push slice->start - _test-input-stream
2199     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
2200     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2201     50/push-eax
2202     # . . call
2203     e8/call  check-ints-equal/disp32
2204     # . . discard args
2205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2206     # check-ints-equal(slice->end - _test-input-stream->data, 13, msg)
2207     # . check-ints-equal(slice->end - _test-input-stream, 25, msg)
2208     # . . push args
2209     68/push  "F - test-next-word-or-expression-returns-whole-expression: end"/imm32
2210     68/push  0x19/imm32
2211     # . . push slice->end - _test-input-stream
2212     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
2213     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
2214     50/push-eax
2215     # . . call
2216     e8/call  check-ints-equal/disp32
2217     # . . discard args
2218     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2219     # . epilogue
2220     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2221     5d/pop-to-ebp
2222     c3/return
2223 
2224 # Grammar:
2225 #   *reg                    -> 0/mod reg/rm32
2226 #   *(reg)                  -> 0/mod reg/rm32
2227 #   *(reg+disp)             -> 2/mod reg/rm32 disp/disp32
2228 #   *(reg1+reg2<<s)         -> 2/mod 4/rm32 reg1/base reg2/index s/scale 0/disp32
2229 #   *(reg1+reg2<<s+disp)    -> 2/mod 4/rm32 reg1/base reg2/index s/scale disp/disp32
2230 # Intermediate structure: base, index, scale, disp
2231 # Default values: base: 0, index: 4 (none), scale: 0, disp: 0
2232 parse-effective-address:  # word-slice: (addr slice) -> base/eax, index/ecx, scale/edx, disp/ebx
2233     # pseudocode:
2234     #   var local-slice = {word-slice->start, word-slice->end}
2235     #   ++local-slice->start to skip '*'
2236     #   initialize defaults: base=0, index=4, scale=0, disp=0
2237     #   if (*local-slice->start != '(') {
2238     #     local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
2239     #     base = get-slice(Registers, local-slice, row-size=12)
2240     #     return
2241     #   }
2242     #   # compound expressions
2243     #   skip whitespace
2244     #   read register into base
2245     #   skip whitespace
2246     #   if (*local-slice->start == ')') goto end
2247     #   if (*local-slice->start == '-') goto displacement
2248     #   if (*local-slice->start != '+') goto error1
2249     #   ++local-slice->start to skip '+'
2250     #   skip whitespace
2251     #   if next 3 characters don't make a register, goto displacement
2252     #   read register into index
2253     #   skip whitespace
2254     #   if (*local-slice->start == ')') goto end
2255     #   if (*local-slice->start == '<') {
2256     #     ++local-slice->start to skip '<'
2257     #     if (*local-slice->start != '<') goto error2
2258     #     ++local-slice->start to skip '<'
2259     #     skip whitespace
2260     #     read integer into scale
2261     #     skip whitespace
2262     #     if (*local-slice->start == ')') goto end
2263     #   }
2264     #   if (*local-slice->start not in '+' '-') goto error3
2265     # displacement:
2266     #   read integer into disp
2267     #   skip whitespace
2268     #   if (*local-slice->start != ')') goto error4
2269     # . prologue
2270     55/push-ebp
2271     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2272     # . save registers
2273     56/push-esi
2274     57/push-edi
2275     # var local-slice/esi: slice = {word-slice->start, word-slice->end}
2276     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2277     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2278     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2279     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
2280     # ++local-slice->start to skip '*'
2281     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2282     # initialize defaults
2283     # base is in edi; we'll move it to eax just before we return
2284     bf/copy-to-edi  0/imm32
2285     b9/copy-to-ecx  4/imm32/no-index
2286     ba/copy-to-edx  0/imm32/.scale
2287     bb/copy-to-ebx  0/imm32/disp
2288 $parse-effective-address:check-for-simple-register:
2289     # if (*local-slice->start == '(') goto compound expression
2290     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2291     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2292     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2293     3d/compare-eax-and  0x28/imm32/open-paren
2294     74/jump-if-=  $parse-effective-address:compound-expression/disp8
2295 $parse-effective-address:simple-register:
2296     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
2297     # . . push args
2298     56/push-esi
2299     68/push  0x2f/imm32/slash
2300     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2301     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2302     # . . call
2303     e8/call  next-token-from-slice/disp32
2304     # . . discard args
2305     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2306     # base = get-slice(Registers, local-slice, row-size=12)
2307     # . eax = get-slice(Registers, local-slice, row-size=12)
2308     # . . push args
2309     68/push  "Registers"/imm32
2310     68/push  0xc/imm32/row-size
2311     56/push-esi
2312     68/push  Registers/imm32
2313     # . . call
2314     e8/call  get-slice/disp32
2315     # . . discard args
2316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2317     # . base = *eax
2318     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy *eax to edi
2319     # return
2320     e9/jump  $parse-effective-address:end/disp32
2321 $parse-effective-address:compound-expression:
2322     # ++local-slice->start to skip '('
2323     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2324     # skip whitespace
2325     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2326     # . . push args
2327     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2328     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2329     # . . call
2330     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2331     # . . discard args
2332     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2333     # . local-slice->start = eax
2334     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2335     # read register into base
2336     # . eax = next-register(local-slice)
2337     # . . push args
2338     56/push-esi
2339     # . . call
2340     e8/call  next-register/disp32
2341     # . . discard args
2342     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2343     # . edi = *eax
2344     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy *eax to edi
2345     # skip whitespace
2346     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2347     # . . push args
2348     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2349     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2350     # . . call
2351     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2352     # . . discard args
2353     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2354     # . local-slice->start = eax
2355     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2356     # if (*local-slice->start == ')') goto end
2357     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2358     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2359     3d/compare-eax-and  0x29/imm32/close-paren
2360     0f 84/jump-if-=  $parse-effective-address:end/disp32
2361     # if (*local-slice->start == '-') goto displacement
2362     3d/compare-eax-and  0x2d/imm32/minus
2363     0f 84/jump-if-=  $parse-effective-address:displacement/disp32
2364     # if (*local-slice->start != '+') goto error1
2365     3d/compare-eax-and  0x2b/imm32/plus
2366     0f 85/jump-if-!=  $parse-effective-address:error1/disp32
2367 $parse-effective-address:check-for-index:
2368     # ++local-slice->start to skip '+'
2369     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2370     # skip whitespace
2371     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2372     # . . push args
2373     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2374     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2375     # . . call
2376     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2377     # . . discard args
2378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2379     # . local-slice->start = eax
2380     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2381 $parse-effective-address:resolve-ambiguity:
2382     # if next 3 characters don't make a register, goto displacement
2383     # . spill ecx
2384     51/push-ecx
2385     # . var tmp/ecx = {local-slice->start, local-slice->start+3}
2386     # . . ecx = local-slice->start
2387     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
2388     # . . eax = local-slice->start+3
2389     05/add-to-eax  3/imm32
2390     # . . push
2391     50/push-eax
2392     51/push-ecx
2393     # . . copy esp to ecx
2394     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2395     # . eax = maybe-get-slice(Register, tmp, row-size=12)
2396     # . . push args
2397     68/push  0xc/imm32/row-size
2398     51/push-ecx
2399     68/push  Registers/imm32
2400     # . . call
2401     e8/call  maybe-get-slice/disp32
2402     # . . discard args
2403     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2404     # . reclaim tmp
2405     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2406     # . restore ecx
2407     59/pop-to-ecx
2408     # . if (eax == 0) goto displacement
2409     3d/compare-eax-and  0/imm32
2410     0f 84/jump-if-=  $parse-effective-address:displacement/disp32
2411 $parse-effective-address:index:
2412     # read register into index
2413     # . eax = next-register(local-slice)
2414     # . . push args
2415     56/push-esi
2416     # . . call
2417     e8/call  next-register/disp32
2418     # . . discard args
2419     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2420     # . ecx = *eax
2421     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2422     # skip whitespace
2423     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2424     # . . push args
2425     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2426     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2427     # . . call
2428     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2429     # . . discard args
2430     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2431     # . local-slice->start = eax
2432     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2433     # if (*local-slice->start == ')') goto end
2434     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2435     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2436     3d/compare-eax-and  0x29/imm32/close-paren
2437     0f 84/jump-if-=  $parse-effective-address:end/disp32
2438 $parse-effective-address:check-for-scale:
2439     # if (*local-slice->start != '<') goto next check
2440     3d/compare-eax-and  0x3c/imm32/less-than
2441     75/jump-if-!=  $parse-effective-address:check-for-displacement/disp8
2442     # ++local-slice->start to skip '<'
2443     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
2444     # if (*local-slice->start != '<') goto error2
2445     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2446     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2447     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2448     3d/compare-eax-and  0x3c/imm32/less-than
2449     0f 85/jump-if-!=  $parse-effective-address:error2/disp32
2450     # ++local-slice->start to skip '<'
2451     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
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 $parse-effective-address:scale:
2464     # read positive integer into scale
2465     # . eax = next-positive-hex-int(local-slice)
2466     # . . push args
2467     56/push-esi
2468     # . . call
2469     e8/call  next-positive-hex-int/disp32
2470     # . . discard args
2471     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2472     # . edx = eax
2473     89/copy                         3/mod/direct    2/rm32/edx    .           .             .           0/r32/eax   .               .                 # copy eax to edx
2474     # skip whitespace
2475     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2476     # . . push args
2477     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2478     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2479     # . . call
2480     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2481     # . . discard args
2482     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2483     # . local-slice->start = eax
2484     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2485     # if (*local-slice->start == ')') goto end
2486     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2487     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2488     3d/compare-eax-and  0x29/imm32/close-paren
2489     74/jump-if-=  $parse-effective-address:end/disp8
2490 $parse-effective-address:check-for-displacement:
2491     # if (*local-slice->start not in '+' '-') goto error3
2492     3d/compare-eax-and  0x2b/imm32/plus
2493     74/jump-if-=  $parse-effective-address:displacement/disp8
2494     3d/compare-eax-and  0x2d/imm32/minus
2495     74/jump-if-=  $parse-effective-address:displacement/disp8
2496     e9/jump  $parse-effective-address:error3/disp32
2497 $parse-effective-address:displacement:
2498     # read integer into disp
2499     # . eax = next-hex-int(local-slice)
2500     # . . push args
2501     56/push-esi
2502     # . . call
2503     e8/call  next-hex-int/disp32
2504     # . . discard args
2505     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2506     # . ebx = eax
2507     89/copy                         3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # copy eax to ebx
2508     # skip whitespace
2509     # . eax = skip-chars-matching-whitespace-in-slice(local-slice->start, local-slice->end)
2510     # . . push args
2511     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
2512     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2513     # . . call
2514     e8/call  skip-chars-matching-whitespace-in-slice/disp32
2515     # . . discard args
2516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2517     # . local-slice->start = eax
2518     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy eax to *esi
2519     # if (*local-slice->start != ')') goto error4
2520     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
2521     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
2522     3d/compare-eax-and  0x29/imm32/close-paren
2523     0f 85/jump-if-!=  $parse-effective-address:error4/disp32
2524 $parse-effective-address:end:
2525     # return base in eax
2526     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
2527     # . reclaim locals
2528     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2529     # . restore registers
2530     5f/pop-to-edi
2531     5e/pop-to-esi
2532     # . epilogue
2533     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2534     5d/pop-to-ebp
2535     c3/return
2536 
2537 $parse-effective-address:error1:
2538     # print(stderr, "error: unexpected character: " eax "\n")
2539     # . write-buffered(Stderr, "error: unexpected character: ")
2540     # . . push args
2541     68/push  "error: unexpected character: "/imm32
2542     68/push  Stderr/imm32
2543     # . . call
2544     e8/call  write-buffered/disp32
2545     # . . discard args
2546     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2547     # . write-int32-hex-buffered(out, eax)
2548     # . . push args
2549     50/push-eax
2550     68/push  Stderr/imm32
2551     # . . call
2552     e8/call  write-int32-hex-buffered/disp32
2553     # . . discard args
2554     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2555     # . write-buffered(Stderr, "\n")
2556     # . . push args
2557     68/push  Newline/imm32
2558     68/push  Stderr/imm32
2559     # . . call
2560     e8/call  write-buffered/disp32
2561     # . . discard args
2562     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2563     # . flush(Stderr)
2564     # . . push args
2565     68/push  Stderr/imm32
2566     # . . call
2567     e8/call  flush/disp32
2568     # . . discard args
2569     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2570     # . syscall(exit, 1)
2571     bb/copy-to-ebx  1/imm32
2572     e8/call  syscall_exit/disp32
2573     # never gets here
2574 
2575 $parse-effective-address:error2:
2576     # print(stderr, "error: '<' can only be followed by '<' but got: " eax "\n")
2577     # . write-buffered(Stderr, "error: '<' can only be followed by '<' but got: ")
2578     # . . push args
2579     68/push  "error: '<' can only be followed by '<' but got: "/imm32
2580     68/push  Stderr/imm32
2581     # . . call
2582     e8/call  write-buffered/disp32
2583     # . . discard args
2584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2585     # . write-int32-hex-buffered(out, eax)
2586     # . . push args
2587     50/push-eax
2588     68/push  Stderr/imm32
2589     # . . call
2590     e8/call  write-int32-hex-buffered/disp32
2591     # . . discard args
2592     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2593     # . write-buffered(Stderr, "\n")
2594     # . . push args
2595     68/push  Newline/imm32
2596     68/push  Stderr/imm32
2597     # . . call
2598     e8/call  write-buffered/disp32
2599     # . . discard args
2600     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2601     # . flush(Stderr)
2602     # . . push args
2603     68/push  Stderr/imm32
2604     # . . call
2605     e8/call  flush/disp32
2606     # . . discard args
2607     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2608     # . syscall(exit, 1)
2609     bb/copy-to-ebx  1/imm32
2610     e8/call  syscall_exit/disp32
2611     # never gets here
2612 
2613 $parse-effective-address:error3:
2614     # print(stderr, "error: unexpected character before displacement: " eax "\n")
2615     # . write-buffered(Stderr, "error: unexpected character before displacement: ")
2616     # . . push args
2617     68/push  "error: unexpected character before displacement: "/imm32
2618     68/push  Stderr/imm32
2619     # . . call
2620     e8/call  write-buffered/disp32
2621     # . . discard args
2622     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2623     # . write-int32-hex-buffered(out, eax)
2624     # . . push args
2625     50/push-eax
2626     68/push  Stderr/imm32
2627     # . . call
2628     e8/call  write-int32-hex-buffered/disp32
2629     # . . discard args
2630     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2631     # . write-buffered(Stderr, "\n")
2632     # . . push args
2633     68/push  Newline/imm32
2634     68/push  Stderr/imm32
2635     # . . call
2636     e8/call  write-buffered/disp32
2637     # . . discard args
2638     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2639     # . flush(Stderr)
2640     # . . push args
2641     68/push  Stderr/imm32
2642     # . . call
2643     e8/call  flush/disp32
2644     # . . discard args
2645     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2646     # . syscall(exit, 1)
2647     bb/copy-to-ebx  1/imm32
2648     e8/call  syscall_exit/disp32
2649     # never gets here
2650 
2651 $parse-effective-address:error4:
2652     # print(stderr, "error: unexpected character after displacement: " eax "; expected ')' to wrap up\n")
2653     # . write-buffered(Stderr, "error: unexpected character after displacement: ")
2654     # . . push args
2655     68/push  "error: unexpected character after displacement: "/imm32
2656     68/push  Stderr/imm32
2657     # . . call
2658     e8/call  write-buffered/disp32
2659     # . . discard args
2660     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2661     # . write-int32-hex-buffered(out, eax)
2662     # . . push args
2663     50/push-eax
2664     68/push  Stderr/imm32
2665     # . . call
2666     e8/call  write-int32-hex-buffered/disp32
2667     # . . discard args
2668     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2669     # . write-buffered(Stderr, "; expected ')' to wrap up\n")
2670     # . . push args
2671     68/push  "; expected ')' to wrap up\n"/imm32
2672     68/push  Stderr/imm32
2673     # . . call
2674     e8/call  write-buffered/disp32
2675     # . . discard args
2676     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2677     # . flush(Stderr)
2678     # . . push args
2679     68/push  Stderr/imm32
2680     # . . call
2681     e8/call  flush/disp32
2682     # . . discard args
2683     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2684     # . syscall(exit, 1)
2685     bb/copy-to-ebx  1/imm32
2686     e8/call  syscall_exit/disp32
2687     # never gets here
2688 
2689 # assumes 'in' starts with a register name, and returns pointer to its code
2690 # side-effect: modifies 'in' to scan past the initial register name
2691 next-register:  # in: (addr slice) -> reg/eax: int
2692     # . prologue
2693     55/push-ebp
2694     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2695     # . save registers
2696     51/push-ecx
2697     56/push-esi
2698     # esi = in
2699     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
2700     # var reg-slice/ecx: slice = {in->start, in->start + 3}
2701     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
2702     05/add-to-eax  3/imm32
2703     50/push-eax
2704     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
2705     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2706     # in->start += 3
2707     81          0/subop/add         0/mod/indirect  6/rm32/esi    .           .             .           .           .               3/imm32           # add to *esi
2708     # eax = get-slice(Registers, reg-slice, row-size=12)
2709     # . . push args
2710     68/push  "next-register"/imm32
2711     68/push  0xc/imm32/row-size
2712     51/push-ecx
2713     68/push  Registers/imm32
2714     # . . call
2715     e8/call  get-slice/disp32
2716     # . . discard args
2717     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
2718 $next-register:end:
2719     # . reclaim locals
2720     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
2721     # . restore registers
2722     5e/pop-to-esi
2723     59/pop-to-ecx
2724     # . epilogue
2725     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2726     5d/pop-to-ebp
2727     c3/return
2728 
2729 test-parse-effective-address-simple:
2730     # . prologue
2731     55/push-ebp
2732     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2733     # var slice/ecx = "*esi"
2734     b8/copy-to-eax  "*esi"/imm32
2735     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2736     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
2737     05/add-to-eax  4/imm32
2738     # . ecx = {eax, ecx}
2739     51/push-ecx
2740     50/push-eax
2741     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2742     # eax, ecx, edx, ebx = parse-effective-address(slice)
2743     # . . push args
2744     51/push-ecx
2745     # . . call
2746     e8/call  parse-effective-address/disp32
2747     # . . discard args
2748     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2749     # slice clobbered beyond this point
2750     # check-ints-equal(eax, 6, msg)
2751     # . . push args
2752     68/push  "F - test-parse-effective-address-simple/base"/imm32
2753     68/push  6/imm32/esi
2754     50/push-eax
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(ecx, 4, msg)
2760     # . . push args
2761     68/push  "F - test-parse-effective-address-simple/index"/imm32
2762     68/push  4/imm32/none
2763     51/push-ecx
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     # check-ints-equal(edx, 0, msg)
2769     # . . push args
2770     68/push  "F - test-parse-effective-address-simple/scale"/imm32
2771     68/push  0/imm32/none
2772     52/push-edx
2773     # . . call
2774     e8/call  check-ints-equal/disp32
2775     # . . discard args
2776     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2777     # check-ints-equal(ebx, 0, msg)
2778     # . . push args
2779     68/push  "F - test-parse-effective-address-simple/displacement"/imm32
2780     68/push  0/imm32/none
2781     53/push-ebx
2782     # . . call
2783     e8/call  check-ints-equal/disp32
2784     # . . discard args
2785     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2786     # . epilogue
2787     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2788     5d/pop-to-ebp
2789     c3/return
2790 
2791 test-parse-effective-address-base:
2792     # . prologue
2793     55/push-ebp
2794     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2795     # var slice/ecx = "*(esi  )"
2796     b8/copy-to-eax  "*(esi  )"/imm32
2797     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2798     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
2799     05/add-to-eax  4/imm32
2800     # . ecx = {eax, ecx}
2801     51/push-ecx
2802     50/push-eax
2803     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2804     # eax, ecx, edx, ebx = parse-effective-address(slice)
2805     # . . push args
2806     51/push-ecx
2807     # . . call
2808     e8/call  parse-effective-address/disp32
2809     # . . discard args
2810     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2811     # slice clobbered beyond this point
2812     # check-ints-equal(eax, 6, msg)
2813     # . . push args
2814     68/push  "F - test-parse-effective-address-base/base"/imm32
2815     68/push  6/imm32/esi
2816     50/push-eax
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(ecx, 4, msg)
2822     # . . push args
2823     68/push  "F - test-parse-effective-address-base/index"/imm32
2824     68/push  4/imm32/none
2825     51/push-ecx
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     # check-ints-equal(edx, 0, msg)
2831     # . . push args
2832     68/push  "F - test-parse-effective-address-base/scale"/imm32
2833     68/push  0/imm32/none
2834     52/push-edx
2835     # . . call
2836     e8/call  check-ints-equal/disp32
2837     # . . discard args
2838     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2839     # check-ints-equal(ebx, 0, msg)
2840     # . . push args
2841     68/push  "F - test-parse-effective-address-base/displacement"/imm32
2842     68/push  0/imm32/none
2843     53/push-ebx
2844     # . . call
2845     e8/call  check-ints-equal/disp32
2846     # . . discard args
2847     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2848     # . epilogue
2849     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2850     5d/pop-to-ebp
2851     c3/return
2852 
2853 test-parse-effective-address-base-displacement:
2854     # . prologue
2855     55/push-ebp
2856     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2857     # var slice/ecx = "*(esi+3)"
2858     b8/copy-to-eax  "*(esi+3)"/imm32
2859     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2860     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
2861     05/add-to-eax  4/imm32
2862     # . ecx = {eax, ecx}
2863     51/push-ecx
2864     50/push-eax
2865     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2866     # eax, ecx, edx, ebx = parse-effective-address(slice)
2867     # . . push args
2868     51/push-ecx
2869     # . . call
2870     e8/call  parse-effective-address/disp32
2871     # . . discard args
2872     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2873     # slice clobbered beyond this point
2874     # check-ints-equal(eax, 6, msg)
2875     # . . push args
2876     68/push  "F - test-parse-effective-address-base-displacement/base"/imm32
2877     68/push  6/imm32/esi
2878     50/push-eax
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(ecx, 4, msg)
2884     # . . push args
2885     68/push  "F - test-parse-effective-address-base-displacement/index"/imm32
2886     68/push  4/imm32/none
2887     51/push-ecx
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     # check-ints-equal(edx, 0, msg)
2893     # . . push args
2894     68/push  "F - test-parse-effective-address-base-displacement/scale"/imm32
2895     68/push  0/imm32/none
2896     52/push-edx
2897     # . . call
2898     e8/call  check-ints-equal/disp32
2899     # . . discard args
2900     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2901     # check-ints-equal(ebx, 3, msg)
2902     # . . push args
2903     68/push  "F - test-parse-effective-address-base-displacement/displacement"/imm32
2904     68/push  3/imm32
2905     53/push-ebx
2906     # . . call
2907     e8/call  check-ints-equal/disp32
2908     # . . discard args
2909     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2910     # . epilogue
2911     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2912     5d/pop-to-ebp
2913     c3/return
2914 
2915 test-parse-effective-address-base-negative-displacement:
2916     # . prologue
2917     55/push-ebp
2918     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2919     # var slice/ecx = "*(esi-3)"
2920     b8/copy-to-eax  "*(esi-3)"/imm32
2921     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2922     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
2923     05/add-to-eax  4/imm32
2924     # . ecx = {eax, ecx}
2925     51/push-ecx
2926     50/push-eax
2927     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2928     # eax, ecx, edx, ebx = parse-effective-address(slice)
2929     # . . push args
2930     51/push-ecx
2931     # . . call
2932     e8/call  parse-effective-address/disp32
2933     # . . discard args
2934     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2935     # slice clobbered beyond this point
2936     # check-ints-equal(eax, 6, msg)
2937     # . . push args
2938     68/push  "F - test-parse-effective-address-base-negative-displacement/base"/imm32
2939     68/push  6/imm32/esi
2940     50/push-eax
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(ecx, 4, msg)
2946     # . . push args
2947     68/push  "F - test-parse-effective-address-base-negative-displacement/index"/imm32
2948     68/push  4/imm32/none
2949     51/push-ecx
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     # check-ints-equal(edx, 0, msg)
2955     # . . push args
2956     68/push  "F - test-parse-effective-address-base-negative-displacement/scale"/imm32
2957     68/push  0/imm32/none
2958     52/push-edx
2959     # . . call
2960     e8/call  check-ints-equal/disp32
2961     # . . discard args
2962     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2963     # check-ints-equal(ebx, -3, msg)
2964     # . . push args
2965     68/push  "F - test-parse-effective-address-base-negative-displacement/displacement"/imm32
2966     68/push  -3/imm32
2967     53/push-ebx
2968     # . . call
2969     e8/call  check-ints-equal/disp32
2970     # . . discard args
2971     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
2972     # . epilogue
2973     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
2974     5d/pop-to-ebp
2975     c3/return
2976 
2977 test-parse-effective-address-base-index:
2978     # . prologue
2979     55/push-ebp
2980     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
2981     # var slice/ecx = "*(esi+ecx)"
2982     b8/copy-to-eax  "*(esi+ecx)"/imm32
2983     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
2984     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
2985     05/add-to-eax  4/imm32
2986     # . ecx = {eax, ecx}
2987     51/push-ecx
2988     50/push-eax
2989     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
2990     # eax, ecx, edx, ebx = parse-effective-address(slice)
2991     # . . push args
2992     51/push-ecx
2993     # . . call
2994     e8/call  parse-effective-address/disp32
2995     # . . discard args
2996     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
2997     # slice clobbered beyond this point
2998     # check-ints-equal(eax, 6, msg)
2999     # . . push args
3000     68/push  "F - test-parse-effective-address-base-index/base"/imm32
3001     68/push  6/imm32/esi
3002     50/push-eax
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(ecx, 1, msg)
3008     # . . push args
3009     68/push  "F - test-parse-effective-address-base-index/index"/imm32
3010     68/push  1/imm32/none
3011     51/push-ecx
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     # check-ints-equal(edx, 0, msg)
3017     # . . push args
3018     68/push  "F - test-parse-effective-address-base-index/scale"/imm32
3019     68/push  0/imm32/none
3020     52/push-edx
3021     # . . call
3022     e8/call  check-ints-equal/disp32
3023     # . . discard args
3024     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3025     # check-ints-equal(ebx, 0, msg)
3026     # . . push args
3027     68/push  "F - test-parse-effective-address-base-index/displacement"/imm32
3028     68/push  0/imm32
3029     53/push-ebx
3030     # . . call
3031     e8/call  check-ints-equal/disp32
3032     # . . discard args
3033     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3034     # . epilogue
3035     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3036     5d/pop-to-ebp
3037     c3/return
3038 
3039 test-parse-effective-address-base-index-scale:
3040     # . prologue
3041     55/push-ebp
3042     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3043     # var slice/ecx = "*(esi+ecx<<2)"
3044     b8/copy-to-eax  "*(esi+ecx<<2)"/imm32
3045     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3046     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
3047     05/add-to-eax  4/imm32
3048     # . ecx = {eax, ecx}
3049     51/push-ecx
3050     50/push-eax
3051     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3052     # eax, ecx, edx, ebx = parse-effective-address(slice)
3053     # . . push args
3054     51/push-ecx
3055     # . . call
3056     e8/call  parse-effective-address/disp32
3057     # . . discard args
3058     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3059     # slice clobbered beyond this point
3060     # check-ints-equal(eax, 6, msg)
3061     # . . push args
3062     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
3063     68/push  6/imm32/esi
3064     50/push-eax
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(ecx, 1, msg)
3070     # . . push args
3071     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
3072     68/push  1/imm32/none
3073     51/push-ecx
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     # check-ints-equal(edx, 2, msg)
3079     # . . push args
3080     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
3081     68/push  2/imm32
3082     52/push-edx
3083     # . . call
3084     e8/call  check-ints-equal/disp32
3085     # . . discard args
3086     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3087     # check-ints-equal(ebx, 0, msg)
3088     # . . push args
3089     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
3090     68/push  0/imm32
3091     53/push-ebx
3092     # . . call
3093     e8/call  check-ints-equal/disp32
3094     # . . discard args
3095     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3096     # . epilogue
3097     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3098     5d/pop-to-ebp
3099     c3/return
3100 
3101 test-parse-effective-address-base-index-scale-displacement:
3102     # . prologue
3103     55/push-ebp
3104     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3105     # var slice/ecx = "*(esi + ecx<<2 - 0x34)"
3106     b8/copy-to-eax  "*(esi + ecx<<2 - 0x34)"/imm32
3107     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
3108     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
3109     05/add-to-eax  4/imm32
3110     # . ecx = {eax, ecx}
3111     51/push-ecx
3112     50/push-eax
3113     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
3114     # eax, ecx, edx, ebx = parse-effective-address(slice)
3115     # . . push args
3116     51/push-ecx
3117     # . . call
3118     e8/call  parse-effective-address/disp32
3119     # . . discard args
3120     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3121     # slice clobbered beyond this point
3122     # check-ints-equal(eax, 6, msg)
3123     # . . push args
3124     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
3125     68/push  6/imm32/esi
3126     50/push-eax
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(ecx, 1, msg)
3132     # . . push args
3133     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
3134     68/push  1/imm32/none
3135     51/push-ecx
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     # check-ints-equal(edx, 2, msg)
3141     # . . push args
3142     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
3143     68/push  2/imm32
3144     52/push-edx
3145     # . . call
3146     e8/call  check-ints-equal/disp32
3147     # . . discard args
3148     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3149     # check-ints-equal(ebx, -0x34, msg)
3150     # . . push args
3151     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
3152     68/push  -0x34/imm32
3153     53/push-ebx
3154     # . . call
3155     e8/call  check-ints-equal/disp32
3156     # . . discard args
3157     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3158     # . epilogue
3159     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3160     5d/pop-to-ebp
3161     c3/return
3162 
3163 # Code generation:
3164 #   if base is esp, then goto emit-sib
3165 #   if base is ebp, then goto emit-sib
3166 #   if index is none and disp is 0, then mod = 0 and rm32 = base
3167 #   if index is none, then mod = 2 and rm32 = base and disp32 = disp
3168 # emit-sib:
3169 #   if index is not none, then mod = 2 and rm32 = 4 and base = base and index = index and disp32 = disp
3170 emit-indirect-mode:  # out: (addr buffered-file), base: int, index: int, scale: int, disp: int
3171     # . prologue
3172     55/push-ebp
3173     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3174 $emit-indirect-mode:check-for-ebp:
3175     # if (base == 5) goto emit-sib
3176     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       5/imm32           # compare *(ebp+12)
3177     74/jump-if-=  $emit-indirect-mode:emit-sib/disp8
3178 $emit-indirect-mode:check-for-esp:
3179     # if (base == 4) goto emit-sib
3180     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       4/imm32           # compare *(ebp+12)
3181     74/jump-if-=  $emit-indirect-mode:emit-sib/disp8
3182 $emit-indirect-mode:check-for-sib:
3183     # if (index == 4/none) goto next check
3184     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      4/imm32           # compare *(ebp+16)
3185     0f 84/jump-if-=  $emit-indirect-mode:check-for-disp/disp32
3186 $emit-indirect-mode:emit-sib:
3187     # emit(out, "2/mod/indirect 4/rm32/sib " base "/base " index "/index " scale "/scale " disp "/disp32")
3188     # . write-buffered(out, "2/mod/*+disp32 4/rm32/sib ")
3189     # . . push args
3190     68/push  "2/mod/*+disp32 4/rm32/sib "/imm32
3191     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3192     # . . call
3193     e8/call  write-buffered/disp32
3194     # . . discard args
3195     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3196     # . write-int32-hex-buffered(out, base)
3197     # . . push args
3198     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3199     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3200     # . . call
3201     e8/call  write-int32-hex-buffered/disp32
3202     # . . discard args
3203     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3204     # . write-buffered(out, "/base ")
3205     # . . push args
3206     68/push  "/base "/imm32
3207     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3208     # . . call
3209     e8/call  write-buffered/disp32
3210     # . . discard args
3211     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3212     # . write-int32-hex-buffered(out, index)
3213     # . . push args
3214     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x10/disp8      .                 # push *(ebp+16)
3215     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3216     # . . call
3217     e8/call  write-int32-hex-buffered/disp32
3218     # . . discard args
3219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3220     # . write-buffered(out, "/index ")
3221     # . . push args
3222     68/push  "/index "/imm32
3223     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3224     # . . call
3225     e8/call  write-buffered/disp32
3226     # . . discard args
3227     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3228     # . write-int32-hex-buffered(out, scale)
3229     # . . push args
3230     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x14/disp8      .                 # push *(ebp+20)
3231     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3232     # . . call
3233     e8/call  write-int32-hex-buffered/disp32
3234     # . . discard args
3235     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3236     # . write-buffered(out, "/scale ")
3237     # . . push args
3238     68/push  "/scale "/imm32
3239     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3240     # . . call
3241     e8/call  write-buffered/disp32
3242     # . . discard args
3243     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3244     # . write-int32-hex-buffered(out, disp)
3245     # . . push args
3246     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3247     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3248     # . . call
3249     e8/call  write-int32-hex-buffered/disp32
3250     # . . discard args
3251     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3252     # . write-buffered(out, "/disp32")
3253     # . . push args
3254     68/push  "/disp32"/imm32
3255     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3256     # . . call
3257     e8/call  write-buffered/disp32
3258     # . . discard args
3259     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3260     e9/jump  $emit-indirect-mode:end/disp32
3261 $emit-indirect-mode:check-for-disp:
3262     # if (disp == 0) goto next check
3263     81          7/subop/compare     1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      0/imm32           # compare *(ebp+24)
3264     74/jump-if-=  $emit-indirect-mode:emit-indirect/disp8
3265 $emit-indirect-mode:emit-disp:
3266     # emit(out, "2/mod/*+disp32 " base "/rm32 " disp "/disp32")
3267     # . write-buffered(out, "2/mod/*+disp32 ")
3268     # . . push args
3269     68/push  "2/mod/*+disp32 "/imm32
3270     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3271     # . . call
3272     e8/call  write-buffered/disp32
3273     # . . discard args
3274     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3275     # . write-int32-hex-buffered(out, base)
3276     # . . push args
3277     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3278     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3279     # . . call
3280     e8/call  write-int32-hex-buffered/disp32
3281     # . . discard args
3282     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3283     # . write-buffered(out, "/rm32 ")
3284     # . . push args
3285     68/push  "/rm32 "/imm32
3286     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3287     # . . call
3288     e8/call  write-buffered/disp32
3289     # . . discard args
3290     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3291     # . write-int32-hex-buffered(out, disp)
3292     # . . push args
3293     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0x18/disp8      .                 # push *(ebp+24)
3294     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3295     # . . call
3296     e8/call  write-int32-hex-buffered/disp32
3297     # . . discard args
3298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3299     # . write-buffered(out, "/disp32")
3300     # . . push args
3301     68/push  "/disp32"/imm32
3302     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3303     # . . call
3304     e8/call  write-buffered/disp32
3305     # . . discard args
3306     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3307     eb/jump  $emit-indirect-mode:end/disp8
3308 $emit-indirect-mode:emit-indirect:
3309     # emit(out, "0/mod/indirect " base "/rm32")
3310     # . write-buffered(out, "0/mod/indirect ")
3311     # . . push args
3312     68/push  "0/mod/indirect "/imm32
3313     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3314     # . . call
3315     e8/call  write-buffered/disp32
3316     # . . discard args
3317     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3318     # . write-int32-hex-buffered(out, base)
3319     # . . push args
3320     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
3321     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3322     # . . call
3323     e8/call  write-int32-hex-buffered/disp32
3324     # . . discard args
3325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3326     # . write-buffered(out, "/rm32")
3327     # . . push args
3328     68/push  "/rm32"/imm32
3329     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3330     # . . call
3331     e8/call  write-buffered/disp32
3332     # . . discard args
3333     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3334 $emit-indirect-mode:end:
3335     # . epilogue
3336     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3337     5d/pop-to-ebp
3338     c3/return
3339 
3340 test-emit-indirect-mode:
3341     # . prologue
3342     55/push-ebp
3343     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3344     # setup
3345     # . clear-stream(_test-output-stream)
3346     # . . push args
3347     68/push  _test-output-stream/imm32
3348     # . . call
3349     e8/call  clear-stream/disp32
3350     # . . discard args
3351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3352     # . clear-stream($_test-output-buffered-file->buffer)
3353     # . . push args
3354     68/push  $_test-output-buffered-file->buffer/imm32
3355     # . . call
3356     e8/call  clear-stream/disp32
3357     # . . discard args
3358     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3359     # emit-indirect-mode(_test-output-buffered-file, 0, 4/none, 0, 0)
3360     # . . write args
3361     68/push  0/imm32/.disp
3362     68/push  0/imm32/.scale
3363     68/push  4/imm32/.index/none
3364     68/push  0/imm32/.base
3365     68/push  _test-output-buffered-file/imm32
3366     # . . call
3367     e8/call  emit-indirect-mode/disp32
3368     # . . discard args
3369     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3370     # . flush(_test-output-buffered-file)
3371     # . . push args
3372     68/push  _test-output-buffered-file/imm32
3373     # . . call
3374     e8/call  flush/disp32
3375     # . . discard args
3376     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3377 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3403     # check-stream-equal(_test-output-stream, "0/mod/indirect 0/rm32", msg)
3404     # . . push args
3405     68/push  "F - test-emit-indirect-mode"/imm32
3406     68/push  "0/mod/indirect 0x00000000/rm32"/imm32
3407     68/push  _test-output-stream/imm32
3408     # . . call
3409     e8/call  check-stream-equal/disp32
3410     # . . discard args
3411     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3412     # . epilogue
3413     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3414     5d/pop-to-ebp
3415     c3/return
3416 
3417 test-emit-indirect-mode-2:
3418     # . prologue
3419     55/push-ebp
3420     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3421     # setup
3422     # . clear-stream(_test-output-stream)
3423     # . . push args
3424     68/push  _test-output-stream/imm32
3425     # . . call
3426     e8/call  clear-stream/disp32
3427     # . . discard args
3428     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3429     # . clear-stream($_test-output-buffered-file->buffer)
3430     # . . push args
3431     68/push  $_test-output-buffered-file->buffer/imm32
3432     # . . call
3433     e8/call  clear-stream/disp32
3434     # . . discard args
3435     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3436     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 0)
3437     # . . write args
3438     68/push  0/imm32/.disp
3439     68/push  0/imm32/.scale
3440     68/push  4/imm32/.index/none
3441     68/push  7/imm32/.base
3442     68/push  _test-output-buffered-file/imm32
3443     # . . call
3444     e8/call  emit-indirect-mode/disp32
3445     # . . discard args
3446     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3447     # . flush(_test-output-buffered-file)
3448     # . . push args
3449     68/push  _test-output-buffered-file/imm32
3450     # . . call
3451     e8/call  flush/disp32
3452     # . . discard args
3453     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3454 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3480     # check-stream-equal(_test-output-stream, "0/mod/indirect 7/rm32", msg)
3481     # . . push args
3482     68/push  "F - test-emit-indirect-mode-2"/imm32
3483     68/push  "0/mod/indirect 0x00000007/rm32"/imm32
3484     68/push  _test-output-stream/imm32
3485     # . . call
3486     e8/call  check-stream-equal/disp32
3487     # . . discard args
3488     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3489     # . epilogue
3490     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3491     5d/pop-to-ebp
3492     c3/return
3493 
3494 test-emit-indirect-mode-with-disp:
3495     # . prologue
3496     55/push-ebp
3497     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3498     # setup
3499     # . clear-stream(_test-output-stream)
3500     # . . push args
3501     68/push  _test-output-stream/imm32
3502     # . . call
3503     e8/call  clear-stream/disp32
3504     # . . discard args
3505     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3506     # . clear-stream($_test-output-buffered-file->buffer)
3507     # . . push args
3508     68/push  $_test-output-buffered-file->buffer/imm32
3509     # . . call
3510     e8/call  clear-stream/disp32
3511     # . . discard args
3512     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3513     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 4)
3514     # . . write args
3515     68/push  4/imm32/.disp
3516     68/push  0/imm32/.scale
3517     68/push  4/imm32/.index/none
3518     68/push  6/imm32/.base
3519     68/push  _test-output-buffered-file/imm32
3520     # . . call
3521     e8/call  emit-indirect-mode/disp32
3522     # . . discard args
3523     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3524     # . flush(_test-output-buffered-file)
3525     # . . push args
3526     68/push  _test-output-buffered-file/imm32
3527     # . . call
3528     e8/call  flush/disp32
3529     # . . discard args
3530     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3531 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3557     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 4/disp32", msg)
3558     # . . push args
3559     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
3560     68/push  "2/mod/*+disp32 0x00000006/rm32 0x00000004/disp32"/imm32
3561     68/push  _test-output-stream/imm32
3562     # . . call
3563     e8/call  check-stream-equal/disp32
3564     # . . discard args
3565     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3566     # . epilogue
3567     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3568     5d/pop-to-ebp
3569     c3/return
3570 
3571 test-emit-indirect-mode-with-disp-negative:
3572     # . prologue
3573     55/push-ebp
3574     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3575     # setup
3576     # . clear-stream(_test-output-stream)
3577     # . . push args
3578     68/push  _test-output-stream/imm32
3579     # . . call
3580     e8/call  clear-stream/disp32
3581     # . . discard args
3582     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3583     # . clear-stream($_test-output-buffered-file->buffer)
3584     # . . push args
3585     68/push  $_test-output-buffered-file->buffer/imm32
3586     # . . call
3587     e8/call  clear-stream/disp32
3588     # . . discard args
3589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3590     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, -4)
3591     # . . write args
3592     68/push  -4/imm32/.disp
3593     68/push  0/imm32/.scale
3594     68/push  4/imm32/.index/none
3595     68/push  6/imm32/.base
3596     68/push  _test-output-buffered-file/imm32
3597     # . . call
3598     e8/call  emit-indirect-mode/disp32
3599     # . . discard args
3600     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3601     # . flush(_test-output-buffered-file)
3602     # . . push args
3603     68/push  _test-output-buffered-file/imm32
3604     # . . call
3605     e8/call  flush/disp32
3606     # . . discard args
3607     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3608 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3634     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 -4/disp32", msg)
3635     # . . push args
3636     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
3637     68/push  "2/mod/*+disp32 0x00000006/rm32 0xfffffffc/disp32"/imm32
3638     68/push  _test-output-stream/imm32
3639     # . . call
3640     e8/call  check-stream-equal/disp32
3641     # . . discard args
3642     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3643     # . epilogue
3644     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3645     5d/pop-to-ebp
3646     c3/return
3647 
3648 test-emit-indirect-mode-with-sib:
3649     # . prologue
3650     55/push-ebp
3651     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3652     # setup
3653     # . clear-stream(_test-output-stream)
3654     # . . push args
3655     68/push  _test-output-stream/imm32
3656     # . . call
3657     e8/call  clear-stream/disp32
3658     # . . discard args
3659     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3660     # . clear-stream($_test-output-buffered-file->buffer)
3661     # . . push args
3662     68/push  $_test-output-buffered-file->buffer/imm32
3663     # . . call
3664     e8/call  clear-stream/disp32
3665     # . . discard args
3666     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3667     # emit-indirect-mode(_test-output-buffered-file, 6/base, 1/index, 2/scale, 4/disp)
3668     # . . write args
3669     68/push  4/imm32/.disp
3670     68/push  2/imm32/.scale
3671     68/push  1/imm32/.index
3672     68/push  6/imm32/.base
3673     68/push  _test-output-buffered-file/imm32
3674     # . . call
3675     e8/call  emit-indirect-mode/disp32
3676     # . . discard args
3677     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3678     # . flush(_test-output-buffered-file)
3679     # . . push args
3680     68/push  _test-output-buffered-file/imm32
3681     # . . call
3682     e8/call  flush/disp32
3683     # . . discard args
3684     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3685 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3711     # check-stream-equal(_test-output-stream, "2/mod/indirect 4/rm32/sib 6/base 1/index 2/scale 4/disp", msg)
3712     # . . push args
3713     68/push  "F - test-emit-indirect-mode-with-sib"/imm32
3714     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000006/base 0x00000001/index 0x00000002/scale 0x00000004/disp32"/imm32
3715     68/push  _test-output-stream/imm32
3716     # . . call
3717     e8/call  check-stream-equal/disp32
3718     # . . discard args
3719     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3720     # . epilogue
3721     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3722     5d/pop-to-ebp
3723     c3/return
3724 
3725 test-emit-indirect-mode-ebp:
3726     # . prologue
3727     55/push-ebp
3728     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3729     # setup
3730     # . clear-stream(_test-output-stream)
3731     # . . push args
3732     68/push  _test-output-stream/imm32
3733     # . . call
3734     e8/call  clear-stream/disp32
3735     # . . discard args
3736     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3737     # . clear-stream($_test-output-buffered-file->buffer)
3738     # . . push args
3739     68/push  $_test-output-buffered-file->buffer/imm32
3740     # . . call
3741     e8/call  clear-stream/disp32
3742     # . . discard args
3743     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3744     # emit-indirect-mode(_test-output-buffered-file, 5/base, 0/index, 0/scale, 0/disp)
3745     # . . write args
3746     68/push  0/imm32/.disp
3747     68/push  0/imm32/.scale
3748     68/push  0/imm32/.index
3749     68/push  5/imm32/.base/ebp
3750     68/push  _test-output-buffered-file/imm32
3751     # . . call
3752     e8/call  emit-indirect-mode/disp32
3753     # . . discard args
3754     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3755     # . flush(_test-output-buffered-file)
3756     # . . push args
3757     68/push  _test-output-buffered-file/imm32
3758     # . . call
3759     e8/call  flush/disp32
3760     # . . discard args
3761     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3762 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3788     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 4/rm32/sib 5/base/ebp 0/index 0/scale 0/disp32", msg)
3789     # . . push args
3790     68/push  "F - test-emit-indirect-mode-ebp"/imm32
3791     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000005/base 0x00000000/index 0x00000000/scale 0x00000000/disp32"/imm32
3792     68/push  _test-output-stream/imm32
3793     # . . call
3794     e8/call  check-stream-equal/disp32
3795     # . . discard args
3796     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3797     # . epilogue
3798     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3799     5d/pop-to-ebp
3800     c3/return
3801 
3802 test-emit-indirect-mode-esp:
3803     # . prologue
3804     55/push-ebp
3805     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3806     # setup
3807     # . clear-stream(_test-output-stream)
3808     # . . push args
3809     68/push  _test-output-stream/imm32
3810     # . . call
3811     e8/call  clear-stream/disp32
3812     # . . discard args
3813     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3814     # . clear-stream($_test-output-buffered-file->buffer)
3815     # . . push args
3816     68/push  $_test-output-buffered-file->buffer/imm32
3817     # . . call
3818     e8/call  clear-stream/disp32
3819     # . . discard args
3820     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3821     # emit-indirect-mode(_test-output-buffered-file, 4/base, 0/index, 0/scale, 0/disp)
3822     # . . write args
3823     68/push  0/imm32/.disp
3824     68/push  0/imm32/.scale
3825     68/push  0/imm32/.index
3826     68/push  4/imm32/.base/esp
3827     68/push  _test-output-buffered-file/imm32
3828     # . . call
3829     e8/call  emit-indirect-mode/disp32
3830     # . . discard args
3831     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x14/imm32        # add to esp
3832     # . flush(_test-output-buffered-file)
3833     # . . push args
3834     68/push  _test-output-buffered-file/imm32
3835     # . . call
3836     e8/call  flush/disp32
3837     # . . discard args
3838     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
3839 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3865     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 4/rm32/sib 4/base/ebp 0/index 0/scale 0/disp32", msg)
3866     # . . push args
3867     68/push  "F - test-emit-indirect-mode-esp"/imm32
3868     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000004/base 0x00000000/index 0x00000000/scale 0x00000000/disp32"/imm32
3869     68/push  _test-output-stream/imm32
3870     # . . call
3871     e8/call  check-stream-equal/disp32
3872     # . . discard args
3873     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3874     # . epilogue
3875     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3876     5d/pop-to-ebp
3877     c3/return
3878 
3879 disp32-mode?:  # in: (addr slice) -> reg/eax: boolean
3880     # . prologue
3881     55/push-ebp
3882     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3883     # . save registers
3884     56/push-esi
3885     57/push-edi
3886     # var local-slice/esi: slice = {in->start, in->end}
3887     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
3888     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3889     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3890     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
3891     # ++local-slice->start to skip '*'
3892     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
3893     # if (*local-slice->start == '(') return false
3894     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
3895     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           0/r32/AL    .               .                 # copy byte at *eax to AL
3896     81          4/subop/and         3/mod/direct    0/rm32/eax    .           .             .           .           .               0xff/imm32        # bitwise and of eax
3897     3d/compare-eax-and  0x28/imm32/open-paren
3898     74/jump-if-=  $disp32-mode?:false/disp8
3899 $disp32-mode?:check-for-register:
3900     # local-slice = next-token-from-slice(local-slice->start, local-slice->end, "/")
3901     # . . push args
3902     56/push-esi
3903     68/push  0x2f/imm32/slash
3904     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3905     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3906     # . . call
3907     e8/call  next-token-from-slice/disp32
3908     # . . discard args
3909     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
3910     # reg-num/eax = maybe-get-slice(Registers, local-slice, row-size=12)
3911     # . . push args
3912     68/push  0xc/imm32/row-size
3913     56/push-esi
3914     68/push  Registers/imm32
3915     # . . cal
3916     e8/call  maybe-get-slice/disp32
3917     # . . discard args
3918     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
3919     # if (eax != 0) return false
3920     3d/compare-eax-and  0/imm32
3921     75/jump-if-!=  $disp32-mode?:false/disp8
3922     # return true
3923     b8/copy-to-eax  1/imm32/true
3924     eb/jump  $disp32-mode?:end/disp8
3925 $disp32-mode?:false:
3926     b8/copy-to-eax  0/imm32/false
3927 $disp32-mode?:end:
3928     # . reclaim locals
3929     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3930     # . restore registers
3931     5f/pop-to-edi
3932     5e/pop-to-esi
3933     # . epilogue
3934     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3935     5d/pop-to-ebp
3936     c3/return
3937 
3938 emit-indirect-disp32:  # out: (addr buffered-file), word-slice: (addr slice)
3939     # . prologue
3940     55/push-ebp
3941     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3942     # . save registers
3943     56/push-esi
3944     # var local-slice/esi: slice = {in->start, in->end}
3945     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   0xc/disp8       .                 # copy *(ebp+12) to esi
3946     ff          6/subop/push        1/mod/*+disp8   6/rm32/esi    .           .             .           .           4/disp8         .                 # push *(esi+4)
3947     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
3948     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
3949     # ++local-slice->start to skip '*'
3950     ff          0/subop/increment   0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # increment *esi
3951     # write-buffered(out, "0/mod/indirect 5/rm32/.disp32 ")
3952     # . . push args
3953     68/push  "0/mod/indirect 5/rm32/.disp32 "/imm32
3954     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3955     # . . call
3956     e8/call  write-buffered/disp32
3957     # . . discard args
3958     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3959     # write-slice-buffered(out, local-slice)
3960     # . . push args
3961     56/push-esi
3962     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3963     # . . call
3964     e8/call  write-slice-buffered/disp32
3965     # . . discard args
3966     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3967     # write-buffered(out, "/disp32")
3968     # . . push args
3969     68/push  "/disp32"/imm32
3970     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
3971     # . . call
3972     e8/call  write-buffered/disp32
3973     # . . discard args
3974     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3975 $emit-indirect-disp32:end:
3976     # . reclaim locals
3977     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
3978     # . restore registers
3979     5e/pop-to-esi
3980     # . epilogue
3981     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
3982     5d/pop-to-ebp
3983     c3/return
3984 
3985 # assumes 'in' starts with optional '+' or '-', optional whitespace, and an unsigned integer
3986 # returns the value of the integer
3987 # side-effect: modifies 'in' to skip past the integer
3988 next-hex-int:  # in: (addr slice) -> result/eax
3989     # . prologue
3990     55/push-ebp
3991     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
3992     # . save registers
3993     51/push-ecx
3994     52/push-edx
3995     53/push-ebx
3996     56/push-esi
3997     57/push-edi
3998     # result/edi = 0
3999     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
4000     # esi = in
4001     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
4002     # edx = in->end
4003     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
4004     # curr/ecx = in->start
4005     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
4006     # negate?/ebx = false
4007     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4008     # eax = *curr
4009     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4010     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4011 $next-hex-int:positive:
4012     # if (*curr == '+') ++curr
4013     3d/compare-eax-and  0x2b/imm32/+
4014     75/jump-if-!=  $next-hex-int:negative/disp8
4015     # . ++curr
4016     41/increment-ecx
4017     eb/jump  $next-hex-int:skip-whitespace/disp8
4018 $next-hex-int:negative:
4019     # else if (*curr == '-') ++curr, negate = true
4020     3d/compare-eax-and  0x2d/imm32/-
4021     75/jump-if-!=  $next-hex-int:skip-whitespace/disp8
4022 $next-hex-int:need-to-negate:
4023     # . ++curr
4024     41/increment-ecx
4025     # . negate = true
4026     bb/copy-to-ebx  1/imm32/true
4027     # fall through
4028 $next-hex-int:skip-whitespace:
4029     # spill eax
4030     50/push-eax
4031     # eax = skip-chars-matching-whitespace-in-slice(word-slice->start, word-slice->end)
4032     # . . push args
4033     52/push-edx
4034     51/push-ecx
4035     # . . call
4036     e8/call  skip-chars-matching-whitespace-in-slice/disp32
4037     # . . discard args
4038     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4039     # ecx = eax
4040     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy eax to ecx
4041     # restore eax
4042     58/pop-to-eax
4043 $next-hex-int:initial-0:
4044     # skip past leading '0x'
4045     # . if (*curr != '0') jump to loop
4046     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4047     3d/compare-eax-and  0x30/imm32/0
4048     75/jump-if-!=  $next-hex-int:loop/disp8
4049     # . ++curr
4050     41/increment-ecx
4051 $next-hex-int:initial-0x:
4052     # . if (curr >= in->end) return result
4053     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4054     73/jump-if-addr>=  $next-hex-int:end/disp8
4055     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
4056     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4057     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4058     3d/compare-eax-and  0x78/imm32/x
4059     75/jump-if-!=  $next-hex-int:loop/disp8
4060     # . ++curr
4061     41/increment-ecx
4062 $next-hex-int:loop:
4063     # if (curr >= in->end) break
4064     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4065     73/jump-if-addr>=  $next-hex-int:break/disp8
4066     # if (!is-hex-digit?(*curr)) break
4067     # . eax = *curr
4068     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4069     # . eax = is-hex-digit?(*curr)
4070     # . . push args
4071     50/push-eax
4072     # . . call
4073     e8/call  is-hex-digit?/disp32
4074     # . . discard args
4075     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4076     # . if (eax == false) break
4077     3d/compare-eax-and  0/imm32/false
4078     74/jump-if-=  $next-hex-int:break/disp8
4079     # eax = from-hex-char(*curr)
4080     # . . copy arg to eax
4081     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4082     # . . call
4083     e8/call  from-hex-char/disp32
4084     # result = result * 16 + eax
4085     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi left by 4 bits
4086     01/add                          3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to edi
4087     # ++curr
4088     41/increment-ecx
4089     # loop
4090     eb/jump  $next-hex-int:loop/disp8
4091 $next-hex-int:break:
4092     # if (negate?) result = -result
4093     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0/imm32/false     # compare ebx
4094     74/jump-if-=  $next-hex-int:end/disp8
4095 $next-hex-int:negate:
4096     f7          3/subop/negate      3/mod/direct    7/rm32/edi    .           .             .           .           .               .                 # negate edi
4097 $next-hex-int:end:
4098     # word-slice->start = curr
4099     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy ecx to *esi
4100     # return edi
4101     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
4102     # . restore registers
4103     5f/pop-to-edi
4104     5e/pop-to-esi
4105     5b/pop-to-ebx
4106     5a/pop-to-edx
4107     59/pop-to-ecx
4108     # . epilogue
4109     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4110     5d/pop-to-ebp
4111     c3/return
4112 
4113 $next-hex-int:abort:
4114     # . _write(2/stderr, error)
4115     # . . push args
4116     68/push  "next-hex-int: invalid hex char: "/imm32
4117     68/push  2/imm32/stderr
4118     # . . call
4119     e8/call  _write/disp32
4120     # . . discard args
4121     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4122     # . clear-stream($Stderr->buffer)
4123     # . . save eax
4124     50/push-eax
4125     # . . push args
4126     68/push  $Stderr->buffer/imm32
4127     # . . call
4128     e8/call  clear-stream/disp32
4129     # . . discard args
4130     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4131     # . . restore eax
4132     58/pop-to-eax
4133     # . write-int32-hex-buffered(Stderr, eax)
4134     # . . push args
4135     50/push-eax
4136     68/push  Stderr/imm32
4137     # . . call
4138     e8/call  write-int32-hex-buffered/disp32
4139     # . . discard args
4140     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4141     # . flush(Stderr)
4142     # . . push args
4143     68/push  Stderr/imm32
4144     # . . call
4145     e8/call  flush/disp32
4146     # . . discard args
4147     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4148     # . _write(2/stderr, "\n")
4149     # . . push args
4150     68/push  Newline/imm32
4151     68/push  2/imm32/stderr
4152     # . . call
4153     e8/call  _write/disp32
4154     # . . discard args
4155     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
4156     # . syscall(exit, 1)
4157     bb/copy-to-ebx  1/imm32
4158     e8/call  syscall_exit/disp32
4159     # never gets here
4160 
4161 test-next-hex-int-single-digit:
4162     # . prologue
4163     55/push-ebp
4164     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4165     # (eax..ecx) = "+a)"
4166     b8/copy-to-eax  "+a)"/imm32
4167     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4168     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
4169     05/add-to-eax  4/imm32
4170     # var slice/ecx = {eax, ecx}
4171     51/push-ecx
4172     50/push-eax
4173     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4174     # eax = next-hex-int(slice)
4175     # . . push args
4176     51/push-ecx
4177     # . . call
4178     e8/call  next-hex-int/disp32
4179     # . . discard args
4180     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4181     # check-ints-equal(eax, 0xa, msg)
4182     # . . push args
4183     68/push  "F - test-next-hex-int-single-digit"/imm32
4184     68/push  0xa/imm32
4185     50/push-eax
4186     # . . call
4187     e8/call  check-ints-equal/disp32
4188     # . . discard args
4189     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4190     # . epilogue
4191     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4192     5d/pop-to-ebp
4193     c3/return
4194 
4195 test-next-hex-int-multi-digit:
4196     # . prologue
4197     55/push-ebp
4198     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4199     # (eax..ecx) = "+ 34a)"
4200     b8/copy-to-eax  "+ 34a)"/imm32
4201     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4202     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
4203     05/add-to-eax  4/imm32
4204     # var slice/ecx = {eax, ecx}
4205     51/push-ecx
4206     50/push-eax
4207     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4208     # eax = next-hex-int(slice)
4209     # . . push args
4210     51/push-ecx
4211     # . . call
4212     e8/call  next-hex-int/disp32
4213     # . . discard args
4214     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4215     # check-ints-equal(eax, 0x34a, msg)
4216     # . . push args
4217     68/push  "F - test-next-hex-int-multi-digit"/imm32
4218     68/push  0x34a/imm32
4219     50/push-eax
4220     # . . call
4221     e8/call  check-ints-equal/disp32
4222     # . . discard args
4223     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4224     # . epilogue
4225     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4226     5d/pop-to-ebp
4227     c3/return
4228 
4229 test-next-hex-int-0x-prefix:
4230     # . prologue
4231     55/push-ebp
4232     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4233     # (eax..ecx) = "+0x34)"
4234     b8/copy-to-eax  "+0x34)"/imm32
4235     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4236     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
4237     05/add-to-eax  4/imm32
4238     # var slice/ecx = {eax, ecx}
4239     51/push-ecx
4240     50/push-eax
4241     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4242     # eax = next-hex-int(slice)
4243     # . . push args
4244     51/push-ecx
4245     # . . call
4246     e8/call  next-hex-int/disp32
4247     # . . discard args
4248     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4249     # check-ints-equal(eax, 0x34, msg)
4250     # . . push args
4251     68/push  "F - test-next-hex-int-0x-prefix"/imm32
4252     68/push  0x34/imm32
4253     50/push-eax
4254     # . . call
4255     e8/call  check-ints-equal/disp32
4256     # . . discard args
4257     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4258     # . epilogue
4259     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4260     5d/pop-to-ebp
4261     c3/return
4262 
4263 test-next-hex-int-zero:
4264     # . prologue
4265     55/push-ebp
4266     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4267     # (eax..ecx) = "+0)"
4268     b8/copy-to-eax  "+0)"/imm32
4269     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4270     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
4271     05/add-to-eax  4/imm32
4272     # var slice/ecx = {eax, ecx}
4273     51/push-ecx
4274     50/push-eax
4275     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4276     # eax = next-hex-int(slice)
4277     # . . push args
4278     51/push-ecx
4279     # . . call
4280     e8/call  next-hex-int/disp32
4281     # . . discard args
4282     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4283     # check-ints-equal(eax, 0, msg)
4284     # . . push args
4285     68/push  "F - test-next-hex-int-zero"/imm32
4286     68/push  0/imm32
4287     50/push-eax
4288     # . . call
4289     e8/call  check-ints-equal/disp32
4290     # . . discard args
4291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4292     # . epilogue
4293     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4294     5d/pop-to-ebp
4295     c3/return
4296 
4297 test-next-hex-int-0-prefix:
4298     # . prologue
4299     55/push-ebp
4300     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4301     # (eax..ecx) = "+ 03)"
4302     b8/copy-to-eax  "+ 03)"/imm32
4303     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4304     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
4305     05/add-to-eax  4/imm32
4306     # var slice/ecx = {eax, ecx}
4307     51/push-ecx
4308     50/push-eax
4309     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4310     # eax = next-hex-int(slice)
4311     # . . push args
4312     51/push-ecx
4313     # . . call
4314     e8/call  next-hex-int/disp32
4315     # . . discard args
4316     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4317     # check-ints-equal(eax, 3, msg)
4318     # . . push args
4319     68/push  "F - test-next-hex-int-0-prefix"/imm32
4320     68/push  3/imm32
4321     50/push-eax
4322     # . . call
4323     e8/call  check-ints-equal/disp32
4324     # . . discard args
4325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4326     # . epilogue
4327     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4328     5d/pop-to-ebp
4329     c3/return
4330 
4331 test-next-hex-int-negative:
4332     # . prologue
4333     55/push-ebp
4334     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4335     # (eax..ecx) = "-03)"
4336     b8/copy-to-eax  "-03)"/imm32
4337     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4338     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
4339     05/add-to-eax  4/imm32
4340     # var slice/ecx = {eax, ecx}
4341     51/push-ecx
4342     50/push-eax
4343     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4344     # eax = next-hex-int(slice)
4345     # . . push args
4346     51/push-ecx
4347     # . . call
4348     e8/call  next-hex-int/disp32
4349     # . . discard args
4350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4351     # check-ints-equal(eax, -3, msg)
4352     # . . push args
4353     68/push  "F - test-next-hex-int-negative"/imm32
4354     68/push  -3/imm32
4355     50/push-eax
4356     # . . call
4357     e8/call  check-ints-equal/disp32
4358     # . . discard args
4359     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4360     # . epilogue
4361     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4362     5d/pop-to-ebp
4363     c3/return
4364 
4365 test-next-hex-int-negative-with-space:
4366     # . prologue
4367     55/push-ebp
4368     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4369     # (eax..ecx) = "- 03)"
4370     b8/copy-to-eax  "- 03)"/imm32
4371     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4372     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
4373     05/add-to-eax  4/imm32
4374     # var slice/ecx = {eax, ecx}
4375     51/push-ecx
4376     50/push-eax
4377     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4378     # eax = next-hex-int(slice)
4379     # . . push args
4380     51/push-ecx
4381     # . . call
4382     e8/call  next-hex-int/disp32
4383     # . . discard args
4384     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4385     # check-ints-equal(eax, -3, msg)
4386     # . . push args
4387     68/push  "F - test-next-hex-int-negative-with-space"/imm32
4388     68/push  -3/imm32
4389     50/push-eax
4390     # . . call
4391     e8/call  check-ints-equal/disp32
4392     # . . discard args
4393     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4394     # . epilogue
4395     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4396     5d/pop-to-ebp
4397     c3/return
4398 
4399 # assumes 'in' starts a positive unsigned integer
4400 # returns the value of the integer
4401 # side-effect: modifies 'in' to skip past the integer
4402 next-positive-hex-int:  # in: (addr slice) -> result/eax
4403     # . prologue
4404     55/push-ebp
4405     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4406     # . save registers
4407     51/push-ecx
4408     52/push-edx
4409     53/push-ebx
4410     56/push-esi
4411     57/push-edi
4412     # result/edi = 0
4413     31/xor                          3/mod/direct    7/rm32/edi    .           .             .           7/r32/edi   .               .                 # clear edi
4414     # esi = in
4415     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
4416     # edx = in->end
4417     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
4418     # curr/ecx = in->start
4419     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
4420     # negate?/ebx = false
4421     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
4422     # eax = *curr
4423     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4424     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4425 $next-positive-hex-int:initial-0:
4426     # skip past leading '0x'
4427     # . if (*curr != '0') jump to loop
4428     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4429     3d/compare-eax-and  0x30/imm32/0
4430     75/jump-if-!=  $next-positive-hex-int:loop/disp8
4431     # . ++curr
4432     41/increment-ecx
4433 $next-positive-hex-int:initial-0x:
4434     # . if (curr >= in->end) return result
4435     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4436     73/jump-if-addr>=  $next-positive-hex-int:end/disp8
4437     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
4438     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
4439     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4440     3d/compare-eax-and  0x78/imm32/x
4441     75/jump-if-!=  $next-positive-hex-int:loop/disp8
4442     # . ++curr
4443     41/increment-ecx
4444 $next-positive-hex-int:loop:
4445     # if (curr >= in->end) break
4446     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
4447     73/jump-if-addr>=  $next-positive-hex-int:end/disp8
4448     # if (!is-hex-digit?(*curr)) break
4449     # . eax = *curr
4450     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4451     # . eax = is-hex-digit?(*curr)
4452     # . . push args
4453     50/push-eax
4454     # . . call
4455     e8/call  is-hex-digit?/disp32
4456     # . . discard args
4457     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4458     # . if (eax == false) break
4459     3d/compare-eax-and  0/imm32/false
4460     74/jump-if-=  $next-positive-hex-int:end/disp8
4461     # eax = from-hex-char(*curr)
4462     # . . copy arg to eax
4463     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
4464     # . . call
4465     e8/call  from-hex-char/disp32
4466     # result = result * 16 + eax
4467     c1/shift    4/subop/left        3/mod/direct    7/rm32/edi    .           .             .           .           .               4/imm8            # shift edi left by 4 bits
4468     01/add                          3/mod/direct    7/rm32/edi    .           .             .           0/r32/eax   .               .                 # add eax to edi
4469     # ++curr
4470     41/increment-ecx
4471     # loop
4472     eb/jump  $next-positive-hex-int:loop/disp8
4473 $next-positive-hex-int:end:
4474     # word-slice->start = curr
4475     89/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy ecx to *esi
4476     # return edi
4477     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           7/r32/edi   .               .                 # copy edi to eax
4478     # . restore registers
4479     5f/pop-to-edi
4480     5e/pop-to-esi
4481     5b/pop-to-ebx
4482     5a/pop-to-edx
4483     59/pop-to-ecx
4484     # . epilogue
4485     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4486     5d/pop-to-ebp
4487     c3/return
4488 
4489 test-next-positive-hex-int-single-digit:
4490     # . prologue
4491     55/push-ebp
4492     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4493     # (eax..ecx) = "a)"
4494     b8/copy-to-eax  "a)"/imm32
4495     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4496     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
4497     05/add-to-eax  4/imm32
4498     # var slice/ecx = {eax, ecx}
4499     51/push-ecx
4500     50/push-eax
4501     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4502     # eax = next-positive-hex-int(slice)
4503     # . . push args
4504     51/push-ecx
4505     # . . call
4506     e8/call  next-positive-hex-int/disp32
4507     # . . discard args
4508     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4509     # check-ints-equal(eax, 0xa, msg)
4510     # . . push args
4511     68/push  "F - test-next-positive-hex-int-single-digit"/imm32
4512     68/push  0xa/imm32
4513     50/push-eax
4514     # . . call
4515     e8/call  check-ints-equal/disp32
4516     # . . discard args
4517     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4518     # . epilogue
4519     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4520     5d/pop-to-ebp
4521     c3/return
4522 
4523 test-next-positive-hex-int-multi-digit:
4524     # . prologue
4525     55/push-ebp
4526     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4527     # (eax..ecx) = "34a)"
4528     b8/copy-to-eax  "34a)"/imm32
4529     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4530     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
4531     05/add-to-eax  4/imm32
4532     # var slice/ecx = {eax, ecx}
4533     51/push-ecx
4534     50/push-eax
4535     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4536     # eax = next-positive-hex-int(slice)
4537     # . . push args
4538     51/push-ecx
4539     # . . call
4540     e8/call  next-positive-hex-int/disp32
4541     # . . discard args
4542     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4543     # check-ints-equal(eax, 0x34a, msg)
4544     # . . push args
4545     68/push  "F - test-next-positive-hex-int-multi-digit"/imm32
4546     68/push  0x34a/imm32
4547     50/push-eax
4548     # . . call
4549     e8/call  check-ints-equal/disp32
4550     # . . discard args
4551     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4552     # . epilogue
4553     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4554     5d/pop-to-ebp
4555     c3/return
4556 
4557 test-next-positive-hex-int-0x-prefix:
4558     # . prologue
4559     55/push-ebp
4560     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4561     # (eax..ecx) = "0x34)"
4562     b8/copy-to-eax  "0x34)"/imm32
4563     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4564     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
4565     05/add-to-eax  4/imm32
4566     # var slice/ecx = {eax, ecx}
4567     51/push-ecx
4568     50/push-eax
4569     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4570     # eax = next-positive-hex-int(slice)
4571     # . . push args
4572     51/push-ecx
4573     # . . call
4574     e8/call  next-positive-hex-int/disp32
4575     # . . discard args
4576     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4577     # check-ints-equal(eax, 0x34, msg)
4578     # . . push args
4579     68/push  "F - test-next-positive-hex-int-0x-prefix"/imm32
4580     68/push  0x34/imm32
4581     50/push-eax
4582     # . . call
4583     e8/call  check-ints-equal/disp32
4584     # . . discard args
4585     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4586     # . epilogue
4587     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4588     5d/pop-to-ebp
4589     c3/return
4590 
4591 test-next-positive-hex-int-zero:
4592     # . prologue
4593     55/push-ebp
4594     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4595     # (eax..ecx) = "0"
4596     b8/copy-to-eax  "0"/imm32
4597     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4598     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
4599     05/add-to-eax  4/imm32
4600     # var slice/ecx = {eax, ecx}
4601     51/push-ecx
4602     50/push-eax
4603     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4604     # eax = next-positive-hex-int(slice)
4605     # . . push args
4606     51/push-ecx
4607     # . . call
4608     e8/call  next-positive-hex-int/disp32
4609     # . . discard args
4610     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4611     # check-ints-equal(eax, 0, msg)
4612     # . . push args
4613     68/push  "F - test-next-positive-hex-int-zero"/imm32
4614     68/push  0/imm32
4615     50/push-eax
4616     # . . call
4617     e8/call  check-ints-equal/disp32
4618     # . . discard args
4619     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4620     # . epilogue
4621     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4622     5d/pop-to-ebp
4623     c3/return
4624 
4625 test-next-positive-hex-int-0-prefix:
4626     # . prologue
4627     55/push-ebp
4628     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
4629     # (eax..ecx) = "03)"
4630     b8/copy-to-eax  "03)"/imm32
4631     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
4632     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
4633     05/add-to-eax  4/imm32
4634     # var slice/ecx = {eax, ecx}
4635     51/push-ecx
4636     50/push-eax
4637     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
4638     # eax = next-positive-hex-int(slice)
4639     # . . push args
4640     51/push-ecx
4641     # . . call
4642     e8/call  next-positive-hex-int/disp32
4643     # . . discard args
4644     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
4645     # check-ints-equal(eax, 3, msg)
4646     # . . push args
4647     68/push  "F - test-next-positive-hex-int-0-prefix"/imm32
4648     68/push  3/imm32
4649     50/push-eax
4650     # . . call
4651     e8/call  check-ints-equal/disp32
4652     # . . discard args
4653     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
4654     # . epilogue
4655     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
4656     5d/pop-to-ebp
4657     c3/return
4658 
4659 # . . vim:nowrap:textwidth=0