.
[mu.git] / linux / 124next-token.subx
blobcc115d7056edf667314c06cdceefba814ab83539
1 # Some tokenization primitives.
3 == code
4 #   instruction                     effective address                                                   register    displacement    immediate
5 # . op          subop               mod             rm32          base        index         scale       r32
6 # . 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
8 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
9 # on reaching end of file, return an empty interval
10 next-token:  # in: (addr stream byte), delimiter: byte, out: (addr slice)
11     # . prologue
12     55/push-ebp
13     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
14     # . save registers
15     50/push-eax
16     51/push-ecx
17     56/push-esi
18     57/push-edi
19     # esi = in
20     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
21     # edi = out
22     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0x10/disp8      .                 # copy *(ebp+16) to edi
23     # skip-chars-matching(in, delimiter)
24     # . . push args
25     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
26     56/push-esi
27     # . . call
28     e8/call  skip-chars-matching/disp32
29     # . . discard args
30     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
31     # out->start = &in->data[in->read]
32     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
33     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
34     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
35     # skip-chars-not-matching(in, delimiter)
36     # . . push args
37     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
38     56/push-esi
39     # . . call
40     e8/call  skip-chars-not-matching/disp32
41     # . . discard args
42     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
43     # out->end = &in->data[in->read]
44     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
45     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
46     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
47     # . restore registers
48     5f/pop-to-edi
49     5e/pop-to-esi
50     59/pop-to-ecx
51     58/pop-to-eax
52     # . epilogue
53     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
54     5d/pop-to-ebp
55     c3/return
57 test-next-token:
58     # . prologue
59     55/push-ebp
60     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
61     # setup
62     # . clear-stream(_test-stream)
63     # . . push args
64     68/push  _test-stream/imm32
65     # . . call
66     e8/call  clear-stream/disp32
67     # . . discard args
68     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
69     # var slice/ecx: slice
70     68/push  0/imm32/end
71     68/push  0/imm32/start
72     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
73     # write(_test-stream, "  ab")
74     # . . push args
75     68/push  "  ab"/imm32
76     68/push  _test-stream/imm32
77     # . . call
78     e8/call  write/disp32
79     # . . discard args
80     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
81     # next-token(_test-stream, 0x20/space, slice)
82     # . . push args
83     51/push-ecx
84     68/push  0x20/imm32
85     68/push  _test-stream/imm32
86     # . . call
87     e8/call  next-token/disp32
88     # . . discard args
89     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
90     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
91     # . check-ints-equal(slice->start - _test-stream, 14, msg)
92     # . . push args
93     68/push  "F - test-next-token: start"/imm32
94     68/push  0xe/imm32
95     # . . push slice->start - _test-stream
96     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
97     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
98     50/push-eax
99     # . . call
100     e8/call  check-ints-equal/disp32
101     # . . discard args
102     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
103     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
104     # . check-ints-equal(slice->end - _test-stream, 16, msg)
105     # . . push args
106     68/push  "F - test-next-token: end"/imm32
107     68/push  0x10/imm32
108     # . . push slice->end - _test-stream
109     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
110     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
111     50/push-eax
112     # . . call
113     e8/call  check-ints-equal/disp32
114     # . . discard args
115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
116     # . epilogue
117     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
118     5d/pop-to-ebp
119     c3/return
121 test-next-token-Eof:
122     # . prologue
123     55/push-ebp
124     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
125     # setup
126     # . clear-stream(_test-stream)
127     # . . push args
128     68/push  _test-stream/imm32
129     # . . call
130     e8/call  clear-stream/disp32
131     # . . discard args
132     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
133     # var slice/ecx: slice
134     68/push  0/imm32/end
135     68/push  0/imm32/start
136     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
137     # write nothing to _test-stream
138     # next-token(_test-stream, 0x20/space, slice)
139     # . . push args
140     51/push-ecx
141     68/push  0x20/imm32
142     68/push  _test-stream/imm32
143     # . . call
144     e8/call  next-token/disp32
145     # . . discard args
146     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
147     # check-ints-equal(slice->end, slice->start, msg)
148     # . . push args
149     68/push  "F - test-next-token-Eof"/imm32
150     ff          6/subop/push        1/mod/*+disp8   1/rm32/ecx    .           .             .           .           4/disp8         .                 # push *(ecx+4)
151     ff          6/subop/push        0/mod/indirect  1/rm32/ecx    .           .             .           .           .               .                 # push *ecx
152     # . . call
153     e8/call  check-ints-equal/disp32
154     # . . discard args
155     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
156     # . epilogue
157     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
158     5d/pop-to-ebp
159     c3/return
161 # extract the next run of characters that are different from a given 'delimiter' (skipping multiple delimiters if necessary)
162 # on reaching end of file, return an empty interval
163 next-token-from-slice:  # start: (addr byte), end: (addr byte), delimiter: byte, out: (addr slice)
164     # . prologue
165     55/push-ebp
166     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
167     # . save registers
168     50/push-eax
169     51/push-ecx
170     52/push-edx
171     57/push-edi
172     # ecx = end
173     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
174     # edx = delimiter
175     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8      .                 # copy *(ebp+16) to edx
176     # edi = out
177     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0x14/disp8      .                 # copy *(ebp+20) to edi
178     # eax = skip-chars-matching-in-slice(start, end, delimiter)
179     # . . push args
180     52/push-edx
181     51/push-ecx
182     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
183     # . . call
184     e8/call  skip-chars-matching-in-slice/disp32
185     # . . discard args
186     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
187     # out->start = eax
188     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
189     # eax = skip-chars-not-matching-in-slice(eax, end, delimiter)
190     # . . push args
191     52/push-edx
192     51/push-ecx
193     50/push-eax
194     # . . call
195     e8/call  skip-chars-not-matching-in-slice/disp32
196     # . . discard args
197     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
198     # out->end = eax
199     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
200     # . restore registers
201     5f/pop-to-edi
202     5a/pop-to-edx
203     59/pop-to-ecx
204     58/pop-to-eax
205     # . epilogue
206     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
207     5d/pop-to-ebp
208     c3/return
210 test-next-token-from-slice:
211     # . prologue
212     55/push-ebp
213     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
214     # (eax..ecx) = "  ab"
215     b8/copy-to-eax  "  ab"/imm32
216     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
217     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
218     05/add-to-eax  4/imm32
219     # var out/edi: slice
220     68/push  0/imm32/end
221     68/push  0/imm32/start
222     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
223     # next-token-from-slice(eax, ecx, 0x20/space, out)
224     # . . push args
225     57/push-edi
226     68/push  0x20/imm32
227     51/push-ecx
228     50/push-eax
229     # . . call
230     e8/call  next-token-from-slice/disp32
231     # . . discard args
232     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
233     # out->start should be at the 'a'
234     # . check-ints-equal(out->start - in->start, 2, msg)
235     # . . push args
236     68/push  "F - test-next-token-from-slice: start"/imm32
237     68/push  2/imm32
238     # . . push out->start - in->start
239     8b/copy                         0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # copy *edi to ecx
240     2b/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract eax from ecx
241     51/push-ecx
242     # . . call
243     e8/call  check-ints-equal/disp32
244     # . . discard args
245     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
246     # out->end should be after the 'b'
247     # check-ints-equal(out->end - in->start, 4, msg)
248     # . . push args
249     68/push  "F - test-next-token-from-slice: end"/imm32
250     68/push  4/imm32
251     # . . push out->end - in->start
252     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
253     2b/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract eax from ecx
254     51/push-ecx
255     # . . call
256     e8/call  check-ints-equal/disp32
257     # . . discard args
258     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
259     # . epilogue
260     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
261     5d/pop-to-ebp
262     c3/return
264 test-next-token-from-slice-Eof:
265     # . prologue
266     55/push-ebp
267     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
268     # var out/edi: slice
269     68/push  0/imm32/end
270     68/push  0/imm32/start
271     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
272     # next-token-from-slice(0, 0, 0x20/space, out)
273     # . . push args
274     57/push-edi
275     68/push  0x20/imm32
276     68/push  0/imm32
277     68/push  0/imm32
278     # . . call
279     e8/call  next-token-from-slice/disp32
280     # . . discard args
281     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
282     # out should be empty
283     # . check-ints-equal(out->end - out->start, 0, msg)
284     # . . push args
285     68/push  "F - test-next-token-from-slice-Eof"/imm32
286     68/push  0/imm32
287     # . . push out->start - in->start
288     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
289     2b/subtract                     0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # subtract *edi from ecx
290     51/push-ecx
291     # . . call
292     e8/call  check-ints-equal/disp32
293     # . . discard args
294     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
295     # . epilogue
296     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
297     5d/pop-to-ebp
298     c3/return
300 test-next-token-from-slice-nothing:
301     # . prologue
302     55/push-ebp
303     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
304     # (eax..ecx) = "    "
305     b8/copy-to-eax  "    "/imm32
306     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
307     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
308     05/add-to-eax  4/imm32
309     # var out/edi: slice
310     68/push  0/imm32/end
311     68/push  0/imm32/start
312     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
313     # next-token-from-slice(in, 0x20/space, out)
314     # . . push args
315     57/push-edi
316     68/push  0x20/imm32
317     51/push-ecx
318     50/push-eax
319     # . . call
320     e8/call  next-token-from-slice/disp32
321     # . . discard args
322     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
323     # out should be empty
324     # . check-ints-equal(out->end - out->start, 0, msg)
325     # . . push args
326     68/push  "F - test-next-token-from-slice-Eof"/imm32
327     68/push  0/imm32
328     # . . push out->start - in->start
329     8b/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(edi+4) to ecx
330     2b/subtract                     0/mod/indirect  7/rm32/edi    .           .             .           1/r32/ecx   .               .                 # subtract *edi from ecx
331     51/push-ecx
332     # . . call
333     e8/call  check-ints-equal/disp32
334     # . . discard args
335     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
336     # . epilogue
337     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
338     5d/pop-to-ebp
339     c3/return
341 skip-chars-matching:  # in: (addr stream byte), delimiter: byte
342     # . prologue
343     55/push-ebp
344     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
345     # . save registers
346     50/push-eax
347     51/push-ecx
348     52/push-edx
349     53/push-ebx
350     56/push-esi
351     # esi = in
352     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
353     # ecx = in->read
354     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
355     # ebx = in->write
356     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy *esi to ebx
357     # edx = delimiter
358     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
359 $skip-chars-matching:loop:
360     # if (in->read >= in->write) break
361     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
362     7d/jump-if->=  $skip-chars-matching:end/disp8
363     # eax = in->data[in->read]
364     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
365     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
366     # if (eax != delimiter) break
367     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax and edx
368     75/jump-if-!=  $skip-chars-matching:end/disp8
369     # ++in->read
370     41/increment-ecx
371     eb/jump  $skip-chars-matching:loop/disp8
372 $skip-chars-matching:end:
373     # persist in->read
374     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
375     # . restore registers
376     5e/pop-to-esi
377     5b/pop-to-ebx
378     5a/pop-to-edx
379     59/pop-to-ecx
380     58/pop-to-eax
381     # . epilogue
382     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
383     5d/pop-to-ebp
384     c3/return
386 test-skip-chars-matching:
387     # setup
388     # . clear-stream(_test-stream)
389     # . . push args
390     68/push  _test-stream/imm32
391     # . . call
392     e8/call  clear-stream/disp32
393     # . . discard args
394     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
395     # write(_test-stream, "  ab")
396     # . . push args
397     68/push  "  ab"/imm32
398     68/push  _test-stream/imm32
399     # . . call
400     e8/call  write/disp32
401     # . . discard args
402     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
403     # skip-chars-matching(_test-stream, 0x20/space)
404     # . . push args
405     68/push  0x20/imm32
406     68/push  _test-stream/imm32
407     # . . call
408     e8/call  skip-chars-matching/disp32
409     # . . discard args
410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
411     # check-ints-equal(_test-stream->read, 2, msg)
412     # . . push args
413     68/push  "F - test-skip-chars-matching"/imm32
414     68/push  2/imm32
415     # . . push *_test-stream->read
416     b8/copy-to-eax  _test-stream/imm32
417     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
418     # . . call
419     e8/call  check-ints-equal/disp32
420     # . . discard args
421     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
422     # end
423     c3/return
425 test-skip-chars-matching-none:
426     # setup
427     # . clear-stream(_test-stream)
428     # . . push args
429     68/push  _test-stream/imm32
430     # . . call
431     e8/call  clear-stream/disp32
432     # . . discard args
433     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
434     # write(_test-stream, "ab")
435     # . . push args
436     68/push  "ab"/imm32
437     68/push  _test-stream/imm32
438     # . . call
439     e8/call  write/disp32
440     # . . discard args
441     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
442     # skip-chars-matching(_test-stream, 0x20/space)
443     # . . push args
444     68/push  0x20/imm32
445     68/push  _test-stream/imm32
446     # . . call
447     e8/call  skip-chars-matching/disp32
448     # . . discard args
449     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
450     # check-ints-equal(_test-stream->read, 0, msg)
451     # . . push args
452     68/push  "F - test-skip-chars-matching-none"/imm32
453     68/push  0/imm32
454     # . . push *_test-stream->read
455     b8/copy-to-eax  _test-stream/imm32
456     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
457     # . . call
458     e8/call  check-ints-equal/disp32
459     # . . discard args
460     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
461     # end
462     c3/return
464 skip-chars-matching-whitespace:  # in: (addr stream byte)
465     # . prologue
466     55/push-ebp
467     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
468     # . save registers
469     50/push-eax
470     51/push-ecx
471     53/push-ebx
472     56/push-esi
473     # esi = in
474     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
475     # ecx = in->read
476     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
477     # ebx = in->write
478     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy *esi to ebx
479 $skip-chars-matching-whitespace:loop:
480     # if (in->read >= in->write) break
481     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
482     7d/jump-if->=  $skip-chars-matching-whitespace:end/disp8
483     # eax = in->data[in->read]
484     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
485     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
486     # if (eax == ' ') goto body
487     3d/compare-eax-and  0x20/imm32/space
488     74/jump-if-=  $skip-chars-matching-whitespace:body/disp8
489     # if (eax == '\n') goto body
490     3d/compare-eax-and  0x0a/imm32/newline
491     74/jump-if-=  $skip-chars-matching-whitespace:body/disp8
492     # if (eax == '\t') goto body
493     3d/compare-eax-and  0x09/imm32/tab
494     74/jump-if-=  $skip-chars-matching-whitespace:body/disp8
495     # if (eax != '\r') break
496     3d/compare-eax-and  0x0d/imm32/cr
497     75/jump-if-!=  $skip-chars-matching-whitespace:end/disp8
498 $skip-chars-matching-whitespace:body:
499     # ++in->read
500     41/increment-ecx
501     eb/jump  $skip-chars-matching-whitespace:loop/disp8
502 $skip-chars-matching-whitespace:end:
503     # persist in->read
504     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
505     # . restore registers
506     5e/pop-to-esi
507     5b/pop-to-ebx
508     59/pop-to-ecx
509     58/pop-to-eax
510     # . epilogue
511     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
512     5d/pop-to-ebp
513     c3/return
515 test-skip-chars-matching-whitespace:
516     # setup
517     # . clear-stream(_test-stream)
518     # . . push args
519     68/push  _test-stream/imm32
520     # . . call
521     e8/call  clear-stream/disp32
522     # . . discard args
523     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
524     # write(_test-stream, " \nab")
525     # . . push args
526     68/push  " \nab"/imm32
527     68/push  _test-stream/imm32
528     # . . call
529     e8/call  write/disp32
530     # . . discard args
531     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
532     # skip-chars-matching-whitespace(_test-stream)
533     # . . push args
534     68/push  _test-stream/imm32
535     # . . call
536     e8/call  skip-chars-matching-whitespace/disp32
537     # . . discard args
538     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
539     # check-ints-equal(_test-stream->read, 2, msg)
540     # . . push args
541     68/push  "F - test-skip-chars-matching-whitespace"/imm32
542     68/push  2/imm32
543     # . . push *_test-stream->read
544     b8/copy-to-eax  _test-stream/imm32
545     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
546     # . . call
547     e8/call  check-ints-equal/disp32
548     # . . discard args
549     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
550     # end
551     c3/return
553 # minor fork of 'skip-chars-matching'
554 skip-chars-not-matching:  # in: (addr stream byte), delimiter: byte
555     # . prologue
556     55/push-ebp
557     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
558     # . save registers
559     50/push-eax
560     51/push-ecx
561     52/push-edx
562     53/push-ebx
563     56/push-esi
564     # esi = in
565     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
566     # ecx = in->read
567     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
568     # ebx = in->write
569     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy *esi to ebx
570     # edx = delimiter
571     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
572 $skip-chars-not-matching:loop:
573     # if (in->read >= in->write) break
574     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
575     7d/jump-if->=  $skip-chars-not-matching:end/disp8
576     # eax = in->data[in->read]
577     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
578     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
579     # if (eax == delimiter) break
580     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           2/r32/edx   .               .                 # compare eax and edx
581     74/jump-if-=  $skip-chars-not-matching:end/disp8
582     # ++in->read
583     41/increment-ecx
584     eb/jump  $skip-chars-not-matching:loop/disp8
585 $skip-chars-not-matching:end:
586     # persist in->read
587     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
588     # . restore registers
589     5e/pop-to-esi
590     5b/pop-to-ebx
591     5a/pop-to-edx
592     59/pop-to-ecx
593     58/pop-to-eax
594     # . epilogue
595     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
596     5d/pop-to-ebp
597     c3/return
599 test-skip-chars-not-matching:
600     # setup
601     # . clear-stream(_test-stream)
602     # . . push args
603     68/push  _test-stream/imm32
604     # . . call
605     e8/call  clear-stream/disp32
606     # . . discard args
607     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
608     # write(_test-stream, "ab ")
609     # . . push args
610     68/push  "ab "/imm32
611     68/push  _test-stream/imm32
612     # . . call
613     e8/call  write/disp32
614     # . . discard args
615     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
616     # skip-chars-not-matching(_test-stream, 0x20/space)
617     # . . push args
618     68/push  0x20/imm32
619     68/push  _test-stream/imm32
620     # . . call
621     e8/call  skip-chars-not-matching/disp32
622     # . . discard args
623     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
624     # check-ints-equal(_test-stream->read, 2, msg)
625     # . . push args
626     68/push  "F - test-skip-chars-not-matching"/imm32
627     68/push  2/imm32
628     # . . push *_test-stream->read
629     b8/copy-to-eax  _test-stream/imm32
630     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
631     # . . call
632     e8/call  check-ints-equal/disp32
633     # . . discard args
634     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
635     # end
636     c3/return
638 test-skip-chars-not-matching-none:
639     # setup
640     # . clear-stream(_test-stream)
641     # . . push args
642     68/push  _test-stream/imm32
643     # . . call
644     e8/call  clear-stream/disp32
645     # . . discard args
646     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
647     # write(_test-stream, " ab")
648     # . . push args
649     68/push  " ab"/imm32
650     68/push  _test-stream/imm32
651     # . . call
652     e8/call  write/disp32
653     # . . discard args
654     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
655     # skip-chars-not-matching(_test-stream, 0x20/space)
656     # . . push args
657     68/push  0x20/imm32
658     68/push  _test-stream/imm32
659     # . . call
660     e8/call  skip-chars-not-matching/disp32
661     # . . discard args
662     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
663     # check-ints-equal(_test-stream->read, 0, msg)
664     # . . push args
665     68/push  "F - test-skip-chars-not-matching-none"/imm32
666     68/push  0/imm32
667     # . . push *_test-stream->read
668     b8/copy-to-eax  _test-stream/imm32
669     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
670     # . . call
671     e8/call  check-ints-equal/disp32
672     # . . discard args
673     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
674     # end
675     c3/return
677 test-skip-chars-not-matching-all:
678     # setup
679     # . clear-stream(_test-stream)
680     # . . push args
681     68/push  _test-stream/imm32
682     # . . call
683     e8/call  clear-stream/disp32
684     # . . discard args
685     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
686     # write(_test-stream, "ab")
687     # . . push args
688     68/push  "ab"/imm32
689     68/push  _test-stream/imm32
690     # . . call
691     e8/call  write/disp32
692     # . . discard args
693     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
694     # skip-chars-not-matching(_test-stream, 0x20/space)
695     # . . push args
696     68/push  0x20/imm32
697     68/push  _test-stream/imm32
698     # . . call
699     e8/call  skip-chars-not-matching/disp32
700     # . . discard args
701     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
702     # check-ints-equal(_test-stream->read, 2, msg)
703     # . . push args
704     68/push  "F - test-skip-chars-not-matching-all"/imm32
705     68/push  2/imm32
706     # . . push *_test-stream->read
707     b8/copy-to-eax  _test-stream/imm32
708     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
709     # . . call
710     e8/call  check-ints-equal/disp32
711     # . . discard args
712     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
713     # end
714     c3/return
716 skip-chars-not-matching-whitespace:  # in: (addr stream byte)
717     # . prologue
718     55/push-ebp
719     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
720     # . save registers
721     50/push-eax
722     51/push-ecx
723     53/push-ebx
724     56/push-esi
725     # esi = in
726     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
727     # ecx = in->read
728     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
729     # ebx = in->write
730     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           3/r32/ebx   .               .                 # copy *esi to ebx
731 $skip-chars-not-matching-whitespace:loop:
732     # if (in->read >= in->write) break
733     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           3/r32/ebx   .               .                 # compare ecx with ebx
734     7d/jump-if->=  $skip-chars-not-matching-whitespace:end/disp8
735     # eax = in->data[in->read]
736     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
737     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
738     # if (eax == ' ') break
739     3d/compare-eax-and  0x20/imm32/space
740     74/jump-if-=  $skip-chars-not-matching-whitespace:end/disp8
741     # if (eax == '\n') break
742     3d/compare-eax-and  0x0a/imm32/newline
743     74/jump-if-=  $skip-chars-not-matching-whitespace:end/disp8
744     # if (eax == '\t') break
745     3d/compare-eax-and  0x09/imm32/tab
746     74/jump-if-=  $skip-chars-not-matching-whitespace:end/disp8
747     # if (eax == '\r') break
748     3d/compare-eax-and  0x0d/imm32/cr
749     74/jump-if-=  $skip-chars-not-matching-whitespace:end/disp8
750     # ++in->read
751     41/increment-ecx
752     eb/jump  $skip-chars-not-matching-whitespace:loop/disp8
753 $skip-chars-not-matching-whitespace:end:
754     # persist in->read
755     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy ecx to *(esi+4)
756     # . restore registers
757     5e/pop-to-esi
758     5b/pop-to-ebx
759     59/pop-to-ecx
760     58/pop-to-eax
761     # . epilogue
762     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
763     5d/pop-to-ebp
764     c3/return
766 test-skip-chars-not-matching-whitespace:
767     # setup
768     # . clear-stream(_test-stream)
769     # . . push args
770     68/push  _test-stream/imm32
771     # . . call
772     e8/call  clear-stream/disp32
773     # . . discard args
774     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
775     # write(_test-stream, "ab\n")
776     # . . push args
777     68/push  "ab\n"/imm32
778     68/push  _test-stream/imm32
779     # . . call
780     e8/call  write/disp32
781     # . . discard args
782     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
783     # skip-chars-not-matching-whitespace(_test-stream)
784     # . . push args
785     68/push  _test-stream/imm32
786     # . . call
787     e8/call  skip-chars-not-matching-whitespace/disp32
788     # . . discard args
789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
790     # check-ints-equal(_test-stream->read, 2, msg)
791     # . . push args
792     68/push  "F - test-skip-chars-not-matching-whitespace"/imm32
793     68/push  2/imm32
794     # . . push *_test-stream->read
795     b8/copy-to-eax  _test-stream/imm32
796     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
797     # . . call
798     e8/call  check-ints-equal/disp32
799     # . . discard args
800     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
801     # end
802     c3/return
804 skip-chars-matching-in-slice:  # curr: (addr byte), end: (addr byte), delimiter: byte -> curr/eax: (addr byte)
805     # . prologue
806     55/push-ebp
807     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
808     # . save registers
809     51/push-ecx
810     52/push-edx
811     53/push-ebx
812     # eax = curr
813     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
814     # ecx = end
815     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
816     # edx = delimiter
817     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8       .                 # copy *(ebp+16) to edx
818     # var c/ebx: byte = 0
819     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
820 $skip-chars-matching-in-slice:loop:
821     # if (curr >= end) break
822     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
823     73/jump-if-addr>=  $skip-chars-matching-in-slice:end/disp8
824     # c = *curr
825     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
826     # if (c != delimiter) break
827     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx and edx
828     75/jump-if-!=  $skip-chars-matching-in-slice:end/disp8
829     # ++curr
830     40/increment-eax
831     eb/jump  $skip-chars-matching-in-slice:loop/disp8
832 $skip-chars-matching-in-slice:end:
833     # . restore registers
834     5b/pop-to-ebx
835     5a/pop-to-edx
836     59/pop-to-ecx
837     # . epilogue
838     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
839     5d/pop-to-ebp
840     c3/return
842 test-skip-chars-matching-in-slice:
843     # (eax..ecx) = "  ab"
844     b8/copy-to-eax  "  ab"/imm32
845     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
846     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
847     05/add-to-eax  4/imm32
848     # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space)
849     # . . push args
850     68/push  0x20/imm32/space
851     51/push-ecx
852     50/push-eax
853     # . . call
854     e8/call  skip-chars-matching-in-slice/disp32
855     # . . discard args
856     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
857     # check-ints-equal(ecx-eax, 2, msg)
858     # . . push args
859     68/push  "F - test-skip-chars-matching-in-slice"/imm32
860     68/push  2/imm32
861     # . . push ecx-eax
862     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
863     51/push-ecx
864     # . . call
865     e8/call  check-ints-equal/disp32
866     # . . discard args
867     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
868     # end
869     c3/return
871 test-skip-chars-matching-in-slice-none:
872     # (eax..ecx) = "ab"
873     b8/copy-to-eax  "ab"/imm32
874     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
875     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
876     05/add-to-eax  4/imm32
877     # eax = skip-chars-matching-in-slice(eax, ecx, 0x20/space)
878     # . . push args
879     68/push  0x20/imm32/space
880     51/push-ecx
881     50/push-eax
882     # . . call
883     e8/call  skip-chars-matching-in-slice/disp32
884     # . . discard args
885     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
886     # check-ints-equal(ecx-eax, 2, msg)
887     # . . push args
888     68/push  "F - test-skip-chars-matching-in-slice-none"/imm32
889     68/push  2/imm32
890     # . . push ecx-eax
891     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
892     51/push-ecx
893     # . . call
894     e8/call  check-ints-equal/disp32
895     # . . discard args
896     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
897     # end
898     c3/return
900 skip-chars-matching-whitespace-in-slice:  # curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte)
901     # . prologue
902     55/push-ebp
903     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
904     # . save registers
905     51/push-ecx
906     53/push-ebx
907     # eax = curr
908     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
909     # ecx = end
910     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
911     # var c/ebx: byte = 0
912     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
913 $skip-chars-matching-whitespace-in-slice:loop:
914     # if (curr >= end) break
915     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
916     0f 83/jump-if-addr>=  $skip-chars-matching-in-slice:end/disp32
917     # c = *curr
918     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
919     # if (c == ' ') goto body
920     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x20/imm32/space  # compare ebx
921     74/jump-if-=  $skip-chars-matching-whitespace-in-slice:body/disp8
922     # if (c == '\n') goto body
923     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0a/imm32/newline  # compare ebx
924     74/jump-if-=  $skip-chars-matching-whitespace-in-slice:body/disp8
925     # if (c == '\t') goto body
926     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x09/imm32/tab    # compare ebx
927     74/jump-if-=  $skip-chars-matching-whitespace-in-slice:body/disp8
928     # if (c != '\r') break
929     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0d/imm32/cr     # compare ebx
930     75/jump-if-!=  $skip-chars-matching-whitespace-in-slice:end/disp8
931 $skip-chars-matching-whitespace-in-slice:body:
932     # ++curr
933     40/increment-eax
934     eb/jump  $skip-chars-matching-whitespace-in-slice:loop/disp8
935 $skip-chars-matching-whitespace-in-slice:end:
936     # . restore registers
937     5b/pop-to-ebx
938     59/pop-to-ecx
939     # . epilogue
940     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
941     5d/pop-to-ebp
942     c3/return
944 test-skip-chars-matching-whitespace-in-slice:
945     # (eax..ecx) = " \nab"
946     b8/copy-to-eax  " \nab"/imm32
947     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
948     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
949     05/add-to-eax  4/imm32
950     # eax = skip-chars-matching-whitespace-in-slice(eax, ecx)
951     # . . push args
952     51/push-ecx
953     50/push-eax
954     # . . call
955     e8/call  skip-chars-matching-whitespace-in-slice/disp32
956     # . . discard args
957     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
958     # check-ints-equal(ecx-eax, 2, msg)
959     # . . push args
960     68/push  "F - test-skip-chars-matching-whitespace-in-slice"/imm32
961     68/push  2/imm32
962     # . . push ecx-eax
963     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
964     51/push-ecx
965     # . . call
966     e8/call  check-ints-equal/disp32
967     # . . discard args
968     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
969     # end
970     c3/return
972 # minor fork of 'skip-chars-matching-in-slice'
973 skip-chars-not-matching-in-slice:  # curr: (addr byte), end: (addr byte), delimiter: byte -> curr/eax: (addr byte)
974     # . prologue
975     55/push-ebp
976     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
977     # . save registers
978     51/push-ecx
979     52/push-edx
980     53/push-ebx
981     # eax = curr
982     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
983     # ecx = end
984     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
985     # edx = delimiter
986     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8       .                 # copy *(ebp+16) to edx
987     # var c/ebx: byte = 0
988     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
989 $skip-chars-not-matching-in-slice:loop:
990     # if (curr >= end) break
991     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
992     73/jump-if-addr>=  $skip-chars-not-matching-in-slice:end/disp8
993     # c = *curr
994     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
995     # if (c == delimiter) break
996     39/compare                      3/mod/direct    3/rm32/ebx    .           .             .           2/r32/edx   .               .                 # compare ebx and edx
997     74/jump-if-=  $skip-chars-not-matching-in-slice:end/disp8
998     # ++curr
999     40/increment-eax
1000     eb/jump  $skip-chars-not-matching-in-slice:loop/disp8
1001 $skip-chars-not-matching-in-slice:end:
1002     # . restore registers
1003     5b/pop-to-ebx
1004     5a/pop-to-edx
1005     59/pop-to-ecx
1006     # . epilogue
1007     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1008     5d/pop-to-ebp
1009     c3/return
1011 test-skip-chars-not-matching-in-slice:
1012     # (eax..ecx) = "ab "
1013     b8/copy-to-eax  "ab "/imm32
1014     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1015     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1016     05/add-to-eax  4/imm32
1017     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
1018     # . . push args
1019     68/push  0x20/imm32/space
1020     51/push-ecx
1021     50/push-eax
1022     # . . call
1023     e8/call  skip-chars-not-matching-in-slice/disp32
1024     # . . discard args
1025     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1026     # check-ints-equal(ecx-eax, 1, msg)
1027     # . . push args
1028     68/push  "F - test-skip-chars-not-matching-in-slice"/imm32
1029     68/push  1/imm32
1030     # . . push ecx-eax
1031     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1032     51/push-ecx
1033     # . . call
1034     e8/call  check-ints-equal/disp32
1035     # . . discard args
1036     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1037     # end
1038     c3/return
1040 test-skip-chars-not-matching-in-slice-none:
1041     # (eax..ecx) = " ab"
1042     b8/copy-to-eax  " ab"/imm32
1043     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1044     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1045     05/add-to-eax  4/imm32
1046     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
1047     # . . push args
1048     68/push  0x20/imm32/space
1049     51/push-ecx
1050     50/push-eax
1051     # . . call
1052     e8/call  skip-chars-not-matching-in-slice/disp32
1053     # . . discard args
1054     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1055     # check-ints-equal(ecx-eax, 3, msg)
1056     # . . push args
1057     68/push  "F - test-skip-chars-not-matching-in-slice-none"/imm32
1058     68/push  3/imm32
1059     # . . push ecx-eax
1060     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1061     51/push-ecx
1062     # . . call
1063     e8/call  check-ints-equal/disp32
1064     # . . discard args
1065     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1066     # end
1067     c3/return
1069 test-skip-chars-not-matching-in-slice-all:
1070     # (eax..ecx) = "ab"
1071     b8/copy-to-eax  "ab"/imm32
1072     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1073     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1074     05/add-to-eax  4/imm32
1075     # eax = skip-chars-not-matching-in-slice(eax, ecx, 0x20/space)
1076     # . . push args
1077     68/push  0x20/imm32/space
1078     51/push-ecx
1079     50/push-eax
1080     # . . call
1081     e8/call  skip-chars-not-matching-in-slice/disp32
1082     # . . discard args
1083     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1084     # check-ints-equal(ecx-eax, 0, msg)
1085     # . . push args
1086     68/push  "F - test-skip-chars-not-matching-in-slice-all"/imm32
1087     68/push  0/imm32
1088     # . . push ecx-eax
1089     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1090     51/push-ecx
1091     # . . call
1092     e8/call  check-ints-equal/disp32
1093     # . . discard args
1094     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1095     # end
1096     c3/return
1098 skip-chars-not-matching-whitespace-in-slice:  # curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte)
1099     # . prologue
1100     55/push-ebp
1101     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1102     # . save registers
1103     51/push-ecx
1104     53/push-ebx
1105     # eax = curr
1106     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
1107     # ecx = end
1108     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   0xc/disp8       .                 # copy *(ebp+12) to ecx
1109     # var c/ebx: byte = 0
1110     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
1111 $skip-chars-not-matching-whitespace-in-slice:loop:
1112     # if (curr >= end) break
1113     39/compare                      3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # compare eax with ecx
1114     0f 83/jump-if-addr>=  $skip-chars-not-matching-in-slice:end/disp32
1115     # c = *curr
1116     8a/copy-byte                    0/mod/indirect  0/rm32/eax    .           .             .           3/r32/BL    .               .                 # copy byte at *eax to BL
1117     # if (c == ' ') break
1118     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x20/imm32/space  # compare ebx
1119     74/jump-if-=  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1120     # if (c == '\n') break
1121     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0a/imm32/newline  # compare ebx
1122     74/jump-if-=  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1123     # if (c == '\t') break
1124     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x09/imm32/tab    # compare ebx
1125     74/jump-if-=  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1126     # if (c == '\r') break
1127     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x0d/imm32/cr     # compare ebx
1128     74/jump-if-=  $skip-chars-not-matching-whitespace-in-slice:end/disp8
1129     # ++curr
1130     40/increment-eax
1131     eb/jump  $skip-chars-not-matching-whitespace-in-slice:loop/disp8
1132 $skip-chars-not-matching-whitespace-in-slice:end:
1133     # . restore registers
1134     5b/pop-to-ebx
1135     59/pop-to-ecx
1136     # . epilogue
1137     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1138     5d/pop-to-ebp
1139     c3/return
1141 test-skip-chars-not-matching-whitespace-in-slice:
1142     # (eax..ecx) = "ab\n"
1143     b8/copy-to-eax  "ab\n"/imm32
1144     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1145     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1146     05/add-to-eax  4/imm32
1147     # eax = skip-chars-not-matching-whitespace-in-slice(eax, ecx)
1148     # . . push args
1149     51/push-ecx
1150     50/push-eax
1151     # . . call
1152     e8/call  skip-chars-not-matching-whitespace-in-slice/disp32
1153     # . . discard args
1154     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1155     # check-ints-equal(ecx-eax, 1, msg)
1156     # . . push args
1157     68/push  "F - test-skip-chars-not-matching-whitespace-in-slice"/imm32
1158     68/push  1/imm32
1159     # . . push ecx-eax
1160     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1161     51/push-ecx
1162     # . . call
1163     e8/call  check-ints-equal/disp32
1164     # . . discard args
1165     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1166     # end
1167     c3/return
1169 # update line->read to end of string literal surrounded by double quotes
1170 # line->read must start out at a double-quote
1171 skip-string:  # line: (addr stream byte)
1172     # . prologue
1173     55/push-ebp
1174     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1175     # . save registers
1176     50/push-eax
1177     51/push-ecx
1178     52/push-edx
1179     # ecx = line
1180     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1181     # eax = skip-string-in-slice(&line->data[line->read], &line->data[line->write])
1182     # . . push &line->data[line->write]
1183     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   8/disp8         .                 # copy *(ecx+8) to edx
1184     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
1185     52/push-edx
1186     # . . push &line->data[line->read]
1187     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1188     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
1189     52/push-edx
1190     # . . call
1191     e8/call  skip-string-in-slice/disp32
1192     # . . discard args
1193     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1194     # line->read = eax - line->data
1195     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
1196     2d/subtract-from-eax  0xc/imm32
1197     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         0/r32/eax   4/disp8         .                 # copy eax to *(ecx+4)
1198 $skip-string:end:
1199     # . restore registers
1200     5a/pop-to-edx
1201     59/pop-to-ecx
1202     58/pop-to-eax
1203     # . epilogue
1204     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1205     5d/pop-to-ebp
1206     c3/return
1208 test-skip-string:
1209     # . prologue
1210     55/push-ebp
1211     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1212     # setup
1213     # . clear-stream(_test-input-stream)
1214     # . . push args
1215     68/push  _test-input-stream/imm32
1216     # . . call
1217     e8/call  clear-stream/disp32
1218     # . . discard args
1219     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1220     # . write(_test-input-stream, "\"abc\" def")
1221     # .                   indices:  0123 45
1222     # . . push args
1223     68/push  "\"abc\" def"/imm32
1224     68/push  _test-input-stream/imm32
1225     # . . call
1226     e8/call  write/disp32
1227     # . . discard args
1228     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1229     # precondition: line->read == 0
1230     # . . push args
1231     68/push  "F - test-skip-string/precondition"/imm32
1232     68/push  0/imm32
1233     b8/copy-to-eax  _test-input-stream/imm32
1234     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1235     # . . call
1236     e8/call  check-ints-equal/disp32
1237     # . . discard args
1238     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1239     # skip-string(_test-input-stream)
1240     # . . push args
1241     68/push  _test-input-stream/imm32
1242     # . . call
1243     e8/call  skip-string/disp32
1244     # . . discard args
1245     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1246     # check-ints-equal(line->read, 5, msg)
1247     # . . push args
1248     68/push  "F - test-skip-string"/imm32
1249     68/push  5/imm32
1250     b8/copy-to-eax  _test-input-stream/imm32
1251     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1252     # . . call
1253     e8/call  check-ints-equal/disp32
1254     # . . discard args
1255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1256     # . epilogue
1257     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1258     5d/pop-to-ebp
1259     c3/return
1261 test-skip-string-ignores-spaces:
1262     # . prologue
1263     55/push-ebp
1264     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1265     # setup
1266     # . clear-stream(_test-input-stream)
1267     # . . push args
1268     68/push  _test-input-stream/imm32
1269     # . . call
1270     e8/call  clear-stream/disp32
1271     # . . discard args
1272     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1273     # . write(_test-input-stream, "\"a b\"/yz")
1274     # .                   indices:  0123 45
1275     # . . push args
1276     68/push  "\"a b\"/yz"/imm32
1277     68/push  _test-input-stream/imm32
1278     # . . call
1279     e8/call  write/disp32
1280     # . . discard args
1281     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1282     # precondition: line->read == 0
1283     # . . push args
1284     68/push  "F - test-skip-string-ignores-spaces/precondition"/imm32
1285     68/push  0/imm32
1286     b8/copy-to-eax  _test-input-stream/imm32
1287     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1288     # . . call
1289     e8/call  check-ints-equal/disp32
1290     # . . discard args
1291     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1292     # skip-string(_test-input-stream)
1293     # . . push args
1294     68/push  _test-input-stream/imm32
1295     # . . call
1296     e8/call  skip-string/disp32
1297     # . . discard args
1298     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1299     # check-ints-equal(line->read, 5, msg)
1300     # . . push args
1301     68/push  "F - test-skip-string-ignores-spaces"/imm32
1302     68/push  5/imm32
1303     b8/copy-to-eax  _test-input-stream/imm32
1304     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1305     # . . call
1306     e8/call  check-ints-equal/disp32
1307     # . . discard args
1308     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1309     # . epilogue
1310     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1311     5d/pop-to-ebp
1312     c3/return
1314 test-skip-string-ignores-escapes:
1315     # . prologue
1316     55/push-ebp
1317     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1318     # setup
1319     # . clear-stream(_test-input-stream)
1320     # . . push args
1321     68/push  _test-input-stream/imm32
1322     # . . call
1323     e8/call  clear-stream/disp32
1324     # . . discard args
1325     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1326     # . write(_test-input-stream, "\"a\\\"b\"/yz")
1327     # .                   indices:  01 2 34 56
1328     # . . push args
1329     68/push  "\"a\\\"b\"/yz"/imm32
1330     68/push  _test-input-stream/imm32
1331     # . . call
1332     e8/call  write/disp32
1333     # . . discard args
1334     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1335     # precondition: line->read == 0
1336     # . . push args
1337     68/push  "F - test-skip-string-ignores-escapes/precondition"/imm32
1338     68/push  0/imm32
1339     b8/copy-to-eax  _test-input-stream/imm32
1340     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1341     # . . call
1342     e8/call  check-ints-equal/disp32
1343     # . . discard args
1344     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1345     # skip-string(_test-input-stream)
1346     # . . push args
1347     68/push  _test-input-stream/imm32
1348     # . . call
1349     e8/call  skip-string/disp32
1350     # . . discard args
1351     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1352     # check-ints-equal(line->read, 6, msg)
1353     # . . push args
1354     68/push  "F - test-skip-string-ignores-escapes"/imm32
1355     68/push  6/imm32
1356     b8/copy-to-eax  _test-input-stream/imm32
1357     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1358     # . . call
1359     e8/call  check-ints-equal/disp32
1360     # . . discard args
1361     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1362     # . epilogue
1363     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1364     5d/pop-to-ebp
1365     c3/return
1367 test-skip-string-works-from-mid-stream:
1368     # . prologue
1369     55/push-ebp
1370     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1371     # setup
1372     # . clear-stream(_test-input-stream)
1373     # . . push args
1374     68/push  _test-input-stream/imm32
1375     # . . call
1376     e8/call  clear-stream/disp32
1377     # . . discard args
1378     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1379     # . write(_test-input-stream, "0 \"a\\\"b\"/yz")
1380     # .                   indices:  01 2 34 56
1381     # . . push args
1382     68/push  "0 \"a\\\"b\"/yz"/imm32
1383     68/push  _test-input-stream/imm32
1384     # . . call
1385     e8/call  write/disp32
1386     # . . discard args
1387     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1388     # precondition: line->read == 2
1389     b8/copy-to-eax  _test-input-stream/imm32
1390     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         2/imm32           # copy to *(eax+4)
1391     # skip-string(_test-input-stream)
1392     # . . push args
1393     68/push  _test-input-stream/imm32
1394     # . . call
1395     e8/call  skip-string/disp32
1396     # . . discard args
1397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1398     # check-ints-equal(line->read, 8, msg)
1399     # . . push args
1400     68/push  "F - test-skip-string-works-from-mid-stream"/imm32
1401     68/push  8/imm32
1402     b8/copy-to-eax  _test-input-stream/imm32
1403     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1404     # . . call
1405     e8/call  check-ints-equal/disp32
1406     # . . discard args
1407     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1408     # . epilogue
1409     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1410     5d/pop-to-ebp
1411     c3/return
1413 skip-string-in-slice:  # curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte)
1414     # . prologue
1415     55/push-ebp
1416     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1417     # . save registers
1418     51/push-ecx
1419     52/push-edx
1420     53/push-ebx
1421     # ecx = curr
1422     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1423     # edx = end
1424     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
1425     # var c/eax: byte = 0
1426     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1427     # skip initial dquote
1428     41/increment-ecx
1429 $skip-string-in-slice:loop:
1430     # if (curr >= end) return curr
1431     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1432     73/jump-if-addr>=  $skip-string-in-slice:return-curr/disp8
1433     # c = *curr
1434     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1435 $skip-string-in-slice:dquote:
1436     # if (c == '"') break
1437     3d/compare-eax-and  0x22/imm32/double-quote
1438     74/jump-if-=  $skip-string-in-slice:break/disp8
1439 $skip-string-in-slice:check-for-escape:
1440     # if (c == '\') escape next char
1441     3d/compare-eax-and  0x5c/imm32/backslash
1442     75/jump-if-!=  $skip-string-in-slice:continue/disp8
1443 $skip-string-in-slice:escape:
1444     41/increment-ecx
1445 $skip-string-in-slice:continue:
1446     # ++curr
1447     41/increment-ecx
1448     eb/jump  $skip-string-in-slice:loop/disp8
1449 $skip-string-in-slice:break:
1450     # skip final dquote
1451     41/increment-ecx
1452 $skip-string-in-slice:return-curr:
1453     # return curr
1454     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to eax
1455 $skip-string-in-slice:end:
1456     # . restore registers
1457     5b/pop-to-ebx
1458     5a/pop-to-edx
1459     59/pop-to-ecx
1460     # . epilogue
1461     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1462     5d/pop-to-ebp
1463     c3/return
1465 test-skip-string-in-slice:
1466     # . prologue
1467     55/push-ebp
1468     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1469     # setup: (eax..ecx) = "\"abc\" def"
1470     b8/copy-to-eax  "\"abc\" def"/imm32
1471     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1472     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1473     05/add-to-eax  4/imm32
1474     # eax = skip-string-in-slice(eax, ecx)
1475     # . . push args
1476     51/push-ecx
1477     50/push-eax
1478     # . . call
1479     e8/call  skip-string-in-slice/disp32
1480     # . . discard args
1481     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1482     # check-ints-equal(ecx-eax, 4, msg)  # number of chars remaining after the string literal
1483     # . . push args
1484     68/push  "F - test-skip-string-in-slice"/imm32
1485     68/push  4/imm32
1486     # . . push ecx-eax
1487     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1488     51/push-ecx
1489     # . . call
1490     e8/call  check-ints-equal/disp32
1491     # . . discard args
1492     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1493     # . epilogue
1494     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1495     5d/pop-to-ebp
1496     c3/return
1498 test-skip-string-in-slice-ignores-spaces:
1499     # . prologue
1500     55/push-ebp
1501     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1502     # setup: (eax..ecx) = "\"a b\"/yz"
1503     b8/copy-to-eax  "\"a b\"/yz"/imm32
1504     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1505     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1506     05/add-to-eax  4/imm32
1507     # eax = skip-string-in-slice(eax, ecx)
1508     # . . push args
1509     51/push-ecx
1510     50/push-eax
1511     # . . call
1512     e8/call  skip-string-in-slice/disp32
1513     # . . discard args
1514     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1515     # check-ints-equal(ecx-eax, 3, msg)  # number of chars remaining after the string literal
1516     # . . push args
1517     68/push  "F - test-skip-string-in-slice-ignores-spaces"/imm32
1518     68/push  3/imm32
1519     # . . push ecx-eax
1520     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1521     51/push-ecx
1522     # . . call
1523     e8/call  check-ints-equal/disp32
1524     # . . discard args
1525     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1526     # . epilogue
1527     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1528     5d/pop-to-ebp
1529     c3/return
1531 test-skip-string-in-slice-ignores-escapes:
1532     # . prologue
1533     55/push-ebp
1534     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1535     # setup: (eax..ecx) = "\"a\\\"b\"/yz"
1536     b8/copy-to-eax  "\"a\\\"b\"/yz"/imm32
1537     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1538     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1539     05/add-to-eax  4/imm32
1540     # eax = skip-string-in-slice(eax, ecx)
1541     # . . push args
1542     51/push-ecx
1543     50/push-eax
1544     # . . call
1545     e8/call  skip-string-in-slice/disp32
1546     # . . discard args
1547     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1548     # check-ints-equal(ecx-eax, 3, msg)  # number of chars remaining after the string literal
1549     # . . push args
1550     68/push  "F - test-skip-string-in-slice-ignores-escapes"/imm32
1551     68/push  3/imm32
1552     # . . push ecx-eax
1553     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1554     51/push-ecx
1555     # . . call
1556     e8/call  check-ints-equal/disp32
1557     # . . discard args
1558     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1559     # . epilogue
1560     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1561     5d/pop-to-ebp
1562     c3/return
1564 test-skip-string-in-slice-stops-at-end:
1565     # . prologue
1566     55/push-ebp
1567     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1568     # setup: (eax..ecx) = "\"abc"  # unbalanced dquote
1569     b8/copy-to-eax  "\"abc"/imm32
1570     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1571     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1572     05/add-to-eax  4/imm32
1573     # eax = skip-string-in-slice(eax, ecx)
1574     # . . push args
1575     51/push-ecx
1576     50/push-eax
1577     # . . call
1578     e8/call  skip-string-in-slice/disp32
1579     # . . discard args
1580     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1581     # check-ints-equal(ecx-eax, 0, msg)  # skipped to end of slice
1582     # . . push args
1583     68/push  "F - test-skip-string-in-slice-stops-at-end"/imm32
1584     68/push  0/imm32
1585     # . . push ecx-eax
1586     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1587     51/push-ecx
1588     # . . call
1589     e8/call  check-ints-equal/disp32
1590     # . . discard args
1591     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1592     # . epilogue
1593     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1594     5d/pop-to-ebp
1595     c3/return
1597 # update line->read to ')'
1598 # line->read ends at ')'
1599 skip-until-close-paren:  # line: (addr stream byte)
1600     # . prologue
1601     55/push-ebp
1602     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1603     # . save registers
1604     50/push-eax
1605     51/push-ecx
1606     52/push-edx
1607     # ecx = line
1608     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1609     # eax = skip-until-close-paren-in-slice(&line->data[line->read], &line->data[line->write])
1610     # . . push &line->data[line->write]
1611     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   8/disp8         .                 # copy *(ecx+8) to edx
1612     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
1613     52/push-edx
1614     # . . push &line->data[line->read]
1615     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
1616     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    1/base/ecx  2/index/edx   .           2/r32/edx   0xc/disp8       .                 # copy ecx+edx+12 to edx
1617     52/push-edx
1618     # . . call
1619     e8/call  skip-until-close-paren-in-slice/disp32
1620     # . . discard args
1621     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1622     # line->read = eax - line->data
1623     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
1624     2d/subtract-from-eax  0xc/imm32
1625     89/copy                         1/mod/*+disp8   1/rm32/ecx    .           .                         0/r32/eax   4/disp8         .                 # copy eax to *(ecx+4)
1626 $skip-until-close-paren:end:
1627     # . restore registers
1628     5a/pop-to-edx
1629     59/pop-to-ecx
1630     58/pop-to-eax
1631     # . epilogue
1632     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1633     5d/pop-to-ebp
1634     c3/return
1636 test-skip-until-close-paren:
1637     # . prologue
1638     55/push-ebp
1639     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1640     # setup
1641     # . clear-stream(_test-input-stream)
1642     # . . push args
1643     68/push  _test-input-stream/imm32
1644     # . . call
1645     e8/call  clear-stream/disp32
1646     # . . discard args
1647     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1648     # . write(_test-input-stream, "*(abc) def")
1649     # .                   indices:  0123 45
1650     # . . push args
1651     68/push  "*(abc) def"/imm32
1652     68/push  _test-input-stream/imm32
1653     # . . call
1654     e8/call  write/disp32
1655     # . . discard args
1656     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1657     # precondition: line->read == 0
1658     # . . push args
1659     68/push  "F - test-skip-until-close-paren/precondition"/imm32
1660     68/push  0/imm32
1661     b8/copy-to-eax  _test-input-stream/imm32
1662     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1663     # . . call
1664     e8/call  check-ints-equal/disp32
1665     # . . discard args
1666     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1667     # skip-until-close-paren(_test-input-stream)
1668     # . . push args
1669     68/push  _test-input-stream/imm32
1670     # . . call
1671     e8/call  skip-until-close-paren/disp32
1672     # . . discard args
1673     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1674     # check-ints-equal(line->read, 5, msg)
1675     # . . push args
1676     68/push  "F - test-skip-until-close-paren"/imm32
1677     68/push  5/imm32
1678     b8/copy-to-eax  _test-input-stream/imm32
1679     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1680     # . . call
1681     e8/call  check-ints-equal/disp32
1682     # . . discard args
1683     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1684     # . epilogue
1685     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1686     5d/pop-to-ebp
1687     c3/return
1689 test-skip-until-close-paren-ignores-spaces:
1690     # . prologue
1691     55/push-ebp
1692     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1693     # setup
1694     # . clear-stream(_test-input-stream)
1695     # . . push args
1696     68/push  _test-input-stream/imm32
1697     # . . call
1698     e8/call  clear-stream/disp32
1699     # . . discard args
1700     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1701     # . write(_test-input-stream, "*(a b)/yz")
1702     # . . push args
1703     68/push  "*(a b)/yz"/imm32
1704     68/push  _test-input-stream/imm32
1705     # . . call
1706     e8/call  write/disp32
1707     # . . discard args
1708     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1709     # precondition: line->read == 0
1710     # . . push args
1711     68/push  "F - test-skip-until-close-paren-ignores-spaces/precondition"/imm32
1712     68/push  0/imm32
1713     b8/copy-to-eax  _test-input-stream/imm32
1714     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1715     # . . call
1716     e8/call  check-ints-equal/disp32
1717     # . . discard args
1718     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1719     # skip-until-close-paren(_test-input-stream)
1720     # . . push args
1721     68/push  _test-input-stream/imm32
1722     # . . call
1723     e8/call  skip-until-close-paren/disp32
1724     # . . discard args
1725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1726     # check-ints-equal(line->read, 5, msg)
1727     # . . push args
1728     68/push  "F - test-skip-until-close-paren-ignores-spaces"/imm32
1729     68/push  5/imm32
1730     b8/copy-to-eax  _test-input-stream/imm32
1731     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1732     # . . call
1733     e8/call  check-ints-equal/disp32
1734     # . . discard args
1735     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1736     # . epilogue
1737     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1738     5d/pop-to-ebp
1739     c3/return
1741 test-skip-until-close-paren-works-from-mid-stream:
1742     # . prologue
1743     55/push-ebp
1744     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1745     # setup
1746     # . clear-stream(_test-input-stream)
1747     # . . push args
1748     68/push  _test-input-stream/imm32
1749     # . . call
1750     e8/call  clear-stream/disp32
1751     # . . discard args
1752     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1753     # . write(_test-input-stream, "0 *(a b)/yz")
1754     # . . push args
1755     68/push  "0 *(a b)/yz"/imm32
1756     68/push  _test-input-stream/imm32
1757     # . . call
1758     e8/call  write/disp32
1759     # . . discard args
1760     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1761     # precondition: _test-input-stream->read == 2
1762     b8/copy-to-eax  _test-input-stream/imm32
1763     c7          0/subop/copy        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         2/imm32           # copy to *(eax+4)
1764     # skip-until-close-paren(_test-input-stream)
1765     # . . push args
1766     68/push  _test-input-stream/imm32
1767     # . . call
1768     e8/call  skip-until-close-paren/disp32
1769     # . . discard args
1770     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
1771     # check-ints-equal(_test-input-stream->read, 7, msg)
1772     # . . push args
1773     68/push  "F - test-skip-until-close-paren-works-from-mid-stream"/imm32
1774     68/push  7/imm32
1775     b8/copy-to-eax  _test-input-stream/imm32
1776     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
1777     # . . call
1778     e8/call  check-ints-equal/disp32
1779     # . . discard args
1780     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1781     # . epilogue
1782     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1783     5d/pop-to-ebp
1784     c3/return
1786 skip-until-close-paren-in-slice:  # curr: (addr byte), end: (addr byte) -> curr/eax: (addr byte)
1787     # . prologue
1788     55/push-ebp
1789     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1790     # . save registers
1791     51/push-ecx
1792     52/push-edx
1793     # ecx = curr
1794     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
1795     # edx = end
1796     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .                         2/r32/edx   0xc/disp8         .               # copy *(ebp+12) to edx
1797     # var c/eax: byte = 0
1798     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
1799     # skip initial dquote
1800     41/increment-ecx
1801 $skip-until-close-paren-in-slice:loop:
1802     # if (curr >= end) break
1803     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
1804     73/jump-if-addr>=  $skip-until-close-paren-in-slice:break/disp8
1805     # c = *curr
1806     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
1807 $skip-until-close-paren-in-slice:check-close:
1808     # if (c == ')') break
1809     3d/compare-eax-and  0x29/imm32/close-paren
1810     74/jump-if-=  $skip-until-close-paren-in-slice:break/disp8
1811     # ++curr
1812     41/increment-ecx
1813     eb/jump  $skip-until-close-paren-in-slice:loop/disp8
1814 $skip-until-close-paren-in-slice:break:
1815     # return curr
1816     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy ecx to eax
1817 $skip-until-close-paren-in-slice:end:
1818     # . restore registers
1819     5a/pop-to-edx
1820     59/pop-to-ecx
1821     # . epilogue
1822     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1823     5d/pop-to-ebp
1824     c3/return
1826 test-skip-until-close-paren-in-slice:
1827     # . prologue
1828     55/push-ebp
1829     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1830     # setup: (eax..ecx) = "*(abc) def"
1831     b8/copy-to-eax  "*(abc) def"/imm32
1832     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1833     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1834     05/add-to-eax  4/imm32
1835     # eax = skip-until-close-paren-in-slice(eax, ecx)
1836     # . . push args
1837     51/push-ecx
1838     50/push-eax
1839     # . . call
1840     e8/call  skip-until-close-paren-in-slice/disp32
1841     # . . discard args
1842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1843     # check-ints-equal(ecx-eax, 5, msg)  # eax is at the ')'
1844     # . . push args
1845     68/push  "F - test-skip-until-close-paren-in-slice"/imm32
1846     68/push  5/imm32
1847     # . . push ecx-eax
1848     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1849     51/push-ecx
1850     # . . call
1851     e8/call  check-ints-equal/disp32
1852     # . . discard args
1853     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1854     # . epilogue
1855     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1856     5d/pop-to-ebp
1857     c3/return
1859 test-skip-until-close-paren-in-slice-ignores-spaces:
1860     # . prologue
1861     55/push-ebp
1862     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1863     # setup: (eax..ecx) = "*(a b)/yz"
1864     b8/copy-to-eax  "*(a b)/yz"/imm32
1865     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1866     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1867     05/add-to-eax  4/imm32
1868     # eax = skip-until-close-paren-in-slice(eax, ecx)
1869     # . . push args
1870     51/push-ecx
1871     50/push-eax
1872     # . . call
1873     e8/call  skip-until-close-paren-in-slice/disp32
1874     # . . discard args
1875     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1876     # check-ints-equal(ecx-eax, 4, msg)  # eax is at the ')'
1877     # . . push args
1878     68/push  "F - test-skip-until-close-paren-in-slice-ignores-spaces"/imm32
1879     68/push  4/imm32
1880     # . . push ecx-eax
1881     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1882     51/push-ecx
1883     # . . call
1884     e8/call  check-ints-equal/disp32
1885     # . . discard args
1886     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1887     # . epilogue
1888     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1889     5d/pop-to-ebp
1890     c3/return
1892 test-skip-until-close-paren-in-slice-stops-at-end:
1893     # . prologue
1894     55/push-ebp
1895     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
1896     # setup: (eax..ecx) = "*(abc"  # unbalanced dquote
1897     b8/copy-to-eax  "*(abc"/imm32
1898     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
1899     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
1900     05/add-to-eax  4/imm32
1901     # eax = skip-until-close-paren-in-slice(eax, ecx)
1902     # . . push args
1903     51/push-ecx
1904     50/push-eax
1905     # . . call
1906     e8/call  skip-until-close-paren-in-slice/disp32
1907     # . . discard args
1908     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
1909     # check-ints-equal(ecx-eax, 0, msg)  # skipped to end of slice
1910     # . . push args
1911     68/push  "F - test-skip-until-close-paren-in-slice-stops-at-end"/imm32
1912     68/push  0/imm32
1913     # . . push ecx-eax
1914     29/subtract                     3/mod/direct    1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract eax from ecx
1915     51/push-ecx
1916     # . . call
1917     e8/call  check-ints-equal/disp32
1918     # . . discard args
1919     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
1920     # . epilogue
1921     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
1922     5d/pop-to-ebp
1923     c3/return
1925 # . . vim:nowrap:textwidth=0