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