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