https://github.com/akkartik/mu/blob/main/311decimal-int.subx

      1 # Helpers for decimal ints.
      2
      3 # if slice doesn't contain a decimal number, return 0
      4 parse-decimal-int-from-slice:  # in: (addr slice) -> out/eax: int
      5     # . prologue
      6     55/push-ebp
      7     89/<- %ebp 4/r32/esp
      8     # . save registers
      9     51/push-ecx
     10     # ecx = in
     11     8b/-> *(ebp+8) 1/r32/ecx
     12     #
     13     (parse-decimal-int-helper *ecx *(ecx+4))  # => eax
     14 $parse-decimal-int-from-slice:end:
     15     # . restore registers
     16     59/pop-to-ecx
     17     # . epilogue
     18     89/<- %esp 5/r32/ebp
     19     5d/pop-to-ebp
     20     c3/return
     21
     22 # if slice doesn't contain a decimal number, return 0
     23 parse-decimal-int:  # in: (addr array byte) -> result/eax: int
     24     # . prologue
     25     55/push-ebp
     26     89/<- %ebp 4/r32/esp
     27     # . save registers
     28     51/push-ecx
     29     52/push-edx
     30     # eax = in
     31     8b/-> *(ebp+8) 0/r32/eax
     32     # var start/ecx: (addr byte) = &in->data
     33     8d/copy-address *(eax+4) 1/r32/ecx
     34     # var end/edx: (addr byte) = &in->data[in->size]
     35     8b/-> *eax 2/r32/edx
     36     8d/copy-address *(eax+edx+4) 2/r32/edx
     37     #
     38     (parse-decimal-int-helper %ecx %edx)  # => eax
     39 $parse-decimal-int:end:
     40     # . restore registers
     41     5a/pop-to-edx
     42     59/pop-to-ecx
     43     # . epilogue
     44     89/<- %esp 5/r32/ebp
     45     5d/pop-to-ebp
     46     c3/return
     47
     48 parse-decimal-int-from-stream:  # in: (addr stream byte) -> result/eax: int
     49     # . prologue
     50     55/push-ebp
     51     89/<- %ebp 4/r32/esp
     52     # . save registers
     53     51/push-ecx
     54     52/push-edx
     55     # eax = in
     56     8b/-> *(ebp+8) 0/r32/eax
     57     # var start/ecx: (addr byte) = &in->data[in->read]
     58     8b/-> *(eax+4) 1/r32/ecx
     59     8d/copy-address *(eax+ecx+0xc) 1/r32/ecx
     60     # var end/edx: (addr byte) = &in->data[in->write]
     61     8b/-> *eax 2/r32/edx
     62     8d/copy-address *(eax+edx+0xc) 2/r32/edx
     63     # trim a trailing newline
     64     {
     65       # speculatively trim
     66       4a/decrement-edx
     67       # if it's a newline, break
     68       8a/byte-> *edx 0/r32/eax
     69       81 4/subop/and %eax 0xff/imm32
     70       3d/compare-eax-and 0xa/imm32/newline
     71       74/jump-if-= break/disp8
     72       # not a newline, so restore it
     73       42/increment-edx
     74     }
     75     #
     76     (parse-decimal-int-helper %ecx %edx)  # => eax
     77 $parse-decimal-int-from-stream:end:
     78     # . restore registers
     79     5a/pop-to-edx
     80     59/pop-to-ecx
     81     # . epilogue
     82     89/<- %esp 5/r32/ebp
     83     5d/pop-to-ebp
     84     c3/return
     85
     86 parse-decimal-int-helper:  # start: (addr byte), end: (addr byte) -> result/eax: int
     87     # . prologue
     88     55/push-ebp
     89     89/<- %ebp 4/r32/esp
     90     # . save registers
     91     51/push-ecx
     92     52/push-edx
     93     53/push-ebx
     94     56/push-esi
     95     57/push-edi
     96     # var curr/esi: (addr byte) = start
     97     8b/-> *(ebp+8) 6/r32/esi
     98     # edi = end
     99     8b/-> *(ebp+0xc) 7/r32/edi
    100     # var negate?/edx: boolean = false
    101     ba/copy-to-edx 0/imm32/false
    102     # if (*curr == '-') ++curr, negate = true
    103     {
    104 $parse-decimal-int-helper:negative:
    105       b8/copy-to-eax 0/imm32
    106       8a/copy-byte *esi 0/r32/AL
    107       3d/compare-eax-and 0x2d/imm32/-
    108       75/jump-if-!= break/disp8
    109       # . ++curr
    110       46/increment-esi
    111       # . negate = true
    112       ba/copy-to-edx  1/imm32/true
    113     }
    114     # spill negate?
    115     52/push-edx
    116     # var result/eax: int = 0
    117     b8/copy-to-eax 0/imm32
    118     # var digit/ecx: int = 0
    119     b9/copy-to-ecx 0/imm32
    120     # const TEN/ebx: int = 10
    121     bb/copy-to-ebx 0xa/imm32
    122     {
    123 $parse-decimal-int-helper:loop:
    124       # if (curr >= in->end) break
    125       39/compare %esi 7/r32/edi
    126       73/jump-if-addr>= break/disp8
    127       # if !decimal-digit?(*curr) return 0
    128       8a/copy-byte *esi 1/r32/CL
    129       50/push-eax
    130       (decimal-digit? %ecx)  # => eax
    131       {
    132         3d/compare-eax-and 0/imm32/false
    133         75/jump-if-!= break/disp8
    134         58/pop-to-eax
    135         b8/copy-to-eax 0/imm32
    136         eb/jump $parse-decimal-int-helper:negate/disp8
    137       }
    138       58/pop-to-eax
    139       # digit = from-decimal-char(*curr)
    140       81 5/subop/subtract %ecx 0x30/imm32/zero
    141       # TODO: error checking
    142       # result = result * 10 + digit
    143       ba/copy-to-edx 0/imm32
    144       f7 4/subop/multiply-into-edx-eax %ebx
    145       # TODO: check edx for overflow
    146       01/add %eax 1/r32/ecx
    147       # ++curr
    148       46/increment-esi
    149       #
    150       eb/jump loop/disp8
    151     }
    152 $parse-decimal-int-helper:negate:
    153     # if (negate?) result = -result
    154     5a/pop-to-edx
    155     {
    156       81 7/subop/compare %edx 0/imm32/false
    157       74/jump-if-= break/disp8
    158       f7 3/subop/negate %eax
    159     }
    160 $parse-decimal-int-helper:end:
    161     # . restore registers
    162     5f/pop-to-edi
    163     5e/pop-to-esi
    164     5b/pop-to-ebx
    165     5a/pop-to-edx
    166     59/pop-to-ecx
    167     # . epilogue
    168     89/<- %esp 5/r32/ebp
    169     5d/pop-to-ebp
    170     c3/return
    171
    172 test-parse-decimal-int-from-slice-single-digit:
    173     # . prologue
    174     55/push-ebp
    175     89/<- %ebp 4/r32/esp
    176     # . save registers
    177     50/push-eax
    178     51/push-ecx
    179     # (eax..ecx) = "3"
    180     b8/copy-to-eax "3"/imm32
    181     8b/-> *eax 1/r32/ecx
    182     8d/copy-address *(eax+ecx+4) 1/r32/ecx
    183     05/add-to-eax 4/imm32
    184     # var slice/ecx: slice = {eax, ecx}
    185     51/push-ecx
    186     50/push-eax
    187     89/<- %ecx 4/r32/esp
    188     #
    189     (parse-decimal-int-from-slice %ecx)  # => eax
    190     (check-ints-equal %eax 3 "F - test-parse-decimal-int-from-slice-single-digit")
    191 $test-parse-decimal-int-helper-single-digit:end:
    192     # . restore registers
    193     59/pop-to-ecx
    194     58/pop-to-eax
    195     # . epilogue
    196     89/<- %esp 5/r32/ebp
    197     5d/pop-to-ebp
    198     c3/return
    199
    200 test-parse-decimal-int-from-slice-multi-digit:
    201     # . prologue
    202     55/push-ebp
    203     89/<- %ebp 4/r32/esp
    204     # . save registers
    205     50/push-eax
    206     51/push-ecx
    207     # (eax..ecx) = "34"
    208     b8/copy-to-eax "34"/imm32
    209     8b/-> *eax 1/r32/ecx
    210     8d/copy-address *(eax+ecx+4) 1/r32/ecx
    211     05/add-to-eax 4/imm32
    212     # var slice/ecx: slice = {eax, ecx}
    213     51/push-ecx
    214     50/push-eax
    215     89/<- %ecx 4/r32/esp
    216     #
    217     (parse-decimal-int-from-slice %ecx)  # => eax
    218     (check-ints-equal %eax 0x22 "F - test-parse-decimal-int-from-slice-multi-digit")  # 34 in hex
    219 $test-parse-decimal-int-helper-multi-digit:end:
    220     # . restore registers
    221     59/pop-to-ecx
    222     58/pop-to-eax
    223     # . epilogue
    224     89/<- %esp 5/r32/ebp
    225     5d/pop-to-ebp
    226     c3/return
    227
    228 test-parse-decimal-int-from-slice-zero:
    229     # . prologue
    230     55/push-ebp
    231     89/<- %ebp 4/r32/esp
    232     # . save registers
    233     50/push-eax
    234     51/push-ecx
    235     # (eax..ecx) = "00"
    236     b8/copy-to-eax "00"/imm32
    237     8b/-> *eax 1/r32/ecx
    238     8d/copy-address *(eax+ecx+4) 1/r32/ecx
    239     05/add-to-eax 4/imm32
    240     # var slice/ecx: slice = {eax, ecx}
    241     51/push-ecx
    242     50/push-eax
    243     89/<- %ecx 4/r32/esp
    244     #
    245     (parse-decimal-int-from-slice %ecx)  # => eax
    246     (check-ints-equal %eax 0 "F - test-parse-decimal-int-from-slice-zero")
    247 $test-parse-decimal-int-helper-zero:end:
    248     # . restore registers
    249     59/pop-to-ecx
    250     58/pop-to-eax
    251     # . epilogue
    252     89/<- %esp 5/r32/ebp
    253     5d/pop-to-ebp
    254     c3/return
    255
    256 test-parse-decimal-int-from-slice-negative:
    257     # . prologue
    258     55/push-ebp
    259     89/<- %ebp 4/r32/esp
    260     # . save registers
    261     50/push-eax
    262     51/push-ecx
    263     # (eax..ecx) = "-3"
    264     b8/copy-to-eax "-3"/imm32
    265     8b/-> *eax 1/r32/ecx
    266     8d/copy-address *(eax+ecx+4) 1/r32/ecx
    267     05/add-to-eax 4/imm32
    268     # var slice/ecx: slice = {eax, ecx}
    269     51/push-ecx
    270     50/push-eax
    271     89/<- %ecx 4/r32/esp
    272     #
    273     (parse-decimal-int-from-slice %ecx)  # => eax
    274     (check-ints-equal %eax -3 "F - test-parse-decimal-int-from-slice-negative")
    275 $test-parse-decimal-int-helper-negative:end:
    276     # . restore registers
    277     59/pop-to-ecx
    278     58/pop-to-eax
    279     # . epilogue
    280     89/<- %esp 5/r32/ebp
    281     5d/pop-to-ebp
    282     c3/return
    283
    284 test-parse-decimal-int-from-slice-multi-digit-negative:
    285     # . prologue
    286     55/push-ebp
    287     89/<- %ebp 4/r32/esp
    288     # . save registers
    289     50/push-eax
    290     51/push-ecx
    291     # (eax..ecx) = "-32"
    292     b8/copy-to-eax "-32"/imm32
    293     8b/-> *eax 1/r32/ecx
    294     8d/copy-address *(eax+ecx+4) 1/r32/ecx
    295     05/add-to-eax 4/imm32
    296     # var slice/ecx: slice = {eax, ecx}
    297     51/push-ecx
    298     50/push-eax
    299     89/<- %ecx 4/r32/esp
    300     #
    301     (parse-decimal-int-from-slice %ecx)  # => eax
    302     (check-ints-equal %eax -0x20 "F - test-parse-decimal-int-from-slice-multi-digit-negative")  # -32 in hex
    303 $test-parse-decimal-int-helper-multi-digit-negative:end:
    304     # . restore registers
    305     59/pop-to-ecx
    306     58/pop-to-eax
    307     # . epilogue
    308     89/<- %esp 5/r32/ebp
    309     5d/pop-to-ebp
    310     c3/return
    311
    312 decimal-size:  # n: int -> result/eax: int
    313     # pseudocode:
    314     #   edi = 0
    315     #   eax = n
    316     #   if eax < 0
    317     #     ++edi  # for '-'
    318     #     negate eax
    319     #   while true
    320     #     edx = 0
    321     #     eax, edx = eax/10, eax%10
    322     #     ++edi
    323     #     if (eax == 0) break
    324     #   eax = edi
    325     #
    326     # . prologue
    327     55/push-ebp
    328     89/<- %ebp 4/r32/esp
    329     # . save registers
    330     51/push-ecx
    331     52/push-edx
    332     57/push-edi
    333     # edi = 0
    334     bf/copy-to-edi 0/imm32
    335     # eax = n
    336     8b/-> *(ebp+8) 0/r32/eax
    337     # if (n < 0) negate n, increment edi
    338     {
    339       3d/compare-eax-with 0/imm32
    340       7d/jump-if->= break/disp8
    341       f7 3/subop/negate %eax
    342       47/increment-edi
    343     }
    344     # const ten/ecx = 10
    345     b9/copy-to-ecx  0xa/imm32
    346     {
    347       ba/copy-to-edx 0/imm32
    348       f7 7/subop/idiv-edx-eax-by %ecx  # eax = edx:eax/10; edx = edx:eax%10
    349       47/increment-edi
    350       3d/compare-eax-and 0/imm32
    351       75/jump-if-!= loop/disp8
    352     }
    353 $decimal-size:end:
    354     89/<- %eax 7/r32/edi
    355     # . restore registers
    356     5f/pop-to-edi
    357     5a/pop-to-edx
    358     59/pop-to-ecx
    359     # . epilogue
    360     89/<- %esp 5/r32/ebp
    361     5d/pop-to-ebp
    362     c3/return
    363
    364 test-decimal-size-of-zero:
    365     # . prologue
    366     55/push-ebp
    367     89/<- %ebp 4/r32/esp
    368     #
    369     (decimal-size 0)  # => eax
    370     (check-ints-equal %eax 1 "F - test-decimal-size-of-zero")
    371 $test-decimal-size-of-zero:end:
    372     # . epilogue
    373     89/<- %esp 5/r32/ebp
    374     5d/pop-to-ebp
    375     c3/return
    376
    377 test-decimal-size-single-digit:
    378     # . prologue
    379     55/push-ebp
    380     89/<- %ebp 4/r32/esp
    381     #
    382     (decimal-size 4)  # => eax
    383     (check-ints-equal %eax 1 "F - test-decimal-size-single-digit")
    384 $test-decimal-size-single-digit:end:
    385     # . epilogue
    386     89/<- %esp 5/r32/ebp
    387     5d/pop-to-ebp
    388     c3/return
    389
    390 test-decimal-size-multi-digit:
    391     # . prologue
    392     55/push-ebp
    393     89/<- %ebp 4/r32/esp
    394     #
    395     (decimal-size 0xa)  # => eax
    396     (check-ints-equal %eax 2 "F - test-decimal-size-multi-digit")
    397 $test-decimal-size-multi-digit:end:
    398     # . epilogue
    399     89/<- %esp 5/r32/ebp
    400     5d/pop-to-ebp
    401     c3/return
    402
    403 test-decimal-size-single-digit-negative:
    404     # . prologue
    405     55/push-ebp
    406     89/<- %ebp 4/r32/esp
    407     #
    408     (decimal-size -4)  # => eax
    409     (check-ints-equal %eax 2 "F - test-decimal-size-single-digit-negative")
    410 $test-decimal-size-single-digit-negative:end:
    411     # . epilogue
    412     89/<- %esp 5/r32/ebp
    413     5d/pop-to-ebp
    414     c3/return
    415
    416 test-decimal-size-multi-digit-negative:
    417     # . prologue
    418     55/push-ebp
    419     89/<- %ebp 4/r32/esp
    420     #
    421     (decimal-size -0xa)  # => eax
    422     (check-ints-equal %eax 3 "F - test-decimal-size-multi-digit-negative")
    423 $test-decimal-size-multi-digit-negative:end:
    424     # . epilogue
    425     89/<- %esp 5/r32/ebp
    426     5d/pop-to-ebp
    427     c3/return
    428
    429 _parse-array-of-decimal-ints:  # ad: (addr allocation-descriptor), s: (addr array byte), out: (addr handle array int)
    430     # pseudocode
    431     #   end = &s->data[s->size]
    432     #   curr = s->data
    433     #   size = 0
    434     #   while true
    435     #     if (curr >= end) break
    436     #     curr = skip-chars-matching-in-slice(curr, end, ' ')
    437     #     if (curr >= end) break
    438     #     curr = skip-chars-not-matching-in-slice(curr, end, ' ')
    439     #     ++size
    440     #   allocate-array(ad, size*4, out)
    441     #   var slice: slice = {s->data, 0}
    442     #   curr = lookup(out)->data
    443     #   while true
    444     #     if (slice->start >= end) break
    445     #     slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
    446     #     if (slice->start >= end) break
    447     #     slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
    448     #     *curr = parse-hex-int-from-slice(slice)
    449     #     curr += 4
    450     #     slice->start = slice->end
    451     #   return result
    452     #
    453     # . prologue
    454     55/push-ebp
    455     89/<- %ebp 4/r32/esp
    456     # . save registers
    457     50/push-eax
    458     51/push-ecx
    459     52/push-edx
    460     53/push-ebx
    461     56/push-esi
    462     57/push-edi
    463     # esi = s
    464     8b/-> *(ebp+0xc) 6/r32/esi
    465     # var curr/ecx: (addr byte) = s->data
    466     8d/copy-address *(esi+4) 1/r32/ecx
    467     # var end/edx: (addr byte) = &s->data[s->size]
    468     # . edx = s->size
    469     8b/-> *esi 2/r32/edx
    470     # . edx += curr
    471     01/add-to %edx 1/r32/ecx
    472     # var size/ebx: int = 0
    473     31/xor-with %ebx 3/r32/ebx
    474 $_parse-array-of-decimal-ints:loop1:
    475     # if (curr >= end) break
    476     39/compare %ecx 2/r32/edx
    477     73/jump-if-addr>= $_parse-array-of-decimal-ints:break1/disp8
    478     # curr = skip-chars-matching-in-slice(curr, end, ' ')
    479     (skip-chars-matching-in-slice %ecx %edx 0x20)  # => eax
    480     89/<- %ecx 0/r32/eax
    481     # if (curr >= end) break
    482     39/compare %ecx 2/r32/edx
    483     73/jump-if-addr>= $_parse-array-of-decimal-ints:break1/disp8
    484     # curr = skip-chars-not-matching-in-slice(curr, end, ' ')
    485     (skip-chars-not-matching-in-slice %ecx %edx 0x20)  # => eax
    486     89/<- %ecx 0/r32/eax
    487     # size += 4
    488     81 0/subop/add %ebx 4/imm32
    489     eb/jump $_parse-array-of-decimal-ints:loop1/disp8
    490 $_parse-array-of-decimal-ints:break1:
    491     (allocate-array *(ebp+8) %ebx *(ebp+0x10))
    492 $_parse-array-of-decimal-ints:pass2:
    493     # var slice/edi: slice = {s->data, 0}
    494     68/push 0/imm32/end
    495     8d/copy-address *(esi+4) 7/r32/edi
    496     57/push-edi
    497     89/<- %edi 4/r32/esp
    498     # curr = lookup(out)->data
    499     8b/-> *(ebp+0x10) 0/r32/eax
    500     (lookup *eax *(eax+4))  # => eax
    501     8d/copy-address *(eax+4) 1/r32/ecx
    502 $_parse-array-of-decimal-ints:loop2:
    503     # if (slice->start >= end) break
    504     39/compare *edi 2/r32/edx
    505     73/jump-if-addr>= $_parse-array-of-decimal-ints:end/disp8
    506     # slice->start = skip-chars-matching-in-slice(slice->start, end, ' ')
    507     (skip-chars-matching-in-slice *edi %edx 0x20)  # => eax
    508     89/<- *edi 0/r32/eax
    509     # if (slice->start >= end) break
    510     39/compare *edi 2/r32/edx
    511     73/jump-if-addr>= $_parse-array-of-decimal-ints:end/disp8
    512     # slice->end = skip-chars-not-matching-in-slice(slice->start, end, ' ')
    513     (skip-chars-not-matching-in-slice *edi %edx 0x20)  # => eax
    514     89/<- *(edi+4) 0/r32/eax
    515     # *curr = parse-hex-int-from-slice(slice)
    516     (parse-decimal-int-from-slice %edi)
    517     89/<- *ecx 0/r32/eax
    518     # curr += 4
    519     81 0/subop/add %ecx 4/imm32
    520     # slice->start = slice->end
    521     8b/-> *(edi+4) 0/r32/eax
    522     89/<- *edi 0/r32/eax
    523     eb/jump $_parse-array-of-decimal-ints:loop2/disp8
    524 $_parse-array-of-decimal-ints:end:
    525     # . reclaim locals
    526     81 0/subop/add %esp 8/imm32
    527     # . restore registers
    528     5f/pop-to-edi
    529     5e/pop-to-esi
    530     5b/pop-to-ebx
    531     5a/pop-to-edx
    532     59/pop-to-ecx
    533     58/pop-to-eax
    534     # . epilogue
    535     89/<- %esp 5/r32/ebp
    536     5d/pop-to-ebp
    537     c3/return
    538
    539 test-parse-array-of-decimal-ints:
    540     # . prologue
    541     55/push-ebp
    542     89/<- %ebp 4/r32/esp
    543     # var h/esi: (handle array int)
    544     68/push 0/imm32
    545     68/push 0/imm32
    546     89/<- %esi 4/r32/esp
    547     # var ecx: (array int) = [1, 2, 3]
    548     68/push 3/imm32
    549     68/push 2/imm32
    550     68/push 1/imm32
    551     68/push 0xc/imm32/size
    552     89/<- %ecx 4/r32/esp
    553     #
    554     (_parse-array-of-decimal-ints Heap "1 2 3" %esi)
    555     (lookup *esi *(esi+4))  # => eax
    556     (array-equal? %ecx %eax)  # => eax
    557     (check-ints-equal %eax 1 "F - test-parse-array-of-decimal-ints")
    558     # . epilogue
    559     89/<- %esp 5/r32/ebp
    560     5d/pop-to-ebp
    561     c3/return
    562
    563 test-parse-array-of-decimal-ints-empty:
    564     # - empty string = empty array
    565     # . prologue
    566     55/push-ebp
    567     89/<- %ebp 4/r32/esp
    568     # var h/esi: handle
    569     68/push 0/imm32
    570     68/push 0/imm32
    571     89/<- %esi 4/r32/esp
    572     #
    573     (_parse-array-of-decimal-ints Heap "" %esi)
    574     (lookup *esi *(esi+4))  # => eax
    575     (check-ints-equal *eax 0 "F - test-parse-array-of-decimal-ints-empty")
    576     # . epilogue
    577     89/<- %esp 5/r32/ebp
    578     5d/pop-to-ebp
    579     c3/return
    580
    581 test-parse-array-of-decimal-ints-just-whitespace:
    582     # - just whitespace = empty array
    583     # . prologue
    584     55/push-ebp
    585     89/<- %ebp 4/r32/esp
    586     # var h/esi: handle
    587     68/push 0/imm32
    588     68/push 0/imm32
    589     89/<- %esi 4/r32/esp
    590     #
    591     (_parse-array-of-decimal-ints Heap Space %esi)
    592     (lookup *esi *(esi+4))  # => eax
    593     (check-ints-equal *eax 0 "F - test-parse-array-of-decimal-ints-just-whitespace")
    594     # . epilogue
    595     89/<- %esp 5/r32/ebp
    596     5d/pop-to-ebp
    597     c3/return
    598
    599 test-parse-array-of-decimal-ints-extra-whitespace:
    600     # . prologue
    601     55/push-ebp
    602     89/<- %ebp 4/r32/esp
    603     # var h/esi: handle
    604     68/push 0/imm32
    605     68/push 0/imm32
    606     89/<- %esi 4/r32/esp
    607     # var ecx: (array int) = [1, 2, 3]
    608     68/push 3/imm32
    609     68/push 2/imm32
    610     68/push 1/imm32
    611     68/push 0xc/imm32/size
    612     89/<- %ecx 4/r32/esp
    613     #
    614     (_parse-array-of-decimal-ints Heap " 1 2  3  " %esi)
    615     (lookup *esi *(esi+4))  # => eax
    616     (array-equal? %ecx %eax)  # => eax
    617     (check-ints-equal %eax 1 "F - test-parse-array-of-decimal-ints-extra-whitespace")
    618     # . epilogue
    619     89/<- %esp 5/r32/ebp
    620     5d/pop-to-ebp
    621     c3/return
    622
    623 parse-array-of-decimal-ints:  # s: (addr array byte), out: (addr handle array int)
    624     # . prologue
    625     55/push-ebp
    626     89/<- %ebp 4/r32/esp
    627     #
    628     (_parse-array-of-decimal-ints Heap *(ebp+8) *(ebp+0xc))
    629 $parse-array-of-decimal-ints:end:
    630     # . epilogue
    631     89/<- %esp 5/r32/ebp
    632     5d/pop-to-ebp
    633     c3/return