https://github.com/akkartik/mu/blob/master/subx/apps/dquotes.subx
   1 # Translate literal strings within double quotes.
   2 # Replace them with references to new variables in the data segment.
   3 #
   4 # To run (from the subx/ directory):
   5 #   $ ./subx translate *.subx apps/dquote.subx -o apps/dquote
   6 #   $ cat x
   7 #   == code
   8 #   ab "cd ef"/imm32
   9 #   $ cat x  |./subx run apps/dquote
  10 #   == code
  11 #   ab __string1/imm32
  12 #   == data
  13 #   __string1:
  14 #     5/imm32
  15 #     0x63/c 0x64/d 0x20/  0x65/e 0x66/f
  16 
  17 == code
  18 #   instruction                     effective address                                                   register    displacement    immediate
  19 # . op          subop               mod             rm32          base        index         scale       r32
  20 # . 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
  21 
  22 Entry:  # run tests if necessary, convert stdin if not
  23 
  24     # for debugging: run a single test
  25 #?     e8/call test-next-word-returns-string-with-escapes/disp32
  26 #?     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  27 #?     eb/jump  $main:end/disp8
  28 
  29     # . prolog
  30     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
  31     # - if argc > 1 and argv[1] == "test", then return run_tests()
  32     # . argc > 1
  33     81          7/subop/compare     1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0/disp8         1/imm32           # compare *EBP
  34     7e/jump-if-lesser-or-equal  $run-main/disp8
  35     # . argv[1] == "test"
  36     # . . push args
  37     68/push  "test"/imm32
  38     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
  39     # . . call
  40     e8/call  kernel-string-equal?/disp32
  41     # . . discard args
  42     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
  43     # . check result
  44     3d/compare-EAX-and  1/imm32
  45     75/jump-if-not-equal  $run-main/disp8
  46     # . run-tests()
  47     e8/call  run-tests/disp32
  48     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
  49     eb/jump  $main:end/disp8
  50 $run-main:
  51     # - otherwise convert stdin
  52     # var ed/EAX : exit-descriptor
  53     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # subtract from ESP
  54     89/copy                         3/mod/direct    0/rm32/EAX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EAX
  55     # configure ed to really exit()
  56     # . ed->target = 0
  57     c7          0/subop/copy        0/mod/direct    0/rm32/EAX    .           .             .           .           .               0/imm32           # copy to *EAX
  58     # return convert(Stdin, 1/stdout, 2/stderr, ed)
  59     # . . push args
  60     50/push-EAX/ed
  61     68/push  Stderr/imm32
  62     68/push  Stdout/imm32
  63     68/push  Stdin/imm32
  64     # . . call
  65     e8/call  convert/disp32
  66     # . . discard args
  67     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x10/imm32        # add to ESP
  68     # . syscall(exit, 0)
  69     bb/copy-to-EBX  0/imm32
  70 $main:end:
  71     b8/copy-to-EAX  1/imm32/exit
  72     cd/syscall  0x80/imm8
  73 
  74 # data structure:
  75 #   row: pair of (address array byte) and (address stream byte)
  76 #   table: (address stream row)
  77 
  78 convert:  # in : (address buffered-file), out : (address buffered-file) -> <void>
  79     # pseudocode:
  80     #   var line = new-stream(512, 1)
  81     #   var new-data-segment = new-stream(Heap, Segment-size, 1)
  82     #   write-stream(new-data-segment, "== data\n")
  83     #   while true
  84     #     clear-stream(line)
  85     #     read-line(in, line)
  86     #     if (line->write == 0) break               # end of file
  87     #     while true
  88     #       var word-slice = next-word(line)
  89     #       if slice-empty?(word-slice)             # end of line
  90     #         break
  91     #       if slice-starts-with?(word-slice, "#")  # comment
  92     #         continue
  93     #       if slice-starts-with?(word-slice, '"')  # string literal <== what we're here for
  94     #         process-string-literal(word-slice, out, new-data-segment)
  95     #       else
  96     #         write-slice(out, word-slice)
  97     #   write-stream-data(out, new-data-segment)
  98     #   flush(out)
  99     #
 100     # . prolog
 101     55/push-EBP
 102     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 103     # . save registers
 104     50/push-EAX
 105     51/push-ECX
 106     52/push-EDX
 107     53/push-EBX
 108     56/push-ESI
 109     57/push-EDI
 110     # var line/ECX : (address stream byte) = stream(512)
 111     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x200/imm32       # subtract from ESP
 112     68/push  0x200/imm32/length
 113     68/push  0/imm32/read
 114     68/push  0/imm32/write
 115     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 116     # var word-slice/EDX = {0, 0}
 117     68/push  0/imm32/end
 118     68/push  0/imm32/curr
 119     89/copy                         3/mod/direct    2/rm32/EDX    .           .             .           4/r32/ESP   .               .                 # copy ESP to EDX
 120     # new-data-segment/EDI = new-stream(Heap, Segment-size, 1)
 121     # . EAX = new-stream(Heap, Segment-size, 1)
 122     # . . push args
 123     68/push  1/imm32
 124     ff          6/subop/push        0/mod/indirect  5/rm32/.disp32            .             .           .           Segment-size/disp32               # push *Segment-size
 125     68/push  Heap/imm32
 126     # . . call
 127     e8/call  new-stream/disp32
 128     # . . discard args
 129     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 130     # . EDI = EAX
 131     89/copy                         3/mod/direct    7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to EDI
 132     # write(new-data-segment, "== data\n")
 133     # . . push args
 134     68/push  "== data\n"/imm32
 135     57/push-EDI
 136     # . . call
 137     e8/call  write/disp32
 138     # . . discard args
 139     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 140 $convert:line-loop:
 141     # clear-stream(line)
 142     # . . push args
 143     51/push-ECX
 144     # . . call
 145     e8/call  clear-stream/disp32
 146     # . . discard args
 147     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 148     # read-line(in, line)
 149     # . . push args
 150     51/push-ECX
 151     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 152     # . . call
 153     e8/call  read-line/disp32
 154     # . . discard args
 155     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 156 $convert:check0:
 157     # if (line->write == 0) break
 158     81          7/subop/compare     0/mod/indirect  1/rm32/ECX    .           .             .           .           .               0/imm32           # compare *ECX
 159     0f 84/jump-if-equal  $convert:break/disp32
 160 $convert:word-loop:
 161     # next-word(line, word-slice)
 162     # . . push args
 163     52/push-EDX
 164     51/push-ECX
 165     # . . call
 166     e8/call  next-word/disp32
 167     # . . discard args
 168     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 169 $convert:check1:
 170     # if (slice-empty?(word-slice)) break
 171     # . EAX = slice-empty?(word-slice)
 172     # . . push args
 173     52/push-EDX
 174     # . . call
 175     e8/call  slice-empty?/disp32
 176     # . . discard args
 177     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 178     # . if (EAX != 0) break
 179     3d/compare-EAX-and  0/imm32
 180     0f 85/jump-if-not-equal  $convert:next-line/disp32
 181 $convert:check-for-comment:
 182     # if (slice-starts-with?(word-slice, "#")) continue
 183     # . start/ESI = word-slice->start
 184     8b/copy                         0/mod/indirect  2/rm32/EDX    .           .             .           6/r32/ESI   .               .                 # copy *EDX to ESI
 185     # . c/EAX = *start
 186     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 187     8a/copy-byte                    0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/AL    .               .                 # copy byte at *ESI to AL
 188     # . if (EAX == '#') continue
 189     3d/compare-EAX-and  0x23/imm32/hash
 190     74/jump-if-equal  $convert:word-loop/disp8
 191 $convert:check-for-string-literal:
 192     3d/compare-EAX-and  0x22/imm32/hash
 193     75/jump-if-not-equal  $convert:regular-word/disp8
 194 $convert:string-literal:
 195     # process-string-literal(word-slice, out, new-data-segment)
 196     # . . push args
 197     57/push-EDI
 198     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 199     52/push-EDX
 200     # . . call
 201     e8/call  process-string-literal/disp32
 202     # . . discard args
 203     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 204     # continue
 205     eb/jump  $convert:next-word/disp8
 206 $convert:regular-word:
 207     # write-slice(out, word-slice)
 208     # . . push args
 209     52/push-EDX
 210     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 211     # . . call
 212     e8/call  write-slice/disp32
 213     # . . discard args
 214     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 215     # fall through
 216 $convert:next-word:
 217     # write-buffered(out, " ")
 218     # . . push args
 219     68/push  " "/imm32
 220     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 221     # . . call
 222     e8/call  write-buffered/disp32
 223     # . . discard args
 224     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 225     # loop
 226     eb/jump  $convert:word-loop/disp8
 227 $convert:next-line:
 228     # write-buffered(out, "\n")
 229     # . . push args
 230     68/push  Newline/imm32
 231     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 232     # . . call
 233     e8/call  write-buffered/disp32
 234     # . . discard args
 235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 236     # loop
 237     e9/jump  $convert:line-loop/disp32
 238 $convert:break:
 239 $convert:end:
 240     # . reclaim locals
 241     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x214/imm32       # add to ESP
 242     # . restore registers
 243     5f/pop-to-EDI
 244     5e/pop-to-ESI
 245     5b/pop-to-EBX
 246     5a/pop-to-EDX
 247     59/pop-to-ECX
 248     58/pop-to-EAX
 249     # . epilog
 250     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 251     5d/pop-to-EBP
 252     c3/return
 253 
 254 # Write out 'string-literal' in a new format to 'out-segment', assign it a new
 255 # label, and write the new label out to 'out'.
 256 process-string-literal:  # string-literal : (address slice), out : (address buffered-file), out-segment : (address stream)
 257     # pseudocode:
 258     #   print(out-segment, "_string#{Next-string-literal}:\n")
 259     #   emit-string-literal-data(out-segment, string-literal)
 260     #   print(out, "_string#{Next-string-literal}")
 261     #   emit-metadata(out, string-literal)
 262     #   ++ *Next-string-literal
 263     #
 264     # . prolog
 265     55/push-EBP
 266     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 267     # . save registers
 268     51/push-ECX
 269     # var int32-stream/ECX = stream(10)  # number of decimal digits a 32-bit number can have
 270     81          5/subop/subtract    3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xa/imm32         # subtract from ESP
 271     68/push  0xa/imm32/decimal-digits-in-32bit-number
 272     68/push  0/imm32/read
 273     68/push  0/imm32/write
 274     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 275     # print(out-segment, "_string#{Next-string-literal}:\n")
 276     # . write(out-segment, "_string")
 277     # . . push args
 278     68/push  "_string"/imm32
 279     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 280     # . . call
 281     e8/call  write/disp32
 282     # . . discard args
 283     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 284     # . print-int32-decimal(out-segment, *Next-string-literal)
 285     # . write(out-segment, ":\n")
 286     # . . push args
 287     68/push  ":\n"/imm32
 288     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 289     # . . call
 290     e8/call  write/disp32
 291     # . . discard args
 292     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 293     # emit-string-literal-data(out-segment, string-literal)
 294     # write(out-segment, "\n")
 295     # . . push args
 296     68/push  Newline/imm32
 297     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0x10/disp8      .                 # push *(EBP+16)
 298     # . . call
 299     e8/call  write/disp32
 300     # . . discard args
 301     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 302     # print(out, "_string#{Next-string-literal}")
 303     # . write-buffered(out, "_string")
 304     # . . push args
 305     68/push  "_string"/imm32
 306     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 307     # . . call
 308     e8/call  write-buffered/disp32
 309     # . . discard args
 310     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 311     # . print-int32-decimal(tmp, *Next-string-literal)
 312     # emit-metadata(out, string-literal)
 313     # ++ *Next-string-literal
 314     ff          0/subop/increment   0/mod/indirect  5/rm32/.disp32            .             .           .           Next-string-literal/disp32        # increment *Num-test-failures
 315 $process-string-literal:end:
 316     # . reclaim locals
 317     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0x16/imm32        # add to ESP
 318     # . restore registers
 319     59/pop-to-ECX
 320     # . epilog
 321     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 322     5d/pop-to-EBP
 323     c3/return
 324 
 325 test-convert-is-idempotent-by-default:
 326     # . prolog
 327     55/push-EBP
 328     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 329     # setup
 330     # . clear-stream(_test-input-stream)
 331     # . . push args
 332     68/push  _test-input-stream/imm32
 333     # . . call
 334     e8/call  clear-stream/disp32
 335     # . . discard args
 336     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 337     # . clear-stream(_test-input-buffered-file+4)
 338     # . . push args
 339     b8/copy-to-EAX  _test-input-buffered-file/imm32
 340     05/add-to-EAX  4/imm32
 341     50/push-EAX
 342     # . . call
 343     e8/call  clear-stream/disp32
 344     # . . discard args
 345     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 346     # . clear-stream(_test-output-stream)
 347     # . . push args
 348     68/push  _test-output-stream/imm32
 349     # . . call
 350     e8/call  clear-stream/disp32
 351     # . . discard args
 352     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 353     # . clear-stream(_test-output-buffered-file+4)
 354     # . . push args
 355     b8/copy-to-EAX  _test-output-buffered-file/imm32
 356     05/add-to-EAX  4/imm32
 357     50/push-EAX
 358     # . . call
 359     e8/call  clear-stream/disp32
 360     # . . discard args
 361     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 362     # initialize input (meta comments in parens)
 363     #   # comment 1
 364     #     # comment 2 indented
 365     #   == code  (new segment)
 366     #   # comment 3 inside a segment
 367     #   1
 368     #                         (empty line)
 369     #   2 3 # comment 4 inline with other contents
 370     #   == data  (new segment)
 371     #   4 5/imm32
 372     # . write(_test-input-stream, "# comment 1\n")
 373     # . . push args
 374     68/push  "# comment 1\n"/imm32
 375     68/push  _test-input-stream/imm32
 376     # . . call
 377     e8/call  write/disp32
 378     # . . discard args
 379     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 380     # . write(_test-input-stream, "  # comment 2 indented\n")
 381     # . . push args
 382     68/push  "  # comment 2 indented\n"/imm32
 383     68/push  _test-input-stream/imm32
 384     # . . call
 385     e8/call  write/disp32
 386     # . . discard args
 387     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 388     # . write(_test-input-stream, "== code\n")
 389     # . . push args
 390     68/push  "== code\n"/imm32
 391     68/push  _test-input-stream/imm32
 392     # . . call
 393     e8/call  write/disp32
 394     # . . discard args
 395     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 396     # . write(_test-input-stream, "# comment 3 inside a segment\n")
 397     # . . push args
 398     68/push  "# comment 3 inside a segment\n"/imm32
 399     68/push  _test-input-stream/imm32
 400     # . . call
 401     e8/call  write/disp32
 402     # . . discard args
 403     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 404     # . write(_test-input-stream, "1\n")
 405     # . . push args
 406     68/push  "1\n"/imm32
 407     68/push  _test-input-stream/imm32
 408     # . . call
 409     e8/call  write/disp32
 410     # . . discard args
 411     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 412     # . write(_test-input-stream, "\n")  # empty line
 413     # . . push args
 414     68/push  "\n"/imm32
 415     68/push  _test-input-stream/imm32
 416     # . . call
 417     e8/call  write/disp32
 418     # . . discard args
 419     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 420     # . write(_test-input-stream, "2 3 # comment 4 inline with other contents\n")
 421     # . . push args
 422     68/push  "2 3 # comment 4 inline with other contents\n"/imm32
 423     68/push  _test-input-stream/imm32
 424     # . . call
 425     e8/call  write/disp32
 426     # . . discard args
 427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 428     # . write(_test-input-stream, "== data\n")
 429     # . . push args
 430     68/push  "== data\n"/imm32
 431     68/push  _test-input-stream/imm32
 432     # . . call
 433     e8/call  write/disp32
 434     # . . discard args
 435     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 436     # . write(_test-input-stream, "4 5/imm32\n")
 437     # . . push args
 438     68/push  "4 5/imm32\n"/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     # . flush(_test-output-buffered-file)
 453     # . . push args
 454     68/push  _test-output-buffered-file/imm32
 455     # . . call
 456     e8/call  flush/disp32
 457     # . . discard args
 458     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 459     # check output
 460     #     (comment dropped for now)
 461     #     (comment dropped for now)
 462     #   == code
 463     #     (comment dropped for now)
 464     #   1
 465     #     (comment dropped for now)
 466     #   2 3
 467     #   == data
 468     #   4 5/imm32
 469     # We don't care right now what exactly happens to comments. Trailing spaces are also minor details.
 470 +-- 26 lines: #?     # dump output ---------------------------------------------------------------------------------------------------------------------------
 496     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 497     # . . push args
 498     68/push  "F - test-convert/0"/imm32
 499     68/push  ""/imm32
 500     68/push  _test-output-stream/imm32
 501     # . . call
 502     e8/call  check-next-stream-line-equal/disp32
 503     # . . discard args
 504     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 505     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 506     # . . push args
 507     68/push  "F - test-convert/1"/imm32
 508     68/push  ""/imm32
 509     68/push  _test-output-stream/imm32
 510     # . . call
 511     e8/call  check-next-stream-line-equal/disp32
 512     # . . discard args
 513     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 514     # . check-next-stream-line-equal(_test-output-stream, "== code ", msg)
 515     # . . push args
 516     68/push  "F - test-convert/2"/imm32
 517     68/push  "== code "/imm32
 518     68/push  _test-output-stream/imm32
 519     # . . call
 520     e8/call  check-next-stream-line-equal/disp32
 521     # . . discard args
 522     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 523     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 524     # . . push args
 525     68/push  "F - test-convert/3"/imm32
 526     68/push  ""/imm32
 527     68/push  _test-output-stream/imm32
 528     # . . call
 529     e8/call  check-next-stream-line-equal/disp32
 530     # . . discard args
 531     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 532     # . check-next-stream-line-equal(_test-output-stream, "1 ", msg)
 533     # . . push args
 534     68/push  "F - test-convert/4"/imm32
 535     68/push  "1 "/imm32
 536     68/push  _test-output-stream/imm32
 537     # . . call
 538     e8/call  check-next-stream-line-equal/disp32
 539     # . . discard args
 540     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 541     # . check-next-stream-line-equal(_test-output-stream, "", msg)
 542     # . . push args
 543     68/push  "F - test-convert/5"/imm32
 544     68/push  ""/imm32
 545     68/push  _test-output-stream/imm32
 546     # . . call
 547     e8/call  check-next-stream-line-equal/disp32
 548     # . . discard args
 549     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 550     # . check-next-stream-line-equal(_test-output-stream, "2 3 ", msg)
 551     # . . push args
 552     68/push  "F - test-convert/6"/imm32
 553     68/push  "2 3 "/imm32
 554     68/push  _test-output-stream/imm32
 555     # . . call
 556     e8/call  check-next-stream-line-equal/disp32
 557     # . . discard args
 558     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 559     # . check-next-stream-line-equal(_test-output-stream, "== data ", msg)
 560     # . . push args
 561     68/push  "F - test-convert/7"/imm32
 562     68/push  "== data "/imm32
 563     68/push  _test-output-stream/imm32
 564     # . . call
 565     e8/call  check-next-stream-line-equal/disp32
 566     # . . discard args
 567     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 568     # . check-next-stream-line-equal(_test-output-stream, "4 5/imm32 ", msg)
 569     # . . push args
 570     68/push  "F - test-convert/8"/imm32
 571     68/push  "4 5/imm32 "/imm32
 572     68/push  _test-output-stream/imm32
 573     # . . call
 574     e8/call  check-next-stream-line-equal/disp32
 575     # . . discard args
 576     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 577     # . epilog
 578     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 579     5d/pop-to-EBP
 580     c3/return
 581 
 582 # (re)compute the bounds of the next word in the line
 583 # return empty string on reaching end of file
 584 next-word:  # line : (address stream byte), out : (address slice)
 585     # . prolog
 586     55/push-EBP
 587     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 588     # . save registers
 589     50/push-EAX
 590     51/push-ECX
 591     56/push-ESI
 592     57/push-EDI
 593     # ESI = line
 594     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 595     # EDI = out
 596     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0xc/disp8       .                 # copy *(EBP+12) to EDI
 597     # skip-chars-matching(line, ' ')
 598     # . . push args
 599     68/push  0x20/imm32/space
 600     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 601     # . . call
 602     e8/call  skip-chars-matching/disp32
 603     # . . discard args
 604     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 605 $next-word:check0:
 606     # if (line->read >= line->write) clear out and return
 607     # . EAX = line->read
 608     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ESI+4) to EAX
 609     # . if (EAX < line->write) goto next check
 610     3b/compare                      0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # compare EAX with *ESI
 611     7c/jump-if-lesser  $next-word:check-for-comment/disp8
 612     # . return out = {0, 0}
 613     c7          0/subop/copy        0/mod/direct    7/rm32/EDI    .           .             .           .           .               0/imm32           # copy to *EDI
 614     c7          0/subop/copy        1/mod/*+disp8   7/rm32/EDI    .           .             .           .           4/disp8         0/imm32           # copy to *(EDI+4)
 615     eb/jump  $next-word:end/disp8
 616 $next-word:check-for-comment:
 617     # out->start = &line->data[line->read]
 618     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 619     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
 620     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
 621     # if line->data[line->read] == '#'
 622     # . EAX = line->data[line->read]
 623     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 624     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
 625     # . compare
 626     3d/compare-EAX-and  0x23/imm32/pound
 627     75/jump-if-not-equal  $next-word:check-for-string-literal/disp8
 628 $next-word:comment:
 629     # out->end = &line->data[line->write]
 630     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           0/r32/EAX   .               .                 # copy *ESI to EAX
 631     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
 632     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
 633     # line->read = line->write  # skip rest of line
 634     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(ESI+4)
 635     # return
 636     eb/jump  $next-word:end/disp8
 637 $next-word:check-for-string-literal:
 638     # if line->data[line->read] == '"'
 639     # . EAX = line->data[line->read]
 640     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 641     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
 642     # . compare
 643     3d/compare-EAX-and  0x22/imm32/dquote
 644     75/jump-if-not-equal  $next-word:regular-word/disp8
 645 $next-word:string-literal:
 646     # ++line->read  # skip '"'
 647     # . persist line->read
 648     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
 649     # . ++line->read
 650     ff          0/subop/increment   1/mod/*+disp8   6/rm32/ESI    .           .             .           .           4/disp8         .                 # increment *(ESI+4)
 651     # parse-string(line, out)
 652     # . . push args
 653     57/push-EDI
 654     56/push-ESI
 655     # . . call
 656     e8/call  parse-string/disp32
 657     # . . discard args
 658     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 659     # fall through
 660 $next-word:regular-word:
 661     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
 662     # . . push args
 663     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 664     # . . call
 665     e8/call  skip-chars-not-matching-whitespace/disp32
 666     # . . discard args
 667     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 668     # out->end = &line->data[line->read]
 669     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 670     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
 671     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
 672 $next-word:end:
 673     # . restore registers
 674     5f/pop-to-EDI
 675     5e/pop-to-ESI
 676     59/pop-to-ECX
 677     58/pop-to-EAX
 678     # . epilog
 679     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 680     5d/pop-to-EBP
 681     c3/return
 682 
 683 parse-string:  # line : (address stream byte), out : (address slice)
 684     # pseudocode:
 685     #   ESI = line
 686     #   curr/ECX = line->data[line->read]
 687     #   max/EDX = line->data[line->write]
 688     #   while curr >= max
 689     #     if (*curr == '"') ++curr, break
 690     #     if (*curr == '\\') curr+=2, continue
 691     #     ++curr
 692     #   line->read = curr - line->data
 693     #   out->end = curr
 694     #
 695     # . prolog
 696     55/push-EBP
 697     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 698     # . save registers
 699     50/push-EAX
 700     51/push-ECX
 701     52/push-EDX
 702     56/push-ESI
 703     # ESI = line
 704     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 705     # curr/ECX = &table->data[table->read]
 706     # . ECX = table->read
 707     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 708     # . ECX = table->data + ECX
 709     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  1/index/ECX   .           1/r32/ECX   0xc/disp8       .                 # copy ESI+ECX+12 to ECX
 710     # max/EDX = &table->data[table->write]
 711     # . EDX = table->write
 712     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           2/r32/EDX   .               .                 # copy *ESI to EDX
 713     # . EDX = table->data + EDX
 714     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/ESI  2/index/EDX   .           2/r32/EDX   0xc/disp8       .                 # copy ESI+EDX+12 to EDX
 715     # clear EAX
 716     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
 717 $parse-string:loop:
 718     # if (curr >= max) break
 719     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
 720     7d/jump-if-greater-or-equal  $parse-string:break/disp8
 721     # c/EAX = *curr
 722     8a/copy-byte                    0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/AL    .               .                 # copy byte at *ECX to AL
 723 $parse-string:check1:
 724     # if (c == '"') break  # rely on caller to skip trailing non-whitespace
 725     3d/compare-EAX-and  0x22/imm32/dquote
 726     74/jump-if-equal  $parse-string:break/disp8
 727 $parse-string:check2:
 728     # if (c == '\\') ++curr
 729     3d/compare-EAX-and  0x5c/imm32/backslash
 730     75/jump-if-not-equal  $parse-string:continue/disp8
 731     # . ++curr
 732     41/increment-ECX
 733 $parse-string:continue:
 734     # ++curr
 735     41/increment-ECX
 736     # loop
 737     eb/jump  $parse-string:loop/disp8
 738 $parse-string:break:
 739     # out->end = curr
 740     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           0/r32/EAX   0xc/disp8       .                 # copy *(EBP+12) to EDI
 741     89/copy                         1/mod/*+disp8   0/rm32/EAX    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(EAX+4)
 742     # line->read = curr - line - 12
 743     29/subtract                     3/mod/direct    1/rm32/ECX    .           .             .           6/r32/ESI   .               .                 # subtract ESI from ECX
 744     81          5/subop/subtract    3/mod/direct    1/rm32/ECX    .           .             .           .           .               0xc/imm32         # subtract from ECX
 745     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
 746 $parse-string:end:
 747     # . restore registers
 748     5e/pop-to-ESI
 749     5a/pop-to-EDX
 750     59/pop-to-ECX
 751     58/pop-to-EAX
 752     # . epilog
 753     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 754     5d/pop-to-EBP
 755     c3/return
 756 
 757 test-next-word:
 758     # . prolog
 759     55/push-EBP
 760     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 761     # setup
 762     # . clear-stream(_test-input-stream)
 763     # . . push args
 764     68/push  _test-input-stream/imm32
 765     # . . call
 766     e8/call  clear-stream/disp32
 767     # . . discard args
 768     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 769     # var slice/ECX = {0, 0}
 770     68/push  0/imm32/end
 771     68/push  0/imm32/start
 772     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 773     # write(_test-input-stream, "  ab")
 774     # . . push args
 775     68/push  "  ab"/imm32
 776     68/push  _test-input-stream/imm32
 777     # . . call
 778     e8/call  write/disp32
 779     # . . discard args
 780     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 781     # next-word(_test-input-stream, slice)
 782     # . . push args
 783     51/push-ECX
 784     68/push  _test-input-stream/imm32
 785     # . . call
 786     e8/call  next-word/disp32
 787     # . . discard args
 788     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 789     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
 790     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
 791     # . . push args
 792     68/push  "F - test-next-word: start"/imm32
 793     68/push  0xe/imm32
 794     # . . push slice->start - _test-input-stream
 795     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
 796     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
 797     50/push-EAX
 798     # . . call
 799     e8/call  check-ints-equal/disp32
 800     # . . discard args
 801     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 802     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
 803     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
 804     # . . push args
 805     68/push  "F - test-next-word: end"/imm32
 806     68/push  0x10/imm32
 807     # . . push slice->end - _test-input-stream
 808     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 809     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
 810     50/push-EAX
 811     # . . call
 812     e8/call  check-ints-equal/disp32
 813     # . . discard args
 814     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 815     # . epilog
 816     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 817     5d/pop-to-EBP
 818     c3/return
 819 
 820 test-next-word-returns-whole-comment:
 821     # . prolog
 822     55/push-EBP
 823     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 824     # setup
 825     # . clear-stream(_test-input-stream)
 826     # . . push args
 827     68/push  _test-input-stream/imm32
 828     # . . call
 829     e8/call  clear-stream/disp32
 830     # . . discard args
 831     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 832     # var slice/ECX = {0, 0}
 833     68/push  0/imm32/end
 834     68/push  0/imm32/start
 835     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 836     # write(_test-input-stream, "  # a")
 837     # . . push args
 838     68/push  "  # a"/imm32
 839     68/push  _test-input-stream/imm32
 840     # . . call
 841     e8/call  write/disp32
 842     # . . discard args
 843     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 844     # next-word(_test-input-stream, slice)
 845     # . . push args
 846     51/push-ECX
 847     68/push  _test-input-stream/imm32
 848     # . . call
 849     e8/call  next-word/disp32
 850     # . . discard args
 851     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 852     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
 853     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
 854     # . . push args
 855     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
 856     68/push  0xe/imm32
 857     # . . push slice->start - _test-input-stream
 858     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
 859     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
 860     50/push-EAX
 861     # . . call
 862     e8/call  check-ints-equal/disp32
 863     # . . discard args
 864     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 865     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
 866     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
 867     # . . push args
 868     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
 869     68/push  0x11/imm32
 870     # . . push slice->end - _test-input-stream
 871     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 872     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
 873     50/push-EAX
 874     # . . call
 875     e8/call  check-ints-equal/disp32
 876     # . . discard args
 877     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 878     # . epilog
 879     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 880     5d/pop-to-EBP
 881     c3/return
 882 
 883 test-next-word-returns-empty-string-on-eof:
 884     # . prolog
 885     55/push-EBP
 886     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 887     # setup
 888     # . clear-stream(_test-input-stream)
 889     # . . push args
 890     68/push  _test-input-stream/imm32
 891     # . . call
 892     e8/call  clear-stream/disp32
 893     # . . discard args
 894     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 895     # var slice/ECX = {0, 0}
 896     68/push  0/imm32/end
 897     68/push  0/imm32/start
 898     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 899     # write nothing to _test-input-stream
 900     # next-word(_test-input-stream, slice)
 901     # . . push args
 902     51/push-ECX
 903     68/push  _test-input-stream/imm32
 904     # . . call
 905     e8/call  next-word/disp32
 906     # . . discard args
 907     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 908     # check-ints-equal(slice->end - slice->start, 0, msg)
 909     # . . push args
 910     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
 911     68/push  0/imm32
 912     # . . push slice->end - slice->start
 913     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 914     2b/subtract                     0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # subtract *ECX from EAX
 915     50/push-EAX
 916     # . . call
 917     e8/call  check-ints-equal/disp32
 918     # . . discard args
 919     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 920     # . epilog
 921     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 922     5d/pop-to-EBP
 923     c3/return
 924 
 925 test-next-word-returns-whole-string:
 926     # . prolog
 927     55/push-EBP
 928     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 929     # setup
 930     # . clear-stream(_test-input-stream)
 931     # . . push args
 932     68/push  _test-input-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     # var slice/ECX = {0, 0}
 938     68/push  0/imm32/end
 939     68/push  0/imm32/start
 940     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 941     # write(_test-input-stream, " \"a b\"/imm32 ")
 942     # . . push args
 943     68/push  " \"a b\"/imm32 "/imm32
 944     68/push  _test-input-stream/imm32
 945     # . . call
 946     e8/call  write/disp32
 947     # . . discard args
 948     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 949     # next-word(_test-input-stream, slice)
 950     # . . push args
 951     51/push-ECX
 952     68/push  _test-input-stream/imm32
 953     # . . call
 954     e8/call  next-word/disp32
 955     # . . discard args
 956     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 957     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
 958     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
 959     # . . push args
 960     68/push  "F - test-next-word-returns-whole-string: start"/imm32
 961     68/push  0xd/imm32
 962     # . . push slice->start - _test-input-stream
 963     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
 964     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
 965     50/push-EAX
 966     # . . call
 967     e8/call  check-ints-equal/disp32
 968     # . . discard args
 969     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 970     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
 971     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
 972     # . . push args
 973     68/push  "F - test-next-word-returns-whole-string: end"/imm32
 974     68/push  0x18/imm32
 975     # . . push slice->end - _test-input-stream
 976     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
 977     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
 978     50/push-EAX
 979     # . . call
 980     e8/call  check-ints-equal/disp32
 981     # . . discard args
 982     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 983     # . epilog
 984     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 985     5d/pop-to-EBP
 986     c3/return
 987 
 988 test-next-word-returns-string-with-escapes:
 989     # . prolog
 990     55/push-EBP
 991     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 992     # setup
 993     # . clear-stream(_test-input-stream)
 994     # . . push args
 995     68/push  _test-input-stream/imm32
 996     # . . call
 997     e8/call  clear-stream/disp32
 998     # . . discard args
 999     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
1000     # var slice/ECX = {0, 0}
1001     68/push  0/imm32/end
1002     68/push  0/imm32/start
1003     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
1004     # write(_test-input-stream, " \"a\\\"b\"/x")
1005     # . . push args
1006     68/push  " \"a\\\"b\"/x"/imm32
1007     68/push  _test-input-stream/imm32
1008     # . . call
1009     e8/call  write/disp32
1010     # . . discard args
1011     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1012     # next-word(_test-input-stream, slice)
1013     # . . push args
1014     51/push-ECX
1015     68/push  _test-input-stream/imm32
1016     # . . call
1017     e8/call  next-word/disp32
1018     # . . discard args
1019     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
1020     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
1021     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
1022     # . . push args
1023     68/push  "F - test-next-word-returns-string-with-escapes: start"/imm32
1024     68/push  0xd/imm32
1025     # . . push slice->start - _test-input-stream
1026     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
1027     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1028     50/push-EAX
1029     # . . call
1030     e8/call  check-ints-equal/disp32
1031     # . . discard args
1032     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1033     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
1034     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
1035     # . . push args
1036     68/push  "F - test-next-word-returns-string-with-escapes: end"/imm32
1037     68/push  0x15/imm32
1038     # . . push slice->end - _test-input-stream
1039     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
1040     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-input-stream/imm32 # subtract from EAX
1041     50/push-EAX
1042     # . . call
1043     e8/call  check-ints-equal/disp32
1044     # . . discard args
1045     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
1046     # . epilog
1047     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
1048     5d/pop-to-EBP
1049     c3/return
1050 
1051 == data
1052 
1053 Segment-size:
1054   # TODO: there's currently a tight size limit on segments because we aren't growing the heap
1055   0x100/imm32/4KB
1056 #?   0x1000/imm32/4KB
1057 
1058 Next-string-literal:
1059   1/imm32
1060 
1061 # . . vim:nowrap:textwidth=0