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