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