https://github.com/akkartik/mu/blob/main/135next-word-or-string.subx
  1 == code
  2 #   instruction                     effective address                                                   register    displacement    immediate
  3 # . op          subop               mod             rm32          base        index         scale       r32
  4 # . 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
  5 
  6 # (re)compute the bounds of the next word or string literal in the line
  7 # return empty string on reaching end of file
  8 next-word-or-string:  # line: (addr stream byte), out: (addr slice)
  9     # . prologue
 10     55/push-ebp
 11     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 12     # . save registers
 13     50/push-eax
 14     51/push-ecx
 15     56/push-esi
 16     57/push-edi
 17     # esi = line
 18     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 19     # edi = out
 20     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   0xc/disp8       .                 # copy *(ebp+12) to edi
 21     # skip-chars-matching(line, ' ')
 22     # . . push args
 23     68/push  0x20/imm32/space
 24     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 25     # . . call
 26     e8/call  skip-chars-matching/disp32
 27     # . . discard args
 28     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 29 $next-word-or-string:check0:
 30     # if (line->read >= line->write) clear out and return
 31     # . eax = line->read
 32     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
 33     # . if (eax < line->write) goto next check
 34     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
 35     7c/jump-if-<  $next-word-or-string:check-for-comment/disp8
 36     # . return out
 37     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
 38     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
 39     eb/jump  $next-word-or-string:end/disp8
 40 $next-word-or-string:check-for-comment:
 41     # out->start = &line->data[line->read]
 42     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 43     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
 44     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
 45     # if (line->data[line->read] != '#') goto next check
 46     # . eax = line->data[line->read]
 47     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 48     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
 49     # . compare
 50     3d/compare-eax-and  0x23/imm32/pound
 51     75/jump-if-!=  $next-word-or-string:check-for-string-literal/disp8
 52 $next-word-or-string:comment:
 53     # out->end = &line->data[line->write]
 54     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
 55     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    6/base/esi  0/index/eax   .           0/r32/eax   0xc/disp8       .                 # copy esi+eax+12 to eax
 56     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
 57     # line->read = line->write  # skip rest of line
 58     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
 59     89/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(esi+4)
 60     # return
 61     eb/jump  $next-word-or-string:end/disp8
 62 $next-word-or-string:check-for-string-literal:
 63     # if (line->data[line->read] != '"') goto next check
 64     # . eax = line->data[line->read]
 65     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 66     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
 67     # . compare
 68     3d/compare-eax-and  0x22/imm32/dquote
 69     75/jump-if-!=  $next-word-or-string:regular-word/disp8
 70 $next-word-or-string:string-literal:
 71     # skip-string(line)
 72     # . . push args
 73     56/push-esi
 74     # . . call
 75     e8/call  skip-string/disp32
 76     # . . discard args
 77     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 78     # fall through
 79 $next-word-or-string:regular-word:
 80     # skip-chars-not-matching-whitespace(line)  # including trailing newline
 81     # . . push args
 82     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 83     # . . call
 84     e8/call  skip-chars-not-matching-whitespace/disp32
 85     # . . discard args
 86     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 87     # out->end = &line->data[line->read]
 88     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 89     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
 90     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
 91 $next-word-or-string:end:
 92     # . restore registers
 93     5f/pop-to-edi
 94     5e/pop-to-esi
 95     59/pop-to-ecx
 96     58/pop-to-eax
 97     # . epilogue
 98     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 99     5d/pop-to-ebp
