https://github.com/akkartik/mu/blob/master/077subx-words.subx
  1 # Helpers for parsing SubX words, with their rules for hex, labels and metadata.
  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 has-metadata?:  # word: (addr slice), s: (addr string) -> eax: boolean
  9     # pseudocode:
 10     #   var twig: &slice = next-token-from-slice(word->start, word->end, '/')  # skip name
 11     #   curr = twig->end
 12     #   while true
 13     #     twig = next-token-from-slice(curr, word->end, '/')
 14     #     if (twig.empty()) break
 15     #     if (slice-equal?(twig, s)) return true
 16     #     curr = twig->end
 17     #   return false
 18     # . prologue
 19     55/push-ebp
 20     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 21     # . save registers
 22     51/push-ecx
 23     52/push-edx
 24     56/push-esi
 25     57/push-edi
 26     # esi = word
 27     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
 28     # var edx: (addr byte) = word->end
 29     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           2/r32/edx   4/disp8         .                 # copy *(esi+4) to edx
 30     # var twig/edi: slice
 31     68/push  0/imm32/end
 32     68/push  0/imm32/start
 33     89/copy                         3/mod/direct    7/rm32/edi    .           .             .           4/r32/esp   .               .                 # copy esp to edi
 34     # next-token-from-slice(word->start, word->end, '/', twig)
 35     # . . push args
 36     57/push-edi
 37     68/push  0x2f/imm32/slash
 38     52/push-edx
 39     ff          6/subop/push        0/mod/indirect  6/rm32/esi    .           .             .           .           .               .                 # push *esi
 40     # . . call
 41     e8/call  next-token-from-slice/disp32
 42     # . . discard args
 43     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 44 $has-metadata?:loop:
 45     # next-token-from-slice(curr, word->end, '/', twig)
 46     # . . push args
 47     57/push-edi
 48     68/push  0x2f/imm32/slash
 49     52/push-edx
 50     ff          6/subop/push        1/mod/*+disp8   7/rm32/edi    .           .             .           .           4/disp8         .                 # push *(edi+4)
 51     # . . call
 52     e8/call  next-token-from-slice/disp32
 53     # . . discard args
 54     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0x10/imm32        # add to esp
 55     # if (slice-empty?(twig)) return false
 56     # . eax = slice-empty?(twig)
 57     # . . push args
 58     57/push-edi
 59     # . . call
 60     e8/call  slice-empty?/disp32
 61     # . . discard args
 62     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 63     # . if (eax != false) return false
 64     3d/compare-eax-and  0/imm32/false
 65     75/jump-if-!=  $has-metadata?:false/disp8
 66     # if (slice-equal?(twig, s)) return true
 67     # . eax = slice-equal?(twig, s)
 68     # . . push args
 69     ff          6/subop/push        1/mod/*+disp8   5/rm32/ebp    .           .             .           .           0xc/disp8       .                 # push *(ebp+12)
 70     57/push-edi
 71     # . . call
 72     e8/call  slice-equal?/disp32
 73     # . . discard args
 74     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 75     # . if (eax != false) return true
 76     3d/compare-eax-and  0/imm32/false
 77     # eax already contains true
 78     75/jump-if-!=  $has-metadata?:end/disp8
 79     eb/jump  $has-metadata?:loop/disp8
 80 $has-metadata?:false:
 81     b8/copy-to-eax  0/imm32/false
 82 $has-metadata?:end:
 83     # . reclaim locals
 84     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
 85     # . restore registers
 86     5f/pop-to-edi
 87     5e/pop-to-esi
 88     5a/pop-to-edx
 89     59/pop-to-ecx
 90     # . epilogue
 91     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 92     5d/pop-to-ebp
 93     c3/return
 94 
 95 test-has-metadata-true:
 96     # . prologue
 97     55/push-ebp
 98     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 99     # (eax..ecx) = "ab/imm32"
100     b8/copy-to-eax  "ab/imm32"/imm32
101     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
102     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
103     05/add-to-eax  4/imm32
104     # var in/esi: slice = {eax, ecx}
105     51/push-ecx
106     50/push-eax
107     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
108     # eax = has-metadata?(esi, "imm32")
109     # . . push args
110     68/push  "imm32"/imm32
111     56/push-esi
112     # . . call
113     e8/call  has-metadata?/disp32
114     # . . discard args
115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
116     # check-ints-equal(eax, 1, msg)
117     # . . push args
118     68/push  "F - test-has-metadata-true"/imm32
119     68/push  1/imm32/true
120     50/push-eax
121     # . . call
122     e8/call  check-ints-equal/disp32
123     # . . discard args
124     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
125     # . epilogue
126     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
127     5d/pop-to-ebp
128     c3/return
129 
130 test-has-metadata-false:
131     # . prologue
132     55/push-ebp
133     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
134     # (eax..ecx) = "ab/c"
135     b8/copy-to-eax  "ab/c"/imm32
136     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
137     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
138     05/add-to-eax  4/imm32
139     # var in/esi: slice = {eax, ecx}
140     51/push-ecx
141     50/push-eax
142     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
143     # eax = has-metadata?(esi, "d")
144     # . . push args
145     68/push  "d"/imm32
146     56/push-esi
147     # . . call
148     e8/call  has-metadata?/disp32
149     # . . discard args
150     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
151     # check-ints-equal(eax, 0, msg)
152     # . . push args
153     68/push  "F - test-has-metadata-false"/imm32
154     68/push  0/imm32/false
155     50/push-eax
156     # . . call
157     e8/call  check-ints-equal/disp32
158     # . . discard args
159     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
160     # . epilogue
161     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
162     5d/pop-to-ebp
163     c3/return
164 
165 test-has-metadata-ignore-name:
166     # . prologue
167     55/push-ebp
168     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
169     # (eax..ecx) = "a/b"
170     b8/copy-to-eax  "a/b"/imm32
171     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
172     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
173     05/add-to-eax  4/imm32
174     # var in/esi: slice = {eax, ecx}
175     51/push-ecx
176     50/push-eax
177     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
178     # eax = has-metadata?(esi, "a")
179     # . . push args
180     68/push  "a"/imm32
181     56/push-esi
182     # . . call
183     e8/call  has-metadata?/disp32
184     # . . discard args
185     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
186     # check-ints-equal(eax, 0, msg)
187     # . . push args
188     68/push  "F - test-has-metadata-ignore-name"/imm32
189     68/push  0/imm32/false
190     50/push-eax
191     # . . call
192     e8/call  check-ints-equal/disp32
193     # . . discard args
194     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
195     # . epilogue
196     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
197     5d/pop-to-ebp
198     c3/return
199 
200 test-has-metadata-multiple-true:
201     # . prologue
202     55/push-ebp
203     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
204     # (eax..ecx) = "a/b/c"
205     b8/copy-to-eax  "a/b/c"/imm32
206     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
207     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
208     05/add-to-eax  4/imm32
209     # var in/esi: slice = {eax, ecx}
210     51/push-ecx
211     50/push-eax
212     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
213     # eax = has-metadata?(esi, "c")
214     # . . push args
215     68/push  "c"/imm32
216     56/push-esi
217     # . . call
218     e8/call  has-metadata?/disp32
219     # . . discard args
220     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
221     # check-ints-equal(eax, 1, msg)
222     # . . push args
223     68/push  "F - test-has-metadata-multiple-true"/imm32
224     68/push  1/imm32/true
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     # . epilogue
231     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
232     5d/pop-to-ebp
233     c3/return
234 
235 test-has-metadata-multiple-false:
236     # . prologue
237     55/push-ebp
238     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
239     # (eax..ecx) = "a/b/c"
240     b8/copy-to-eax  "a/b/c"/imm32
241     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
242     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
243     05/add-to-eax  4/imm32
244     # var in/esi: slice = {eax, ecx}
245     51/push-ecx
246     50/push-eax
247     89/copy                         3/mod/direct    6/rm32/esi    .           .             .           4/r32/esp   .               .                 # copy esp to esi
248     # eax = has-metadata?(esi, "d")
249     # . . push args
250     68/push  "d"/imm32
251     56/push-esi
252     # . . call
253     e8/call  has-metadata?/disp32
254     # . . discard args
255     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
256     # check-ints-equal(eax, 0, msg)
257     # . . push args
258     68/push  "F - test-has-metadata-multiple-false"/imm32
259     68/push  0/imm32/false
260     50/push-eax
261     # . . call
262     e8/call  check-ints-equal/disp32
263     # . . discard args
264     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
265     # . epilogue
266     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
267     5d/pop-to-ebp
268     c3/return
269 
270 # conditions for 'valid' names that are not at risk of looking like hex numbers
271 # keep in sync with the rules in labels.cc
272 #: - if it starts with a digit, it's treated as a number. If it can't be
273 #:   parsed as hex it will raise an error.
274 #: - if it starts with '-' it's treated as a number.
275 #: - if it starts with '0x' it's treated as a number. (redundant)
276 #: - if it's two characters long, it can't be a name. Either it's a hex
277 #:   byte, or it raises an error.
278 is-valid-name?:  # in: (addr slice) -> eax: boolean
279     # . prologue
280     55/push-ebp
281     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
282     # . save registers
283     51/push-ecx
284     56/push-esi
285     # esi = in
286     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           6/r32/esi   8/disp8         .                 # copy *(ebp+8) to esi
287     # var start/ecx: (addr byte) = in->start
288     8b/copy                         0/mod/indirect  6/rm32/esi    .           .             .           1/r32/ecx   .               .                 # copy *esi to ecx
289 $is-valid-name?:check0:
290     # if (start >= in->end) return false
291     3b/compare                      1/mod/*+disp8   6/rm32/esi    .           .             .           1/r32/ecx   4/disp8         .                 # compare ecx with *(esi+4)
292     73/jump-if-addr>=  $is-valid-name?:false/disp8
293 $is-valid-name?:check1:
294     # var len/eax: int = in->end - start
295     8b/copy                         1/mod/*+disp8   6/rm32/esi    .           .             .           0/r32/eax   4/disp8         .                 # copy *(esi+4) to eax
296     29/subtract                     3/mod/direct    0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # subtract ecx from eax
297     # if (eax == 2) return false
298     3d/compare-eax-and  2/imm32
299     74/jump-if-=  $is-valid-name?:false/disp8
300 $is-valid-name?:check2:
301     # var c/eax: (addr byte) = *start
302     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
303     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
304     # if (c == "-") return false
305     3d/compare-eax-and  2d/imm32/-
306     74/jump-if-=  $is-valid-name?:false/disp8
307 $is-valid-name?:check3a:
308     # if (c < "0") return true
309     3d/compare-eax-with  30/imm32/0
310     7c/jump-if-<  $is-valid-name?:true/disp8
311 $is-valid-name?:check3b:
312     # if (c > "9") return true
313     3d/compare-eax-with  39/imm32/9
314     7f/jump-if->  $is-valid-name?:true/disp8
315 $is-valid-name?:false:
316     # return false
317     b8/copy-to-eax  0/imm32/false
318     eb/jump  $is-valid-name?:end/disp8
319 $is-valid-name?:true:
320     # return true
321     b8/copy-to-eax  1/imm32/true
322 $is-valid-name?:end:
323     # . restore registers
324     5e/pop-to-esi
325     59/pop-to-ecx
326     # . epilogue
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-is-valid-name-digit-prefix:
332     # . prologue
333     55/push-ebp
334     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
335     # (eax..ecx) = "34"
336     b8/copy-to-eax  "34"/imm32
337     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
338     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
339     05/add-to-eax  4/imm32
340     # var slice/ecx: slice = {eax, ecx}
341     51/push-ecx
342     50/push-eax
343     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
344     # eax = is-valid-name?(slice)
345     # . . push args
346     51/push-ecx
347     # . . call
348     e8/call  is-valid-name?/disp32
349     # . . discard args
350     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
351     # check-ints-equal(eax, 0, msg)
352     # . . push args
353     68/push  "F - test-is-valid-name-digit-prefix"/imm32
354     68/push  0/imm32/false
355     50/push-eax
356     # . . call
357     e8/call  check-ints-equal/disp32
358     # . . discard args
359     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
360     # . epilogue
361     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
362     5d/pop-to-ebp
363     c3/return
364 
365 test-is-valid-name-negative-prefix:
366     # . prologue
367     55/push-ebp
368     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
369     # (eax..ecx) = "-0x34"
370     b8/copy-to-eax  "-0x34"/imm32
371     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
372     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
373     05/add-to-eax  4/imm32
374     # var slice/ecx: slice = {eax, ecx}
375     51/push-ecx
376     50/push-eax
377     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
378     # eax = is-valid-name?(slice)
379     # . . push args
380     51/push-ecx
381     # . . call
382     e8/call  is-valid-name?/disp32
383     # . . discard args
384     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
385     # check-ints-equal(eax, 0, msg)
386     # . . push args
387     68/push  "F - test-is-valid-name-negative-prefix"/imm32
388     68/push  0/imm32/false
389     50/push-eax
390     # . . call
391     e8/call  check-ints-equal/disp32
392     # . . discard args
393     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
394     # . epilogue
395     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
396     5d/pop-to-ebp
397     c3/return
398 
399 test-is-valid-name-0x-prefix:
400     # . prologue
401     55/push-ebp
402     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
403     # (eax..ecx) = "0x34"
404     b8/copy-to-eax  "0x34"/imm32
405     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
406     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
407     05/add-to-eax  4/imm32
408     # var slice/ecx: slice = {eax, ecx}
409     51/push-ecx
410     50/push-eax
411     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
412     # eax = is-valid-name?(slice)
413     # . . push args
414     51/push-ecx
415     # . . call
416     e8/call  is-valid-name?/disp32
417     # . . discard args
418     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
419     # check-ints-equal(eax, 0, msg)
420     # . . push args
421     68/push  "F - test-is-valid-name-0x-prefix"/imm32
422     68/push  0/imm32/false
423     50/push-eax
424     # . . call
425     e8/call  check-ints-equal/disp32
426     # . . discard args
427     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
428     # . epilogue
429     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
430     5d/pop-to-ebp
431     c3/return
432 
433 test-is-valid-name-starts-with-pre-digit:
434     # . prologue
435     55/push-ebp
436     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
437     # (eax..ecx) = "/03"
438     b8/copy-to-eax  "/03"/imm32
439     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
440     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
441     05/add-to-eax  4/imm32
442     # var slice/ecx: slice = {eax, ecx}
443     51/push-ecx
444     50/push-eax
445     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
446     # eax = is-valid-name?(slice)
447     # . . push args
448     51/push-ecx
449     # . . call
450     e8/call  is-valid-name?/disp32
451     # . . discard args
452     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
453     # check-ints-equal(eax, 1, msg)
454     # . . push args
455     68/push  "F - test-is-valid-name-starts-with-pre-digit"/imm32
456     68/push  1/imm32/true
457     50/push-eax
458     # . . call
459     e8/call  check-ints-equal/disp32
460     # . . discard args
461     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
462     # . epilogue
463     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
464     5d/pop-to-ebp
465     c3/return
466 
467 test-is-valid-name-starts-with-post-digit:
468     # . prologue
469     55/push-ebp
470     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
471     # (eax..ecx) = "q34"
472     b8/copy-to-eax  "q34"/imm32
473     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
474     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
475     05/add-to-eax  4/imm32
476     # var slice/ecx: slice = {eax, ecx}
477     51/push-ecx
478     50/push-eax
479     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
480     # eax = is-valid-name?(slice)
481     # . . push args
482     51/push-ecx
483     # . . call
484     e8/call  is-valid-name?/disp32
485     # . . discard args
486     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
487     # check-ints-equal(eax, 1, msg)
488     # . . push args
489     68/push  "F - test-is-valid-name-starts-with-post-digit"/imm32
490     68/push  1/imm32/true
491     50/push-eax
492     # . . call
493     e8/call  check-ints-equal/disp32
494     # . . discard args
495     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
496     # . epilogue
497     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
498     5d/pop-to-ebp
499     c3/return
500 
501 test-is-valid-name-starts-with-digit:
502     # . prologue
503     55/push-ebp
504     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
505     # (eax..ecx) = "0x34"
506     b8/copy-to-eax  "0x34"/imm32
507     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
508     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
509     05/add-to-eax  4/imm32
510     # var slice/ecx: slice = {eax, ecx}
511     51/push-ecx
512     50/push-eax
513     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
514     # eax = is-valid-name?(slice)
515     # . . push args
516     51/push-ecx
517     # . . call
518     e8/call  is-valid-name?/disp32
519     # . . discard args
520     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
521     # check-ints-equal(eax, 0, msg)
522     # . . push args
523     68/push  "F - test-is-valid-name-starts-with-digit"/imm32
524     68/push  0/imm32/false
525     50/push-eax
526     # . . call
527     e8/call  check-ints-equal/disp32
528     # . . discard args
529     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
530     # . epilogue
531     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
532     5d/pop-to-ebp
533     c3/return
534 
535 is-label?: # word: (addr slice) -> eax: boolean
536     # . prologue
537     55/push-ebp
538     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
539     # . save registers
540     51/push-ecx
541     # ecx = word
542     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
543     # var end/ecx: (addr byte) = word->end
544     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           1/r32/ecx   4/disp8         .                 # copy *(ecx+4) to ecx
545     # return *(end - 1) == ':'
546     # . eax = *(end-1)
547     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
548     8a/copy-byte                    1/mod/*+disp8   1/rm32/ecx    .           .             .           0/r32/AL    -1/disp8         .                 # copy byte at *(ecx-1) to AL
549     # . return (eax == ':')
550     3d/compare-eax-and  0x3a/imm32/colon
551     b8/copy-to-eax  1/imm32/true
552     74/jump-if-=  $is-label?:end/disp8
553     b8/copy-to-eax  0/imm32/false
554 $is-label?:end:
555     # . restore registers
556     59/pop-to-ecx
557     # . epilogue
558     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
559     5d/pop-to-ebp
560     c3/return
561 
562 test-is-label?:
563     # . prologue
564     55/push-ebp
565     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
566 $test-is-label?:true:
567     # (eax..ecx) = "AAA:"
568     b8/copy-to-eax  "AAA:"/imm32
569     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
570     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
571     05/add-to-eax  4/imm32
572     # var slice/ecx: slice = {eax, ecx}
573     51/push-ecx
574     50/push-eax
575     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
576     # is-label?(slice/ecx)
577     # . . push args
578     51/push-ecx
579     # . . call
580     e8/call  is-label?/disp32
581     # . . discard args
582     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
583     # check-ints-equal(eax, 1, msg)
584     # . . push args
585     68/push  "F - test-is-label?:true"/imm32
586     68/push  1/imm32
587     50/push-eax
588     # . . call
589     e8/call  check-ints-equal/disp32
590     # . . discard args
591     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
592 $test-is-label?:false:
593     # (eax..ecx) = "AAA"
594     b8/copy-to-eax  "AAA"/imm32
595     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
596     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  1/index/ecx   .           1/r32/ecx   4/disp8         .                 # copy eax+ecx+4 to ecx
597     05/add-to-eax  4/imm32
598     # var slice/ecx: slice = {eax, ecx}
599     51/push-ecx
600     50/push-eax
601     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
602     # is-label?(slice/ecx)
603     # . . push args
604     51/push-ecx
605     # . . call
606     e8/call  is-label?/disp32
607     # . . discard args
608     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
609     # check-ints-equal(eax, 0, msg)
610     # . . push args
611     68/push  "F - test-is-label?:false"/imm32
612     68/push  0/imm32
613     50/push-eax
614     # . . call
615     e8/call  check-ints-equal/disp32
616     # . . discard args
617     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
618     # . epilogue
619     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
620     5d/pop-to-ebp
621     c3/return
622 
623 # . . vim:nowrap:textwidth=0