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