100     c3/return
101 
102 test-next-word-or-string:
103     # . prologue
104     55/push-ebp
105     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
106     # setup
107     # . clear-stream(_test-input-stream)
108     # . . push args
109     68/push  _test-input-stream/imm32
110     # . . call
111     e8/call  clear-stream/disp32
112     # . . discard args
113     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
114     # var slice/ecx: slice
115     68/push  0/imm32/end
116     68/push  0/imm32/start
117     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
118     # write(_test-input-stream, "  ab")
119     # . . push args
120     68/push  "  ab"/imm32
121     68/push  _test-input-stream/imm32
122     # . . call
123     e8/call  write/disp32
124     # . . discard args
125     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
126     # next-word-or-string(_test-input-stream, slice)
127     # . . push args
128     51/push-ecx
129     68/push  _test-input-stream/imm32
130     # . . call
131     e8/call  next-word-or-string/disp32
132     # . . discard args
133     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
134     # check-ints-equal(_test-input-stream->read, 4, msg)
135     # . . push args
136     68/push  "F - test-next-word-or-string/updates-stream-read-correctly"/imm32
137     68/push  4/imm32
138     b8/copy-to-eax  _test-input-stream/imm32
139     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
140     # . . call
141     e8/call  check-ints-equal/disp32
142     # . . discard args
143     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
144     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
145     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
146     # . . push args
147     68/push  "F - test-next-word-or-string: start"/imm32
148     68/push  0xe/imm32
149     # . . push slice->start - _test-input-stream
150     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
151     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
152     50/push-eax
153     # . . call
154     e8/call  check-ints-equal/disp32
155     # . . discard args
156     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
157     # check-ints-equal(slice->end - _test-input-stream->data, 4, msg)
158     # . check-ints-equal(slice->end - _test-input-stream, 16, msg)
159     # . . push args
160     68/push  "F - test-next-word-or-string: end"/imm32
161     68/push  0x10/imm32
162     # . . push slice->end - _test-input-stream
163     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
164     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
165     50/push-eax
166     # . . call
167     e8/call  check-ints-equal/disp32
168     # . . discard args
169     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
170     # . epilogue
171     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
172     5d/pop-to-ebp
173     c3/return
174 
175 test-next-word-or-string-returns-whole-comment:
176     # . prologue
177     55/push-ebp
178     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
179     # setup
180     # . clear-stream(_test-input-stream)
181     # . . push args
182     68/push  _test-input-stream/imm32
183     # . . call
184     e8/call  clear-stream/disp32
185     # . . discard args
186     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
187     # var slice/ecx: slice
188     68/push  0/imm32/end
189     68/push  0/imm32/start
190     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
191     # write(_test-input-stream, "  # a")
192     # . . push args
193     68/push  "  # a"/imm32
194     68/push  _test-input-stream/imm32
195     # . . call
196     e8/call  write/disp32
197     # . . discard args
198     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
199     # next-word-or-string(_test-input-stream, slice)
200     # . . push args
201     51/push-ecx
202     68/push  _test-input-stream/imm32
203     # . . call
204     e8/call  next-word-or-string/disp32
205     # . . discard args
206     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
207     # check-ints-equal(_test-input-stream->read, 5, msg)
208     # . . push args
209     68/push  "F - test-next-word-or-string-returns-whole-comment/updates-stream-read-correctly"/imm32
210     68/push  5/imm32
211     b8/copy-to-eax  _test-input-stream/imm32
212     ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           4/disp8         .                 # push *(eax+4)
213     # . . call
214     e8/call  check-ints-equal/disp32
215     # . . discard args
216     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
217     # check-ints-equal(slice->start - _test-input-stream->data, 2, msg)
218     # . check-ints-equal(slice->start - _test-input-stream, 14, msg)
219     # . . push args
220     68/push  "F - test-next-word-or-string-returns-whole-comment: start"/imm32
221     68/push  0xe/imm32
222     # . . push slice->start - _test-input-stream
223     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
224     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
225     50/push-eax
226     # . . call
227     e8/call  check-ints-equal/disp32
228     # . . discard args
229     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
230     # check-ints-equal(slice->end - _test-input-stream->data, 5, msg)
231     # . check-ints-equal(slice->end - _test-input-stream, 17, msg)
232     # . . push args
233     68/push  "F - test-next-word-or-string-returns-whole-comment: end"/imm32
234     68/push  0x11/imm32
235     # . . push slice->end - _test-input-stream
236     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
237     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
238     50/push-eax
239     # . . call
240     e8/call  check-ints-equal/disp32
241     # . . discard args
242     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
243     # . epilogue
244     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
245     5d/pop-to-ebp
246     c3/return
247 
248 test-next-word-or-string-returns-empty-slice-on-eof:
249     # . prologue
250     55/push-ebp
251     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
252     # setup
253     # . clear-stream(_test-input-stream)
254     # . . push args
255     68/push  _test-input-stream/imm32
256     # . . call
257     e8/call  clear-stream/disp32
258     # . . discard args
259     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
260     # var slice/ecx: slice
261     68/push  0/imm32/end
262     68/push  0/imm32/start
263     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
264     # write nothing to _test-input-stream
265     # next-word-or-string(_test-input-stream, slice)
266     # . . push args
267     51/push-ecx
268     68/push  _test-input-stream/imm32
269     # . . call
270     e8/call  next-word-or-string/disp32
271     # . . discard args
272     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
273     # check-ints-equal(slice->end - slice->start, 0, msg)
274     # . . push args
275     68/push  "F - test-next-word-or-string-returns-empty-string-on-eof"/imm32
276     68/push  0/imm32
277     # . . push slice->end - slice->start
278     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
279     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
280     50/push-eax
281     # . . call
282     e8/call  check-ints-equal/disp32
283     # . . discard args
284     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
285     # . epilogue
286     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
287     5d/pop-to-ebp
288     c3/return
289 
290 test-next-word-or-string-returns-string-literal:
291     # . prologue
292     55/push-ebp
293     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
294     # setup
295     # . clear-stream(_test-input-stream)
296     # . . push args
297     68/push  _test-input-stream/imm32
298     # . . call
299     e8/call  clear-stream/disp32
300     # . . discard args
301     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
302     # var slice/ecx: slice
303     68/push  0/imm32/end
304     68/push  0/imm32/start
305     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
306     # write(_test-input-stream, " \"a b\"/imm32 ")
307     # . . push args
308     68/push  " \"a b\"/imm32 "/imm32
309     68/push  _test-input-stream/imm32
310     # . . call
311     e8/call  write/disp32
312     # . . discard args
313     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
314     # next-word-or-string(_test-input-stream, slice)
315     # . . push args
316     51/push-ecx
317     68/push  _test-input-stream/imm32
318     # . . call
319     e8/call  next-word-or-string/disp32
320     # . . discard args
321     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
322     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
323     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
324     # . . push args
325     68/push  "F - test-next-word-or-string-returns-string-literal: start"/imm32
326     68/push  0xd/imm32
327     # . . push slice->start - _test-input-stream
328     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
329     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
330     50/push-eax
331     # . . call
332     e8/call  check-ints-equal/disp32
333     # . . discard args
334     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
335     # check-ints-equal(slice->end - _test-input-stream->data, 12, msg)
336     # . check-ints-equal(slice->end - _test-input-stream, 24, msg)
337     # . . push args
338     68/push  "F - test-next-word-or-string-returns-string-literal: end"/imm32
339     68/push  0x18/imm32
340     # . . push slice->end - _test-input-stream
341     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
342     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
343     50/push-eax
344     # . . call
345     e8/call  check-ints-equal/disp32
346     # . . discard args
347     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
348     # . epilogue
349     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
350     5d/pop-to-ebp
351     c3/return
352 
353 test-next-word-or-string-returns-string-with-escapes:
354     # . prologue
355     55/push-ebp
356     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
357     # setup
358     # . clear-stream(_test-input-stream)
359     # . . push args
360     68/push  _test-input-stream/imm32
361     # . . call
362     e8/call  clear-stream/disp32
363     # . . discard args
364     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
365     # var slice/ecx: slice
366     68/push  0/imm32/end
367     68/push  0/imm32/start
368     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
369     # write(_test-input-stream, " \"a\\\"b\"/x")
370     # . . push args
371     68/push  " \"a\\\"b\"/x"/imm32
372     68/push  _test-input-stream/imm32
373     # . . call
374     e8/call  write/disp32
375     # . . discard args
376     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
377     # next-word-or-string(_test-input-stream, slice)
378     # . . push args
379     51/push-ecx
380     68/push  _test-input-stream/imm32
381     # . . call
382     e8/call  next-word-or-string/disp32
383     # . . discard args
384     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
385     # check-ints-equal(slice->start - _test-input-stream->data, 1, msg)
386     # . check-ints-equal(slice->start - _test-input-stream, 13, msg)
387     # . . push args
388     68/push  "F - test-next-word-or-string-returns-string-with-escapes: start"/imm32
389     68/push  0xd/imm32
390     # . . push slice->start - _test-input-stream
391     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
392     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
393     50/push-eax
394     # . . call
395     e8/call  check-ints-equal/disp32
396     # . . discard args
397     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
398     # check-ints-equal(slice->end - _test-input-stream->data, 9, msg)
399     # . check-ints-equal(slice->end - _test-input-stream, 21, msg)
400     # . . push args
401     68/push  "F - test-next-word-or-string-returns-string-with-escapes: end"/imm32
402     68/push  0x15/imm32
403     # . . push slice->end - _test-input-stream
404     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
405     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-input-stream/imm32 # subtract from eax
406     50/push-eax
407     # . . call
408     e8/call  check-ints-equal/disp32
409     # . . discard args
410     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
411     # . epilogue
412     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
413     5d/pop-to-ebp
414     c3/return
415 
416 # . . vim:nowrap:textwidth=0