https://github.com/akkartik/mu/blob/main/linux/sigils.subx

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