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