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