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