https://github.com/akkartik/mu/blob/master/067parse-hex.subx
  1 # some utilities for converting numbers from hex
  2 # lowercase letters only for now
  3 
  4 == code
  5 #   instruction                     effective address                                                   register    displacement    immediate
  6 # . op          subop               mod             rm32          base        index         scale       r32
  7 # . 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
  8 
  9 is-hex-int?:  # in : (address slice) -> eax : boolean
 10     # . prologue
 11     55/push-ebp
 12     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 13     # . save registers
 14     51/push-ecx
 15     52/push-edx
 16     53/push-ebx
 17     # ecx = s
 18     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
 19     # edx = s->end
 20     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
 21     # curr/ecx = s->start
 22     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # copy *ecx to ecx
 23     # if s is empty return false
 24     b8/copy-to-eax  0/imm32/false
 25     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 26     73/jump-if-greater-or-equal-unsigned  $is-hex-int?:end/disp8
 27     # skip past leading '-'
 28     # . if (*curr == '-') ++curr
 29     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 30     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
 31     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x2d/imm32/-      # compare ebx
 32     75/jump-if-not-equal  $is-hex-int?:initial-0/disp8
 33     # . ++curr
 34     41/increment-ecx
 35     # skip past leading '0x'
 36 $is-hex-int?:initial-0:
 37     # . if (*curr != '0') jump to loop
 38     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 39     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
 40     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x30/imm32/0      # compare ebx
 41     75/jump-if-not-equal  $is-hex-int?:loop/disp8
 42     # . ++curr
 43     41/increment-ecx
 44 $is-hex-int?:initial-0x:
 45     # . if (curr >= in->end) return true
 46     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 47     73/jump-if-greater-or-equal-unsigned  $is-hex-int?:true/disp8
 48     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
 49     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
 50     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           3/r32/BL    .               .                 # copy byte at *ecx to BL
 51     81          7/subop/compare     3/mod/direct    3/rm32/ebx    .           .             .           .           .               0x78/imm32/x      # compare ebx
 52     75/jump-if-not-equal  $is-hex-int?:loop/disp8
 53     # . ++curr
 54     41/increment-ecx
 55 $is-hex-int?:loop:
 56     # if (curr >= in->end) return true
 57     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
 58     73/jump-if-greater-or-equal-unsigned  $is-hex-int?:true/disp8
 59     # eax = is-hex-digit?(*curr)
 60     # . . push args
 61     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
 62     50/push-eax
 63     # . . call
 64     e8/call  is-hex-digit?/disp32
 65     # . . discard args
 66     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
 67     # if (eax == false) return false
 68     3d/compare-eax-and  0/imm32
 69     74/jump-if-equal  $is-hex-int?:end/disp8
 70     # ++curr
 71     41/increment-ecx
 72     # loop
 73     eb/jump  $is-hex-int?:loop/disp8
 74 $is-hex-int?:true:
 75     # return true
 76     b8/copy-to-eax  1/imm32/true
 77 $is-hex-int?:end:
 78     # . restore registers
 79     5b/pop-to-ebx
 80     5a/pop-to-edx
 81     59/pop-to-ecx
 82     # . epilogue
 83     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
 84     5d/pop-to-ebp
 85     c3/return
 86 
 87 test-is-hex-int:
 88     # . prologue
 89     55/push-ebp
 90     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
 91     # (eax..ecx) = "34"
 92     b8/copy-to-eax  "34"/imm32
 93     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
 94     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
 95     05/add-to-eax  4/imm32
 96     # var slice/ecx = {eax, ecx}
 97     51/push-ecx
 98     50/push-eax
 99     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
