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