diff --git a/apps/assort.subx b/apps/assort.subx index 917dfb5c..e150a924 100644 --- a/apps/assort.subx +++ b/apps/assort.subx @@ -7,7 +7,7 @@ # because we don't know if they refer to the line above or the line below. # # To run: -# $ ./subx translate *.subx apps/assort.subx -o apps/assort +# $ ./subx translate 0*.subx apps/subx-common.subx apps/assort.subx -o apps/assort # $ cat x # == code # abc diff --git a/apps/crenshaw2-1.subx b/apps/crenshaw2-1.subx index bd8cc5d6..7f91d06e 100644 --- a/apps/crenshaw2-1.subx +++ b/apps/crenshaw2-1.subx @@ -3,7 +3,7 @@ # except that we support hex digits. # # To run: -# $ ./subx translate *.subx apps/crenshaw2-1.subx -o apps/crenshaw2-1 +# $ ./subx translate 0*.subx apps/crenshaw2-1.subx -o apps/crenshaw2-1 # $ echo '3' |./subx run apps/crenshaw2-1 # Expected output: # # syscall(exit, 3) diff --git a/apps/crenshaw2-1b.subx b/apps/crenshaw2-1b.subx index cb75033f..e19d5aeb 100644 --- a/apps/crenshaw2-1b.subx +++ b/apps/crenshaw2-1b.subx @@ -3,7 +3,7 @@ # except that we support hex numbers of multiple digits. # # To run: -# $ ./subx translate *.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b +# $ ./subx translate 0*.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b # $ echo '1a' |./subx run apps/crenshaw2-1b # Expected output: # # syscall(exit, 1a) diff --git a/apps/desugar.subx b/apps/desugar.subx index 4110ddec..e66e58b1 100644 --- a/apps/desugar.subx +++ b/apps/desugar.subx @@ -1,5 +1,8 @@ # Experimental syntax sugar for SubX programs. # +# To run: +# $ ./subx translate 0*.subx apps/subx-common.subx apps/desugar.subx -o apps/desugar +# # We're experimenting with the following expressions: # # 1. diff --git a/apps/dquotes.subx b/apps/dquotes.subx index 20343d15..1375211c 100644 --- a/apps/dquotes.subx +++ b/apps/dquotes.subx @@ -2,7 +2,7 @@ # Replace them with references to new variables in the data segment. # # To run: -# $ ./subx translate *.subx apps/dquotes.subx -o apps/dquotes +# $ ./subx translate 0*.subx apps/subx-common.subx apps/dquotes.subx -o apps/dquotes # $ cat x # == code # ab "cd ef"/imm32 diff --git a/apps/factorial.subx b/apps/factorial.subx index f28a3657..8bbcedbd 100644 --- a/apps/factorial.subx +++ b/apps/factorial.subx @@ -1,7 +1,7 @@ ## compute the factorial of 5, and return the result in the exit code # # To run: -# $ ./subx translate apps/factorial.subx -o apps/factorial +# $ ./subx translate 0*.subx apps/factorial.subx -o apps/factorial # $ ./subx run apps/factorial # Expected result: # $ echo $? diff --git a/apps/handle.subx b/apps/handle.subx index 95364821..01e6adf9 100644 --- a/apps/handle.subx +++ b/apps/handle.subx @@ -13,7 +13,7 @@ # offset 4: address # # To run: -# $ ./subx translate *.subx apps/handle.subx -o apps/handle +# $ ./subx translate 0*.subx apps/handle.subx -o apps/handle # $ ./subx run apps/handle # Expected result is a successful lookup followed by a hard abort: # lookup succeeded diff --git a/apps/hex.subx b/apps/hex.subx index a2580e66..d5be93f4 100644 --- a/apps/hex.subx +++ b/apps/hex.subx @@ -3,7 +3,7 @@ # comments between '#' and newline. # # To run: -# $ ./subx translate *.subx apps/hex.subx -o apps/hex +# $ ./subx translate 0*.subx apps/subx-common.subx apps/hex.subx -o apps/hex # $ echo '80 81 82 # comment' |./subx run apps/hex |xxd - # Expected output: # 00000000: 8081 82 diff --git a/apps/pack.subx b/apps/pack.subx index 9499f3b3..029952e3 100644 --- a/apps/pack.subx +++ b/apps/pack.subx @@ -3,7 +3,7 @@ # uses are left untouched. # # To run: -# $ ./subx translate *.subx apps/pack.subx -o apps/pack +# $ ./subx translate 0*.subx apps/subx-common.subx apps/pack.subx -o apps/pack # $ echo '05/add-to-EAX 0x20/imm32' |./subx run apps/pack # Expected output: # 05 20 00 00 00 # 05/add-to-EAX 0x20/imm32 diff --git a/apps/survey.subx b/apps/survey.subx index d226ecc0..ee9f637d 100644 --- a/apps/survey.subx +++ b/apps/survey.subx @@ -5,7 +5,7 @@ # b) add segment headers with addresses and offsets correctly filled in # # To build: -# $ ./subx translate *.subx apps/survey.subx -o apps/survey +# $ ./subx translate 0*.subx apps/subx-common.subx apps/survey.subx -o apps/survey # # The expected input is a stream of bytes with segment headers, comments and # some interspersed labels. diff --git a/apps/tests.subx b/apps/tests.subx index 12f4902b..70456506 100644 --- a/apps/tests.subx +++ b/apps/tests.subx @@ -1,5 +1,8 @@ # Generate code for a new function called 'run-tests' which calls in sequence # all functions starting with 'test-'. +# +# To build: +# $ ./subx translate 0*.subx apps/subx-common.subx apps/tests.subx -o apps/tests == code # instruction effective address register displacement immediate diff --git a/html/apps/assort.subx.html b/html/apps/assort.subx.html index 466f6591..e392fa95 100644 --- a/html/apps/assort.subx.html +++ b/html/apps/assort.subx.html @@ -69,7 +69,7 @@ if ('onhashchange' in window) { 7 # because we don't know if they refer to the line above or the line below. 8 # 9 # To run: - 10 # $ ./subx translate *.subx apps/assort.subx -o apps/assort + 10 # $ ./subx translate 0*.subx apps/subx-common.subx apps/assort.subx -o apps/assort 11 # $ cat x 12 # == code 13 # abc diff --git a/html/apps/crenshaw2-1.subx.html b/html/apps/crenshaw2-1.subx.html index bc5eb083..d1efdecf 100644 --- a/html/apps/crenshaw2-1.subx.html +++ b/html/apps/crenshaw2-1.subx.html @@ -64,7 +64,7 @@ if ('onhashchange' in window) { 3 # except that we support hex digits. 4 # 5 # To run: - 6 # $ ./subx translate *.subx apps/crenshaw2-1.subx -o apps/crenshaw2-1 + 6 # $ ./subx translate 0*.subx apps/crenshaw2-1.subx -o apps/crenshaw2-1 7 # $ echo '3' |./subx run apps/crenshaw2-1 8 # Expected output: 9 # # syscall(exit, 3) diff --git a/html/apps/crenshaw2-1b.subx.html b/html/apps/crenshaw2-1b.subx.html index 5db393ba..72652b97 100644 --- a/html/apps/crenshaw2-1b.subx.html +++ b/html/apps/crenshaw2-1b.subx.html @@ -64,7 +64,7 @@ if ('onhashchange' in window) { 3 # except that we support hex numbers of multiple digits. 4 # 5 # To run: - 6 # $ ./subx translate *.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b + 6 # $ ./subx translate 0*.subx apps/crenshaw2-1b.subx -o apps/crenshaw2-1b 7 # $ echo '1a' |./subx run apps/crenshaw2-1b 8 # Expected output: 9 # # syscall(exit, 1a) diff --git a/html/apps/desugar.subx.html b/html/apps/desugar.subx.html index 404e846f..6655a4bd 100644 --- a/html/apps/desugar.subx.html +++ b/html/apps/desugar.subx.html @@ -62,3900 +62,4047 @@ if ('onhashchange' in window) {
    1 # Experimental syntax sugar for SubX programs.
    2 #
-   3 # We're experimenting with the following expressions:
-   4 #
-   5 # 1.
-   6 #   $ echo "ab %eax"  |  ./subx run apps/desugar
-   7 #   ab 3/mod 0/rm32
-   8 #
-   9 # 2.
-  10 #   $ echo "ab *eax"  |  ./subx run apps/desugar
-  11 #   ab 0/mod 0/rm32
-  12 #
-  13 # 3.
-  14 #   $ echo "ab *(eax+4)"  |  ./subx run apps/desugar
-  15 #   ab 2/mod 0/rm32 4/disp32
-  16 #
-  17 # 4.
-  18 #   $ echo "ab *(eax+ecx)"  |  ./subx run apps/desugar
-  19 #   ab 0/mod 4/rm32 0/base 1/index 0/scale
-  20 #
-  21 # 5.
-  22 #   $ echo "ab *(eax+ecx+4)"  |  ./subx run apps/desugar
-  23 #   ab 2/mod 4/rm32 0/base 1/index 0/scale 4/disp32
-  24 #
-  25 # 6.
-  26 #   $ echo "ab *(eax+ecx<<2+4)"  |  ./subx run apps/desugar
-  27 #   ab 2/mod 4/rm32 0/base 1/index 2/scale 4/disp32
-  28 #
-  29 # Addition isn't commutative here. Template must always be (base+index<<scale+disp),
-  30 # though some components are optional as described above.
+   3 # To run:
+   4 #   $ ./subx translate 0*.subx apps/subx-common.subx apps/desugar.subx  -o apps/desugar
+   5 #
+   6 # We're experimenting with the following expressions:
+   7 #
+   8 # 1.
+   9 #   $ echo "ab %eax"  |  ./subx run apps/desugar
+  10 #   ab 3/mod 0/rm32
+  11 #
+  12 # 2.
+  13 #   $ echo "ab *eax"  |  ./subx run apps/desugar
+  14 #   ab 0/mod 0/rm32
+  15 #
+  16 # 3.
+  17 #   $ echo "ab *(eax+4)"  |  ./subx run apps/desugar
+  18 #   ab 2/mod 0/rm32 4/disp32
+  19 #
+  20 # 4.
+  21 #   $ echo "ab *(eax+ecx)"  |  ./subx run apps/desugar
+  22 #   ab 0/mod 4/rm32 0/base 1/index 0/scale
+  23 #
+  24 # 5.
+  25 #   $ echo "ab *(eax+ecx+4)"  |  ./subx run apps/desugar
+  26 #   ab 2/mod 4/rm32 0/base 1/index 0/scale 4/disp32
+  27 #
+  28 # 6.
+  29 #   $ echo "ab *(eax+ecx<<2+4)"  |  ./subx run apps/desugar
+  30 #   ab 2/mod 4/rm32 0/base 1/index 2/scale 4/disp32
   31 #
-  32 # No metadata allowed inside '*(...)'.
-  33 # Whitespace inside '*(...)' is ok. But not immediately after the '*' or '%'.
-  34 
-  35 == code
-  36 #   instruction                     effective address                                                   register    displacement    immediate
-  37 # . op          subop               mod             rm32          base        index         scale       r32
-  38 # . 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
-  39 
-  40 Entry:  # run tests if necessary, convert stdin if not
-  41     # initialize heap
-  42     # . Heap = new-segment(Heap-size)
-  43     # . . push args
-  44     68/push  Heap/imm32
-  45     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
-  46     # . . call
-  47     e8/call  new-segment/disp32
-  48     # . . discard args
-  49     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-  50 
-  51     # run tests if necessary, convert stdin if not
-  52     # . prolog
-  53     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-  54     # - if argc > 1 and argv[1] == "test", then return run_tests()
-  55     # . argc > 1
-  56     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
-  57     7e/jump-if-lesser-or-equal  $run-main/disp8
-  58     # . argv[1] == "test"
-  59     # . . push args
-  60     68/push  "test"/imm32
-  61     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-  62     # . . call
-  63     e8/call  kernel-string-equal?/disp32
-  64     # . . discard args
-  65     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-  66     # . check result
-  67     3d/compare-EAX-and  1/imm32
-  68     75/jump-if-not-equal  $run-main/disp8
-  69     # . run-tests()
-  70     e8/call  run-tests/disp32
-  71     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
-  72     eb/jump  $main:end/disp8
-  73 $run-main:
-  74     # - otherwise convert stdin
-  75     # convert(Stdin, Stdout)
-  76     # . . push args
-  77     68/push  Stdout/imm32
-  78     68/push  Stdin/imm32
-  79     # . . call
-  80     e8/call  convert/disp32
-  81     # . . discard args
-  82     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
-  83     # . syscall(exit, 0)
-  84     bb/copy-to-EBX  0/imm32
-  85 $main:end:
-  86     b8/copy-to-EAX  1/imm32/exit
-  87     cd/syscall  0x80/imm8
-  88 
-  89 # error messages considered:
-  90 #   *x + 34                 -> error: base+disp addressing must be within '()'
-  91 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
-  92     # pseudocode:
-  93     #   var line = new-stream(512, 1)
-  94     #   while true
-  95     #     clear-stream(line)
-  96     #     read-line-buffered(in, line)
-  97     #     if (line->write == 0) break                     # end of file
-  98     #     while true
-  99     #       var word-slice = next-word-or-expression(line)
- 100     #       if slice-empty?(word-slice)                   # end of line
- 101     #         break
- 102     #       if slice-starts-with?(word-slice, "#")        # comment
- 103     #         continue
- 104     #       if slice-starts-with?(word-slice, '%')        # direct mode
- 105     #         emit-direct-mode(word-slice, out)
- 106     #       else if slice-starts-with?(word-slice, '*')   # indirect mode
- 107     #         base, index, scale, disp = parse-effective-address(word-slice)
- 108     #         emit-indirect-mode(out, base, index, scale, disp)
- 109     #       else if slice-starts-with?(word-slice, '+')
- 110     #         abort("'+' only permitted within '*(...)'")
- 111     #       else
- 112     #         write-slice-buffered(out, word-slice)
- 113     #       write(out, " ")
- 114     #     write(out, "\n")
- 115     #   flush(out)
- 116     #
- 117     # . prolog
- 118     55/push-EBP
- 119     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 120     # . save registers
- 121     50/push-EAX
- 122     51/push-ECX
- 123     52/push-EDX
- 124     53/push-EBX
- 125     # var line/ECX : (address stream byte) = stream(512)
- 126     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
- 127     68/push  0x200/imm32/length
- 128     68/push  0/imm32/read
- 129     68/push  0/imm32/write
- 130     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
- 131     # var word-slice/EDX = {0, 0}
- 132     68/push  0/imm32/end
- 133     68/push  0/imm32/start
- 134     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
- 135 $convert:line-loop:
- 136     # clear-stream(line)
- 137     # . . push args
- 138     51/push-ECX
- 139     # . . call
- 140     e8/call  clear-stream/disp32
- 141     # . . discard args
- 142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 143     # read-line-buffered(in, line)
- 144     # . . push args
- 145     51/push-ECX
- 146     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 147     # . . call
- 148     e8/call  read-line-buffered/disp32
- 149     # . . discard args
- 150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 151 $convert:check0:
- 152     # if (line->write == 0) break
- 153     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
- 154     0f 84/jump-if-equal  $convert:break/disp32
- 155 $convert:word-loop:
- 156     # next-word-or-expression(line, word-slice)
- 157     # . . push args
- 158     52/push-EDX
- 159     51/push-ECX
- 160     # . . call
- 161     e8/call  next-word-or-expression/disp32
- 162     # . . discard args
- 163     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 164 $convert:check1:
- 165     # if (slice-empty?(word-slice)) break
- 166     # . EAX = slice-empty?(word-slice)
- 167     # . . push args
- 168     52/push-EDX
- 169     # . . call
- 170     e8/call  slice-empty?/disp32
- 171     # . . discard args
- 172     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 173     # . if (EAX != 0) break
- 174     3d/compare-EAX-and  0/imm32
- 175     0f 85/jump-if-not-equal  $convert:next-line/disp32
- 176 $convert:check-for-comment:
- 177     # if (slice-starts-with?(word-slice, "#")) continue
- 178     # . start/EBX = word-slice->start
- 179     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
- 180     # . c/EAX = *start
- 181     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
- 182     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
- 183     # . if (EAX == '#') continue
- 184     3d/compare-EAX-and  0x23/imm32/hash
- 185     74/jump-if-equal  $convert:word-loop/disp8
- 186 $convert:check-for-direct-mode:
- 187     # if (!slice-starts-with?(word-slice, "%")) goto next check
- 188     3d/compare-EAX-and  0x25/imm32/percent
- 189     75/jump-if-not-equal  $convert:check-for-indirect-mode/disp8
- 190 $convert:direct-mode:
- 191 +-- 46 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
- 237     # emit-direct-mode(word-slice, out)
- 238     # . . push args
- 239     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 240     52/push-EDX
- 241     # . . call
- 242     e8/call  emit-direct-mode/disp32
- 243     # . . discard args
- 244     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 245     # continue
- 246     e9/jump  $convert:next-word/disp32
- 247 $convert:check-for-indirect-mode:
- 248     # if (!slice-starts-with?(word-slice, "*")) goto next check
- 249     3d/compare-EAX-and  0x2a/imm32/asterisk
- 250     75/jump-if-not-equal  $convert:check-for-invalid-addition/disp8
- 251 $convert:indirect-mode:
- 252     # spill registers
- 253     50/push-EAX
- 254     51/push-ECX
- 255     52/push-EDX
- 256     53/push-EBX
- 257     # base/EAX, index/ECX, scale/EDX, disp/EBX = parse-effective-address(word-slice)
- 258     # . . push args
- 259     52/push-EDX
- 260     # . . call
- 261     e8/call  parse-effective-address/disp32
- 262     # . . discard args
- 263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 264     # emit-indirect-mode(out, base, index, scale, disp)
- 265     # . . push args
- 266     53/push-EBX
- 267     52/push-EDX
- 268     51/push-ECX
- 269     50/push-EAX
- 270     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 271     # . . call
- 272     e8/call  emit-indirect-mode/disp32
- 273     # . . discard args
- 274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
- 275     # restore registers
- 276     5b/pop-to-EBX
- 277     5a/pop-to-EDX
- 278     59/pop-to-ECX
- 279     58/pop-to-EAX
- 280     # continue
- 281     e9/jump  $convert:next-word/disp32
- 282 $convert:check-for-invalid-addition:
- 283     # if (slice-starts-with?(word-slice, "+")) goto error1
- 284     3d/compare-EAX-and  0x2b/imm32/plus
- 285     74/jump-if-equal  $convert:error1/disp8
- 286 $convert:check-for-invalid-left-shift:
- 287     # if (slice-starts-with?(word-slice, "<")) goto error1
- 288     3d/compare-EAX-and  0x3c/imm32/less-than
- 289     74/jump-if-equal  $convert:error1/disp8
- 290 $convert:regular-word:
- 291     # write-slice-buffered(out, word-slice)
- 292     # . . push args
- 293     52/push-EDX
- 294     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 295     # . . call
- 296     e8/call  write-slice-buffered/disp32
- 297     # . . discard args
- 298     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 299     # fall through
- 300 $convert:next-word:
- 301     # write-buffered(out, " ")
- 302     # . . push args
- 303     68/push  " "/imm32
- 304     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 305     # . . call
- 306     e8/call  write-buffered/disp32
- 307     # . . discard args
- 308     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 309     # loop
- 310     e9/jump  $convert:word-loop/disp32
- 311 $convert:next-line:
- 312     # write-buffered(out, "\n")
- 313     # . . push args
- 314     68/push  Newline/imm32
- 315     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 316     # . . call
- 317     e8/call  write-buffered/disp32
- 318     # . . discard args
- 319     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 320     # loop
- 321     e9/jump  $convert:line-loop/disp32
- 322 $convert:break:
- 323     # flush(out)
- 324     # . . push args
- 325     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
- 326     # . . call
- 327     e8/call  flush/disp32
- 328     # . . discard args
- 329     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 330 $convert:end:
- 331     # . reclaim locals
- 332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
- 333     # . restore registers
- 334     5b/pop-to-EBX
- 335     5a/pop-to-EDX
- 336     59/pop-to-ECX
- 337     58/pop-to-EAX
- 338     # . epilog
- 339     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 340     5d/pop-to-EBP
- 341     c3/return
- 342 
- 343 $convert:error1:
- 344     # print(stderr, "error: '" EAX "' only permitted within '*(...)' in '" line "'")
- 345     # . write-buffered(Stderr, "error: '")
- 346     # . . push args
- 347     68/push  "error: '"/imm32
- 348     68/push  Stderr/imm32
- 349     # . . call
- 350     e8/call  write-buffered/disp32
- 351     # . . discard args
- 352     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 353     # . write-byte-buffered(Stderr, EAX)
- 354     # . . push args
- 355     50/push-EAX
- 356     68/push  Stderr/imm32
- 357     # . . call
- 358     e8/call  write-byte-buffered/disp32
- 359     # . . discard args
- 360     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 361     # . write-buffered(Stderr, "' only permitted within '*(...)' in '")
- 362     # . . push args
- 363     68/push  "' only permitted within '*(...)' in '"/imm32
- 364     68/push  Stderr/imm32
- 365     # . . call
- 366     e8/call  write-buffered/disp32
- 367     # . . discard args
- 368     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 369     # . write-stream-data(Stderr, line)
- 370     # . . push args
- 371     51/push-ECX
- 372     68/push  Stderr/imm32
- 373     # . . call
- 374     e8/call  write-stream-data/disp32
- 375     # . . discard args
- 376     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 377     # . write-buffered(Stderr, "'")
- 378     # . . push args
- 379     68/push  "'"/imm32
- 380     68/push  Stderr/imm32
- 381     # . . call
- 382     e8/call  write-buffered/disp32
- 383     # . . discard args
- 384     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 385     # . flush(Stderr)
- 386     # . . push args
- 387     68/push  Stderr/imm32
- 388     # . . call
- 389     e8/call  flush/disp32
- 390     # . . discard args
- 391     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 392     # . syscall(exit, 1)
- 393     bb/copy-to-EBX  1/imm32
- 394     b8/copy-to-EAX  1/imm32/exit
- 395     cd/syscall  0x80/imm8
- 396     # never gets here
- 397 
- 398 test-convert-passes-most-words-through:
- 399     # . prolog
- 400     55/push-EBP
- 401     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 402     # setup
- 403     # . clear-stream(_test-input-stream)
- 404     # . . push args
- 405     68/push  _test-input-stream/imm32
- 406     # . . call
- 407     e8/call  clear-stream/disp32
- 408     # . . discard args
- 409     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 410     # . clear-stream(_test-input-buffered-file+4)
- 411     # . . push args
- 412     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 413     05/add-to-EAX  4/imm32
- 414     50/push-EAX
- 415     # . . call
- 416     e8/call  clear-stream/disp32
- 417     # . . discard args
- 418     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 419     # . clear-stream(_test-output-stream)
- 420     # . . push args
- 421     68/push  _test-output-stream/imm32
- 422     # . . call
- 423     e8/call  clear-stream/disp32
- 424     # . . discard args
- 425     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 426     # . clear-stream(_test-output-buffered-file+4)
- 427     # . . push args
- 428     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 429     05/add-to-EAX  4/imm32
- 430     50/push-EAX
- 431     # . . call
- 432     e8/call  clear-stream/disp32
- 433     # . . discard args
- 434     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 435     # initialize input
- 436     # . write(_test-input-stream, "== abcd 0x1")
- 437     # . . push args
- 438     68/push  "== abcd 0x1"/imm32
- 439     68/push  _test-input-stream/imm32
- 440     # . . call
- 441     e8/call  write/disp32
- 442     # . . discard args
- 443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 444     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 445     # . . push args
- 446     68/push  _test-output-buffered-file/imm32
- 447     68/push  _test-input-buffered-file/imm32
- 448     # . . call
- 449     e8/call  convert/disp32
- 450     # . . discard args
- 451     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 452     # check that the line just passed through
- 453     # . flush(_test-output-buffered-file)
- 454     # . . push args
- 455     68/push  _test-output-buffered-file/imm32
- 456     # . . call
- 457     e8/call  flush/disp32
- 458     # . . discard args
- 459     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 460 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
- 486     # . check-stream-equal(_test-output-stream, "== abcd 0x1 \n", msg)
- 487     # . . push args
- 488     68/push  "F - test-convert-passes-most-words-through"/imm32
- 489     68/push  "== abcd 0x1 \n"/imm32
- 490     68/push  _test-output-stream/imm32
- 491     # . . call
- 492     e8/call  check-stream-equal/disp32
- 493     # . . discard args
- 494     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 495     # . epilog
- 496     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 497     5d/pop-to-EBP
- 498     c3/return
- 499 
- 500 test-convert-direct-mode:
- 501     # . prolog
- 502     55/push-EBP
- 503     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 504     # setup
- 505     # . clear-stream(_test-input-stream)
- 506     # . . push args
- 507     68/push  _test-input-stream/imm32
- 508     # . . call
- 509     e8/call  clear-stream/disp32
- 510     # . . discard args
- 511     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 512     # . clear-stream(_test-input-buffered-file+4)
- 513     # . . push args
- 514     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 515     05/add-to-EAX  4/imm32
- 516     50/push-EAX
- 517     # . . call
- 518     e8/call  clear-stream/disp32
- 519     # . . discard args
- 520     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 521     # . clear-stream(_test-output-stream)
- 522     # . . push args
- 523     68/push  _test-output-stream/imm32
- 524     # . . call
- 525     e8/call  clear-stream/disp32
- 526     # . . discard args
- 527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 528     # . clear-stream(_test-output-buffered-file+4)
- 529     # . . push args
- 530     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 531     05/add-to-EAX  4/imm32
- 532     50/push-EAX
- 533     # . . call
- 534     e8/call  clear-stream/disp32
- 535     # . . discard args
- 536     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 537     # initialize input
- 538     # . write(_test-input-stream, "ab %ecx")
- 539     # . . push args
- 540     68/push  "ab %ecx"/imm32
- 541     68/push  _test-input-stream/imm32
- 542     # . . call
- 543     e8/call  write/disp32
- 544     # . . discard args
- 545     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 546     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 547     # . . push args
- 548     68/push  _test-output-buffered-file/imm32
- 549     68/push  _test-input-buffered-file/imm32
- 550     # . . call
- 551     e8/call  convert/disp32
- 552     # . . discard args
- 553     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 554     # check that the line just passed through
- 555     # . flush(_test-output-buffered-file)
- 556     # . . push args
- 557     68/push  _test-output-buffered-file/imm32
- 558     # . . call
- 559     e8/call  flush/disp32
- 560     # . . discard args
- 561     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 562 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
- 588     # . check-stream-equal(_test-output-stream, "ab 3/mod/direct 0x00000001/rm32 \n", msg)
- 589     # . . push args
- 590     68/push  "F - test-convert-direct-mode"/imm32
- 591     68/push  "ab 3/mod/direct 0x00000001/rm32 \n"/imm32
- 592     68/push  _test-output-stream/imm32
- 593     # . . call
- 594     e8/call  check-stream-equal/disp32
- 595     # . . discard args
- 596     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 597     # . epilog
- 598     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 599     5d/pop-to-EBP
- 600     c3/return
- 601 
- 602 test-convert-register-indirect-mode:
- 603     # . prolog
- 604     55/push-EBP
- 605     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 606     # setup
- 607     # . clear-stream(_test-input-stream)
- 608     # . . push args
- 609     68/push  _test-input-stream/imm32
- 610     # . . call
- 611     e8/call  clear-stream/disp32
- 612     # . . discard args
- 613     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 614     # . clear-stream(_test-input-buffered-file+4)
- 615     # . . push args
- 616     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 617     05/add-to-EAX  4/imm32
- 618     50/push-EAX
- 619     # . . call
- 620     e8/call  clear-stream/disp32
- 621     # . . discard args
- 622     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 623     # . clear-stream(_test-output-stream)
- 624     # . . push args
- 625     68/push  _test-output-stream/imm32
- 626     # . . call
- 627     e8/call  clear-stream/disp32
- 628     # . . discard args
- 629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 630     # . clear-stream(_test-output-buffered-file+4)
- 631     # . . push args
- 632     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 633     05/add-to-EAX  4/imm32
- 634     50/push-EAX
- 635     # . . call
- 636     e8/call  clear-stream/disp32
- 637     # . . discard args
- 638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 639     # initialize input
- 640     # . write(_test-input-stream, "ab *ecx")
- 641     # . . push args
- 642     68/push  "ab *ecx"/imm32
- 643     68/push  _test-input-stream/imm32
- 644     # . . call
- 645     e8/call  write/disp32
- 646     # . . discard args
- 647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 648     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 649     # . . push args
- 650     68/push  _test-output-buffered-file/imm32
- 651     68/push  _test-input-buffered-file/imm32
- 652     # . . call
- 653     e8/call  convert/disp32
- 654     # . . discard args
- 655     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 656     # check that the line just passed through
- 657     # . flush(_test-output-buffered-file)
- 658     # . . push args
- 659     68/push  _test-output-buffered-file/imm32
- 660     # . . call
- 661     e8/call  flush/disp32
- 662     # . . discard args
- 663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 664 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
- 690     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 0x00000001/rm32 \n", msg)
- 691     # . . push args
- 692     68/push  "F - test-convert-indirect-mode"/imm32
- 693     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
- 694     68/push  _test-output-stream/imm32
- 695     # . . call
- 696     e8/call  check-stream-equal/disp32
- 697     # . . discard args
- 698     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 699     # . epilog
- 700     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 701     5d/pop-to-EBP
- 702     c3/return
- 703 
- 704 test-convert-register-indirect-mode-without-displacement:
- 705     # . prolog
- 706     55/push-EBP
- 707     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 708     # setup
- 709     # . clear-stream(_test-input-stream)
- 710     # . . push args
- 711     68/push  _test-input-stream/imm32
- 712     # . . call
- 713     e8/call  clear-stream/disp32
- 714     # . . discard args
- 715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 716     # . clear-stream(_test-input-buffered-file+4)
- 717     # . . push args
- 718     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 719     05/add-to-EAX  4/imm32
- 720     50/push-EAX
- 721     # . . call
- 722     e8/call  clear-stream/disp32
- 723     # . . discard args
- 724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 725     # . clear-stream(_test-output-stream)
- 726     # . . push args
- 727     68/push  _test-output-stream/imm32
- 728     # . . call
- 729     e8/call  clear-stream/disp32
- 730     # . . discard args
- 731     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 732     # . clear-stream(_test-output-buffered-file+4)
- 733     # . . push args
- 734     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 735     05/add-to-EAX  4/imm32
- 736     50/push-EAX
- 737     # . . call
- 738     e8/call  clear-stream/disp32
- 739     # . . discard args
- 740     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 741     # initialize input
- 742     # . write(_test-input-stream, "ab *(ecx)")
- 743     # . . push args
- 744     68/push  "ab *(ecx)"/imm32
- 745     68/push  _test-input-stream/imm32
- 746     # . . call
- 747     e8/call  write/disp32
- 748     # . . discard args
- 749     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 750     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 751     # . . push args
- 752     68/push  _test-output-buffered-file/imm32
- 753     68/push  _test-input-buffered-file/imm32
- 754     # . . call
- 755     e8/call  convert/disp32
- 756     # . . discard args
- 757     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 758     # check that the line just passed through
- 759     # . flush(_test-output-buffered-file)
- 760     # . . push args
- 761     68/push  _test-output-buffered-file/imm32
- 762     # . . call
- 763     e8/call  flush/disp32
- 764     # . . discard args
- 765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 766 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
- 792     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 1/rm32 \n", msg)
- 793     # . . push args
- 794     68/push  "F - test-convert-indirect-mode-without-displacement"/imm32
- 795     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
- 796     68/push  _test-output-stream/imm32
- 797     # . . call
- 798     e8/call  check-stream-equal/disp32
- 799     # . . discard args
- 800     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 801     # . epilog
- 802     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 803     5d/pop-to-EBP
- 804     c3/return
- 805 
- 806 test-convert-register-indirect-mode-with-displacement:
- 807     # . prolog
- 808     55/push-EBP
- 809     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 810     # setup
- 811     # . clear-stream(_test-input-stream)
- 812     # . . push args
- 813     68/push  _test-input-stream/imm32
- 814     # . . call
- 815     e8/call  clear-stream/disp32
- 816     # . . discard args
- 817     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 818     # . clear-stream(_test-input-buffered-file+4)
- 819     # . . push args
- 820     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 821     05/add-to-EAX  4/imm32
- 822     50/push-EAX
- 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-stream)
- 828     # . . push args
- 829     68/push  _test-output-stream/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     # . clear-stream(_test-output-buffered-file+4)
- 835     # . . push args
- 836     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 837     05/add-to-EAX  4/imm32
- 838     50/push-EAX
- 839     # . . call
- 840     e8/call  clear-stream/disp32
- 841     # . . discard args
- 842     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 843     # initialize input
- 844     # . write(_test-input-stream, "ab *(ecx+4)")
- 845     # . . push args
- 846     68/push  "ab *(ecx+4)"/imm32
- 847     68/push  _test-input-stream/imm32
- 848     # . . call
- 849     e8/call  write/disp32
- 850     # . . discard args
- 851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 852     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 853     # . . push args
- 854     68/push  _test-output-buffered-file/imm32
- 855     68/push  _test-input-buffered-file/imm32
- 856     # . . call
- 857     e8/call  convert/disp32
- 858     # . . discard args
- 859     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 860     # check that the line just passed through
- 861     # . flush(_test-output-buffered-file)
- 862     # . . push args
- 863     68/push  _test-output-buffered-file/imm32
- 864     # . . call
- 865     e8/call  flush/disp32
- 866     # . . discard args
- 867     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 868 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
- 894     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 1/rm32 4/disp32 \n", msg)
- 895     # . . push args
- 896     68/push  "F - test-convert-indirect-mode-with-displacement"/imm32
- 897     68/push  "ab 2/mod/*+disp32 0x00000001/rm32 0x00000004/disp32 \n"/imm32
- 898     68/push  _test-output-stream/imm32
- 899     # . . call
- 900     e8/call  check-stream-equal/disp32
- 901     # . . discard args
- 902     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
- 903     # . epilog
- 904     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
- 905     5d/pop-to-EBP
- 906     c3/return
- 907 
- 908 # boss level
- 909 test-convert-register-indirect-mode-with-sib-byte:
- 910     # . prolog
- 911     55/push-EBP
- 912     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 913     # setup
- 914     # . clear-stream(_test-input-stream)
- 915     # . . push args
- 916     68/push  _test-input-stream/imm32
- 917     # . . call
- 918     e8/call  clear-stream/disp32
- 919     # . . discard args
- 920     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 921     # . clear-stream(_test-input-buffered-file+4)
- 922     # . . push args
- 923     b8/copy-to-EAX  _test-input-buffered-file/imm32
- 924     05/add-to-EAX  4/imm32
- 925     50/push-EAX
- 926     # . . call
- 927     e8/call  clear-stream/disp32
- 928     # . . discard args
- 929     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 930     # . clear-stream(_test-output-stream)
- 931     # . . push args
- 932     68/push  _test-output-stream/imm32
- 933     # . . call
- 934     e8/call  clear-stream/disp32
- 935     # . . discard args
- 936     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 937     # . clear-stream(_test-output-buffered-file+4)
- 938     # . . push args
- 939     b8/copy-to-EAX  _test-output-buffered-file/imm32
- 940     05/add-to-EAX  4/imm32
- 941     50/push-EAX
- 942     # . . call
- 943     e8/call  clear-stream/disp32
- 944     # . . discard args
- 945     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 946     # initialize input
- 947     # . write(_test-input-stream, "ab *(ecx + edx<<3 + 4)")
- 948     # . . push args
- 949     68/push  "ab *(ecx + edx<<3 + 4)"/imm32
- 950     68/push  _test-input-stream/imm32
- 951     # . . call
- 952     e8/call  write/disp32
- 953     # . . discard args
- 954     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 955     # convert(_test-input-buffered-file, _test-output-buffered-file)
- 956     # . . push args
- 957     68/push  _test-output-buffered-file/imm32
- 958     68/push  _test-input-buffered-file/imm32
- 959     # . . call
- 960     e8/call  convert/disp32
- 961     # . . discard args
- 962     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 963     # check that the line just passed through
- 964     # . flush(_test-output-buffered-file)
- 965     # . . push args
- 966     68/push  _test-output-buffered-file/imm32
- 967     # . . call
- 968     e8/call  flush/disp32
- 969     # . . discard args
- 970     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 971 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
- 997     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale 4/disp32 \n", msg)
- 998     # . . push args
- 999     68/push  "F - test-convert-indirect-mode-with-sib-byte"/imm32
-1000     68/push  "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0x00000004/disp32 \n"/imm32
-1001     68/push  _test-output-stream/imm32
-1002     # . . call
-1003     e8/call  check-stream-equal/disp32
-1004     # . . discard args
-1005     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1006     # . epilog
-1007     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1008     5d/pop-to-EBP
-1009     c3/return
-1010 
-1011 test-convert-register-indirect-mode-with-sib-byte-negative-displacement:
-1012     # . prolog
-1013     55/push-EBP
-1014     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1015     # setup
-1016     # . clear-stream(_test-input-stream)
-1017     # . . push args
-1018     68/push  _test-input-stream/imm32
-1019     # . . call
-1020     e8/call  clear-stream/disp32
-1021     # . . discard args
-1022     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1023     # . clear-stream(_test-input-buffered-file+4)
-1024     # . . push args
-1025     b8/copy-to-EAX  _test-input-buffered-file/imm32
-1026     05/add-to-EAX  4/imm32
-1027     50/push-EAX
-1028     # . . call
-1029     e8/call  clear-stream/disp32
-1030     # . . discard args
-1031     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1032     # . clear-stream(_test-output-stream)
-1033     # . . push args
-1034     68/push  _test-output-stream/imm32
-1035     # . . call
-1036     e8/call  clear-stream/disp32
-1037     # . . discard args
-1038     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1039     # . clear-stream(_test-output-buffered-file+4)
-1040     # . . push args
-1041     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1042     05/add-to-EAX  4/imm32
-1043     50/push-EAX
-1044     # . . call
-1045     e8/call  clear-stream/disp32
-1046     # . . discard args
-1047     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1048     # initialize input
-1049     # . write(_test-input-stream, "ab *(ecx + edx<<3 - 4)")
-1050     # . . push args
-1051     68/push  "ab *(ecx + edx<<3 - 4)"/imm32
-1052     68/push  _test-input-stream/imm32
-1053     # . . call
-1054     e8/call  write/disp32
-1055     # . . discard args
-1056     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1057     # convert(_test-input-buffered-file, _test-output-buffered-file)
-1058     # . . push args
-1059     68/push  _test-output-buffered-file/imm32
-1060     68/push  _test-input-buffered-file/imm32
-1061     # . . call
-1062     e8/call  convert/disp32
-1063     # . . discard args
-1064     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1065     # check that the line just passed through
-1066     # . flush(_test-output-buffered-file)
-1067     # . . push args
-1068     68/push  _test-output-buffered-file/imm32
-1069     # . . call
-1070     e8/call  flush/disp32
-1071     # . . discard args
-1072     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1073 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
-1099     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale -4/disp32 \n", msg)
-1100     # . . push args
-1101     68/push  "F - test-convert-indirect-mode-with-sib-byte-negative-displacement"/imm32
-1102     68/push  "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0xfffffffc/disp32 \n"/imm32
-1103     68/push  _test-output-stream/imm32
-1104     # . . call
-1105     e8/call  check-stream-equal/disp32
-1106     # . . discard args
-1107     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1108     # . epilog
-1109     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1110     5d/pop-to-EBP
-1111     c3/return
-1112 
-1113 # beware: modifies 'word'
-1114 emit-direct-mode:  # word : (address slice), out : (address buffered-file)
-1115     # . prolog
-1116     55/push-EBP
-1117     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1118     # . save registers
-1119     50/push-EAX
-1120     # ++word->start
-1121     # . EAX = word
-1122     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
-1123     # . ++(*EAX)
-1124     ff          0/subop/increment   0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # increment *EAX
-1125     # reg-num/EAX = get-slice(Registers, word, row-size=8)
-1126     # . . push args
-1127     68/push  "Registers"/imm32
-1128     68/push  8/imm32/row-size
-1129     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1130     68/push  Registers/imm32
-1131     # . . call
-1132     e8/call  get-slice/disp32
-1133     # . . discard args
-1134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-1135     # write-buffered(out, "3/mod/direct ")
-1136     # . . push args
-1137     68/push  "3/mod/direct "/imm32
-1138     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-1139     # . . call
-1140     e8/call  write-buffered/disp32
-1141     # . . discard args
-1142     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1143     # print-int32-buffered(out, *EAX)
-1144     # . . push args
-1145     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
-1146     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-1147     # . . call
-1148     e8/call  print-int32-buffered/disp32
-1149     # . . discard args
-1150     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1151     # write-buffered(out, "/rm32")
-1152     # . . push args
-1153     68/push  "/rm32"/imm32
-1154     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-1155     # . . call
-1156     e8/call  write-buffered/disp32
-1157     # . . discard args
-1158     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1159 $emit-direct-mode:end:
-1160     # . restore registers
-1161     58/pop-to-EAX
-1162     # . epilog
-1163     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1164     5d/pop-to-EBP
-1165     c3/return
-1166 
-1167 test-emit-direct-mode:
-1168     # . prolog
-1169     55/push-EBP
-1170     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1171     # setup
-1172     # . clear-stream(_test-output-stream)
-1173     # . . push args
-1174     68/push  _test-output-stream/imm32
-1175     # . . call
-1176     e8/call  clear-stream/disp32
-1177     # . . discard args
-1178     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1179     # . clear-stream(_test-output-buffered-file+4)
-1180     # . . push args
-1181     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1182     05/add-to-EAX  4/imm32
-1183     50/push-EAX
-1184     # . . call
-1185     e8/call  clear-stream/disp32
-1186     # . . discard args
-1187     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1188     # var slice/ECX = "%eax"
-1189     b8/copy-to-EAX  "%eax"/imm32
-1190     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-1191     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
-1192     05/add-to-EAX  4/imm32
-1193     # . ECX = {EAX, ECX}
-1194     51/push-ECX
-1195     50/push-EAX
-1196     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1197     # emit-direct-mode(str, _test-output-buffered-file)
-1198     # . . push args
-1199     68/push  _test-output-buffered-file/imm32
-1200     51/push-ECX
-1201     # . . call
-1202     e8/call  emit-direct-mode/disp32
-1203     # . . discard args
-1204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32         # add to ESP
-1205     # . flush(_test-output-buffered-file)
-1206     # . . push args
-1207     68/push  _test-output-buffered-file/imm32
-1208     # . . call
-1209     e8/call  flush/disp32
-1210     # . . discard args
-1211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1212 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
-1238     # check-stream-equal(_test-output-stream, "3/mod/direct 0/rm32", msg)
-1239     # . . push args
-1240     68/push  "F - test-emit-direct-mode/0"/imm32
-1241     68/push  "3/mod/direct 0x00000000/rm32"/imm32
-1242     68/push  _test-output-stream/imm32
-1243     # . . call
-1244     e8/call  check-stream-equal/disp32
-1245     # . . discard args
-1246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1247     # . epilog
-1248     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1249     5d/pop-to-EBP
-1250     c3/return
-1251 
-1252 test-emit-direct-mode-2:
-1253     # . prolog
-1254     55/push-EBP
-1255     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1256     # setup
-1257     # . clear-stream(_test-output-stream)
-1258     # . . push args
-1259     68/push  _test-output-stream/imm32
-1260     # . . call
-1261     e8/call  clear-stream/disp32
-1262     # . . discard args
-1263     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1264     # . clear-stream(_test-output-buffered-file+4)
-1265     # . . push args
-1266     b8/copy-to-EAX  _test-output-buffered-file/imm32
-1267     05/add-to-EAX  4/imm32
-1268     50/push-EAX
-1269     # . . call
-1270     e8/call  clear-stream/disp32
-1271     # . . discard args
-1272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1273     # var slice/ECX = "%edi"
-1274     b8/copy-to-EAX  "%edi"/imm32
-1275     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-1276     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
-1277     05/add-to-EAX  4/imm32
-1278     # . ECX = {EAX, ECX}
-1279     51/push-ECX
-1280     50/push-EAX
-1281     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1282     # emit-direct-mode(str/ECX, _test-output-buffered-file)
-1283     # . . push args
-1284     68/push  _test-output-buffered-file/imm32
-1285     51/push-ECX
-1286     # . . call
-1287     e8/call  emit-direct-mode/disp32
-1288     # . . discard args
-1289     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32         # add to ESP
-1290     # . flush(_test-output-buffered-file)
-1291     # . . push args
-1292     68/push  _test-output-buffered-file/imm32
-1293     # . . call
-1294     e8/call  flush/disp32
-1295     # . . discard args
-1296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1297 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
-1323     # check-stream-equal(_test-output-stream, "3/mod/direct 7/rm32", msg)
-1324     # . . push args
-1325     68/push  "F - test-emit-direct-mode/1"/imm32
-1326     68/push  "3/mod/direct 0x00000007/rm32"/imm32
-1327     68/push  _test-output-stream/imm32
-1328     # . . call
-1329     e8/call  check-stream-equal/disp32
-1330     # . . discard args
-1331     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1332     # . epilog
-1333     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1334     5d/pop-to-EBP
-1335     c3/return
-1336 
-1337 # (re)compute the bounds of the next word or parenthetical expression in the line
-1338 # return empty string on reaching end of file
-1339 #
-1340 # error messages considered:
-1341 #   * ...                   -> error: no space after '*'
-1342 #   *(...                   -> error: *(...) expression must be all on a single line
-1343 next-word-or-expression:  # line : (address stream byte), out : (address slice)
-1344     # . prolog
-1345     55/push-EBP
-1346     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1347     # . save registers
-1348     50/push-EAX
-1349     51/push-ECX
-1350     56/push-ESI
-1351     57/push-EDI
-1352     # ESI = line
-1353     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-1354     # EDI = out
-1355     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
-1356     # skip-chars-matching(line, ' ')
-1357     # . . push args
-1358     68/push  0x20/imm32/space
-1359     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1360     # . . call
-1361     e8/call  skip-chars-matching/disp32
-1362     # . . discard args
-1363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1364 $next-word-or-expression:check0:
-1365     # if (line->read >= line->write) clear out and return
-1366     # . EAX = line->read
-1367     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
-1368     # . if (EAX < line->write) goto next check
-1369     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
-1370     7c/jump-if-lesser  $next-word-or-expression:check-for-comment/disp8
-1371     # . return out = {0, 0}
-1372     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
-1373     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
-1374     eb/jump  $next-word-or-expression:end/disp8
-1375 $next-word-or-expression:check-for-comment:
-1376     # out->start = &line->data[line->read]
-1377     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-1378     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
-1379     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
-1380     # if (line->data[line->read] != '#') goto next check
-1381     # . EAX = line->data[line->read]
-1382     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-1383     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
-1384     # . compare
-1385     3d/compare-EAX-and  0x23/imm32/pound
-1386     75/jump-if-not-equal  $next-word-or-expression:check-for-paren/disp8
-1387 $next-word-or-expression:comment:
-1388     # out->end = &line->data[line->write]
-1389     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-1390     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
-1391     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-1392     # line->read = line->write  # skip rest of line
-1393     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-1394     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
-1395     # return
-1396     eb/jump  $next-word-or-expression:end/disp8
-1397 $next-word-or-expression:check-for-paren:
-1398     # if (line->data[line->read] != '*') goto next check
-1399     # . EAX = line->data[line->read]
-1400     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-1401     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
-1402     # . compare
-1403     3d/compare-EAX-and  0x2a/imm32/asterisk
-1404     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
-1405     # if (line->data[line->read] == ' ') goto error1
-1406     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
-1407     # . compare
-1408     3d/compare-EAX-and  0x20/imm32/space
-1409     74/jump-if-equal  $next-word-or-expression:error1/disp8
-1410     # if (line->data[line->read] != '(') goto regular word
-1411     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
-1412     # . compare
-1413     3d/compare-EAX-and  0x28/imm32/open-paren
-1414     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
-1415 $next-word-or-expression:paren:
-1416     # skip-until-close-paren(line)
-1417     # . . push args
-1418     56/push-ESI
-1419     # . . call
-1420     e8/call  skip-until-close-paren/disp32
-1421     # . . discard args
-1422     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1423     # if (line->data[line->read] != ')') goto error2
-1424     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-1425     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
-1426     # . compare
-1427     3d/compare-EAX-and  0x29/imm32/close-paren
-1428     75/jump-if-not-equal  $next-word-or-expression:error2/disp8
-1429     # skip ')'
-1430     ff          0/subop/increment   1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # increment *(ESI+4)
-1431     # fall through
-1432 $next-word-or-expression:regular-word:
-1433     # skip-chars-not-matching-whitespace(line)  # including trailing newline
-1434     # . . push args
-1435     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1436     # . . call
-1437     e8/call  skip-chars-not-matching-whitespace/disp32
-1438     # . . discard args
-1439     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1440     # out->end = &line->data[line->read]
-1441     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
-1442     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
-1443     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
-1444 $next-word-or-expression:end:
-1445     # . restore registers
-1446     5f/pop-to-EDI
-1447     5e/pop-to-ESI
-1448     59/pop-to-ECX
-1449     58/pop-to-EAX
-1450     # . epilog
-1451     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1452     5d/pop-to-EBP
-1453     c3/return
-1454 
-1455 $next-word-or-expression:error1:
-1456     # print(stderr, "error: no space allowed after '*' in '" line "'")
-1457     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
-1458     # . . push args
-1459     68/push  "error: no space allowed after '*' in '"/imm32
-1460     68/push  Stderr/imm32
-1461     # . . call
-1462     e8/call  write-buffered/disp32
-1463     # . . discard args
-1464     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1465     # . write-stream-data(Stderr, line)
-1466     # . . push args
-1467     56/push-ESI
-1468     68/push  Stderr/imm32
-1469     # . . call
-1470     e8/call  write-stream-data/disp32
-1471     # . . discard args
-1472     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1473     # . write-buffered(Stderr, "'")
-1474     # . . push args
-1475     68/push  "'"/imm32
-1476     68/push  Stderr/imm32
-1477     # . . call
-1478     e8/call  write-buffered/disp32
-1479     # . . discard args
-1480     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1481     # . flush(Stderr)
-1482     # . . push args
-1483     68/push  Stderr/imm32
-1484     # . . call
-1485     e8/call  flush/disp32
-1486     # . . discard args
-1487     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1488     # . syscall(exit, 1)
-1489     bb/copy-to-EBX  1/imm32
-1490     b8/copy-to-EAX  1/imm32/exit
-1491     cd/syscall  0x80/imm8
-1492     # never gets here
-1493 
-1494 $next-word-or-expression:error2:
-1495     # print(stderr, "error: no space allowed after '*' in '" line "'")
-1496     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
-1497     # . . push args
-1498     68/push  "error: *(...) expression must be all on a single line in '"/imm32
-1499     68/push  Stderr/imm32
-1500     # . . call
-1501     e8/call  write-buffered/disp32
-1502     # . . discard args
-1503     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1504     # . write-stream-data(Stderr, line)
-1505     # . . push args
-1506     56/push-ESI
-1507     68/push  Stderr/imm32
-1508     # . . call
-1509     e8/call  write-stream-data/disp32
-1510     # . . discard args
-1511     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1512     # . write-buffered(Stderr, "'")
-1513     # . . push args
-1514     68/push  "'"/imm32
-1515     68/push  Stderr/imm32
-1516     # . . call
-1517     e8/call  write-buffered/disp32
-1518     # . . discard args
-1519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1520     # . flush(Stderr)
-1521     # . . push args
-1522     68/push  Stderr/imm32
-1523     # . . call
-1524     e8/call  flush/disp32
-1525     # . . discard args
-1526     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1527     # . syscall(exit, 1)
-1528     bb/copy-to-EBX  1/imm32
-1529     b8/copy-to-EAX  1/imm32/exit
-1530     cd/syscall  0x80/imm8
-1531     # never gets here
-1532 
-1533 test-next-word-or-expression:
-1534     # . prolog
-1535     55/push-EBP
-1536     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1537     # setup
-1538     # . clear-stream(_test-input-stream)
-1539     # . . push args
-1540     68/push  _test-input-stream/imm32
-1541     # . . call
-1542     e8/call  clear-stream/disp32
-1543     # . . discard args
-1544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1545     # var slice/ECX = {0, 0}
-1546     68/push  0/imm32/end
-1547     68/push  0/imm32/start
-1548     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1549     # write(_test-input-stream, "  ab")
-1550     # . . push args
-1551     68/push  "  ab"/imm32
-1552     68/push  _test-input-stream/imm32
-1553     # . . call
-1554     e8/call  write/disp32
-1555     # . . discard args
-1556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1557     # next-word-or-expression(_test-input-stream, slice)
-1558     # . . push args
-1559     51/push-ECX
-1560     68/push  _test-input-stream/imm32
-1561     # . . call
-1562     e8/call  next-word-or-expression/disp32
-1563     # . . discard args
-1564     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1565     # check-ints-equal(_test-input-stream->read, 4, msg)
-1566     # . . push args
-1567     68/push  "F - test-next-word-or-expression/updates-stream-read-correctly"/imm32
-1568     68/push  4/imm32
-1569     b8/copy-to-EAX  _test-input-stream/imm32
-1570     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-1571     # . . call
-1572     e8/call  check-ints-equal/disp32
-1573     # . . discard args
-1574     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1575     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
-1576     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
-1577     # . . push args
-1578     68/push  "F - test-next-word-or-expression: start"/imm32
-1579     68/push  0xe/imm32
-1580     # . . push slice->start - _test-input-stream
-1581     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-1582     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
-1583     50/push-EAX
-1584     # . . call
-1585     e8/call  check-ints-equal/disp32
-1586     # . . discard args
-1587     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1588     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
-1589     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
-1590     # . . push args
-1591     68/push  "F - test-next-word-or-expression: end"/imm32
-1592     68/push  0x10/imm32
-1593     # . . push slice->end - _test-input-stream
-1594     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-1595     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
-1596     50/push-EAX
-1597     # . . call
-1598     e8/call  check-ints-equal/disp32
-1599     # . . discard args
-1600     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1601     # . epilog
-1602     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1603     5d/pop-to-EBP
-1604     c3/return
-1605 
-1606 test-next-word-or-expression-returns-whole-comment:
-1607     # . prolog
-1608     55/push-EBP
-1609     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1610     # setup
-1611     # . clear-stream(_test-input-stream)
-1612     # . . push args
-1613     68/push  _test-input-stream/imm32
-1614     # . . call
-1615     e8/call  clear-stream/disp32
-1616     # . . discard args
-1617     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1618     # var slice/ECX = {0, 0}
-1619     68/push  0/imm32/end
-1620     68/push  0/imm32/start
-1621     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1622     # write(_test-input-stream, "  # a")
-1623     # . . push args
-1624     68/push  "  # a"/imm32
-1625     68/push  _test-input-stream/imm32
-1626     # . . call
-1627     e8/call  write/disp32
-1628     # . . discard args
-1629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1630     # next-word-or-expression(_test-input-stream, slice)
-1631     # . . push args
-1632     51/push-ECX
-1633     68/push  _test-input-stream/imm32
-1634     # . . call
-1635     e8/call  next-word-or-expression/disp32
-1636     # . . discard args
-1637     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1638     # check-ints-equal(_test-input-stream->read, 5, msg)
-1639     # . . push args
-1640     68/push  "F - test-next-word-or-expression-returns-whole-comment/updates-stream-read-correctly"/imm32
-1641     68/push  5/imm32
-1642     b8/copy-to-EAX  _test-input-stream/imm32
-1643     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-1644     # . . call
-1645     e8/call  check-ints-equal/disp32
-1646     # . . discard args
-1647     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1648     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
-1649     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
-1650     # . . push args
-1651     68/push  "F - test-next-word-or-expression-returns-whole-comment: start"/imm32
-1652     68/push  0xe/imm32
-1653     # . . push slice->start - _test-input-stream
-1654     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-1655     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
-1656     50/push-EAX
-1657     # . . call
-1658     e8/call  check-ints-equal/disp32
-1659     # . . discard args
-1660     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1661     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
-1662     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
-1663     # . . push args
-1664     68/push  "F - test-next-word-or-expression-returns-whole-comment: end"/imm32
-1665     68/push  0x11/imm32
-1666     # . . push slice->end - _test-input-stream
-1667     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-1668     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
-1669     50/push-EAX
-1670     # . . call
-1671     e8/call  check-ints-equal/disp32
-1672     # . . discard args
-1673     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1674     # . epilog
-1675     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1676     5d/pop-to-EBP
-1677     c3/return
-1678 
-1679 test-next-word-or-expression-returns-empty-expression-on-eof:
-1680     # . prolog
-1681     55/push-EBP
-1682     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1683     # setup
-1684     # . clear-stream(_test-input-stream)
-1685     # . . push args
-1686     68/push  _test-input-stream/imm32
-1687     # . . call
-1688     e8/call  clear-stream/disp32
-1689     # . . discard args
-1690     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1691     # var slice/ECX = {0, 0}
-1692     68/push  0/imm32/end
-1693     68/push  0/imm32/start
-1694     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1695     # write nothing to _test-input-stream
-1696     # next-word-or-expression(_test-input-stream, slice)
-1697     # . . push args
-1698     51/push-ECX
-1699     68/push  _test-input-stream/imm32
-1700     # . . call
-1701     e8/call  next-word-or-expression/disp32
-1702     # . . discard args
-1703     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1704     # check-ints-equal(slice->end - slice->start, 0, msg)
-1705     # . . push args
-1706     68/push  "F - test-next-word-or-expression-returns-empty-expression-on-eof"/imm32
-1707     68/push  0/imm32
-1708     # . . push slice->end - slice->start
-1709     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-1710     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
-1711     50/push-EAX
-1712     # . . call
-1713     e8/call  check-ints-equal/disp32
-1714     # . . discard args
-1715     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1716     # . epilog
-1717     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1718     5d/pop-to-EBP
-1719     c3/return
-1720 
-1721 test-next-word-or-expression-returns-whole-expression:
-1722     # . prolog
-1723     55/push-EBP
-1724     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1725     # setup
-1726     # . clear-stream(_test-input-stream)
-1727     # . . push args
-1728     68/push  _test-input-stream/imm32
-1729     # . . call
-1730     e8/call  clear-stream/disp32
-1731     # . . discard args
-1732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1733     # var slice/ECX = {0, 0}
-1734     68/push  0/imm32/end
-1735     68/push  0/imm32/start
-1736     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1737     # write(_test-input-stream, " *(a b)/imm32 ")
-1738     # . . push args
-1739     68/push  " *(a b)/imm32 "/imm32
-1740     68/push  _test-input-stream/imm32
-1741     # . . call
-1742     e8/call  write/disp32
-1743     # . . discard args
-1744     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1745     # next-word-or-expression(_test-input-stream, slice)
-1746     # . . push args
-1747     51/push-ECX
-1748     68/push  _test-input-stream/imm32
-1749     # . . call
-1750     e8/call  next-word-or-expression/disp32
-1751     # . . discard args
-1752     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1753     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
-1754     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
-1755     # . . push args
-1756     68/push  "F - test-next-word-or-expression-returns-whole-expression: start"/imm32
-1757     68/push  0xd/imm32
-1758     # . . push slice->start - _test-input-stream
-1759     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
-1760     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
-1761     50/push-EAX
+  32 # Addition isn't commutative here. Template must always be (base+index<<scale+disp),
+  33 # though some components are optional as described above.
+  34 #
+  35 # No metadata allowed inside '*(...)'.
+  36 # Whitespace inside '*(...)' is ok. But not immediately after the '*' or '%'.
+  37 
+  38 == code
+  39 #   instruction                     effective address                                                   register    displacement    immediate
+  40 # . op          subop               mod             rm32          base        index         scale       r32
+  41 # . 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
+  42 
+  43 Entry:  # run tests if necessary, convert stdin if not
+  44     # initialize heap
+  45     # . Heap = new-segment(Heap-size)
+  46     # . . push args
+  47     68/push  Heap/imm32
+  48     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Heap-size/disp32                  # push *Heap-size
+  49     # . . call
+  50     e8/call  new-segment/disp32
+  51     # . . discard args
+  52     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  53 
+  54     # run tests if necessary, convert stdin if not
+  55     # . prolog
+  56     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+  57     # - if argc > 1 and argv[1] == "test", then return run_tests()
+  58     # . argc > 1
+  59     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
+  60     7e/jump-if-lesser-or-equal  $run-main/disp8
+  61     # . argv[1] == "test"
+  62     # . . push args
+  63     68/push  "test"/imm32
+  64     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+  65     # . . call
+  66     e8/call  kernel-string-equal?/disp32
+  67     # . . discard args
+  68     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+  69     # . check result
+  70     3d/compare-EAX-and  1/imm32
+  71     75/jump-if-not-equal  $run-main/disp8
+  72     # . run-tests()
+  73     e8/call  run-tests/disp32
+  74     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+  75     eb/jump  $main:end/disp8
+  76 $run-main:
+  77     # - otherwise convert stdin
+  78     # convert(Stdin, Stdout)
+  79     # . . push args
+  80     68/push  Stdout/imm32
+  81     68/push  Stdin/imm32
+  82     # . . call
+  83     e8/call  convert/disp32
+  84     # . . discard args
+  85     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x8/imm32         # add to ESP
+  86     # . syscall(exit, 0)
+  87     bb/copy-to-EBX  0/imm32
+  88 $main:end:
+  89     b8/copy-to-EAX  1/imm32/exit
+  90     cd/syscall  0x80/imm8
+  91 
+  92 # error messages considered:
+  93 #   *x + 34                 -> error: base+disp addressing must be within '()'
+  94 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+  95     # pseudocode:
+  96     #   var line = new-stream(512, 1)
+  97     #   while true
+  98     #     clear-stream(line)
+  99     #     read-line-buffered(in, line)
+ 100     #     if (line->write == 0) break                     # end of file
+ 101     #     while true
+ 102     #       var word-slice = next-word-or-expression(line)
+ 103     #       if slice-empty?(word-slice)                   # end of line
+ 104     #         break
+ 105     #       if slice-starts-with?(word-slice, "#")        # comment
+ 106     #         continue
+ 107     #       if slice-starts-with?(word-slice, '%')        # direct mode
+ 108     #         emit-direct-mode(word-slice, out)
+ 109     #       else if slice-starts-with?(word-slice, '*')   # indirect mode
+ 110     #         base, index, scale, disp = parse-effective-address(word-slice)
+ 111     #         emit-indirect-mode(out, base, index, scale, disp)
+ 112     #       else if slice-starts-with?(word-slice, '+')
+ 113     #         abort("'+' only permitted within '*(...)'")
+ 114     #       else
+ 115     #         write-slice-buffered(out, word-slice)
+ 116     #       write(out, " ")
+ 117     #     write(out, "\n")
+ 118     #   flush(out)
+ 119     #
+ 120     # . prolog
+ 121     55/push-EBP
+ 122     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 123     # . save registers
+ 124     50/push-EAX
+ 125     51/push-ECX
+ 126     52/push-EDX
+ 127     53/push-EBX
+ 128     # var line/ECX : (address stream byte) = stream(512)
+ 129     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
+ 130     68/push  0x200/imm32/length
+ 131     68/push  0/imm32/read
+ 132     68/push  0/imm32/write
+ 133     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+ 134     # var word-slice/EDX = {0, 0}
+ 135     68/push  0/imm32/end
+ 136     68/push  0/imm32/start
+ 137     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+ 138 $convert:line-loop:
+ 139     # clear-stream(line)
+ 140     # . . push args
+ 141     51/push-ECX
+ 142     # . . call
+ 143     e8/call  clear-stream/disp32
+ 144     # . . discard args
+ 145     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 146     # read-line-buffered(in, line)
+ 147     # . . push args
+ 148     51/push-ECX
+ 149     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 150     # . . call
+ 151     e8/call  read-line-buffered/disp32
+ 152     # . . discard args
+ 153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 154 $convert:check0:
+ 155     # if (line->write == 0) break
+ 156     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
+ 157     0f 84/jump-if-equal  $convert:break/disp32
+ 158 $convert:word-loop:
+ 159     # next-word-or-expression(line, word-slice)
+ 160     # . . push args
+ 161     52/push-EDX
+ 162     51/push-ECX
+ 163     # . . call
+ 164     e8/call  next-word-or-expression/disp32
+ 165     # . . discard args
+ 166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 167 $convert:check1:
+ 168     # if (slice-empty?(word-slice)) break
+ 169     # . EAX = slice-empty?(word-slice)
+ 170     # . . push args
+ 171     52/push-EDX
+ 172     # . . call
+ 173     e8/call  slice-empty?/disp32
+ 174     # . . discard args
+ 175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 176     # . if (EAX != 0) break
+ 177     3d/compare-EAX-and  0/imm32
+ 178     0f 85/jump-if-not-equal  $convert:next-line/disp32
+ 179 $convert:check-for-comment:
+ 180     # if (slice-starts-with?(word-slice, "#")) continue
+ 181     # . start/EBX = word-slice->start
+ 182     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           3/r32/EBX   .               .                 # copy *EDX to EBX
+ 183     # . c/EAX = *start
+ 184     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+ 185     8a/copy-byte                    0/mod/indirect  3/rm32/EBX    .           .             .           0/r32/AL    .               .                 # copy byte at *EBX to AL
+ 186     # . if (EAX == '#') continue
+ 187     3d/compare-EAX-and  0x23/imm32/hash
+ 188     74/jump-if-equal  $convert:word-loop/disp8
+ 189 $convert:check-for-direct-mode:
+ 190     # if (!slice-starts-with?(word-slice, "%")) goto next check
+ 191     3d/compare-EAX-and  0x25/imm32/percent
+ 192     75/jump-if-not-equal  $convert:check-for-indirect-mode/disp8
+ 193 $convert:direct-mode:
+ 194 +-- 46 lines: #?     # dump word-slice -----------------------------------------------------------------------------------------------------------------------
+ 240     # emit-direct-mode(word-slice, out)
+ 241     # . . push args
+ 242     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 243     52/push-EDX
+ 244     # . . call
+ 245     e8/call  emit-direct-mode/disp32
+ 246     # . . discard args
+ 247     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 248     # continue
+ 249     e9/jump  $convert:next-word/disp32
+ 250 $convert:check-for-indirect-mode:
+ 251     # if (!slice-starts-with?(word-slice, "*")) goto next check
+ 252     3d/compare-EAX-and  0x2a/imm32/asterisk
+ 253     75/jump-if-not-equal  $convert:check-for-invalid-addition/disp8
+ 254 $convert:indirect-mode:
+ 255     # spill registers
+ 256     50/push-EAX
+ 257     51/push-ECX
+ 258     52/push-EDX
+ 259     53/push-EBX
+ 260     # base/EAX, index/ECX, scale/EDX, disp/EBX = parse-effective-address(word-slice)
+ 261     # . . push args
+ 262     52/push-EDX
+ 263     # . . call
+ 264     e8/call  parse-effective-address/disp32
+ 265     # . . discard args
+ 266     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 267     # emit-indirect-mode(out, base, index, scale, disp)
+ 268     # . . push args
+ 269     53/push-EBX
+ 270     52/push-EDX
+ 271     51/push-ECX
+ 272     50/push-EAX
+ 273     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 274     # . . call
+ 275     e8/call  emit-indirect-mode/disp32
+ 276     # . . discard args
+ 277     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+ 278     # restore registers
+ 279     5b/pop-to-EBX
+ 280     5a/pop-to-EDX
+ 281     59/pop-to-ECX
+ 282     58/pop-to-EAX
+ 283     # continue
+ 284     e9/jump  $convert:next-word/disp32
+ 285 $convert:check-for-invalid-addition:
+ 286     # if (slice-starts-with?(word-slice, "+")) goto error1
+ 287     3d/compare-EAX-and  0x2b/imm32/plus
+ 288     74/jump-if-equal  $convert:error1/disp8
+ 289 $convert:check-for-invalid-left-shift:
+ 290     # if (slice-starts-with?(word-slice, "<")) goto error1
+ 291     3d/compare-EAX-and  0x3c/imm32/less-than
+ 292     74/jump-if-equal  $convert:error1/disp8
+ 293 $convert:regular-word:
+ 294     # write-slice-buffered(out, word-slice)
+ 295     # . . push args
+ 296     52/push-EDX
+ 297     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 298     # . . call
+ 299     e8/call  write-slice-buffered/disp32
+ 300     # . . discard args
+ 301     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 302     # fall through
+ 303 $convert:next-word:
+ 304     # write-buffered(out, " ")
+ 305     # . . push args
+ 306     68/push  " "/imm32
+ 307     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 308     # . . call
+ 309     e8/call  write-buffered/disp32
+ 310     # . . discard args
+ 311     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 312     # loop
+ 313     e9/jump  $convert:word-loop/disp32
+ 314 $convert:next-line:
+ 315     # write-buffered(out, "\n")
+ 316     # . . push args
+ 317     68/push  Newline/imm32
+ 318     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 319     # . . call
+ 320     e8/call  write-buffered/disp32
+ 321     # . . discard args
+ 322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 323     # loop
+ 324     e9/jump  $convert:line-loop/disp32
+ 325 $convert:break:
+ 326     # flush(out)
+ 327     # . . push args
+ 328     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+ 329     # . . call
+ 330     e8/call  flush/disp32
+ 331     # . . discard args
+ 332     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 333 $convert:end:
+ 334     # . reclaim locals
+ 335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
+ 336     # . restore registers
+ 337     5b/pop-to-EBX
+ 338     5a/pop-to-EDX
+ 339     59/pop-to-ECX
+ 340     58/pop-to-EAX
+ 341     # . epilog
+ 342     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 343     5d/pop-to-EBP
+ 344     c3/return
+ 345 
+ 346 $convert:error1:
+ 347     # print(stderr, "error: '" EAX "' only permitted within '*(...)' in '" line "'")
+ 348     # . write-buffered(Stderr, "error: '")
+ 349     # . . push args
+ 350     68/push  "error: '"/imm32
+ 351     68/push  Stderr/imm32
+ 352     # . . call
+ 353     e8/call  write-buffered/disp32
+ 354     # . . discard args
+ 355     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 356     # . write-byte-buffered(Stderr, EAX)
+ 357     # . . push args
+ 358     50/push-EAX
+ 359     68/push  Stderr/imm32
+ 360     # . . call
+ 361     e8/call  write-byte-buffered/disp32
+ 362     # . . discard args
+ 363     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 364     # . write-buffered(Stderr, "' only permitted within '*(...)' in '")
+ 365     # . . push args
+ 366     68/push  "' only permitted within '*(...)' in '"/imm32
+ 367     68/push  Stderr/imm32
+ 368     # . . call
+ 369     e8/call  write-buffered/disp32
+ 370     # . . discard args
+ 371     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 372     # . write-stream-data(Stderr, line)
+ 373     # . . push args
+ 374     51/push-ECX
+ 375     68/push  Stderr/imm32
+ 376     # . . call
+ 377     e8/call  write-stream-data/disp32
+ 378     # . . discard args
+ 379     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 380     # . write-buffered(Stderr, "'")
+ 381     # . . push args
+ 382     68/push  "'"/imm32
+ 383     68/push  Stderr/imm32
+ 384     # . . call
+ 385     e8/call  write-buffered/disp32
+ 386     # . . discard args
+ 387     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 388     # . flush(Stderr)
+ 389     # . . push args
+ 390     68/push  Stderr/imm32
+ 391     # . . call
+ 392     e8/call  flush/disp32
+ 393     # . . discard args
+ 394     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 395     # . syscall(exit, 1)
+ 396     bb/copy-to-EBX  1/imm32
+ 397     b8/copy-to-EAX  1/imm32/exit
+ 398     cd/syscall  0x80/imm8
+ 399     # never gets here
+ 400 
+ 401 test-convert-passes-most-words-through:
+ 402     # . prolog
+ 403     55/push-EBP
+ 404     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 405     # setup
+ 406     # . clear-stream(_test-input-stream)
+ 407     # . . push args
+ 408     68/push  _test-input-stream/imm32
+ 409     # . . call
+ 410     e8/call  clear-stream/disp32
+ 411     # . . discard args
+ 412     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 413     # . clear-stream(_test-input-buffered-file+4)
+ 414     # . . push args
+ 415     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 416     05/add-to-EAX  4/imm32
+ 417     50/push-EAX
+ 418     # . . call
+ 419     e8/call  clear-stream/disp32
+ 420     # . . discard args
+ 421     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 422     # . clear-stream(_test-output-stream)
+ 423     # . . push args
+ 424     68/push  _test-output-stream/imm32
+ 425     # . . call
+ 426     e8/call  clear-stream/disp32
+ 427     # . . discard args
+ 428     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 429     # . clear-stream(_test-output-buffered-file+4)
+ 430     # . . push args
+ 431     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 432     05/add-to-EAX  4/imm32
+ 433     50/push-EAX
+ 434     # . . call
+ 435     e8/call  clear-stream/disp32
+ 436     # . . discard args
+ 437     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 438     # initialize input
+ 439     # . write(_test-input-stream, "== abcd 0x1")
+ 440     # . . push args
+ 441     68/push  "== abcd 0x1"/imm32
+ 442     68/push  _test-input-stream/imm32
+ 443     # . . call
+ 444     e8/call  write/disp32
+ 445     # . . discard args
+ 446     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 447     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 448     # . . push args
+ 449     68/push  _test-output-buffered-file/imm32
+ 450     68/push  _test-input-buffered-file/imm32
+ 451     # . . call
+ 452     e8/call  convert/disp32
+ 453     # . . discard args
+ 454     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 455     # check that the line just passed through
+ 456     # . flush(_test-output-buffered-file)
+ 457     # . . push args
+ 458     68/push  _test-output-buffered-file/imm32
+ 459     # . . call
+ 460     e8/call  flush/disp32
+ 461     # . . discard args
+ 462     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 463 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
+ 489     # . check-stream-equal(_test-output-stream, "== abcd 0x1 \n", msg)
+ 490     # . . push args
+ 491     68/push  "F - test-convert-passes-most-words-through"/imm32
+ 492     68/push  "== abcd 0x1 \n"/imm32
+ 493     68/push  _test-output-stream/imm32
+ 494     # . . call
+ 495     e8/call  check-stream-equal/disp32
+ 496     # . . discard args
+ 497     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 498     # . epilog
+ 499     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 500     5d/pop-to-EBP
+ 501     c3/return
+ 502 
+ 503 test-convert-direct-mode:
+ 504     # . prolog
+ 505     55/push-EBP
+ 506     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 507     # setup
+ 508     # . clear-stream(_test-input-stream)
+ 509     # . . push args
+ 510     68/push  _test-input-stream/imm32
+ 511     # . . call
+ 512     e8/call  clear-stream/disp32
+ 513     # . . discard args
+ 514     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 515     # . clear-stream(_test-input-buffered-file+4)
+ 516     # . . push args
+ 517     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 518     05/add-to-EAX  4/imm32
+ 519     50/push-EAX
+ 520     # . . call
+ 521     e8/call  clear-stream/disp32
+ 522     # . . discard args
+ 523     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 524     # . clear-stream(_test-output-stream)
+ 525     # . . push args
+ 526     68/push  _test-output-stream/imm32
+ 527     # . . call
+ 528     e8/call  clear-stream/disp32
+ 529     # . . discard args
+ 530     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 531     # . clear-stream(_test-output-buffered-file+4)
+ 532     # . . push args
+ 533     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 534     05/add-to-EAX  4/imm32
+ 535     50/push-EAX
+ 536     # . . call
+ 537     e8/call  clear-stream/disp32
+ 538     # . . discard args
+ 539     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 540     # initialize input
+ 541     # . write(_test-input-stream, "ab %ecx")
+ 542     # . . push args
+ 543     68/push  "ab %ecx"/imm32
+ 544     68/push  _test-input-stream/imm32
+ 545     # . . call
+ 546     e8/call  write/disp32
+ 547     # . . discard args
+ 548     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 549     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 550     # . . push args
+ 551     68/push  _test-output-buffered-file/imm32
+ 552     68/push  _test-input-buffered-file/imm32
+ 553     # . . call
+ 554     e8/call  convert/disp32
+ 555     # . . discard args
+ 556     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 557     # check that the line just passed through
+ 558     # . flush(_test-output-buffered-file)
+ 559     # . . push args
+ 560     68/push  _test-output-buffered-file/imm32
+ 561     # . . call
+ 562     e8/call  flush/disp32
+ 563     # . . discard args
+ 564     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 565 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
+ 591     # . check-stream-equal(_test-output-stream, "ab 3/mod/direct 0x00000001/rm32 \n", msg)
+ 592     # . . push args
+ 593     68/push  "F - test-convert-direct-mode"/imm32
+ 594     68/push  "ab 3/mod/direct 0x00000001/rm32 \n"/imm32
+ 595     68/push  _test-output-stream/imm32
+ 596     # . . call
+ 597     e8/call  check-stream-equal/disp32
+ 598     # . . discard args
+ 599     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 600     # . epilog
+ 601     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 602     5d/pop-to-EBP
+ 603     c3/return
+ 604 
+ 605 test-convert-register-indirect-mode:
+ 606     # . prolog
+ 607     55/push-EBP
+ 608     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 609     # setup
+ 610     # . clear-stream(_test-input-stream)
+ 611     # . . push args
+ 612     68/push  _test-input-stream/imm32
+ 613     # . . call
+ 614     e8/call  clear-stream/disp32
+ 615     # . . discard args
+ 616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 617     # . clear-stream(_test-input-buffered-file+4)
+ 618     # . . push args
+ 619     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 620     05/add-to-EAX  4/imm32
+ 621     50/push-EAX
+ 622     # . . call
+ 623     e8/call  clear-stream/disp32
+ 624     # . . discard args
+ 625     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 626     # . clear-stream(_test-output-stream)
+ 627     # . . push args
+ 628     68/push  _test-output-stream/imm32
+ 629     # . . call
+ 630     e8/call  clear-stream/disp32
+ 631     # . . discard args
+ 632     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 633     # . clear-stream(_test-output-buffered-file+4)
+ 634     # . . push args
+ 635     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 636     05/add-to-EAX  4/imm32
+ 637     50/push-EAX
+ 638     # . . call
+ 639     e8/call  clear-stream/disp32
+ 640     # . . discard args
+ 641     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 642     # initialize input
+ 643     # . write(_test-input-stream, "ab *ecx")
+ 644     # . . push args
+ 645     68/push  "ab *ecx"/imm32
+ 646     68/push  _test-input-stream/imm32
+ 647     # . . call
+ 648     e8/call  write/disp32
+ 649     # . . discard args
+ 650     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 651     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 652     # . . push args
+ 653     68/push  _test-output-buffered-file/imm32
+ 654     68/push  _test-input-buffered-file/imm32
+ 655     # . . call
+ 656     e8/call  convert/disp32
+ 657     # . . discard args
+ 658     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 659     # check that the line just passed through
+ 660     # . flush(_test-output-buffered-file)
+ 661     # . . push args
+ 662     68/push  _test-output-buffered-file/imm32
+ 663     # . . call
+ 664     e8/call  flush/disp32
+ 665     # . . discard args
+ 666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 667 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
+ 693     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 0x00000001/rm32 \n", msg)
+ 694     # . . push args
+ 695     68/push  "F - test-convert-indirect-mode"/imm32
+ 696     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
+ 697     68/push  _test-output-stream/imm32
+ 698     # . . call
+ 699     e8/call  check-stream-equal/disp32
+ 700     # . . discard args
+ 701     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 702     # . epilog
+ 703     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 704     5d/pop-to-EBP
+ 705     c3/return
+ 706 
+ 707 test-convert-register-indirect-mode-without-displacement:
+ 708     # . prolog
+ 709     55/push-EBP
+ 710     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 711     # setup
+ 712     # . clear-stream(_test-input-stream)
+ 713     # . . push args
+ 714     68/push  _test-input-stream/imm32
+ 715     # . . call
+ 716     e8/call  clear-stream/disp32
+ 717     # . . discard args
+ 718     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 719     # . clear-stream(_test-input-buffered-file+4)
+ 720     # . . push args
+ 721     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 722     05/add-to-EAX  4/imm32
+ 723     50/push-EAX
+ 724     # . . call
+ 725     e8/call  clear-stream/disp32
+ 726     # . . discard args
+ 727     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 728     # . clear-stream(_test-output-stream)
+ 729     # . . push args
+ 730     68/push  _test-output-stream/imm32
+ 731     # . . call
+ 732     e8/call  clear-stream/disp32
+ 733     # . . discard args
+ 734     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 735     # . clear-stream(_test-output-buffered-file+4)
+ 736     # . . push args
+ 737     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 738     05/add-to-EAX  4/imm32
+ 739     50/push-EAX
+ 740     # . . call
+ 741     e8/call  clear-stream/disp32
+ 742     # . . discard args
+ 743     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 744     # initialize input
+ 745     # . write(_test-input-stream, "ab *(ecx)")
+ 746     # . . push args
+ 747     68/push  "ab *(ecx)"/imm32
+ 748     68/push  _test-input-stream/imm32
+ 749     # . . call
+ 750     e8/call  write/disp32
+ 751     # . . discard args
+ 752     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 753     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 754     # . . push args
+ 755     68/push  _test-output-buffered-file/imm32
+ 756     68/push  _test-input-buffered-file/imm32
+ 757     # . . call
+ 758     e8/call  convert/disp32
+ 759     # . . discard args
+ 760     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 761     # check that the line just passed through
+ 762     # . flush(_test-output-buffered-file)
+ 763     # . . push args
+ 764     68/push  _test-output-buffered-file/imm32
+ 765     # . . call
+ 766     e8/call  flush/disp32
+ 767     # . . discard args
+ 768     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 769 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
+ 795     # . check-stream-equal(_test-output-stream, "ab 0/mod/indirect 1/rm32 \n", msg)
+ 796     # . . push args
+ 797     68/push  "F - test-convert-indirect-mode-without-displacement"/imm32
+ 798     68/push  "ab 0/mod/indirect 0x00000001/rm32 \n"/imm32
+ 799     68/push  _test-output-stream/imm32
+ 800     # . . call
+ 801     e8/call  check-stream-equal/disp32
+ 802     # . . discard args
+ 803     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 804     # . epilog
+ 805     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 806     5d/pop-to-EBP
+ 807     c3/return
+ 808 
+ 809 test-convert-register-indirect-mode-with-displacement:
+ 810     # . prolog
+ 811     55/push-EBP
+ 812     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 813     # setup
+ 814     # . clear-stream(_test-input-stream)
+ 815     # . . push args
+ 816     68/push  _test-input-stream/imm32
+ 817     # . . call
+ 818     e8/call  clear-stream/disp32
+ 819     # . . discard args
+ 820     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 821     # . clear-stream(_test-input-buffered-file+4)
+ 822     # . . push args
+ 823     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 824     05/add-to-EAX  4/imm32
+ 825     50/push-EAX
+ 826     # . . call
+ 827     e8/call  clear-stream/disp32
+ 828     # . . discard args
+ 829     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 830     # . clear-stream(_test-output-stream)
+ 831     # . . push args
+ 832     68/push  _test-output-stream/imm32
+ 833     # . . call
+ 834     e8/call  clear-stream/disp32
+ 835     # . . discard args
+ 836     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 837     # . clear-stream(_test-output-buffered-file+4)
+ 838     # . . push args
+ 839     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 840     05/add-to-EAX  4/imm32
+ 841     50/push-EAX
+ 842     # . . call
+ 843     e8/call  clear-stream/disp32
+ 844     # . . discard args
+ 845     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 846     # initialize input
+ 847     # . write(_test-input-stream, "ab *(ecx+4)")
+ 848     # . . push args
+ 849     68/push  "ab *(ecx+4)"/imm32
+ 850     68/push  _test-input-stream/imm32
+ 851     # . . call
+ 852     e8/call  write/disp32
+ 853     # . . discard args
+ 854     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 855     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 856     # . . push args
+ 857     68/push  _test-output-buffered-file/imm32
+ 858     68/push  _test-input-buffered-file/imm32
+ 859     # . . call
+ 860     e8/call  convert/disp32
+ 861     # . . discard args
+ 862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 863     # check that the line just passed through
+ 864     # . flush(_test-output-buffered-file)
+ 865     # . . push args
+ 866     68/push  _test-output-buffered-file/imm32
+ 867     # . . call
+ 868     e8/call  flush/disp32
+ 869     # . . discard args
+ 870     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 871 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
+ 897     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 1/rm32 4/disp32 \n", msg)
+ 898     # . . push args
+ 899     68/push  "F - test-convert-indirect-mode-with-displacement"/imm32
+ 900     68/push  "ab 2/mod/*+disp32 0x00000001/rm32 0x00000004/disp32 \n"/imm32
+ 901     68/push  _test-output-stream/imm32
+ 902     # . . call
+ 903     e8/call  check-stream-equal/disp32
+ 904     # . . discard args
+ 905     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+ 906     # . epilog
+ 907     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+ 908     5d/pop-to-EBP
+ 909     c3/return
+ 910 
+ 911 # boss level
+ 912 test-convert-register-indirect-mode-with-sib-byte:
+ 913     # . prolog
+ 914     55/push-EBP
+ 915     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 916     # setup
+ 917     # . clear-stream(_test-input-stream)
+ 918     # . . push args
+ 919     68/push  _test-input-stream/imm32
+ 920     # . . call
+ 921     e8/call  clear-stream/disp32
+ 922     # . . discard args
+ 923     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 924     # . clear-stream(_test-input-buffered-file+4)
+ 925     # . . push args
+ 926     b8/copy-to-EAX  _test-input-buffered-file/imm32
+ 927     05/add-to-EAX  4/imm32
+ 928     50/push-EAX
+ 929     # . . call
+ 930     e8/call  clear-stream/disp32
+ 931     # . . discard args
+ 932     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 933     # . clear-stream(_test-output-stream)
+ 934     # . . push args
+ 935     68/push  _test-output-stream/imm32
+ 936     # . . call
+ 937     e8/call  clear-stream/disp32
+ 938     # . . discard args
+ 939     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 940     # . clear-stream(_test-output-buffered-file+4)
+ 941     # . . push args
+ 942     b8/copy-to-EAX  _test-output-buffered-file/imm32
+ 943     05/add-to-EAX  4/imm32
+ 944     50/push-EAX
+ 945     # . . call
+ 946     e8/call  clear-stream/disp32
+ 947     # . . discard args
+ 948     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 949     # initialize input
+ 950     # . write(_test-input-stream, "ab *(ecx + edx<<3 + 4)")
+ 951     # . . push args
+ 952     68/push  "ab *(ecx + edx<<3 + 4)"/imm32
+ 953     68/push  _test-input-stream/imm32
+ 954     # . . call
+ 955     e8/call  write/disp32
+ 956     # . . discard args
+ 957     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 958     # convert(_test-input-buffered-file, _test-output-buffered-file)
+ 959     # . . push args
+ 960     68/push  _test-output-buffered-file/imm32
+ 961     68/push  _test-input-buffered-file/imm32
+ 962     # . . call
+ 963     e8/call  convert/disp32
+ 964     # . . discard args
+ 965     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 966     # check that the line just passed through
+ 967     # . flush(_test-output-buffered-file)
+ 968     # . . push args
+ 969     68/push  _test-output-buffered-file/imm32
+ 970     # . . call
+ 971     e8/call  flush/disp32
+ 972     # . . discard args
+ 973     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 974 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
+1000     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale 4/disp32 \n", msg)
+1001     # . . push args
+1002     68/push  "F - test-convert-indirect-mode-with-sib-byte"/imm32
+1003     68/push  "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0x00000004/disp32 \n"/imm32
+1004     68/push  _test-output-stream/imm32
+1005     # . . call
+1006     e8/call  check-stream-equal/disp32
+1007     # . . discard args
+1008     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1009     # . epilog
+1010     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1011     5d/pop-to-EBP
+1012     c3/return
+1013 
+1014 test-convert-register-indirect-mode-with-sib-byte-negative-displacement:
+1015     # . prolog
+1016     55/push-EBP
+1017     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1018     # setup
+1019     # . clear-stream(_test-input-stream)
+1020     # . . push args
+1021     68/push  _test-input-stream/imm32
+1022     # . . call
+1023     e8/call  clear-stream/disp32
+1024     # . . discard args
+1025     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1026     # . clear-stream(_test-input-buffered-file+4)
+1027     # . . push args
+1028     b8/copy-to-EAX  _test-input-buffered-file/imm32
+1029     05/add-to-EAX  4/imm32
+1030     50/push-EAX
+1031     # . . call
+1032     e8/call  clear-stream/disp32
+1033     # . . discard args
+1034     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1035     # . clear-stream(_test-output-stream)
+1036     # . . push args
+1037     68/push  _test-output-stream/imm32
+1038     # . . call
+1039     e8/call  clear-stream/disp32
+1040     # . . discard args
+1041     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1042     # . clear-stream(_test-output-buffered-file+4)
+1043     # . . push args
+1044     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1045     05/add-to-EAX  4/imm32
+1046     50/push-EAX
+1047     # . . call
+1048     e8/call  clear-stream/disp32
+1049     # . . discard args
+1050     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1051     # initialize input
+1052     # . write(_test-input-stream, "ab *(ecx + edx<<3 - 4)")
+1053     # . . push args
+1054     68/push  "ab *(ecx + edx<<3 - 4)"/imm32
+1055     68/push  _test-input-stream/imm32
+1056     # . . call
+1057     e8/call  write/disp32
+1058     # . . discard args
+1059     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1060     # convert(_test-input-buffered-file, _test-output-buffered-file)
+1061     # . . push args
+1062     68/push  _test-output-buffered-file/imm32
+1063     68/push  _test-input-buffered-file/imm32
+1064     # . . call
+1065     e8/call  convert/disp32
+1066     # . . discard args
+1067     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1068     # check that the line just passed through
+1069     # . flush(_test-output-buffered-file)
+1070     # . . push args
+1071     68/push  _test-output-buffered-file/imm32
+1072     # . . call
+1073     e8/call  flush/disp32
+1074     # . . discard args
+1075     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1076 +-- 26 lines: #?     # dump _test-output-stream --------------------------------------------------------------------------------------------------------------
+1102     # . check-stream-equal(_test-output-stream, "ab 2/mod/*+disp32 4/rm32/sib 1/base 2/index 3/scale -4/disp32 \n", msg)
+1103     # . . push args
+1104     68/push  "F - test-convert-indirect-mode-with-sib-byte-negative-displacement"/imm32
+1105     68/push  "ab 2/mod/*+disp32 4/rm32/sib 0x00000001/base 0x00000002/index 0x00000003/scale 0xfffffffc/disp32 \n"/imm32
+1106     68/push  _test-output-stream/imm32
+1107     # . . call
+1108     e8/call  check-stream-equal/disp32
+1109     # . . discard args
+1110     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1111     # . epilog
+1112     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1113     5d/pop-to-EBP
+1114     c3/return
+1115 
+1116 # beware: modifies 'word'
+1117 emit-direct-mode:  # word : (address slice), out : (address buffered-file)
+1118     # . prolog
+1119     55/push-EBP
+1120     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1121     # . save registers
+1122     50/push-EAX
+1123     # ++word->start
+1124     # . EAX = word
+1125     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   8/disp8         .                 # copy *(EBP+8) to EAX
+1126     # . ++(*EAX)
+1127     ff          0/subop/increment   0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # increment *EAX
+1128     # reg-num/EAX = get-slice(Registers, word, row-size=8)
+1129     # . . push args
+1130     68/push  "Registers"/imm32
+1131     68/push  8/imm32/row-size
+1132     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1133     68/push  Registers/imm32
+1134     # . . call
+1135     e8/call  get-slice/disp32
+1136     # . . discard args
+1137     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+1138     # write-buffered(out, "3/mod/direct ")
+1139     # . . push args
+1140     68/push  "3/mod/direct "/imm32
+1141     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1142     # . . call
+1143     e8/call  write-buffered/disp32
+1144     # . . discard args
+1145     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1146     # print-int32-buffered(out, *EAX)
+1147     # . . push args
+1148     ff          6/subop/push        0/mod/indirect  0/rm32/EAX    .           .             .           .           .               .                 # push *EAX
+1149     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1150     # . . call
+1151     e8/call  print-int32-buffered/disp32
+1152     # . . discard args
+1153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1154     # write-buffered(out, "/rm32")
+1155     # . . push args
+1156     68/push  "/rm32"/imm32
+1157     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+1158     # . . call
+1159     e8/call  write-buffered/disp32
+1160     # . . discard args
+1161     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1162 $emit-direct-mode:end:
+1163     # . restore registers
+1164     58/pop-to-EAX
+1165     # . epilog
+1166     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1167     5d/pop-to-EBP
+1168     c3/return
+1169 
+1170 test-emit-direct-mode:
+1171     # . prolog
+1172     55/push-EBP
+1173     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1174     # setup
+1175     # . clear-stream(_test-output-stream)
+1176     # . . push args
+1177     68/push  _test-output-stream/imm32
+1178     # . . call
+1179     e8/call  clear-stream/disp32
+1180     # . . discard args
+1181     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1182     # . clear-stream(_test-output-buffered-file+4)
+1183     # . . push args
+1184     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1185     05/add-to-EAX  4/imm32
+1186     50/push-EAX
+1187     # . . call
+1188     e8/call  clear-stream/disp32
+1189     # . . discard args
+1190     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1191     # var slice/ECX = "%eax"
+1192     b8/copy-to-EAX  "%eax"/imm32
+1193     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1194     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
+1195     05/add-to-EAX  4/imm32
+1196     # . ECX = {EAX, ECX}
+1197     51/push-ECX
+1198     50/push-EAX
+1199     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1200     # emit-direct-mode(str, _test-output-buffered-file)
+1201     # . . push args
+1202     68/push  _test-output-buffered-file/imm32
+1203     51/push-ECX
+1204     # . . call
+1205     e8/call  emit-direct-mode/disp32
+1206     # . . discard args
+1207     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32         # add to ESP
+1208     # . flush(_test-output-buffered-file)
+1209     # . . push args
+1210     68/push  _test-output-buffered-file/imm32
+1211     # . . call
+1212     e8/call  flush/disp32
+1213     # . . discard args
+1214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1215 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+1241     # check-stream-equal(_test-output-stream, "3/mod/direct 0/rm32", msg)
+1242     # . . push args
+1243     68/push  "F - test-emit-direct-mode/0"/imm32
+1244     68/push  "3/mod/direct 0x00000000/rm32"/imm32
+1245     68/push  _test-output-stream/imm32
+1246     # . . call
+1247     e8/call  check-stream-equal/disp32
+1248     # . . discard args
+1249     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1250     # . epilog
+1251     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1252     5d/pop-to-EBP
+1253     c3/return
+1254 
+1255 test-emit-direct-mode-2:
+1256     # . prolog
+1257     55/push-EBP
+1258     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1259     # setup
+1260     # . clear-stream(_test-output-stream)
+1261     # . . push args
+1262     68/push  _test-output-stream/imm32
+1263     # . . call
+1264     e8/call  clear-stream/disp32
+1265     # . . discard args
+1266     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1267     # . clear-stream(_test-output-buffered-file+4)
+1268     # . . push args
+1269     b8/copy-to-EAX  _test-output-buffered-file/imm32
+1270     05/add-to-EAX  4/imm32
+1271     50/push-EAX
+1272     # . . call
+1273     e8/call  clear-stream/disp32
+1274     # . . discard args
+1275     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1276     # var slice/ECX = "%edi"
+1277     b8/copy-to-EAX  "%edi"/imm32
+1278     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+1279     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
+1280     05/add-to-EAX  4/imm32
+1281     # . ECX = {EAX, ECX}
+1282     51/push-ECX
+1283     50/push-EAX
+1284     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1285     # emit-direct-mode(str/ECX, _test-output-buffered-file)
+1286     # . . push args
+1287     68/push  _test-output-buffered-file/imm32
+1288     51/push-ECX
+1289     # . . call
+1290     e8/call  emit-direct-mode/disp32
+1291     # . . discard args
+1292     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32         # add to ESP
+1293     # . flush(_test-output-buffered-file)
+1294     # . . push args
+1295     68/push  _test-output-buffered-file/imm32
+1296     # . . call
+1297     e8/call  flush/disp32
+1298     # . . discard args
+1299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1300 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+1326     # check-stream-equal(_test-output-stream, "3/mod/direct 7/rm32", msg)
+1327     # . . push args
+1328     68/push  "F - test-emit-direct-mode/1"/imm32
+1329     68/push  "3/mod/direct 0x00000007/rm32"/imm32
+1330     68/push  _test-output-stream/imm32
+1331     # . . call
+1332     e8/call  check-stream-equal/disp32
+1333     # . . discard args
+1334     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1335     # . epilog
+1336     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1337     5d/pop-to-EBP
+1338     c3/return
+1339 
+1340 # (re)compute the bounds of the next word or parenthetical expression in the line
+1341 # return empty string on reaching end of file
+1342 #
+1343 # error messages considered:
+1344 #   * ...                   -> error: no space after '*'
+1345 #   *(...                   -> error: *(...) expression must be all on a single line
+1346 next-word-or-expression:  # line : (address stream byte), out : (address slice)
+1347     # . prolog
+1348     55/push-EBP
+1349     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1350     # . save registers
+1351     50/push-EAX
+1352     51/push-ECX
+1353     56/push-ESI
+1354     57/push-EDI
+1355     # ESI = line
+1356     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+1357     # EDI = out
+1358     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
+1359     # skip-chars-matching(line, ' ')
+1360     # . . push args
+1361     68/push  0x20/imm32/space
+1362     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1363     # . . call
+1364     e8/call  skip-chars-matching/disp32
+1365     # . . discard args
+1366     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1367 $next-word-or-expression:check0:
+1368     # if (line->read >= line->write) clear out and return
+1369     # . EAX = line->read
+1370     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
+1371     # . if (EAX < line->write) goto next check
+1372     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
+1373     7c/jump-if-lesser  $next-word-or-expression:check-for-comment/disp8
+1374     # . return out = {0, 0}
+1375     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
+1376     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
+1377     e9/jump  $next-word-or-expression:end/disp32
+1378 $next-word-or-expression:check-for-comment:
+1379     # out->start = &line->data[line->read]
+1380     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+1381     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
+1382     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
+1383     # if (line->data[line->read] != '#') goto next check
+1384     # . EAX = line->data[line->read]
+1385     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+1386     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
+1387     # . compare
+1388     3d/compare-EAX-and  0x23/imm32/pound
+1389     75/jump-if-not-equal  $next-word-or-expression:check-for-string-literal/disp8
+1390 $next-word-or-expression:comment:
+1391     # out->end = &line->data[line->write]
+1392     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+1393     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
+1394     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+1395     # line->read = line->write  # skip rest of line
+1396     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+1397     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
+1398     # return
+1399     eb/jump  $next-word-or-expression:end/disp8
+1400 $next-word-or-expression:check-for-string-literal:
+1401     # if (line->data[line->read] != '"') goto next check
+1402     # . EAX = line->data[line->read]
+1403     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+1404     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
+1405     # . compare
+1406     3d/compare-EAX-and  0x22/imm32/dquote
+1407     75/jump-if-not-equal  $next-word-or-expression:check-for-paren/disp8
+1408 $next-word-or-expression:string-literal:
+1409     # skip-string(line)
+1410     # . . push args
+1411     56/push-ESI
+1412     # . . call
+1413     e8/call  skip-string/disp32
+1414     # . . discard args
+1415     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1416     # skip rest of word
+1417     eb/jump  $next-word-or-expression:regular-word/disp8
+1418 $next-word-or-expression:check-for-paren:
+1419     # if (line->data[line->read] != '*') goto next check
+1420     # . EAX = line->data[line->read]
+1421     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+1422     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
+1423     # . compare
+1424     3d/compare-EAX-and  0x2a/imm32/asterisk
+1425     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
+1426     # if (line->data[line->read] == ' ') goto error1
+1427     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
+1428     # . compare
+1429     3d/compare-EAX-and  0x20/imm32/space
+1430     74/jump-if-equal  $next-word-or-expression:error1/disp8
+1431     # if (line->data[line->read] != '(') goto regular word
+1432     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
+1433     # . compare
+1434     3d/compare-EAX-and  0x28/imm32/open-paren
+1435     75/jump-if-not-equal  $next-word-or-expression:regular-word/disp8
+1436 $next-word-or-expression:paren:
+1437     # skip-until-close-paren(line)
+1438     # . . push args
+1439     56/push-ESI
+1440     # . . call
+1441     e8/call  skip-until-close-paren/disp32
+1442     # . . discard args
+1443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1444     # if (line->data[line->read] != ')') goto error2
+1445     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+1446     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
+1447     # . compare
+1448     3d/compare-EAX-and  0x29/imm32/close-paren
+1449     75/jump-if-not-equal  $next-word-or-expression:error2/disp8
+1450     # skip ')'
+1451     ff          0/subop/increment   1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # increment *(ESI+4)
+1452     # fall through
+1453 $next-word-or-expression:regular-word:
+1454     # skip-chars-not-matching-whitespace(line)  # including trailing newline
+1455     # . . push args
+1456     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+1457     # . . call
+1458     e8/call  skip-chars-not-matching-whitespace/disp32
+1459     # . . discard args
+1460     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1461     # out->end = &line->data[line->read]
+1462     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
+1463     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
+1464     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
+1465 $next-word-or-expression:end:
+1466     # . restore registers
+1467     5f/pop-to-EDI
+1468     5e/pop-to-ESI
+1469     59/pop-to-ECX
+1470     58/pop-to-EAX
+1471     # . epilog
+1472     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1473     5d/pop-to-EBP
+1474     c3/return
+1475 
+1476 $next-word-or-expression:error1:
+1477     # print(stderr, "error: no space allowed after '*' in '" line "'")
+1478     # . write-buffered(Stderr, "error: no space allowed after '*' in '")
+1479     # . . push args
+1480     68/push  "error: no space allowed after '*' in '"/imm32
+1481     68/push  Stderr/imm32
+1482     # . . call
+1483     e8/call  write-buffered/disp32
+1484     # . . discard args
+1485     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1486     # . write-stream-data(Stderr, line)
+1487     # . . push args
+1488     56/push-ESI
+1489     68/push  Stderr/imm32
+1490     # . . call
+1491     e8/call  write-stream-data/disp32
+1492     # . . discard args
+1493     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1494     # . write-buffered(Stderr, "'")
+1495     # . . push args
+1496     68/push  "'"/imm32
+1497     68/push  Stderr/imm32
+1498     # . . call
+1499     e8/call  write-buffered/disp32
+1500     # . . discard args
+1501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1502     # . flush(Stderr)
+1503     # . . push args
+1504     68/push  Stderr/imm32
+1505     # . . call
+1506     e8/call  flush/disp32
+1507     # . . discard args
+1508     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1509     # . syscall(exit, 1)
+1510     bb/copy-to-EBX  1/imm32
+1511     b8/copy-to-EAX  1/imm32/exit
+1512     cd/syscall  0x80/imm8
+1513     # never gets here
+1514 
+1515 $next-word-or-expression:error2:
+1516     # print(stderr, "error: no space allowed after '*' in '" line "'")
+1517     # . write-buffered(Stderr, "error: *(...) expression must be all on a single line in '")
+1518     # . . push args
+1519     68/push  "error: *(...) expression must be all on a single line in '"/imm32
+1520     68/push  Stderr/imm32
+1521     # . . call
+1522     e8/call  write-buffered/disp32
+1523     # . . discard args
+1524     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1525     # . write-stream-data(Stderr, line)
+1526     # . . push args
+1527     56/push-ESI
+1528     68/push  Stderr/imm32
+1529     # . . call
+1530     e8/call  write-stream-data/disp32
+1531     # . . discard args
+1532     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1533     # . write-buffered(Stderr, "'")
+1534     # . . push args
+1535     68/push  "'"/imm32
+1536     68/push  Stderr/imm32
+1537     # . . call
+1538     e8/call  write-buffered/disp32
+1539     # . . discard args
+1540     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1541     # . flush(Stderr)
+1542     # . . push args
+1543     68/push  Stderr/imm32
+1544     # . . call
+1545     e8/call  flush/disp32
+1546     # . . discard args
+1547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1548     # . syscall(exit, 1)
+1549     bb/copy-to-EBX  1/imm32
+1550     b8/copy-to-EAX  1/imm32/exit
+1551     cd/syscall  0x80/imm8
+1552     # never gets here
+1553 
+1554 test-next-word-or-expression:
+1555     # . prolog
+1556     55/push-EBP
+1557     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1558     # setup
+1559     # . clear-stream(_test-input-stream)
+1560     # . . push args
+1561     68/push  _test-input-stream/imm32
+1562     # . . call
+1563     e8/call  clear-stream/disp32
+1564     # . . discard args
+1565     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1566     # var slice/ECX = {0, 0}
+1567     68/push  0/imm32/end
+1568     68/push  0/imm32/start
+1569     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1570     # write(_test-input-stream, "  ab")
+1571     # . . push args
+1572     68/push  "  ab"/imm32
+1573     68/push  _test-input-stream/imm32
+1574     # . . call
+1575     e8/call  write/disp32
+1576     # . . discard args
+1577     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1578     # next-word-or-expression(_test-input-stream, slice)
+1579     # . . push args
+1580     51/push-ECX
+1581     68/push  _test-input-stream/imm32
+1582     # . . call
+1583     e8/call  next-word-or-expression/disp32
+1584     # . . discard args
+1585     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1586     # check-ints-equal(_test-input-stream->read, 4, msg)
+1587     # . . push args
+1588     68/push  "F - test-next-word-or-expression/updates-stream-read-correctly"/imm32
+1589     68/push  4/imm32
+1590     b8/copy-to-EAX  _test-input-stream/imm32
+1591     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+1592     # . . call
+1593     e8/call  check-ints-equal/disp32
+1594     # . . discard args
+1595     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1596     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
+1597     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
+1598     # . . push args
+1599     68/push  "F - test-next-word-or-expression: start"/imm32
+1600     68/push  0xe/imm32
+1601     # . . push slice->start - _test-input-stream
+1602     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+1603     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1604     50/push-EAX
+1605     # . . call
+1606     e8/call  check-ints-equal/disp32
+1607     # . . discard args
+1608     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1609     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
+1610     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
+1611     # . . push args
+1612     68/push  "F - test-next-word-or-expression: end"/imm32
+1613     68/push  0x10/imm32
+1614     # . . push slice->end - _test-input-stream
+1615     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1616     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1617     50/push-EAX
+1618     # . . call
+1619     e8/call  check-ints-equal/disp32
+1620     # . . discard args
+1621     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1622     # . epilog
+1623     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1624     5d/pop-to-EBP
+1625     c3/return
+1626 
+1627 test-next-word-or-expression-returns-whole-comment:
+1628     # . prolog
+1629     55/push-EBP
+1630     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1631     # setup
+1632     # . clear-stream(_test-input-stream)
+1633     # . . push args
+1634     68/push  _test-input-stream/imm32
+1635     # . . call
+1636     e8/call  clear-stream/disp32
+1637     # . . discard args
+1638     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1639     # var slice/ECX = {0, 0}
+1640     68/push  0/imm32/end
+1641     68/push  0/imm32/start
+1642     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1643     # write(_test-input-stream, "  # a")
+1644     # . . push args
+1645     68/push  "  # a"/imm32
+1646     68/push  _test-input-stream/imm32
+1647     # . . call
+1648     e8/call  write/disp32
+1649     # . . discard args
+1650     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1651     # next-word-or-expression(_test-input-stream, slice)
+1652     # . . push args
+1653     51/push-ECX
+1654     68/push  _test-input-stream/imm32
+1655     # . . call
+1656     e8/call  next-word-or-expression/disp32
+1657     # . . discard args
+1658     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1659     # check-ints-equal(_test-input-stream->read, 5, msg)
+1660     # . . push args
+1661     68/push  "F - test-next-word-or-expression-returns-whole-comment/updates-stream-read-correctly"/imm32
+1662     68/push  5/imm32
+1663     b8/copy-to-EAX  _test-input-stream/imm32
+1664     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+1665     # . . call
+1666     e8/call  check-ints-equal/disp32
+1667     # . . discard args
+1668     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1669     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
+1670     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
+1671     # . . push args
+1672     68/push  "F - test-next-word-or-expression-returns-whole-comment: start"/imm32
+1673     68/push  0xe/imm32
+1674     # . . push slice->start - _test-input-stream
+1675     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+1676     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1677     50/push-EAX
+1678     # . . call
+1679     e8/call  check-ints-equal/disp32
+1680     # . . discard args
+1681     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1682     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
+1683     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
+1684     # . . push args
+1685     68/push  "F - test-next-word-or-expression-returns-whole-comment: end"/imm32
+1686     68/push  0x11/imm32
+1687     # . . push slice->end - _test-input-stream
+1688     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1689     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1690     50/push-EAX
+1691     # . . call
+1692     e8/call  check-ints-equal/disp32
+1693     # . . discard args
+1694     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1695     # . epilog
+1696     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1697     5d/pop-to-EBP
+1698     c3/return
+1699 
+1700 test-next-word-or-expression-returns-empty-slice-on-eof:
+1701     # . prolog
+1702     55/push-EBP
+1703     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1704     # setup
+1705     # . clear-stream(_test-input-stream)
+1706     # . . push args
+1707     68/push  _test-input-stream/imm32
+1708     # . . call
+1709     e8/call  clear-stream/disp32
+1710     # . . discard args
+1711     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1712     # var slice/ECX = {0, 0}
+1713     68/push  0/imm32/end
+1714     68/push  0/imm32/start
+1715     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1716     # write nothing to _test-input-stream
+1717     # next-word-or-expression(_test-input-stream, slice)
+1718     # . . push args
+1719     51/push-ECX
+1720     68/push  _test-input-stream/imm32
+1721     # . . call
+1722     e8/call  next-word-or-expression/disp32
+1723     # . . discard args
+1724     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1725     # check-ints-equal(slice->end - slice->start, 0, msg)
+1726     # . . push args
+1727     68/push  "F - test-next-word-or-expression-returns-empty-expression-on-eof"/imm32
+1728     68/push  0/imm32
+1729     # . . push slice->end - slice->start
+1730     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1731     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
+1732     50/push-EAX
+1733     # . . call
+1734     e8/call  check-ints-equal/disp32
+1735     # . . discard args
+1736     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1737     # . epilog
+1738     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1739     5d/pop-to-EBP
+1740     c3/return
+1741 
+1742 test-next-word-or-expression-returns-string-literal:
+1743     # . prolog
+1744     55/push-EBP
+1745     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1746     # setup
+1747     # . clear-stream(_test-input-stream)
+1748     # . . push args
+1749     68/push  _test-input-stream/imm32
+1750     # . . call
+1751     e8/call  clear-stream/disp32
+1752     # . . discard args
+1753     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1754     # var slice/ECX = {0, 0}
+1755     68/push  0/imm32/end
+1756     68/push  0/imm32/start
+1757     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1758     # write(_test-input-stream, " \"a b\"/imm32 ")
+1759     # . . push args
+1760     68/push  " \"a b\"/imm32 "/imm32
+1761     68/push  _test-input-stream/imm32
 1762     # . . call
-1763     e8/call  check-ints-equal/disp32
+1763     e8/call  write/disp32
 1764     # . . discard args
-1765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1766     # check-ints-equal(slice->end - _test-input-stream->data, 13, msg)
-1767     # . check-ints-equal(slice->end - _test-input-stream, 25, msg)
-1768     # . . push args
-1769     68/push  "F - test-next-word-or-expression-returns-whole-expression: end"/imm32
-1770     68/push  0x19/imm32
-1771     # . . push slice->end - _test-input-stream
-1772     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
-1773     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
-1774     50/push-EAX
-1775     # . . call
-1776     e8/call  check-ints-equal/disp32
-1777     # . . discard args
-1778     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1779     # . epilog
-1780     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-1781     5d/pop-to-EBP
-1782     c3/return
-1783 
-1784 # Grammar:
-1785 #   *reg                    -> 0/mod reg/rm32
-1786 #   *(reg)                  -> 0/mod reg/rm32
-1787 #   *(reg+disp)             -> 2/mod reg/rm32 disp/disp32
-1788 #   *(reg1+reg2<<s)         -> 2/mod 4/rm32 reg1/base reg2/index s/scale 0/disp32
-1789 #   *(reg1+reg2<<s+disp)    -> 2/mod 4/rm32 reg1/base reg2/index s/scale disp/disp32
-1790 # Intermediate structure: base, index, scale, disp
-1791 # Default values: base: 0, index: 4 (none), scale: 0, disp: 0
-1792 # beware: modifies 'word'
-1793 parse-effective-address:  # word : (address slice) -> base/EAX, index/ECX, scale/EDX, disp/EBX
-1794     # pseudocode:
-1795     #   ++word->start to skip '*'
-1796     #   initialize defaults: base=0, index=4, scale=0, disp=0
-1797     #   if (*word->start != '(') {
-1798     #     base = get-slice(Registers, word, row-size=8)
-1799     #     return
-1800     #   }
-1801     #   # compound expressions
-1802     #   skip whitespace
-1803     #   read register into base
-1804     #   skip whitespace
-1805     #   if (*word->start == ')') goto end
-1806     #   if (*word->start == '-') goto displacement
-1807     #   if (*word->start != '+') goto error1
-1808     #   ++word->start to skip '+'
-1809     #   skip whitespace
-1810     #   if next 3 characters don't make a register, goto displacement
-1811     #   read register into index
-1812     #   skip whitespace
-1813     #   if (*word->start == ')') goto end
-1814     #   if (*word->start == '<') {
-1815     #     ++word->start to skip '<'
-1816     #     if (*word->start != '<') goto error2
-1817     #     ++word->start to skip '<'
-1818     #     skip whitespace
-1819     #     read integer into scale
-1820     #     skip whitespace
-1821     #     if (*word->start == ')') goto end
-1822     #   }
-1823     #   if (*word->start not in '+' '-') goto error3
-1824     # displacement:
-1825     #   read integer into disp
-1826     #   skip whitespace
-1827     #   if (*word->start != ')') goto error4
-1828     # . prolog
-1829     55/push-EBP
-1830     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-1831     # . save registers
-1832     56/push-ESI
-1833     57/push-EDI
-1834     # ESI = word
-1835     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-1836     # ++word->start to skip '*'
-1837     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
-1838     # initialize defaults
-1839     # base is in EDI; we'll move it to EAX just before we return
-1840     bf/copy-to-EDI  0/imm32
-1841     b9/copy-to-ECX  4/imm32/no-index
-1842     ba/copy-to-EDX  0/imm32/.scale
-1843     bb/copy-to-EBX  0/imm32/disp
-1844 $parse-effective-address:check-for-simple-register:
-1845     # if (*word->start == '(') goto compound expression
-1846     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-1847     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
-1848     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
-1849     3d/compare-EAX-and  0x28/imm32/open-paren
-1850     74/jump-if-equal  $parse-effective-address:compound-expression/disp8
-1851 $parse-effective-address:simple-register:
-1852     # base = get-slice(Registers, word, row-size=8)
-1853     # . EAX = get-slice(Registers, word, row-size=8)
-1854     # . . push args
-1855     68/push  "Registers"/imm32
-1856     68/push  8/imm32/row-size
-1857     56/push-ESI
-1858     68/push  Registers/imm32
+1765     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1766     # next-word-or-expression(_test-input-stream, slice)
+1767     # . . push args
+1768     51/push-ECX
+1769     68/push  _test-input-stream/imm32
+1770     # . . call
+1771     e8/call  next-word-or-expression/disp32
+1772     # . . discard args
+1773     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1774     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
+1775     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
+1776     # . . push args
+1777     68/push  "F - test-next-word-or-expression-returns-string-literal: start"/imm32
+1778     68/push  0xd/imm32
+1779     # . . push slice->start - _test-input-stream
+1780     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+1781     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1782     50/push-EAX
+1783     # . . call
+1784     e8/call  check-ints-equal/disp32
+1785     # . . discard args
+1786     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1787     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
+1788     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
+1789     # . . push args
+1790     68/push  "F - test-next-word-or-expression-returns-string-literal: end"/imm32
+1791     68/push  0x18/imm32
+1792     # . . push slice->end - _test-input-stream
+1793     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1794     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1795     50/push-EAX
+1796     # . . call
+1797     e8/call  check-ints-equal/disp32
+1798     # . . discard args
+1799     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1800     # . epilog
+1801     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1802     5d/pop-to-EBP
+1803     c3/return
+1804 
+1805 test-next-word-or-expression-returns-string-with-escapes:
+1806     # . prolog
+1807     55/push-EBP
+1808     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1809     # setup
+1810     # . clear-stream(_test-input-stream)
+1811     # . . push args
+1812     68/push  _test-input-stream/imm32
+1813     # . . call
+1814     e8/call  clear-stream/disp32
+1815     # . . discard args
+1816     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1817     # var slice/ECX = {0, 0}
+1818     68/push  0/imm32/end
+1819     68/push  0/imm32/start
+1820     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1821     # write(_test-input-stream, " \"a\\\"b\"/x")
+1822     # . . push args
+1823     68/push  " \"a\\\"b\"/x"/imm32
+1824     68/push  _test-input-stream/imm32
+1825     # . . call
+1826     e8/call  write/disp32
+1827     # . . discard args
+1828     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1829     # next-word-or-expression(_test-input-stream, slice)
+1830     # . . push args
+1831     51/push-ECX
+1832     68/push  _test-input-stream/imm32
+1833     # . . call
+1834     e8/call  next-word-or-expression/disp32
+1835     # . . discard args
+1836     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1837     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
+1838     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
+1839     # . . push args
+1840     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: start"/imm32
+1841     68/push  0xd/imm32
+1842     # . . push slice->start - _test-input-stream
+1843     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+1844     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1845     50/push-EAX
+1846     # . . call
+1847     e8/call  check-ints-equal/disp32
+1848     # . . discard args
+1849     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1850     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
+1851     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
+1852     # . . push args
+1853     68/push  "F - test-next-word-or-expression-returns-string-with-escapes: end"/imm32
+1854     68/push  0x15/imm32
+1855     # . . push slice->end - _test-input-stream
+1856     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1857     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1858     50/push-EAX
 1859     # . . call
-1860     e8/call  get-slice/disp32
+1860     e8/call  check-ints-equal/disp32
 1861     # . . discard args
-1862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-1863     # . base = *EAX
-1864     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy *EAX to EDI
-1865     # return
-1866     e9/jump  $parse-effective-address:end/disp32
-1867 $parse-effective-address:compound-expression:
-1868     # ++word->start to skip '('
-1869     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
-1870     # skip whitespace
-1871     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
-1872     # . . push args
-1873     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
-1874     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-1875     # . . call
-1876     e8/call  skip-chars-matching-whitespace-in-slice/disp32
-1877     # . . discard args
-1878     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1879     # . word->start = EAX
-1880     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
-1881     # read register into base
-1882     # . EAX = next-register(word)
-1883     # . . push args
-1884     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1885     # . . call
-1886     e8/call  next-register/disp32
-1887     # . . discard args
-1888     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1889     # . EDI = *EAX
-1890     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy *EAX to EDI
-1891     # skip whitespace
-1892     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
+1862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1863     # . epilog
+1864     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1865     5d/pop-to-EBP
+1866     c3/return
+1867 
+1868 test-next-word-or-expression-returns-whole-expression:
+1869     # . prolog
+1870     55/push-EBP
+1871     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1872     # setup
+1873     # . clear-stream(_test-input-stream)
+1874     # . . push args
+1875     68/push  _test-input-stream/imm32
+1876     # . . call
+1877     e8/call  clear-stream/disp32
+1878     # . . discard args
+1879     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+1880     # var slice/ECX = {0, 0}
+1881     68/push  0/imm32/end
+1882     68/push  0/imm32/start
+1883     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+1884     # write(_test-input-stream, " *(a b)/imm32 ")
+1885     # . . push args
+1886     68/push  " *(a b)/imm32 "/imm32
+1887     68/push  _test-input-stream/imm32
+1888     # . . call
+1889     e8/call  write/disp32
+1890     # . . discard args
+1891     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+1892     # next-word-or-expression(_test-input-stream, slice)
 1893     # . . push args
-1894     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
-1895     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+1894     51/push-ECX
+1895     68/push  _test-input-stream/imm32
 1896     # . . call
-1897     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+1897     e8/call  next-word-or-expression/disp32
 1898     # . . discard args
 1899     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1900     # . word->start = EAX
-1901     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
-1902     # if (*word->start == ')') goto end
-1903     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
-1904     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
-1905     3d/compare-EAX-and  0x29/imm32/close-paren
-1906     0f 84/jump-if-equal  $parse-effective-address:end/disp32
-1907     # if (*word->start == '-') goto displacement
-1908     3d/compare-EAX-and  0x2d/imm32/minus
-1909     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
-1910     # if (*word->start != '+') goto error1
-1911     3d/compare-EAX-and  0x2b/imm32/plus
-1912     0f 85/jump-if-not-equal  $parse-effective-address:error1/disp32
-1913 $parse-effective-address:check-for-index:
-1914     # ++word->start to skip '+'
-1915     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
-1916     # skip whitespace
-1917     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
-1918     # . . push args
-1919     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
-1920     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-1921     # . . call
-1922     e8/call  skip-chars-matching-whitespace-in-slice/disp32
-1923     # . . discard args
-1924     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1925     # . word->start = EAX
-1926     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
-1927 $parse-effective-address:resolve-ambiguity:
-1928     # if next 3 characters don't make a register, goto displacement
-1929     # . spill ECX
-1930     51/push-ECX
-1931     # . var tmp/ECX = {word->start, word->start+3}
-1932     # . . ECX = word->start
-1933     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
-1934     # . . EAX = word->start+3
-1935     05/add-to-EAX  3/imm32
-1936     # . . push
-1937     50/push-EAX
-1938     51/push-ECX
-1939     # . . copy ESP to ECX
-1940     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-1941     # . EAX = maybe-get-slice(Register, tmp, row-size=8)
-1942     # . . push args
-1943     68/push  8/imm32/row-size
-1944     51/push-ECX
-1945     68/push  Registers/imm32
-1946     # . . call
-1947     e8/call  maybe-get-slice/disp32
-1948     # . . discard args
-1949     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-1950     # . reclaim tmp
-1951     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1952     # . restore ECX
-1953     59/pop-to-ECX
-1954     # . if (EAX == 0) goto displacement
-1955     3d/compare-EAX-and  0/imm32
-1956     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
-1957 $parse-effective-address:index:
-1958     # read register into index
-1959     # . EAX = next-register(word)
-1960     # . . push args
-1961     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-1962     # . . call
-1963     e8/call  next-register/disp32
-1964     # . . discard args
-1965     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-1966     # . ECX = *EAX
-1967     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-1968     # skip whitespace
-1969     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
-1970     # . . push args
-1971     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
-1972     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-1973     # . . call
-1974     e8/call  skip-chars-matching-whitespace-in-slice/disp32
-1975     # . . discard args
-1976     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-1977     # . word->start = EAX
-1978     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
-1979     # if (*word->start == ')') goto end
-1980     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
-1981     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
-1982     3d/compare-EAX-and  0x29/imm32/close-paren
-1983     0f 84/jump-if-equal  $parse-effective-address:end/disp32
-1984 $parse-effective-address:check-for-scale:
-1985     # if (*word->start != '<') goto next check
-1986     3d/compare-EAX-and  0x3c/imm32/less-than
-1987     75/jump-if-not-equal  $parse-effective-address:check-for-displacement/disp8
-1988     # ++word->start to skip '<'
-1989     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
-1990     # if (*word->start != '<') goto error2
-1991     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-1992     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
-1993     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
-1994     3d/compare-EAX-and  0x3c/imm32/less-than
-1995     0f 85/jump-if-not-equal  $parse-effective-address:error2/disp32
-1996     # ++word->start to skip '<'
-1997     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
-1998     # skip whitespace
-1999     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
-2000     # . . push args
-2001     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
-2002     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-2003     # . . call
-2004     e8/call  skip-chars-matching-whitespace-in-slice/disp32
-2005     # . . discard args
-2006     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2007     # . word->start = EAX
-2008     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
-2009 $parse-effective-address:scale:
-2010     # read positive integer into scale
-2011     # . EAX = next-positive-hex-int(word)
-2012     # . . push args
-2013     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2014     # . . call
-2015     e8/call  next-positive-hex-int/disp32
-2016     # . . discard args
-2017     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2018     # . EDX = EAX
-2019     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
-2020     # skip whitespace
-2021     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
-2022     # . . push args
-2023     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
-2024     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-2025     # . . call
-2026     e8/call  skip-chars-matching-whitespace-in-slice/disp32
-2027     # . . discard args
-2028     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2029     # . word->start = EAX
-2030     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
-2031     # if (*word->start == ')') goto end
-2032     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
-2033     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
-2034     3d/compare-EAX-and  0x29/imm32/close-paren
-2035     74/jump-if-equal  $parse-effective-address:end/disp8
-2036 $parse-effective-address:check-for-displacement:
-2037     # if (*word->start not in '+' '-') goto error3
-2038     3d/compare-EAX-and  0x2b/imm32/plus
-2039     74/jump-if-equal  $parse-effective-address:displacement/disp8
-2040     3d/compare-EAX-and  0x2d/imm32/minus
-2041     74/jump-if-equal  $parse-effective-address:displacement/disp8
-2042     e9/jump  $parse-effective-address:error3/disp32
-2043 $parse-effective-address:displacement:
-2044     # read integer into disp
-2045     # . EAX = next-hex-int(word)
-2046     # . . push args
-2047     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2048     # . . call
-2049     e8/call  next-hex-int/disp32
-2050     # . . discard args
-2051     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2052     # . EBX = EAX
-2053     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
-2054     # skip whitespace
-2055     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
-2056     # . . push args
-2057     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
-2058     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-2059     # . . call
-2060     e8/call  skip-chars-matching-whitespace-in-slice/disp32
-2061     # . . discard args
-2062     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2063     # . word->start = EAX
-2064     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
-2065     # if (*word->start != ')') goto error4
-2066     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
-2067     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
-2068     3d/compare-EAX-and  0x29/imm32/close-paren
-2069     0f 85/jump-if-not-equal  $parse-effective-address:error4/disp32
-2070 $parse-effective-address:end:
-2071     # return base in EAX
-2072     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy EDI to EAX
-2073     # . restore registers
-2074     5f/pop-to-EDI
-2075     5e/pop-to-ESI
-2076     # . epilog
-2077     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2078     5d/pop-to-EBP
-2079     c3/return
-2080 
-2081 $parse-effective-address:error1:
-2082     # print(stderr, "error: unexpected character: " EAX "\n")
-2083     # . write-buffered(Stderr, "error: unexpected character: ")
-2084     # . . push args
-2085     68/push  "error: unexpected character: "/imm32
-2086     68/push  Stderr/imm32
-2087     # . . call
-2088     e8/call  write-buffered/disp32
-2089     # . . discard args
-2090     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2091     # . print-int32-buffered(out, EAX)
-2092     # . . push args
-2093     50/push-EAX
-2094     68/push  Stderr/imm32
-2095     # . . call
-2096     e8/call  print-int32-buffered/disp32
-2097     # . . discard args
+1900     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
+1901     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
+1902     # . . push args
+1903     68/push  "F - test-next-word-or-expression-returns-whole-expression: start"/imm32
+1904     68/push  0xd/imm32
+1905     # . . push slice->start - _test-input-stream
+1906     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
+1907     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1908     50/push-EAX
+1909     # . . call
+1910     e8/call  check-ints-equal/disp32
+1911     # . . discard args
+1912     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1913     # check-ints-equal(slice->end - _test-input-stream->data, 13, msg)
+1914     # . check-ints-equal(slice->end - _test-input-stream, 25, msg)
+1915     # . . push args
+1916     68/push  "F - test-next-word-or-expression-returns-whole-expression: end"/imm32
+1917     68/push  0x19/imm32
+1918     # . . push slice->end - _test-input-stream
+1919     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
+1920     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
+1921     50/push-EAX
+1922     # . . call
+1923     e8/call  check-ints-equal/disp32
+1924     # . . discard args
+1925     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+1926     # . epilog
+1927     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+1928     5d/pop-to-EBP
+1929     c3/return
+1930 
+1931 # Grammar:
+1932 #   *reg                    -> 0/mod reg/rm32
+1933 #   *(reg)                  -> 0/mod reg/rm32
+1934 #   *(reg+disp)             -> 2/mod reg/rm32 disp/disp32
+1935 #   *(reg1+reg2<<s)         -> 2/mod 4/rm32 reg1/base reg2/index s/scale 0/disp32
+1936 #   *(reg1+reg2<<s+disp)    -> 2/mod 4/rm32 reg1/base reg2/index s/scale disp/disp32
+1937 # Intermediate structure: base, index, scale, disp
+1938 # Default values: base: 0, index: 4 (none), scale: 0, disp: 0
+1939 # beware: modifies 'word'
+1940 parse-effective-address:  # word : (address slice) -> base/EAX, index/ECX, scale/EDX, disp/EBX
+1941     # pseudocode:
+1942     #   ++word->start to skip '*'
+1943     #   initialize defaults: base=0, index=4, scale=0, disp=0
+1944     #   if (*word->start != '(') {
+1945     #     base = get-slice(Registers, word, row-size=8)
+1946     #     return
+1947     #   }
+1948     #   # compound expressions
+1949     #   skip whitespace
+1950     #   read register into base
+1951     #   skip whitespace
+1952     #   if (*word->start == ')') goto end
+1953     #   if (*word->start == '-') goto displacement
+1954     #   if (*word->start != '+') goto error1
+1955     #   ++word->start to skip '+'
+1956     #   skip whitespace
+1957     #   if next 3 characters don't make a register, goto displacement
+1958     #   read register into index
+1959     #   skip whitespace
+1960     #   if (*word->start == ')') goto end
+1961     #   if (*word->start == '<') {
+1962     #     ++word->start to skip '<'
+1963     #     if (*word->start != '<') goto error2
+1964     #     ++word->start to skip '<'
+1965     #     skip whitespace
+1966     #     read integer into scale
+1967     #     skip whitespace
+1968     #     if (*word->start == ')') goto end
+1969     #   }
+1970     #   if (*word->start not in '+' '-') goto error3
+1971     # displacement:
+1972     #   read integer into disp
+1973     #   skip whitespace
+1974     #   if (*word->start != ')') goto error4
+1975     # . prolog
+1976     55/push-EBP
+1977     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+1978     # . save registers
+1979     56/push-ESI
+1980     57/push-EDI
+1981     # ESI = word
+1982     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+1983     # ++word->start to skip '*'
+1984     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
+1985     # initialize defaults
+1986     # base is in EDI; we'll move it to EAX just before we return
+1987     bf/copy-to-EDI  0/imm32
+1988     b9/copy-to-ECX  4/imm32/no-index
+1989     ba/copy-to-EDX  0/imm32/.scale
+1990     bb/copy-to-EBX  0/imm32/disp
+1991 $parse-effective-address:check-for-simple-register:
+1992     # if (*word->start == '(') goto compound expression
+1993     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+1994     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
+1995     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
+1996     3d/compare-EAX-and  0x28/imm32/open-paren
+1997     74/jump-if-equal  $parse-effective-address:compound-expression/disp8
+1998 $parse-effective-address:simple-register:
+1999     # base = get-slice(Registers, word, row-size=8)
+2000     # . EAX = get-slice(Registers, word, row-size=8)
+2001     # . . push args
+2002     68/push  "Registers"/imm32
+2003     68/push  8/imm32/row-size
+2004     56/push-ESI
+2005     68/push  Registers/imm32
+2006     # . . call
+2007     e8/call  get-slice/disp32
+2008     # . . discard args
+2009     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+2010     # . base = *EAX
+2011     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy *EAX to EDI
+2012     # return
+2013     e9/jump  $parse-effective-address:end/disp32
+2014 $parse-effective-address:compound-expression:
+2015     # ++word->start to skip '('
+2016     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
+2017     # skip whitespace
+2018     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
+2019     # . . push args
+2020     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+2021     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+2022     # . . call
+2023     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+2024     # . . discard args
+2025     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2026     # . word->start = EAX
+2027     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
+2028     # read register into base
+2029     # . EAX = next-register(word)
+2030     # . . push args
+2031     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2032     # . . call
+2033     e8/call  next-register/disp32
+2034     # . . discard args
+2035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2036     # . EDI = *EAX
+2037     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy *EAX to EDI
+2038     # skip whitespace
+2039     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
+2040     # . . push args
+2041     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+2042     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+2043     # . . call
+2044     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+2045     # . . discard args
+2046     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2047     # . word->start = EAX
+2048     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
+2049     # if (*word->start == ')') goto end
+2050     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
+2051     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
+2052     3d/compare-EAX-and  0x29/imm32/close-paren
+2053     0f 84/jump-if-equal  $parse-effective-address:end/disp32
+2054     # if (*word->start == '-') goto displacement
+2055     3d/compare-EAX-and  0x2d/imm32/minus
+2056     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
+2057     # if (*word->start != '+') goto error1
+2058     3d/compare-EAX-and  0x2b/imm32/plus
+2059     0f 85/jump-if-not-equal  $parse-effective-address:error1/disp32
+2060 $parse-effective-address:check-for-index:
+2061     # ++word->start to skip '+'
+2062     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
+2063     # skip whitespace
+2064     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
+2065     # . . push args
+2066     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+2067     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+2068     # . . call
+2069     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+2070     # . . discard args
+2071     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2072     # . word->start = EAX
+2073     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
+2074 $parse-effective-address:resolve-ambiguity:
+2075     # if next 3 characters don't make a register, goto displacement
+2076     # . spill ECX
+2077     51/push-ECX
+2078     # . var tmp/ECX = {word->start, word->start+3}
+2079     # . . ECX = word->start
+2080     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+2081     # . . EAX = word->start+3
+2082     05/add-to-EAX  3/imm32
+2083     # . . push
+2084     50/push-EAX
+2085     51/push-ECX
+2086     # . . copy ESP to ECX
+2087     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2088     # . EAX = maybe-get-slice(Register, tmp, row-size=8)
+2089     # . . push args
+2090     68/push  8/imm32/row-size
+2091     51/push-ECX
+2092     68/push  Registers/imm32
+2093     # . . call
+2094     e8/call  maybe-get-slice/disp32
+2095     # . . discard args
+2096     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2097     # . reclaim tmp
 2098     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2099     # . write-buffered(Stderr, "\n")
-2100     # . . push args
-2101     68/push  "\n"/imm32
-2102     68/push  Stderr/imm32
-2103     # . . call
-2104     e8/call  write-buffered/disp32
-2105     # . . discard args
-2106     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2107     # . flush(Stderr)
-2108     # . . push args
-2109     68/push  Stderr/imm32
-2110     # . . call
-2111     e8/call  flush/disp32
-2112     # . . discard args
-2113     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2114     # . syscall(exit, 1)
-2115     bb/copy-to-EBX  1/imm32
-2116     b8/copy-to-EAX  1/imm32/exit
-2117     cd/syscall  0x80/imm8
-2118     # never gets here
-2119 
-2120 $parse-effective-address:error2:
-2121     # print(stderr, "error: '<' can only be followed by '<' but got: " EAX "\n")
-2122     # . write-buffered(Stderr, "error: '<' can only be followed by '<' but got: ")
-2123     # . . push args
-2124     68/push  "error: '<' can only be followed by '<' but got: "/imm32
-2125     68/push  Stderr/imm32
-2126     # . . call
-2127     e8/call  write-buffered/disp32
-2128     # . . discard args
-2129     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2130     # . print-int32-buffered(out, EAX)
-2131     # . . push args
-2132     50/push-EAX
-2133     68/push  Stderr/imm32
-2134     # . . call
-2135     e8/call  print-int32-buffered/disp32
-2136     # . . discard args
-2137     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2138     # . write-buffered(Stderr, "\n")
-2139     # . . push args
-2140     68/push  "\n"/imm32
-2141     68/push  Stderr/imm32
-2142     # . . call
-2143     e8/call  write-buffered/disp32
-2144     # . . discard args
-2145     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2146     # . flush(Stderr)
+2099     # . restore ECX
+2100     59/pop-to-ECX
+2101     # . if (EAX == 0) goto displacement
+2102     3d/compare-EAX-and  0/imm32
+2103     0f 84/jump-if-equal  $parse-effective-address:displacement/disp32
+2104 $parse-effective-address:index:
+2105     # read register into index
+2106     # . EAX = next-register(word)
+2107     # . . push args
+2108     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2109     # . . call
+2110     e8/call  next-register/disp32
+2111     # . . discard args
+2112     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2113     # . ECX = *EAX
+2114     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2115     # skip whitespace
+2116     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
+2117     # . . push args
+2118     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+2119     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+2120     # . . call
+2121     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+2122     # . . discard args
+2123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2124     # . word->start = EAX
+2125     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
+2126     # if (*word->start == ')') goto end
+2127     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
+2128     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
+2129     3d/compare-EAX-and  0x29/imm32/close-paren
+2130     0f 84/jump-if-equal  $parse-effective-address:end/disp32
+2131 $parse-effective-address:check-for-scale:
+2132     # if (*word->start != '<') goto next check
+2133     3d/compare-EAX-and  0x3c/imm32/less-than
+2134     75/jump-if-not-equal  $parse-effective-address:check-for-displacement/disp8
+2135     # ++word->start to skip '<'
+2136     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
+2137     # if (*word->start != '<') goto error2
+2138     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+2139     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
+2140     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
+2141     3d/compare-EAX-and  0x3c/imm32/less-than
+2142     0f 85/jump-if-not-equal  $parse-effective-address:error2/disp32
+2143     # ++word->start to skip '<'
+2144     ff          0/subop/increment   0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # increment *ESI
+2145     # skip whitespace
+2146     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
 2147     # . . push args
-2148     68/push  Stderr/imm32
-2149     # . . call
-2150     e8/call  flush/disp32
-2151     # . . discard args
-2152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2153     # . syscall(exit, 1)
-2154     bb/copy-to-EBX  1/imm32
-2155     b8/copy-to-EAX  1/imm32/exit
-2156     cd/syscall  0x80/imm8
-2157     # never gets here
-2158 
-2159 $parse-effective-address:error3:
-2160     # print(stderr, "error: unexpected character before displacement: " EAX "\n")
-2161     # . write-buffered(Stderr, "error: unexpected character before displacement: ")
-2162     # . . push args
-2163     68/push  "error: unexpected character before displacement: "/imm32
-2164     68/push  Stderr/imm32
-2165     # . . call
-2166     e8/call  write-buffered/disp32
-2167     # . . discard args
-2168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2169     # . print-int32-buffered(out, EAX)
-2170     # . . push args
-2171     50/push-EAX
-2172     68/push  Stderr/imm32
-2173     # . . call
-2174     e8/call  print-int32-buffered/disp32
-2175     # . . discard args
-2176     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2177     # . write-buffered(Stderr, "\n")
-2178     # . . push args
-2179     68/push  "\n"/imm32
-2180     68/push  Stderr/imm32
-2181     # . . call
-2182     e8/call  write-buffered/disp32
-2183     # . . discard args
-2184     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2185     # . flush(Stderr)
-2186     # . . push args
-2187     68/push  Stderr/imm32
-2188     # . . call
-2189     e8/call  flush/disp32
-2190     # . . discard args
-2191     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2192     # . syscall(exit, 1)
-2193     bb/copy-to-EBX  1/imm32
-2194     b8/copy-to-EAX  1/imm32/exit
-2195     cd/syscall  0x80/imm8
-2196     # never gets here
-2197 
-2198 $parse-effective-address:error4:
-2199     # print(stderr, "error: unexpected character after displacement: " EAX "; expected ')' to wrap up\n")
-2200     # . write-buffered(Stderr, "error: unexpected character after displacement: ")
-2201     # . . push args
-2202     68/push  "error: unexpected character after displacement: "/imm32
-2203     68/push  Stderr/imm32
-2204     # . . call
-2205     e8/call  write-buffered/disp32
-2206     # . . discard args
-2207     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2208     # . print-int32-buffered(out, EAX)
-2209     # . . push args
-2210     50/push-EAX
-2211     68/push  Stderr/imm32
-2212     # . . call
-2213     e8/call  print-int32-buffered/disp32
-2214     # . . discard args
-2215     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2216     # . write-buffered(Stderr, "; expected ')' to wrap up\n")
-2217     # . . push args
-2218     68/push  "; expected ')' to wrap up\n"/imm32
-2219     68/push  Stderr/imm32
-2220     # . . call
-2221     e8/call  write-buffered/disp32
-2222     # . . discard args
-2223     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2224     # . flush(Stderr)
-2225     # . . push args
-2226     68/push  Stderr/imm32
-2227     # . . call
-2228     e8/call  flush/disp32
-2229     # . . discard args
-2230     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2231     # . syscall(exit, 1)
-2232     bb/copy-to-EBX  1/imm32
-2233     b8/copy-to-EAX  1/imm32/exit
-2234     cd/syscall  0x80/imm8
-2235     # never gets here
-2236 
-2237 # assumes 'in' starts with a register name, and returns pointer to its code
-2238 # side-effect: modifies 'in' to scan past the initial register name
-2239 next-register:  # in : (address slice) -> reg/EAX : int
-2240     # . prolog
-2241     55/push-EBP
-2242     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2243     # . save registers
-2244     51/push-ECX
-2245     56/push-ESI
-2246     # ESI = in
-2247     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-2248     # var reg-slice/ECX : (address slice) = {in->start, in->start + 3}
-2249     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
-2250     05/add-to-EAX  3/imm32
-2251     50/push-EAX
-2252     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
-2253     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2254     # in->start += 3
-2255     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               3/imm32           # add to *ESI
-2256     # EAX = get-slice(Registers, word, row-size=8)
-2257     # . . push args
-2258     68/push  "next-register"/imm32
-2259     68/push  8/imm32/row-size
-2260     51/push-ECX
-2261     68/push  Registers/imm32
-2262     # . . call
-2263     e8/call  get-slice/disp32
-2264     # . . discard args
-2265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
-2266 $next-register:end:
-2267     # reclaim locals
-2268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2269     # . restore registers
-2270     5e/pop-to-ESI
-2271     59/pop-to-ECX
-2272     # . epilog
-2273     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2274     5d/pop-to-EBP
-2275     c3/return
-2276 
-2277 test-parse-effective-address-simple:
-2278     # . prolog
-2279     55/push-EBP
-2280     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2281     # var slice/ECX = "*esi"
-2282     b8/copy-to-EAX  "*esi"/imm32
-2283     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2284     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
-2285     05/add-to-EAX  4/imm32
-2286     # . ECX = {EAX, ECX}
-2287     51/push-ECX
-2288     50/push-EAX
-2289     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2290     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
-2291     # . . push args
-2292     51/push-ECX
-2293     # . . call
-2294     e8/call  parse-effective-address/disp32
-2295     # . . discard args
-2296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2297     # slice clobbered beyond this point
-2298     # check-ints-equal(EAX, 6, msg)
-2299     # . . push args
-2300     68/push  "F - test-parse-effective-address-simple/base"/imm32
-2301     68/push  6/imm32/ESI
-2302     50/push-EAX
-2303     # . . call
-2304     e8/call  check-ints-equal/disp32
-2305     # . . discard args
-2306     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2307     # check-ints-equal(ECX, 4, msg)
-2308     # . . push args
-2309     68/push  "F - test-parse-effective-address-simple/index"/imm32
-2310     68/push  4/imm32/none
-2311     51/push-ECX
+2148     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+2149     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+2150     # . . call
+2151     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+2152     # . . discard args
+2153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2154     # . word->start = EAX
+2155     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
+2156 $parse-effective-address:scale:
+2157     # read positive integer into scale
+2158     # . EAX = next-positive-hex-int(word)
+2159     # . . push args
+2160     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2161     # . . call
+2162     e8/call  next-positive-hex-int/disp32
+2163     # . . discard args
+2164     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2165     # . EDX = EAX
+2166     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDX
+2167     # skip whitespace
+2168     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
+2169     # . . push args
+2170     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+2171     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+2172     # . . call
+2173     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+2174     # . . discard args
+2175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2176     # . word->start = EAX
+2177     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
+2178     # if (*word->start == ')') goto end
+2179     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
+2180     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
+2181     3d/compare-EAX-and  0x29/imm32/close-paren
+2182     74/jump-if-equal  $parse-effective-address:end/disp8
+2183 $parse-effective-address:check-for-displacement:
+2184     # if (*word->start not in '+' '-') goto error3
+2185     3d/compare-EAX-and  0x2b/imm32/plus
+2186     74/jump-if-equal  $parse-effective-address:displacement/disp8
+2187     3d/compare-EAX-and  0x2d/imm32/minus
+2188     74/jump-if-equal  $parse-effective-address:displacement/disp8
+2189     e9/jump  $parse-effective-address:error3/disp32
+2190 $parse-effective-address:displacement:
+2191     # read integer into disp
+2192     # . EAX = next-hex-int(word)
+2193     # . . push args
+2194     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2195     # . . call
+2196     e8/call  next-hex-int/disp32
+2197     # . . discard args
+2198     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2199     # . EBX = EAX
+2200     89/copy                         3/mod/direct    3/rm32/EBX    .           .             .           0/r32/EAX   .               .                 # copy EAX to EBX
+2201     # skip whitespace
+2202     # . EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
+2203     # . . push args
+2204     ff          6/subop/push        1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # push *(ESI+4)
+2205     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+2206     # . . call
+2207     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+2208     # . . discard args
+2209     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2210     # . word->start = EAX
+2211     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *ESI
+2212     # if (*word->start != ')') goto error4
+2213     8a/copy-byte                    0/mod/indirect  0/rm32/EAX    .           .             .           0/r32/AL    .               .                 # copy byte at *EAX to AL
+2214     81          4/subop/and         3/mod/direct    0/rm32/EAX    .           .             .           .           .               0xff/imm32        # bitwise and of EAX
+2215     3d/compare-EAX-and  0x29/imm32/close-paren
+2216     0f 85/jump-if-not-equal  $parse-effective-address:error4/disp32
+2217 $parse-effective-address:end:
+2218     # return base in EAX
+2219     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy EDI to EAX
+2220     # . restore registers
+2221     5f/pop-to-EDI
+2222     5e/pop-to-ESI
+2223     # . epilog
+2224     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2225     5d/pop-to-EBP
+2226     c3/return
+2227 
+2228 $parse-effective-address:error1:
+2229     # print(stderr, "error: unexpected character: " EAX "\n")
+2230     # . write-buffered(Stderr, "error: unexpected character: ")
+2231     # . . push args
+2232     68/push  "error: unexpected character: "/imm32
+2233     68/push  Stderr/imm32
+2234     # . . call
+2235     e8/call  write-buffered/disp32
+2236     # . . discard args
+2237     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2238     # . print-int32-buffered(out, EAX)
+2239     # . . push args
+2240     50/push-EAX
+2241     68/push  Stderr/imm32
+2242     # . . call
+2243     e8/call  print-int32-buffered/disp32
+2244     # . . discard args
+2245     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2246     # . write-buffered(Stderr, "\n")
+2247     # . . push args
+2248     68/push  "\n"/imm32
+2249     68/push  Stderr/imm32
+2250     # . . call
+2251     e8/call  write-buffered/disp32
+2252     # . . discard args
+2253     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2254     # . flush(Stderr)
+2255     # . . push args
+2256     68/push  Stderr/imm32
+2257     # . . call
+2258     e8/call  flush/disp32
+2259     # . . discard args
+2260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2261     # . syscall(exit, 1)
+2262     bb/copy-to-EBX  1/imm32
+2263     b8/copy-to-EAX  1/imm32/exit
+2264     cd/syscall  0x80/imm8
+2265     # never gets here
+2266 
+2267 $parse-effective-address:error2:
+2268     # print(stderr, "error: '<' can only be followed by '<' but got: " EAX "\n")
+2269     # . write-buffered(Stderr, "error: '<' can only be followed by '<' but got: ")
+2270     # . . push args
+2271     68/push  "error: '<' can only be followed by '<' but got: "/imm32
+2272     68/push  Stderr/imm32
+2273     # . . call
+2274     e8/call  write-buffered/disp32
+2275     # . . discard args
+2276     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2277     # . print-int32-buffered(out, EAX)
+2278     # . . push args
+2279     50/push-EAX
+2280     68/push  Stderr/imm32
+2281     # . . call
+2282     e8/call  print-int32-buffered/disp32
+2283     # . . discard args
+2284     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2285     # . write-buffered(Stderr, "\n")
+2286     # . . push args
+2287     68/push  "\n"/imm32
+2288     68/push  Stderr/imm32
+2289     # . . call
+2290     e8/call  write-buffered/disp32
+2291     # . . discard args
+2292     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2293     # . flush(Stderr)
+2294     # . . push args
+2295     68/push  Stderr/imm32
+2296     # . . call
+2297     e8/call  flush/disp32
+2298     # . . discard args
+2299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2300     # . syscall(exit, 1)
+2301     bb/copy-to-EBX  1/imm32
+2302     b8/copy-to-EAX  1/imm32/exit
+2303     cd/syscall  0x80/imm8
+2304     # never gets here
+2305 
+2306 $parse-effective-address:error3:
+2307     # print(stderr, "error: unexpected character before displacement: " EAX "\n")
+2308     # . write-buffered(Stderr, "error: unexpected character before displacement: ")
+2309     # . . push args
+2310     68/push  "error: unexpected character before displacement: "/imm32
+2311     68/push  Stderr/imm32
 2312     # . . call
-2313     e8/call  check-ints-equal/disp32
+2313     e8/call  write-buffered/disp32
 2314     # . . discard args
-2315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2316     # check-ints-equal(EDX, 0, msg)
+2315     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2316     # . print-int32-buffered(out, EAX)
 2317     # . . push args
-2318     68/push  "F - test-parse-effective-address-simple/scale"/imm32
-2319     68/push  0/imm32/none
-2320     52/push-EDX
-2321     # . . call
-2322     e8/call  check-ints-equal/disp32
-2323     # . . discard args
-2324     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2325     # check-ints-equal(EBX, 0, msg)
-2326     # . . push args
-2327     68/push  "F - test-parse-effective-address-simple/displacement"/imm32
-2328     68/push  0/imm32/none
-2329     53/push-EBX
-2330     # . . call
-2331     e8/call  check-ints-equal/disp32
-2332     # . . discard args
-2333     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2334     # . epilog
-2335     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2336     5d/pop-to-EBP
-2337     c3/return
-2338 
-2339 test-parse-effective-address-base:
-2340     # . prolog
-2341     55/push-EBP
-2342     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2343     # var slice/ECX = "*(esi  )"
-2344     b8/copy-to-EAX  "*(esi  )"/imm32
-2345     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2346     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
-2347     05/add-to-EAX  4/imm32
-2348     # . ECX = {EAX, ECX}
-2349     51/push-ECX
-2350     50/push-EAX
-2351     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2352     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
-2353     # . . push args
-2354     51/push-ECX
-2355     # . . call
-2356     e8/call  parse-effective-address/disp32
-2357     # . . discard args
-2358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2359     # slice clobbered beyond this point
-2360     # check-ints-equal(EAX, 6, msg)
-2361     # . . push args
-2362     68/push  "F - test-parse-effective-address-base/base"/imm32
-2363     68/push  6/imm32/ESI
-2364     50/push-EAX
-2365     # . . call
-2366     e8/call  check-ints-equal/disp32
-2367     # . . discard args
-2368     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2369     # check-ints-equal(ECX, 4, msg)
-2370     # . . push args
-2371     68/push  "F - test-parse-effective-address-base/index"/imm32
-2372     68/push  4/imm32/none
-2373     51/push-ECX
+2318     50/push-EAX
+2319     68/push  Stderr/imm32
+2320     # . . call
+2321     e8/call  print-int32-buffered/disp32
+2322     # . . discard args
+2323     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2324     # . write-buffered(Stderr, "\n")
+2325     # . . push args
+2326     68/push  "\n"/imm32
+2327     68/push  Stderr/imm32
+2328     # . . call
+2329     e8/call  write-buffered/disp32
+2330     # . . discard args
+2331     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2332     # . flush(Stderr)
+2333     # . . push args
+2334     68/push  Stderr/imm32
+2335     # . . call
+2336     e8/call  flush/disp32
+2337     # . . discard args
+2338     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2339     # . syscall(exit, 1)
+2340     bb/copy-to-EBX  1/imm32
+2341     b8/copy-to-EAX  1/imm32/exit
+2342     cd/syscall  0x80/imm8
+2343     # never gets here
+2344 
+2345 $parse-effective-address:error4:
+2346     # print(stderr, "error: unexpected character after displacement: " EAX "; expected ')' to wrap up\n")
+2347     # . write-buffered(Stderr, "error: unexpected character after displacement: ")
+2348     # . . push args
+2349     68/push  "error: unexpected character after displacement: "/imm32
+2350     68/push  Stderr/imm32
+2351     # . . call
+2352     e8/call  write-buffered/disp32
+2353     # . . discard args
+2354     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2355     # . print-int32-buffered(out, EAX)
+2356     # . . push args
+2357     50/push-EAX
+2358     68/push  Stderr/imm32
+2359     # . . call
+2360     e8/call  print-int32-buffered/disp32
+2361     # . . discard args
+2362     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2363     # . write-buffered(Stderr, "; expected ')' to wrap up\n")
+2364     # . . push args
+2365     68/push  "; expected ')' to wrap up\n"/imm32
+2366     68/push  Stderr/imm32
+2367     # . . call
+2368     e8/call  write-buffered/disp32
+2369     # . . discard args
+2370     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2371     # . flush(Stderr)
+2372     # . . push args
+2373     68/push  Stderr/imm32
 2374     # . . call
-2375     e8/call  check-ints-equal/disp32
+2375     e8/call  flush/disp32
 2376     # . . discard args
-2377     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2378     # check-ints-equal(EDX, 0, msg)
-2379     # . . push args
-2380     68/push  "F - test-parse-effective-address-base/scale"/imm32
-2381     68/push  0/imm32/none
-2382     52/push-EDX
-2383     # . . call
-2384     e8/call  check-ints-equal/disp32
-2385     # . . discard args
-2386     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2387     # check-ints-equal(EBX, 0, msg)
-2388     # . . push args
-2389     68/push  "F - test-parse-effective-address-base/displacement"/imm32
-2390     68/push  0/imm32/none
-2391     53/push-EBX
-2392     # . . call
-2393     e8/call  check-ints-equal/disp32
-2394     # . . discard args
-2395     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2396     # . epilog
-2397     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2398     5d/pop-to-EBP
-2399     c3/return
-2400 
-2401 test-parse-effective-address-base-displacement:
-2402     # . prolog
-2403     55/push-EBP
-2404     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2405     # var slice/ECX = "*(esi+3)"
-2406     b8/copy-to-EAX  "*(esi+3)"/imm32
-2407     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2408     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
-2409     05/add-to-EAX  4/imm32
-2410     # . ECX = {EAX, ECX}
-2411     51/push-ECX
-2412     50/push-EAX
-2413     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2414     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
-2415     # . . push args
-2416     51/push-ECX
-2417     # . . call
-2418     e8/call  parse-effective-address/disp32
-2419     # . . discard args
-2420     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2421     # slice clobbered beyond this point
-2422     # check-ints-equal(EAX, 6, msg)
-2423     # . . push args
-2424     68/push  "F - test-parse-effective-address-base-displacement/base"/imm32
-2425     68/push  6/imm32/ESI
-2426     50/push-EAX
-2427     # . . call
-2428     e8/call  check-ints-equal/disp32
-2429     # . . discard args
-2430     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2431     # check-ints-equal(ECX, 4, msg)
-2432     # . . push args
-2433     68/push  "F - test-parse-effective-address-base-displacement/index"/imm32
-2434     68/push  4/imm32/none
-2435     51/push-ECX
-2436     # . . call
-2437     e8/call  check-ints-equal/disp32
-2438     # . . discard args
-2439     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2440     # check-ints-equal(EDX, 0, msg)
-2441     # . . push args
-2442     68/push  "F - test-parse-effective-address-base-displacement/scale"/imm32
-2443     68/push  0/imm32/none
-2444     52/push-EDX
-2445     # . . call
-2446     e8/call  check-ints-equal/disp32
-2447     # . . discard args
-2448     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2449     # check-ints-equal(EBX, 3, msg)
-2450     # . . push args
-2451     68/push  "F - test-parse-effective-address-base-displacement/displacement"/imm32
-2452     68/push  3/imm32
-2453     53/push-EBX
-2454     # . . call
-2455     e8/call  check-ints-equal/disp32
-2456     # . . discard args
-2457     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2458     # . epilog
-2459     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2460     5d/pop-to-EBP
-2461     c3/return
-2462 
-2463 test-parse-effective-address-base-negative-displacement:
-2464     # . prolog
-2465     55/push-EBP
-2466     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2467     # var slice/ECX = "*(esi-3)"
-2468     b8/copy-to-EAX  "*(esi-3)"/imm32
-2469     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2470     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
-2471     05/add-to-EAX  4/imm32
-2472     # . ECX = {EAX, ECX}
-2473     51/push-ECX
-2474     50/push-EAX
-2475     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2476     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
-2477     # . . push args
-2478     51/push-ECX
-2479     # . . call
-2480     e8/call  parse-effective-address/disp32
-2481     # . . discard args
-2482     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2483     # slice clobbered beyond this point
-2484     # check-ints-equal(EAX, 6, msg)
-2485     # . . push args
-2486     68/push  "F - test-parse-effective-address-base-negative-displacement/base"/imm32
-2487     68/push  6/imm32/ESI
-2488     50/push-EAX
-2489     # . . call
-2490     e8/call  check-ints-equal/disp32
-2491     # . . discard args
-2492     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2493     # check-ints-equal(ECX, 4, msg)
-2494     # . . push args
-2495     68/push  "F - test-parse-effective-address-base-negative-displacement/index"/imm32
-2496     68/push  4/imm32/none
-2497     51/push-ECX
-2498     # . . call
-2499     e8/call  check-ints-equal/disp32
-2500     # . . discard args
-2501     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2502     # check-ints-equal(EDX, 0, msg)
-2503     # . . push args
-2504     68/push  "F - test-parse-effective-address-base-negative-displacement/scale"/imm32
-2505     68/push  0/imm32/none
-2506     52/push-EDX
-2507     # . . call
-2508     e8/call  check-ints-equal/disp32
-2509     # . . discard args
-2510     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2511     # check-ints-equal(EBX, -3, msg)
-2512     # . . push args
-2513     68/push  "F - test-parse-effective-address-base-negative-displacement/displacement"/imm32
-2514     68/push  -3/imm32
-2515     53/push-EBX
-2516     # . . call
-2517     e8/call  check-ints-equal/disp32
-2518     # . . discard args
-2519     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2520     # . epilog
-2521     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2522     5d/pop-to-EBP
-2523     c3/return
-2524 
-2525 test-parse-effective-address-base-index:
-2526     # . prolog
-2527     55/push-EBP
-2528     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2529     # var slice/ECX = "*(esi+ecx)"
-2530     b8/copy-to-EAX  "*(esi+ecx)"/imm32
-2531     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2532     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
-2533     05/add-to-EAX  4/imm32
-2534     # . ECX = {EAX, ECX}
-2535     51/push-ECX
-2536     50/push-EAX
-2537     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2538     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
-2539     # . . push args
-2540     51/push-ECX
-2541     # . . call
-2542     e8/call  parse-effective-address/disp32
-2543     # . . discard args
-2544     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2545     # slice clobbered beyond this point
-2546     # check-ints-equal(EAX, 6, msg)
-2547     # . . push args
-2548     68/push  "F - test-parse-effective-address-base-index/base"/imm32
-2549     68/push  6/imm32/ESI
-2550     50/push-EAX
-2551     # . . call
-2552     e8/call  check-ints-equal/disp32
-2553     # . . discard args
-2554     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2555     # check-ints-equal(ECX, 1, msg)
-2556     # . . push args
-2557     68/push  "F - test-parse-effective-address-base-index/index"/imm32
-2558     68/push  1/imm32/none
-2559     51/push-ECX
-2560     # . . call
-2561     e8/call  check-ints-equal/disp32
-2562     # . . discard args
-2563     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2564     # check-ints-equal(EDX, 0, msg)
-2565     # . . push args
-2566     68/push  "F - test-parse-effective-address-base-index/scale"/imm32
-2567     68/push  0/imm32/none
-2568     52/push-EDX
-2569     # . . call
-2570     e8/call  check-ints-equal/disp32
-2571     # . . discard args
-2572     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2573     # check-ints-equal(EBX, 0, msg)
-2574     # . . push args
-2575     68/push  "F - test-parse-effective-address-base-index/displacement"/imm32
-2576     68/push  0/imm32
-2577     53/push-EBX
-2578     # . . call
-2579     e8/call  check-ints-equal/disp32
-2580     # . . discard args
-2581     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2582     # . epilog
-2583     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2584     5d/pop-to-EBP
-2585     c3/return
-2586 
-2587 test-parse-effective-address-base-index-scale:
-2588     # . prolog
-2589     55/push-EBP
-2590     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2591     # var slice/ECX = "*(esi+ecx<<2)"
-2592     b8/copy-to-EAX  "*(esi+ecx<<2)"/imm32
-2593     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2594     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
-2595     05/add-to-EAX  4/imm32
-2596     # . ECX = {EAX, ECX}
-2597     51/push-ECX
-2598     50/push-EAX
-2599     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2600     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
-2601     # . . push args
-2602     51/push-ECX
-2603     # . . call
-2604     e8/call  parse-effective-address/disp32
-2605     # . . discard args
-2606     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2607     # slice clobbered beyond this point
-2608     # check-ints-equal(EAX, 6, msg)
-2609     # . . push args
-2610     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
-2611     68/push  6/imm32/ESI
-2612     50/push-EAX
-2613     # . . call
-2614     e8/call  check-ints-equal/disp32
-2615     # . . discard args
-2616     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2617     # check-ints-equal(ECX, 1, msg)
-2618     # . . push args
-2619     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
-2620     68/push  1/imm32/none
-2621     51/push-ECX
-2622     # . . call
-2623     e8/call  check-ints-equal/disp32
-2624     # . . discard args
-2625     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2626     # check-ints-equal(EDX, 2, msg)
-2627     # . . push args
-2628     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
-2629     68/push  2/imm32
-2630     52/push-EDX
-2631     # . . call
-2632     e8/call  check-ints-equal/disp32
-2633     # . . discard args
-2634     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2635     # check-ints-equal(EBX, 0, msg)
-2636     # . . push args
-2637     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
-2638     68/push  0/imm32
-2639     53/push-EBX
-2640     # . . call
-2641     e8/call  check-ints-equal/disp32
-2642     # . . discard args
-2643     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2644     # . epilog
-2645     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2646     5d/pop-to-EBP
-2647     c3/return
-2648 
-2649 test-parse-effective-address-base-index-scale-displacement:
-2650     # . prolog
-2651     55/push-EBP
-2652     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2653     # var slice/ECX = "*(esi + ecx<<2 - 0x34)"
-2654     b8/copy-to-EAX  "*(esi + ecx<<2 - 0x34)"/imm32
-2655     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-2656     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
-2657     05/add-to-EAX  4/imm32
-2658     # . ECX = {EAX, ECX}
-2659     51/push-ECX
-2660     50/push-EAX
-2661     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-2662     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
-2663     # . . push args
-2664     51/push-ECX
-2665     # . . call
-2666     e8/call  parse-effective-address/disp32
-2667     # . . discard args
-2668     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2669     # slice clobbered beyond this point
-2670     # check-ints-equal(EAX, 6, msg)
-2671     # . . push args
-2672     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
-2673     68/push  6/imm32/ESI
-2674     50/push-EAX
-2675     # . . call
-2676     e8/call  check-ints-equal/disp32
-2677     # . . discard args
-2678     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2679     # check-ints-equal(ECX, 1, msg)
-2680     # . . push args
-2681     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
-2682     68/push  1/imm32/none
-2683     51/push-ECX
-2684     # . . call
-2685     e8/call  check-ints-equal/disp32
-2686     # . . discard args
-2687     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2688     # check-ints-equal(EDX, 2, msg)
-2689     # . . push args
-2690     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
-2691     68/push  2/imm32
-2692     52/push-EDX
-2693     # . . call
-2694     e8/call  check-ints-equal/disp32
-2695     # . . discard args
-2696     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2697     # check-ints-equal(EBX, -0x34, msg)
-2698     # . . push args
-2699     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
-2700     68/push  -0x34/imm32
-2701     53/push-EBX
-2702     # . . call
-2703     e8/call  check-ints-equal/disp32
-2704     # . . discard args
-2705     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2706     # . epilog
-2707     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2708     5d/pop-to-EBP
-2709     c3/return
-2710 
-2711 # Code generation:
-2712 #   if index is none and disp is 0, then mod = 0 and rm32 = base
-2713 #   if index is none, then mod = 2 and rm32 = base and disp32 = disp
-2714 #   if index is not none, then mod = 2 and rm32 = 4 and base = base and index = index and disp32 = disp
-2715 emit-indirect-mode:  # out : (address buffered-file), base : int, index : int, scale : int, disp : int
-2716     # . prolog
-2717     55/push-EBP
-2718     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2719 $emit-indirect-mode:check-for-sib:
-2720     # if (index == 4/none) goto next check
-2721     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      4/imm32           # compare *(EBP+16)
-2722     0f 84/jump-if-equal  $emit-indirect-mode:check-for-disp/disp32
-2723 $emit-indirect-mode:emit-sib:
-2724     # emit(out, "2/mod/indirect 4/rm32/sib " base "/base " index "/index " scale "/scale " disp "/disp32")
-2725     # . write-buffered(out, "2/mod/*+disp32 4/rm32/sib ")
-2726     # . . push args
-2727     68/push  "2/mod/*+disp32 4/rm32/sib "/imm32
-2728     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2729     # . . call
-2730     e8/call  write-buffered/disp32
-2731     # . . discard args
-2732     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2733     # . print-int32-buffered(out, base)
-2734     # . . push args
-2735     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-2736     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2737     # . . call
-2738     e8/call  print-int32-buffered/disp32
-2739     # . . discard args
-2740     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2741     # . write-buffered(out, "/base ")
-2742     # . . push args
-2743     68/push  "/base "/imm32
-2744     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2745     # . . call
-2746     e8/call  write-buffered/disp32
-2747     # . . discard args
-2748     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2749     # . print-int32-buffered(out, index)
-2750     # . . push args
-2751     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
-2752     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2753     # . . call
-2754     e8/call  print-int32-buffered/disp32
-2755     # . . discard args
-2756     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2757     # . write-buffered(out, "/index ")
-2758     # . . push args
-2759     68/push  "/index "/imm32
-2760     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2761     # . . call
-2762     e8/call  write-buffered/disp32
-2763     # . . discard args
-2764     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2765     # . print-int32-buffered(out, scale)
-2766     # . . push args
-2767     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
-2768     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2377     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2378     # . syscall(exit, 1)
+2379     bb/copy-to-EBX  1/imm32
+2380     b8/copy-to-EAX  1/imm32/exit
+2381     cd/syscall  0x80/imm8
+2382     # never gets here
+2383 
+2384 # assumes 'in' starts with a register name, and returns pointer to its code
+2385 # side-effect: modifies 'in' to scan past the initial register name
+2386 next-register:  # in : (address slice) -> reg/EAX : int
+2387     # . prolog
+2388     55/push-EBP
+2389     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2390     # . save registers
+2391     51/push-ECX
+2392     56/push-ESI
+2393     # ESI = in
+2394     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+2395     # var reg-slice/ECX : (address slice) = {in->start, in->start + 3}
+2396     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
+2397     05/add-to-EAX  3/imm32
+2398     50/push-EAX
+2399     ff          6/subop/push        0/mod/indirect  6/rm32/ESI    .           .             .           .           .               .                 # push *ESI
+2400     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2401     # in->start += 3
+2402     81          0/subop/add         0/mod/indirect  6/rm32/ESI    .           .             .           .           .               3/imm32           # add to *ESI
+2403     # EAX = get-slice(Registers, word, row-size=8)
+2404     # . . push args
+2405     68/push  "next-register"/imm32
+2406     68/push  8/imm32/row-size
+2407     51/push-ECX
+2408     68/push  Registers/imm32
+2409     # . . call
+2410     e8/call  get-slice/disp32
+2411     # . . discard args
+2412     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
+2413 $next-register:end:
+2414     # reclaim locals
+2415     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2416     # . restore registers
+2417     5e/pop-to-ESI
+2418     59/pop-to-ECX
+2419     # . epilog
+2420     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2421     5d/pop-to-EBP
+2422     c3/return
+2423 
+2424 test-parse-effective-address-simple:
+2425     # . prolog
+2426     55/push-EBP
+2427     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2428     # var slice/ECX = "*esi"
+2429     b8/copy-to-EAX  "*esi"/imm32
+2430     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2431     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
+2432     05/add-to-EAX  4/imm32
+2433     # . ECX = {EAX, ECX}
+2434     51/push-ECX
+2435     50/push-EAX
+2436     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2437     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
+2438     # . . push args
+2439     51/push-ECX
+2440     # . . call
+2441     e8/call  parse-effective-address/disp32
+2442     # . . discard args
+2443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2444     # slice clobbered beyond this point
+2445     # check-ints-equal(EAX, 6, msg)
+2446     # . . push args
+2447     68/push  "F - test-parse-effective-address-simple/base"/imm32
+2448     68/push  6/imm32/ESI
+2449     50/push-EAX
+2450     # . . call
+2451     e8/call  check-ints-equal/disp32
+2452     # . . discard args
+2453     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2454     # check-ints-equal(ECX, 4, msg)
+2455     # . . push args
+2456     68/push  "F - test-parse-effective-address-simple/index"/imm32
+2457     68/push  4/imm32/none
+2458     51/push-ECX
+2459     # . . call
+2460     e8/call  check-ints-equal/disp32
+2461     # . . discard args
+2462     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2463     # check-ints-equal(EDX, 0, msg)
+2464     # . . push args
+2465     68/push  "F - test-parse-effective-address-simple/scale"/imm32
+2466     68/push  0/imm32/none
+2467     52/push-EDX
+2468     # . . call
+2469     e8/call  check-ints-equal/disp32
+2470     # . . discard args
+2471     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2472     # check-ints-equal(EBX, 0, msg)
+2473     # . . push args
+2474     68/push  "F - test-parse-effective-address-simple/displacement"/imm32
+2475     68/push  0/imm32/none
+2476     53/push-EBX
+2477     # . . call
+2478     e8/call  check-ints-equal/disp32
+2479     # . . discard args
+2480     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2481     # . epilog
+2482     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2483     5d/pop-to-EBP
+2484     c3/return
+2485 
+2486 test-parse-effective-address-base:
+2487     # . prolog
+2488     55/push-EBP
+2489     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2490     # var slice/ECX = "*(esi  )"
+2491     b8/copy-to-EAX  "*(esi  )"/imm32
+2492     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2493     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
+2494     05/add-to-EAX  4/imm32
+2495     # . ECX = {EAX, ECX}
+2496     51/push-ECX
+2497     50/push-EAX
+2498     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2499     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
+2500     # . . push args
+2501     51/push-ECX
+2502     # . . call
+2503     e8/call  parse-effective-address/disp32
+2504     # . . discard args
+2505     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2506     # slice clobbered beyond this point
+2507     # check-ints-equal(EAX, 6, msg)
+2508     # . . push args
+2509     68/push  "F - test-parse-effective-address-base/base"/imm32
+2510     68/push  6/imm32/ESI
+2511     50/push-EAX
+2512     # . . call
+2513     e8/call  check-ints-equal/disp32
+2514     # . . discard args
+2515     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2516     # check-ints-equal(ECX, 4, msg)
+2517     # . . push args
+2518     68/push  "F - test-parse-effective-address-base/index"/imm32
+2519     68/push  4/imm32/none
+2520     51/push-ECX
+2521     # . . call
+2522     e8/call  check-ints-equal/disp32
+2523     # . . discard args
+2524     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2525     # check-ints-equal(EDX, 0, msg)
+2526     # . . push args
+2527     68/push  "F - test-parse-effective-address-base/scale"/imm32
+2528     68/push  0/imm32/none
+2529     52/push-EDX
+2530     # . . call
+2531     e8/call  check-ints-equal/disp32
+2532     # . . discard args
+2533     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2534     # check-ints-equal(EBX, 0, msg)
+2535     # . . push args
+2536     68/push  "F - test-parse-effective-address-base/displacement"/imm32
+2537     68/push  0/imm32/none
+2538     53/push-EBX
+2539     # . . call
+2540     e8/call  check-ints-equal/disp32
+2541     # . . discard args
+2542     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2543     # . epilog
+2544     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2545     5d/pop-to-EBP
+2546     c3/return
+2547 
+2548 test-parse-effective-address-base-displacement:
+2549     # . prolog
+2550     55/push-EBP
+2551     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2552     # var slice/ECX = "*(esi+3)"
+2553     b8/copy-to-EAX  "*(esi+3)"/imm32
+2554     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2555     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
+2556     05/add-to-EAX  4/imm32
+2557     # . ECX = {EAX, ECX}
+2558     51/push-ECX
+2559     50/push-EAX
+2560     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2561     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
+2562     # . . push args
+2563     51/push-ECX
+2564     # . . call
+2565     e8/call  parse-effective-address/disp32
+2566     # . . discard args
+2567     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2568     # slice clobbered beyond this point
+2569     # check-ints-equal(EAX, 6, msg)
+2570     # . . push args
+2571     68/push  "F - test-parse-effective-address-base-displacement/base"/imm32
+2572     68/push  6/imm32/ESI
+2573     50/push-EAX
+2574     # . . call
+2575     e8/call  check-ints-equal/disp32
+2576     # . . discard args
+2577     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2578     # check-ints-equal(ECX, 4, msg)
+2579     # . . push args
+2580     68/push  "F - test-parse-effective-address-base-displacement/index"/imm32
+2581     68/push  4/imm32/none
+2582     51/push-ECX
+2583     # . . call
+2584     e8/call  check-ints-equal/disp32
+2585     # . . discard args
+2586     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2587     # check-ints-equal(EDX, 0, msg)
+2588     # . . push args
+2589     68/push  "F - test-parse-effective-address-base-displacement/scale"/imm32
+2590     68/push  0/imm32/none
+2591     52/push-EDX
+2592     # . . call
+2593     e8/call  check-ints-equal/disp32
+2594     # . . discard args
+2595     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2596     # check-ints-equal(EBX, 3, msg)
+2597     # . . push args
+2598     68/push  "F - test-parse-effective-address-base-displacement/displacement"/imm32
+2599     68/push  3/imm32
+2600     53/push-EBX
+2601     # . . call
+2602     e8/call  check-ints-equal/disp32
+2603     # . . discard args
+2604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2605     # . epilog
+2606     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2607     5d/pop-to-EBP
+2608     c3/return
+2609 
+2610 test-parse-effective-address-base-negative-displacement:
+2611     # . prolog
+2612     55/push-EBP
+2613     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2614     # var slice/ECX = "*(esi-3)"
+2615     b8/copy-to-EAX  "*(esi-3)"/imm32
+2616     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2617     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
+2618     05/add-to-EAX  4/imm32
+2619     # . ECX = {EAX, ECX}
+2620     51/push-ECX
+2621     50/push-EAX
+2622     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2623     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
+2624     # . . push args
+2625     51/push-ECX
+2626     # . . call
+2627     e8/call  parse-effective-address/disp32
+2628     # . . discard args
+2629     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2630     # slice clobbered beyond this point
+2631     # check-ints-equal(EAX, 6, msg)
+2632     # . . push args
+2633     68/push  "F - test-parse-effective-address-base-negative-displacement/base"/imm32
+2634     68/push  6/imm32/ESI
+2635     50/push-EAX
+2636     # . . call
+2637     e8/call  check-ints-equal/disp32
+2638     # . . discard args
+2639     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2640     # check-ints-equal(ECX, 4, msg)
+2641     # . . push args
+2642     68/push  "F - test-parse-effective-address-base-negative-displacement/index"/imm32
+2643     68/push  4/imm32/none
+2644     51/push-ECX
+2645     # . . call
+2646     e8/call  check-ints-equal/disp32
+2647     # . . discard args
+2648     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2649     # check-ints-equal(EDX, 0, msg)
+2650     # . . push args
+2651     68/push  "F - test-parse-effective-address-base-negative-displacement/scale"/imm32
+2652     68/push  0/imm32/none
+2653     52/push-EDX
+2654     # . . call
+2655     e8/call  check-ints-equal/disp32
+2656     # . . discard args
+2657     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2658     # check-ints-equal(EBX, -3, msg)
+2659     # . . push args
+2660     68/push  "F - test-parse-effective-address-base-negative-displacement/displacement"/imm32
+2661     68/push  -3/imm32
+2662     53/push-EBX
+2663     # . . call
+2664     e8/call  check-ints-equal/disp32
+2665     # . . discard args
+2666     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2667     # . epilog
+2668     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2669     5d/pop-to-EBP
+2670     c3/return
+2671 
+2672 test-parse-effective-address-base-index:
+2673     # . prolog
+2674     55/push-EBP
+2675     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2676     # var slice/ECX = "*(esi+ecx)"
+2677     b8/copy-to-EAX  "*(esi+ecx)"/imm32
+2678     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2679     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
+2680     05/add-to-EAX  4/imm32
+2681     # . ECX = {EAX, ECX}
+2682     51/push-ECX
+2683     50/push-EAX
+2684     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2685     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
+2686     # . . push args
+2687     51/push-ECX
+2688     # . . call
+2689     e8/call  parse-effective-address/disp32
+2690     # . . discard args
+2691     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2692     # slice clobbered beyond this point
+2693     # check-ints-equal(EAX, 6, msg)
+2694     # . . push args
+2695     68/push  "F - test-parse-effective-address-base-index/base"/imm32
+2696     68/push  6/imm32/ESI
+2697     50/push-EAX
+2698     # . . call
+2699     e8/call  check-ints-equal/disp32
+2700     # . . discard args
+2701     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2702     # check-ints-equal(ECX, 1, msg)
+2703     # . . push args
+2704     68/push  "F - test-parse-effective-address-base-index/index"/imm32
+2705     68/push  1/imm32/none
+2706     51/push-ECX
+2707     # . . call
+2708     e8/call  check-ints-equal/disp32
+2709     # . . discard args
+2710     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2711     # check-ints-equal(EDX, 0, msg)
+2712     # . . push args
+2713     68/push  "F - test-parse-effective-address-base-index/scale"/imm32
+2714     68/push  0/imm32/none
+2715     52/push-EDX
+2716     # . . call
+2717     e8/call  check-ints-equal/disp32
+2718     # . . discard args
+2719     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2720     # check-ints-equal(EBX, 0, msg)
+2721     # . . push args
+2722     68/push  "F - test-parse-effective-address-base-index/displacement"/imm32
+2723     68/push  0/imm32
+2724     53/push-EBX
+2725     # . . call
+2726     e8/call  check-ints-equal/disp32
+2727     # . . discard args
+2728     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2729     # . epilog
+2730     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2731     5d/pop-to-EBP
+2732     c3/return
+2733 
+2734 test-parse-effective-address-base-index-scale:
+2735     # . prolog
+2736     55/push-EBP
+2737     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2738     # var slice/ECX = "*(esi+ecx<<2)"
+2739     b8/copy-to-EAX  "*(esi+ecx<<2)"/imm32
+2740     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2741     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
+2742     05/add-to-EAX  4/imm32
+2743     # . ECX = {EAX, ECX}
+2744     51/push-ECX
+2745     50/push-EAX
+2746     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2747     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
+2748     # . . push args
+2749     51/push-ECX
+2750     # . . call
+2751     e8/call  parse-effective-address/disp32
+2752     # . . discard args
+2753     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2754     # slice clobbered beyond this point
+2755     # check-ints-equal(EAX, 6, msg)
+2756     # . . push args
+2757     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
+2758     68/push  6/imm32/ESI
+2759     50/push-EAX
+2760     # . . call
+2761     e8/call  check-ints-equal/disp32
+2762     # . . discard args
+2763     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2764     # check-ints-equal(ECX, 1, msg)
+2765     # . . push args
+2766     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
+2767     68/push  1/imm32/none
+2768     51/push-ECX
 2769     # . . call
-2770     e8/call  print-int32-buffered/disp32
+2770     e8/call  check-ints-equal/disp32
 2771     # . . discard args
-2772     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2773     # . write-buffered(out, "/scale ")
+2772     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2773     # check-ints-equal(EDX, 2, msg)
 2774     # . . push args
-2775     68/push  "/scale "/imm32
-2776     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2777     # . . call
-2778     e8/call  write-buffered/disp32
-2779     # . . discard args
-2780     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2781     # . print-int32-buffered(out, disp)
-2782     # . . push args
-2783     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
-2784     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2785     # . . call
-2786     e8/call  print-int32-buffered/disp32
-2787     # . . discard args
-2788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2789     # . write-buffered(out, "/disp32")
-2790     # . . push args
-2791     68/push  "/disp32"/imm32
-2792     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2793     # . . call
-2794     e8/call  write-buffered/disp32
-2795     # . . discard args
-2796     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2797     e9/jump  $emit-indirect-mode:end/disp32
-2798 $emit-indirect-mode:check-for-disp:
-2799     # if (disp == 0) goto next check
-2800     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      0/imm32           # compare *(EBP+24)
-2801     74/jump-if-equal  $emit-indirect-mode:emit-indirect/disp8
-2802 $emit-indirect-mode:emit-disp:
-2803     # emit(out, "2/mod/*+disp32 " base "/rm32 " disp "/disp32")
-2804     # . write-buffered(out, "2/mod/*+disp32 ")
-2805     # . . push args
-2806     68/push  "2/mod/*+disp32 "/imm32
-2807     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2808     # . . call
-2809     e8/call  write-buffered/disp32
-2810     # . . discard args
-2811     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2812     # . print-int32-buffered(out, base)
-2813     # . . push args
-2814     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-2815     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2816     # . . call
-2817     e8/call  print-int32-buffered/disp32
-2818     # . . discard args
-2819     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2820     # . write-buffered(out, "/rm32 ")
-2821     # . . push args
-2822     68/push  "/rm32 "/imm32
-2823     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2824     # . . call
-2825     e8/call  write-buffered/disp32
-2826     # . . discard args
-2827     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2828     # . print-int32-buffered(out, disp)
-2829     # . . push args
-2830     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
-2831     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2832     # . . call
-2833     e8/call  print-int32-buffered/disp32
-2834     # . . discard args
-2835     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2836     # . write-buffered(out, "/disp32")
-2837     # . . push args
-2838     68/push  "/disp32"/imm32
-2839     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2775     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
+2776     68/push  2/imm32
+2777     52/push-EDX
+2778     # . . call
+2779     e8/call  check-ints-equal/disp32
+2780     # . . discard args
+2781     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2782     # check-ints-equal(EBX, 0, msg)
+2783     # . . push args
+2784     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
+2785     68/push  0/imm32
+2786     53/push-EBX
+2787     # . . call
+2788     e8/call  check-ints-equal/disp32
+2789     # . . discard args
+2790     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2791     # . epilog
+2792     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2793     5d/pop-to-EBP
+2794     c3/return
+2795 
+2796 test-parse-effective-address-base-index-scale-displacement:
+2797     # . prolog
+2798     55/push-EBP
+2799     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2800     # var slice/ECX = "*(esi + ecx<<2 - 0x34)"
+2801     b8/copy-to-EAX  "*(esi + ecx<<2 - 0x34)"/imm32
+2802     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+2803     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
+2804     05/add-to-EAX  4/imm32
+2805     # . ECX = {EAX, ECX}
+2806     51/push-ECX
+2807     50/push-EAX
+2808     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+2809     # EAX, ECX, EDX, EBX = parse-effective-address(slice)
+2810     # . . push args
+2811     51/push-ECX
+2812     # . . call
+2813     e8/call  parse-effective-address/disp32
+2814     # . . discard args
+2815     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+2816     # slice clobbered beyond this point
+2817     # check-ints-equal(EAX, 6, msg)
+2818     # . . push args
+2819     68/push  "F - test-parse-effective-address-base-index-scale/base"/imm32
+2820     68/push  6/imm32/ESI
+2821     50/push-EAX
+2822     # . . call
+2823     e8/call  check-ints-equal/disp32
+2824     # . . discard args
+2825     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2826     # check-ints-equal(ECX, 1, msg)
+2827     # . . push args
+2828     68/push  "F - test-parse-effective-address-base-index-scale/index"/imm32
+2829     68/push  1/imm32/none
+2830     51/push-ECX
+2831     # . . call
+2832     e8/call  check-ints-equal/disp32
+2833     # . . discard args
+2834     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2835     # check-ints-equal(EDX, 2, msg)
+2836     # . . push args
+2837     68/push  "F - test-parse-effective-address-base-index-scale/scale"/imm32
+2838     68/push  2/imm32
+2839     52/push-EDX
 2840     # . . call
-2841     e8/call  write-buffered/disp32
+2841     e8/call  check-ints-equal/disp32
 2842     # . . discard args
-2843     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2844     eb/jump  $emit-indirect-mode:end/disp8
-2845 $emit-indirect-mode:emit-indirect:
-2846     # emit(out, "0/mod/indirect " base "/rm32")
-2847     # . write-buffered(out, "0/mod/indirect ")
-2848     # . . push args
-2849     68/push  "0/mod/indirect "/imm32
-2850     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2851     # . . call
-2852     e8/call  write-buffered/disp32
-2853     # . . discard args
-2854     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2855     # . print-int32-buffered(out, base)
-2856     # . . push args
-2857     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-2858     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2859     # . . call
-2860     e8/call  print-int32-buffered/disp32
-2861     # . . discard args
-2862     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2863     # . write-buffered(out, "/rm32")
-2864     # . . push args
-2865     68/push  "/rm32"/imm32
-2866     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-2867     # . . call
-2868     e8/call  write-buffered/disp32
-2869     # . . discard args
-2870     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-2871 $emit-indirect-mode:end:
-2872     # . epilog
-2873     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2874     5d/pop-to-EBP
-2875     c3/return
-2876 
-2877 test-emit-indirect-mode:
-2878     # . prolog
-2879     55/push-EBP
-2880     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2881     # setup
-2882     # . clear-stream(_test-output-stream)
-2883     # . . push args
-2884     68/push  _test-output-stream/imm32
-2885     # . . call
-2886     e8/call  clear-stream/disp32
-2887     # . . discard args
-2888     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2889     # . clear-stream(_test-output-buffered-file+4)
-2890     # . . push args
-2891     b8/copy-to-EAX  _test-output-buffered-file/imm32
-2892     05/add-to-EAX  4/imm32
-2893     50/push-EAX
-2894     # . . call
-2895     e8/call  clear-stream/disp32
-2896     # . . discard args
-2897     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2898     # emit-indirect-mode(_test-output-buffered-file, 0, 4/none, 0, 0)
-2899     # . . write args
-2900     68/push  0/imm32/.disp
-2901     68/push  0/imm32/.scale
-2902     68/push  4/imm32/.index/none
-2903     68/push  0/imm32/.base
-2904     68/push  _test-output-buffered-file/imm32
-2905     # . . call
-2906     e8/call  emit-indirect-mode/disp32
-2907     # . . discard args
-2908     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
-2909     # . flush(_test-output-buffered-file)
-2910     # . . push args
-2911     68/push  _test-output-buffered-file/imm32
-2912     # . . call
-2913     e8/call  flush/disp32
-2914     # . . discard args
-2915     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2916 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
-2942     # check-stream-equal(_test-output-stream, "0/mod/indirect 0/rm32", msg)
-2943     # . . push args
-2944     68/push  "F - test-emit-indirect-mode"/imm32
-2945     68/push  "0/mod/indirect 0x00000000/rm32"/imm32
-2946     68/push  _test-output-stream/imm32
-2947     # . . call
-2948     e8/call  check-stream-equal/disp32
-2949     # . . discard args
-2950     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-2951     # . epilog
-2952     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-2953     5d/pop-to-EBP
-2954     c3/return
-2955 
-2956 test-emit-indirect-mode-2:
-2957     # . prolog
-2958     55/push-EBP
-2959     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-2960     # setup
-2961     # . clear-stream(_test-output-stream)
-2962     # . . push args
-2963     68/push  _test-output-stream/imm32
-2964     # . . call
-2965     e8/call  clear-stream/disp32
-2966     # . . discard args
-2967     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2968     # . clear-stream(_test-output-buffered-file+4)
-2969     # . . push args
-2970     b8/copy-to-EAX  _test-output-buffered-file/imm32
-2971     05/add-to-EAX  4/imm32
-2972     50/push-EAX
-2973     # . . call
-2974     e8/call  clear-stream/disp32
-2975     # . . discard args
-2976     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2977     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 0)
-2978     # . . write args
-2979     68/push  0/imm32/.disp
-2980     68/push  0/imm32/.scale
-2981     68/push  4/imm32/.index/none
-2982     68/push  7/imm32/.base
-2983     68/push  _test-output-buffered-file/imm32
-2984     # . . call
-2985     e8/call  emit-indirect-mode/disp32
-2986     # . . discard args
-2987     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
-2988     # . flush(_test-output-buffered-file)
-2989     # . . push args
-2990     68/push  _test-output-buffered-file/imm32
-2991     # . . call
-2992     e8/call  flush/disp32
-2993     # . . discard args
-2994     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-2995 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
-3021     # check-stream-equal(_test-output-stream, "0/mod/indirect 7/rm32", msg)
-3022     # . . push args
-3023     68/push  "F - test-emit-indirect-mode-2"/imm32
-3024     68/push  "0/mod/indirect 0x00000007/rm32"/imm32
-3025     68/push  _test-output-stream/imm32
-3026     # . . call
-3027     e8/call  check-stream-equal/disp32
-3028     # . . discard args
-3029     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3030     # . epilog
-3031     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3032     5d/pop-to-EBP
-3033     c3/return
-3034 
-3035 test-emit-indirect-mode-with-disp:
-3036     # . prolog
-3037     55/push-EBP
-3038     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3039     # setup
-3040     # . clear-stream(_test-output-stream)
-3041     # . . push args
-3042     68/push  _test-output-stream/imm32
-3043     # . . call
-3044     e8/call  clear-stream/disp32
-3045     # . . discard args
-3046     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3047     # . clear-stream(_test-output-buffered-file+4)
-3048     # . . push args
-3049     b8/copy-to-EAX  _test-output-buffered-file/imm32
-3050     05/add-to-EAX  4/imm32
-3051     50/push-EAX
+2843     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2844     # check-ints-equal(EBX, -0x34, msg)
+2845     # . . push args
+2846     68/push  "F - test-parse-effective-address-base-index-scale/displacement"/imm32
+2847     68/push  -0x34/imm32
+2848     53/push-EBX
+2849     # . . call
+2850     e8/call  check-ints-equal/disp32
+2851     # . . discard args
+2852     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+2853     # . epilog
+2854     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+2855     5d/pop-to-EBP
+2856     c3/return
+2857 
+2858 # Code generation:
+2859 #   if index is none and disp is 0, then mod = 0 and rm32 = base
+2860 #   if index is none, then mod = 2 and rm32 = base and disp32 = disp
+2861 #   if index is not none, then mod = 2 and rm32 = 4 and base = base and index = index and disp32 = disp
+2862 emit-indirect-mode:  # out : (address buffered-file), base : int, index : int, scale : int, disp : int
+2863     # . prolog
+2864     55/push-EBP
+2865     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+2866 $emit-indirect-mode:check-for-sib:
+2867     # if (index == 4/none) goto next check
+2868     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      4/imm32           # compare *(EBP+16)
+2869     0f 84/jump-if-equal  $emit-indirect-mode:check-for-disp/disp32
+2870 $emit-indirect-mode:emit-sib:
+2871     # emit(out, "2/mod/indirect 4/rm32/sib " base "/base " index "/index " scale "/scale " disp "/disp32")
+2872     # . write-buffered(out, "2/mod/*+disp32 4/rm32/sib ")
+2873     # . . push args
+2874     68/push  "2/mod/*+disp32 4/rm32/sib "/imm32
+2875     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2876     # . . call
+2877     e8/call  write-buffered/disp32
+2878     # . . discard args
+2879     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2880     # . print-int32-buffered(out, base)
+2881     # . . push args
+2882     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2883     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2884     # . . call
+2885     e8/call  print-int32-buffered/disp32
+2886     # . . discard args
+2887     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2888     # . write-buffered(out, "/base ")
+2889     # . . push args
+2890     68/push  "/base "/imm32
+2891     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2892     # . . call
+2893     e8/call  write-buffered/disp32
+2894     # . . discard args
+2895     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2896     # . print-int32-buffered(out, index)
+2897     # . . push args
+2898     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
+2899     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2900     # . . call
+2901     e8/call  print-int32-buffered/disp32
+2902     # . . discard args
+2903     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2904     # . write-buffered(out, "/index ")
+2905     # . . push args
+2906     68/push  "/index "/imm32
+2907     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2908     # . . call
+2909     e8/call  write-buffered/disp32
+2910     # . . discard args
+2911     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2912     # . print-int32-buffered(out, scale)
+2913     # . . push args
+2914     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x14/disp8      .                 # push *(EBP+20)
+2915     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2916     # . . call
+2917     e8/call  print-int32-buffered/disp32
+2918     # . . discard args
+2919     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2920     # . write-buffered(out, "/scale ")
+2921     # . . push args
+2922     68/push  "/scale "/imm32
+2923     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2924     # . . call
+2925     e8/call  write-buffered/disp32
+2926     # . . discard args
+2927     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2928     # . print-int32-buffered(out, disp)
+2929     # . . push args
+2930     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
+2931     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2932     # . . call
+2933     e8/call  print-int32-buffered/disp32
+2934     # . . discard args
+2935     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2936     # . write-buffered(out, "/disp32")
+2937     # . . push args
+2938     68/push  "/disp32"/imm32
+2939     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2940     # . . call
+2941     e8/call  write-buffered/disp32
+2942     # . . discard args
+2943     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2944     e9/jump  $emit-indirect-mode:end/disp32
+2945 $emit-indirect-mode:check-for-disp:
+2946     # if (disp == 0) goto next check
+2947     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      0/imm32           # compare *(EBP+24)
+2948     74/jump-if-equal  $emit-indirect-mode:emit-indirect/disp8
+2949 $emit-indirect-mode:emit-disp:
+2950     # emit(out, "2/mod/*+disp32 " base "/rm32 " disp "/disp32")
+2951     # . write-buffered(out, "2/mod/*+disp32 ")
+2952     # . . push args
+2953     68/push  "2/mod/*+disp32 "/imm32
+2954     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2955     # . . call
+2956     e8/call  write-buffered/disp32
+2957     # . . discard args
+2958     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2959     # . print-int32-buffered(out, base)
+2960     # . . push args
+2961     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+2962     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2963     # . . call
+2964     e8/call  print-int32-buffered/disp32
+2965     # . . discard args
+2966     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2967     # . write-buffered(out, "/rm32 ")
+2968     # . . push args
+2969     68/push  "/rm32 "/imm32
+2970     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2971     # . . call
+2972     e8/call  write-buffered/disp32
+2973     # . . discard args
+2974     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2975     # . print-int32-buffered(out, disp)
+2976     # . . push args
+2977     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x18/disp8      .                 # push *(EBP+24)
+2978     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2979     # . . call
+2980     e8/call  print-int32-buffered/disp32
+2981     # . . discard args
+2982     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2983     # . write-buffered(out, "/disp32")
+2984     # . . push args
+2985     68/push  "/disp32"/imm32
+2986     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2987     # . . call
+2988     e8/call  write-buffered/disp32
+2989     # . . discard args
+2990     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+2991     eb/jump  $emit-indirect-mode:end/disp8
+2992 $emit-indirect-mode:emit-indirect:
+2993     # emit(out, "0/mod/indirect " base "/rm32")
+2994     # . write-buffered(out, "0/mod/indirect ")
+2995     # . . push args
+2996     68/push  "0/mod/indirect "/imm32
+2997     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+2998     # . . call
+2999     e8/call  write-buffered/disp32
+3000     # . . discard args
+3001     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3002     # . print-int32-buffered(out, base)
+3003     # . . push args
+3004     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+3005     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3006     # . . call
+3007     e8/call  print-int32-buffered/disp32
+3008     # . . discard args
+3009     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3010     # . write-buffered(out, "/rm32")
+3011     # . . push args
+3012     68/push  "/rm32"/imm32
+3013     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+3014     # . . call
+3015     e8/call  write-buffered/disp32
+3016     # . . discard args
+3017     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3018 $emit-indirect-mode:end:
+3019     # . epilog
+3020     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3021     5d/pop-to-EBP
+3022     c3/return
+3023 
+3024 test-emit-indirect-mode:
+3025     # . prolog
+3026     55/push-EBP
+3027     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3028     # setup
+3029     # . clear-stream(_test-output-stream)
+3030     # . . push args
+3031     68/push  _test-output-stream/imm32
+3032     # . . call
+3033     e8/call  clear-stream/disp32
+3034     # . . discard args
+3035     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3036     # . clear-stream(_test-output-buffered-file+4)
+3037     # . . push args
+3038     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3039     05/add-to-EAX  4/imm32
+3040     50/push-EAX
+3041     # . . call
+3042     e8/call  clear-stream/disp32
+3043     # . . discard args
+3044     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3045     # emit-indirect-mode(_test-output-buffered-file, 0, 4/none, 0, 0)
+3046     # . . write args
+3047     68/push  0/imm32/.disp
+3048     68/push  0/imm32/.scale
+3049     68/push  4/imm32/.index/none
+3050     68/push  0/imm32/.base
+3051     68/push  _test-output-buffered-file/imm32
 3052     # . . call
-3053     e8/call  clear-stream/disp32
+3053     e8/call  emit-indirect-mode/disp32
 3054     # . . discard args
-3055     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3056     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 4)
-3057     # . . write args
-3058     68/push  4/imm32/.disp
-3059     68/push  0/imm32/.scale
-3060     68/push  4/imm32/.index/none
-3061     68/push  6/imm32/.base
-3062     68/push  _test-output-buffered-file/imm32
-3063     # . . call
-3064     e8/call  emit-indirect-mode/disp32
-3065     # . . discard args
-3066     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
-3067     # . flush(_test-output-buffered-file)
-3068     # . . push args
-3069     68/push  _test-output-buffered-file/imm32
-3070     # . . call
-3071     e8/call  flush/disp32
-3072     # . . discard args
-3073     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3074 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
-3100     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 4/disp32", msg)
-3101     # . . push args
-3102     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
-3103     68/push  "2/mod/*+disp32 0x00000006/rm32 0x00000004/disp32"/imm32
-3104     68/push  _test-output-stream/imm32
-3105     # . . call
-3106     e8/call  check-stream-equal/disp32
-3107     # . . discard args
-3108     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3109     # . epilog
-3110     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3111     5d/pop-to-EBP
-3112     c3/return
-3113 
-3114 test-emit-indirect-mode-with-disp-negative:
-3115     # . prolog
-3116     55/push-EBP
-3117     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3118     # setup
-3119     # . clear-stream(_test-output-stream)
-3120     # . . push args
-3121     68/push  _test-output-stream/imm32
-3122     # . . call
-3123     e8/call  clear-stream/disp32
-3124     # . . discard args
-3125     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3126     # . clear-stream(_test-output-buffered-file+4)
-3127     # . . push args
-3128     b8/copy-to-EAX  _test-output-buffered-file/imm32
-3129     05/add-to-EAX  4/imm32
-3130     50/push-EAX
+3055     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+3056     # . flush(_test-output-buffered-file)
+3057     # . . push args
+3058     68/push  _test-output-buffered-file/imm32
+3059     # . . call
+3060     e8/call  flush/disp32
+3061     # . . discard args
+3062     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3063 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+3089     # check-stream-equal(_test-output-stream, "0/mod/indirect 0/rm32", msg)
+3090     # . . push args
+3091     68/push  "F - test-emit-indirect-mode"/imm32
+3092     68/push  "0/mod/indirect 0x00000000/rm32"/imm32
+3093     68/push  _test-output-stream/imm32
+3094     # . . call
+3095     e8/call  check-stream-equal/disp32
+3096     # . . discard args
+3097     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3098     # . epilog
+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-emit-indirect-mode-2:
+3104     # . prolog
+3105     55/push-EBP
+3106     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3107     # setup
+3108     # . clear-stream(_test-output-stream)
+3109     # . . push args
+3110     68/push  _test-output-stream/imm32
+3111     # . . call
+3112     e8/call  clear-stream/disp32
+3113     # . . discard args
+3114     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3115     # . clear-stream(_test-output-buffered-file+4)
+3116     # . . push args
+3117     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3118     05/add-to-EAX  4/imm32
+3119     50/push-EAX
+3120     # . . call
+3121     e8/call  clear-stream/disp32
+3122     # . . discard args
+3123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3124     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 0)
+3125     # . . write args
+3126     68/push  0/imm32/.disp
+3127     68/push  0/imm32/.scale
+3128     68/push  4/imm32/.index/none
+3129     68/push  7/imm32/.base
+3130     68/push  _test-output-buffered-file/imm32
 3131     # . . call
-3132     e8/call  clear-stream/disp32
+3132     e8/call  emit-indirect-mode/disp32
 3133     # . . discard args
-3134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3135     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, -4)
-3136     # . . write args
-3137     68/push  -4/imm32/.disp
-3138     68/push  0/imm32/.scale
-3139     68/push  4/imm32/.index/none
-3140     68/push  6/imm32/.base
-3141     68/push  _test-output-buffered-file/imm32
-3142     # . . call
-3143     e8/call  emit-indirect-mode/disp32
-3144     # . . discard args
-3145     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
-3146     # . flush(_test-output-buffered-file)
-3147     # . . push args
-3148     68/push  _test-output-buffered-file/imm32
-3149     # . . call
-3150     e8/call  flush/disp32
-3151     # . . discard args
-3152     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3153 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
-3179     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 -4/disp32", msg)
-3180     # . . push args
-3181     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
-3182     68/push  "2/mod/*+disp32 0x00000006/rm32 0xfffffffc/disp32"/imm32
-3183     68/push  _test-output-stream/imm32
-3184     # . . call
-3185     e8/call  check-stream-equal/disp32
-3186     # . . discard args
-3187     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3188     # . epilog
-3189     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3190     5d/pop-to-EBP
-3191     c3/return
-3192 
-3193 test-emit-indirect-mode-with-sib:
-3194     # . prolog
-3195     55/push-EBP
-3196     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3197     # setup
-3198     # . clear-stream(_test-output-stream)
-3199     # . . push args
-3200     68/push  _test-output-stream/imm32
-3201     # . . call
-3202     e8/call  clear-stream/disp32
-3203     # . . discard args
-3204     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3205     # . clear-stream(_test-output-buffered-file+4)
-3206     # . . push args
-3207     b8/copy-to-EAX  _test-output-buffered-file/imm32
-3208     05/add-to-EAX  4/imm32
-3209     50/push-EAX
+3134     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+3135     # . flush(_test-output-buffered-file)
+3136     # . . push args
+3137     68/push  _test-output-buffered-file/imm32
+3138     # . . call
+3139     e8/call  flush/disp32
+3140     # . . discard args
+3141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3142 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+3168     # check-stream-equal(_test-output-stream, "0/mod/indirect 7/rm32", msg)
+3169     # . . push args
+3170     68/push  "F - test-emit-indirect-mode-2"/imm32
+3171     68/push  "0/mod/indirect 0x00000007/rm32"/imm32
+3172     68/push  _test-output-stream/imm32
+3173     # . . call
+3174     e8/call  check-stream-equal/disp32
+3175     # . . discard args
+3176     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3177     # . epilog
+3178     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3179     5d/pop-to-EBP
+3180     c3/return
+3181 
+3182 test-emit-indirect-mode-with-disp:
+3183     # . prolog
+3184     55/push-EBP
+3185     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3186     # setup
+3187     # . clear-stream(_test-output-stream)
+3188     # . . push args
+3189     68/push  _test-output-stream/imm32
+3190     # . . call
+3191     e8/call  clear-stream/disp32
+3192     # . . discard args
+3193     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3194     # . clear-stream(_test-output-buffered-file+4)
+3195     # . . push args
+3196     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3197     05/add-to-EAX  4/imm32
+3198     50/push-EAX
+3199     # . . call
+3200     e8/call  clear-stream/disp32
+3201     # . . discard args
+3202     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3203     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, 4)
+3204     # . . write args
+3205     68/push  4/imm32/.disp
+3206     68/push  0/imm32/.scale
+3207     68/push  4/imm32/.index/none
+3208     68/push  6/imm32/.base
+3209     68/push  _test-output-buffered-file/imm32
 3210     # . . call
-3211     e8/call  clear-stream/disp32
+3211     e8/call  emit-indirect-mode/disp32
 3212     # . . discard args
-3213     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3214     # emit-indirect-mode(_test-output-buffered-file, 6/base, 1/index, 2/scale, 4/disp)
-3215     # . . write args
-3216     68/push  4/imm32/.disp
-3217     68/push  2/imm32/.scale
-3218     68/push  1/imm32/.index
-3219     68/push  6/imm32/.base
-3220     68/push  _test-output-buffered-file/imm32
-3221     # . . call
-3222     e8/call  emit-indirect-mode/disp32
-3223     # . . discard args
-3224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
-3225     # . flush(_test-output-buffered-file)
-3226     # . . push args
-3227     68/push  _test-output-buffered-file/imm32
-3228     # . . call
-3229     e8/call  flush/disp32
-3230     # . . discard args
-3231     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3232 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
-3258     # check-stream-equal(_test-output-stream, "2/mod/indirect 4/rm32/sib 6/base 1/index 2/scale 4/disp", msg)
-3259     # . . push args
-3260     68/push  "F - test-emit-indirect-mode-with-sib"/imm32
-3261     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000006/base 0x00000001/index 0x00000002/scale 0x00000004/disp32"/imm32
-3262     68/push  _test-output-stream/imm32
-3263     # . . call
-3264     e8/call  check-stream-equal/disp32
-3265     # . . discard args
-3266     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3267     # . epilog
-3268     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3269     5d/pop-to-EBP
-3270     c3/return
-3271 
-3272 # update line->read to ')'
-3273 # line->read ends at ')'
-3274 skip-until-close-paren:  # line : (address stream)
-3275     # . prolog
-3276     55/push-EBP
-3277     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3278     # . save registers
-3279     50/push-EAX
-3280     51/push-ECX
-3281     52/push-EDX
-3282     # ECX = line
-3283     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
-3284     # EAX = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write])
-3285     # . . push &line->data[line->write]
-3286     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   8/disp8         .                 # copy *(ECX+8) to EDX
-3287     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ECX+EDX+12 to EDX
-3288     52/push-EDX
-3289     # . . push &line->data[line->read]
-3290     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
-3291     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ECX+EDX+12 to EDX
-3292     52/push-EDX
-3293     # . . call
-3294     e8/call  skip-until-close-paren-in-slice/disp32
-3295     # . . discard args
-3296     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3297     # line->read = EAX - line->data
-3298     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
-3299     2d/subtract-from-EAX  0xc/imm32
-3300     89/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         0/r32/EAX   4/disp8         .                 # copy EAX to *(ECX+4)
-3301 $skip-until-close-paren:end:
-3302     # . restore registers
-3303     5a/pop-to-EDX
-3304     59/pop-to-ECX
-3305     58/pop-to-EAX
-3306     # . epilog
-3307     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3308     5d/pop-to-EBP
-3309     c3/return
-3310 
-3311 test-skip-until-close-paren:
-3312     # . prolog
-3313     55/push-EBP
-3314     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3315     # setup
-3316     # . clear-stream(_test-input-stream)
-3317     # . . push args
-3318     68/push  _test-input-stream/imm32
-3319     # . . call
-3320     e8/call  clear-stream/disp32
-3321     # . . discard args
-3322     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3323     # . write(_test-input-stream, "*(abc) def")
-3324     # .                   indices:  0123 45
-3325     # . . push args
-3326     68/push  "*(abc) def"/imm32
-3327     68/push  _test-input-stream/imm32
-3328     # . . call
-3329     e8/call  write/disp32
-3330     # . . discard args
-3331     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3332     # precondition: line->read == 0
-3333     # . . push args
-3334     68/push  "F - test-skip-until-close-paren/precondition"/imm32
-3335     68/push  0/imm32
-3336     b8/copy-to-EAX  _test-input-stream/imm32
-3337     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-3338     # . . call
-3339     e8/call  check-ints-equal/disp32
-3340     # . . discard args
-3341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3342     # skip-until-close-paren(_test-input-stream)
-3343     # . . push args
-3344     68/push  _test-input-stream/imm32
-3345     # . . call
-3346     e8/call  skip-until-close-paren/disp32
-3347     # . . discard args
-3348     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3349     # check-ints-equal(line->read, 5, msg)
-3350     # . . push args
-3351     68/push  "F - test-skip-until-close-paren"/imm32
-3352     68/push  5/imm32
-3353     b8/copy-to-EAX  _test-input-stream/imm32
-3354     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-3355     # . . call
-3356     e8/call  check-ints-equal/disp32
-3357     # . . discard args
-3358     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3359     # . epilog
-3360     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3361     5d/pop-to-EBP
-3362     c3/return
-3363 
-3364 test-skip-until-close-paren-ignores-spaces:
-3365     # . prolog
-3366     55/push-EBP
-3367     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3368     # setup
-3369     # . clear-stream(_test-input-stream)
-3370     # . . push args
-3371     68/push  _test-input-stream/imm32
-3372     # . . call
-3373     e8/call  clear-stream/disp32
-3374     # . . discard args
-3375     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3376     # . write(_test-input-stream, "*(a b)/yz")
-3377     # . . push args
-3378     68/push  "*(a b)/yz"/imm32
-3379     68/push  _test-input-stream/imm32
-3380     # . . call
-3381     e8/call  write/disp32
-3382     # . . discard args
-3383     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3384     # precondition: line->read == 0
-3385     # . . push args
-3386     68/push  "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32
-3387     68/push  0/imm32
-3388     b8/copy-to-EAX  _test-input-stream/imm32
-3389     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-3390     # . . call
-3391     e8/call  check-ints-equal/disp32
-3392     # . . discard args
-3393     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3394     # skip-until-close-paren(_test-input-stream)
-3395     # . . push args
-3396     68/push  _test-input-stream/imm32
-3397     # . . call
-3398     e8/call  skip-until-close-paren/disp32
-3399     # . . discard args
-3400     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3401     # check-ints-equal(line->read, 5, msg)
-3402     # . . push args
-3403     68/push  "F - test-skip-until-close-paren-ignores-spaces"/imm32
-3404     68/push  5/imm32
-3405     b8/copy-to-EAX  _test-input-stream/imm32
-3406     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-3407     # . . call
-3408     e8/call  check-ints-equal/disp32
-3409     # . . discard args
-3410     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3411     # . epilog
-3412     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3413     5d/pop-to-EBP
-3414     c3/return
-3415 
-3416 test-skip-until-close-paren-works-from-mid-stream:
-3417     # . prolog
-3418     55/push-EBP
-3419     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3420     # setup
-3421     # . clear-stream(_test-input-stream)
-3422     # . . push args
-3423     68/push  _test-input-stream/imm32
-3424     # . . call
-3425     e8/call  clear-stream/disp32
-3426     # . . discard args
-3427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3428     # . write(_test-input-stream, "0 *(a b)/yz")
-3429     # . . push args
-3430     68/push  "0 *(a b)/yz"/imm32
-3431     68/push  _test-input-stream/imm32
-3432     # . . call
-3433     e8/call  write/disp32
-3434     # . . discard args
-3435     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3436     # precondition: _test-input-stream->read == 2
-3437     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         2/imm32           # copy to *(EAX+4)
-3438     # skip-until-close-paren(_test-input-stream)
-3439     # . . push args
-3440     68/push  _test-input-stream/imm32
-3441     # . . call
-3442     e8/call  skip-until-close-paren/disp32
-3443     # . . discard args
-3444     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3445     # check-ints-equal(_test-input-stream->read, 7, msg)
-3446     # . . push args
-3447     68/push  "F - test-skip-until-close-paren-works-from-mid-stream"/imm32
-3448     68/push  7/imm32
-3449     b8/copy-to-EAX  _test-input-stream/imm32
-3450     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
-3451     # . . call
-3452     e8/call  check-ints-equal/disp32
-3453     # . . discard args
-3454     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3455     # . epilog
-3456     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3457     5d/pop-to-EBP
-3458     c3/return
-3459 
-3460 skip-until-close-paren-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/EAX
-3461     # . prolog
-3462     55/push-EBP
-3463     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3464     # . save registers
-3465     51/push-ECX
-3466     52/push-EDX
-3467     # ECX = curr
-3468     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
-3469     # EDX = end
-3470     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8         .               # copy *(EBP+12) to EDX
-3471     # EAX = 0
-3472     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-3473     # skip initial dquote
-3474     41/increment-ECX
-3475 $skip-until-close-paren-in-slice:loop:
-3476     # if (curr >= end) break
-3477     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-3478     73/jump-if-greater-unsigned-or-equal  $skip-until-close-paren-in-slice:break/disp8
-3479     # AL = *curr
-3480     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-3481 $skip-until-close-paren-in-slice:check-close:
-3482     # if (EAX == ')') break
-3483     3d/compare-EAX-and  0x29/imm32/close-paren
-3484     74/jump-if-equal  $skip-until-close-paren-in-slice:break/disp8
-3485     # ++curr
-3486     41/increment-ECX
-3487     eb/jump  $skip-until-close-paren-in-slice:loop/disp8
-3488 $skip-until-close-paren-in-slice:break:
-3489     # return curr
-3490     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to EAX
-3491 $skip-until-close-paren-in-slice:end:
-3492     # . restore registers
-3493     5a/pop-to-EDX
-3494     59/pop-to-ECX
-3495     # . epilog
-3496     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3497     5d/pop-to-EBP
-3498     c3/return
-3499 
-3500 test-skip-until-close-paren-in-slice:
-3501     # . prolog
-3502     55/push-EBP
-3503     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3504     # setup: (EAX..ECX) = "*(abc) def"
-3505     b8/copy-to-EAX  "*(abc) def"/imm32
-3506     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3507     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
-3508     05/add-to-EAX  4/imm32
-3509     # EAX = skip-until-close-paren-in-slice(EAX, ECX)
-3510     # . . push args
-3511     51/push-ECX
-3512     50/push-EAX
-3513     # . . call
-3514     e8/call  skip-until-close-paren-in-slice/disp32
-3515     # . . discard args
-3516     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3517     # check-ints-equal(ECX-EAX, 5, msg)  # EAX is at the ')'
-3518     # . . push args
-3519     68/push  "F - test-skip-until-close-paren-in-slice"/imm32
-3520     68/push  5/imm32
-3521     # . . push ECX-EAX
-3522     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-3523     51/push-ECX
-3524     # . . call
-3525     e8/call  check-ints-equal/disp32
-3526     # . . discard args
-3527     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3528     # . epilog
-3529     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3530     5d/pop-to-EBP
-3531     c3/return
-3532 
-3533 test-skip-until-close-paren-in-slice-ignores-spaces:
-3534     # . prolog
-3535     55/push-EBP
-3536     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3537     # setup: (EAX..ECX) = "*(a b)/yz"
-3538     b8/copy-to-EAX  "*(a b)/yz"/imm32
-3539     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3540     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
-3541     05/add-to-EAX  4/imm32
-3542     # EAX = skip-until-close-paren-in-slice(EAX, ECX)
-3543     # . . push args
-3544     51/push-ECX
-3545     50/push-EAX
-3546     # . . call
-3547     e8/call  skip-until-close-paren-in-slice/disp32
-3548     # . . discard args
-3549     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3550     # check-ints-equal(ECX-EAX, 4, msg)  # EAX is at the ')'
-3551     # . . push args
-3552     68/push  "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32
-3553     68/push  4/imm32
-3554     # . . push ECX-EAX
-3555     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-3556     51/push-ECX
-3557     # . . call
-3558     e8/call  check-ints-equal/disp32
-3559     # . . discard args
-3560     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3561     # . epilog
-3562     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3563     5d/pop-to-EBP
-3564     c3/return
-3565 
-3566 test-skip-until-close-paren-in-slice-stops-at-end:
-3567     # . prolog
-3568     55/push-EBP
-3569     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3570     # setup: (EAX..ECX) = "*(abc"  # unbalanced dquote
-3571     b8/copy-to-EAX  "*(abc"/imm32
-3572     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3573     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
-3574     05/add-to-EAX  4/imm32
-3575     # EAX = skip-until-close-paren-in-slice(EAX, ECX)
+3213     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+3214     # . flush(_test-output-buffered-file)
+3215     # . . push args
+3216     68/push  _test-output-buffered-file/imm32
+3217     # . . call
+3218     e8/call  flush/disp32
+3219     # . . discard args
+3220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3221 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+3247     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 4/disp32", msg)
+3248     # . . push args
+3249     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
+3250     68/push  "2/mod/*+disp32 0x00000006/rm32 0x00000004/disp32"/imm32
+3251     68/push  _test-output-stream/imm32
+3252     # . . call
+3253     e8/call  check-stream-equal/disp32
+3254     # . . discard args
+3255     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3256     # . epilog
+3257     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3258     5d/pop-to-EBP
+3259     c3/return
+3260 
+3261 test-emit-indirect-mode-with-disp-negative:
+3262     # . prolog
+3263     55/push-EBP
+3264     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3265     # setup
+3266     # . clear-stream(_test-output-stream)
+3267     # . . push args
+3268     68/push  _test-output-stream/imm32
+3269     # . . call
+3270     e8/call  clear-stream/disp32
+3271     # . . discard args
+3272     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3273     # . clear-stream(_test-output-buffered-file+4)
+3274     # . . push args
+3275     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3276     05/add-to-EAX  4/imm32
+3277     50/push-EAX
+3278     # . . call
+3279     e8/call  clear-stream/disp32
+3280     # . . discard args
+3281     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3282     # emit-indirect-mode(_test-output-buffered-file, 6, 4/none, 0, -4)
+3283     # . . write args
+3284     68/push  -4/imm32/.disp
+3285     68/push  0/imm32/.scale
+3286     68/push  4/imm32/.index/none
+3287     68/push  6/imm32/.base
+3288     68/push  _test-output-buffered-file/imm32
+3289     # . . call
+3290     e8/call  emit-indirect-mode/disp32
+3291     # . . discard args
+3292     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x14/imm32        # add to ESP
+3293     # . flush(_test-output-buffered-file)
+3294     # . . push args
+3295     68/push  _test-output-buffered-file/imm32
+3296     # . . call
+3297     e8/call  flush/disp32
+3298     # . . discard args
+3299     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3300 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
+3326     # check-stream-equal(_test-output-stream, "2/mod/*+disp32 6/rm32 -4/disp32", msg)
+3327     # . . push args
+3328     68/push  "F - test-emit-indirect-mode-with-disp"/imm32
+3329     68/push  "2/mod/*+disp32 0x00000006/rm32 0xfffffffc/disp32"/imm32
+3330     68/push  _test-output-stream/imm32
+3331     # . . call
+3332     e8/call  check-stream-equal/disp32
+3333     # . . discard args
+3334     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3335     # . epilog
+3336     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3337     5d/pop-to-EBP
+3338     c3/return
+3339 
+3340 test-emit-indirect-mode-with-sib:
+3341     # . prolog
+3342     55/push-EBP
+3343     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3344     # setup
+3345     # . clear-stream(_test-output-stream)
+3346     # . . push args
+3347     68/push  _test-output-stream/imm32
+3348     # . . call
+3349     e8/call  clear-stream/disp32
+3350     # . . discard args
+3351     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3352     # . clear-stream(_test-output-buffered-file+4)
+3353     # . . push args
+3354     b8/copy-to-EAX  _test-output-buffered-file/imm32
+3355     05/add-to-EAX  4/imm32
+3356     50/push-EAX
+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, 6/base, 1/index, 2/scale, 4/disp)
+3362     # . . write args
+3363     68/push  4/imm32/.disp
+3364     68/push  2/imm32/.scale
+3365     68/push  1/imm32/.index
+3366     68/push  6/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, "2/mod/indirect 4/rm32/sib 6/base 1/index 2/scale 4/disp", msg)
+3406     # . . push args
+3407     68/push  "F - test-emit-indirect-mode-with-sib"/imm32
+3408     68/push  "2/mod/*+disp32 4/rm32/sib 0x00000006/base 0x00000001/index 0x00000002/scale 0x00000004/disp32"/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     # . epilog
+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 # update line->read to ')'
+3420 # line->read ends at ')'
+3421 skip-until-close-paren:  # line : (address stream)
+3422     # . prolog
+3423     55/push-EBP
+3424     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3425     # . save registers
+3426     50/push-EAX
+3427     51/push-ECX
+3428     52/push-EDX
+3429     # ECX = line
+3430     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+3431     # EAX = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write])
+3432     # . . push &line->data[line->write]
+3433     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   8/disp8         .                 # copy *(ECX+8) to EDX
+3434     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ECX+EDX+12 to EDX
+3435     52/push-EDX
+3436     # . . push &line->data[line->read]
+3437     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
+3438     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ECX  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ECX+EDX+12 to EDX
+3439     52/push-EDX
+3440     # . . call
+3441     e8/call  skip-until-close-paren-in-slice/disp32
+3442     # . . discard args
+3443     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3444     # line->read = EAX - line->data
+3445     29/subtract                     3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # subtract ECX from EAX
+3446     2d/subtract-from-EAX  0xc/imm32
+3447     89/copy                         1/mod/*+disp8   1/rm32/ECX    .           .                         0/r32/EAX   4/disp8         .                 # copy EAX to *(ECX+4)
+3448 $skip-until-close-paren:end:
+3449     # . restore registers
+3450     5a/pop-to-EDX
+3451     59/pop-to-ECX
+3452     58/pop-to-EAX
+3453     # . epilog
+3454     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3455     5d/pop-to-EBP
+3456     c3/return
+3457 
+3458 test-skip-until-close-paren:
+3459     # . prolog
+3460     55/push-EBP
+3461     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3462     # setup
+3463     # . clear-stream(_test-input-stream)
+3464     # . . push args
+3465     68/push  _test-input-stream/imm32
+3466     # . . call
+3467     e8/call  clear-stream/disp32
+3468     # . . discard args
+3469     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3470     # . write(_test-input-stream, "*(abc) def")
+3471     # .                   indices:  0123 45
+3472     # . . push args
+3473     68/push  "*(abc) def"/imm32
+3474     68/push  _test-input-stream/imm32
+3475     # . . call
+3476     e8/call  write/disp32
+3477     # . . discard args
+3478     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3479     # precondition: line->read == 0
+3480     # . . push args
+3481     68/push  "F - test-skip-until-close-paren/precondition"/imm32
+3482     68/push  0/imm32
+3483     b8/copy-to-EAX  _test-input-stream/imm32
+3484     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+3485     # . . call
+3486     e8/call  check-ints-equal/disp32
+3487     # . . discard args
+3488     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3489     # skip-until-close-paren(_test-input-stream)
+3490     # . . push args
+3491     68/push  _test-input-stream/imm32
+3492     # . . call
+3493     e8/call  skip-until-close-paren/disp32
+3494     # . . discard args
+3495     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3496     # check-ints-equal(line->read, 5, msg)
+3497     # . . push args
+3498     68/push  "F - test-skip-until-close-paren"/imm32
+3499     68/push  5/imm32
+3500     b8/copy-to-EAX  _test-input-stream/imm32
+3501     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+3502     # . . call
+3503     e8/call  check-ints-equal/disp32
+3504     # . . discard args
+3505     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3506     # . epilog
+3507     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3508     5d/pop-to-EBP
+3509     c3/return
+3510 
+3511 test-skip-until-close-paren-ignores-spaces:
+3512     # . prolog
+3513     55/push-EBP
+3514     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3515     # setup
+3516     # . clear-stream(_test-input-stream)
+3517     # . . push args
+3518     68/push  _test-input-stream/imm32
+3519     # . . call
+3520     e8/call  clear-stream/disp32
+3521     # . . discard args
+3522     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3523     # . write(_test-input-stream, "*(a b)/yz")
+3524     # . . push args
+3525     68/push  "*(a b)/yz"/imm32
+3526     68/push  _test-input-stream/imm32
+3527     # . . call
+3528     e8/call  write/disp32
+3529     # . . discard args
+3530     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3531     # precondition: line->read == 0
+3532     # . . push args
+3533     68/push  "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32
+3534     68/push  0/imm32
+3535     b8/copy-to-EAX  _test-input-stream/imm32
+3536     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+3537     # . . call
+3538     e8/call  check-ints-equal/disp32
+3539     # . . discard args
+3540     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3541     # skip-until-close-paren(_test-input-stream)
+3542     # . . push args
+3543     68/push  _test-input-stream/imm32
+3544     # . . call
+3545     e8/call  skip-until-close-paren/disp32
+3546     # . . discard args
+3547     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3548     # check-ints-equal(line->read, 5, msg)
+3549     # . . push args
+3550     68/push  "F - test-skip-until-close-paren-ignores-spaces"/imm32
+3551     68/push  5/imm32
+3552     b8/copy-to-EAX  _test-input-stream/imm32
+3553     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+3554     # . . call
+3555     e8/call  check-ints-equal/disp32
+3556     # . . discard args
+3557     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3558     # . epilog
+3559     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3560     5d/pop-to-EBP
+3561     c3/return
+3562 
+3563 test-skip-until-close-paren-works-from-mid-stream:
+3564     # . prolog
+3565     55/push-EBP
+3566     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3567     # setup
+3568     # . clear-stream(_test-input-stream)
+3569     # . . push args
+3570     68/push  _test-input-stream/imm32
+3571     # . . call
+3572     e8/call  clear-stream/disp32
+3573     # . . discard args
+3574     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3575     # . write(_test-input-stream, "0 *(a b)/yz")
 3576     # . . push args
-3577     51/push-ECX
-3578     50/push-EAX
+3577     68/push  "0 *(a b)/yz"/imm32
+3578     68/push  _test-input-stream/imm32
 3579     # . . call
-3580     e8/call  skip-until-close-paren-in-slice/disp32
+3580     e8/call  write/disp32
 3581     # . . discard args
 3582     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3583     # check-ints-equal(ECX-EAX, 0, msg)  # skipped to end of slice
-3584     # . . push args
-3585     68/push  "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32
-3586     68/push  0/imm32
-3587     # . . push ECX-EAX
-3588     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
-3589     51/push-ECX
-3590     # . . call
-3591     e8/call  check-ints-equal/disp32
-3592     # . . discard args
-3593     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3594     # . epilog
-3595     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3596     5d/pop-to-EBP
-3597     c3/return
-3598 
-3599 # assumes 'in' starts with optional '+' or '-', optional whitespace, and an unsigned integer
-3600 # returns the value of the integer
-3601 # side-effect: modifies 'in' to skip past the integer
-3602 next-hex-int:  # in : (address slice) -> result/EAX
-3603     # . prolog
-3604     55/push-EBP
-3605     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3606     # . save registers
-3607     51/push-ECX
-3608     52/push-EDX
-3609     53/push-EBX
-3610     56/push-ESI
-3611     57/push-EDI
-3612     # result/EDI = 0
-3613     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
-3614     # ESI = in
-3615     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-3616     # EDX = in->end
-3617     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
-3618     # curr/ECX = in->start
-3619     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
-3620     # negate?/EBX = false
-3621     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-3622     # EAX = *curr
-3623     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-3624     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-3625 $next-hex-int:positive:
-3626     # if (*curr == '+') ++curr
-3627     3d/compare-EAX-and  0x2b/imm32/+
-3628     75/jump-if-not-equal  $next-hex-int:negative/disp8
-3629     # . ++curr
-3630     41/increment-ECX
-3631     eb/jump  $next-hex-int:skip-whitespace/disp8
-3632 $next-hex-int:negative:
-3633     # else if (*curr == '-') ++curr, negate = true
-3634     3d/compare-EAX-and  0x2d/imm32/-
-3635     75/jump-if-not-equal  $next-hex-int:skip-whitespace/disp8
-3636 $next-hex-int:need-to-negate:
-3637     # . ++curr
-3638     41/increment-ECX
-3639     # . negate = true
-3640     bb/copy-to-EBX  1/imm32/true
-3641     # fall through
-3642 $next-hex-int:skip-whitespace:
-3643     # spill EAX
-3644     50/push-EAX
-3645     # EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
-3646     # . . push args
-3647     52/push-EDX
-3648     51/push-ECX
-3649     # . . call
-3650     e8/call  skip-chars-matching-whitespace-in-slice/disp32
-3651     # . . discard args
-3652     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3653     # ECX = EAX
-3654     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
-3655     # restore EAX
-3656     58/pop-to-EAX
-3657 $next-hex-int:initial-0:
-3658     # skip past leading '0x'
-3659     # . if (*curr != '0') jump to loop
-3660     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-3661     3d/compare-EAX-and  0x30/imm32/0
-3662     75/jump-if-not-equal  $next-hex-int:loop/disp8
-3663     # . ++curr
-3664     41/increment-ECX
-3665 $next-hex-int:initial-0x:
-3666     # . if (curr >= in->end) return result
-3667     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-3668     73/jump-if-greater-or-equal-unsigned  $next-hex-int:end/disp8
-3669     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
-3670     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-3671     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-3672     3d/compare-EAX-and  0x78/imm32/x
-3673     75/jump-if-not-equal  $next-hex-int:loop/disp8
-3674     # . ++curr
-3675     41/increment-ECX
-3676 $next-hex-int:loop:
-3677     # if (curr >= in->end) break
-3678     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-3679     73/jump-if-greater-or-equal-unsigned  $next-hex-int:break/disp8
-3680     # if (!is-hex-digit?(*curr)) break
-3681     # . EAX = *curr
-3682     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-3683     # . EAX = is-hex-digit?(*curr)
-3684     # . . push args
-3685     50/push-EAX
-3686     # . . call
-3687     e8/call  is-hex-digit?/disp32
-3688     # . . discard args
-3689     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3690     # . if (EAX == 0) break
-3691     3d/compare-EAX-and  0/imm32
-3692     74/jump-if-equal  $next-hex-int:break/disp8
-3693     # EAX = from-hex-char(*curr)
-3694     # . . copy arg to EAX
-3695     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-3696     # . . call
-3697     e8/call  from-hex-char/disp32
-3698     # result = result * 16 + EAX
-3699     c1/shift    4/subop/left        3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm8            # shift EDI left by 4 bits
-3700     01/add                          3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to EDI
-3701     # ++curr
-3702     41/increment-ECX
-3703     # loop
-3704     eb/jump  $next-hex-int:loop/disp8
-3705 $next-hex-int:break:
-3706     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
-3707     74/jump-if-equal  $next-hex-int:end/disp8
-3708 $next-hex-int:negate:
-3709     f7          3/subop/negate      3/mod/direct    7/rm32/EDI    .           .             .           .           .               .                 # negate EDI
-3710 $next-hex-int:end:
-3711     # word->start = curr
-3712     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy ECX to *ESI
-3713     # return EDI
-3714     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy EDI to EAX
-3715     # . restore registers
-3716     5f/pop-to-EDI
-3717     5e/pop-to-ESI
-3718     5b/pop-to-EBX
-3719     5a/pop-to-EDX
-3720     59/pop-to-ECX
-3721     # . epilog
-3722     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3723     5d/pop-to-EBP
-3724     c3/return
-3725 
-3726 $next-hex-int:abort:
-3727     # . _write(2/stderr, error)
-3728     # . . push args
-3729     68/push  "next-hex-int: invalid hex char: "/imm32
-3730     68/push  2/imm32/stderr
-3731     # . . call
-3732     e8/call  _write/disp32
-3733     # . . discard args
-3734     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3735     # . clear-stream(Stderr+4)
-3736     # . . save EAX
-3737     50/push-EAX
-3738     # . . push args
-3739     b8/copy-to-EAX  Stderr/imm32
-3740     05/add-to-EAX  4/imm32
-3741     50/push-EAX
-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     # . . restore EAX
-3747     58/pop-to-EAX
-3748     # . print-int32-buffered(Stderr, EAX)
-3749     # . . push args
-3750     50/push-EAX
-3751     68/push  Stderr/imm32
-3752     # . . call
-3753     e8/call  print-int32-buffered/disp32
-3754     # . . discard args
-3755     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3756     # . flush(Stderr)
-3757     # . . push args
-3758     68/push  Stderr/imm32
-3759     # . . call
-3760     e8/call  flush/disp32
-3761     # . . discard args
-3762     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3763     # . _write(2/stderr, "\n")
-3764     # . . push args
-3765     68/push  "\n"/imm32
-3766     68/push  2/imm32/stderr
-3767     # . . call
-3768     e8/call  _write/disp32
-3769     # . . discard args
-3770     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-3771     # . syscall(exit, 1)
-3772     bb/copy-to-EBX  1/imm32
-3773     b8/copy-to-EAX  1/imm32/exit
-3774     cd/syscall  0x80/imm8
-3775     # never gets here
-3776 
-3777 test-next-hex-int-single-digit:
-3778     # . prolog
-3779     55/push-EBP
-3780     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3781     # (EAX..ECX) = "+a)"
-3782     b8/copy-to-EAX  "+a)"/imm32
-3783     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3784     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
-3785     05/add-to-EAX  4/imm32
-3786     # var slice/ECX = {EAX, ECX}
-3787     51/push-ECX
-3788     50/push-EAX
-3789     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3790     # EAX = next-hex-int(slice)
-3791     # . . push args
-3792     51/push-ECX
-3793     # . . call
-3794     e8/call  next-hex-int/disp32
-3795     # . . discard args
-3796     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3797     # check-ints-equal(EAX, 0xa, msg)
-3798     # . . push args
-3799     68/push  "F - test-next-hex-int-single-digit"/imm32
-3800     68/push  0xa/imm32
-3801     50/push-EAX
-3802     # . . call
-3803     e8/call  check-ints-equal/disp32
-3804     # . . discard args
-3805     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3806     # . epilog
-3807     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3808     5d/pop-to-EBP
-3809     c3/return
-3810 
-3811 test-next-hex-int-multi-digit:
-3812     # . prolog
-3813     55/push-EBP
-3814     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3815     # (EAX..ECX) = "+ 34a)"
-3816     b8/copy-to-EAX  "+ 34a)"/imm32
-3817     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3818     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
-3819     05/add-to-EAX  4/imm32
-3820     # var slice/ECX = {EAX, ECX}
-3821     51/push-ECX
-3822     50/push-EAX
-3823     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3824     # EAX = next-hex-int(slice)
-3825     # . . push args
-3826     51/push-ECX
-3827     # . . call
-3828     e8/call  next-hex-int/disp32
-3829     # . . discard args
-3830     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3831     # check-ints-equal(EAX, 0x34a, msg)
-3832     # . . push args
-3833     68/push  "F - test-next-hex-int-multi-digit"/imm32
-3834     68/push  0x34a/imm32
-3835     50/push-EAX
-3836     # . . call
-3837     e8/call  check-ints-equal/disp32
-3838     # . . discard args
-3839     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3840     # . epilog
-3841     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3842     5d/pop-to-EBP
-3843     c3/return
-3844 
-3845 test-next-hex-int-0x-prefix:
-3846     # . prolog
-3847     55/push-EBP
-3848     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3849     # (EAX..ECX) = "+0x34)"
-3850     b8/copy-to-EAX  "+0x34)"/imm32
-3851     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3852     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
-3853     05/add-to-EAX  4/imm32
-3854     # var slice/ECX = {EAX, ECX}
-3855     51/push-ECX
-3856     50/push-EAX
-3857     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3858     # EAX = next-hex-int(slice)
-3859     # . . push args
-3860     51/push-ECX
-3861     # . . call
-3862     e8/call  next-hex-int/disp32
-3863     # . . discard args
-3864     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3865     # check-ints-equal(EAX, 0x34, msg)
-3866     # . . push args
-3867     68/push  "F - test-next-hex-int-0x-prefix"/imm32
-3868     68/push  0x34/imm32
-3869     50/push-EAX
-3870     # . . call
-3871     e8/call  check-ints-equal/disp32
-3872     # . . discard args
-3873     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3874     # . epilog
-3875     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3876     5d/pop-to-EBP
-3877     c3/return
-3878 
-3879 test-next-hex-int-zero:
-3880     # . prolog
-3881     55/push-EBP
-3882     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3883     # (EAX..ECX) = "+0)"
-3884     b8/copy-to-EAX  "+0)"/imm32
-3885     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3886     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
+3583     # precondition: _test-input-stream->read == 2
+3584     c7          0/subop/copy        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         2/imm32           # copy to *(EAX+4)
+3585     # skip-until-close-paren(_test-input-stream)
+3586     # . . push args
+3587     68/push  _test-input-stream/imm32
+3588     # . . call
+3589     e8/call  skip-until-close-paren/disp32
+3590     # . . discard args
+3591     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3592     # check-ints-equal(_test-input-stream->read, 7, msg)
+3593     # . . push args
+3594     68/push  "F - test-skip-until-close-paren-works-from-mid-stream"/imm32
+3595     68/push  7/imm32
+3596     b8/copy-to-EAX  _test-input-stream/imm32
+3597     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
+3598     # . . call
+3599     e8/call  check-ints-equal/disp32
+3600     # . . discard args
+3601     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3602     # . epilog
+3603     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3604     5d/pop-to-EBP
+3605     c3/return
+3606 
+3607 skip-until-close-paren-in-slice:  # curr : (address byte), end : (address byte) -> new_curr/EAX
+3608     # . prolog
+3609     55/push-EBP
+3610     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3611     # . save registers
+3612     51/push-ECX
+3613     52/push-EDX
+3614     # ECX = curr
+3615     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
+3616     # EDX = end
+3617     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .                         2/r32/EDX   0xc/disp8         .               # copy *(EBP+12) to EDX
+3618     # EAX = 0
+3619     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+3620     # skip initial dquote
+3621     41/increment-ECX
+3622 $skip-until-close-paren-in-slice:loop:
+3623     # if (curr >= end) break
+3624     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+3625     73/jump-if-greater-unsigned-or-equal  $skip-until-close-paren-in-slice:break/disp8
+3626     # AL = *curr
+3627     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+3628 $skip-until-close-paren-in-slice:check-close:
+3629     # if (EAX == ')') break
+3630     3d/compare-EAX-and  0x29/imm32/close-paren
+3631     74/jump-if-equal  $skip-until-close-paren-in-slice:break/disp8
+3632     # ++curr
+3633     41/increment-ECX
+3634     eb/jump  $skip-until-close-paren-in-slice:loop/disp8
+3635 $skip-until-close-paren-in-slice:break:
+3636     # return curr
+3637     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy ECX to EAX
+3638 $skip-until-close-paren-in-slice:end:
+3639     # . restore registers
+3640     5a/pop-to-EDX
+3641     59/pop-to-ECX
+3642     # . epilog
+3643     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3644     5d/pop-to-EBP
+3645     c3/return
+3646 
+3647 test-skip-until-close-paren-in-slice:
+3648     # . prolog
+3649     55/push-EBP
+3650     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3651     # setup: (EAX..ECX) = "*(abc) def"
+3652     b8/copy-to-EAX  "*(abc) def"/imm32
+3653     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3654     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
+3655     05/add-to-EAX  4/imm32
+3656     # EAX = skip-until-close-paren-in-slice(EAX, ECX)
+3657     # . . push args
+3658     51/push-ECX
+3659     50/push-EAX
+3660     # . . call
+3661     e8/call  skip-until-close-paren-in-slice/disp32
+3662     # . . discard args
+3663     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3664     # check-ints-equal(ECX-EAX, 5, msg)  # EAX is at the ')'
+3665     # . . push args
+3666     68/push  "F - test-skip-until-close-paren-in-slice"/imm32
+3667     68/push  5/imm32
+3668     # . . push ECX-EAX
+3669     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+3670     51/push-ECX
+3671     # . . call
+3672     e8/call  check-ints-equal/disp32
+3673     # . . discard args
+3674     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3675     # . epilog
+3676     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3677     5d/pop-to-EBP
+3678     c3/return
+3679 
+3680 test-skip-until-close-paren-in-slice-ignores-spaces:
+3681     # . prolog
+3682     55/push-EBP
+3683     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3684     # setup: (EAX..ECX) = "*(a b)/yz"
+3685     b8/copy-to-EAX  "*(a b)/yz"/imm32
+3686     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3687     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
+3688     05/add-to-EAX  4/imm32
+3689     # EAX = skip-until-close-paren-in-slice(EAX, ECX)
+3690     # . . push args
+3691     51/push-ECX
+3692     50/push-EAX
+3693     # . . call
+3694     e8/call  skip-until-close-paren-in-slice/disp32
+3695     # . . discard args
+3696     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3697     # check-ints-equal(ECX-EAX, 4, msg)  # EAX is at the ')'
+3698     # . . push args
+3699     68/push  "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32
+3700     68/push  4/imm32
+3701     # . . push ECX-EAX
+3702     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+3703     51/push-ECX
+3704     # . . call
+3705     e8/call  check-ints-equal/disp32
+3706     # . . discard args
+3707     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3708     # . epilog
+3709     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3710     5d/pop-to-EBP
+3711     c3/return
+3712 
+3713 test-skip-until-close-paren-in-slice-stops-at-end:
+3714     # . prolog
+3715     55/push-EBP
+3716     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3717     # setup: (EAX..ECX) = "*(abc"  # unbalanced dquote
+3718     b8/copy-to-EAX  "*(abc"/imm32
+3719     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3720     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
+3721     05/add-to-EAX  4/imm32
+3722     # EAX = skip-until-close-paren-in-slice(EAX, ECX)
+3723     # . . push args
+3724     51/push-ECX
+3725     50/push-EAX
+3726     # . . call
+3727     e8/call  skip-until-close-paren-in-slice/disp32
+3728     # . . discard args
+3729     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3730     # check-ints-equal(ECX-EAX, 0, msg)  # skipped to end of slice
+3731     # . . push args
+3732     68/push  "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32
+3733     68/push  0/imm32
+3734     # . . push ECX-EAX
+3735     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract EAX from ECX
+3736     51/push-ECX
+3737     # . . call
+3738     e8/call  check-ints-equal/disp32
+3739     # . . discard args
+3740     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3741     # . epilog
+3742     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3743     5d/pop-to-EBP
+3744     c3/return
+3745 
+3746 # assumes 'in' starts with optional '+' or '-', optional whitespace, and an unsigned integer
+3747 # returns the value of the integer
+3748 # side-effect: modifies 'in' to skip past the integer
+3749 next-hex-int:  # in : (address slice) -> result/EAX
+3750     # . prolog
+3751     55/push-EBP
+3752     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3753     # . save registers
+3754     51/push-ECX
+3755     52/push-EDX
+3756     53/push-EBX
+3757     56/push-ESI
+3758     57/push-EDI
+3759     # result/EDI = 0
+3760     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
+3761     # ESI = in
+3762     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+3763     # EDX = in->end
+3764     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
+3765     # curr/ECX = in->start
+3766     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+3767     # negate?/EBX = false
+3768     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+3769     # EAX = *curr
+3770     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+3771     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+3772 $next-hex-int:positive:
+3773     # if (*curr == '+') ++curr
+3774     3d/compare-EAX-and  0x2b/imm32/+
+3775     75/jump-if-not-equal  $next-hex-int:negative/disp8
+3776     # . ++curr
+3777     41/increment-ECX
+3778     eb/jump  $next-hex-int:skip-whitespace/disp8
+3779 $next-hex-int:negative:
+3780     # else if (*curr == '-') ++curr, negate = true
+3781     3d/compare-EAX-and  0x2d/imm32/-
+3782     75/jump-if-not-equal  $next-hex-int:skip-whitespace/disp8
+3783 $next-hex-int:need-to-negate:
+3784     # . ++curr
+3785     41/increment-ECX
+3786     # . negate = true
+3787     bb/copy-to-EBX  1/imm32/true
+3788     # fall through
+3789 $next-hex-int:skip-whitespace:
+3790     # spill EAX
+3791     50/push-EAX
+3792     # EAX = skip-chars-matching-whitespace-in-slice(word->start, word->end)
+3793     # . . push args
+3794     52/push-EDX
+3795     51/push-ECX
+3796     # . . call
+3797     e8/call  skip-chars-matching-whitespace-in-slice/disp32
+3798     # . . discard args
+3799     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3800     # ECX = EAX
+3801     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy EAX to ECX
+3802     # restore EAX
+3803     58/pop-to-EAX
+3804 $next-hex-int:initial-0:
+3805     # skip past leading '0x'
+3806     # . if (*curr != '0') jump to loop
+3807     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+3808     3d/compare-EAX-and  0x30/imm32/0
+3809     75/jump-if-not-equal  $next-hex-int:loop/disp8
+3810     # . ++curr
+3811     41/increment-ECX
+3812 $next-hex-int:initial-0x:
+3813     # . if (curr >= in->end) return result
+3814     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+3815     73/jump-if-greater-or-equal-unsigned  $next-hex-int:end/disp8
+3816     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
+3817     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+3818     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+3819     3d/compare-EAX-and  0x78/imm32/x
+3820     75/jump-if-not-equal  $next-hex-int:loop/disp8
+3821     # . ++curr
+3822     41/increment-ECX
+3823 $next-hex-int:loop:
+3824     # if (curr >= in->end) break
+3825     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+3826     73/jump-if-greater-or-equal-unsigned  $next-hex-int:break/disp8
+3827     # if (!is-hex-digit?(*curr)) break
+3828     # . EAX = *curr
+3829     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+3830     # . EAX = is-hex-digit?(*curr)
+3831     # . . push args
+3832     50/push-EAX
+3833     # . . call
+3834     e8/call  is-hex-digit?/disp32
+3835     # . . discard args
+3836     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3837     # . if (EAX == 0) break
+3838     3d/compare-EAX-and  0/imm32
+3839     74/jump-if-equal  $next-hex-int:break/disp8
+3840     # EAX = from-hex-char(*curr)
+3841     # . . copy arg to EAX
+3842     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+3843     # . . call
+3844     e8/call  from-hex-char/disp32
+3845     # result = result * 16 + EAX
+3846     c1/shift    4/subop/left        3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm8            # shift EDI left by 4 bits
+3847     01/add                          3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to EDI
+3848     # ++curr
+3849     41/increment-ECX
+3850     # loop
+3851     eb/jump  $next-hex-int:loop/disp8
+3852 $next-hex-int:break:
+3853     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
+3854     74/jump-if-equal  $next-hex-int:end/disp8
+3855 $next-hex-int:negate:
+3856     f7          3/subop/negate      3/mod/direct    7/rm32/EDI    .           .             .           .           .               .                 # negate EDI
+3857 $next-hex-int:end:
+3858     # word->start = curr
+3859     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy ECX to *ESI
+3860     # return EDI
+3861     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy EDI to EAX
+3862     # . restore registers
+3863     5f/pop-to-EDI
+3864     5e/pop-to-ESI
+3865     5b/pop-to-EBX
+3866     5a/pop-to-EDX
+3867     59/pop-to-ECX
+3868     # . epilog
+3869     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3870     5d/pop-to-EBP
+3871     c3/return
+3872 
+3873 $next-hex-int:abort:
+3874     # . _write(2/stderr, error)
+3875     # . . push args
+3876     68/push  "next-hex-int: invalid hex char: "/imm32
+3877     68/push  2/imm32/stderr
+3878     # . . call
+3879     e8/call  _write/disp32
+3880     # . . discard args
+3881     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3882     # . clear-stream(Stderr+4)
+3883     # . . save EAX
+3884     50/push-EAX
+3885     # . . push args
+3886     b8/copy-to-EAX  Stderr/imm32
 3887     05/add-to-EAX  4/imm32
-3888     # var slice/ECX = {EAX, ECX}
-3889     51/push-ECX
-3890     50/push-EAX
-3891     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3892     # EAX = next-hex-int(slice)
-3893     # . . push args
-3894     51/push-ECX
-3895     # . . call
-3896     e8/call  next-hex-int/disp32
-3897     # . . discard args
-3898     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3899     # check-ints-equal(EAX, 0, msg)
-3900     # . . push args
-3901     68/push  "F - test-next-hex-int-zero"/imm32
-3902     68/push  0/imm32
-3903     50/push-EAX
-3904     # . . call
-3905     e8/call  check-ints-equal/disp32
-3906     # . . discard args
-3907     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3908     # . epilog
-3909     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3910     5d/pop-to-EBP
-3911     c3/return
-3912 
-3913 test-next-hex-int-0-prefix:
-3914     # . prolog
-3915     55/push-EBP
-3916     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3917     # (EAX..ECX) = "+ 03)"
-3918     b8/copy-to-EAX  "+ 03)"/imm32
-3919     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3920     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
-3921     05/add-to-EAX  4/imm32
-3922     # var slice/ECX = {EAX, ECX}
-3923     51/push-ECX
-3924     50/push-EAX
-3925     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3926     # EAX = next-hex-int(slice)
-3927     # . . push args
-3928     51/push-ECX
-3929     # . . call
-3930     e8/call  next-hex-int/disp32
-3931     # . . discard args
-3932     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3933     # check-ints-equal(EAX, 3, msg)
-3934     # . . push args
-3935     68/push  "F - test-next-hex-int-0-prefix"/imm32
-3936     68/push  3/imm32
-3937     50/push-EAX
-3938     # . . call
-3939     e8/call  check-ints-equal/disp32
-3940     # . . discard args
-3941     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3942     # . epilog
-3943     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3944     5d/pop-to-EBP
-3945     c3/return
-3946 
-3947 test-next-hex-int-negative:
-3948     # . prolog
-3949     55/push-EBP
-3950     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3951     # (EAX..ECX) = "-03)"
-3952     b8/copy-to-EAX  "-03)"/imm32
-3953     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3954     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
-3955     05/add-to-EAX  4/imm32
-3956     # var slice/ECX = {EAX, ECX}
-3957     51/push-ECX
-3958     50/push-EAX
-3959     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3960     # EAX = next-hex-int(slice)
-3961     # . . push args
-3962     51/push-ECX
-3963     # . . call
-3964     e8/call  next-hex-int/disp32
-3965     # . . discard args
-3966     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-3967     # check-ints-equal(EAX, -3, msg)
-3968     # . . push args
-3969     68/push  "F - test-next-hex-int-negative"/imm32
-3970     68/push  -3/imm32
-3971     50/push-EAX
-3972     # . . call
-3973     e8/call  check-ints-equal/disp32
-3974     # . . discard args
-3975     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-3976     # . epilog
-3977     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-3978     5d/pop-to-EBP
-3979     c3/return
-3980 
-3981 test-next-hex-int-negative-with-space:
-3982     # . prolog
-3983     55/push-EBP
-3984     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-3985     # (EAX..ECX) = "- 03)"
-3986     b8/copy-to-EAX  "- 03)"/imm32
-3987     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-3988     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
-3989     05/add-to-EAX  4/imm32
-3990     # var slice/ECX = {EAX, ECX}
-3991     51/push-ECX
-3992     50/push-EAX
-3993     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-3994     # EAX = next-hex-int(slice)
-3995     # . . push args
-3996     51/push-ECX
-3997     # . . call
-3998     e8/call  next-hex-int/disp32
-3999     # . . discard args
-4000     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-4001     # check-ints-equal(EAX, -3, msg)
-4002     # . . push args
-4003     68/push  "F - test-next-hex-int-negative-with-space"/imm32
-4004     68/push  -3/imm32
-4005     50/push-EAX
-4006     # . . call
-4007     e8/call  check-ints-equal/disp32
-4008     # . . discard args
-4009     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-4010     # . epilog
-4011     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-4012     5d/pop-to-EBP
-4013     c3/return
-4014 
-4015 # assumes 'in' starts a positive unsigned integer
-4016 # returns the value of the integer
-4017 # side-effect: modifies 'in' to skip past the integer
-4018 next-positive-hex-int:  # in : (address slice) -> result/EAX
-4019     # . prolog
-4020     55/push-EBP
-4021     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-4022     # . save registers
-4023     51/push-ECX
-4024     52/push-EDX
-4025     53/push-EBX
-4026     56/push-ESI
-4027     57/push-EDI
-4028     # result/EDI = 0
-4029     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
-4030     # ESI = in
-4031     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
-4032     # EDX = in->end
-4033     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
-4034     # curr/ECX = in->start
-4035     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
-4036     # negate?/EBX = false
-4037     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-4038     # EAX = *curr
-4039     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-4040     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-4041 $next-positive-hex-int:initial-0:
-4042     # skip past leading '0x'
-4043     # . if (*curr != '0') jump to loop
-4044     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-4045     3d/compare-EAX-and  0x30/imm32/0
-4046     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
-4047     # . ++curr
-4048     41/increment-ECX
-4049 $next-positive-hex-int:initial-0x:
-4050     # . if (curr >= in->end) return result
-4051     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-4052     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
-4053     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
-4054     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
-4055     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-4056     3d/compare-EAX-and  0x78/imm32/x
-4057     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
-4058     # . ++curr
-4059     41/increment-ECX
-4060 $next-positive-hex-int:loop:
-4061     # if (curr >= in->end) break
-4062     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
-4063     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
-4064     # if (!is-hex-digit?(*curr)) break
-4065     # . EAX = *curr
-4066     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-4067     # . EAX = is-hex-digit?(*curr)
-4068     # . . push args
-4069     50/push-EAX
-4070     # . . call
-4071     e8/call  is-hex-digit?/disp32
-4072     # . . discard args
-4073     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-4074     # . if (EAX == 0) break
-4075     3d/compare-EAX-and  0/imm32
-4076     74/jump-if-equal  $next-positive-hex-int:end/disp8
-4077     # EAX = from-hex-char(*curr)
-4078     # . . copy arg to EAX
-4079     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
-4080     # . . call
-4081     e8/call  from-hex-char/disp32
-4082     # result = result * 16 + EAX
-4083     c1/shift    4/subop/left        3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm8            # shift EDI left by 4 bits
-4084     01/add                          3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to EDI
-4085     # ++curr
-4086     41/increment-ECX
-4087     # loop
-4088     eb/jump  $next-positive-hex-int:loop/disp8
-4089 $next-positive-hex-int:end:
-4090     # word->start = curr
-4091     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy ECX to *ESI
-4092     # return EDI
-4093     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy EDI to EAX
-4094     # . restore registers
-4095     5f/pop-to-EDI
-4096     5e/pop-to-ESI
-4097     5b/pop-to-EBX
-4098     5a/pop-to-EDX
-4099     59/pop-to-ECX
-4100     # . epilog
-4101     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-4102     5d/pop-to-EBP
-4103     c3/return
-4104 
-4105 test-next-positive-hex-int-single-digit:
-4106     # . prolog
-4107     55/push-EBP
-4108     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-4109     # (EAX..ECX) = "a)"
-4110     b8/copy-to-EAX  "a)"/imm32
-4111     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-4112     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
-4113     05/add-to-EAX  4/imm32
-4114     # var slice/ECX = {EAX, ECX}
-4115     51/push-ECX
-4116     50/push-EAX
-4117     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-4118     # EAX = next-positive-hex-int(slice)
-4119     # . . push args
-4120     51/push-ECX
-4121     # . . call
-4122     e8/call  next-positive-hex-int/disp32
-4123     # . . discard args
-4124     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-4125     # check-ints-equal(EAX, 0xa, msg)
-4126     # . . push args
-4127     68/push  "F - test-next-positive-hex-int-single-digit"/imm32
-4128     68/push  0xa/imm32
-4129     50/push-EAX
-4130     # . . call
-4131     e8/call  check-ints-equal/disp32
-4132     # . . discard args
-4133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-4134     # . epilog
-4135     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-4136     5d/pop-to-EBP
-4137     c3/return
-4138 
-4139 test-next-positive-hex-int-multi-digit:
-4140     # . prolog
-4141     55/push-EBP
-4142     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-4143     # (EAX..ECX) = "34a)"
-4144     b8/copy-to-EAX  "34a)"/imm32
-4145     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-4146     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
-4147     05/add-to-EAX  4/imm32
-4148     # var slice/ECX = {EAX, ECX}
-4149     51/push-ECX
-4150     50/push-EAX
-4151     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-4152     # EAX = next-positive-hex-int(slice)
-4153     # . . push args
-4154     51/push-ECX
-4155     # . . call
-4156     e8/call  next-positive-hex-int/disp32
-4157     # . . discard args
-4158     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-4159     # check-ints-equal(EAX, 0x34a, msg)
-4160     # . . push args
-4161     68/push  "F - test-next-positive-hex-int-multi-digit"/imm32
-4162     68/push  0x34a/imm32
-4163     50/push-EAX
-4164     # . . call
-4165     e8/call  check-ints-equal/disp32
-4166     # . . discard args
-4167     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-4168     # . epilog
-4169     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-4170     5d/pop-to-EBP
-4171     c3/return
-4172 
-4173 test-next-positive-hex-int-0x-prefix:
-4174     # . prolog
-4175     55/push-EBP
-4176     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-4177     # (EAX..ECX) = "0x34)"
-4178     b8/copy-to-EAX  "0x34)"/imm32
-4179     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-4180     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
-4181     05/add-to-EAX  4/imm32
-4182     # var slice/ECX = {EAX, ECX}
-4183     51/push-ECX
-4184     50/push-EAX
-4185     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-4186     # EAX = next-positive-hex-int(slice)
-4187     # . . push args
-4188     51/push-ECX
-4189     # . . call
-4190     e8/call  next-positive-hex-int/disp32
-4191     # . . discard args
-4192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-4193     # check-ints-equal(EAX, 0x34, msg)
-4194     # . . push args
-4195     68/push  "F - test-next-positive-hex-int-0x-prefix"/imm32
-4196     68/push  0x34/imm32
-4197     50/push-EAX
-4198     # . . call
-4199     e8/call  check-ints-equal/disp32
-4200     # . . discard args
-4201     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-4202     # . epilog
-4203     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-4204     5d/pop-to-EBP
-4205     c3/return
-4206 
-4207 test-next-positive-hex-int-zero:
-4208     # . prolog
-4209     55/push-EBP
-4210     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-4211     # (EAX..ECX) = "0"
-4212     b8/copy-to-EAX  "0"/imm32
-4213     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-4214     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
-4215     05/add-to-EAX  4/imm32
-4216     # var slice/ECX = {EAX, ECX}
-4217     51/push-ECX
-4218     50/push-EAX
-4219     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-4220     # EAX = next-positive-hex-int(slice)
-4221     # . . push args
-4222     51/push-ECX
-4223     # . . call
-4224     e8/call  next-positive-hex-int/disp32
-4225     # . . discard args
-4226     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-4227     # check-ints-equal(EAX, 0, msg)
-4228     # . . push args
-4229     68/push  "F - test-next-positive-hex-int-zero"/imm32
-4230     68/push  0/imm32
-4231     50/push-EAX
-4232     # . . call
-4233     e8/call  check-ints-equal/disp32
-4234     # . . discard args
-4235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-4236     # . epilog
-4237     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-4238     5d/pop-to-EBP
-4239     c3/return
-4240 
-4241 test-next-positive-hex-int-0-prefix:
-4242     # . prolog
-4243     55/push-EBP
-4244     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
-4245     # (EAX..ECX) = "03)"
-4246     b8/copy-to-EAX  "03)"/imm32
-4247     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
-4248     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
-4249     05/add-to-EAX  4/imm32
-4250     # var slice/ECX = {EAX, ECX}
-4251     51/push-ECX
-4252     50/push-EAX
-4253     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-4254     # EAX = next-positive-hex-int(slice)
-4255     # . . push args
-4256     51/push-ECX
-4257     # . . call
-4258     e8/call  next-positive-hex-int/disp32
-4259     # . . discard args
-4260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-4261     # check-ints-equal(EAX, 3, msg)
-4262     # . . push args
-4263     68/push  "F - test-next-positive-hex-int-0-prefix"/imm32
-4264     68/push  3/imm32
-4265     50/push-EAX
-4266     # . . call
-4267     e8/call  check-ints-equal/disp32
-4268     # . . discard args
-4269     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-4270     # . epilog
-4271     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-4272     5d/pop-to-EBP
-4273     c3/return
-4274 
-4275 == data
-4276 Registers:  # (table string int)
-4277   # a table is a stream
-4278   0x40/imm32/write
-4279   0/imm32/read
-4280   0x40/imm32/length
-4281   # data
-4282   "eax"/imm32  0/imm32
-4283   "ecx"/imm32  1/imm32
-4284   "edx"/imm32  2/imm32
-4285   "ebx"/imm32  3/imm32
-4286   "esp"/imm32  4/imm32
-4287   "ebp"/imm32  5/imm32
-4288   "esi"/imm32  6/imm32
-4289   "edi"/imm32  7/imm32
-4290 
-4291 # . . vim:nowrap:textwidth=0
+3888     50/push-EAX
+3889     # . . call
+3890     e8/call  clear-stream/disp32
+3891     # . . discard args
+3892     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3893     # . . restore EAX
+3894     58/pop-to-EAX
+3895     # . print-int32-buffered(Stderr, EAX)
+3896     # . . push args
+3897     50/push-EAX
+3898     68/push  Stderr/imm32
+3899     # . . call
+3900     e8/call  print-int32-buffered/disp32
+3901     # . . discard args
+3902     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3903     # . flush(Stderr)
+3904     # . . push args
+3905     68/push  Stderr/imm32
+3906     # . . call
+3907     e8/call  flush/disp32
+3908     # . . discard args
+3909     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3910     # . _write(2/stderr, "\n")
+3911     # . . push args
+3912     68/push  "\n"/imm32
+3913     68/push  2/imm32/stderr
+3914     # . . call
+3915     e8/call  _write/disp32
+3916     # . . discard args
+3917     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+3918     # . syscall(exit, 1)
+3919     bb/copy-to-EBX  1/imm32
+3920     b8/copy-to-EAX  1/imm32/exit
+3921     cd/syscall  0x80/imm8
+3922     # never gets here
+3923 
+3924 test-next-hex-int-single-digit:
+3925     # . prolog
+3926     55/push-EBP
+3927     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3928     # (EAX..ECX) = "+a)"
+3929     b8/copy-to-EAX  "+a)"/imm32
+3930     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3931     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
+3932     05/add-to-EAX  4/imm32
+3933     # var slice/ECX = {EAX, ECX}
+3934     51/push-ECX
+3935     50/push-EAX
+3936     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+3937     # EAX = next-hex-int(slice)
+3938     # . . push args
+3939     51/push-ECX
+3940     # . . call
+3941     e8/call  next-hex-int/disp32
+3942     # . . discard args
+3943     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3944     # check-ints-equal(EAX, 0xa, msg)
+3945     # . . push args
+3946     68/push  "F - test-next-hex-int-single-digit"/imm32
+3947     68/push  0xa/imm32
+3948     50/push-EAX
+3949     # . . call
+3950     e8/call  check-ints-equal/disp32
+3951     # . . discard args
+3952     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3953     # . epilog
+3954     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3955     5d/pop-to-EBP
+3956     c3/return
+3957 
+3958 test-next-hex-int-multi-digit:
+3959     # . prolog
+3960     55/push-EBP
+3961     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3962     # (EAX..ECX) = "+ 34a)"
+3963     b8/copy-to-EAX  "+ 34a)"/imm32
+3964     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3965     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
+3966     05/add-to-EAX  4/imm32
+3967     # var slice/ECX = {EAX, ECX}
+3968     51/push-ECX
+3969     50/push-EAX
+3970     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+3971     # EAX = next-hex-int(slice)
+3972     # . . push args
+3973     51/push-ECX
+3974     # . . call
+3975     e8/call  next-hex-int/disp32
+3976     # . . discard args
+3977     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+3978     # check-ints-equal(EAX, 0x34a, msg)
+3979     # . . push args
+3980     68/push  "F - test-next-hex-int-multi-digit"/imm32
+3981     68/push  0x34a/imm32
+3982     50/push-EAX
+3983     # . . call
+3984     e8/call  check-ints-equal/disp32
+3985     # . . discard args
+3986     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+3987     # . epilog
+3988     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+3989     5d/pop-to-EBP
+3990     c3/return
+3991 
+3992 test-next-hex-int-0x-prefix:
+3993     # . prolog
+3994     55/push-EBP
+3995     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+3996     # (EAX..ECX) = "+0x34)"
+3997     b8/copy-to-EAX  "+0x34)"/imm32
+3998     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+3999     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
+4000     05/add-to-EAX  4/imm32
+4001     # var slice/ECX = {EAX, ECX}
+4002     51/push-ECX
+4003     50/push-EAX
+4004     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4005     # EAX = next-hex-int(slice)
+4006     # . . push args
+4007     51/push-ECX
+4008     # . . call
+4009     e8/call  next-hex-int/disp32
+4010     # . . discard args
+4011     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4012     # check-ints-equal(EAX, 0x34, msg)
+4013     # . . push args
+4014     68/push  "F - test-next-hex-int-0x-prefix"/imm32
+4015     68/push  0x34/imm32
+4016     50/push-EAX
+4017     # . . call
+4018     e8/call  check-ints-equal/disp32
+4019     # . . discard args
+4020     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4021     # . epilog
+4022     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4023     5d/pop-to-EBP
+4024     c3/return
+4025 
+4026 test-next-hex-int-zero:
+4027     # . prolog
+4028     55/push-EBP
+4029     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4030     # (EAX..ECX) = "+0)"
+4031     b8/copy-to-EAX  "+0)"/imm32
+4032     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4033     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
+4034     05/add-to-EAX  4/imm32
+4035     # var slice/ECX = {EAX, ECX}
+4036     51/push-ECX
+4037     50/push-EAX
+4038     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4039     # EAX = next-hex-int(slice)
+4040     # . . push args
+4041     51/push-ECX
+4042     # . . call
+4043     e8/call  next-hex-int/disp32
+4044     # . . discard args
+4045     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4046     # check-ints-equal(EAX, 0, msg)
+4047     # . . push args
+4048     68/push  "F - test-next-hex-int-zero"/imm32
+4049     68/push  0/imm32
+4050     50/push-EAX
+4051     # . . call
+4052     e8/call  check-ints-equal/disp32
+4053     # . . discard args
+4054     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4055     # . epilog
+4056     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4057     5d/pop-to-EBP
+4058     c3/return
+4059 
+4060 test-next-hex-int-0-prefix:
+4061     # . prolog
+4062     55/push-EBP
+4063     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4064     # (EAX..ECX) = "+ 03)"
+4065     b8/copy-to-EAX  "+ 03)"/imm32
+4066     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4067     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
+4068     05/add-to-EAX  4/imm32
+4069     # var slice/ECX = {EAX, ECX}
+4070     51/push-ECX
+4071     50/push-EAX
+4072     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4073     # EAX = next-hex-int(slice)
+4074     # . . push args
+4075     51/push-ECX
+4076     # . . call
+4077     e8/call  next-hex-int/disp32
+4078     # . . discard args
+4079     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4080     # check-ints-equal(EAX, 3, msg)
+4081     # . . push args
+4082     68/push  "F - test-next-hex-int-0-prefix"/imm32
+4083     68/push  3/imm32
+4084     50/push-EAX
+4085     # . . call
+4086     e8/call  check-ints-equal/disp32
+4087     # . . discard args
+4088     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4089     # . epilog
+4090     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4091     5d/pop-to-EBP
+4092     c3/return
+4093 
+4094 test-next-hex-int-negative:
+4095     # . prolog
+4096     55/push-EBP
+4097     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4098     # (EAX..ECX) = "-03)"
+4099     b8/copy-to-EAX  "-03)"/imm32
+4100     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4101     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
+4102     05/add-to-EAX  4/imm32
+4103     # var slice/ECX = {EAX, ECX}
+4104     51/push-ECX
+4105     50/push-EAX
+4106     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4107     # EAX = next-hex-int(slice)
+4108     # . . push args
+4109     51/push-ECX
+4110     # . . call
+4111     e8/call  next-hex-int/disp32
+4112     # . . discard args
+4113     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4114     # check-ints-equal(EAX, -3, msg)
+4115     # . . push args
+4116     68/push  "F - test-next-hex-int-negative"/imm32
+4117     68/push  -3/imm32
+4118     50/push-EAX
+4119     # . . call
+4120     e8/call  check-ints-equal/disp32
+4121     # . . discard args
+4122     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4123     # . epilog
+4124     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4125     5d/pop-to-EBP
+4126     c3/return
+4127 
+4128 test-next-hex-int-negative-with-space:
+4129     # . prolog
+4130     55/push-EBP
+4131     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4132     # (EAX..ECX) = "- 03)"
+4133     b8/copy-to-EAX  "- 03)"/imm32
+4134     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4135     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
+4136     05/add-to-EAX  4/imm32
+4137     # var slice/ECX = {EAX, ECX}
+4138     51/push-ECX
+4139     50/push-EAX
+4140     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4141     # EAX = next-hex-int(slice)
+4142     # . . push args
+4143     51/push-ECX
+4144     # . . call
+4145     e8/call  next-hex-int/disp32
+4146     # . . discard args
+4147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4148     # check-ints-equal(EAX, -3, msg)
+4149     # . . push args
+4150     68/push  "F - test-next-hex-int-negative-with-space"/imm32
+4151     68/push  -3/imm32
+4152     50/push-EAX
+4153     # . . call
+4154     e8/call  check-ints-equal/disp32
+4155     # . . discard args
+4156     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4157     # . epilog
+4158     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4159     5d/pop-to-EBP
+4160     c3/return
+4161 
+4162 # assumes 'in' starts a positive unsigned integer
+4163 # returns the value of the integer
+4164 # side-effect: modifies 'in' to skip past the integer
+4165 next-positive-hex-int:  # in : (address slice) -> result/EAX
+4166     # . prolog
+4167     55/push-EBP
+4168     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4169     # . save registers
+4170     51/push-ECX
+4171     52/push-EDX
+4172     53/push-EBX
+4173     56/push-ESI
+4174     57/push-EDI
+4175     # result/EDI = 0
+4176     31/xor                          3/mod/direct    7/rm32/EDI    .           .             .           7/r32/EDI   .               .                 # clear EDI
+4177     # ESI = in
+4178     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
+4179     # EDX = in->end
+4180     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ESI+4) to EDX
+4181     # curr/ECX = in->start
+4182     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy *ESI to ECX
+4183     # negate?/EBX = false
+4184     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+4185     # EAX = *curr
+4186     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+4187     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+4188 $next-positive-hex-int:initial-0:
+4189     # skip past leading '0x'
+4190     # . if (*curr != '0') jump to loop
+4191     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+4192     3d/compare-EAX-and  0x30/imm32/0
+4193     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
+4194     # . ++curr
+4195     41/increment-ECX
+4196 $next-positive-hex-int:initial-0x:
+4197     # . if (curr >= in->end) return result
+4198     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+4199     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
+4200     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
+4201     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
+4202     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+4203     3d/compare-EAX-and  0x78/imm32/x
+4204     75/jump-if-not-equal  $next-positive-hex-int:loop/disp8
+4205     # . ++curr
+4206     41/increment-ECX
+4207 $next-positive-hex-int:loop:
+4208     # if (curr >= in->end) break
+4209     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
+4210     73/jump-if-greater-or-equal-unsigned  $next-positive-hex-int:end/disp8
+4211     # if (!is-hex-digit?(*curr)) break
+4212     # . EAX = *curr
+4213     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+4214     # . EAX = is-hex-digit?(*curr)
+4215     # . . push args
+4216     50/push-EAX
+4217     # . . call
+4218     e8/call  is-hex-digit?/disp32
+4219     # . . discard args
+4220     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4221     # . if (EAX == 0) break
+4222     3d/compare-EAX-and  0/imm32
+4223     74/jump-if-equal  $next-positive-hex-int:end/disp8
+4224     # EAX = from-hex-char(*curr)
+4225     # . . copy arg to EAX
+4226     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
+4227     # . . call
+4228     e8/call  from-hex-char/disp32
+4229     # result = result * 16 + EAX
+4230     c1/shift    4/subop/left        3/mod/direct    7/rm32/EDI    .           .             .           .           .               4/imm8            # shift EDI left by 4 bits
+4231     01/add                          3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # add EAX to EDI
+4232     # ++curr
+4233     41/increment-ECX
+4234     # loop
+4235     eb/jump  $next-positive-hex-int:loop/disp8
+4236 $next-positive-hex-int:end:
+4237     # word->start = curr
+4238     89/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           1/r32/ECX   .               .                 # copy ECX to *ESI
+4239     # return EDI
+4240     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           7/r32/EDI   .               .                 # copy EDI to EAX
+4241     # . restore registers
+4242     5f/pop-to-EDI
+4243     5e/pop-to-ESI
+4244     5b/pop-to-EBX
+4245     5a/pop-to-EDX
+4246     59/pop-to-ECX
+4247     # . epilog
+4248     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4249     5d/pop-to-EBP
+4250     c3/return
+4251 
+4252 test-next-positive-hex-int-single-digit:
+4253     # . prolog
+4254     55/push-EBP
+4255     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4256     # (EAX..ECX) = "a)"
+4257     b8/copy-to-EAX  "a)"/imm32
+4258     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4259     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
+4260     05/add-to-EAX  4/imm32
+4261     # var slice/ECX = {EAX, ECX}
+4262     51/push-ECX
+4263     50/push-EAX
+4264     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4265     # EAX = next-positive-hex-int(slice)
+4266     # . . push args
+4267     51/push-ECX
+4268     # . . call
+4269     e8/call  next-positive-hex-int/disp32
+4270     # . . discard args
+4271     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4272     # check-ints-equal(EAX, 0xa, msg)
+4273     # . . push args
+4274     68/push  "F - test-next-positive-hex-int-single-digit"/imm32
+4275     68/push  0xa/imm32
+4276     50/push-EAX
+4277     # . . call
+4278     e8/call  check-ints-equal/disp32
+4279     # . . discard args
+4280     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4281     # . epilog
+4282     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4283     5d/pop-to-EBP
+4284     c3/return
+4285 
+4286 test-next-positive-hex-int-multi-digit:
+4287     # . prolog
+4288     55/push-EBP
+4289     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4290     # (EAX..ECX) = "34a)"
+4291     b8/copy-to-EAX  "34a)"/imm32
+4292     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4293     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
+4294     05/add-to-EAX  4/imm32
+4295     # var slice/ECX = {EAX, ECX}
+4296     51/push-ECX
+4297     50/push-EAX
+4298     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4299     # EAX = next-positive-hex-int(slice)
+4300     # . . push args
+4301     51/push-ECX
+4302     # . . call
+4303     e8/call  next-positive-hex-int/disp32
+4304     # . . discard args
+4305     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4306     # check-ints-equal(EAX, 0x34a, msg)
+4307     # . . push args
+4308     68/push  "F - test-next-positive-hex-int-multi-digit"/imm32
+4309     68/push  0x34a/imm32
+4310     50/push-EAX
+4311     # . . call
+4312     e8/call  check-ints-equal/disp32
+4313     # . . discard args
+4314     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4315     # . epilog
+4316     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4317     5d/pop-to-EBP
+4318     c3/return
+4319 
+4320 test-next-positive-hex-int-0x-prefix:
+4321     # . prolog
+4322     55/push-EBP
+4323     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4324     # (EAX..ECX) = "0x34)"
+4325     b8/copy-to-EAX  "0x34)"/imm32
+4326     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4327     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
+4328     05/add-to-EAX  4/imm32
+4329     # var slice/ECX = {EAX, ECX}
+4330     51/push-ECX
+4331     50/push-EAX
+4332     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4333     # EAX = next-positive-hex-int(slice)
+4334     # . . push args
+4335     51/push-ECX
+4336     # . . call
+4337     e8/call  next-positive-hex-int/disp32
+4338     # . . discard args
+4339     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4340     # check-ints-equal(EAX, 0x34, msg)
+4341     # . . push args
+4342     68/push  "F - test-next-positive-hex-int-0x-prefix"/imm32
+4343     68/push  0x34/imm32
+4344     50/push-EAX
+4345     # . . call
+4346     e8/call  check-ints-equal/disp32
+4347     # . . discard args
+4348     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4349     # . epilog
+4350     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4351     5d/pop-to-EBP
+4352     c3/return
+4353 
+4354 test-next-positive-hex-int-zero:
+4355     # . prolog
+4356     55/push-EBP
+4357     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4358     # (EAX..ECX) = "0"
+4359     b8/copy-to-EAX  "0"/imm32
+4360     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4361     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
+4362     05/add-to-EAX  4/imm32
+4363     # var slice/ECX = {EAX, ECX}
+4364     51/push-ECX
+4365     50/push-EAX
+4366     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4367     # EAX = next-positive-hex-int(slice)
+4368     # . . push args
+4369     51/push-ECX
+4370     # . . call
+4371     e8/call  next-positive-hex-int/disp32
+4372     # . . discard args
+4373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4374     # check-ints-equal(EAX, 0, msg)
+4375     # . . push args
+4376     68/push  "F - test-next-positive-hex-int-zero"/imm32
+4377     68/push  0/imm32
+4378     50/push-EAX
+4379     # . . call
+4380     e8/call  check-ints-equal/disp32
+4381     # . . discard args
+4382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4383     # . epilog
+4384     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4385     5d/pop-to-EBP
+4386     c3/return
+4387 
+4388 test-next-positive-hex-int-0-prefix:
+4389     # . prolog
+4390     55/push-EBP
+4391     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+4392     # (EAX..ECX) = "03)"
+4393     b8/copy-to-EAX  "03)"/imm32
+4394     8b/copy                         0/mod/indirect  0/rm32/EAX    .           .             .           1/r32/ECX   .               .                 # copy *EAX to ECX
+4395     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
+4396     05/add-to-EAX  4/imm32
+4397     # var slice/ECX = {EAX, ECX}
+4398     51/push-ECX
+4399     50/push-EAX
+4400     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+4401     # EAX = next-positive-hex-int(slice)
+4402     # . . push args
+4403     51/push-ECX
+4404     # . . call
+4405     e8/call  next-positive-hex-int/disp32
+4406     # . . discard args
+4407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+4408     # check-ints-equal(EAX, 3, msg)
+4409     # . . push args
+4410     68/push  "F - test-next-positive-hex-int-0-prefix"/imm32
+4411     68/push  3/imm32
+4412     50/push-EAX
+4413     # . . call
+4414     e8/call  check-ints-equal/disp32
+4415     # . . discard args
+4416     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+4417     # . epilog
+4418     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+4419     5d/pop-to-EBP
+4420     c3/return
+4421 
+4422 == data
+4423 Registers:  # (table string int)
+4424   # a table is a stream
+4425   0x40/imm32/write
+4426   0/imm32/read
+4427   0x40/imm32/length
+4428   # data
+4429   "eax"/imm32  0/imm32
+4430   "ecx"/imm32  1/imm32
+4431   "edx"/imm32  2/imm32
+4432   "ebx"/imm32  3/imm32
+4433   "esp"/imm32  4/imm32
+4434   "ebp"/imm32  5/imm32
+4435   "esi"/imm32  6/imm32
+4436   "edi"/imm32  7/imm32
+4437 
+4438 # . . vim:nowrap:textwidth=0
 
diff --git a/html/apps/dquotes.subx.html b/html/apps/dquotes.subx.html index ecffb4d6..6e9d009a 100644 --- a/html/apps/dquotes.subx.html +++ b/html/apps/dquotes.subx.html @@ -65,7 +65,7 @@ if ('onhashchange' in window) { 2 # Replace them with references to new variables in the data segment. 3 # 4 # To run: - 5 # $ ./subx translate *.subx apps/dquotes.subx -o apps/dquotes + 5 # $ ./subx translate 0*.subx apps/subx-common.subx apps/dquotes.subx -o apps/dquotes 6 # $ cat x 7 # == code 8 # ab "cd ef"/imm32 @@ -369,7 +369,7 @@ if ('onhashchange' in window) { 306 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 307 # . print-int32-decimal(out-segment, *Next-string-literal) 308 # . . push args - 309 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal + 309 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal 310 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) 311 # . . call 312 e8/call print-int32-decimal/disp32 @@ -410,7 +410,7 @@ if ('onhashchange' in window) { 347 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 348 # . print-int32-decimal(int32-stream, *Next-string-literal) 349 # . . push args - 350 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal + 350 ff 6/subop/push 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # push *Next-string-literal 351 51/push-ECX 352 # . . call 353 e8/call print-int32-decimal/disp32 @@ -433,7 +433,7 @@ if ('onhashchange' in window) { 370 # . . discard args 371 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 372 # ++ *Next-string-literal - 373 ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # increment *Num-test-failures + 373 ff 0/subop/increment 0/mod/indirect 5/rm32/.disp32 . . . Next-string-literal/disp32 # increment *Num-test-failures 374 $process-string-literal:end: 375 # . reclaim locals 376 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x16/imm32 # add to ESP @@ -892,7 +892,7 @@ if ('onhashchange' in window) { 886 56/push-ESI 887 52/push-EDX 888 # . . call - 889 e8/call string-length-at-start-of-slice/disp32 + 889 e8/call string-length-at-start-of-slice/disp32 890 # . . discard args 891 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 892 # print(out, "#{len}/imm32 ") @@ -962,7 +962,7 @@ if ('onhashchange' in window) { 956 74/jump-if-equal $emit-string-literal-data:char-done/disp8 957 # . write(out, "/") 958 # . . push args - 959 68/push Slash/imm32 + 959 68/push Slash/imm32 960 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 961 # . . call 962 e8/call write/disp32 @@ -979,7 +979,7 @@ if ('onhashchange' in window) { 973 $emit-string-literal-data:char-done: 974 # write(out, " ") 975 # . . push args - 976 68/push Space/imm32 + 976 68/push Space/imm32 977 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 978 # . . call 979 e8/call write/disp32 @@ -1048,8 +1048,8 @@ if ('onhashchange' in window) { 1042 # . . discard args 1043 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1044 # var slice/ECX = '"abc"/d' -1045 68/push _test-slice-abc-limit/imm32 -1046 68/push _test-slice-abc/imm32 +1045 68/push _test-slice-abc-limit/imm32 +1046 68/push _test-slice-abc/imm32 1047 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX 1048 # emit-string-literal-data(_test-output-stream, slice) 1049 # . . push args @@ -1127,8 +1127,8 @@ if ('onhashchange' in window) { 1171 # . . discard args 1172 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1173 # var slice/ECX = '"a b"' -1174 68/push _test-slice-a-space-b-limit/imm32 -1175 68/push _test-slice-a-space-b/imm32 +1174 68/push _test-slice-a-space-b-limit/imm32 +1175 68/push _test-slice-a-space-b/imm32 1176 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX 1177 # emit-string-literal-data(_test-output-stream, slice) 1178 # . . push args @@ -1166,8 +1166,8 @@ if ('onhashchange' in window) { 1235 # . . discard args 1236 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1237 # var slice/ECX = '"a\"b"' -1238 68/push _test-slice-a-dquote-b-limit/imm32 -1239 68/push _test-slice-a-dquote-b/imm32 +1238 68/push _test-slice-a-dquote-b-limit/imm32 +1239 68/push _test-slice-a-dquote-b/imm32 1240 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX 1241 # emit-string-literal-data(_test-output-stream, slice) 1242 # . . push args @@ -1205,8 +1205,8 @@ if ('onhashchange' in window) { 1299 # . . discard args 1300 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1301 # var slice/ECX = '"a\nb"' -1302 68/push _test-slice-a-newline-b-limit/imm32 -1303 68/push _test-slice-a-newline-b/imm32 +1302 68/push _test-slice-a-newline-b-limit/imm32 +1303 68/push _test-slice-a-newline-b/imm32 1304 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX 1305 # emit-string-literal-data(_test-output-stream, slice) 1306 # . . push args @@ -1280,7 +1280,7 @@ if ('onhashchange' in window) { 1399 52/push-EDX 1400 51/push-ECX 1401 # . . call -1402 e8/call skip-string-in-slice/disp32 +1402 e8/call skip-string-in-slice/disp32 1403 # . . discard args 1404 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 1405 # . curr = EAX @@ -1583,8 +1583,8 @@ if ('onhashchange' in window) { 1702 # . . discard args 1703 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1704 # var slice/ECX = "\"abc/def\"/ghi" -1705 68/push _test-slice-literal-string-with-limit/imm32 -1706 68/push _test-slice-literal-string/imm32/start +1705 68/push _test-slice-literal-string-with-limit/imm32 +1706 68/push _test-slice-literal-string/imm32/start 1707 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX 1708 # emit-metadata(_test-output-buffered-file, slice) 1709 # . . push args @@ -1685,7 +1685,7 @@ if ('onhashchange' in window) { 1829 # . . push args 1830 56/push-ESI 1831 # . . call -1832 e8/call skip-string/disp32 +1832 e8/call skip-string/disp32 1833 # . . discard args 1834 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP 1835 # fall through @@ -1858,7 +1858,7 @@ if ('onhashchange' in window) { 2002 5d/pop-to-EBP 2003 c3/return 2004 -2005 test-next-word-or-string-returns-empty-string-on-eof: +2005 test-next-word-or-string-returns-empty-slice-on-eof: 2006 # . prolog 2007 55/push-EBP 2008 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1900,7 +1900,7 @@ if ('onhashchange' in window) { 2044 5d/pop-to-EBP 2045 c3/return 2046 -2047 test-next-word-or-string-returns-whole-string: +2047 test-next-word-or-string-returns-string-literal: 2048 # . prolog 2049 55/push-EBP 2050 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -1935,7 +1935,7 @@ if ('onhashchange' in window) { 2079 # check-ints-equal(slice->start - _test-input-stream->data, 1, msg) 2080 # . check-ints-equal(slice->start - _test-input-stream, 13, msg) 2081 # . . push args -2082 68/push "F - test-next-word-or-string-returns-whole-string: start"/imm32 +2082 68/push "F - test-next-word-or-string-returns-string-literal: start"/imm32 2083 68/push 0xd/imm32 2084 # . . push slice->start - _test-input-stream 2085 8b/copy 0/mod/indirect 1/rm32/ECX . . . 0/r32/EAX . . # copy *ECX to EAX @@ -1948,7 +1948,7 @@ if ('onhashchange' in window) { 2092 # check-ints-equal(slice->end - _test-input-stream->data, 12, msg) 2093 # . check-ints-equal(slice->end - _test-input-stream, 24, msg) 2094 # . . push args -2095 68/push "F - test-next-word-or-string-returns-whole-string: end"/imm32 +2095 68/push "F - test-next-word-or-string-returns-string-literal: end"/imm32 2096 68/push 0x18/imm32 2097 # . . push slice->end - _test-input-stream 2098 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/EAX 4/disp8 . # copy *(ECX+4) to EAX @@ -2026,592 +2026,165 @@ if ('onhashchange' in window) { 2170 5d/pop-to-EBP 2171 c3/return 2172 -2173 # update line->read to end of string literal surrounded by double quotes -2174 # line->read must start out at a double-quote -2175 skip-string: # line : (address stream) -2176 # . prolog -2177 55/push-EBP -2178 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2179 # . save registers -2180 50/push-EAX -2181 51/push-ECX -2182 52/push-EDX -2183 # ECX = line -2184 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -2185 # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write]) -2186 # . . push &line->data[line->write] -2187 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 8/disp8 . # copy *(ECX+8) to EDX -2188 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX -2189 52/push-EDX -2190 # . . push &line->data[line->read] -2191 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX -2192 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX -2193 52/push-EDX -2194 # . . call -2195 e8/call skip-string-in-slice/disp32 -2196 # . . discard args -2197 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2198 # line->read = EAX - line->data -2199 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX -2200 2d/subtract-from-EAX 0xc/imm32 -2201 89/copy 1/mod/*+disp8 1/rm32/ECX . . 0/r32/EAX 4/disp8 . # copy EAX to *(ECX+4) -2202 $skip-string:end: -2203 # . restore registers -2204 5a/pop-to-EDX -2205 59/pop-to-ECX -2206 58/pop-to-EAX -2207 # . epilog -2208 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2209 5d/pop-to-EBP -2210 c3/return -2211 -2212 test-skip-string: -2213 # . prolog -2214 55/push-EBP -2215 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2216 # setup -2217 # . clear-stream(_test-input-stream) -2218 # . . push args -2219 68/push _test-input-stream/imm32 -2220 # . . call -2221 e8/call clear-stream/disp32 -2222 # . . discard args -2223 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2224 # . write(_test-input-stream, "\"abc\" def") -2225 # . indices: 0123 45 -2226 # . . push args -2227 68/push "\"abc\" def"/imm32 -2228 68/push _test-input-stream/imm32 -2229 # . . call -2230 e8/call write/disp32 -2231 # . . discard args -2232 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2233 # precondition: line->read == 0 +2173 string-length-at-start-of-slice: # curr : (address byte), end : (address byte) -> length/EAX +2174 # . prolog +2175 55/push-EBP +2176 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2177 # . save registers +2178 51/push-ECX +2179 52/push-EDX +2180 53/push-EBX +2181 # ECX = curr +2182 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX +2183 # EDX = end +2184 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX +2185 # length/EAX = 0 +2186 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2187 # EBX = 0 +2188 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX +2189 # skip initial dquote +2190 41/increment-ECX +2191 $string-length-at-start-of-slice:loop: +2192 # if (curr >= end) return length +2193 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX +2194 73/jump-if-greater-unsigned-or-equal $string-length-at-start-of-slice:end/disp8 +2195 # BL = *curr +2196 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL +2197 $string-length-at-start-of-slice:dquote: +2198 # if (EBX == '"') break +2199 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x22/imm32/dquote # compare EBX +2200 74/jump-if-equal $string-length-at-start-of-slice:end/disp8 +2201 $string-length-at-start-of-slice:check-for-escape: +2202 # if (EBX == '\') escape next char +2203 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x5c/imm32/backslash # compare EBX +2204 75/jump-if-not-equal $string-length-at-start-of-slice:continue/disp8 +2205 $string-length-at-start-of-slice:escape: +2206 # increment curr but not result +2207 41/increment-ECX +2208 $string-length-at-start-of-slice:continue: +2209 # ++result +2210 40/increment-EAX +2211 # ++curr +2212 41/increment-ECX +2213 eb/jump $string-length-at-start-of-slice:loop/disp8 +2214 $string-length-at-start-of-slice:end: +2215 # . restore registers +2216 5b/pop-to-EBX +2217 5a/pop-to-EDX +2218 59/pop-to-ECX +2219 # . epilog +2220 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2221 5d/pop-to-EBP +2222 c3/return +2223 +2224 test-string-length-at-start-of-slice: +2225 # . prolog +2226 55/push-EBP +2227 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2228 # setup: (EAX..ECX) = "\"abc\" def" +2229 b8/copy-to-EAX "\"abc\" def"/imm32 +2230 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2231 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 +2232 05/add-to-EAX 4/imm32 +2233 # EAX = string-length-at-start-of-slice(EAX, ECX) 2234 # . . push args -2235 68/push "F - test-skip-string/precondition"/imm32 -2236 68/push 0/imm32 -2237 b8/copy-to-EAX _test-input-stream/imm32 -2238 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -2239 # . . call -2240 e8/call check-ints-equal/disp32 -2241 # . . discard args -2242 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2243 # skip-string(_test-input-stream) -2244 # . . push args -2245 68/push _test-input-stream/imm32 +2235 51/push-ECX +2236 50/push-EAX +2237 # . . call +2238 e8/call string-length-at-start-of-slice/disp32 +2239 # . . discard args +2240 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2241 # check-ints-equal(EAX, 3, msg) +2242 # . . push args +2243 68/push "F - test-string-length-at-start-of-slice"/imm32 +2244 68/push 3/imm32 +2245 50/push-EAX 2246 # . . call -2247 e8/call skip-string/disp32 +2247 e8/call check-ints-equal/disp32 2248 # . . discard args -2249 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2250 # check-ints-equal(line->read, 5, msg) -2251 # . . push args -2252 68/push "F - test-skip-string"/imm32 -2253 68/push 5/imm32 -2254 b8/copy-to-EAX _test-input-stream/imm32 -2255 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -2256 # . . call -2257 e8/call check-ints-equal/disp32 -2258 # . . discard args -2259 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2260 # . epilog -2261 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2262 5d/pop-to-EBP -2263 c3/return -2264 -2265 test-skip-string-ignores-spaces: -2266 # . prolog -2267 55/push-EBP -2268 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2269 # setup -2270 # . clear-stream(_test-input-stream) -2271 # . . push args -2272 68/push _test-input-stream/imm32 -2273 # . . call -2274 e8/call clear-stream/disp32 -2275 # . . discard args -2276 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2277 # . write(_test-input-stream, "\"a b\"/yz") -2278 # . indices: 0123 45 -2279 # . . push args -2280 68/push "\"a b\"/yz"/imm32 -2281 68/push _test-input-stream/imm32 -2282 # . . call -2283 e8/call write/disp32 -2284 # . . discard args -2285 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2286 # precondition: line->read == 0 -2287 # . . push args -2288 68/push "F - test-skip-string-ignores-spaces/precondition"/imm32 -2289 68/push 0/imm32 -2290 b8/copy-to-EAX _test-input-stream/imm32 -2291 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -2292 # . . call -2293 e8/call check-ints-equal/disp32 -2294 # . . discard args -2295 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2296 # skip-string(_test-input-stream) -2297 # . . push args -2298 68/push _test-input-stream/imm32 -2299 # . . call -2300 e8/call skip-string/disp32 -2301 # . . discard args -2302 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2303 # check-ints-equal(line->read, 5, msg) -2304 # . . push args -2305 68/push "F - test-skip-string-ignores-spaces"/imm32 -2306 68/push 5/imm32 -2307 b8/copy-to-EAX _test-input-stream/imm32 -2308 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -2309 # . . call -2310 e8/call check-ints-equal/disp32 -2311 # . . discard args -2312 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2313 # . epilog -2314 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2315 5d/pop-to-EBP -2316 c3/return +2249 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2250 # . epilog +2251 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2252 5d/pop-to-EBP +2253 c3/return +2254 +2255 test-string-length-at-start-of-slice-escaped: +2256 # . prolog +2257 55/push-EBP +2258 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2259 # setup: (EAX..ECX) = "\"ab\\c\" def" +2260 b8/copy-to-EAX "\"ab\\c\" def"/imm32 +2261 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2262 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 +2263 05/add-to-EAX 4/imm32 +2264 # EAX = string-length-at-start-of-slice(EAX, ECX) +2265 # . . push args +2266 51/push-ECX +2267 50/push-EAX +2268 # . . call +2269 e8/call string-length-at-start-of-slice/disp32 +2270 # . . discard args +2271 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2272 # check-ints-equal(EAX, 3, msg) +2273 # . . push args +2274 68/push "F - test-string-length-at-start-of-slice-escaped"/imm32 +2275 68/push 3/imm32 +2276 50/push-EAX +2277 # . . call +2278 e8/call check-ints-equal/disp32 +2279 # . . discard args +2280 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2281 # . epilog +2282 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2283 5d/pop-to-EBP +2284 c3/return +2285 +2286 == data +2287 +2288 Next-string-literal: # tracks the next auto-generated variable name +2289 1/imm32 +2290 +2291 # length-prefixed string containing just a single space +2292 Space: +2293 # size +2294 1/imm32 +2295 # data +2296 20/space +2297 +2298 # length-prefixed string containing just a single slash +2299 Slash: +2300 # size +2301 1/imm32 +2302 # data +2303 2f/slash +2304 +2305 _test-slice-abc: +2306 22/dquote 61/a 62/b 63/c 22/dquote # "abc" +2307 2f/slash 64/d +2308 _test-slice-abc-limit: +2309 +2310 _test-slice-a-space-b: +2311 22/dquote 61/a 20/space 62/b 22/dquote # "a b" +2312 _test-slice-a-space-b-limit: +2313 +2314 _test-slice-a-dquote-b: +2315 22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote # "a\"b" +2316 _test-slice-a-dquote-b-limit: 2317 -2318 test-skip-string-ignores-escapes: -2319 # . prolog -2320 55/push-EBP -2321 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2322 # setup -2323 # . clear-stream(_test-input-stream) -2324 # . . push args -2325 68/push _test-input-stream/imm32 -2326 # . . call -2327 e8/call clear-stream/disp32 -2328 # . . discard args -2329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2330 # . write(_test-input-stream, "\"a\\\"b\"/yz") -2331 # . indices: 01 2 34 56 -2332 # . . push args -2333 68/push "\"a\\\"b\"/yz"/imm32 -2334 68/push _test-input-stream/imm32 -2335 # . . call -2336 e8/call write/disp32 -2337 # . . discard args -2338 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2339 # precondition: line->read == 0 -2340 # . . push args -2341 68/push "F - test-skip-string-ignores-escapes/precondition"/imm32 -2342 68/push 0/imm32 -2343 b8/copy-to-EAX _test-input-stream/imm32 -2344 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -2345 # . . call -2346 e8/call check-ints-equal/disp32 -2347 # . . discard args -2348 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2349 # skip-string(_test-input-stream) -2350 # . . push args -2351 68/push _test-input-stream/imm32 -2352 # . . call -2353 e8/call skip-string/disp32 -2354 # . . discard args -2355 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2356 # check-ints-equal(line->read, 6, msg) -2357 # . . push args -2358 68/push "F - test-skip-string-ignores-escapes"/imm32 -2359 68/push 6/imm32 -2360 b8/copy-to-EAX _test-input-stream/imm32 -2361 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -2362 # . . call -2363 e8/call check-ints-equal/disp32 -2364 # . . discard args -2365 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2366 # . epilog -2367 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2368 5d/pop-to-EBP -2369 c3/return -2370 -2371 test-skip-string-works-from-mid-stream: -2372 # . prolog -2373 55/push-EBP -2374 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2375 # setup -2376 # . clear-stream(_test-input-stream) -2377 # . . push args -2378 68/push _test-input-stream/imm32 -2379 # . . call -2380 e8/call clear-stream/disp32 -2381 # . . discard args -2382 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2383 # . write(_test-input-stream, "0 \"a\\\"b\"/yz") -2384 # . indices: 01 2 34 56 -2385 # . . push args -2386 68/push "0 \"a\\\"b\"/yz"/imm32 -2387 68/push _test-input-stream/imm32 -2388 # . . call -2389 e8/call write/disp32 -2390 # . . discard args -2391 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2392 # precondition: line->read == 2 -2393 c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 2/imm32 # copy to *(EAX+4) -2394 # skip-string(_test-input-stream) -2395 # . . push args -2396 68/push _test-input-stream/imm32 -2397 # . . call -2398 e8/call skip-string/disp32 -2399 # . . discard args -2400 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2401 # check-ints-equal(line->read, 8, msg) -2402 # . . push args -2403 68/push "F - test-skip-string-works-from-mid-stream"/imm32 -2404 68/push 8/imm32 -2405 b8/copy-to-EAX _test-input-stream/imm32 -2406 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) -2407 # . . call -2408 e8/call check-ints-equal/disp32 -2409 # . . discard args -2410 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2411 # . epilog -2412 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2413 5d/pop-to-EBP -2414 c3/return -2415 -2416 skip-string-in-slice: # curr : (address byte), end : (address byte) -> new_curr/EAX -2417 # . prolog -2418 55/push-EBP -2419 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2420 # . save registers -2421 51/push-ECX -2422 52/push-EDX -2423 53/push-EBX -2424 # ECX = curr -2425 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -2426 # EDX = end -2427 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX -2428 # EAX = 0 -2429 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2430 # skip initial dquote -2431 41/increment-ECX -2432 $skip-string-in-slice:loop: -2433 # if (curr >= end) return curr -2434 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -2435 73/jump-if-greater-unsigned-or-equal $skip-string-in-slice:return-curr/disp8 -2436 # AL = *curr -2437 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -2438 $skip-string-in-slice:dquote: -2439 # if (EAX == '"') break -2440 3d/compare-EAX-and 0x22/imm32/double-quote -2441 74/jump-if-equal $skip-string-in-slice:break/disp8 -2442 $skip-string-in-slice:check-for-escape: -2443 # if (EAX == '\') escape next char -2444 3d/compare-EAX-and 0x5c/imm32/backslash -2445 75/jump-if-not-equal $skip-string-in-slice:continue/disp8 -2446 $skip-string-in-slice:escape: -2447 41/increment-ECX -2448 $skip-string-in-slice:continue: -2449 # ++curr -2450 41/increment-ECX -2451 eb/jump $skip-string-in-slice:loop/disp8 -2452 $skip-string-in-slice:break: -2453 # skip final dquote -2454 41/increment-ECX -2455 $skip-string-in-slice:return-curr: -2456 # return curr -2457 89/copy 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to EAX -2458 $skip-string-in-slice:end: -2459 # . restore registers -2460 5b/pop-to-EBX -2461 5a/pop-to-EDX -2462 59/pop-to-ECX -2463 # . epilog -2464 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2465 5d/pop-to-EBP -2466 c3/return -2467 -2468 test-skip-string-in-slice: -2469 # . prolog -2470 55/push-EBP -2471 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2472 # setup: (EAX..ECX) = "\"abc\" def" -2473 b8/copy-to-EAX "\"abc\" def"/imm32 -2474 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2475 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 -2476 05/add-to-EAX 4/imm32 -2477 # EAX = skip-string-in-slice(EAX, ECX) -2478 # . . push args -2479 51/push-ECX -2480 50/push-EAX -2481 # . . call -2482 e8/call skip-string-in-slice/disp32 -2483 # . . discard args -2484 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2485 # check-ints-equal(ECX-EAX, 4, msg) # number of chars remaining after the string literal -2486 # . . push args -2487 68/push "F - test-skip-string-in-slice"/imm32 -2488 68/push 4/imm32 -2489 # . . push ECX-EAX -2490 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX -2491 51/push-ECX -2492 # . . call -2493 e8/call check-ints-equal/disp32 -2494 # . . discard args -2495 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2496 # . epilog -2497 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2498 5d/pop-to-EBP -2499 c3/return -2500 -2501 test-skip-string-in-slice-ignores-spaces: -2502 # . prolog -2503 55/push-EBP -2504 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2505 # setup: (EAX..ECX) = "\"a b\"/yz" -2506 b8/copy-to-EAX "\"a b\"/yz"/imm32 -2507 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2508 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 -2509 05/add-to-EAX 4/imm32 -2510 # EAX = skip-string-in-slice(EAX, ECX) -2511 # . . push args -2512 51/push-ECX -2513 50/push-EAX -2514 # . . call -2515 e8/call skip-string-in-slice/disp32 -2516 # . . discard args -2517 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2518 # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal -2519 # . . push args -2520 68/push "F - test-skip-string-in-slice-ignores-spaces"/imm32 -2521 68/push 3/imm32 -2522 # . . push ECX-EAX -2523 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX -2524 51/push-ECX -2525 # . . call -2526 e8/call check-ints-equal/disp32 -2527 # . . discard args -2528 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2529 # . epilog -2530 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2531 5d/pop-to-EBP -2532 c3/return -2533 -2534 test-skip-string-in-slice-ignores-escapes: -2535 # . prolog -2536 55/push-EBP -2537 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2538 # setup: (EAX..ECX) = "\"a\\\"b\"/yz" -2539 b8/copy-to-EAX "\"a\\\"b\"/yz"/imm32 -2540 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2541 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 -2542 05/add-to-EAX 4/imm32 -2543 # EAX = skip-string-in-slice(EAX, ECX) -2544 # . . push args -2545 51/push-ECX -2546 50/push-EAX -2547 # . . call -2548 e8/call skip-string-in-slice/disp32 -2549 # . . discard args -2550 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2551 # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal -2552 # . . push args -2553 68/push "F - test-skip-string-in-slice-ignores-escapes"/imm32 -2554 68/push 3/imm32 -2555 # . . push ECX-EAX -2556 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX -2557 51/push-ECX -2558 # . . call -2559 e8/call check-ints-equal/disp32 -2560 # . . discard args -2561 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2562 # . epilog -2563 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2564 5d/pop-to-EBP -2565 c3/return -2566 -2567 test-skip-string-in-slice-stops-at-end: -2568 # . prolog -2569 55/push-EBP -2570 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2571 # setup: (EAX..ECX) = "\"abc" # unbalanced dquote -2572 b8/copy-to-EAX "\"abc"/imm32 -2573 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2574 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 -2575 05/add-to-EAX 4/imm32 -2576 # EAX = skip-string-in-slice(EAX, ECX) -2577 # . . push args -2578 51/push-ECX -2579 50/push-EAX -2580 # . . call -2581 e8/call skip-string-in-slice/disp32 -2582 # . . discard args -2583 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2584 # check-ints-equal(ECX-EAX, 0, msg) # skipped to end of slice -2585 # . . push args -2586 68/push "F - test-skip-string-in-slice-stops-at-end"/imm32 -2587 68/push 0/imm32 -2588 # . . push ECX-EAX -2589 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX -2590 51/push-ECX -2591 # . . call -2592 e8/call check-ints-equal/disp32 -2593 # . . discard args -2594 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2595 # . epilog -2596 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2597 5d/pop-to-EBP -2598 c3/return -2599 -2600 string-length-at-start-of-slice: # curr : (address byte), end : (address byte) -> length/EAX -2601 # . prolog -2602 55/push-EBP -2603 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2604 # . save registers -2605 51/push-ECX -2606 52/push-EDX -2607 53/push-EBX -2608 # ECX = curr -2609 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -2610 # EDX = end -2611 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX -2612 # length/EAX = 0 -2613 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2614 # EBX = 0 -2615 31/xor 3/mod/direct 3/rm32/EBX . . . 3/r32/EBX . . # clear EBX -2616 # skip initial dquote -2617 41/increment-ECX -2618 $string-length-at-start-of-slice:loop: -2619 # if (curr >= end) return length -2620 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -2621 73/jump-if-greater-unsigned-or-equal $string-length-at-start-of-slice:end/disp8 -2622 # BL = *curr -2623 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 3/r32/BL . . # copy byte at *ECX to BL -2624 $string-length-at-start-of-slice:dquote: -2625 # if (EBX == '"') break -2626 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x22/imm32/dquote # compare EBX -2627 74/jump-if-equal $string-length-at-start-of-slice:end/disp8 -2628 $string-length-at-start-of-slice:check-for-escape: -2629 # if (EBX == '\') escape next char -2630 81 7/subop/compare 3/mod/direct 3/rm32/EBX . . . . . 0x5c/imm32/backslash # compare EBX -2631 75/jump-if-not-equal $string-length-at-start-of-slice:continue/disp8 -2632 $string-length-at-start-of-slice:escape: -2633 # increment curr but not result -2634 41/increment-ECX -2635 $string-length-at-start-of-slice:continue: -2636 # ++result -2637 40/increment-EAX -2638 # ++curr -2639 41/increment-ECX -2640 eb/jump $string-length-at-start-of-slice:loop/disp8 -2641 $string-length-at-start-of-slice:end: -2642 # . restore registers -2643 5b/pop-to-EBX -2644 5a/pop-to-EDX -2645 59/pop-to-ECX -2646 # . epilog -2647 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2648 5d/pop-to-EBP -2649 c3/return -2650 -2651 test-string-length-at-start-of-slice: -2652 # . prolog -2653 55/push-EBP -2654 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2655 # setup: (EAX..ECX) = "\"abc\" def" -2656 b8/copy-to-EAX "\"abc\" def"/imm32 -2657 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2658 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 -2659 05/add-to-EAX 4/imm32 -2660 # EAX = string-length-at-start-of-slice(EAX, ECX) -2661 # . . push args -2662 51/push-ECX -2663 50/push-EAX -2664 # . . call -2665 e8/call string-length-at-start-of-slice/disp32 -2666 # . . discard args -2667 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2668 # check-ints-equal(EAX, 3, msg) -2669 # . . push args -2670 68/push "F - test-string-length-at-start-of-slice"/imm32 -2671 68/push 3/imm32 -2672 50/push-EAX -2673 # . . call -2674 e8/call check-ints-equal/disp32 -2675 # . . discard args -2676 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2677 # . epilog -2678 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2679 5d/pop-to-EBP -2680 c3/return -2681 -2682 test-string-length-at-start-of-slice-escaped: -2683 # . prolog -2684 55/push-EBP -2685 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2686 # setup: (EAX..ECX) = "\"ab\\c\" def" -2687 b8/copy-to-EAX "\"ab\\c\" def"/imm32 -2688 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2689 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 -2690 05/add-to-EAX 4/imm32 -2691 # EAX = string-length-at-start-of-slice(EAX, ECX) -2692 # . . push args -2693 51/push-ECX -2694 50/push-EAX -2695 # . . call -2696 e8/call string-length-at-start-of-slice/disp32 -2697 # . . discard args -2698 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -2699 # check-ints-equal(EAX, 3, msg) -2700 # . . push args -2701 68/push "F - test-string-length-at-start-of-slice-escaped"/imm32 -2702 68/push 3/imm32 -2703 50/push-EAX -2704 # . . call -2705 e8/call check-ints-equal/disp32 -2706 # . . discard args -2707 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2708 # . epilog -2709 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2710 5d/pop-to-EBP -2711 c3/return -2712 -2713 == data -2714 -2715 Next-string-literal: # tracks the next auto-generated variable name -2716 1/imm32 -2717 -2718 # length-prefixed string containing just a single space -2719 Space: -2720 # size -2721 1/imm32 -2722 # data -2723 20/space -2724 -2725 # length-prefixed string containing just a single slash -2726 Slash: -2727 # size -2728 1/imm32 -2729 # data -2730 2f/slash -2731 -2732 _test-slice-abc: -2733 22/dquote 61/a 62/b 63/c 22/dquote # "abc" -2734 2f/slash 64/d -2735 _test-slice-abc-limit: -2736 -2737 _test-slice-a-space-b: -2738 22/dquote 61/a 20/space 62/b 22/dquote # "a b" -2739 _test-slice-a-space-b-limit: -2740 -2741 _test-slice-a-dquote-b: -2742 22/dquote 61/a 5c/backslash 22/dquote 62/b 22/dquote # "a\"b" -2743 _test-slice-a-dquote-b-limit: -2744 -2745 _test-slice-a-newline-b: -2746 22/dquote 61/a 5c/backslash 6e/n 62/b 22/dquote # "a\nb" -2747 _test-slice-a-newline-b-limit: -2748 -2749 # "abc/def"/ghi -2750 _test-slice-literal-string: -2751 22/dquote -2752 61/a 62/b 63/c # abc -2753 2f/slash 64/d 65/e 66/f # /def -2754 22/dquote -2755 2f/slash 67/g 68/h 69/i # /ghi -2756 _test-slice-literal-string-with-limit: -2757 -2758 # . . vim:nowrap:textwidth=0 +2318 _test-slice-a-newline-b: +2319 22/dquote 61/a 5c/backslash 6e/n 62/b 22/dquote # "a\nb" +2320 _test-slice-a-newline-b-limit: +2321 +2322 # "abc/def"/ghi +2323 _test-slice-literal-string: +2324 22/dquote +2325 61/a 62/b 63/c # abc +2326 2f/slash 64/d 65/e 66/f # /def +2327 22/dquote +2328 2f/slash 67/g 68/h 69/i # /ghi +2329 _test-slice-literal-string-with-limit: +2330 +2331 # . . vim:nowrap:textwidth=0 diff --git a/html/apps/factorial.subx.html b/html/apps/factorial.subx.html index 780c9ce1..cd88c2a1 100644 --- a/html/apps/factorial.subx.html +++ b/html/apps/factorial.subx.html @@ -62,7 +62,7 @@ if ('onhashchange' in window) { 1 ## compute the factorial of 5, and return the result in the exit code 2 # 3 # To run: - 4 # $ ./subx translate apps/factorial.subx -o apps/factorial + 4 # $ ./subx translate 0*.subx apps/factorial.subx -o apps/factorial 5 # $ ./subx run apps/factorial 6 # Expected result: 7 # $ echo $? diff --git a/html/apps/handle.subx.html b/html/apps/handle.subx.html index 6b660afa..cee6d903 100644 --- a/html/apps/handle.subx.html +++ b/html/apps/handle.subx.html @@ -76,7 +76,7 @@ if ('onhashchange' in window) { 13 # offset 4: address 14 # 15 # To run: - 16 # $ ./subx translate *.subx apps/handle.subx -o apps/handle + 16 # $ ./subx translate 0*.subx apps/handle.subx -o apps/handle 17 # $ ./subx run apps/handle 18 # Expected result is a successful lookup followed by a hard abort: 19 # lookup succeeded diff --git a/html/apps/hex.subx.html b/html/apps/hex.subx.html index 214b5e33..0a0e1ec7 100644 --- a/html/apps/hex.subx.html +++ b/html/apps/hex.subx.html @@ -64,7 +64,7 @@ if ('onhashchange' in window) { 3 # comments between '#' and newline. 4 # 5 # To run: - 6 # $ ./subx translate *.subx apps/hex.subx -o apps/hex + 6 # $ ./subx translate 0*.subx apps/subx-common.subx apps/hex.subx -o apps/hex 7 # $ echo '80 81 82 # comment' |./subx run apps/hex |xxd - 8 # Expected output: 9 # 00000000: 8081 82 diff --git a/html/apps/pack.subx.html b/html/apps/pack.subx.html index 6b647e6f..b35b6508 100644 --- a/html/apps/pack.subx.html +++ b/html/apps/pack.subx.html @@ -65,7 +65,7 @@ if ('onhashchange' in window) { 3 # uses are left untouched. 4 # 5 # To run: - 6 # $ ./subx translate *.subx apps/pack.subx -o apps/pack + 6 # $ ./subx translate 0*.subx apps/subx-common.subx apps/pack.subx -o apps/pack 7 # $ echo '05/add-to-EAX 0x20/imm32' |./subx run apps/pack 8 # Expected output: 9 # 05 20 00 00 00 # 05/add-to-EAX 0x20/imm32 diff --git a/html/apps/subx-common.subx.html b/html/apps/subx-common.subx.html index f7c7e257..fb34fb72 100644 --- a/html/apps/subx-common.subx.html +++ b/html/apps/subx-common.subx.html @@ -333,479 +333,479 @@ if ('onhashchange' in window) { 270 5d/pop-to-EBP 271 c3/return 272 - 273 # write an entire stream's contents to a buffered-file - 274 # ways to do this: - 275 # - construct a 'maximal slice' and pass it to write-slice-buffered - 276 # - flush the buffered-file and pass the stream directly to its fd (disabling buffering) - 277 # we'll go with the first way for now - 278 write-stream-data: # f : (address buffered-file), s : (address stream) -> <void> - 279 # . prolog - 280 55/push-EBP - 281 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 282 # . save registers - 283 50/push-EAX - 284 51/push-ECX - 285 56/push-ESI - 286 # ESI = s - 287 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 288 # var slice/ECX = {s->data, s->data + s->write} - 289 # . push s->data + s->write - 290 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX - 291 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 - 292 50/push-EAX - 293 # . push s->data - 294 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 0xc/disp8 . # copy ESI+12 to EAX - 295 50/push-EAX - 296 # . ECX = ESP - 297 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 298 # write-slice-buffered(f, slice) - 299 # . . push args - 300 51/push-ECX - 301 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 302 # . . call - 303 e8/call write-slice-buffered/disp32 - 304 # . . discard args - 305 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 306 $write-stream-data:end: - 307 # . restore locals - 308 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 309 # . restore registers - 310 5e/pop-to-ESI - 311 59/pop-to-ECX - 312 58/pop-to-EAX - 313 # . epilog - 314 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 315 5d/pop-to-EBP - 316 c3/return - 317 - 318 test-write-stream-data: - 319 # . prolog - 320 55/push-EBP - 321 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 322 # setup - 323 # . clear-stream(_test-output-stream) - 324 # . . push args - 325 68/push _test-output-stream/imm32 - 326 # . . call - 327 e8/call clear-stream/disp32 - 328 # . . discard args - 329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 330 # . clear-stream(_test-output-buffered-file+4) - 331 # . . push args - 332 b8/copy-to-EAX _test-output-buffered-file/imm32 - 333 05/add-to-EAX 4/imm32 - 334 50/push-EAX - 335 # . . call - 336 e8/call clear-stream/disp32 - 337 # . . discard args - 338 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 339 # . clear-stream(_test-input-stream) - 340 # . . push args - 341 68/push _test-input-stream/imm32 - 342 # . . call - 343 e8/call clear-stream/disp32 - 344 # . . discard args - 345 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 346 # initialize input - 347 # . write(_test-input-stream, "abcd") - 348 # . . push args - 349 68/push "abcd"/imm32 - 350 68/push _test-input-stream/imm32 - 351 # . . call - 352 e8/call write/disp32 - 353 # . . discard args - 354 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 355 # write-stream-data(_test-output-buffered-file, _test-input-stream) - 356 # . . push args - 357 68/push _test-input-stream/imm32 - 358 68/push _test-output-buffered-file/imm32 - 359 # . . call - 360 e8/call write-stream-data/disp32 - 361 # . . discard args - 362 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 363 # check that the write happened as expected - 364 # . flush(_test-output-buffered-file) - 365 # . . push args - 366 68/push _test-output-buffered-file/imm32 - 367 # . . call - 368 e8/call flush/disp32 - 369 # . . discard args - 370 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 371 # . check-stream-equal(_test-output-stream, "abcd", msg) - 372 # . . push args - 373 68/push "F - test-write-stream-data"/imm32 - 374 68/push "abcd"/imm32 - 375 68/push _test-output-stream/imm32 - 376 # . . call - 377 e8/call check-stream-equal/disp32 - 378 # . . discard args - 379 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 380 # . epilog - 381 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 382 5d/pop-to-EBP - 383 c3/return - 384 - 385 has-metadata?: # word : (address slice), s : (address string) -> EAX : boolean - 386 # pseudocode: - 387 # var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name - 388 # curr = twig->end - 389 # while true - 390 # twig = next-token-from-slice(curr, word->end, '/') - 391 # if (twig.empty()) break - 392 # if (slice-equal?(twig, s)) return true - 393 # curr = twig->end - 394 # return false - 395 # . prolog - 396 55/push-EBP - 397 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 398 # . save registers - 399 51/push-ECX - 400 52/push-EDX - 401 56/push-ESI - 402 57/push-EDI - 403 # ESI = word - 404 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI - 405 # EDX = word->end - 406 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX - 407 # var twig/EDI : (address slice) = {0, 0} - 408 68/push 0/imm32/end - 409 68/push 0/imm32/start - 410 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI - 411 # next-token-from-slice(word->start, word->end, '/', twig) - 412 # . . push args - 413 57/push-EDI - 414 68/push 0x2f/imm32/slash - 415 52/push-EDX - 416 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - 417 # . . call - 418 e8/call next-token-from-slice/disp32 - 419 # . . discard args - 420 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 421 # curr/ECX = twig->end - 422 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX - 423 $has-metadata?:loop: - 424 # next-token-from-slice(curr, word->end, '/', twig) - 425 # . . push args - 426 57/push-EDI - 427 68/push 0x2f/imm32/slash - 428 52/push-EDX - 429 51/push-ECX - 430 # . . call - 431 e8/call next-token-from-slice/disp32 - 432 # . . discard args - 433 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 434 # if (slice-empty?(twig)) return false - 435 # . EAX = slice-empty?(twig) - 436 # . . push args - 437 57/push-EDI - 438 # . . call - 439 e8/call slice-empty?/disp32 - 440 # . . discard args - 441 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 442 # . if (EAX != 0) return false - 443 3d/compare-EAX-and 0/imm32 - 444 75/jump-if-not-equal $has-metadata?:false/disp8 - 445 # if (slice-equal?(twig, s)) return true - 446 # . EAX = slice-equal?(twig, s) - 447 # . . push args - 448 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) - 449 57/push-EDI - 450 # . . call - 451 e8/call slice-equal?/disp32 - 452 # . . discard args - 453 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 454 # . if (EAX != 0) return true - 455 3d/compare-EAX-and 0/imm32 - 456 75/jump-if-not-equal $has-metadata?:true/disp8 - 457 # curr = twig->end - 458 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX - 459 eb/jump $has-metadata?:loop/disp8 - 460 $has-metadata?:true: - 461 b8/copy-to-EAX 1/imm32/true - 462 eb/jump $has-metadata?:end/disp8 - 463 $has-metadata?:false: - 464 b8/copy-to-EAX 0/imm32/false - 465 $has-metadata?:end: - 466 # . reclaim locals - 467 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 468 # . restore registers - 469 5f/pop-to-EDI - 470 5e/pop-to-ESI - 471 5a/pop-to-EDX - 472 59/pop-to-ECX - 473 # . epilog - 474 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 475 5d/pop-to-EBP - 476 c3/return - 477 - 478 test-has-metadata-true: - 479 # . prolog - 480 55/push-EBP - 481 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 482 # (EAX..ECX) = "ab/imm32" - 483 b8/copy-to-EAX "ab/imm32"/imm32 - 484 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 485 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 - 486 05/add-to-EAX 4/imm32 - 487 # var in/ESI : (address slice) = {EAX, ECX} - 488 51/push-ECX - 489 50/push-EAX - 490 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - 491 # EAX = has-metadata?(ESI, "imm32") - 492 # . . push args - 493 68/push "imm32"/imm32 - 494 56/push-ESI - 495 # . . call - 496 e8/call has-metadata?/disp32 - 497 # . . discard args - 498 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 499 # check-ints-equal(EAX, 1, msg) - 500 # . . push args - 501 68/push "F - test-has-metadata-true"/imm32 - 502 68/push 1/imm32/true - 503 50/push-EAX - 504 # . . call - 505 e8/call check-ints-equal/disp32 - 506 # . . discard args - 507 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 508 # . epilog - 509 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 510 5d/pop-to-EBP - 511 c3/return - 512 - 513 test-has-metadata-false: - 514 # . prolog - 515 55/push-EBP - 516 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 517 # (EAX..ECX) = "ab/c" - 518 b8/copy-to-EAX "ab/c"/imm32 - 519 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 520 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 - 521 05/add-to-EAX 4/imm32 - 522 # var in/ESI : (address slice) = {EAX, ECX} - 523 51/push-ECX - 524 50/push-EAX - 525 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - 526 # EAX = has-metadata?(ESI, "d") - 527 # . . push args - 528 68/push "d"/imm32 - 529 56/push-ESI - 530 # . . call - 531 e8/call has-metadata?/disp32 - 532 # . . discard args - 533 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 534 # check-ints-equal(EAX, 0, msg) - 535 # . . push args - 536 68/push "F - test-has-metadata-false"/imm32 - 537 68/push 0/imm32/false - 538 50/push-EAX - 539 # . . call - 540 e8/call check-ints-equal/disp32 - 541 # . . discard args - 542 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 543 # . epilog - 544 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 545 5d/pop-to-EBP - 546 c3/return - 547 - 548 test-has-metadata-ignore-name: - 549 # . prolog - 550 55/push-EBP - 551 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 552 # (EAX..ECX) = "a/b" - 553 b8/copy-to-EAX "a/b"/imm32 - 554 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 555 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 - 556 05/add-to-EAX 4/imm32 - 557 # var in/ESI : (address slice) = {EAX, ECX} - 558 51/push-ECX - 559 50/push-EAX - 560 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - 561 # EAX = has-metadata?(ESI, "a") - 562 # . . push args - 563 68/push "a"/imm32 - 564 56/push-ESI - 565 # . . call - 566 e8/call has-metadata?/disp32 - 567 # . . discard args - 568 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 569 # check-ints-equal(EAX, 0, msg) - 570 # . . push args - 571 68/push "F - test-has-metadata-ignore-name"/imm32 - 572 68/push 0/imm32/false - 573 50/push-EAX - 574 # . . call - 575 e8/call check-ints-equal/disp32 - 576 # . . discard args - 577 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 578 # . epilog - 579 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 580 5d/pop-to-EBP - 581 c3/return - 582 - 583 test-has-metadata-multiple-true: - 584 # . prolog - 585 55/push-EBP - 586 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 587 # (EAX..ECX) = "a/b/c" - 588 b8/copy-to-EAX "a/b/c"/imm32 - 589 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 590 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 - 591 05/add-to-EAX 4/imm32 - 592 # var in/ESI : (address slice) = {EAX, ECX} - 593 51/push-ECX - 594 50/push-EAX - 595 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - 596 # EAX = has-metadata?(ESI, "c") - 597 # . . push args - 598 68/push "c"/imm32 - 599 56/push-ESI - 600 # . . call - 601 e8/call has-metadata?/disp32 - 602 # . . discard args - 603 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 604 # check-ints-equal(EAX, 1, msg) - 605 # . . push args - 606 68/push "F - test-has-metadata-multiple-true"/imm32 - 607 68/push 1/imm32/true - 608 50/push-EAX - 609 # . . call - 610 e8/call check-ints-equal/disp32 - 611 # . . discard args - 612 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 613 # . epilog - 614 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 615 5d/pop-to-EBP - 616 c3/return - 617 - 618 test-has-metadata-multiple-false: - 619 # . prolog - 620 55/push-EBP - 621 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 622 # (EAX..ECX) = "a/b/c" - 623 b8/copy-to-EAX "a/b/c"/imm32 - 624 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 625 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 - 626 05/add-to-EAX 4/imm32 - 627 # var in/ESI : (address slice) = {EAX, ECX} - 628 51/push-ECX - 629 50/push-EAX - 630 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI - 631 # EAX = has-metadata?(ESI, "d") - 632 # . . push args - 633 68/push "d"/imm32 - 634 56/push-ESI - 635 # . . call - 636 e8/call has-metadata?/disp32 - 637 # . . discard args - 638 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 639 # check-ints-equal(EAX, 0, msg) - 640 # . . push args - 641 68/push "F - test-has-metadata-multiple-false"/imm32 - 642 68/push 0/imm32/false - 643 50/push-EAX - 644 # . . call - 645 e8/call check-ints-equal/disp32 - 646 # . . discard args - 647 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 648 # . epilog - 649 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 650 5d/pop-to-EBP - 651 c3/return - 652 - 653 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print - 654 # it in 'width' bytes of hex, least significant first. - 655 # Otherwise just print the entire word including metadata. - 656 # Always print a trailing space. - 657 emit: # out : (address buffered-file), word : (address slice), width : int -> <void> - 658 # . prolog - 659 55/push-EBP - 660 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 661 # . save registers - 662 50/push-EAX - 663 56/push-ESI - 664 57/push-EDI - 665 # ESI = word - 666 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI - 667 # var name/EDI : (address slice) = {0, 0} - 668 68/push 0/imm32/end - 669 68/push 0/imm32/start - 670 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI - 671 # datum = next-token-from-slice(word->start, word->end, '/') - 672 # . . push args - 673 57/push-EDI - 674 68/push 0x2f/imm32/slash - 675 ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) - 676 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI - 677 # . . call - 678 e8/call next-token-from-slice/disp32 - 679 # . . discard args - 680 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP - 681 # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return - 682 # . EAX = is-valid-name?(name) - 683 # . . push args - 684 57/push-EDI - 685 # . . call - 686 e8/call is-valid-name?/disp32 - 687 # . . discard args - 688 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 689 # . if (EAX != 0) - 690 3d/compare-EAX-and 0/imm32 - 691 74/jump-if-equal $emit:hex-int/disp8 - 692 $emit:name: - 693 # . write-slice-buffered(out, word) - 694 # . . push args - 695 56/push-ESI - 696 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 697 # . . call - 698 e8/call write-slice-buffered/disp32 - 699 # . . discard args - 700 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 701 # . write-buffered(out, " ") - 702 # . . push args - 703 68/push " "/imm32 - 704 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) - 705 # . . call - 706 e8/call write-buffered/disp32 - 707 # . . discard args - 708 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP - 709 # . return - 710 eb/jump $emit:end/disp8 - 711 # otherwise emit-hex(out, parse-hex-int(datum), width) - 712 # (Weird shit can happen here if the datum of 'word' isn't either a valid - 713 # name or a hex number, but we're only going to be passing in real legal - 714 # programs. We just want to make sure that valid names aren't treated as - 715 # (valid) hex numbers.) - 716 $emit:hex-int: - 717 # . value/EAX = parse-hex-int(datum) - 718 # . . push args - 719 57/push-EDI - 720 # . . call - 721 e8/call parse-hex-int/disp32 - 722 # . . discard args - 723 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 724 # . emit-hex(out, value, width) - 725 # . . push args - 726 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) - 727 50/push-EAX + 273 # update line->read to end of string literal surrounded by double quotes + 274 # line->read must start out at a double-quote + 275 skip-string: # line : (address stream) + 276 # . prolog + 277 55/push-EBP + 278 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 279 # . save registers + 280 50/push-EAX + 281 51/push-ECX + 282 52/push-EDX + 283 # ECX = line + 284 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + 285 # EAX = skip-string-in-slice(&line->data[line->read], &line->data[line->write]) + 286 # . . push &line->data[line->write] + 287 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 8/disp8 . # copy *(ECX+8) to EDX + 288 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX + 289 52/push-EDX + 290 # . . push &line->data[line->read] + 291 8b/copy 1/mod/*+disp8 1/rm32/ECX . . 2/r32/EDX 4/disp8 . # copy *(ECX+4) to EDX + 292 8d/copy-address 1/mod/*+disp8 4/rm32/sib 1/base/ECX 2/index/EDX . 2/r32/EDX 0xc/disp8 . # copy ECX+EDX+12 to EDX + 293 52/push-EDX + 294 # . . call + 295 e8/call skip-string-in-slice/disp32 + 296 # . . discard args + 297 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 298 # line->read = EAX - line->data + 299 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX + 300 2d/subtract-from-EAX 0xc/imm32 + 301 89/copy 1/mod/*+disp8 1/rm32/ECX . . 0/r32/EAX 4/disp8 . # copy EAX to *(ECX+4) + 302 $skip-string:end: + 303 # . restore registers + 304 5a/pop-to-EDX + 305 59/pop-to-ECX + 306 58/pop-to-EAX + 307 # . epilog + 308 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 309 5d/pop-to-EBP + 310 c3/return + 311 + 312 test-skip-string: + 313 # . prolog + 314 55/push-EBP + 315 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 316 # setup + 317 # . clear-stream(_test-input-stream) + 318 # . . push args + 319 68/push _test-input-stream/imm32 + 320 # . . call + 321 e8/call clear-stream/disp32 + 322 # . . discard args + 323 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 324 # . write(_test-input-stream, "\"abc\" def") + 325 # . indices: 0123 45 + 326 # . . push args + 327 68/push "\"abc\" def"/imm32 + 328 68/push _test-input-stream/imm32 + 329 # . . call + 330 e8/call write/disp32 + 331 # . . discard args + 332 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 333 # precondition: line->read == 0 + 334 # . . push args + 335 68/push "F - test-skip-string/precondition"/imm32 + 336 68/push 0/imm32 + 337 b8/copy-to-EAX _test-input-stream/imm32 + 338 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + 339 # . . call + 340 e8/call check-ints-equal/disp32 + 341 # . . discard args + 342 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 343 # skip-string(_test-input-stream) + 344 # . . push args + 345 68/push _test-input-stream/imm32 + 346 # . . call + 347 e8/call skip-string/disp32 + 348 # . . discard args + 349 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 350 # check-ints-equal(line->read, 5, msg) + 351 # . . push args + 352 68/push "F - test-skip-string"/imm32 + 353 68/push 5/imm32 + 354 b8/copy-to-EAX _test-input-stream/imm32 + 355 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + 356 # . . call + 357 e8/call check-ints-equal/disp32 + 358 # . . discard args + 359 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 360 # . epilog + 361 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 362 5d/pop-to-EBP + 363 c3/return + 364 + 365 test-skip-string-ignores-spaces: + 366 # . prolog + 367 55/push-EBP + 368 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 369 # setup + 370 # . clear-stream(_test-input-stream) + 371 # . . push args + 372 68/push _test-input-stream/imm32 + 373 # . . call + 374 e8/call clear-stream/disp32 + 375 # . . discard args + 376 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 377 # . write(_test-input-stream, "\"a b\"/yz") + 378 # . indices: 0123 45 + 379 # . . push args + 380 68/push "\"a b\"/yz"/imm32 + 381 68/push _test-input-stream/imm32 + 382 # . . call + 383 e8/call write/disp32 + 384 # . . discard args + 385 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 386 # precondition: line->read == 0 + 387 # . . push args + 388 68/push "F - test-skip-string-ignores-spaces/precondition"/imm32 + 389 68/push 0/imm32 + 390 b8/copy-to-EAX _test-input-stream/imm32 + 391 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + 392 # . . call + 393 e8/call check-ints-equal/disp32 + 394 # . . discard args + 395 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 396 # skip-string(_test-input-stream) + 397 # . . push args + 398 68/push _test-input-stream/imm32 + 399 # . . call + 400 e8/call skip-string/disp32 + 401 # . . discard args + 402 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 403 # check-ints-equal(line->read, 5, msg) + 404 # . . push args + 405 68/push "F - test-skip-string-ignores-spaces"/imm32 + 406 68/push 5/imm32 + 407 b8/copy-to-EAX _test-input-stream/imm32 + 408 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + 409 # . . call + 410 e8/call check-ints-equal/disp32 + 411 # . . discard args + 412 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 413 # . epilog + 414 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 415 5d/pop-to-EBP + 416 c3/return + 417 + 418 test-skip-string-ignores-escapes: + 419 # . prolog + 420 55/push-EBP + 421 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 422 # setup + 423 # . clear-stream(_test-input-stream) + 424 # . . push args + 425 68/push _test-input-stream/imm32 + 426 # . . call + 427 e8/call clear-stream/disp32 + 428 # . . discard args + 429 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 430 # . write(_test-input-stream, "\"a\\\"b\"/yz") + 431 # . indices: 01 2 34 56 + 432 # . . push args + 433 68/push "\"a\\\"b\"/yz"/imm32 + 434 68/push _test-input-stream/imm32 + 435 # . . call + 436 e8/call write/disp32 + 437 # . . discard args + 438 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 439 # precondition: line->read == 0 + 440 # . . push args + 441 68/push "F - test-skip-string-ignores-escapes/precondition"/imm32 + 442 68/push 0/imm32 + 443 b8/copy-to-EAX _test-input-stream/imm32 + 444 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + 445 # . . call + 446 e8/call check-ints-equal/disp32 + 447 # . . discard args + 448 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 449 # skip-string(_test-input-stream) + 450 # . . push args + 451 68/push _test-input-stream/imm32 + 452 # . . call + 453 e8/call skip-string/disp32 + 454 # . . discard args + 455 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 456 # check-ints-equal(line->read, 6, msg) + 457 # . . push args + 458 68/push "F - test-skip-string-ignores-escapes"/imm32 + 459 68/push 6/imm32 + 460 b8/copy-to-EAX _test-input-stream/imm32 + 461 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + 462 # . . call + 463 e8/call check-ints-equal/disp32 + 464 # . . discard args + 465 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 466 # . epilog + 467 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 468 5d/pop-to-EBP + 469 c3/return + 470 + 471 test-skip-string-works-from-mid-stream: + 472 # . prolog + 473 55/push-EBP + 474 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 475 # setup + 476 # . clear-stream(_test-input-stream) + 477 # . . push args + 478 68/push _test-input-stream/imm32 + 479 # . . call + 480 e8/call clear-stream/disp32 + 481 # . . discard args + 482 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 483 # . write(_test-input-stream, "0 \"a\\\"b\"/yz") + 484 # . indices: 01 2 34 56 + 485 # . . push args + 486 68/push "0 \"a\\\"b\"/yz"/imm32 + 487 68/push _test-input-stream/imm32 + 488 # . . call + 489 e8/call write/disp32 + 490 # . . discard args + 491 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 492 # precondition: line->read == 2 + 493 c7 0/subop/copy 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 2/imm32 # copy to *(EAX+4) + 494 # skip-string(_test-input-stream) + 495 # . . push args + 496 68/push _test-input-stream/imm32 + 497 # . . call + 498 e8/call skip-string/disp32 + 499 # . . discard args + 500 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 501 # check-ints-equal(line->read, 8, msg) + 502 # . . push args + 503 68/push "F - test-skip-string-works-from-mid-stream"/imm32 + 504 68/push 8/imm32 + 505 b8/copy-to-EAX _test-input-stream/imm32 + 506 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 4/disp8 . # push *(EAX+4) + 507 # . . call + 508 e8/call check-ints-equal/disp32 + 509 # . . discard args + 510 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 511 # . epilog + 512 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 513 5d/pop-to-EBP + 514 c3/return + 515 + 516 skip-string-in-slice: # curr : (address byte), end : (address byte) -> new_curr/EAX + 517 # . prolog + 518 55/push-EBP + 519 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 520 # . save registers + 521 51/push-ECX + 522 52/push-EDX + 523 53/push-EBX + 524 # ECX = curr + 525 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX + 526 # EDX = end + 527 8b/copy 1/mod/*+disp8 5/rm32/EBP . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX + 528 # EAX = 0 + 529 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX + 530 # skip initial dquote + 531 41/increment-ECX + 532 $skip-string-in-slice:loop: + 533 # if (curr >= end) return curr + 534 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX + 535 73/jump-if-greater-unsigned-or-equal $skip-string-in-slice:return-curr/disp8 + 536 # AL = *curr + 537 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL + 538 $skip-string-in-slice:dquote: + 539 # if (EAX == '"') break + 540 3d/compare-EAX-and 0x22/imm32/double-quote + 541 74/jump-if-equal $skip-string-in-slice:break/disp8 + 542 $skip-string-in-slice:check-for-escape: + 543 # if (EAX == '\') escape next char + 544 3d/compare-EAX-and 0x5c/imm32/backslash + 545 75/jump-if-not-equal $skip-string-in-slice:continue/disp8 + 546 $skip-string-in-slice:escape: + 547 41/increment-ECX + 548 $skip-string-in-slice:continue: + 549 # ++curr + 550 41/increment-ECX + 551 eb/jump $skip-string-in-slice:loop/disp8 + 552 $skip-string-in-slice:break: + 553 # skip final dquote + 554 41/increment-ECX + 555 $skip-string-in-slice:return-curr: + 556 # return curr + 557 89/copy 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # copy ECX to EAX + 558 $skip-string-in-slice:end: + 559 # . restore registers + 560 5b/pop-to-EBX + 561 5a/pop-to-EDX + 562 59/pop-to-ECX + 563 # . epilog + 564 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 565 5d/pop-to-EBP + 566 c3/return + 567 + 568 test-skip-string-in-slice: + 569 # . prolog + 570 55/push-EBP + 571 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 572 # setup: (EAX..ECX) = "\"abc\" def" + 573 b8/copy-to-EAX "\"abc\" def"/imm32 + 574 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 575 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 + 576 05/add-to-EAX 4/imm32 + 577 # EAX = skip-string-in-slice(EAX, ECX) + 578 # . . push args + 579 51/push-ECX + 580 50/push-EAX + 581 # . . call + 582 e8/call skip-string-in-slice/disp32 + 583 # . . discard args + 584 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 585 # check-ints-equal(ECX-EAX, 4, msg) # number of chars remaining after the string literal + 586 # . . push args + 587 68/push "F - test-skip-string-in-slice"/imm32 + 588 68/push 4/imm32 + 589 # . . push ECX-EAX + 590 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX + 591 51/push-ECX + 592 # . . call + 593 e8/call check-ints-equal/disp32 + 594 # . . discard args + 595 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 596 # . epilog + 597 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 598 5d/pop-to-EBP + 599 c3/return + 600 + 601 test-skip-string-in-slice-ignores-spaces: + 602 # . prolog + 603 55/push-EBP + 604 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 605 # setup: (EAX..ECX) = "\"a b\"/yz" + 606 b8/copy-to-EAX "\"a b\"/yz"/imm32 + 607 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 608 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 + 609 05/add-to-EAX 4/imm32 + 610 # EAX = skip-string-in-slice(EAX, ECX) + 611 # . . push args + 612 51/push-ECX + 613 50/push-EAX + 614 # . . call + 615 e8/call skip-string-in-slice/disp32 + 616 # . . discard args + 617 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 618 # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal + 619 # . . push args + 620 68/push "F - test-skip-string-in-slice-ignores-spaces"/imm32 + 621 68/push 3/imm32 + 622 # . . push ECX-EAX + 623 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX + 624 51/push-ECX + 625 # . . call + 626 e8/call check-ints-equal/disp32 + 627 # . . discard args + 628 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 629 # . epilog + 630 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 631 5d/pop-to-EBP + 632 c3/return + 633 + 634 test-skip-string-in-slice-ignores-escapes: + 635 # . prolog + 636 55/push-EBP + 637 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 638 # setup: (EAX..ECX) = "\"a\\\"b\"/yz" + 639 b8/copy-to-EAX "\"a\\\"b\"/yz"/imm32 + 640 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 641 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 + 642 05/add-to-EAX 4/imm32 + 643 # EAX = skip-string-in-slice(EAX, ECX) + 644 # . . push args + 645 51/push-ECX + 646 50/push-EAX + 647 # . . call + 648 e8/call skip-string-in-slice/disp32 + 649 # . . discard args + 650 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 651 # check-ints-equal(ECX-EAX, 3, msg) # number of chars remaining after the string literal + 652 # . . push args + 653 68/push "F - test-skip-string-in-slice-ignores-escapes"/imm32 + 654 68/push 3/imm32 + 655 # . . push ECX-EAX + 656 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX + 657 51/push-ECX + 658 # . . call + 659 e8/call check-ints-equal/disp32 + 660 # . . discard args + 661 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 662 # . epilog + 663 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 664 5d/pop-to-EBP + 665 c3/return + 666 + 667 test-skip-string-in-slice-stops-at-end: + 668 # . prolog + 669 55/push-EBP + 670 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 671 # setup: (EAX..ECX) = "\"abc" # unbalanced dquote + 672 b8/copy-to-EAX "\"abc"/imm32 + 673 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 674 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 + 675 05/add-to-EAX 4/imm32 + 676 # EAX = skip-string-in-slice(EAX, ECX) + 677 # . . push args + 678 51/push-ECX + 679 50/push-EAX + 680 # . . call + 681 e8/call skip-string-in-slice/disp32 + 682 # . . discard args + 683 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 684 # check-ints-equal(ECX-EAX, 0, msg) # skipped to end of slice + 685 # . . push args + 686 68/push "F - test-skip-string-in-slice-stops-at-end"/imm32 + 687 68/push 0/imm32 + 688 # . . push ECX-EAX + 689 29/subtract 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # subtract EAX from ECX + 690 51/push-ECX + 691 # . . call + 692 e8/call check-ints-equal/disp32 + 693 # . . discard args + 694 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 695 # . epilog + 696 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 697 5d/pop-to-EBP + 698 c3/return + 699 + 700 # write an entire stream's contents to a buffered-file + 701 # ways to do this: + 702 # - construct a 'maximal slice' and pass it to write-slice-buffered + 703 # - flush the buffered-file and pass the stream directly to its fd (disabling buffering) + 704 # we'll go with the first way for now + 705 write-stream-data: # f : (address buffered-file), s : (address stream) -> <void> + 706 # . prolog + 707 55/push-EBP + 708 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 709 # . save registers + 710 50/push-EAX + 711 51/push-ECX + 712 56/push-ESI + 713 # ESI = s + 714 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI + 715 # var slice/ECX = {s->data, s->data + s->write} + 716 # . push s->data + s->write + 717 8b/copy 0/mod/indirect 6/rm32/ESI . . . 0/r32/EAX . . # copy *ESI to EAX + 718 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 + 719 50/push-EAX + 720 # . push s->data + 721 8d/copy-address 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 0xc/disp8 . # copy ESI+12 to EAX + 722 50/push-EAX + 723 # . ECX = ESP + 724 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX + 725 # write-slice-buffered(f, slice) + 726 # . . push args + 727 51/push-ECX 728 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 729 # . . call - 730 e8/call emit-hex/disp32 + 730 e8/call write-slice-buffered/disp32 731 # . . discard args - 732 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 733 $emit:end: - 734 # . reclaim locals + 732 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 733 $write-stream-data:end: + 734 # . restore locals 735 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP 736 # . restore registers - 737 5f/pop-to-EDI - 738 5e/pop-to-ESI + 737 5e/pop-to-ESI + 738 59/pop-to-ECX 739 58/pop-to-EAX 740 # . epilog 741 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP 742 5d/pop-to-EBP 743 c3/return 744 - 745 test-emit-number: + 745 test-write-stream-data: 746 # . prolog 747 55/push-EBP 748 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP @@ -826,1289 +826,1716 @@ if ('onhashchange' in window) { 763 e8/call clear-stream/disp32 764 # . . discard args 765 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 766 # (EAX..ECX) = "30" - 767 b8/copy-to-EAX "30"/imm32 - 768 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 769 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 - 770 05/add-to-EAX 4/imm32 - 771 # var slice/ECX = {EAX, ECX} - 772 51/push-ECX - 773 50/push-EAX - 774 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 775 # emit(_test-output-buffered-file, slice, 1) - 776 # . . push args - 777 68/push 1/imm32 - 778 51/push-ECX - 779 68/push _test-output-buffered-file/imm32 - 780 # . . call - 781 e8/call emit/disp32 - 782 # . . discard args - 783 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 784 # flush(_test-output-buffered-file) - 785 # . . push args - 786 68/push _test-output-buffered-file/imm32 - 787 # . . call - 788 e8/call flush/disp32 - 789 # . . discard args - 790 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 791 # check-stream-equal(_test-output-stream, "30 ", msg) + 766 # . clear-stream(_test-input-stream) + 767 # . . push args + 768 68/push _test-input-stream/imm32 + 769 # . . call + 770 e8/call clear-stream/disp32 + 771 # . . discard args + 772 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 773 # initialize input + 774 # . write(_test-input-stream, "abcd") + 775 # . . push args + 776 68/push "abcd"/imm32 + 777 68/push _test-input-stream/imm32 + 778 # . . call + 779 e8/call write/disp32 + 780 # . . discard args + 781 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 782 # write-stream-data(_test-output-buffered-file, _test-input-stream) + 783 # . . push args + 784 68/push _test-input-stream/imm32 + 785 68/push _test-output-buffered-file/imm32 + 786 # . . call + 787 e8/call write-stream-data/disp32 + 788 # . . discard args + 789 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 790 # check that the write happened as expected + 791 # . flush(_test-output-buffered-file) 792 # . . push args - 793 68/push "F - test-emit-number/1"/imm32 - 794 68/push "30 "/imm32 - 795 68/push _test-output-stream/imm32 - 796 # . . call - 797 e8/call check-stream-equal/disp32 - 798 # . . discard args - 799 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 800 # . epilog - 801 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 802 5d/pop-to-EBP - 803 c3/return - 804 - 805 test-emit-negative-number: - 806 # test support for sign-extending negative numbers - 807 # . prolog - 808 55/push-EBP - 809 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 810 # setup - 811 # . clear-stream(_test-output-stream) - 812 # . . push args - 813 68/push _test-output-stream/imm32 - 814 # . . call - 815 e8/call clear-stream/disp32 - 816 # . . discard args - 817 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 818 # . clear-stream(_test-output-buffered-file+4) - 819 # . . push args - 820 b8/copy-to-EAX _test-output-buffered-file/imm32 - 821 05/add-to-EAX 4/imm32 - 822 50/push-EAX - 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 # (EAX..ECX) = "-2" - 828 b8/copy-to-EAX "-2"/imm32 - 829 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 830 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 - 831 05/add-to-EAX 4/imm32 - 832 # var slice/ECX = {EAX, ECX} - 833 51/push-ECX - 834 50/push-EAX - 835 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 836 # emit(_test-output-buffered-file, slice, 2) - 837 # . . push args - 838 68/push 2/imm32 - 839 51/push-ECX - 840 68/push _test-output-buffered-file/imm32 - 841 # . . call - 842 e8/call emit/disp32 - 843 # . . discard args - 844 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 845 # flush(_test-output-buffered-file) - 846 # . . push args - 847 68/push _test-output-buffered-file/imm32 - 848 # . . call - 849 e8/call flush/disp32 - 850 # . . discard args - 851 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 852 # check-stream-equal(_test-output-stream, "fe ff ", msg) - 853 # . . push args - 854 68/push "F - test-emit-number/1"/imm32 - 855 68/push "fe ff "/imm32 - 856 68/push _test-output-stream/imm32 + 793 68/push _test-output-buffered-file/imm32 + 794 # . . call + 795 e8/call flush/disp32 + 796 # . . discard args + 797 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 798 # . check-stream-equal(_test-output-stream, "abcd", msg) + 799 # . . push args + 800 68/push "F - test-write-stream-data"/imm32 + 801 68/push "abcd"/imm32 + 802 68/push _test-output-stream/imm32 + 803 # . . call + 804 e8/call check-stream-equal/disp32 + 805 # . . discard args + 806 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 807 # . epilog + 808 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 809 5d/pop-to-EBP + 810 c3/return + 811 + 812 has-metadata?: # word : (address slice), s : (address string) -> EAX : boolean + 813 # pseudocode: + 814 # var twig : &slice = next-token-from-slice(word->start, word->end, '/') # skip name + 815 # curr = twig->end + 816 # while true + 817 # twig = next-token-from-slice(curr, word->end, '/') + 818 # if (twig.empty()) break + 819 # if (slice-equal?(twig, s)) return true + 820 # curr = twig->end + 821 # return false + 822 # . prolog + 823 55/push-EBP + 824 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 825 # . save registers + 826 51/push-ECX + 827 52/push-EDX + 828 56/push-ESI + 829 57/push-EDI + 830 # ESI = word + 831 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI + 832 # EDX = word->end + 833 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 2/r32/EDX 4/disp8 . # copy *(ESI+4) to EDX + 834 # var twig/EDI : (address slice) = {0, 0} + 835 68/push 0/imm32/end + 836 68/push 0/imm32/start + 837 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI + 838 # next-token-from-slice(word->start, word->end, '/', twig) + 839 # . . push args + 840 57/push-EDI + 841 68/push 0x2f/imm32/slash + 842 52/push-EDX + 843 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI + 844 # . . call + 845 e8/call next-token-from-slice/disp32 + 846 # . . discard args + 847 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 848 # curr/ECX = twig->end + 849 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX + 850 $has-metadata?:loop: + 851 # next-token-from-slice(curr, word->end, '/', twig) + 852 # . . push args + 853 57/push-EDI + 854 68/push 0x2f/imm32/slash + 855 52/push-EDX + 856 51/push-ECX 857 # . . call - 858 e8/call check-stream-equal/disp32 + 858 e8/call next-token-from-slice/disp32 859 # . . discard args - 860 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 861 # . epilog - 862 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 863 5d/pop-to-EBP - 864 c3/return - 865 - 866 test-emit-number-with-metadata: - 867 # . prolog - 868 55/push-EBP - 869 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 870 # setup - 871 # . clear-stream(_test-output-stream) - 872 # . . push args - 873 68/push _test-output-stream/imm32 - 874 # . . call - 875 e8/call clear-stream/disp32 - 876 # . . discard args - 877 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 878 # . clear-stream(_test-output-buffered-file+4) - 879 # . . push args - 880 b8/copy-to-EAX _test-output-buffered-file/imm32 - 881 05/add-to-EAX 4/imm32 - 882 50/push-EAX - 883 # . . call - 884 e8/call clear-stream/disp32 - 885 # . . discard args - 886 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 887 # (EAX..ECX) = "-2/foo" - 888 b8/copy-to-EAX "-2/foo"/imm32 - 889 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 890 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 - 891 05/add-to-EAX 4/imm32 - 892 # var slice/ECX = {EAX, ECX} - 893 51/push-ECX - 894 50/push-EAX - 895 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 896 # emit(_test-output-buffered-file, slice, 2) - 897 # . . push args - 898 68/push 2/imm32 - 899 51/push-ECX - 900 68/push _test-output-buffered-file/imm32 - 901 # . . call - 902 e8/call emit/disp32 - 903 # . . discard args - 904 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 905 # flush(_test-output-buffered-file) - 906 # . . push args - 907 68/push _test-output-buffered-file/imm32 - 908 # . . call - 909 e8/call flush/disp32 - 910 # . . discard args - 911 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 912 # the '/foo' will have no impact on the output - 913 # check-stream-equal(_test-output-stream, "fe ff ", msg) - 914 # . . push args - 915 68/push "F - test-emit-number-with-metadata"/imm32 - 916 68/push "fe ff "/imm32 - 917 68/push _test-output-stream/imm32 - 918 # . . call - 919 e8/call check-stream-equal/disp32 - 920 # . . discard args - 921 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 922 # . epilog - 923 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 924 5d/pop-to-EBP - 925 c3/return - 926 - 927 test-emit-non-number: - 928 # . prolog - 929 55/push-EBP - 930 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 931 # setup - 932 # . clear-stream(_test-output-stream) - 933 # . . push args - 934 68/push _test-output-stream/imm32 - 935 # . . call - 936 e8/call clear-stream/disp32 - 937 # . . discard args - 938 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 939 # . clear-stream(_test-output-buffered-file+4) - 940 # . . push args - 941 b8/copy-to-EAX _test-output-buffered-file/imm32 - 942 05/add-to-EAX 4/imm32 - 943 50/push-EAX - 944 # . . call - 945 e8/call clear-stream/disp32 - 946 # . . discard args - 947 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 948 # (EAX..ECX) = "xyz" - 949 b8/copy-to-EAX "xyz"/imm32 - 950 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX - 951 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 - 952 05/add-to-EAX 4/imm32 - 953 # var slice/ECX = {EAX, ECX} - 954 51/push-ECX - 955 50/push-EAX - 956 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX - 957 # emit(_test-output-buffered-file, slice, 2) - 958 # . . push args - 959 68/push 2/imm32 - 960 51/push-ECX - 961 68/push _test-output-buffered-file/imm32 - 962 # . . call - 963 e8/call emit/disp32 - 964 # . . discard args - 965 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 966 # flush(_test-output-buffered-file) - 967 # . . push args - 968 68/push _test-output-buffered-file/imm32 - 969 # . . call - 970 e8/call flush/disp32 - 971 # . . discard args - 972 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 973 # check-stream-equal(_test-output-stream, "xyz", msg) - 974 # . . push args - 975 68/push "F - test-emit-non-number"/imm32 - 976 68/push "xyz "/imm32 - 977 68/push _test-output-stream/imm32 - 978 # . . call - 979 e8/call check-stream-equal/disp32 - 980 # . . discard args - 981 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP - 982 # . epilog - 983 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP - 984 5d/pop-to-EBP - 985 c3/return - 986 - 987 test-emit-non-number-with-metadata: - 988 # . prolog - 989 55/push-EBP - 990 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP - 991 # setup - 992 # . clear-stream(_test-output-stream) - 993 # . . push args - 994 68/push _test-output-stream/imm32 - 995 # . . call - 996 e8/call clear-stream/disp32 - 997 # . . discard args - 998 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP - 999 # . clear-stream(_test-output-buffered-file+4) -1000 # . . push args -1001 b8/copy-to-EAX _test-output-buffered-file/imm32 -1002 05/add-to-EAX 4/imm32 -1003 50/push-EAX -1004 # . . call -1005 e8/call clear-stream/disp32 -1006 # . . discard args -1007 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1008 # (EAX..ECX) = "xyz/" -1009 b8/copy-to-EAX "xyz/"/imm32 -1010 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1011 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 -1012 05/add-to-EAX 4/imm32 -1013 # var slice/ECX = {EAX, ECX} -1014 51/push-ECX -1015 50/push-EAX -1016 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1017 # emit(_test-output-buffered-file, slice, 2) -1018 # . . push args -1019 68/push 2/imm32 + 860 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP + 861 # if (slice-empty?(twig)) return false + 862 # . EAX = slice-empty?(twig) + 863 # . . push args + 864 57/push-EDI + 865 # . . call + 866 e8/call slice-empty?/disp32 + 867 # . . discard args + 868 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP + 869 # . if (EAX != 0) return false + 870 3d/compare-EAX-and 0/imm32 + 871 75/jump-if-not-equal $has-metadata?:false/disp8 + 872 # if (slice-equal?(twig, s)) return true + 873 # . EAX = slice-equal?(twig, s) + 874 # . . push args + 875 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0xc/disp8 . # push *(EBP+12) + 876 57/push-EDI + 877 # . . call + 878 e8/call slice-equal?/disp32 + 879 # . . discard args + 880 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 881 # . if (EAX != 0) return true + 882 3d/compare-EAX-and 0/imm32 + 883 75/jump-if-not-equal $has-metadata?:true/disp8 + 884 # curr = twig->end + 885 8b/copy 1/mod/*+disp8 7/rm32/EDI . . . 1/r32/ECX 4/disp8 . # copy *(EDI+4) to ECX + 886 eb/jump $has-metadata?:loop/disp8 + 887 $has-metadata?:true: + 888 b8/copy-to-EAX 1/imm32/true + 889 eb/jump $has-metadata?:end/disp8 + 890 $has-metadata?:false: + 891 b8/copy-to-EAX 0/imm32/false + 892 $has-metadata?:end: + 893 # . reclaim locals + 894 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 895 # . restore registers + 896 5f/pop-to-EDI + 897 5e/pop-to-ESI + 898 5a/pop-to-EDX + 899 59/pop-to-ECX + 900 # . epilog + 901 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 902 5d/pop-to-EBP + 903 c3/return + 904 + 905 test-has-metadata-true: + 906 # . prolog + 907 55/push-EBP + 908 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 909 # (EAX..ECX) = "ab/imm32" + 910 b8/copy-to-EAX "ab/imm32"/imm32 + 911 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 912 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 + 913 05/add-to-EAX 4/imm32 + 914 # var in/ESI : (address slice) = {EAX, ECX} + 915 51/push-ECX + 916 50/push-EAX + 917 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI + 918 # EAX = has-metadata?(ESI, "imm32") + 919 # . . push args + 920 68/push "imm32"/imm32 + 921 56/push-ESI + 922 # . . call + 923 e8/call has-metadata?/disp32 + 924 # . . discard args + 925 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 926 # check-ints-equal(EAX, 1, msg) + 927 # . . push args + 928 68/push "F - test-has-metadata-true"/imm32 + 929 68/push 1/imm32/true + 930 50/push-EAX + 931 # . . call + 932 e8/call check-ints-equal/disp32 + 933 # . . discard args + 934 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 935 # . epilog + 936 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 937 5d/pop-to-EBP + 938 c3/return + 939 + 940 test-has-metadata-false: + 941 # . prolog + 942 55/push-EBP + 943 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 944 # (EAX..ECX) = "ab/c" + 945 b8/copy-to-EAX "ab/c"/imm32 + 946 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 947 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 + 948 05/add-to-EAX 4/imm32 + 949 # var in/ESI : (address slice) = {EAX, ECX} + 950 51/push-ECX + 951 50/push-EAX + 952 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI + 953 # EAX = has-metadata?(ESI, "d") + 954 # . . push args + 955 68/push "d"/imm32 + 956 56/push-ESI + 957 # . . call + 958 e8/call has-metadata?/disp32 + 959 # . . discard args + 960 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 961 # check-ints-equal(EAX, 0, msg) + 962 # . . push args + 963 68/push "F - test-has-metadata-false"/imm32 + 964 68/push 0/imm32/false + 965 50/push-EAX + 966 # . . call + 967 e8/call check-ints-equal/disp32 + 968 # . . discard args + 969 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP + 970 # . epilog + 971 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP + 972 5d/pop-to-EBP + 973 c3/return + 974 + 975 test-has-metadata-ignore-name: + 976 # . prolog + 977 55/push-EBP + 978 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP + 979 # (EAX..ECX) = "a/b" + 980 b8/copy-to-EAX "a/b"/imm32 + 981 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX + 982 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 + 983 05/add-to-EAX 4/imm32 + 984 # var in/ESI : (address slice) = {EAX, ECX} + 985 51/push-ECX + 986 50/push-EAX + 987 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI + 988 # EAX = has-metadata?(ESI, "a") + 989 # . . push args + 990 68/push "a"/imm32 + 991 56/push-ESI + 992 # . . call + 993 e8/call has-metadata?/disp32 + 994 # . . discard args + 995 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP + 996 # check-ints-equal(EAX, 0, msg) + 997 # . . push args + 998 68/push "F - test-has-metadata-ignore-name"/imm32 + 999 68/push 0/imm32/false +1000 50/push-EAX +1001 # . . call +1002 e8/call check-ints-equal/disp32 +1003 # . . discard args +1004 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1005 # . epilog +1006 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1007 5d/pop-to-EBP +1008 c3/return +1009 +1010 test-has-metadata-multiple-true: +1011 # . prolog +1012 55/push-EBP +1013 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1014 # (EAX..ECX) = "a/b/c" +1015 b8/copy-to-EAX "a/b/c"/imm32 +1016 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1017 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 +1018 05/add-to-EAX 4/imm32 +1019 # var in/ESI : (address slice) = {EAX, ECX} 1020 51/push-ECX -1021 68/push _test-output-buffered-file/imm32 -1022 # . . call -1023 e8/call emit/disp32 -1024 # . . discard args -1025 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1026 # flush(_test-output-buffered-file) -1027 # . . push args -1028 68/push _test-output-buffered-file/imm32 -1029 # . . call -1030 e8/call flush/disp32 -1031 # . . discard args -1032 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1033 # check-stream-equal(_test-output-stream, "xyz/", msg) -1034 # . . push args -1035 68/push "F - test-emit-non-number-with-metadata"/imm32 -1036 68/push "xyz/ "/imm32 -1037 68/push _test-output-stream/imm32 -1038 # . . call -1039 e8/call check-stream-equal/disp32 -1040 # . . discard args -1041 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1042 # . epilog -1043 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1044 5d/pop-to-EBP -1045 c3/return -1046 -1047 test-emit-non-number-with-all-hex-digits-and-metadata: -1048 # . prolog -1049 55/push-EBP -1050 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1051 # setup -1052 # . clear-stream(_test-output-stream) -1053 # . . push args -1054 68/push _test-output-stream/imm32 -1055 # . . call -1056 e8/call clear-stream/disp32 -1057 # . . discard args -1058 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1059 # . clear-stream(_test-output-buffered-file+4) -1060 # . . push args -1061 b8/copy-to-EAX _test-output-buffered-file/imm32 -1062 05/add-to-EAX 4/imm32 -1063 50/push-EAX -1064 # . . call -1065 e8/call clear-stream/disp32 -1066 # . . discard args -1067 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1068 # (EAX..ECX) = "abcd/xyz" -1069 b8/copy-to-EAX "abcd/xyz"/imm32 -1070 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1071 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 -1072 05/add-to-EAX 4/imm32 -1073 # var slice/ECX = {EAX, ECX} -1074 51/push-ECX -1075 50/push-EAX -1076 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1077 # emit(_test-output-buffered-file, slice, 2) -1078 # . . push args -1079 68/push 2/imm32 -1080 51/push-ECX -1081 68/push _test-output-buffered-file/imm32 -1082 # . . call -1083 e8/call emit/disp32 -1084 # . . discard args -1085 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1086 # flush(_test-output-buffered-file) -1087 # . . push args -1088 68/push _test-output-buffered-file/imm32 -1089 # . . call -1090 e8/call flush/disp32 -1091 # . . discard args -1092 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1093 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1119 # check-stream-equal(_test-output-stream, "abcd/xyz") -1120 # . . push args -1121 68/push "F - test-emit-non-number-with-all-hex-digits"/imm32 -1122 68/push "abcd/xyz "/imm32 -1123 68/push _test-output-stream/imm32 +1021 50/push-EAX +1022 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI +1023 # EAX = has-metadata?(ESI, "c") +1024 # . . push args +1025 68/push "c"/imm32 +1026 56/push-ESI +1027 # . . call +1028 e8/call has-metadata?/disp32 +1029 # . . discard args +1030 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1031 # check-ints-equal(EAX, 1, msg) +1032 # . . push args +1033 68/push "F - test-has-metadata-multiple-true"/imm32 +1034 68/push 1/imm32/true +1035 50/push-EAX +1036 # . . call +1037 e8/call check-ints-equal/disp32 +1038 # . . discard args +1039 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1040 # . epilog +1041 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1042 5d/pop-to-EBP +1043 c3/return +1044 +1045 test-has-metadata-multiple-false: +1046 # . prolog +1047 55/push-EBP +1048 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1049 # (EAX..ECX) = "a/b/c" +1050 b8/copy-to-EAX "a/b/c"/imm32 +1051 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1052 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 +1053 05/add-to-EAX 4/imm32 +1054 # var in/ESI : (address slice) = {EAX, ECX} +1055 51/push-ECX +1056 50/push-EAX +1057 89/copy 3/mod/direct 6/rm32/ESI . . . 4/r32/ESP . . # copy ESP to ESI +1058 # EAX = has-metadata?(ESI, "d") +1059 # . . push args +1060 68/push "d"/imm32 +1061 56/push-ESI +1062 # . . call +1063 e8/call has-metadata?/disp32 +1064 # . . discard args +1065 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1066 # check-ints-equal(EAX, 0, msg) +1067 # . . push args +1068 68/push "F - test-has-metadata-multiple-false"/imm32 +1069 68/push 0/imm32/false +1070 50/push-EAX +1071 # . . call +1072 e8/call check-ints-equal/disp32 +1073 # . . discard args +1074 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1075 # . epilog +1076 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1077 5d/pop-to-EBP +1078 c3/return +1079 +1080 # If datum of 'word' is not a valid name, it must be a hex int. Parse and print +1081 # it in 'width' bytes of hex, least significant first. +1082 # Otherwise just print the entire word including metadata. +1083 # Always print a trailing space. +1084 emit: # out : (address buffered-file), word : (address slice), width : int -> <void> +1085 # . prolog +1086 55/push-EBP +1087 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1088 # . save registers +1089 50/push-EAX +1090 56/push-ESI +1091 57/push-EDI +1092 # ESI = word +1093 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 0xc/disp8 . # copy *(EBP+12) to ESI +1094 # var name/EDI : (address slice) = {0, 0} +1095 68/push 0/imm32/end +1096 68/push 0/imm32/start +1097 89/copy 3/mod/direct 7/rm32/EDI . . . 4/r32/ESP . . # copy ESP to EDI +1098 # datum = next-token-from-slice(word->start, word->end, '/') +1099 # . . push args +1100 57/push-EDI +1101 68/push 0x2f/imm32/slash +1102 ff 6/subop/push 1/mod/*+disp8 6/rm32/ESI . . . . 4/disp8 . # push *(ESI+4) +1103 ff 6/subop/push 0/mod/indirect 6/rm32/ESI . . . . . . # push *ESI +1104 # . . call +1105 e8/call next-token-from-slice/disp32 +1106 # . . discard args +1107 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0x10/imm32 # add to ESP +1108 # if (is-valid-name?(datum)) write-slice-buffered(out, word) and return +1109 # . EAX = is-valid-name?(name) +1110 # . . push args +1111 57/push-EDI +1112 # . . call +1113 e8/call is-valid-name?/disp32 +1114 # . . discard args +1115 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1116 # . if (EAX != 0) +1117 3d/compare-EAX-and 0/imm32 +1118 74/jump-if-equal $emit:hex-int/disp8 +1119 $emit:name: +1120 # . write-slice-buffered(out, word) +1121 # . . push args +1122 56/push-ESI +1123 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) 1124 # . . call -1125 e8/call check-stream-equal/disp32 +1125 e8/call write-slice-buffered/disp32 1126 # . . discard args -1127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1128 # . epilog -1129 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1130 5d/pop-to-EBP -1131 c3/return -1132 -1133 # conditions for 'valid' names that are not at risk of looking like hex numbers -1134 # keep in sync with the rules in labels.cc -1135 #: - if it starts with a digit, it's treated as a number. If it can't be -1136 #: parsed as hex it will raise an error. -1137 #: - if it starts with '-' it's treated as a number. -1138 #: - if it starts with '0x' it's treated as a number. (redundant) -1139 #: - if it's two characters long, it can't be a name. Either it's a hex -1140 #: byte, or it raises an error. -1141 is-valid-name?: # in : (address slice) -> EAX : boolean -1142 # . prolog -1143 55/push-EBP -1144 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1145 # . save registers -1146 51/push-ECX -1147 56/push-ESI -1148 # ESI = in -1149 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI -1150 # start/ECX = in->start -1151 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX -1152 # end/EAX = in->end -1153 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX -1154 $is-valid-name?:check0: -1155 # if (start >= end) return false -1156 39/compare 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # compare ECX with EAX -1157 73/jump-if-greater-or-equal-unsigned $is-valid-name?:false/disp8 -1158 $is-valid-name?:check1: -1159 # EAX -= ECX -1160 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX -1161 # if (EAX == 2) return false -1162 3d/compare-EAX-and 2/imm32 -1163 74/jump-if-equal $is-valid-name?:false/disp8 -1164 $is-valid-name?:check2: -1165 # c/EAX = *ECX -1166 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -1167 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -1168 # if (c == "-") return false -1169 3d/compare-EAX-and 2d/imm32/- -1170 74/jump-if-equal $is-valid-name?:false/disp8 -1171 $is-valid-name?:check3a: -1172 # if (c < "0") return true -1173 3d/compare-EAX-with 30/imm32/0 -1174 7c/jump-if-lesser $is-valid-name?:true/disp8 -1175 $is-valid-name?:check3b: -1176 # if (c > "9") return true -1177 3d/compare-EAX-with 39/imm32/9 -1178 7f/jump-if-greater $is-valid-name?:true/disp8 -1179 $is-valid-name?:false: -1180 # return false -1181 b8/copy-to-EAX 0/imm32/false -1182 eb/jump $is-valid-name?:end/disp8 -1183 $is-valid-name?:true: -1184 # return true -1185 b8/copy-to-EAX 1/imm32/true -1186 $is-valid-name?:end: -1187 # . restore registers -1188 5e/pop-to-ESI -1189 59/pop-to-ECX -1190 # . epilog -1191 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1192 5d/pop-to-EBP -1193 c3/return -1194 -1195 test-is-valid-name-digit-prefix: -1196 # . prolog -1197 55/push-EBP -1198 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1199 # (EAX..ECX) = "34" -1200 b8/copy-to-EAX "34"/imm32 -1201 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1202 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 -1203 05/add-to-EAX 4/imm32 -1204 # var slice/ECX = {EAX, ECX} +1127 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1128 # . write-buffered(out, " ") +1129 # . . push args +1130 68/push " "/imm32 +1131 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1132 # . . call +1133 e8/call write-buffered/disp32 +1134 # . . discard args +1135 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1136 # . return +1137 eb/jump $emit:end/disp8 +1138 # otherwise emit-hex(out, parse-hex-int(datum), width) +1139 # (Weird shit can happen here if the datum of 'word' isn't either a valid +1140 # name or a hex number, but we're only going to be passing in real legal +1141 # programs. We just want to make sure that valid names aren't treated as +1142 # (valid) hex numbers.) +1143 $emit:hex-int: +1144 # . value/EAX = parse-hex-int(datum) +1145 # . . push args +1146 57/push-EDI +1147 # . . call +1148 e8/call parse-hex-int/disp32 +1149 # . . discard args +1150 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1151 # . emit-hex(out, value, width) +1152 # . . push args +1153 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 0x10/disp8 . # push *(EBP+16) +1154 50/push-EAX +1155 ff 6/subop/push 1/mod/*+disp8 5/rm32/EBP . . . . 8/disp8 . # push *(EBP+8) +1156 # . . call +1157 e8/call emit-hex/disp32 +1158 # . . discard args +1159 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1160 $emit:end: +1161 # . reclaim locals +1162 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1163 # . restore registers +1164 5f/pop-to-EDI +1165 5e/pop-to-ESI +1166 58/pop-to-EAX +1167 # . epilog +1168 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1169 5d/pop-to-EBP +1170 c3/return +1171 +1172 test-emit-number: +1173 # . prolog +1174 55/push-EBP +1175 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1176 # setup +1177 # . clear-stream(_test-output-stream) +1178 # . . push args +1179 68/push _test-output-stream/imm32 +1180 # . . call +1181 e8/call clear-stream/disp32 +1182 # . . discard args +1183 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1184 # . clear-stream(_test-output-buffered-file+4) +1185 # . . push args +1186 b8/copy-to-EAX _test-output-buffered-file/imm32 +1187 05/add-to-EAX 4/imm32 +1188 50/push-EAX +1189 # . . call +1190 e8/call clear-stream/disp32 +1191 # . . discard args +1192 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1193 # (EAX..ECX) = "30" +1194 b8/copy-to-EAX "30"/imm32 +1195 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1196 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 +1197 05/add-to-EAX 4/imm32 +1198 # var slice/ECX = {EAX, ECX} +1199 51/push-ECX +1200 50/push-EAX +1201 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1202 # emit(_test-output-buffered-file, slice, 1) +1203 # . . push args +1204 68/push 1/imm32 1205 51/push-ECX -1206 50/push-EAX -1207 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1208 # EAX = is-valid-name?(slice) -1209 # . . push args -1210 51/push-ECX -1211 # . . call -1212 e8/call is-valid-name?/disp32 -1213 # . . discard args -1214 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1215 # check-ints-equal(EAX, 0, msg) -1216 # . . push args -1217 68/push "F - test-is-valid-name-digit-prefix"/imm32 -1218 68/push 0/imm32/false -1219 50/push-EAX -1220 # . . call -1221 e8/call check-ints-equal/disp32 -1222 # . . discard args -1223 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1224 # . epilog -1225 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1226 5d/pop-to-EBP -1227 c3/return -1228 -1229 test-is-valid-name-negative-prefix: -1230 # . prolog -1231 55/push-EBP -1232 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1233 # (EAX..ECX) = "-0x34" -1234 b8/copy-to-EAX "-0x34"/imm32 -1235 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1236 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 -1237 05/add-to-EAX 4/imm32 -1238 # var slice/ECX = {EAX, ECX} -1239 51/push-ECX -1240 50/push-EAX -1241 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1242 # EAX = is-valid-name?(slice) -1243 # . . push args -1244 51/push-ECX -1245 # . . call -1246 e8/call is-valid-name?/disp32 -1247 # . . discard args -1248 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1249 # check-ints-equal(EAX, 0, msg) -1250 # . . push args -1251 68/push "F - test-is-valid-name-negative-prefix"/imm32 -1252 68/push 0/imm32/false -1253 50/push-EAX -1254 # . . call -1255 e8/call check-ints-equal/disp32 -1256 # . . discard args -1257 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1258 # . epilog -1259 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1260 5d/pop-to-EBP -1261 c3/return -1262 -1263 test-is-valid-name-0x-prefix: -1264 # . prolog -1265 55/push-EBP -1266 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1267 # (EAX..ECX) = "0x34" -1268 b8/copy-to-EAX "0x34"/imm32 -1269 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1270 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 -1271 05/add-to-EAX 4/imm32 -1272 # var slice/ECX = {EAX, ECX} -1273 51/push-ECX -1274 50/push-EAX -1275 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1276 # EAX = is-valid-name?(slice) -1277 # . . push args -1278 51/push-ECX -1279 # . . call -1280 e8/call is-valid-name?/disp32 -1281 # . . discard args -1282 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1283 # check-ints-equal(EAX, 0, msg) -1284 # . . push args -1285 68/push "F - test-is-valid-name-0x-prefix"/imm32 -1286 68/push 0/imm32/false -1287 50/push-EAX -1288 # . . call -1289 e8/call check-ints-equal/disp32 -1290 # . . discard args -1291 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1292 # . epilog -1293 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1294 5d/pop-to-EBP -1295 c3/return -1296 -1297 test-is-valid-name-starts-with-pre-digit: -1298 # . prolog -1299 55/push-EBP -1300 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1301 # (EAX..ECX) = "/03" -1302 b8/copy-to-EAX "/03"/imm32 -1303 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1304 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 -1305 05/add-to-EAX 4/imm32 -1306 # var slice/ECX = {EAX, ECX} -1307 51/push-ECX -1308 50/push-EAX -1309 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1310 # EAX = is-valid-name?(slice) -1311 # . . push args -1312 51/push-ECX -1313 # . . call -1314 e8/call is-valid-name?/disp32 -1315 # . . discard args -1316 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1317 # check-ints-equal(EAX, 1, msg) -1318 # . . push args -1319 68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32 -1320 68/push 1/imm32/true +1206 68/push _test-output-buffered-file/imm32 +1207 # . . call +1208 e8/call emit/disp32 +1209 # . . discard args +1210 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1211 # flush(_test-output-buffered-file) +1212 # . . push args +1213 68/push _test-output-buffered-file/imm32 +1214 # . . call +1215 e8/call flush/disp32 +1216 # . . discard args +1217 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1218 # check-stream-equal(_test-output-stream, "30 ", msg) +1219 # . . push args +1220 68/push "F - test-emit-number/1"/imm32 +1221 68/push "30 "/imm32 +1222 68/push _test-output-stream/imm32 +1223 # . . call +1224 e8/call check-stream-equal/disp32 +1225 # . . discard args +1226 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1227 # . epilog +1228 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1229 5d/pop-to-EBP +1230 c3/return +1231 +1232 test-emit-negative-number: +1233 # test support for sign-extending negative numbers +1234 # . prolog +1235 55/push-EBP +1236 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1237 # setup +1238 # . clear-stream(_test-output-stream) +1239 # . . push args +1240 68/push _test-output-stream/imm32 +1241 # . . call +1242 e8/call clear-stream/disp32 +1243 # . . discard args +1244 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1245 # . clear-stream(_test-output-buffered-file+4) +1246 # . . push args +1247 b8/copy-to-EAX _test-output-buffered-file/imm32 +1248 05/add-to-EAX 4/imm32 +1249 50/push-EAX +1250 # . . call +1251 e8/call clear-stream/disp32 +1252 # . . discard args +1253 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1254 # (EAX..ECX) = "-2" +1255 b8/copy-to-EAX "-2"/imm32 +1256 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1257 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 +1258 05/add-to-EAX 4/imm32 +1259 # var slice/ECX = {EAX, ECX} +1260 51/push-ECX +1261 50/push-EAX +1262 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1263 # emit(_test-output-buffered-file, slice, 2) +1264 # . . push args +1265 68/push 2/imm32 +1266 51/push-ECX +1267 68/push _test-output-buffered-file/imm32 +1268 # . . call +1269 e8/call emit/disp32 +1270 # . . discard args +1271 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1272 # flush(_test-output-buffered-file) +1273 # . . push args +1274 68/push _test-output-buffered-file/imm32 +1275 # . . call +1276 e8/call flush/disp32 +1277 # . . discard args +1278 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1279 # check-stream-equal(_test-output-stream, "fe ff ", msg) +1280 # . . push args +1281 68/push "F - test-emit-number/1"/imm32 +1282 68/push "fe ff "/imm32 +1283 68/push _test-output-stream/imm32 +1284 # . . call +1285 e8/call check-stream-equal/disp32 +1286 # . . discard args +1287 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1288 # . epilog +1289 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1290 5d/pop-to-EBP +1291 c3/return +1292 +1293 test-emit-number-with-metadata: +1294 # . prolog +1295 55/push-EBP +1296 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1297 # setup +1298 # . clear-stream(_test-output-stream) +1299 # . . push args +1300 68/push _test-output-stream/imm32 +1301 # . . call +1302 e8/call clear-stream/disp32 +1303 # . . discard args +1304 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1305 # . clear-stream(_test-output-buffered-file+4) +1306 # . . push args +1307 b8/copy-to-EAX _test-output-buffered-file/imm32 +1308 05/add-to-EAX 4/imm32 +1309 50/push-EAX +1310 # . . call +1311 e8/call clear-stream/disp32 +1312 # . . discard args +1313 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1314 # (EAX..ECX) = "-2/foo" +1315 b8/copy-to-EAX "-2/foo"/imm32 +1316 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1317 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 +1318 05/add-to-EAX 4/imm32 +1319 # var slice/ECX = {EAX, ECX} +1320 51/push-ECX 1321 50/push-EAX -1322 # . . call -1323 e8/call check-ints-equal/disp32 -1324 # . . discard args -1325 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1326 # . epilog -1327 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1328 5d/pop-to-EBP -1329 c3/return -1330 -1331 test-is-valid-name-starts-with-post-digit: -1332 # . prolog -1333 55/push-EBP -1334 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1335 # (EAX..ECX) = "q34" -1336 b8/copy-to-EAX "q34"/imm32 -1337 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1338 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 -1339 05/add-to-EAX 4/imm32 -1340 # var slice/ECX = {EAX, ECX} -1341 51/push-ECX -1342 50/push-EAX -1343 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1344 # EAX = is-valid-name?(slice) -1345 # . . push args -1346 51/push-ECX -1347 # . . call -1348 e8/call is-valid-name?/disp32 -1349 # . . discard args -1350 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1351 # check-ints-equal(EAX, 1, msg) -1352 # . . push args -1353 68/push "F - test-is-valid-name-starts-with-post-digit"/imm32 -1354 68/push 1/imm32/true -1355 50/push-EAX -1356 # . . call -1357 e8/call check-ints-equal/disp32 -1358 # . . discard args -1359 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1360 # . epilog -1361 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1362 5d/pop-to-EBP -1363 c3/return -1364 -1365 test-is-valid-name-starts-with-digit: -1366 # . prolog -1367 55/push-EBP -1368 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1369 # (EAX..ECX) = "0x34" -1370 b8/copy-to-EAX "0x34"/imm32 -1371 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1372 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 -1373 05/add-to-EAX 4/imm32 -1374 # var slice/ECX = {EAX, ECX} -1375 51/push-ECX -1376 50/push-EAX -1377 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1378 # EAX = is-valid-name?(slice) -1379 # . . push args -1380 51/push-ECX -1381 # . . call -1382 e8/call is-valid-name?/disp32 -1383 # . . discard args -1384 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1385 # check-ints-equal(EAX, 0, msg) -1386 # . . push args -1387 68/push "F - test-is-valid-name-starts-with-digit"/imm32 -1388 68/push 0/imm32/false -1389 50/push-EAX -1390 # . . call -1391 e8/call check-ints-equal/disp32 -1392 # . . discard args -1393 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1394 # . epilog -1395 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1396 5d/pop-to-EBP -1397 c3/return -1398 -1399 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte -1400 emit-hex: # out : (address buffered-file), n : int, width : int -> <void> -1401 # . prolog -1402 55/push-EBP -1403 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1404 # . save registers -1405 50/push-EAX -1406 51/push-ECX -1407 52/push-EDX -1408 53/push-EBX -1409 57/push-EDI -1410 # EDI = out -1411 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI -1412 # EBX = n -1413 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX -1414 # EDX = width -1415 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX -1416 # var curr/ECX = 0 -1417 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX -1418 $emit-hex:loop: -1419 # if (curr >= width) break -1420 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -1421 7d/jump-if-greater-or-equal $emit-hex:end/disp8 -1422 # print-byte-buffered(out, EBX) -1423 # . . push args -1424 53/push-EBX -1425 57/push-EDI -1426 # . . call -1427 e8/call print-byte-buffered/disp32 -1428 # . . discard args -1429 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1430 # write-byte-buffered(out, ' ') -1431 # . . push args -1432 68/push 0x20/imm32/space -1433 57/push-EDI -1434 # . . call -1435 e8/call write-byte-buffered/disp32 -1436 # . . discard args -1437 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1438 # EBX = EBX >> 8 -1439 c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/EBX . . . . . 8/imm8 # shift EBX right by 8 bits, while padding zeroes -1440 $emit-hex:continue: -1441 # ++curr -1442 41/increment-ECX -1443 eb/jump $emit-hex:loop/disp8 -1444 $emit-hex:end: -1445 # . restore registers -1446 5f/pop-to-EDI -1447 5b/pop-to-EBX -1448 5a/pop-to-EDX -1449 59/pop-to-ECX -1450 58/pop-to-EAX -1451 # . epilog -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-hex-single-byte: -1457 # setup -1458 # . clear-stream(_test-output-stream) -1459 # . . push args -1460 68/push _test-output-stream/imm32 -1461 # . . call -1462 e8/call clear-stream/disp32 -1463 # . . discard args -1464 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1465 # . clear-stream(_test-output-buffered-file+4) -1466 # . . push args -1467 b8/copy-to-EAX _test-output-buffered-file/imm32 -1468 05/add-to-EAX 4/imm32 -1469 50/push-EAX -1470 # . . call -1471 e8/call clear-stream/disp32 -1472 # . . discard args -1473 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1474 # emit-hex(_test-output-buffered-file, 0xab, 1) -1475 # . . push args -1476 68/push 1/imm32 -1477 68/push 0xab/imm32 -1478 68/push _test-output-buffered-file/imm32 -1479 # . . call -1480 e8/call emit-hex/disp32 -1481 # . . discard args -1482 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1483 # flush(_test-output-buffered-file) -1484 # . . push args -1485 68/push _test-output-buffered-file/imm32 -1486 # . . call -1487 e8/call flush/disp32 -1488 # . . discard args -1489 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1490 # check-ints-equal(*_test-output-stream->data, 'ab ', msg) -1491 # . . push args -1492 68/push "F - test-emit-hex-single-byte"/imm32 -1493 68/push 0x206261/imm32 -1494 # . . push *_test-output-stream->data -1495 b8/copy-to-EAX _test-output-stream/imm32 -1496 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) -1497 # . . call -1498 e8/call check-ints-equal/disp32 -1499 # . . discard args -1500 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1501 # . end -1502 c3/return -1503 -1504 test-emit-hex-multiple-byte: -1505 # setup -1506 # . clear-stream(_test-output-stream) -1507 # . . push args -1508 68/push _test-output-stream/imm32 +1322 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1323 # emit(_test-output-buffered-file, slice, 2) +1324 # . . push args +1325 68/push 2/imm32 +1326 51/push-ECX +1327 68/push _test-output-buffered-file/imm32 +1328 # . . call +1329 e8/call emit/disp32 +1330 # . . discard args +1331 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1332 # flush(_test-output-buffered-file) +1333 # . . push args +1334 68/push _test-output-buffered-file/imm32 +1335 # . . call +1336 e8/call flush/disp32 +1337 # . . discard args +1338 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1339 # the '/foo' will have no impact on the output +1340 # check-stream-equal(_test-output-stream, "fe ff ", msg) +1341 # . . push args +1342 68/push "F - test-emit-number-with-metadata"/imm32 +1343 68/push "fe ff "/imm32 +1344 68/push _test-output-stream/imm32 +1345 # . . call +1346 e8/call check-stream-equal/disp32 +1347 # . . discard args +1348 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1349 # . epilog +1350 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1351 5d/pop-to-EBP +1352 c3/return +1353 +1354 test-emit-non-number: +1355 # . prolog +1356 55/push-EBP +1357 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1358 # setup +1359 # . clear-stream(_test-output-stream) +1360 # . . push args +1361 68/push _test-output-stream/imm32 +1362 # . . call +1363 e8/call clear-stream/disp32 +1364 # . . discard args +1365 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1366 # . clear-stream(_test-output-buffered-file+4) +1367 # . . push args +1368 b8/copy-to-EAX _test-output-buffered-file/imm32 +1369 05/add-to-EAX 4/imm32 +1370 50/push-EAX +1371 # . . call +1372 e8/call clear-stream/disp32 +1373 # . . discard args +1374 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1375 # (EAX..ECX) = "xyz" +1376 b8/copy-to-EAX "xyz"/imm32 +1377 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1378 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 +1379 05/add-to-EAX 4/imm32 +1380 # var slice/ECX = {EAX, ECX} +1381 51/push-ECX +1382 50/push-EAX +1383 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1384 # emit(_test-output-buffered-file, slice, 2) +1385 # . . push args +1386 68/push 2/imm32 +1387 51/push-ECX +1388 68/push _test-output-buffered-file/imm32 +1389 # . . call +1390 e8/call emit/disp32 +1391 # . . discard args +1392 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1393 # flush(_test-output-buffered-file) +1394 # . . push args +1395 68/push _test-output-buffered-file/imm32 +1396 # . . call +1397 e8/call flush/disp32 +1398 # . . discard args +1399 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1400 # check-stream-equal(_test-output-stream, "xyz", msg) +1401 # . . push args +1402 68/push "F - test-emit-non-number"/imm32 +1403 68/push "xyz "/imm32 +1404 68/push _test-output-stream/imm32 +1405 # . . call +1406 e8/call check-stream-equal/disp32 +1407 # . . discard args +1408 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1409 # . epilog +1410 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1411 5d/pop-to-EBP +1412 c3/return +1413 +1414 test-emit-non-number-with-metadata: +1415 # . prolog +1416 55/push-EBP +1417 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1418 # setup +1419 # . clear-stream(_test-output-stream) +1420 # . . push args +1421 68/push _test-output-stream/imm32 +1422 # . . call +1423 e8/call clear-stream/disp32 +1424 # . . discard args +1425 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1426 # . clear-stream(_test-output-buffered-file+4) +1427 # . . push args +1428 b8/copy-to-EAX _test-output-buffered-file/imm32 +1429 05/add-to-EAX 4/imm32 +1430 50/push-EAX +1431 # . . call +1432 e8/call clear-stream/disp32 +1433 # . . discard args +1434 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1435 # (EAX..ECX) = "xyz/" +1436 b8/copy-to-EAX "xyz/"/imm32 +1437 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1438 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 +1439 05/add-to-EAX 4/imm32 +1440 # var slice/ECX = {EAX, ECX} +1441 51/push-ECX +1442 50/push-EAX +1443 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1444 # emit(_test-output-buffered-file, slice, 2) +1445 # . . push args +1446 68/push 2/imm32 +1447 51/push-ECX +1448 68/push _test-output-buffered-file/imm32 +1449 # . . call +1450 e8/call emit/disp32 +1451 # . . discard args +1452 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1453 # flush(_test-output-buffered-file) +1454 # . . push args +1455 68/push _test-output-buffered-file/imm32 +1456 # . . call +1457 e8/call flush/disp32 +1458 # . . discard args +1459 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1460 # check-stream-equal(_test-output-stream, "xyz/", msg) +1461 # . . push args +1462 68/push "F - test-emit-non-number-with-metadata"/imm32 +1463 68/push "xyz/ "/imm32 +1464 68/push _test-output-stream/imm32 +1465 # . . call +1466 e8/call check-stream-equal/disp32 +1467 # . . discard args +1468 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1469 # . epilog +1470 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1471 5d/pop-to-EBP +1472 c3/return +1473 +1474 test-emit-non-number-with-all-hex-digits-and-metadata: +1475 # . prolog +1476 55/push-EBP +1477 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1478 # setup +1479 # . clear-stream(_test-output-stream) +1480 # . . push args +1481 68/push _test-output-stream/imm32 +1482 # . . call +1483 e8/call clear-stream/disp32 +1484 # . . discard args +1485 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1486 # . clear-stream(_test-output-buffered-file+4) +1487 # . . push args +1488 b8/copy-to-EAX _test-output-buffered-file/imm32 +1489 05/add-to-EAX 4/imm32 +1490 50/push-EAX +1491 # . . call +1492 e8/call clear-stream/disp32 +1493 # . . discard args +1494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1495 # (EAX..ECX) = "abcd/xyz" +1496 b8/copy-to-EAX "abcd/xyz"/imm32 +1497 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1498 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 +1499 05/add-to-EAX 4/imm32 +1500 # var slice/ECX = {EAX, ECX} +1501 51/push-ECX +1502 50/push-EAX +1503 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1504 # emit(_test-output-buffered-file, slice, 2) +1505 # . . push args +1506 68/push 2/imm32 +1507 51/push-ECX +1508 68/push _test-output-buffered-file/imm32 1509 # . . call -1510 e8/call clear-stream/disp32 +1510 e8/call emit/disp32 1511 # . . discard args -1512 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1513 # . clear-stream(_test-output-buffered-file+4) +1512 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1513 # flush(_test-output-buffered-file) 1514 # . . push args -1515 b8/copy-to-EAX _test-output-buffered-file/imm32 -1516 05/add-to-EAX 4/imm32 -1517 50/push-EAX -1518 # . . call -1519 e8/call clear-stream/disp32 -1520 # . . discard args -1521 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1522 # emit-hex(_test-output-buffered-file, 0x1234, 2) -1523 # . . push args -1524 68/push 2/imm32 -1525 68/push 0x1234/imm32 -1526 68/push _test-output-buffered-file/imm32 -1527 # . . call -1528 e8/call emit-hex/disp32 -1529 # . . discard args -1530 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1531 # flush(_test-output-buffered-file) -1532 # . . push args -1533 68/push _test-output-buffered-file/imm32 -1534 # . . call -1535 e8/call flush/disp32 -1536 # . . discard args -1537 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1538 # check-stream-equal(_test-output-stream, "34 12 ", msg) -1539 # . . push args -1540 68/push "F - test-emit-hex-multiple-byte/1"/imm32 -1541 68/push "34 12 "/imm32 -1542 68/push _test-output-stream/imm32 -1543 # . . call -1544 e8/call check-stream-equal/disp32 -1545 # . . discard args -1546 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1547 # . end -1548 c3/return -1549 -1550 test-emit-hex-zero-pad: -1551 # setup -1552 # . clear-stream(_test-output-stream) -1553 # . . push args -1554 68/push _test-output-stream/imm32 -1555 # . . call -1556 e8/call clear-stream/disp32 -1557 # . . discard args -1558 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1559 # . clear-stream(_test-output-buffered-file+4) -1560 # . . push args -1561 b8/copy-to-EAX _test-output-buffered-file/imm32 -1562 05/add-to-EAX 4/imm32 -1563 50/push-EAX -1564 # . . call -1565 e8/call clear-stream/disp32 -1566 # . . discard args -1567 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1568 # emit-hex(_test-output-buffered-file, 0xab, 2) -1569 # . . push args -1570 68/push 2/imm32 -1571 68/push 0xab/imm32 -1572 68/push _test-output-buffered-file/imm32 -1573 # . . call -1574 e8/call emit-hex/disp32 -1575 # . . discard args -1576 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1577 # flush(_test-output-buffered-file) -1578 # . . push args -1579 68/push _test-output-buffered-file/imm32 -1580 # . . call -1581 e8/call flush/disp32 -1582 # . . discard args -1583 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1584 # check(_test-output-stream->data == 'ab 00 ') -1585 # . . push args -1586 68/push "F - test-emit-hex-zero-pad/1"/imm32 -1587 68/push "ab 00 "/imm32 -1588 68/push _test-output-stream/imm32 -1589 # . . call -1590 e8/call check-stream-equal/disp32 -1591 # . . discard args -1592 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1593 # . end -1594 c3/return -1595 -1596 test-emit-hex-negative: -1597 # setup -1598 # . clear-stream(_test-output-stream) -1599 # . . push args -1600 68/push _test-output-stream/imm32 -1601 # . . call -1602 e8/call clear-stream/disp32 -1603 # . . discard args -1604 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1605 # . clear-stream(_test-output-buffered-file+4) -1606 # . . push args -1607 b8/copy-to-EAX _test-output-buffered-file/imm32 -1608 05/add-to-EAX 4/imm32 -1609 50/push-EAX -1610 # . . call -1611 e8/call clear-stream/disp32 -1612 # . . discard args -1613 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1614 # emit-hex(_test-output-buffered-file, -1, 2) -1615 # . . push args -1616 68/push 2/imm32 -1617 68/push -1/imm32 -1618 68/push _test-output-buffered-file/imm32 -1619 # . . call -1620 e8/call emit-hex/disp32 -1621 # . . discard args -1622 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1623 # flush(_test-output-buffered-file) -1624 # . . push args -1625 68/push _test-output-buffered-file/imm32 -1626 # . . call -1627 e8/call flush/disp32 -1628 # . . discard args -1629 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1630 # check-stream-equal(_test-output-stream == "ff ff ") -1631 # . . push args -1632 68/push "F - test-emit-hex-negative/1"/imm32 -1633 68/push "ff ff "/imm32 -1634 68/push _test-output-stream/imm32 -1635 # . . call -1636 e8/call check-stream-equal/disp32 -1637 # . . discard args -1638 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1639 # . end -1640 c3/return -1641 -1642 # print 'arr' in hex with a space after every byte -1643 emit-hex-array: # out : (address buffered-file), arr : (address array byte) -> <void> -1644 # . prolog -1645 55/push-EBP -1646 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1647 # . save registers -1648 50/push-EAX -1649 51/push-ECX -1650 52/push-EDX -1651 57/push-EDI -1652 # EDI = out -1653 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI -1654 # EDX = arr # <== 0xbdffffe4 -1655 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX -1656 # curr/ECX = arr->data -1657 8d/copy-address 1/mod/*+disp8 2/rm32/EDX . . . 1/r32/ECX 4/disp8 . # copy EDX+4 to ECX -1658 # max/EDX = arr->data + arr->length -1659 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX -1660 01/add 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # add ECX to EDX -1661 # EAX = 0 -1662 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -1663 $emit-hex-array:loop: -1664 # if (curr >= width) break -1665 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX -1666 73/jump-if-greater-or-equal-unsigned $emit-hex-array:end/disp8 -1667 # emit-hex(out, *curr, width=1) -1668 # . . push args -1669 68/push 1/imm32/width -1670 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL -1671 50/push-EAX -1672 57/push-EDI -1673 # . . call -1674 e8/call emit-hex/disp32 -1675 # . . discard args -1676 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1677 # ++curr -1678 41/increment-ECX -1679 eb/jump $emit-hex-array:loop/disp8 -1680 $emit-hex-array:end: -1681 # . restore registers -1682 5f/pop-to-EDI -1683 5a/pop-to-EDX -1684 59/pop-to-ECX -1685 58/pop-to-EAX -1686 # . epilog -1687 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1688 5d/pop-to-EBP -1689 c3/return -1690 -1691 test-emit-hex-array: -1692 # . prolog -1693 55/push-EBP -1694 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1695 # setup -1696 # . clear-stream(_test-output-stream) -1697 # . . push args -1698 68/push _test-output-stream/imm32 -1699 # . . call -1700 e8/call clear-stream/disp32 -1701 # . . discard args -1702 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1703 # . clear-stream(_test-output-buffered-file+4) +1515 68/push _test-output-buffered-file/imm32 +1516 # . . call +1517 e8/call flush/disp32 +1518 # . . discard args +1519 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1520 +-- 26 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +1546 # check-stream-equal(_test-output-stream, "abcd/xyz") +1547 # . . push args +1548 68/push "F - test-emit-non-number-with-all-hex-digits"/imm32 +1549 68/push "abcd/xyz "/imm32 +1550 68/push _test-output-stream/imm32 +1551 # . . call +1552 e8/call check-stream-equal/disp32 +1553 # . . discard args +1554 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1555 # . epilog +1556 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1557 5d/pop-to-EBP +1558 c3/return +1559 +1560 # conditions for 'valid' names that are not at risk of looking like hex numbers +1561 # keep in sync with the rules in labels.cc +1562 #: - if it starts with a digit, it's treated as a number. If it can't be +1563 #: parsed as hex it will raise an error. +1564 #: - if it starts with '-' it's treated as a number. +1565 #: - if it starts with '0x' it's treated as a number. (redundant) +1566 #: - if it's two characters long, it can't be a name. Either it's a hex +1567 #: byte, or it raises an error. +1568 is-valid-name?: # in : (address slice) -> EAX : boolean +1569 # . prolog +1570 55/push-EBP +1571 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1572 # . save registers +1573 51/push-ECX +1574 56/push-ESI +1575 # ESI = in +1576 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 6/r32/ESI 8/disp8 . # copy *(EBP+8) to ESI +1577 # start/ECX = in->start +1578 8b/copy 0/mod/indirect 6/rm32/ESI . . . 1/r32/ECX . . # copy *ESI to ECX +1579 # end/EAX = in->end +1580 8b/copy 1/mod/*+disp8 6/rm32/ESI . . . 0/r32/EAX 4/disp8 . # copy *(ESI+4) to EAX +1581 $is-valid-name?:check0: +1582 # if (start >= end) return false +1583 39/compare 3/mod/direct 1/rm32/ECX . . . 0/r32/EAX . . # compare ECX with EAX +1584 73/jump-if-greater-or-equal-unsigned $is-valid-name?:false/disp8 +1585 $is-valid-name?:check1: +1586 # EAX -= ECX +1587 29/subtract 3/mod/direct 0/rm32/EAX . . . 1/r32/ECX . . # subtract ECX from EAX +1588 # if (EAX == 2) return false +1589 3d/compare-EAX-and 2/imm32 +1590 74/jump-if-equal $is-valid-name?:false/disp8 +1591 $is-valid-name?:check2: +1592 # c/EAX = *ECX +1593 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +1594 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +1595 # if (c == "-") return false +1596 3d/compare-EAX-and 2d/imm32/- +1597 74/jump-if-equal $is-valid-name?:false/disp8 +1598 $is-valid-name?:check3a: +1599 # if (c < "0") return true +1600 3d/compare-EAX-with 30/imm32/0 +1601 7c/jump-if-lesser $is-valid-name?:true/disp8 +1602 $is-valid-name?:check3b: +1603 # if (c > "9") return true +1604 3d/compare-EAX-with 39/imm32/9 +1605 7f/jump-if-greater $is-valid-name?:true/disp8 +1606 $is-valid-name?:false: +1607 # return false +1608 b8/copy-to-EAX 0/imm32/false +1609 eb/jump $is-valid-name?:end/disp8 +1610 $is-valid-name?:true: +1611 # return true +1612 b8/copy-to-EAX 1/imm32/true +1613 $is-valid-name?:end: +1614 # . restore registers +1615 5e/pop-to-ESI +1616 59/pop-to-ECX +1617 # . epilog +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 test-is-valid-name-digit-prefix: +1623 # . prolog +1624 55/push-EBP +1625 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1626 # (EAX..ECX) = "34" +1627 b8/copy-to-EAX "34"/imm32 +1628 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1629 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 +1630 05/add-to-EAX 4/imm32 +1631 # var slice/ECX = {EAX, ECX} +1632 51/push-ECX +1633 50/push-EAX +1634 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1635 # EAX = is-valid-name?(slice) +1636 # . . push args +1637 51/push-ECX +1638 # . . call +1639 e8/call is-valid-name?/disp32 +1640 # . . discard args +1641 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1642 # check-ints-equal(EAX, 0, msg) +1643 # . . push args +1644 68/push "F - test-is-valid-name-digit-prefix"/imm32 +1645 68/push 0/imm32/false +1646 50/push-EAX +1647 # . . call +1648 e8/call check-ints-equal/disp32 +1649 # . . discard args +1650 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1651 # . epilog +1652 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1653 5d/pop-to-EBP +1654 c3/return +1655 +1656 test-is-valid-name-negative-prefix: +1657 # . prolog +1658 55/push-EBP +1659 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1660 # (EAX..ECX) = "-0x34" +1661 b8/copy-to-EAX "-0x34"/imm32 +1662 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1663 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 +1664 05/add-to-EAX 4/imm32 +1665 # var slice/ECX = {EAX, ECX} +1666 51/push-ECX +1667 50/push-EAX +1668 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1669 # EAX = is-valid-name?(slice) +1670 # . . push args +1671 51/push-ECX +1672 # . . call +1673 e8/call is-valid-name?/disp32 +1674 # . . discard args +1675 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1676 # check-ints-equal(EAX, 0, msg) +1677 # . . push args +1678 68/push "F - test-is-valid-name-negative-prefix"/imm32 +1679 68/push 0/imm32/false +1680 50/push-EAX +1681 # . . call +1682 e8/call check-ints-equal/disp32 +1683 # . . discard args +1684 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1685 # . epilog +1686 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1687 5d/pop-to-EBP +1688 c3/return +1689 +1690 test-is-valid-name-0x-prefix: +1691 # . prolog +1692 55/push-EBP +1693 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1694 # (EAX..ECX) = "0x34" +1695 b8/copy-to-EAX "0x34"/imm32 +1696 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1697 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 +1698 05/add-to-EAX 4/imm32 +1699 # var slice/ECX = {EAX, ECX} +1700 51/push-ECX +1701 50/push-EAX +1702 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1703 # EAX = is-valid-name?(slice) 1704 # . . push args -1705 b8/copy-to-EAX _test-output-buffered-file/imm32 -1706 05/add-to-EAX 4/imm32 -1707 50/push-EAX -1708 # . . call -1709 e8/call clear-stream/disp32 -1710 # . . discard args -1711 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1712 # var arr/ECX (address array byte) = [01, 02, 03] -1713 68/push 0x00030201/imm32 # bytes 01 02 03 -1714 68/push 3/imm32/length -1715 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1716 # emit-hex-array(_test-output-buffered-file, arr) -1717 # . . push args -1718 51/push-ECX -1719 68/push _test-output-buffered-file/imm32 -1720 # . . call -1721 e8/call emit-hex-array/disp32 -1722 # . . discard args -1723 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1724 # . flush(_test-output-buffered-file) -1725 # . . push args -1726 68/push _test-output-buffered-file/imm32 -1727 # . . call -1728 e8/call flush/disp32 -1729 # . . discard args -1730 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1731 +-- 33 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- -1764 # check-next-stream-line-equal(_test-output-stream, "01 02 03 ", msg) -1765 # . . push args -1766 68/push "F - test-emit-hex-array"/imm32 -1767 68/push "01 02 03 "/imm32 -1768 68/push _test-output-stream/imm32 -1769 # . . call -1770 e8/call check-next-stream-line-equal/disp32 -1771 # . . discard args -1772 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1773 # . epilog -1774 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1775 5d/pop-to-EBP -1776 c3/return -1777 -1778 compute-width: # word : (address array byte) -> EAX : int -1779 # . prolog -1780 55/push-EBP -1781 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1782 # . save registers -1783 51/push-ECX -1784 # EAX = word -1785 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to ECX -1786 # ECX = word + word->length -1787 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -1788 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 -1789 # EAX = word->data -1790 05/add-to-EAX 4/imm32 -1791 # var in/ECX : (address slice) = {EAX, ECX} -1792 51/push-ECX -1793 50/push-EAX -1794 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -1795 # return compute-width-of-slice(ECX) -1796 # . . push args -1797 51/push-ECX -1798 # . . call -1799 e8/call compute-width-of-slice/disp32 -1800 # . . discard args -1801 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1802 $compute-width:end: -1803 # . reclaim locals -1804 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1805 # . restore registers -1806 59/pop-to-ECX -1807 # . epilog -1808 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1809 5d/pop-to-EBP -1810 c3/return -1811 -1812 compute-width-of-slice: # s : (address slice) -> EAX : int -1813 # . prolog -1814 55/push-EBP -1815 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1816 # . save registers -1817 51/push-ECX -1818 # ECX = s -1819 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -1820 # if (has-metadata?(word, "imm32")) return 4 -1821 # . EAX = has-metadata?(word, "imm32") -1822 # . . push args -1823 68/push "imm32"/imm32 -1824 51/push-ECX -1825 # . . call -1826 e8/call has-metadata?/disp32 -1827 # . . discard args -1828 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1829 # . if (EAX != 0) return 4 -1830 3d/compare-EAX-and 0/imm32 -1831 b8/copy-to-EAX 4/imm32 # ZF is set, so we can overwrite EAX now -1832 75/jump-if-not-equal $compute-width-of-slice:end/disp8 -1833 # if (has-metadata?(word, "disp32")) return 4 -1834 # . EAX = has-metadata?(word, "disp32") -1835 # . . push args -1836 68/push "disp32"/imm32 -1837 51/push-ECX -1838 # . . call -1839 e8/call has-metadata?/disp32 -1840 # . . discard args -1841 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1842 # . if (EAX != 0) return 4 -1843 3d/compare-EAX-and 0/imm32 -1844 b8/copy-to-EAX 4/imm32 # ZF is set, so we can overwrite EAX now -1845 75/jump-if-not-equal $compute-width-of-slice:end/disp8 -1846 # if (has-metadata?(word, "imm16")) return 2 -1847 # . EAX = has-metadata?(word, "imm16") -1848 # . . push args -1849 68/push "imm16"/imm32 -1850 51/push-ECX -1851 # . . call -1852 e8/call has-metadata?/disp32 -1853 # . . discard args -1854 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1855 # . if (EAX != 0) return 2 -1856 3d/compare-EAX-and 0/imm32 -1857 b8/copy-to-EAX 2/imm32 # ZF is set, so we can overwrite EAX now -1858 75/jump-if-not-equal $compute-width-of-slice:end/disp8 -1859 # if (has-metadata?(word, "disp16")) return 2 -1860 # . EAX = has-metadata?(word, "disp16") -1861 # . . push args -1862 68/push "disp16"/imm32 -1863 51/push-ECX -1864 # . . call -1865 e8/call has-metadata?/disp32 -1866 # . . discard args -1867 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP -1868 # . if (EAX != 0) return 2 -1869 3d/compare-EAX-and 0/imm32 -1870 b8/copy-to-EAX 2/imm32 # ZF is set, so we can overwrite EAX now -1871 75/jump-if-not-equal $compute-width-of-slice:end/disp8 -1872 # otherwise return 1 -1873 b8/copy-to-EAX 1/imm32 -1874 $compute-width-of-slice:end: -1875 # . restore registers +1705 51/push-ECX +1706 # . . call +1707 e8/call is-valid-name?/disp32 +1708 # . . discard args +1709 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1710 # check-ints-equal(EAX, 0, msg) +1711 # . . push args +1712 68/push "F - test-is-valid-name-0x-prefix"/imm32 +1713 68/push 0/imm32/false +1714 50/push-EAX +1715 # . . call +1716 e8/call check-ints-equal/disp32 +1717 # . . discard args +1718 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1719 # . epilog +1720 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1721 5d/pop-to-EBP +1722 c3/return +1723 +1724 test-is-valid-name-starts-with-pre-digit: +1725 # . prolog +1726 55/push-EBP +1727 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1728 # (EAX..ECX) = "/03" +1729 b8/copy-to-EAX "/03"/imm32 +1730 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1731 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 +1732 05/add-to-EAX 4/imm32 +1733 # var slice/ECX = {EAX, ECX} +1734 51/push-ECX +1735 50/push-EAX +1736 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1737 # EAX = is-valid-name?(slice) +1738 # . . push args +1739 51/push-ECX +1740 # . . call +1741 e8/call is-valid-name?/disp32 +1742 # . . discard args +1743 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1744 # check-ints-equal(EAX, 1, msg) +1745 # . . push args +1746 68/push "F - test-is-valid-name-starts-with-pre-digit"/imm32 +1747 68/push 1/imm32/true +1748 50/push-EAX +1749 # . . call +1750 e8/call check-ints-equal/disp32 +1751 # . . discard args +1752 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1753 # . epilog +1754 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1755 5d/pop-to-EBP +1756 c3/return +1757 +1758 test-is-valid-name-starts-with-post-digit: +1759 # . prolog +1760 55/push-EBP +1761 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1762 # (EAX..ECX) = "q34" +1763 b8/copy-to-EAX "q34"/imm32 +1764 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1765 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 +1766 05/add-to-EAX 4/imm32 +1767 # var slice/ECX = {EAX, ECX} +1768 51/push-ECX +1769 50/push-EAX +1770 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1771 # EAX = is-valid-name?(slice) +1772 # . . push args +1773 51/push-ECX +1774 # . . call +1775 e8/call is-valid-name?/disp32 +1776 # . . discard args +1777 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1778 # check-ints-equal(EAX, 1, msg) +1779 # . . push args +1780 68/push "F - test-is-valid-name-starts-with-post-digit"/imm32 +1781 68/push 1/imm32/true +1782 50/push-EAX +1783 # . . call +1784 e8/call check-ints-equal/disp32 +1785 # . . discard args +1786 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1787 # . epilog +1788 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1789 5d/pop-to-EBP +1790 c3/return +1791 +1792 test-is-valid-name-starts-with-digit: +1793 # . prolog +1794 55/push-EBP +1795 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1796 # (EAX..ECX) = "0x34" +1797 b8/copy-to-EAX "0x34"/imm32 +1798 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +1799 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 +1800 05/add-to-EAX 4/imm32 +1801 # var slice/ECX = {EAX, ECX} +1802 51/push-ECX +1803 50/push-EAX +1804 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +1805 # EAX = is-valid-name?(slice) +1806 # . . push args +1807 51/push-ECX +1808 # . . call +1809 e8/call is-valid-name?/disp32 +1810 # . . discard args +1811 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1812 # check-ints-equal(EAX, 0, msg) +1813 # . . push args +1814 68/push "F - test-is-valid-name-starts-with-digit"/imm32 +1815 68/push 0/imm32/false +1816 50/push-EAX +1817 # . . call +1818 e8/call check-ints-equal/disp32 +1819 # . . discard args +1820 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1821 # . epilog +1822 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1823 5d/pop-to-EBP +1824 c3/return +1825 +1826 # print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte +1827 emit-hex: # out : (address buffered-file), n : int, width : int -> <void> +1828 # . prolog +1829 55/push-EBP +1830 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +1831 # . save registers +1832 50/push-EAX +1833 51/push-ECX +1834 52/push-EDX +1835 53/push-EBX +1836 57/push-EDI +1837 # EDI = out +1838 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI +1839 # EBX = n +1840 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 3/r32/EBX 0xc/disp8 . # copy *(EBP+12) to EBX +1841 # EDX = width +1842 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0x10/disp8 . # copy *(EBP+16) to EDX +1843 # var curr/ECX = 0 +1844 31/xor 3/mod/direct 1/rm32/ECX . . . 1/r32/ECX . . # clear ECX +1845 $emit-hex:loop: +1846 # if (curr >= width) break +1847 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX +1848 7d/jump-if-greater-or-equal $emit-hex:end/disp8 +1849 # print-byte-buffered(out, EBX) +1850 # . . push args +1851 53/push-EBX +1852 57/push-EDI +1853 # . . call +1854 e8/call print-byte-buffered/disp32 +1855 # . . discard args +1856 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1857 # write-byte-buffered(out, ' ') +1858 # . . push args +1859 68/push 0x20/imm32/space +1860 57/push-EDI +1861 # . . call +1862 e8/call write-byte-buffered/disp32 +1863 # . . discard args +1864 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +1865 # EBX = EBX >> 8 +1866 c1/shift 5/subop/logic-right 3/mod/direct 3/rm32/EBX . . . . . 8/imm8 # shift EBX right by 8 bits, while padding zeroes +1867 $emit-hex:continue: +1868 # ++curr +1869 41/increment-ECX +1870 eb/jump $emit-hex:loop/disp8 +1871 $emit-hex:end: +1872 # . restore registers +1873 5f/pop-to-EDI +1874 5b/pop-to-EBX +1875 5a/pop-to-EDX 1876 59/pop-to-ECX -1877 # . epilog -1878 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -1879 5d/pop-to-EBP -1880 c3/return -1881 -1882 test-compute-width: -1883 # . prolog -1884 55/push-EBP -1885 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -1886 $test-compute-width:imm8: -1887 # EAX = compute-width("0x2/imm8") -1888 # . . push args -1889 68/push "0x2/imm8"/imm32 -1890 # . . call -1891 e8/call compute-width/disp32 -1892 # . . discard args -1893 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1894 # check-ints-equal(EAX, 1, msg) -1895 # . . push args -1896 68/push "F - test-compute-width: 0x2/imm8"/imm32 -1897 50/push-EAX -1898 68/push 1/imm32 -1899 # . . call -1900 e8/call check-ints-equal/disp32 -1901 # . . discard args -1902 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1903 $test-compute-width:imm16: -1904 # EAX = compute-width("4/imm16") -1905 # . . push args -1906 68/push "4/imm16"/imm32 -1907 # . . call -1908 e8/call compute-width/disp32 -1909 # . . discard args -1910 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1911 # check-ints-equal(EAX, 2, msg) -1912 # . . push args -1913 68/push "F - test-compute-width: 4/imm16"/imm32 -1914 50/push-EAX -1915 68/push 2/imm32 -1916 # . . call -1917 e8/call check-ints-equal/disp32 -1918 # . . discard args -1919 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1920 $test-compute-width:imm32: -1921 # EAX = compute-width("4/imm32") -1922 # . . push args -1923 68/push "4/imm32"/imm32 +1877 58/pop-to-EAX +1878 # . epilog +1879 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +1880 5d/pop-to-EBP +1881 c3/return +1882 +1883 test-emit-hex-single-byte: +1884 # setup +1885 # . clear-stream(_test-output-stream) +1886 # . . push args +1887 68/push _test-output-stream/imm32 +1888 # . . call +1889 e8/call clear-stream/disp32 +1890 # . . discard args +1891 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1892 # . clear-stream(_test-output-buffered-file+4) +1893 # . . push args +1894 b8/copy-to-EAX _test-output-buffered-file/imm32 +1895 05/add-to-EAX 4/imm32 +1896 50/push-EAX +1897 # . . call +1898 e8/call clear-stream/disp32 +1899 # . . discard args +1900 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1901 # emit-hex(_test-output-buffered-file, 0xab, 1) +1902 # . . push args +1903 68/push 1/imm32 +1904 68/push 0xab/imm32 +1905 68/push _test-output-buffered-file/imm32 +1906 # . . call +1907 e8/call emit-hex/disp32 +1908 # . . discard args +1909 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1910 # flush(_test-output-buffered-file) +1911 # . . push args +1912 68/push _test-output-buffered-file/imm32 +1913 # . . call +1914 e8/call flush/disp32 +1915 # . . discard args +1916 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1917 # check-ints-equal(*_test-output-stream->data, 'ab ', msg) +1918 # . . push args +1919 68/push "F - test-emit-hex-single-byte"/imm32 +1920 68/push 0x206261/imm32 +1921 # . . push *_test-output-stream->data +1922 b8/copy-to-EAX _test-output-stream/imm32 +1923 ff 6/subop/push 1/mod/*+disp8 0/rm32/EAX . . . . 0xc/disp8 . # push *(EAX+12) 1924 # . . call -1925 e8/call compute-width/disp32 +1925 e8/call check-ints-equal/disp32 1926 # . . discard args -1927 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1928 # check-ints-equal(EAX, 4, msg) -1929 # . . push args -1930 68/push "F - test-compute-width: 4/imm32"/imm32 -1931 50/push-EAX -1932 68/push 4/imm32 -1933 # . . call -1934 e8/call check-ints-equal/disp32 -1935 # . . discard args -1936 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1937 $test-compute-width:disp8: -1938 # EAX = compute-width("foo/disp8") -1939 # . . push args -1940 68/push "foo/disp8"/imm32 -1941 # . . call -1942 e8/call compute-width/disp32 -1943 # . . discard args -1944 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1945 # check-ints-equal(EAX, 1, msg) -1946 # . . push args -1947 68/push "F - test-compute-width: foo/disp8"/imm32 -1948 50/push-EAX -1949 68/push 1/imm32 -1950 # . . call -1951 e8/call check-ints-equal/disp32 -1952 # . . discard args -1953 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1954 $test-compute-width:disp16: -1955 # EAX = compute-width("foo/disp16") -1956 # . . push args -1957 68/push "foo/disp16"/imm32 -1958 # . . call -1959 e8/call compute-width/disp32 -1960 # . . discard args -1961 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1962 # check-ints-equal(EAX, 2, msg) -1963 # . . push args -1964 68/push "F - test-compute-width: foo/disp16"/imm32 -1965 50/push-EAX -1966 68/push 2/imm32 -1967 # . . call -1968 e8/call check-ints-equal/disp32 -1969 # . . discard args -1970 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1971 $test-compute-width:disp32: -1972 # EAX = compute-width("foo/disp32") -1973 # . . push args -1974 68/push "foo/disp32"/imm32 -1975 # . . call -1976 e8/call compute-width/disp32 -1977 # . . discard args -1978 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1979 # check-ints-equal(EAX, 4, msg) +1927 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1928 # . end +1929 c3/return +1930 +1931 test-emit-hex-multiple-byte: +1932 # setup +1933 # . clear-stream(_test-output-stream) +1934 # . . push args +1935 68/push _test-output-stream/imm32 +1936 # . . call +1937 e8/call clear-stream/disp32 +1938 # . . discard args +1939 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1940 # . clear-stream(_test-output-buffered-file+4) +1941 # . . push args +1942 b8/copy-to-EAX _test-output-buffered-file/imm32 +1943 05/add-to-EAX 4/imm32 +1944 50/push-EAX +1945 # . . call +1946 e8/call clear-stream/disp32 +1947 # . . discard args +1948 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1949 # emit-hex(_test-output-buffered-file, 0x1234, 2) +1950 # . . push args +1951 68/push 2/imm32 +1952 68/push 0x1234/imm32 +1953 68/push _test-output-buffered-file/imm32 +1954 # . . call +1955 e8/call emit-hex/disp32 +1956 # . . discard args +1957 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1958 # flush(_test-output-buffered-file) +1959 # . . push args +1960 68/push _test-output-buffered-file/imm32 +1961 # . . call +1962 e8/call flush/disp32 +1963 # . . discard args +1964 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1965 # check-stream-equal(_test-output-stream, "34 12 ", msg) +1966 # . . push args +1967 68/push "F - test-emit-hex-multiple-byte/1"/imm32 +1968 68/push "34 12 "/imm32 +1969 68/push _test-output-stream/imm32 +1970 # . . call +1971 e8/call check-stream-equal/disp32 +1972 # . . discard args +1973 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +1974 # . end +1975 c3/return +1976 +1977 test-emit-hex-zero-pad: +1978 # setup +1979 # . clear-stream(_test-output-stream) 1980 # . . push args -1981 68/push "F - test-compute-width: foo/disp32"/imm32 -1982 50/push-EAX -1983 68/push 4/imm32 -1984 # . . call -1985 e8/call check-ints-equal/disp32 -1986 # . . discard args -1987 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -1988 $test-compute-width:no-metadata: -1989 # EAX = compute-width("45") -1990 # . . push args -1991 68/push "45"/imm32 -1992 # . . call -1993 e8/call compute-width/disp32 -1994 # . . discard args -1995 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -1996 # check-ints-equal(EAX, 1, msg) -1997 # . . push args -1998 68/push "F - test-compute-width: 45 (no metadata)"/imm32 -1999 50/push-EAX -2000 68/push 1/imm32 -2001 # . . call -2002 e8/call check-ints-equal/disp32 -2003 # . . discard args -2004 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2005 # . epilog -2006 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2007 5d/pop-to-EBP -2008 c3/return -2009 -2010 is-label?: # word : (address slice) -> EAX : boolean -2011 # . prolog -2012 55/push-EBP -2013 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2014 # . save registers -2015 51/push-ECX -2016 # ECX = word -2017 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX -2018 # ECX = word->end -2019 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 1/r32/ECX 4/disp8 . # copy *(ECX+4) to ECX -2020 # return *(word->end - 1) == ':' -2021 # . EAX = 0 -2022 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX -2023 # . EAX = *((char *) word->end - 1) -2024 8a/copy-byte 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/AL -1/disp8 . # copy byte at *(ECX-1) to AL -2025 # . return (EAX == ':') -2026 3d/compare-EAX-and 0x3a/imm32/colon -2027 b8/copy-to-EAX 1/imm32/true -2028 74/jump-if-equal $is-label?:end/disp8 -2029 b8/copy-to-EAX 0/imm32/false -2030 $is-label?:end: -2031 # . restore registers -2032 59/pop-to-ECX -2033 # . epilog -2034 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2035 5d/pop-to-EBP -2036 c3/return -2037 -2038 test-is-label?: -2039 # . prolog -2040 55/push-EBP -2041 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP -2042 $test-is-label?:true: -2043 # (EAX..ECX) = "AAA:" -2044 b8/copy-to-EAX "AAA:"/imm32 -2045 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2046 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 -2047 05/add-to-EAX 4/imm32 -2048 # var slice/ECX = {EAX, ECX} -2049 51/push-ECX -2050 50/push-EAX -2051 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -2052 # is-label?(slice/ECX) -2053 # . . push args -2054 51/push-ECX -2055 # . . call -2056 e8/call is-label?/disp32 -2057 # . . discard args -2058 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2059 # check-ints-equal(EAX, 1, msg) -2060 # . . push args -2061 68/push "F - test-is-label?:true"/imm32 -2062 68/push 1/imm32 -2063 50/push-EAX -2064 # . . call -2065 e8/call check-ints-equal/disp32 -2066 # . . discard args -2067 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2068 $test-is-label?:false: -2069 # (EAX..ECX) = "AAA" -2070 b8/copy-to-EAX "AAA"/imm32 -2071 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX -2072 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 -2073 05/add-to-EAX 4/imm32 -2074 # var slice/ECX = {EAX, ECX} -2075 51/push-ECX -2076 50/push-EAX -2077 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX -2078 # is-label?(slice/ECX) -2079 # . . push args -2080 51/push-ECX -2081 # . . call -2082 e8/call is-label?/disp32 -2083 # . . discard args -2084 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP -2085 # check-ints-equal(EAX, 0, msg) -2086 # . . push args -2087 68/push "F - test-is-label?:false"/imm32 -2088 68/push 0/imm32 -2089 50/push-EAX -2090 # . . call -2091 e8/call check-ints-equal/disp32 -2092 # . . discard args -2093 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP -2094 # . epilog -2095 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP -2096 5d/pop-to-EBP -2097 c3/return -2098 -2099 == data -2100 -2101 _test-data-segment: -2102 64/d 61/a 74/t 61/a -2103 _test-data-segment-end: -2104 -2105 # . . vim:nowrap:textwidth=0 +1981 68/push _test-output-stream/imm32 +1982 # . . call +1983 e8/call clear-stream/disp32 +1984 # . . discard args +1985 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1986 # . clear-stream(_test-output-buffered-file+4) +1987 # . . push args +1988 b8/copy-to-EAX _test-output-buffered-file/imm32 +1989 05/add-to-EAX 4/imm32 +1990 50/push-EAX +1991 # . . call +1992 e8/call clear-stream/disp32 +1993 # . . discard args +1994 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +1995 # emit-hex(_test-output-buffered-file, 0xab, 2) +1996 # . . push args +1997 68/push 2/imm32 +1998 68/push 0xab/imm32 +1999 68/push _test-output-buffered-file/imm32 +2000 # . . call +2001 e8/call emit-hex/disp32 +2002 # . . discard args +2003 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2004 # flush(_test-output-buffered-file) +2005 # . . push args +2006 68/push _test-output-buffered-file/imm32 +2007 # . . call +2008 e8/call flush/disp32 +2009 # . . discard args +2010 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2011 # check(_test-output-stream->data == 'ab 00 ') +2012 # . . push args +2013 68/push "F - test-emit-hex-zero-pad/1"/imm32 +2014 68/push "ab 00 "/imm32 +2015 68/push _test-output-stream/imm32 +2016 # . . call +2017 e8/call check-stream-equal/disp32 +2018 # . . discard args +2019 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2020 # . end +2021 c3/return +2022 +2023 test-emit-hex-negative: +2024 # setup +2025 # . clear-stream(_test-output-stream) +2026 # . . push args +2027 68/push _test-output-stream/imm32 +2028 # . . call +2029 e8/call clear-stream/disp32 +2030 # . . discard args +2031 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2032 # . clear-stream(_test-output-buffered-file+4) +2033 # . . push args +2034 b8/copy-to-EAX _test-output-buffered-file/imm32 +2035 05/add-to-EAX 4/imm32 +2036 50/push-EAX +2037 # . . call +2038 e8/call clear-stream/disp32 +2039 # . . discard args +2040 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2041 # emit-hex(_test-output-buffered-file, -1, 2) +2042 # . . push args +2043 68/push 2/imm32 +2044 68/push -1/imm32 +2045 68/push _test-output-buffered-file/imm32 +2046 # . . call +2047 e8/call emit-hex/disp32 +2048 # . . discard args +2049 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2050 # flush(_test-output-buffered-file) +2051 # . . push args +2052 68/push _test-output-buffered-file/imm32 +2053 # . . call +2054 e8/call flush/disp32 +2055 # . . discard args +2056 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2057 # check-stream-equal(_test-output-stream == "ff ff ") +2058 # . . push args +2059 68/push "F - test-emit-hex-negative/1"/imm32 +2060 68/push "ff ff "/imm32 +2061 68/push _test-output-stream/imm32 +2062 # . . call +2063 e8/call check-stream-equal/disp32 +2064 # . . discard args +2065 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2066 # . end +2067 c3/return +2068 +2069 # print 'arr' in hex with a space after every byte +2070 emit-hex-array: # out : (address buffered-file), arr : (address array byte) -> <void> +2071 # . prolog +2072 55/push-EBP +2073 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2074 # . save registers +2075 50/push-EAX +2076 51/push-ECX +2077 52/push-EDX +2078 57/push-EDI +2079 # EDI = out +2080 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 7/r32/EDI 8/disp8 . # copy *(EBP+8) to EDI +2081 # EDX = arr # <== 0xbdffffe4 +2082 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 2/r32/EDX 0xc/disp8 . # copy *(EBP+12) to EDX +2083 # curr/ECX = arr->data +2084 8d/copy-address 1/mod/*+disp8 2/rm32/EDX . . . 1/r32/ECX 4/disp8 . # copy EDX+4 to ECX +2085 # max/EDX = arr->data + arr->length +2086 8b/copy 0/mod/indirect 2/rm32/EDX . . . 2/r32/EDX . . # copy *EDX to EDX +2087 01/add 3/mod/direct 2/rm32/EDX . . . 1/r32/ECX . . # add ECX to EDX +2088 # EAX = 0 +2089 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2090 $emit-hex-array:loop: +2091 # if (curr >= width) break +2092 39/compare 3/mod/direct 1/rm32/ECX . . . 2/r32/EDX . . # compare ECX with EDX +2093 73/jump-if-greater-or-equal-unsigned $emit-hex-array:end/disp8 +2094 # emit-hex(out, *curr, width=1) +2095 # . . push args +2096 68/push 1/imm32/width +2097 8a/copy-byte 0/mod/indirect 1/rm32/ECX . . . 0/r32/AL . . # copy byte at *ECX to AL +2098 50/push-EAX +2099 57/push-EDI +2100 # . . call +2101 e8/call emit-hex/disp32 +2102 # . . discard args +2103 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2104 # ++curr +2105 41/increment-ECX +2106 eb/jump $emit-hex-array:loop/disp8 +2107 $emit-hex-array:end: +2108 # . restore registers +2109 5f/pop-to-EDI +2110 5a/pop-to-EDX +2111 59/pop-to-ECX +2112 58/pop-to-EAX +2113 # . epilog +2114 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2115 5d/pop-to-EBP +2116 c3/return +2117 +2118 test-emit-hex-array: +2119 # . prolog +2120 55/push-EBP +2121 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2122 # setup +2123 # . clear-stream(_test-output-stream) +2124 # . . push args +2125 68/push _test-output-stream/imm32 +2126 # . . call +2127 e8/call clear-stream/disp32 +2128 # . . discard args +2129 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2130 # . clear-stream(_test-output-buffered-file+4) +2131 # . . push args +2132 b8/copy-to-EAX _test-output-buffered-file/imm32 +2133 05/add-to-EAX 4/imm32 +2134 50/push-EAX +2135 # . . call +2136 e8/call clear-stream/disp32 +2137 # . . discard args +2138 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2139 # var arr/ECX (address array byte) = [01, 02, 03] +2140 68/push 0x00030201/imm32 # bytes 01 02 03 +2141 68/push 3/imm32/length +2142 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2143 # emit-hex-array(_test-output-buffered-file, arr) +2144 # . . push args +2145 51/push-ECX +2146 68/push _test-output-buffered-file/imm32 +2147 # . . call +2148 e8/call emit-hex-array/disp32 +2149 # . . discard args +2150 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2151 # . flush(_test-output-buffered-file) +2152 # . . push args +2153 68/push _test-output-buffered-file/imm32 +2154 # . . call +2155 e8/call flush/disp32 +2156 # . . discard args +2157 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2158 +-- 33 lines: #? # dump output --------------------------------------------------------------------------------------------------------------------------- +2191 # check-next-stream-line-equal(_test-output-stream, "01 02 03 ", msg) +2192 # . . push args +2193 68/push "F - test-emit-hex-array"/imm32 +2194 68/push "01 02 03 "/imm32 +2195 68/push _test-output-stream/imm32 +2196 # . . call +2197 e8/call check-next-stream-line-equal/disp32 +2198 # . . discard args +2199 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2200 # . epilog +2201 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2202 5d/pop-to-EBP +2203 c3/return +2204 +2205 compute-width: # word : (address array byte) -> EAX : int +2206 # . prolog +2207 55/push-EBP +2208 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2209 # . save registers +2210 51/push-ECX +2211 # EAX = word +2212 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 0/r32/EAX 8/disp8 . # copy *(EBP+8) to ECX +2213 # ECX = word + word->length +2214 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2215 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 +2216 # EAX = word->data +2217 05/add-to-EAX 4/imm32 +2218 # var in/ECX : (address slice) = {EAX, ECX} +2219 51/push-ECX +2220 50/push-EAX +2221 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2222 # return compute-width-of-slice(ECX) +2223 # . . push args +2224 51/push-ECX +2225 # . . call +2226 e8/call compute-width-of-slice/disp32 +2227 # . . discard args +2228 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2229 $compute-width:end: +2230 # . reclaim locals +2231 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2232 # . restore registers +2233 59/pop-to-ECX +2234 # . epilog +2235 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2236 5d/pop-to-EBP +2237 c3/return +2238 +2239 compute-width-of-slice: # s : (address slice) -> EAX : int +2240 # . prolog +2241 55/push-EBP +2242 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2243 # . save registers +2244 51/push-ECX +2245 # ECX = s +2246 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX +2247 # if (has-metadata?(word, "imm32")) return 4 +2248 # . EAX = has-metadata?(word, "imm32") +2249 # . . push args +2250 68/push "imm32"/imm32 +2251 51/push-ECX +2252 # . . call +2253 e8/call has-metadata?/disp32 +2254 # . . discard args +2255 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2256 # . if (EAX != 0) return 4 +2257 3d/compare-EAX-and 0/imm32 +2258 b8/copy-to-EAX 4/imm32 # ZF is set, so we can overwrite EAX now +2259 75/jump-if-not-equal $compute-width-of-slice:end/disp8 +2260 # if (has-metadata?(word, "disp32")) return 4 +2261 # . EAX = has-metadata?(word, "disp32") +2262 # . . push args +2263 68/push "disp32"/imm32 +2264 51/push-ECX +2265 # . . call +2266 e8/call has-metadata?/disp32 +2267 # . . discard args +2268 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2269 # . if (EAX != 0) return 4 +2270 3d/compare-EAX-and 0/imm32 +2271 b8/copy-to-EAX 4/imm32 # ZF is set, so we can overwrite EAX now +2272 75/jump-if-not-equal $compute-width-of-slice:end/disp8 +2273 # if (has-metadata?(word, "imm16")) return 2 +2274 # . EAX = has-metadata?(word, "imm16") +2275 # . . push args +2276 68/push "imm16"/imm32 +2277 51/push-ECX +2278 # . . call +2279 e8/call has-metadata?/disp32 +2280 # . . discard args +2281 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2282 # . if (EAX != 0) return 2 +2283 3d/compare-EAX-and 0/imm32 +2284 b8/copy-to-EAX 2/imm32 # ZF is set, so we can overwrite EAX now +2285 75/jump-if-not-equal $compute-width-of-slice:end/disp8 +2286 # if (has-metadata?(word, "disp16")) return 2 +2287 # . EAX = has-metadata?(word, "disp16") +2288 # . . push args +2289 68/push "disp16"/imm32 +2290 51/push-ECX +2291 # . . call +2292 e8/call has-metadata?/disp32 +2293 # . . discard args +2294 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 8/imm32 # add to ESP +2295 # . if (EAX != 0) return 2 +2296 3d/compare-EAX-and 0/imm32 +2297 b8/copy-to-EAX 2/imm32 # ZF is set, so we can overwrite EAX now +2298 75/jump-if-not-equal $compute-width-of-slice:end/disp8 +2299 # otherwise return 1 +2300 b8/copy-to-EAX 1/imm32 +2301 $compute-width-of-slice:end: +2302 # . restore registers +2303 59/pop-to-ECX +2304 # . epilog +2305 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2306 5d/pop-to-EBP +2307 c3/return +2308 +2309 test-compute-width: +2310 # . prolog +2311 55/push-EBP +2312 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2313 $test-compute-width:imm8: +2314 # EAX = compute-width("0x2/imm8") +2315 # . . push args +2316 68/push "0x2/imm8"/imm32 +2317 # . . call +2318 e8/call compute-width/disp32 +2319 # . . discard args +2320 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2321 # check-ints-equal(EAX, 1, msg) +2322 # . . push args +2323 68/push "F - test-compute-width: 0x2/imm8"/imm32 +2324 50/push-EAX +2325 68/push 1/imm32 +2326 # . . call +2327 e8/call check-ints-equal/disp32 +2328 # . . discard args +2329 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2330 $test-compute-width:imm16: +2331 # EAX = compute-width("4/imm16") +2332 # . . push args +2333 68/push "4/imm16"/imm32 +2334 # . . call +2335 e8/call compute-width/disp32 +2336 # . . discard args +2337 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2338 # check-ints-equal(EAX, 2, msg) +2339 # . . push args +2340 68/push "F - test-compute-width: 4/imm16"/imm32 +2341 50/push-EAX +2342 68/push 2/imm32 +2343 # . . call +2344 e8/call check-ints-equal/disp32 +2345 # . . discard args +2346 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2347 $test-compute-width:imm32: +2348 # EAX = compute-width("4/imm32") +2349 # . . push args +2350 68/push "4/imm32"/imm32 +2351 # . . call +2352 e8/call compute-width/disp32 +2353 # . . discard args +2354 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2355 # check-ints-equal(EAX, 4, msg) +2356 # . . push args +2357 68/push "F - test-compute-width: 4/imm32"/imm32 +2358 50/push-EAX +2359 68/push 4/imm32 +2360 # . . call +2361 e8/call check-ints-equal/disp32 +2362 # . . discard args +2363 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2364 $test-compute-width:disp8: +2365 # EAX = compute-width("foo/disp8") +2366 # . . push args +2367 68/push "foo/disp8"/imm32 +2368 # . . call +2369 e8/call compute-width/disp32 +2370 # . . discard args +2371 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2372 # check-ints-equal(EAX, 1, msg) +2373 # . . push args +2374 68/push "F - test-compute-width: foo/disp8"/imm32 +2375 50/push-EAX +2376 68/push 1/imm32 +2377 # . . call +2378 e8/call check-ints-equal/disp32 +2379 # . . discard args +2380 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2381 $test-compute-width:disp16: +2382 # EAX = compute-width("foo/disp16") +2383 # . . push args +2384 68/push "foo/disp16"/imm32 +2385 # . . call +2386 e8/call compute-width/disp32 +2387 # . . discard args +2388 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2389 # check-ints-equal(EAX, 2, msg) +2390 # . . push args +2391 68/push "F - test-compute-width: foo/disp16"/imm32 +2392 50/push-EAX +2393 68/push 2/imm32 +2394 # . . call +2395 e8/call check-ints-equal/disp32 +2396 # . . discard args +2397 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2398 $test-compute-width:disp32: +2399 # EAX = compute-width("foo/disp32") +2400 # . . push args +2401 68/push "foo/disp32"/imm32 +2402 # . . call +2403 e8/call compute-width/disp32 +2404 # . . discard args +2405 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2406 # check-ints-equal(EAX, 4, msg) +2407 # . . push args +2408 68/push "F - test-compute-width: foo/disp32"/imm32 +2409 50/push-EAX +2410 68/push 4/imm32 +2411 # . . call +2412 e8/call check-ints-equal/disp32 +2413 # . . discard args +2414 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2415 $test-compute-width:no-metadata: +2416 # EAX = compute-width("45") +2417 # . . push args +2418 68/push "45"/imm32 +2419 # . . call +2420 e8/call compute-width/disp32 +2421 # . . discard args +2422 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2423 # check-ints-equal(EAX, 1, msg) +2424 # . . push args +2425 68/push "F - test-compute-width: 45 (no metadata)"/imm32 +2426 50/push-EAX +2427 68/push 1/imm32 +2428 # . . call +2429 e8/call check-ints-equal/disp32 +2430 # . . discard args +2431 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2432 # . epilog +2433 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2434 5d/pop-to-EBP +2435 c3/return +2436 +2437 is-label?: # word : (address slice) -> EAX : boolean +2438 # . prolog +2439 55/push-EBP +2440 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2441 # . save registers +2442 51/push-ECX +2443 # ECX = word +2444 8b/copy 1/mod/*+disp8 5/rm32/EBP . . . 1/r32/ECX 8/disp8 . # copy *(EBP+8) to ECX +2445 # ECX = word->end +2446 8b/copy 1/mod/*+disp8 1/rm32/ECX . . . 1/r32/ECX 4/disp8 . # copy *(ECX+4) to ECX +2447 # return *(word->end - 1) == ':' +2448 # . EAX = 0 +2449 31/xor 3/mod/direct 0/rm32/EAX . . . 0/r32/EAX . . # clear EAX +2450 # . EAX = *((char *) word->end - 1) +2451 8a/copy-byte 1/mod/*+disp8 1/rm32/ECX . . . 0/r32/AL -1/disp8 . # copy byte at *(ECX-1) to AL +2452 # . return (EAX == ':') +2453 3d/compare-EAX-and 0x3a/imm32/colon +2454 b8/copy-to-EAX 1/imm32/true +2455 74/jump-if-equal $is-label?:end/disp8 +2456 b8/copy-to-EAX 0/imm32/false +2457 $is-label?:end: +2458 # . restore registers +2459 59/pop-to-ECX +2460 # . epilog +2461 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2462 5d/pop-to-EBP +2463 c3/return +2464 +2465 test-is-label?: +2466 # . prolog +2467 55/push-EBP +2468 89/copy 3/mod/direct 5/rm32/EBP . . . 4/r32/ESP . . # copy ESP to EBP +2469 $test-is-label?:true: +2470 # (EAX..ECX) = "AAA:" +2471 b8/copy-to-EAX "AAA:"/imm32 +2472 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2473 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 +2474 05/add-to-EAX 4/imm32 +2475 # var slice/ECX = {EAX, ECX} +2476 51/push-ECX +2477 50/push-EAX +2478 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2479 # is-label?(slice/ECX) +2480 # . . push args +2481 51/push-ECX +2482 # . . call +2483 e8/call is-label?/disp32 +2484 # . . discard args +2485 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2486 # check-ints-equal(EAX, 1, msg) +2487 # . . push args +2488 68/push "F - test-is-label?:true"/imm32 +2489 68/push 1/imm32 +2490 50/push-EAX +2491 # . . call +2492 e8/call check-ints-equal/disp32 +2493 # . . discard args +2494 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2495 $test-is-label?:false: +2496 # (EAX..ECX) = "AAA" +2497 b8/copy-to-EAX "AAA"/imm32 +2498 8b/copy 0/mod/indirect 0/rm32/EAX . . . 1/r32/ECX . . # copy *EAX to ECX +2499 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 +2500 05/add-to-EAX 4/imm32 +2501 # var slice/ECX = {EAX, ECX} +2502 51/push-ECX +2503 50/push-EAX +2504 89/copy 3/mod/direct 1/rm32/ECX . . . 4/r32/ESP . . # copy ESP to ECX +2505 # is-label?(slice/ECX) +2506 # . . push args +2507 51/push-ECX +2508 # . . call +2509 e8/call is-label?/disp32 +2510 # . . discard args +2511 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 4/imm32 # add to ESP +2512 # check-ints-equal(EAX, 0, msg) +2513 # . . push args +2514 68/push "F - test-is-label?:false"/imm32 +2515 68/push 0/imm32 +2516 50/push-EAX +2517 # . . call +2518 e8/call check-ints-equal/disp32 +2519 # . . discard args +2520 81 0/subop/add 3/mod/direct 4/rm32/ESP . . . . . 0xc/imm32 # add to ESP +2521 # . epilog +2522 89/copy 3/mod/direct 4/rm32/ESP . . . 5/r32/EBP . . # copy EBP to ESP +2523 5d/pop-to-EBP +2524 c3/return +2525 +2526 == data +2527 +2528 _test-data-segment: +2529 64/d 61/a 74/t 61/a +2530 _test-data-segment-end: +2531 +2532 # . . vim:nowrap:textwidth=0 diff --git a/html/apps/survey.subx.html b/html/apps/survey.subx.html index 2c549f7f..4c0a4528 100644 --- a/html/apps/survey.subx.html +++ b/html/apps/survey.subx.html @@ -68,7 +68,7 @@ if ('onhashchange' in window) { 5 # b) add segment headers with addresses and offsets correctly filled in 6 # 7 # To build: - 8 # $ ./subx translate *.subx apps/survey.subx -o apps/survey + 8 # $ ./subx translate 0*.subx apps/subx-common.subx apps/survey.subx -o apps/survey 9 # 10 # The expected input is a stream of bytes with segment headers, comments and 11 # some interspersed labels. diff --git a/html/apps/tests.subx.html b/html/apps/tests.subx.html index 0b97db0b..4ac2fb96 100644 --- a/html/apps/tests.subx.html +++ b/html/apps/tests.subx.html @@ -60,283 +60,286 @@ if ('onhashchange' in window) {
   1 # Generate code for a new function called 'run-tests' which calls in sequence
   2 # all functions starting with 'test-'.
-  3 
-  4 == code
-  5 #   instruction                     effective address                                                   register    displacement    immediate
-  6 # . op          subop               mod             rm32          base        index         scale       r32
-  7 # . 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
-  8 
-  9 Entry:
- 10     # Heap = new-segment(Heap-size)
- 11     # . . push args
- 12     68/push  Heap/imm32
- 13     68/push  Heap-size/imm32
- 14     # . . call
- 15     e8/call  new-segment/disp32
- 16     # . . discard args
- 17     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 18     # initialize-trace-stream(256KB)
- 19     # . . push args
- 20     68/push  0x40000/imm32/256KB
- 21     # . . call
- 22     e8/call  initialize-trace-stream/disp32
- 23     # . . discard args
- 24     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
- 25 
- 26     # run tests if necessary, convert stdin if not
- 27     # . prolog
- 28     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 29     # initialize heap
- 30     # - if argc > 1 and argv[1] == "test", then return run_tests()
- 31     # . argc > 1
- 32     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
- 33     7e/jump-if-lesser-or-equal  $run-main/disp8
- 34     # . argv[1] == "test"
- 35     # . . push args
- 36     68/push  "test"/imm32
- 37     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
- 38     # . . call
- 39     e8/call  kernel-string-equal?/disp32
- 40     # . . discard args
- 41     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 42     # . check result
- 43     3d/compare-EAX-and  1/imm32
- 44     75/jump-if-not-equal  $run-main/disp8
- 45     # . run-tests()
- 46     e8/call  run-tests/disp32
- 47     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
- 48     eb/jump  $main:end/disp8
- 49 $run-main:
- 50     # - otherwise convert stdin
- 51     # convert(Stdin, Stdout)
- 52     # . . push args
- 53     68/push  Stdout/imm32
- 54     68/push  Stdin/imm32
- 55     # . . call
- 56     e8/call  convert/disp32
- 57     # . . discard args
- 58     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
- 59     # . syscall(exit, 0)
- 60     bb/copy-to-EBX  0/imm32
- 61 $main:end:
- 62     b8/copy-to-EAX  1/imm32/exit
- 63     cd/syscall  0x80/imm8
- 64 
- 65 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
- 66     # pseudocode
- 67     #   bool tests-found = false
- 68     #   var line = new-stream(512, 1)
- 69     #   var new-code-segment = new-stream(Segment-size, 1)
- 70     #   write(new-code-segment, "\n==code\n")
- 71     #   write(new-code-segment, "run-tests:\n")
- 72     #   while true
- 73     #     clear-stream(line)
- 74     #     read-line-buffered(in, line)
- 75     #     if (line->write == 0) break               # end of file
- 76     #     var word-slice = next-word(line)
- 77     #     if is-label?(word-slice)
- 78     #       if slice-starts-with?(word-slice, "test-")
- 79     #         tests-found = true
- 80     #         write(new-code-segment, "  e8/call  ")
- 81     #         write-slice(new-code-segment, word-slice)
- 82     #         write(new-code-segment, "/disp32\n")
- 83     #     rewind-stream(line)
- 84     #     write-stream-data(out, line)
- 85     #   if tests-found
- 86     #     write(new-code-segment, "  c3/return\n")
- 87     #     write-stream-data(out, new-code-segment)
- 88     #   flush(out)
- 89     #
- 90     # . prolog
- 91     55/push-EBP
- 92     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
- 93     # . save registers
- 94     50/push-EAX
- 95     51/push-ECX
- 96     52/push-EDX
- 97     53/push-EBX
- 98     57/push-EDI
- 99     # var line/ECX : (address stream byte) = stream(512)
-100     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
-101     68/push  0x200/imm32/length
-102     68/push  0/imm32/read
-103     68/push  0/imm32/write
-104     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
-105     # var word-slice/EDX = {0, 0}
-106     68/push  0/imm32/end
-107     68/push  0/imm32/start
-108     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
-109     # tests-found?/EBX = false
-110     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
-111     # new-code-segment/EDI = new-stream(Heap, Segment-size, 1)
-112     # . EAX = new-stream(Heap, Segment-size, 1)
-113     # . . push args
-114     68/push  1/imm32
-115     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
-116     68/push  Heap/imm32
-117     # . . call
-118     e8/call  new-stream/disp32
-119     # . . discard args
-120     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
-121     # . EDI = EAX
-122     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
-123     # write(new-code-segment, "\n== code\n")
-124     # . . push args
-125     68/push  "\n== code\n"/imm32
-126     57/push-EDI
-127     # . . call
-128     e8/call  write/disp32
-129     # . . discard args
-130     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-131     # write(new-code-segment, "run-tests:\n")
-132     # . . push args
-133     68/push  "run-tests:\n"/imm32
-134     57/push-EDI
-135     # . . call
-136     e8/call  write/disp32
-137     # . . discard args
-138     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-139 $convert:loop:
-140     # clear-stream(line)
-141     # . . push args
-142     51/push-ECX
-143     # . . call
-144     e8/call  clear-stream/disp32
-145     # . . discard args
-146     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-147     # read-line-buffered(in, line)
-148     # . . push args
-149     51/push-ECX
-150     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
-151     # . . call
-152     e8/call  read-line-buffered/disp32
-153     # . . discard args
-154     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-155 $convert:check0:
-156     # if (line->write == 0) break
-157     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
-158     0f 84/jump-if-equal  $convert:break/disp32
-159     # next-word(line, word-slice)
-160     # . . push args
-161     52/push-EDX
-162     51/push-ECX
-163     # . . call
-164     e8/call  next-word/disp32
-165     # . . discard args
-166     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-167 $convert:check-for-label:
-168     # if (!is-label?(word-slice)) continue
-169     # . EAX = is-label?(word-slice)
-170     # . . push args
-171     52/push-EDX
-172     # . . call
-173     e8/call  is-label?/disp32
-174     # . . discard args
-175     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-176     # . if (EAX == 0) continue
-177     3d/compare-EAX-and  0/imm32
-178     74/jump-if-equal  $convert:continue/disp8
-179 $convert:check-label-prefix:
-180     # strip trailing ':' from word-slice
-181     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # decrement *(EDX+4)
-182     # if !slice-starts-with?(word-slice, "test-") continue
-183     # . . push args
-184     68/push  "test-"/imm32
-185     52/push-EDX
-186     # . . call
-187     e8/call  slice-starts-with?/disp32
-188     # . . discard args
-189     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-190     # . if (EAX == 0) break
-191     3d/compare-EAX-and  0/imm32
-192     74/jump-if-equal  $convert:continue/disp8
-193 $convert:call-test-function:
-194     # tests-found? = true
-195     bb/copy-to-EBX  1/imm32/true
-196     # write(new-code-segment, "  e8/call  ")
-197     # . . push args
-198     68/push  "  e8/call  "/imm32
-199     57/push-EDI
-200     # . . call
-201     e8/call  write/disp32
-202     # . . discard args
-203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-204     # write-slice(new-code-segment, word-slice)
-205     # . . push args
-206     52/push-EDX
-207     57/push-EDI
-208     # . . call
-209     e8/call  write-slice/disp32
-210     # . . discard args
-211     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-212     # write(new-code-segment, "/disp32\n")
-213     # . . push args
-214     68/push  "/disp32\n"/imm32
-215     57/push-EDI
-216     # . . call
-217     e8/call  write/disp32
-218     # . . discard args
-219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-220 $convert:continue:
-221     # rewind-stream(line)
-222     # . . push args
-223     51/push-ECX
-224     # . . call
-225     e8/call  rewind-stream/disp32
-226     # . . discard args
-227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-228     # write-stream-data(out, line)
-229     # . . push args
-230     51/push-ECX
-231     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-232     # . . call
-233     e8/call  write-stream-data/disp32
-234     # . . discard args
-235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-236     # loop
-237     e9/jump  $convert:loop/disp32
-238 $convert:break:
-239     # if (!tests-found?) goto end
-240     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
-241     74/jump-if-equal  $convert:end/disp8
-242     # write(new-code-segment, "  c3/return\n")
-243     # . . push args
-244     68/push  "  c3/return\n"/imm32
-245     57/push-EDI
-246     # . . call
-247     e8/call  write/disp32
-248     # . . discard args
-249     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-250     # write-stream-data(out, new-code-segment)
-251     # . . push args
-252     57/push-EDI
-253     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-254     # . . call
-255     e8/call  write-stream-data/disp32
-256     # . . discard args
-257     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
-258 $convert:end:
-259     # flush(out)
-260     # . . push args
-261     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
-262     # . . call
-263     e8/call  flush/disp32
-264     # . . discard args
-265     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
-266     # . reclaim locals
-267     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
-268     # . restore registers
-269     5f/pop-to-EDI
-270     5b/pop-to-EBX
-271     5a/pop-to-EDX
-272     59/pop-to-ECX
-273     58/pop-to-EAX
-274     # . epilog
-275     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
-276     5d/pop-to-EBP
-277     c3/return
-278 
-279 # . . vim:nowrap:textwidth=0
+  3 #
+  4 # To build:
+  5 #   $ ./subx translate 0*.subx apps/subx-common.subx apps/tests.subx  -o apps/tests
+  6 
+  7 == code
+  8 #   instruction                     effective address                                                   register    displacement    immediate
+  9 # . op          subop               mod             rm32          base        index         scale       r32
+ 10 # . 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
+ 11 
+ 12 Entry:
+ 13     # Heap = new-segment(Heap-size)
+ 14     # . . push args
+ 15     68/push  Heap/imm32
+ 16     68/push  Heap-size/imm32
+ 17     # . . call
+ 18     e8/call  new-segment/disp32
+ 19     # . . discard args
+ 20     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 21     # initialize-trace-stream(256KB)
+ 22     # . . push args
+ 23     68/push  0x40000/imm32/256KB
+ 24     # . . call
+ 25     e8/call  initialize-trace-stream/disp32
+ 26     # . . discard args
+ 27     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+ 28 
+ 29     # run tests if necessary, convert stdin if not
+ 30     # . prolog
+ 31     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 32     # initialize heap
+ 33     # - if argc > 1 and argv[1] == "test", then return run_tests()
+ 34     # . argc > 1
+ 35     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
+ 36     7e/jump-if-lesser-or-equal  $run-main/disp8
+ 37     # . argv[1] == "test"
+ 38     # . . push args
+ 39     68/push  "test"/imm32
+ 40     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+ 41     # . . call
+ 42     e8/call  kernel-string-equal?/disp32
+ 43     # . . discard args
+ 44     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 45     # . check result
+ 46     3d/compare-EAX-and  1/imm32
+ 47     75/jump-if-not-equal  $run-main/disp8
+ 48     # . run-tests()
+ 49     e8/call  run-tests/disp32
+ 50     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
+ 51     eb/jump  $main:end/disp8
+ 52 $run-main:
+ 53     # - otherwise convert stdin
+ 54     # convert(Stdin, Stdout)
+ 55     # . . push args
+ 56     68/push  Stdout/imm32
+ 57     68/push  Stdin/imm32
+ 58     # . . call
+ 59     e8/call  convert/disp32
+ 60     # . . discard args
+ 61     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+ 62     # . syscall(exit, 0)
+ 63     bb/copy-to-EBX  0/imm32
+ 64 $main:end:
+ 65     b8/copy-to-EAX  1/imm32/exit
+ 66     cd/syscall  0x80/imm8
+ 67 
+ 68 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
+ 69     # pseudocode
+ 70     #   bool tests-found = false
+ 71     #   var line = new-stream(512, 1)
+ 72     #   var new-code-segment = new-stream(Segment-size, 1)
+ 73     #   write(new-code-segment, "\n==code\n")
+ 74     #   write(new-code-segment, "run-tests:\n")
+ 75     #   while true
+ 76     #     clear-stream(line)
+ 77     #     read-line-buffered(in, line)
+ 78     #     if (line->write == 0) break               # end of file
+ 79     #     var word-slice = next-word(line)
+ 80     #     if is-label?(word-slice)
+ 81     #       if slice-starts-with?(word-slice, "test-")
+ 82     #         tests-found = true
+ 83     #         write(new-code-segment, "  e8/call  ")
+ 84     #         write-slice(new-code-segment, word-slice)
+ 85     #         write(new-code-segment, "/disp32\n")
+ 86     #     rewind-stream(line)
+ 87     #     write-stream-data(out, line)
+ 88     #   if tests-found
+ 89     #     write(new-code-segment, "  c3/return\n")
+ 90     #     write-stream-data(out, new-code-segment)
+ 91     #   flush(out)
+ 92     #
+ 93     # . prolog
+ 94     55/push-EBP
+ 95     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
+ 96     # . save registers
+ 97     50/push-EAX
+ 98     51/push-ECX
+ 99     52/push-EDX
+100     53/push-EBX
+101     57/push-EDI
+102     # var line/ECX : (address stream byte) = stream(512)
+103     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
+104     68/push  0x200/imm32/length
+105     68/push  0/imm32/read
+106     68/push  0/imm32/write
+107     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
+108     # var word-slice/EDX = {0, 0}
+109     68/push  0/imm32/end
+110     68/push  0/imm32/start
+111     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
+112     # tests-found?/EBX = false
+113     31/xor                          3/mod/direct    3/rm32/EBX    .           .             .           3/r32/EBX   .               .                 # clear EBX
+114     # new-code-segment/EDI = new-stream(Heap, Segment-size, 1)
+115     # . EAX = new-stream(Heap, Segment-size, 1)
+116     # . . push args
+117     68/push  1/imm32
+118     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
+119     68/push  Heap/imm32
+120     # . . call
+121     e8/call  new-stream/disp32
+122     # . . discard args
+123     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
+124     # . EDI = EAX
+125     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
+126     # write(new-code-segment, "\n== code\n")
+127     # . . push args
+128     68/push  "\n== code\n"/imm32
+129     57/push-EDI
+130     # . . call
+131     e8/call  write/disp32
+132     # . . discard args
+133     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+134     # write(new-code-segment, "run-tests:\n")
+135     # . . push args
+136     68/push  "run-tests:\n"/imm32
+137     57/push-EDI
+138     # . . call
+139     e8/call  write/disp32
+140     # . . discard args
+141     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+142 $convert:loop:
+143     # clear-stream(line)
+144     # . . push args
+145     51/push-ECX
+146     # . . call
+147     e8/call  clear-stream/disp32
+148     # . . discard args
+149     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+150     # read-line-buffered(in, line)
+151     # . . push args
+152     51/push-ECX
+153     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
+154     # . . call
+155     e8/call  read-line-buffered/disp32
+156     # . . discard args
+157     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+158 $convert:check0:
+159     # if (line->write == 0) break
+160     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
+161     0f 84/jump-if-equal  $convert:break/disp32
+162     # next-word(line, word-slice)
+163     # . . push args
+164     52/push-EDX
+165     51/push-ECX
+166     # . . call
+167     e8/call  next-word/disp32
+168     # . . discard args
+169     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+170 $convert:check-for-label:
+171     # if (!is-label?(word-slice)) continue
+172     # . EAX = is-label?(word-slice)
+173     # . . push args
+174     52/push-EDX
+175     # . . call
+176     e8/call  is-label?/disp32
+177     # . . discard args
+178     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+179     # . if (EAX == 0) continue
+180     3d/compare-EAX-and  0/imm32
+181     74/jump-if-equal  $convert:continue/disp8
+182 $convert:check-label-prefix:
+183     # strip trailing ':' from word-slice
+184     ff          1/subop/decrement   1/mod/*+disp8   2/rm32/EDX    .           .             .           .           4/disp8         .                 # decrement *(EDX+4)
+185     # if !slice-starts-with?(word-slice, "test-") continue
+186     # . . push args
+187     68/push  "test-"/imm32
+188     52/push-EDX
+189     # . . call
+190     e8/call  slice-starts-with?/disp32
+191     # . . discard args
+192     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+193     # . if (EAX == 0) break
+194     3d/compare-EAX-and  0/imm32
+195     74/jump-if-equal  $convert:continue/disp8
+196 $convert:call-test-function:
+197     # tests-found? = true
+198     bb/copy-to-EBX  1/imm32/true
+199     # write(new-code-segment, "  e8/call  ")
+200     # . . push args
+201     68/push  "  e8/call  "/imm32
+202     57/push-EDI
+203     # . . call
+204     e8/call  write/disp32
+205     # . . discard args
+206     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+207     # write-slice(new-code-segment, word-slice)
+208     # . . push args
+209     52/push-EDX
+210     57/push-EDI
+211     # . . call
+212     e8/call  write-slice/disp32
+213     # . . discard args
+214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+215     # write(new-code-segment, "/disp32\n")
+216     # . . push args
+217     68/push  "/disp32\n"/imm32
+218     57/push-EDI
+219     # . . call
+220     e8/call  write/disp32
+221     # . . discard args
+222     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+223 $convert:continue:
+224     # rewind-stream(line)
+225     # . . push args
+226     51/push-ECX
+227     # . . call
+228     e8/call  rewind-stream/disp32
+229     # . . discard args
+230     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+231     # write-stream-data(out, line)
+232     # . . push args
+233     51/push-ECX
+234     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+235     # . . call
+236     e8/call  write-stream-data/disp32
+237     # . . discard args
+238     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+239     # loop
+240     e9/jump  $convert:loop/disp32
+241 $convert:break:
+242     # if (!tests-found?) goto end
+243     81          7/subop/compare     3/mod/direct    3/rm32/EBX    .           .             .           .           .               0/imm32           # compare EBX
+244     74/jump-if-equal  $convert:end/disp8
+245     # write(new-code-segment, "  c3/return\n")
+246     # . . push args
+247     68/push  "  c3/return\n"/imm32
+248     57/push-EDI
+249     # . . call
+250     e8/call  write/disp32
+251     # . . discard args
+252     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+253     # write-stream-data(out, new-code-segment)
+254     # . . push args
+255     57/push-EDI
+256     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+257     # . . call
+258     e8/call  write-stream-data/disp32
+259     # . . discard args
+260     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
+261 $convert:end:
+262     # flush(out)
+263     # . . push args
+264     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
+265     # . . call
+266     e8/call  flush/disp32
+267     # . . discard args
+268     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
+269     # . reclaim locals
+270     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
+271     # . restore registers
+272     5f/pop-to-EDI
+273     5b/pop-to-EBX
+274     5a/pop-to-EDX
+275     59/pop-to-ECX
+276     58/pop-to-EAX
+277     # . epilog
+278     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
+279     5d/pop-to-EBP
+280     c3/return
+281 
+282 # . . vim:nowrap:textwidth=0