100     # eax = is-hex-int?(slice)
101     # . . push args
102     51/push-ecx
103     # . . call
104     e8/call  is-hex-int?/disp32
105     # . . discard args
106     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
107     # check-ints-equal(eax, 1, msg)
108     # . . push args
109     68/push  "F - test-is-hex-int"/imm32
110     68/push  1/imm32/true
111     50/push-eax
112     # . . call
113     e8/call  check-ints-equal/disp32
114     # . . discard args
115     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
116     # . epilogue
117     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
118     5d/pop-to-ebp
119     c3/return
120 
121 test-is-hex-int-handles-letters:
122     # . prologue
123     55/push-ebp
124     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
125     # (eax..ecx) = "34a"
126     b8/copy-to-eax  "34a"/imm32
127     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
128     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
129     05/add-to-eax  4/imm32
130     # var slice/ecx = {eax, ecx}
131     51/push-ecx
132     50/push-eax
133     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
134     # eax = is-hex-int?(slice)
135     # . . push args
136     51/push-ecx
137     # . . call
138     e8/call  is-hex-int?/disp32
139     # . . discard args
140     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
141     # check-ints-equal(eax, 1, msg)
142     # . . push args
143     68/push  "F - test-is-hex-int-handles-letters"/imm32
144     68/push  1/imm32/true
145     50/push-eax
146     # . . call
147     e8/call  check-ints-equal/disp32
148     # . . discard args
149     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
150     # . epilogue
151     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
152     5d/pop-to-ebp
153     c3/return
154 
155 test-is-hex-int-with-trailing-char:
156     # . prologue
157     55/push-ebp
158     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
159     # (eax..ecx) = "34q"
160     b8/copy-to-eax  "34q"/imm32
161     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
162     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
163     05/add-to-eax  4/imm32
164     # var slice/ecx = {eax, ecx}
165     51/push-ecx
166     50/push-eax
167     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
168     # eax = is-hex-int?(slice)
169     # . . push args
170     51/push-ecx
171     # . . call
172     e8/call  is-hex-int?/disp32
173     # . . discard args
174     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
175     # check-ints-equal(eax, 0, msg)
176     # . . push args
177     68/push  "F - test-is-hex-int-with-trailing-char"/imm32
178     68/push  0/imm32/false
179     50/push-eax
180     # . . call
181     e8/call  check-ints-equal/disp32
182     # . . discard args
183     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
184     # . epilogue
185     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
186     5d/pop-to-ebp
187     c3/return
188 
189 test-is-hex-int-with-leading-char:
190     # . prologue
191     55/push-ebp
192     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
193     # (eax..ecx) = "q34"
194     b8/copy-to-eax  "q34"/imm32
195     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
196     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
197     05/add-to-eax  4/imm32
198     # var slice/ecx = {eax, ecx}
199     51/push-ecx
200     50/push-eax
201     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
202     # eax = is-hex-int?(slice)
203     # . . push args
204     51/push-ecx
205     # . . call
206     e8/call  is-hex-int?/disp32
207     # . . discard args
208     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
209     # check-ints-equal(eax, 0, msg)
210     # . . push args
211     68/push  "F - test-is-hex-int-with-leading-char"/imm32
212     68/push  0/imm32/false
213     50/push-eax
214     # . . call
215     e8/call  check-ints-equal/disp32
216     # . . discard args
217     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
218     # . epilogue
219     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
220     5d/pop-to-ebp
221     c3/return
222 
223 test-is-hex-int-empty:
224     # . prologue
225     55/push-ebp
226     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
227     # var slice/ecx = ""
228     68/push  0/imm32
229     68/push  0/imm32
230     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
231     # eax = is-hex-int?(slice)
232     # . . push args
233     51/push-ecx
234     # . . call
235     e8/call  is-hex-int?/disp32
236     # . . discard args
237     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
238     # check-ints-equal(eax, 0, msg)
239     # . . push args
240     68/push  "F - test-is-hex-int-empty"/imm32
241     68/push  0/imm32/false
242     50/push-eax
243     # . . call
244     e8/call  check-ints-equal/disp32
245     # . . discard args
246     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
247     # . epilogue
248     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
249     5d/pop-to-ebp
250     c3/return
251 
252 test-is-hex-int-handles-0x-prefix:
253     # . prologue
254     55/push-ebp
255     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
256     # (eax..ecx) = "0x3a"
257     b8/copy-to-eax  "0x3a"/imm32
258     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
259     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
260     05/add-to-eax  4/imm32
261     # var slice/ecx = {eax, ecx}
262     51/push-ecx
263     50/push-eax
264     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
265     # eax = is-hex-int?(slice)
266     # . . push args
267     51/push-ecx
268     # . . call
269     e8/call  is-hex-int?/disp32
270     # . . discard args
271     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
272     # check-ints-equal(eax, 1, msg)
273     # . . push args
274     68/push  "F - test-is-hex-int-handles-0x-prefix"/imm32
275     68/push  1/imm32/true
276     50/push-eax
277     # . . call
278     e8/call  check-ints-equal/disp32
279     # . . discard args
280     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
281     # . epilogue
282     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
283     5d/pop-to-ebp
284     c3/return
285 
286 test-is-hex-int-handles-negative:
287     # . prologue
288     55/push-ebp
289     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
290     # (eax..ecx) = "-34a"
291     b8/copy-to-eax  "-34a"/imm32
292     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
293     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
294     05/add-to-eax  4/imm32
295     # var slice/ecx = {eax, ecx}
296     51/push-ecx
297     50/push-eax
298     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
299     # eax = is-hex-int?(slice)
300     # . . push args
301     51/push-ecx
302     # . . call
303     e8/call  is-hex-int?/disp32
304     # . . discard args
305     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
306     # check-ints-equal(eax, 1, msg)
307     # . . push args
308     68/push  "F - test-is-hex-int-handles-negative"/imm32
309     68/push  1/imm32/true
310     50/push-eax
311     # . . call
312     e8/call  check-ints-equal/disp32
313     # . . discard args
314     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
315     # . epilogue
316     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
317     5d/pop-to-ebp
318     c3/return
319 
320 test-is-hex-int-handles-negative-0x-prefix:
321     # . prologue
322     55/push-ebp
323     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
324     # (eax..ecx) = "-0x3a"
325     b8/copy-to-eax  "-0x3a"/imm32
326     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
327     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
328     05/add-to-eax  4/imm32
329     # var slice/ecx = {eax, ecx}
330     51/push-ecx
331     50/push-eax
332     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
333     # eax = is-hex-int?(slice)
334     # . . push args
335     51/push-ecx
336     # . . call
337     e8/call  is-hex-int?/disp32
338     # . . discard args
339     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
340     # check-ints-equal(eax, 1, msg)
341     # . . push args
342     68/push  "F - test-is-hex-int-handles-negative-0x-prefix"/imm32
343     68/push  1/imm32/true
344     50/push-eax
345     # . . call
346     e8/call  check-ints-equal/disp32
347     # . . discard args
348     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
349     # . epilogue
350     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
351     5d/pop-to-ebp
352     c3/return
353 
354 parse-hex-int:  # in : (address slice) -> result/eax
355     # . prologue
356     55/push-ebp
357     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
358     # . save registers
359     51/push-ecx
360     52/push-edx
361     53/push-ebx
362     56/push-esi
363     # result/ebx = 0
364     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
365     # ecx = s
366     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
367     # edx = s->end
368     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
369     # curr/ecx = s->start
370     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # copy *ecx to ecx
371     # negate?/esi = false
372     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
373 $parse-hex-int:negative:
374     # . if (*curr == '-') negate = true
375     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
376     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
377     3d/compare-eax-and  0x2d/imm32/-
378     75/jump-if-not-equal  $parse-hex-int:initial-0/disp8
379     # . ++curr
380     41/increment-ecx
381     # . negate = true
382     be/copy-to-esi  1/imm32/true
383 $parse-hex-int:initial-0:
384     # skip past leading '0x'
385     # . if (*curr != '0') jump to loop
386     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
387     3d/compare-eax-and  0x30/imm32/0
388     75/jump-if-not-equal  $parse-hex-int:loop/disp8
389     # . ++curr
390     41/increment-ecx
391 $parse-hex-int:initial-0x:
392     # . if (curr >= in->end) return result
393     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
394     73/jump-if-greater-or-equal-unsigned  $parse-hex-int:end/disp8
395     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
396     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
397     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
398     3d/compare-eax-and  0x78/imm32/x
399     75/jump-if-not-equal  $parse-hex-int:loop/disp8
400     # . ++curr
401     41/increment-ecx
402 $parse-hex-int:loop:
403     # if (curr >= in->end) break
404     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
405     73/jump-if-greater-or-equal-unsigned  $parse-hex-int:negate/disp8
406     # eax = from-hex-char(*curr)
407     # . . copy arg to eax
408     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
409     # . . call
410     e8/call  from-hex-char/disp32
411     # result = result * 16 + eax
412     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               4/imm8            # shift ebx left by 4 bits
413     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
414     # ++curr
415     41/increment-ecx
416     # loop
417     eb/jump  $parse-hex-int:loop/disp8
418 $parse-hex-int:negate:
419     81          7/subop/compare     3/mod/direct    6/rm32/esi    .           .             .           .           .               0/imm32           # compare esi
420     74/jump-if-equal  $parse-hex-int:end/disp8
421     f7          3/subop/negate      3/mod/direct    3/rm32/ebx    .           .             .           .           .               .                 # negate ebx
422 $parse-hex-int:end:
423     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to eax
424     # . restore registers
425     5e/pop-to-esi
426     5b/pop-to-ebx
427     5a/pop-to-edx
428     59/pop-to-ecx
429     # . epilogue
430     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
431     5d/pop-to-ebp
432     c3/return
433 
434 test-parse-hex-int-single-digit:
435     # . prologue
436     55/push-ebp
437     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
438     # (eax..ecx) = "a"
439     b8/copy-to-eax  "a"/imm32
440     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
441     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
442     05/add-to-eax  4/imm32
443     # var slice/ecx = {eax, ecx}
444     51/push-ecx
445     50/push-eax
446     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
447     # eax = parse-hex-int(slice)
448     # . . push args
449     51/push-ecx
450     # . . call
451     e8/call  parse-hex-int/disp32
452     # . . discard args
453     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
454     # check-ints-equal(eax, 0xa, msg)
455     # . . push args
456     68/push  "F - test-parse-hex-int-single-digit"/imm32
457     68/push  0xa/imm32
458     50/push-eax
459     # . . call
460     e8/call  check-ints-equal/disp32
461     # . . discard args
462     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
463     # . epilogue
464     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
465     5d/pop-to-ebp
466     c3/return
467 
468 test-parse-hex-int-multi-digit:
469     # . prologue
470     55/push-ebp
471     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
472     # (eax..ecx) = "34a"
473     b8/copy-to-eax  "34a"/imm32
474     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
475     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
476     05/add-to-eax  4/imm32
477     # var slice/ecx = {eax, ecx}
478     51/push-ecx
479     50/push-eax
480     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
481     # eax = parse-hex-int(slice)
482     # . . push args
483     51/push-ecx
484     # . . call
485     e8/call  parse-hex-int/disp32
486     # . . discard args
487     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
488     # check-ints-equal(eax, 0x34a, msg)
489     # . . push args
490     68/push  "F - test-parse-hex-int-multi-digit"/imm32
491     68/push  0x34a/imm32
492     50/push-eax
493     # . . call
494     e8/call  check-ints-equal/disp32
495     # . . discard args
496     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
497     # . epilogue
498     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
499     5d/pop-to-ebp
500     c3/return
501 
502 test-parse-hex-int-0x-prefix:
503     # . prologue
504     55/push-ebp
505     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
506     # (eax..ecx) = "0x34"
507     b8/copy-to-eax  "0x34"/imm32
508     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
509     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
510     05/add-to-eax  4/imm32
511     # var slice/ecx = {eax, ecx}
512     51/push-ecx
513     50/push-eax
514     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
515     # eax = parse-hex-int(slice)
516     # . . push args
517     51/push-ecx
518     # . . call
519     e8/call  parse-hex-int/disp32
520     # . . discard args
521     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
522     # check-ints-equal(eax, 0x34a, msg)
523     # . . push args
524     68/push  "F - test-parse-hex-int-0x-prefix"/imm32
525     68/push  0x34/imm32
526     50/push-eax
527     # . . call
528     e8/call  check-ints-equal/disp32
529     # . . discard args
530     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
531     # . epilogue
532     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
533     5d/pop-to-ebp
534     c3/return
535 
536 test-parse-hex-int-zero:
537     # . prologue
538     55/push-ebp
539     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
540     # (eax..ecx) = "0"
541     b8/copy-to-eax  "0"/imm32
542     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
543     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
544     05/add-to-eax  4/imm32
545     # var slice/ecx = {eax, ecx}
546     51/push-ecx
547     50/push-eax
548     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
549     # eax = parse-hex-int(slice)
550     # . . push args
551     51/push-ecx
552     # . . call
553     e8/call  parse-hex-int/disp32
554     # . . discard args
555     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
556     # check-ints-equal(eax, 0x34a, msg)
557     # . . push args
558     68/push  "F - test-parse-hex-int-zero"/imm32
559     68/push  0/imm32
560     50/push-eax
561     # . . call
562     e8/call  check-ints-equal/disp32
563     # . . discard args
564     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
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-parse-hex-int-0-prefix:
571     # . prologue
572     55/push-ebp
573     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
574     # (eax..ecx) = "03"
575     b8/copy-to-eax  "03"/imm32
576     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
577     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
578     05/add-to-eax  4/imm32
579     # var slice/ecx = {eax, ecx}
580     51/push-ecx
581     50/push-eax
582     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
583     # eax = parse-hex-int(slice)
584     # . . push args
585     51/push-ecx
586     # . . call
587     e8/call  parse-hex-int/disp32
588     # . . discard args
589     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
590     # check-ints-equal(eax, 0x3, msg)
591     # . . push args
592     68/push  "F - test-parse-hex-int-0-prefix"/imm32
593     68/push  0x3/imm32
594     50/push-eax
595     # . . call
596     e8/call  check-ints-equal/disp32
597     # . . discard args
598     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
599     # . epilogue
600     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
601     5d/pop-to-ebp
602     c3/return
603 
604 test-parse-hex-int-negative:
605     # . prologue
606     55/push-ebp
607     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
608     # (eax..ecx) = "-03"
609     b8/copy-to-eax  "-03"/imm32
610     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
611     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
612     05/add-to-eax  4/imm32
613     # var slice/ecx = {eax, ecx}
614     51/push-ecx
615     50/push-eax
616     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
617     # eax = parse-hex-int(slice)
618     # . . push args
619     51/push-ecx
620     # . . call
621     e8/call  parse-hex-int/disp32
622     # . . discard args
623     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
624     # check-ints-equal(eax, -3, msg)
625     # . . push args
626     68/push  "F - test-parse-hex-int-negative"/imm32
627     68/push  -3/imm32
628     50/push-eax
629     # . . call
630     e8/call  check-ints-equal/disp32
631     # . . discard args
632     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
633     # . epilogue
634     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
635     5d/pop-to-ebp
636     c3/return
637 
638 is-hex-digit?:  # c : byte -> eax : boolean
639     # . prologue
640     55/push-ebp
641     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
642     # . save registers
643     51/push-ecx
644     # ecx = c
645     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
646     # return false if c < '0'
647     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x30/imm32        # compare ecx
648     7c/jump-if-lesser  $is-hex-digit?:false/disp8
649     # return true if c <= '9'
650     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x39/imm32        # compare ecx
651     7e/jump-if-lesser-or-equal  $is-hex-digit?:true/disp8
652     # drop case
653     25/and-eax-with 0x5f/imm32
654     # return false if c > 'f'
655     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x66/imm32        # compare ecx
656     7f/jump-if-greater  $is-hex-digit?:false/disp8
657     # return true if c >= 'a'
658     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x61/imm32        # compare ecx
659     7d/jump-if-greater-or-equal  $is-hex-digit?:true/disp8
660     # otherwise return false
661 $is-hex-digit?:false:
662     b8/copy-to-eax  0/imm32/false
663     eb/jump $is-hex-digit?:end/disp8
664 $is-hex-digit?:true:
665     b8/copy-to-eax  1/imm32/true
666 $is-hex-digit?:end:
667     # . restore registers
668     59/pop-to-ecx
669     # . epilogue
670     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
671     5d/pop-to-ebp
672     c3/return
673 
674 test-hex-below-0:
675     # eax = is-hex-digit?(0x2f)
676     # . . push args
677     68/push  0x2f/imm32
678     # . . call
679     e8/call  is-hex-digit?/disp32
680     # . . discard args
681     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
682     # check-ints-equal(eax, 0, msg)
683     # . . push args
684     68/push  "F - test-hex-below-0"/imm32
685     68/push  0/imm32/false
686     50/push-eax
687     # . . call
688     e8/call  check-ints-equal/disp32
689     # . . discard args
690     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
691     c3/return
692 
693 test-hex-0-to-9:
694     # eax = is-hex-digit?(0x30)
695     # . . push args
696     68/push  0x30/imm32
697     # . . call
698     e8/call  is-hex-digit?/disp32
699     # . . discard args
700     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
701     # check-ints-equal(eax, 1, msg)
702     # . . push args
703     68/push  "F - test-hex-at-0"/imm32
704     68/push  1/imm32/true
705     50/push-eax
706     # . . call
707     e8/call  check-ints-equal/disp32
708     # . . discard args
709     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
710     # eax = is-hex-digit?(0x39)
711     # . . push args
712     68/push  0x39/imm32
713     # . . call
714     e8/call  is-hex-digit?/disp32
715     # . . discard args
716     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
717     # check-ints-equal(eax, 1, msg)
718     # . . push args
719     68/push  "F - test-hex-at-9"/imm32
720     68/push  1/imm32/true
721     50/push-eax
722     # . . call
723     e8/call  check-ints-equal/disp32
724     # . . discard args
725     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
726     c3/return
727 
728 test-hex-above-9-to-a:
729     # eax = is-hex-digit?(0x3a)
730     # . . push args
731     68/push  0x3a/imm32
732     # . . call
733     e8/call  is-hex-digit?/disp32
734     # . . discard args
735     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
736     # check-ints-equal(eax, 0, msg)
737     # . . push args
738     68/push  "F - test-hex-above-9-to-a"/imm32
739     68/push  0/imm32/false
740     50/push-eax
741     # . . call
742     e8/call  check-ints-equal/disp32
743     # . . discard args
744     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
745     c3/return
746 
747 test-hex-a-to-f:
748     # eax = is-hex-digit?(0x61)
749     # . . push args
750     68/push  0x61/imm32
751     # . . call
752     e8/call  is-hex-digit?/disp32
753     # . . discard args
754     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
755     # check-ints-equal(eax, 1, msg)
756     # . . push args
757     68/push  "F - test-hex-at-a"/imm32
758     68/push  1/imm32/true
759     50/push-eax
760     # . . call
761     e8/call  check-ints-equal/disp32
762     # . . discard args
763     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
764     # eax = is-hex-digit?(0x66)
765     # . . push args
766     68/push  0x66/imm32
767     # . . call
768     e8/call  is-hex-digit?/disp32
769     # . . discard args
770     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
771     # check-ints-equal(eax, 1, msg)
772     # . . push args
773     68/push  "F - test-hex-at-f"/imm32
774     68/push  1/imm32/true
775     50/push-eax
776     # . . call
777     e8/call  check-ints-equal/disp32
778     # . . discard args
779     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
780     c3/return
781 
782 test-hex-above-f:
783     # eax = is-hex-digit?(0x67)
784     # . . push args
785     68/push  0x67/imm32
786     # . . call
787     e8/call  is-hex-digit?/disp32
788     # . . discard args
789     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
790     # check-ints-equal(eax, 0, msg)
791     # . . push args
792     68/push  "F - test-hex-above-f"/imm32
793     68/push  0/imm32/false
794     50/push-eax
795     # . . call
796     e8/call  check-ints-equal/disp32
797     # . . discard args
798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
799     c3/return
800 
801 from-hex-char:  # in/eax : byte -> out/eax : nibble
802 $from-hex-char:check0:
803     # if (eax < '0') goto abort
804     3d/compare-eax-with  0x30/imm32/0
805     7c/jump-if-lesser  $from-hex-char:abort/disp8
806 $from-hex-char:check1:
807     # if (eax > 'f') goto abort
808     3d/compare-eax-with  0x66/imm32/f
809     7f/jump-if-greater  $from-hex-char:abort/disp8
810 $from-hex-char:check2:
811     # if (eax > '9') goto next check
812     3d/compare-eax-with  0x39/imm32/9
813     7f/jump-if-greater  $from-hex-char:check3/disp8
814 $from-hex-char:digit:
815     # return eax - '0'
816     2d/subtract-from-eax  0x30/imm32/0
817     c3/return
818 $from-hex-char:check3:
819     # if (eax < 'a') goto abort
820     3d/compare-eax-with  0x61/imm32/a
821     7c/jump-if-lesser  $from-hex-char:abort/disp8
822 $from-hex-char:letter:
823     # return eax - ('a'-10)
824     2d/subtract-from-eax  0x57/imm32/a-10
825     c3/return
826 
827 $from-hex-char:abort:
828     # . _write(2/stderr, error)
829     # . . push args
830     68/push  "invalid hex char: "/imm32
831     68/push  2/imm32/stderr
832     # . . call
833     e8/call  _write/disp32
834     # . . discard args
835     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
836     # . clear-stream(Stderr->buffer)
837     # . . save eax
838     50/push-eax
839     # . . push args
840     68/push  Stderr->buffer/imm32
841     # . . call
842     e8/call  clear-stream/disp32
843     # . . discard args
844     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
845     # . . restore eax
846     58/pop-to-eax
847     # . print-int32-buffered(Stderr, eax)
848     # . . push args
849     50/push-eax
850     68/push  Stderr/imm32
851     # . . call
852     e8/call  print-int32-buffered/disp32
853     # . . discard args
854     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
855     # . flush(Stderr)
856     # . . push args
857     68/push  Stderr/imm32
858     # . . call
859     e8/call  flush/disp32
860     # . . discard args
861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
862     # . _write(2/stderr, "\n")
863     # . . push args
864     68/push  Newline/imm32
865     68/push  2/imm32/stderr
866     # . . call
867     e8/call  _write/disp32
868     # . . discard args
869     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
870     # . syscall(exit, 1)
871     bb/copy-to-ebx  1/imm32
872     b8/copy-to-eax  1/imm32/exit
873     cd/syscall  0x80/imm8
874     # never gets here
875 
876 # . . vim:nowrap:textwidth=0