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