https://github.com/akkartik/mu/blob/master/subx/071hex.subx
  1 # some utilities for converting numbers to/from hex
  2 # lowercase letters only
  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 test-is-hex-int-handles-0x-prefix/disp32
 11     e8/call  run-tests/disp32  # 'run-tests' is a function created automatically by SubX. It calls all functions that start with 'test-'.
 12     # syscall(exit, Num-test-failures)
 13     8b/copy                         0/mod/indirect  5/rm32/.disp32            .             .           3/r32/EBX   Num-test-failures/disp32          # copy *Num-test-failures to EBX
 14     b8/copy-to-EAX  1/imm32/exit
 15     cd/syscall  0x80/imm8
 16 
 17 is-hex-int?:  # in : (address slice) -> bool/EAX
 18     # . prolog
 19     55/push-EBP
 20     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
 21     # . save registers
 22     51/push-ECX
 23     52/push-EDX
 24     53/push-EBX
 25     # ECX = s
 26     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
 27     # EDX = s->end
 28     8b/copy                         1/mod/*+disp8   1/rm32/ECX    .           .             .           2/r32/EDX   4/disp8         .                 # copy *(ECX+4) to EDX
 29     # curr/ECX = s->start
 30     8b/copy                         0/mod/indirect  1/rm32/ECX    .           .             .           1/r32/ECX   .               .                 # copy *ECX to ECX
 31     # if s is empty return false
 32     b8/copy-to-EAX  0/imm32/false
 33     39/compare                      3/mod/direct    1/rm32/ECX    .           .             .           2/r32/EDX   .               .                 # compare ECX with EDX
 34     7d/jump-if-greater-or-equal  $is-hex-int?:end/disp8
 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-byte?(*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-byte?/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 is-hex-byte?:  # c : byte -> bool/EAX
262     # . prolog
263     55/push-EBP
264     89/copy                         3/mod/direct    5/rm32/EBP    .           .             .           4/r32/ESP   .               .                 # copy ESP to EBP
265     # . save registers
266     51/push-ECX
267     # ECX = c
268     8b/copy                         1/mod/*+disp8   5/rm32/EBP    .           .             .           1/r32/ECX   8/disp8         .                 # copy *(EBP+8) to ECX
269     # return false if c < '0'
270     b8/copy-to-EAX  0/imm32/false
271     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x30/imm32        # compare ECX
272     7c/jump-if-lesser  $is-hex-byte?:end/disp8
273     # return false if c > 'f'
274     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x66/imm32        # compare ECX
275     7f/jump-if-greater  $is-hex-byte?:end/disp8
276     # return true if c <= '9'
277     b8/copy-to-EAX  1/imm32/true
278     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x39/imm32        # compare ECX
279     7e/jump-if-lesser-or-equal  $is-hex-byte?:end/disp8
280     # return true if c >= 'a'
281     81          7/subop/compare     3/mod/direct    1/rm32/ECX    .           .             .           .           .               0x61/imm32        # compare ECX
282     7d/jump-if-greater-or-equal  $is-hex-byte?:end/disp8
283     # otherwise return false
284     b8/copy-to-EAX  0/imm32/false
285 $is-hex-byte?:end:
286     # . restore registers
287     59/pop-to-ECX
288     # . epilog
289     89/copy                         3/mod/direct    4/rm32/ESP    .           .             .           5/r32/EBP   .               .                 # copy EBP to ESP
290     5d/pop-to-EBP
291     c3/return
292 
293 test-hex-below-0:
294     # EAX = is-hex-byte?(0x2f)
295     # . . push args
296     68/push  0x2f/imm32
297     # . . call
298     e8/call  is-hex-byte?/disp32
299     # . . discard args
300     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
301     # check-ints-equal(EAX, 0, msg)
302     # . . push args
303     68/push  "F - test-hex-below-0"/imm32
304     68/push  0/imm32/false
305     50/push-EAX
306     # . . call
307     e8/call  check-ints-equal/disp32
308     # . . discard args
309     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
310     c3/return
311 
312 test-hex-0-to-9:
313     # EAX = is-hex-byte?(0x30)
314     # . . push args
315     68/push  0x30/imm32
316     # . . call
317     e8/call  is-hex-byte?/disp32
318     # . . discard args
319     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
320     # check-ints-equal(EAX, 1, msg)
321     # . . push args
322     68/push  "F - test-hex-at-0"/imm32
323     68/push  1/imm32/true
324     50/push-EAX
325     # . . call
326     e8/call  check-ints-equal/disp32
327     # . . discard args
328     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
329     # EAX = is-hex-byte?(0x39)
330     # . . push args
331     68/push  0x39/imm32
332     # . . call
333     e8/call  is-hex-byte?/disp32
334     # . . discard args
335     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
336     # check-ints-equal(EAX, 1, msg)
337     # . . push args
338     68/push  "F - test-hex-at-9"/imm32
339     68/push  1/imm32/true
340     50/push-EAX
341     # . . call
342     e8/call  check-ints-equal/disp32
343     # . . discard args
344     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
345     c3/return
346 
347 test-hex-above-9-to-a:
348     # EAX = is-hex-byte?(0x3a)
349     # . . push args
350     68/push  0x3a/imm32
351     # . . call
352     e8/call  is-hex-byte?/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, 0, msg)
356     # . . push args
357     68/push  "F - test-hex-above-9-to-a"/imm32
358     68/push  0/imm32/false
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     c3/return
365 
366 test-hex-a-to-f:
367     # EAX = is-hex-byte?(0x61)
368     # . . push args
369     68/push  0x61/imm32
370     # . . call
371     e8/call  is-hex-byte?/disp32
372     # . . discard args
373     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
374     # check-ints-equal(EAX, 1, msg)
375     # . . push args
376     68/push  "F - test-hex-at-a"/imm32
377     68/push  1/imm32/true
378     50/push-EAX
379     # . . call
380     e8/call  check-ints-equal/disp32
381     # . . discard args
382     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
383     # EAX = is-hex-byte?(0x66)
384     # . . push args
385     68/push  0x66/imm32
386     # . . call
387     e8/call  is-hex-byte?/disp32
388     # . . discard args
389     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
390     # check-ints-equal(EAX, 1, msg)
391     # . . push args
392     68/push  "F - test-hex-at-f"/imm32
393     68/push  1/imm32/true
394     50/push-EAX
395     # . . call
396     e8/call  check-ints-equal/disp32
397     # . . discard args
398     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
399     c3/return
400 
401 test-hex-above-f:
402     # EAX = is-hex-byte?(0x67)
403     # . . push args
404     68/push  0x67/imm32
405     # . . call
406     e8/call  is-hex-byte?/disp32
407     # . . discard args
408     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               4/imm32           # add to ESP
409     # check-ints-equal(EAX, 0, msg)
410     # . . push args
411     68/push  "F - test-hex-above-f"/imm32
412     68/push  0/imm32/false
413     50/push-EAX
414     # . . call
415     e8/call  check-ints-equal/disp32
416     # . . discard args
417     81          0/subop/add         3/mod/direct    4/rm32/ESP    .           .             .           .           .               0xc/imm32         # add to ESP
418     c3/return
419 
420 parse-hex-digit:  # in/EAX : byte -> out/EAX : num
421     # no error checking; accepts argument in EAX
422     # if EAX <= '9' return EAX - '0'
423     3d/compare-EAX  0x39/imm32/9
424     7f/jump-if-greater  $parse-hex-digit:else/disp8
425     2d/subtract-from-EAX  0x30/imm32/0
426     c3/return
427 $parse-hex-digit:else:
428     # otherwise return EAX - 'a' + 10
429     2d/subtract-from-EAX  0x57/imm32/a-10
430     c3/return
431 
432 == data
433 
434 _test-slice-empty:
435   # nothing
436 _test-slice-empty-end:
437 
438 _test-slice-hex-int:
439   33/3 34/4
440 _test-slice-hex-int-end:
441 
442 _test-slice-hex-int-with-0x-prefix:
443   30/0 78/x 33/3 34/4
444 _test-slice-hex-int-with-0x-prefix-end:
445 
446 _test-slice-hex-int-letters:
447   33/3 34/4 61/a
448 _test-slice-hex-int-letters-end:
449 
450 _test-slice-char-and-digits:
451   71/q 33/3 34/4
452 _test-slice-char-and-digits-end:
453 
454 _test-slice-digits-and-char:
455   33/3 34/4 71/q
456 _test-slice-digits-and-char-end:
457 
458 # . . vim:nowrap:textwidth=0