https://github.com/akkartik/mu/blob/main/118parse-hex-int.subx
  1 # some utilities for converting numbers 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 hex-int?:  # in: (addr slice) -> result/eax: boolean
 10     # . prologue
 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     # var curr/ecx: (addr byte) = 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-addr>=  $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-!=  $hex-int?:initial-0/disp8
 33     # . ++curr
 34     41/increment-ecx
 35     # skip past leading '0x'
 36 $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-!=  $hex-int?:loop/disp8
 42     # . ++curr
 43     41/increment-ecx
 44 $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-addr>=  $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-!=  $hex-int?:loop/disp8
 53     # . ++curr
 54     41/increment-ecx
 55 $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-addr>=  $hex-int?:true/disp8
 59     # var eax: boolean = 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  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/false
 69     74/jump-if-=  $hex-int?:end/disp8
 70     # ++curr
 71     41/increment-ecx
 72     # loop
 73     eb/jump  $hex-int?:loop/disp8
 74 $hex-int?:true:
 75     # return true
 76     b8/copy-to-eax  1/imm32/true
 77 $hex-int?:end:
 78     # . restore registers
 79     5b/pop-to-ebx
 80     5a/pop-to-edx
 81     59/pop-to-ecx
 82     # . epilogue
 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-hex-int:
 88     # . prologue
 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: slice = {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 = hex-int?(slice)
101     # . . push args
102     51/push-ecx
103     # . . call
104     e8/call  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-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     # . epilogue
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-hex-int-handles-letters:
122     # . prologue
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: slice = {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 = hex-int?(slice)
135     # . . push args
136     51/push-ecx
137     # . . call
138     e8/call  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-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     # . epilogue
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-hex-int-with-trailing-char:
156     # . prologue
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: slice = {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 = hex-int?(slice)
169     # . . push args
170     51/push-ecx
171     # . . call
172     e8/call  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-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     # . epilogue
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-hex-int-with-leading-char:
190     # . prologue
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: slice = {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 = hex-int?(slice)
203     # . . push args
204     51/push-ecx
205     # . . call
206     e8/call  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-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     # . epilogue
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-hex-int-empty:
224     # . prologue
225     55/push-ebp
226     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
227     # var slice/ecx: slice = ""
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 = hex-int?(slice)
232     # . . push args
233     51/push-ecx
234     # . . call
235     e8/call  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-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     # . epilogue
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-hex-int-handles-0x-prefix:
253     # . prologue
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: slice = {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 = hex-int?(slice)
266     # . . push args
267     51/push-ecx
268     # . . call
269     e8/call  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-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     # . epilogue
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-hex-int-handles-negative:
287     # . prologue
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: slice = {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 = hex-int?(slice)
300     # . . push args
301     51/push-ecx
302     # . . call
303     e8/call  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-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     # . epilogue
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-hex-int-handles-negative-0x-prefix:
321     # . prologue
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: slice = {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 = hex-int?(slice)
334     # . . push args
335     51/push-ecx
336     # . . call
337     e8/call  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-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     # . epilogue
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: (addr array byte) -> result/eax: int
355     # . prologue
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     # eax = in
362     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           0/r32/eax   8/disp8         .                 # copy *(ebp+8) to eax
363     # var curr/ecx: (addr byte) = &in->data
364     8d/copy-address                 1/mod/*+disp8   0/rm32/eax    .           .             .           1/r32/ecx   4/disp8         .                 # copy eax+4 to ecx
365     # var max/edx: (addr byte) = &in->data[in->size]
366     # . edx = in->size
367     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           2/r32/edx   .               .                 # copy *eax to edx
368     # . edx = in->data + in->size
369     8d/copy-address                 1/mod/*+disp8   4/rm32/sib    0/base/eax  2/index/edx   .           2/r32/edx   4/disp8         .                 # copy eax+edx+4 to edx
370     # return parse-hex-int-helper(curr, max)
371     # . . push args
372     52/push-edx
373     51/push-ecx
374     # . . call
375     e8/call  parse-hex-int-helper/disp32
376     # . . discard args
377     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
378 $parse-hex-int:end:
379     # . restore registers
380     5a/pop-to-edx
381     59/pop-to-ecx
382     # . epilogue
383     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
384     5d/pop-to-ebp
385     c3/return
386 
387 parse-hex-int-from-slice:  # in: (addr slice) -> result/eax: int
388     # . prologue
389     55/push-ebp
390     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
391     # . save registers
392     51/push-ecx
393     52/push-edx
394     # ecx = in
395     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
396     # var max/edx: (addr byte) = in->end
397     8b/copy                         1/mod/*+disp8   1/rm32/ecx    .           .             .           2/r32/edx   4/disp8         .                 # copy *(ecx+4) to edx
398     # var curr/ecx: (addr byte) = in->start
399     8b/copy                         0/mod/indirect  1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # copy *ecx to ecx
400     # return parse-hex-int-helper(curr, max)
401     # . . push args
402     52/push-edx
403     51/push-ecx
404     # . . call
405     e8/call  parse-hex-int-helper/disp32
406     # . . discard args
407     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
408 $parse-hex-int-from-slice:end:
409     # . restore registers
410     5a/pop-to-edx
411     59/pop-to-ecx
412     # . epilogue
413     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
414     5d/pop-to-ebp
415     c3/return
416 
417 parse-hex-int-helper:  # start: (addr byte), end: (addr byte) -> result/eax: int
418     # . prologue
419     55/push-ebp
420     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
421     # . save registers
422     51/push-ecx
423     52/push-edx
424     53/push-ebx
425     56/push-esi
426     # var curr/ecx: (addr byte) = start
427     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
428     # edx = max
429     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0xc/disp8       .                 # copy *(ebp+12) to edx
430     # var result/ebx: int = 0
431     31/xor                          3/mod/direct    3/rm32/ebx    .           .             .           3/r32/ebx   .               .                 # clear ebx
432     # var negate?/esi: boolean = false
433     31/xor                          3/mod/direct    6/rm32/esi    .           .             .           6/r32/esi   .               .                 # clear esi
434 $parse-hex-int-helper:negative:
435     # if (*curr == '-') ++curr, negate = true
436     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
437     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
438     3d/compare-eax-and  0x2d/imm32/-
439     75/jump-if-!=  $parse-hex-int-helper:initial-0/disp8
440     # . ++curr
441     41/increment-ecx
442     # . negate = true
443     be/copy-to-esi  1/imm32/true
444 $parse-hex-int-helper:initial-0:
445     # skip past leading '0x'
446     # . if (*curr != '0') jump to loop
447     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
448     3d/compare-eax-and  0x30/imm32/0
449     75/jump-if-!=  $parse-hex-int-helper:loop/disp8
450     # . ++curr
451     41/increment-ecx
452 $parse-hex-int-helper:initial-0x:
453     # . if (curr >= in->end) return result
454     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
455     73/jump-if-addr>=  $parse-hex-int-helper:end/disp8
456     # . if (*curr != 'x') jump to loop  # the previous '0' is still valid so doesn't need to be checked again
457     31/xor                          3/mod/direct    0/rm32/eax    .           .             .           0/r32/eax   .               .                 # clear eax
458     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
459     3d/compare-eax-and  0x78/imm32/x
460     75/jump-if-!=  $parse-hex-int-helper:loop/disp8
461     # . ++curr
462     41/increment-ecx
463 $parse-hex-int-helper:loop:
464     # if (curr >= in->end) break
465     39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
466     73/jump-if-addr>=  $parse-hex-int-helper:negate/disp8
467     # var eax: int = from-hex-char(*curr)
468     # . . copy arg to eax
469     8a/copy-byte                    0/mod/indirect  1/rm32/ecx    .           .             .           0/r32/AL    .               .                 # copy byte at *ecx to AL
470     # . . call
471     e8/call  from-hex-char/disp32
472     # result = result * 16 + eax
473     c1/shift    4/subop/left        3/mod/direct    3/rm32/ebx    .           .             .           .           .               4/imm8            # shift ebx left by 4 bits
474     01/add                          3/mod/direct    3/rm32/ebx    .           .             .           0/r32/eax   .               .                 # add eax to ebx
475     # ++curr
476     41/increment-ecx
477     # loop
478     eb/jump  $parse-hex-int-helper:loop/disp8
479 $parse-hex-int-helper:negate:
480     # if (negate?) result = -result
481     81          7/subop/compare     3/mod/direct    6/rm32/esi    .           .             .           .           .               0/imm32/false     # compare esi
482     74/jump-if-=  $parse-hex-int-helper:end/disp8
483     f7          3/subop/negate      3/mod/direct    3/rm32/ebx    .           .             .           .           .               .                 # negate ebx
484 $parse-hex-int-helper:end:
485     # return result
486     89/copy                         3/mod/direct    0/rm32/eax    .           .             .           3/r32/ebx   .               .                 # copy ebx to eax
487     # . restore registers
488     5e/pop-to-esi
489     5b/pop-to-ebx
490     5a/pop-to-edx
491     59/pop-to-ecx
492     # . epilogue
493     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
494     5d/pop-to-ebp
495     c3/return
496 
497 test-parse-hex-int-from-slice-single-digit:
498     # . prologue
499     55/push-ebp
500     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
501     # (eax..ecx) = "a"
502     b8/copy-to-eax  "a"/imm32
503     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
504     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
505     05/add-to-eax  4/imm32
506     # var slice/ecx: slice = {eax, ecx}
507     51/push-ecx
508     50/push-eax
509     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
510     # eax = parse-hex-int-from-slice(slice)
511     # . . push args
512     51/push-ecx
513     # . . call
514     e8/call  parse-hex-int-from-slice/disp32
515     # . . discard args
516     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
517     # check-ints-equal(eax, 0xa, msg)
518     # . . push args
519     68/push  "F - test-parse-hex-int-from-slice-single-digit"/imm32
520     68/push  0xa/imm32
521     50/push-eax
522     # . . call
523     e8/call  check-ints-equal/disp32
524     # . . discard args
525     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
526     # . epilogue
527     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
528     5d/pop-to-ebp
529     c3/return
530 
531 test-parse-hex-int-from-slice-multi-digit:
532     # . prologue
533     55/push-ebp
534     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
535     # (eax..ecx) = "34a"
536     b8/copy-to-eax  "34a"/imm32
537     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
538     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
539     05/add-to-eax  4/imm32
540     # var slice/ecx: slice = {eax, ecx}
541     51/push-ecx
542     50/push-eax
543     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
544     # eax = parse-hex-int-from-slice(slice)
545     # . . push args
546     51/push-ecx
547     # . . call
548     e8/call  parse-hex-int-from-slice/disp32
549     # . . discard args
550     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
551     # check-ints-equal(eax, 0x34a, msg)
552     # . . push args
553     68/push  "F - test-parse-hex-int-from-slice-multi-digit"/imm32
554     68/push  0x34a/imm32
555     50/push-eax
556     # . . call
557     e8/call  check-ints-equal/disp32
558     # . . discard args
559     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
560     # . epilogue
561     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
562     5d/pop-to-ebp
563     c3/return
564 
565 test-parse-hex-int-from-slice-0x-prefix:
566     # . prologue
567     55/push-ebp
568     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
569     # (eax..ecx) = "0x34"
570     b8/copy-to-eax  "0x34"/imm32
571     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
572     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
573     05/add-to-eax  4/imm32
574     # var slice/ecx: slice = {eax, ecx}
575     51/push-ecx
576     50/push-eax
577     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
578     # eax = parse-hex-int-from-slice(slice)
579     # . . push args
580     51/push-ecx
581     # . . call
582     e8/call  parse-hex-int-from-slice/disp32
583     # . . discard args
584     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
585     # check-ints-equal(eax, 0x34, msg)
586     # . . push args
587     68/push  "F - test-parse-hex-int-from-slice-0x-prefix"/imm32
588     68/push  0x34/imm32
589     50/push-eax
590     # . . call
591     e8/call  check-ints-equal/disp32
592     # . . discard args
593     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
594     # . epilogue
595     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
596     5d/pop-to-ebp
597     c3/return
598 
599 test-parse-hex-int-from-slice-zero:
600     # . prologue
601     55/push-ebp
602     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
603     # (eax..ecx) = "0"
604     b8/copy-to-eax  "0"/imm32
605     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
606     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
607     05/add-to-eax  4/imm32
608     # var slice/ecx: slice = {eax, ecx}
609     51/push-ecx
610     50/push-eax
611     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
612     # eax = parse-hex-int-from-slice(slice)
613     # . . push args
614     51/push-ecx
615     # . . call
616     e8/call  parse-hex-int-from-slice/disp32
617     # . . discard args
618     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
619     # check-ints-equal(eax, 0, msg)
620     # . . push args
621     68/push  "F - test-parse-hex-int-from-slice-zero"/imm32
622     68/push  0/imm32
623     50/push-eax
624     # . . call
625     e8/call  check-ints-equal/disp32
626     # . . discard args
627     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
628     # . epilogue
629     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
630     5d/pop-to-ebp
631     c3/return
632 
633 test-parse-hex-int-from-slice-0-prefix:
634     # . prologue
635     55/push-ebp
636     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
637     # (eax..ecx) = "03"
638     b8/copy-to-eax  "03"/imm32
639     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
640     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
641     05/add-to-eax  4/imm32
642     # var slice/ecx: slice = {eax, ecx}
643     51/push-ecx
644     50/push-eax
645     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
646     # eax = parse-hex-int-from-slice(slice)
647     # . . push args
648     51/push-ecx
649     # . . call
650     e8/call  parse-hex-int-from-slice/disp32
651     # . . discard args
652     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
653     # check-ints-equal(eax, 0x3, msg)
654     # . . push args
655     68/push  "F - test-parse-hex-int-from-slice-0-prefix"/imm32
656     68/push  0x3/imm32
657     50/push-eax
658     # . . call
659     e8/call  check-ints-equal/disp32
660     # . . discard args
661     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
662     # . epilogue
663     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
664     5d/pop-to-ebp
665     c3/return
666 
667 test-parse-hex-int-from-slice-negative:
668     # . prologue
669     55/push-ebp
670     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
671     # (eax..ecx) = "-03"
672     b8/copy-to-eax  "-03"/imm32
673     8b/copy                         0/mod/indirect  0/rm32/eax    .           .             .           1/r32/ecx   .               .                 # copy *eax to ecx
674     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
675     05/add-to-eax  4/imm32
676     # var slice/ecx: slice = {eax, ecx}
677     51/push-ecx
678     50/push-eax
679     89/copy                         3/mod/direct    1/rm32/ecx    .           .             .           4/r32/esp   .               .                 # copy esp to ecx
680     # eax = parse-hex-int-from-slice(slice)
681     # . . push args
682     51/push-ecx
683     # . . call
684     e8/call  parse-hex-int-from-slice/disp32
685     # . . discard args
686     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
687     # check-ints-equal(eax, -3, msg)
688     # . . push args
689     68/push  "F - test-parse-hex-int-from-slice-negative"/imm32
690     68/push  -3/imm32
691     50/push-eax
692     # . . call
693     e8/call  check-ints-equal/disp32
694     # . . discard args
695     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
696     # . epilogue
697     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
698     5d/pop-to-ebp
699     c3/return
700 
701 hex-digit?:  # c: byte -> result/eax: boolean
702     # . prologue
703     55/push-ebp
704     89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
705     # . save registers
706     51/push-ecx
707     # ecx = c
708     8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           1/r32/ecx   8/disp8         .                 # copy *(ebp+8) to ecx
709     # return false if c < '0'
710     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x30/imm32        # compare ecx
711     7c/jump-if-<  $hex-digit?:false/disp8
712     # return true if c <= '9'
713     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x39/imm32        # compare ecx
714     7e/jump-if-<=  $hex-digit?:true/disp8
715     # drop case
716     25/and-eax-with 0x5f/imm32
717     # return false if c > 'f'
718     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x66/imm32        # compare ecx
719     7f/jump-if->  $hex-digit?:false/disp8
720     # return true if c >= 'a'
721     81          7/subop/compare     3/mod/direct    1/rm32/ecx    .           .             .           .           .               0x61/imm32        # compare ecx
722     7d/jump-if->=  $hex-digit?:true/disp8
723     # otherwise return false
724 $hex-digit?:false:
725     b8/copy-to-eax  0/imm32/false
726     eb/jump $hex-digit?:end/disp8
727 $hex-digit?:true:
728     b8/copy-to-eax  1/imm32/true
729 $hex-digit?:end:
730     # . restore registers
731     59/pop-to-ecx
732     # . epilogue
733     89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
734     5d/pop-to-ebp
735     c3/return
736 
737 test-hex-below-0:
738     # eax = hex-digit?(0x2f)
739     # . . push args
740     68/push  0x2f/imm32
741     # . . call
742     e8/call  hex-digit?/disp32
743     # . . discard args
744     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
745     # check-ints-equal(eax, 0, msg)
746     # . . push args
747     68/push  "F - test-hex-below-0"/imm32
748     68/push  0/imm32/false
749     50/push-eax
750     # . . call
751     e8/call  check-ints-equal/disp32
752     # . . discard args
753     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
754     c3/return
755 
756 test-hex-0-to-9:
757     # eax = hex-digit?(0x30)
758     # . . push args
759     68/push  0x30/imm32
760     # . . call
761     e8/call  hex-digit?/disp32
762     # . . discard args
763     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
764     # check-ints-equal(eax, 1, msg)
765     # . . push args
766     68/push  "F - test-hex-at-0"/imm32
767     68/push  1/imm32/true
768     50/push-eax
769     # . . call
770     e8/call  check-ints-equal/disp32
771     # . . discard args
772     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
773     # eax = hex-digit?(0x39)
774     # . . push args
775     68/push  0x39/imm32
776     # . . call
777     e8/call  hex-digit?/disp32
778     # . . discard args
779     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
780     # check-ints-equal(eax, 1, msg)
781     # . . push args
782     68/push  "F - test-hex-at-9"/imm32
783     68/push  1/imm32/true
784     50/push-eax
785     # . . call
786     e8/call  check-ints-equal/disp32
787     # . . discard args
788     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
789     c3/return
790 
791 test-hex-above-9-to-a:
792     # eax = hex-digit?(0x3a)
793     # . . push args
794     68/push  0x3a/imm32
795     # . . call
796     e8/call  hex-digit?/disp32
797     # . . discard args
798     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
799     # check-ints-equal(eax, 0, msg)
800     # . . push args
801     68/push  "F - test-hex-above-9-to-a"/imm32
802     68/push  0/imm32/false
803     50/push-eax
804     # . . call
805     e8/call  check-ints-equal/disp32
806     # . . discard args
807     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
808     c3/return
809 
810 test-hex-a-to-f:
811     # eax = hex-digit?(0x61)
812     # . . push args
813     68/push  0x61/imm32
814     # . . call
815     e8/call  hex-digit?/disp32
816     # . . discard args
817     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
818     # check-ints-equal(eax, 1, msg)
819     # . . push args
820     68/push  "F - test-hex-at-a"/imm32
821     68/push  1/imm32/true
822     50/push-eax
823     # . . call
824     e8/call  check-ints-equal/disp32
825     # . . discard args
826     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
827     # eax = hex-digit?(0x66)
828     # . . push args
829     68/push  0x66/imm32
830     # . . call
831     e8/call  hex-digit?/disp32
832     # . . discard args
833     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
834     # check-ints-equal(eax, 1, msg)
835     # . . push args
836     68/push  "F - test-hex-at-f"/imm32
837     68/push  1/imm32/true
838     50/push-eax
839     # . . call
840     e8/call  check-ints-equal/disp32
841     # . . discard args
842     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
843     c3/return
844 
845 test-hex-above-f:
846     # eax = hex-digit?(0x67)
847     # . . push args
848     68/push  0x67/imm32
849     # . . call
850     e8/call  hex-digit?/disp32
851     # . . discard args
852     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
853     # check-ints-equal(eax, 0, msg)
854     # . . push args
855     68/push  "F - test-hex-above-f"/imm32
856     68/push  0/imm32/false
857     50/push-eax
858     # . . call
859     e8/call  check-ints-equal/disp32
860     # . . discard args
861     81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
862     c3/return
863 
864 from-hex-char:  # in/eax: byte -> out/eax: nibble
865 $from-hex-char:check0:
866     # if (eax < '0') goto abort
867     3d/compare-eax-with  0x30/imm32/0
868     7c/jump-if-<  $from-hex-char:abort/disp8
869 $from-hex-char:check1:
870     # if (eax > 'f') goto abort
871     3d/compare-eax-with  0x66/imm32/f
872     7f/jump-if->  $from-hex-char:abort/disp8
873 $from-hex-char:check2:
874     # if (eax > '9') goto next check
875     3d/compare-eax-with  0x39/imm32/9
876     7f/jump-if->  $from-hex-char:check3/disp8
877 $from-hex-char:digit:
878     # return eax - '0'
879     2d/subtract-from-eax  0x30/imm32/0
880     c3/return
881 $from-hex-char:check3:
882     # if (eax < 'a') goto abort
883     3d/compare-eax-with  0x61/imm32/a
884     7c/jump-if-<  $from-hex-char:abort/disp8
885 $from-hex-char:letter:
886     # return eax - ('a'-10)
887     2d/subtract-from-eax  0x57/imm32/a-10
888     c3/return
889 
890 $from-hex-char:abort:
891     (abort "invalid hex char")
892     # never gets here
893 
894 # . . vim:nowrap:textwidth=0