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     (draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0 "invalid hex char" 3 0)  # 3=cyan
    892     {
    893       eb/jump loop/disp8
    894     }
    895     # never gets here
    896
    897 # . . vim:nowrap:textwidth=0