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