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