https://github.com/akkartik/mu/blob/master/subx/065hex.subx

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