https://github.com/akkartik/mu/blob/master/127next-word.subx
  1 # Tokenize by whitespace.
  2 
  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
  7 
  8 # (re)compute the bounds of the next word in the line
  9 # return empty string on reaching end of file
 10 next-word:  # line: (addr stream 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 = line
 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   0xc/disp8       .                 # copy *(ebp+12) to edi
 23     # skip-chars-matching-whitespace(line)
 24     # . . push args
 25     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 26     # . . call
 27     e8/call  skip-chars-matching-whitespace/disp32
 28     # . . discard args
 29     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 30 $next-word:check0:
 31     # if (line->read >= line->write) clear out and return
 32     # . eax = line->read
 33     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
 34     # . if (eax < line->write) goto next check
 35     3b/compare                      0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # compare eax with *esi
 36     7c/jump-if-<  $next-word:check-for-comment/disp8
 37     # . return out
 38     c7          0/subop/copy        0/mod/direct    7/rm32/edi    .           .             .           .           .               0/imm32           # copy to *edi
 39     c7          0/subop/copy        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         0/imm32           # copy to *(edi+4)
 40     eb/jump  $next-word:end/disp8
 41 $next-word:check-for-comment:
 42     # out->start = &line->data[line->read]
 43     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 44     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
 45     89/copy                         0/mod/indirect  7/rm32/edi    .           .             .           0/r32/eax   .               .                 # copy eax to *edi
 46     # if (line->data[line->read] == '#') out->end = &line->data[line->write]), skip rest of stream and return
 47     # . eax = line->data[line->read]
 48     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
 49     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
 50     # . compare
 51     3d/compare-eax-and  0x23/imm32/pound
 52     75/jump-if-!=  $next-word:regular-word/disp8
 53 $next-word:comment:
 54     # . out->end = &line->data[line->write]
 55     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           0/r32/eax   .               .                 # copy *esi to eax
 56     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
 57     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
 58     # . line->read = line->write
 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:end/disp8
 62 $next-word:regular-word:
 63     # otherwise skip-chars-not-matching-whitespace(line)  # including trailing newline
 64     # . . push args
 65     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           8/disp8         .                 # push *(ebp+8)
 66     # . . call
 67     e8/call  skip-chars-not-matching-whitespace/disp32
 68     # . . discard args
 69     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 70     # out->end = &line->data[line->read]
 71     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(esi+4) to ecx
 72     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
 73     89/copy                         1/mod/*+disp8   7/rm32/edi    .           .             .           0/r32/eax   4/disp8         .                 # copy eax to *(edi+4)
 74 $next-word:end:
 75     # . restore registers
 76     5f/pop-to-edi
 77     5e/pop-to-esi
 78     59/pop-to-ecx
 79     58/pop-to-eax
 80     # . epilogue
 81     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 82     5d/pop-to-ebp
 83     c3/return
 84 
 85 test-next-word:
 86     # . prologue
 87     55/push-ebp
 88     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 89     # setup
 90     # . clear-stream(_test-stream)
 91     # . . push args
 92     68/push  _test-stream/imm32
 93     # . . call
 94     e8/call  clear-stream/disp32
 95     # . . discard args
 96     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 97     # var slice/ecx: slice
 98     68/push  0/imm32/end
 99     68/push  0/imm32/start
100     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
101     # write(_test-stream, "  ab")
102     # . . push args
103     68/push  "  ab"/imm32
104     68/push  _test-stream/imm32
105     # . . call
106     e8/call  write/disp32
107     # . . discard args
108     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
109     # next-word(_test-stream, slice)
110     # . . push args
111     51/push-ecx
112     68/push  _test-stream/imm32
113     # . . call
114     e8/call  next-word/disp32
115     # . . discard args
116     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
117     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
118     # . check-ints-equal(slice->start - _test-stream, 14, msg)
119     # . . push args
120     68/push  "F - test-next-word: start"/imm32
121     68/push  0xe/imm32
122     # . . push slice->start - _test-stream
123     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
124     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
125     50/push-eax
126     # . . call
127     e8/call  check-ints-equal/disp32
128     # . . discard args
129     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
130     # check-ints-equal(slice->end - _test-stream->data, 4, msg)
131     # . check-ints-equal(slice->end - _test-stream, 16, msg)
132     # . . push args
133     68/push  "F - test-next-word: end"/imm32
134     68/push  0x10/imm32
135     # . . push slice->end - _test-stream
136     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
137     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
138     50/push-eax
139     # . . call
140     e8/call  check-ints-equal/disp32
141     # . . discard args
142     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
143     # . epilogue
144     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
145     5d/pop-to-ebp
146     c3/return
147 
148 test-next-word-returns-whole-comment:
149     # . prologue
150     55/push-ebp
151     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
152     # setup
153     # . clear-stream(_test-stream)
154     # . . push args
155     68/push  _test-stream/imm32
156     # . . call
157     e8/call  clear-stream/disp32
158     # . . discard args
159     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
160     # var slice/ecx: slice
161     68/push  0/imm32/end
162     68/push  0/imm32/start
163     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
164     # write(_test-stream, "  # a")
165     # . . push args
166     68/push  "  # a"/imm32
167     68/push  _test-stream/imm32
168     # . . call
169     e8/call  write/disp32
170     # . . discard args
171     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
172     # next-word(_test-stream, slice)
173     # . . push args
174     51/push-ecx
175     68/push  _test-stream/imm32
176     # . . call
177     e8/call  next-word/disp32
178     # . . discard args
179     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
180     # check-ints-equal(slice->start - _test-stream->data, 2, msg)
181     # . check-ints-equal(slice->start - _test-stream, 14, msg)
182     # . . push args
183     68/push  "F - test-next-word-returns-whole-comment: start"/imm32
184     68/push  0xe/imm32
185     # . . push slice->start - _test-stream
186     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # copy *ecx to eax
187     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
188     50/push-eax
189     # . . call
190     e8/call  check-ints-equal/disp32
191     # . . discard args
192     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
193     # check-ints-equal(slice->end - _test-stream->data, 5, msg)
194     # . check-ints-equal(slice->end - _test-stream, 17, msg)
195     # . . push args
196     68/push  "F - test-next-word-returns-whole-comment: end"/imm32
197     68/push  0x11/imm32
198     # . . push slice->end - _test-stream
199     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
200     81          5/subop/subtract    3/mod/direct    0/rm32/eax    .           .             .           .           .               _test-stream/imm32 # subtract from eax
201     50/push-eax
202     # . . call
203     e8/call  check-ints-equal/disp32
204     # . . discard args
205     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
206     # . epilogue
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-next-word-returns-empty-string-on-eof:
212     # . prologue
213     55/push-ebp
214     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
215     # setup
216     # . clear-stream(_test-stream)
217     # . . push args
218     68/push  _test-stream/imm32
219     # . . call
220     e8/call  clear-stream/disp32
221     # . . discard args
222     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
223     # var slice/ecx: slice
224     68/push  0/imm32/end
225     68/push  0/imm32/start
226     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
227     # write nothing to _test-stream
228     # next-word(_test-stream, slice)
229     # . . push args
230     51/push-ecx
231     68/push  _test-stream/imm32
232     # . . call
233     e8/call  next-word/disp32
234     # . . discard args
235     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
236     # check-ints-equal(slice->end - slice->start, 0, msg)
237     # . . push args
238     68/push  "F - test-next-word-returns-empty-string-on-eof"/imm32
239     68/push  0/imm32
240     # . . push slice->end - slice->start
241     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
242     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
243     50/push-eax
244     # . . call
245     e8/call  check-ints-equal/disp32
246     # . . discard args
247     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
248     # . epilogue
249     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
250     5d/pop-to-ebp
251     c3/return
252 
253 test-next-word-returns-empty-string-on-newline:
254     # . prologue
255     55/push-ebp
256     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
257     # setup
258     # . clear-stream(_test-stream)
259     # . . push args
260     68/push  _test-stream/imm32
261     # . . call
262     e8/call  clear-stream/disp32
263     # . . discard args
264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
265     # var slice/ecx: slice
266     68/push  0/imm32/end
267     68/push  0/imm32/start
268     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
269     # write some whitespace and a newline
270     # . . push args
271     68/push  "  \n"/imm32
272     68/push  _test-stream/imm32
273     # . . call
274     e8/call  write/disp32
275     # . . discard args
276     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
277     # next-word(_test-stream, slice)
278     # . . push args
279     51/push-ecx
280     68/push  _test-stream/imm32
281     # . . call
282     e8/call  next-word/disp32
283     # . . discard args
284     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
285     # check-ints-equal(slice->end - slice->start, 0, msg)
286     # . . push args
287     68/push  "F - test-next-word-returns-empty-string-on-newline"/imm32
288     68/push  0/imm32
289     # . . push slice->end - slice->start
290     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/eax   4/disp8         .                 # copy *(ecx+4) to eax
291     2b/subtract                     0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/eax   .               .                 # subtract *ecx from eax
292     50/push-eax
293     # . . call
294     e8/call  check-ints-equal/disp32
295     # . . discard args
296     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
297     # . epilogue
298     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
299     5d/pop-to-ebp
300     c3/return