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