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