https://github.com/akkartik/mu/blob/master/subx/069next-token.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 # main:
  7     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
  8     # syscall(exit, Num-test-failures)
  9     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 10     b8/copy-to-EAX  1/imm32/exit
 11     cd/syscall  0x80/imm8
 12 
 13 # new data structure: a slice is an open interval of addresses [start, end) that includes 'start' but not 'end'
 14 
 15 # extract the next run of characters that are different from a given 'delimiter'
 16 # on eof return an empty interval
 17 next-token:  # in : (address stream), delimiter : byte, out : (address slice)
 18     # . prolog
 19     55/push-EBP
 20     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 21     # . save registers
 22     50/push-EAX
 23     51/push-ECX
 24     56/push-ESI
 25     57/push-EDI
 26     # ESI = in
 27     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
 28     # EDI = out
 29     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           7/r32/EDI   0x10/disp8      .                 # copy *(EBP+16) to EDI
 30     # skip-chars-matching(in, delimiter)
 31     # . . push args
 32     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 33     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 34     # . . call
 35     e8/call  skip-chars-matching/disp32
 36     # . . discard args
 37     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 38     # out->start = &in->data[in->read]
 39     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 40     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
 41     89/copy                         0/mod/indirect  7/rm32/EDI    .           .             .           0/r32/EAX   .               .                 # copy EAX to *EDI
 42     # skip-chars-not-matching(in, delimiter)
 43     # . . push args
 44     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           0xc/disp8       .                 # push *(EBP+12)
 45     ff          6/subop/push        1/mod/*+disp8   5/rm32/EBP    .           .             .           .           8/disp8         .                 # push *(EBP+8)
 46     # . . call
 47     e8/call  skip-chars-not-matching/disp32
 48     # . . discard args
 49     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 50     # out->end = &in->data[in->read]
 51     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
 52     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
 53     89/copy                         1/mod/*+disp8   7/rm32/EDI    .           .             .           0/r32/EAX   4/disp8         .                 # copy EAX to *(EDI+4)
 54     # . restore registers
 55     5f/pop-to-EDI
 56     5e/pop-to-ESI
 57     59/pop-to-ECX
 58     58/pop-to-EAX
 59     # . epilog
 60     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
 61     5d/pop-to-EBP
 62     c3/return
 63 
 64 test-next-token:
 65     # . prolog
 66     55/push-EBP
 67     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 68     # setup
 69     # . clear-stream(_test-stream)
 70     # . . push args
 71     68/push  _test-stream/imm32
 72     # . . call
 73     e8/call  clear-stream/disp32
 74     # . . discard args
 75     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
 76     # var slice/ECX = {0, 0}
 77     68/push  0/imm32/end
 78     68/push  0/imm32/start
 79     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
 80     # write(_test-stream, "  ab")
 81     # . . push args
 82     68/push  "  ab"/imm32
 83     68/push  _test-stream/imm32
 84     # . . call
 85     e8/call  write/disp32
 86     # . . discard args
 87     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
 88     # next-token(_test-stream, 0x20/space, slice)
 89     # . . push args
 90     51/push-ECX
 91     68/push  0x20/imm32
 92     68/push  _test-stream/imm32
 93     # . . call
 94     e8/call  next-token/disp32
 95     # . . discard args
 96     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
 97     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
 98     # . check-ints-equal(slice->start - _test-stream, 14, msg)
 99     # . . push args
100     68/push  "F - test-next-token: start"/imm32
101     68/push  0xe/imm32
102     # . . push slice->start - _test-stream
103     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           0/r32/EAX   .               .                 # copy *ECX to EAX
104     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
105     50/push-EAX
106     # . . call
107     e8/call  check-ints-equal/disp32
108     # . . discard args
109     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
110     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
111     # . check-ints-equal(slice->end - _test-stream, 16, msg)
112     # . . push args
113     68/push  "F - test-next-token: end"/imm32
114     68/push  0x10/imm32
115     # . . push slice->end - _test-stream
116     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           0/r32/EAX   4/disp8         .                 # copy *(ECX+4) to EAX
117     81          5/subop/subtract    3/mod/direct    0/rm32/EAX    .           .             .           .           .               _test-stream/imm32 # subtract from EAX
118     50/push-EAX
119     # . . call
120     e8/call  check-ints-equal/disp32
121     # . . discard args
122     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
123     # . epilog
124     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
125     5d/pop-to-EBP
126     c3/return
127 
128 test-next-token-eof:
129     # . prolog
130     55/push-EBP
131     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
132     # setup
133     # . clear-stream(_test-stream)
134     # . . push args
135     68/push  _test-stream/imm32
136     # . . call
137     e8/call  clear-stream/disp32
138     # . . discard args
139     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
140     # var slice/ECX = {0, 0}
141     68/push  0/imm32/end
142     68/push  0/imm32/start
143     89/copy                         3/mod/direct    1/rm32/ECX    .           .             .           4/r32/ESP   .               .                 # copy ESP to ECX
144     # write nothing to _test-stream
145     # next-token(_test-stream, 0x20/space, slice)
146     # . . push args
147     51/push-ECX
148     68/push  0x20/imm32
149     68/push  _test-stream/imm32
150     # . . call
151     e8/call  next-token/disp32
152     # . . discard args
153     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
154     # check-ints-equal(slice->end, slice->start, msg)
155     # . . push args
156     68/push  "F - test-next-token-eof"/imm32
157     ff          6/subop/push        1/mod/*+disp8   1/rm32/ECX    .           .             .           .           4/disp8         .                 # push *(ECX+4)
158     ff          6/subop/push        0/mod/indirect  1/rm32/ECX    .           .             .           .           .               .                 # push *ECX
159     # . . call
160     e8/call  check-ints-equal/disp32
161     # . . discard args
162     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
163     # . epilog
164     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
165     5d/pop-to-EBP
166     c3/return
167 
168 skip-chars-matching:  # in : (address stream), delimiter : byte
169     # . prolog
170     55/push-EBP
171     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
172     # . save registers
173     50/push-EAX
174     51/push-ECX
175     52/push-EDX
176     56/push-ESI
177     # ESI = in
178     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
179     # ECX = in->read
180     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
181     # EBX = in->write
182     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
183     # EDX = delimiter
184     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
185 $skip-chars-matching:loop:
186     # if (in->read >= in->write) break
187     3b/compare                      3/mod/direct    3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # compare ECX with *EBX
188     7d/jump-if-greater-or-equal  $skip-chars-matching:end/disp8
189     # EAX = in->data[in->read]
190     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
191     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
192     # if (EAX != delimiter) break
193     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
194     75/jump-if-not-equal  $skip-chars-matching:end/disp8
195     # ++in->read
196     41/inc-ECX
197     eb/jump  $skip-chars-matching:loop/disp8
198 $skip-chars-matching:end:
199     # persist in->read
200     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
201     # . restore registers
202     5e/pop-to-ESI
203     5a/pop-to-EDX
204     59/pop-to-ECX
205     58/pop-to-EAX
206     # . epilog
207     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
208     5d/pop-to-EBP
209     c3/return
210 
211 test-skip-chars-matching:
212     # setup
213     # . clear-stream(_test-stream)
214     # . . push args
215     68/push  _test-stream/imm32
216     # . . call
217     e8/call  clear-stream/disp32
218     # . . discard args
219     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
220     # write(_test-stream, "  ab")
221     # . . push args
222     68/push  "  ab"/imm32
223     68/push  _test-stream/imm32
224     # . . call
225     e8/call  write/disp32
226     # . . discard args
227     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
228     # skip-chars-matching(_test-stream, 0x20/space)
229     # . . push args
230     68/push  0x20/imm32
231     68/push  _test-stream/imm32
232     # . . call
233     e8/call  skip-chars-matching/disp32
234     # . . discard args
235     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
236     # check-ints-equal(_test-stream->read, 2, msg)
237     # . . push args
238     68/push  "F - test-skip-chars-matching"/imm32
239     68/push  2/imm32
240     # . . push *_test-stream->read
241     b8/copy-to-EAX  _test-stream/imm32
242     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
243     # . . call
244     e8/call  check-ints-equal/disp32
245     # . . discard args
246     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
247     # end
248     c3/return
249 
250 test-skip-chars-matching-none:
251     # setup
252     # . clear-stream(_test-stream)
253     # . . push args
254     68/push  _test-stream/imm32
255     # . . call
256     e8/call  clear-stream/disp32
257     # . . discard args
258     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
259     # write(_test-stream, "ab")
260     # . . push args
261     68/push  "ab"/imm32
262     68/push  _test-stream/imm32
263     # . . call
264     e8/call  write/disp32
265     # . . discard args
266     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
267     # skip-chars-matching(_test-stream, 0x20/space)
268     # . . push args
269     68/push  0x20/imm32
270     68/push  _test-stream/imm32
271     # . . call
272     e8/call  skip-chars-matching/disp32
273     # . . discard args
274     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
275     # check-ints-equal(_test-stream->read, 0, msg)
276     # . . push args
277     68/push  "F - test-skip-chars-matching-none"/imm32
278     68/push  0/imm32
279     # . . push *_test-stream->read
280     b8/copy-to-EAX  _test-stream/imm32
281     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
282     # . . call
283     e8/call  check-ints-equal/disp32
284     # . . discard args
285     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
286     # end
287     c3/return
288 
289 # minor fork of 'skip-chars-matching'
290 skip-chars-not-matching:  # in : (address stream), delimiter : byte
291     # . prolog
292     55/push-EBP
293     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
294     # . save registers
295     50/push-EAX
296     51/push-ECX
297     52/push-EDX
298     56/push-ESI
299     # ESI = in
300     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           6/r32/ESI   8/disp8         .                 # copy *(EBP+8) to ESI
301     # ECX = in->read
302     8b/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy *(ESI+4) to ECX
303     # EBX = in->write
304     8b/copy                         0/mod/indirect  6/rm32/ESI    .           .             .           3/r32/EBX   .               .                 # copy *ESI to EBX
305     # EDX = delimiter
306     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           2/r32/EDX   0xc/disp8       .                 # copy *(EBP+12) to EDX
307 $skip-chars-not-matching:loop:
308     # if (in->read >= in->write) break
309     3b/compare                      3/mod/direct    3/rm32/EBX    .           .             .           1/r32/ECX   .               .                 # compare ECX with *EBX
310     7d/jump-if-greater-or-equal  $skip-chars-not-matching:end/disp8
311     # EAX = in->data[in->read]
312     31/xor                          3/mod/direct    0/rm32/EAX    .           .             .           0/r32/EAX   .               .                 # clear EAX
313     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
314     # if (EAX == delimiter) break
315     39/compare                      3/mod/direct    0/rm32/EAX    .           .             .           2/r32/EDX   .               .                 # compare EAX and EDX
316     74/jump-if-equal  $skip-chars-not-matching:end/disp8
317     # ++in->read
318     41/inc-ECX
319     eb/jump  $skip-chars-not-matching:loop/disp8
320 $skip-chars-not-matching:end:
321     # persist in->read
322     89/copy                         1/mod/*+disp8   6/rm32/ESI    .           .             .           1/r32/ECX   4/disp8         .                 # copy ECX to *(ESI+4)
323     # . restore registers
324     5e/pop-to-ESI
325     5a/pop-to-EDX
326     59/pop-to-ECX
327     58/pop-to-EAX
328     # . epilog
329     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
330     5d/pop-to-EBP
331     c3/return
332 
333 test-skip-chars-not-matching:
334     # setup
335     # . clear-stream(_test-stream)
336     # . . push args
337     68/push  _test-stream/imm32
338     # . . call
339     e8/call  clear-stream/disp32
340     # . . discard args
341     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
342     # write(_test-stream, "ab ")
343     # . . push args
344     68/push  "ab "/imm32
345     68/push  _test-stream/imm32
346     # . . call
347     e8/call  write/disp32
348     # . . discard args
349     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
350     # skip-chars-not-matching(_test-stream, 0x20/space)
351     # . . push args
352     68/push  0x20/imm32
353     68/push  _test-stream/imm32
354     # . . call
355     e8/call  skip-chars-not-matching/disp32
356     # . . discard args
357     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
358     # check-ints-equal(_test-stream->read, 2, msg)
359     # . . push args
360     68/push  "F - test-skip-chars-not-matching"/imm32
361     68/push  2/imm32
362     # . . push *_test-stream->read
363     b8/copy-to-EAX  _test-stream/imm32
364     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
365     # . . call
366     e8/call  check-ints-equal/disp32
367     # . . discard args
368     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
369     # end
370     c3/return
371 
372 test-skip-chars-not-matching-none:
373     # setup
374     # . clear-stream(_test-stream)
375     # . . push args
376     68/push  _test-stream/imm32
377     # . . call
378     e8/call  clear-stream/disp32
379     # . . discard args
380     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
381     # write(_test-stream, " ab")
382     # . . push args
383     68/push  " ab"/imm32
384     68/push  _test-stream/imm32
385     # . . call
386     e8/call  write/disp32
387     # . . discard args
388     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
389     # skip-chars-not-matching(_test-stream, 0x20/space)
390     # . . push args
391     68/push  0x20/imm32
392     68/push  _test-stream/imm32
393     # . . call
394     e8/call  skip-chars-not-matching/disp32
395     # . . discard args
396     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
397     # check-ints-equal(_test-stream->read, 0, msg)
398     # . . push args
399     68/push  "F - test-skip-chars-not-matching-none"/imm32
400     68/push  0/imm32
401     # . . push *_test-stream->read
402     b8/copy-to-EAX  _test-stream/imm32
403     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
404     # . . call
405     e8/call  check-ints-equal/disp32
406     # . . discard args
407     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
408     # end
409     c3/return
410 
411 test-skip-chars-not-matching-all:
412     # setup
413     # . clear-stream(_test-stream)
414     # . . push args
415     68/push  _test-stream/imm32
416     # . . call
417     e8/call  clear-stream/disp32
418     # . . discard args
419     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
420     # write(_test-stream, "ab")
421     # . . push args
422     68/push  "ab"/imm32
423     68/push  _test-stream/imm32
424     # . . call
425     e8/call  write/disp32
426     # . . discard args
427     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
428     # skip-chars-not-matching(_test-stream, 0x20/space)
429     # . . push args
430     68/push  0x20/imm32
431     68/push  _test-stream/imm32
432     # . . call
433     e8/call  skip-chars-not-matching/disp32
434     # . . discard args
435     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               8/imm32           # add to ESP
436     # check-ints-equal(_test-stream->read, 2, msg)
437     # . . push args
438     68/push  "F - test-skip-chars-not-matching-all"/imm32
439     68/push  2/imm32
440     # . . push *_test-stream->read
441     b8/copy-to-EAX  _test-stream/imm32
442     ff          6/subop/push        1/mod/*+disp8   0/rm32/EAX    .           .             .           .           4/disp8         .                 # push *(EAX+4)
443     # . . call
444     e8/call  check-ints-equal/disp32
445     # . . discard args
446     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
447     # end
448     c3/return
449 
450 # . . vim:nowrap:textwidth=0