|
| 1 | + assume adl=1 |
| 2 | + |
| 3 | +;------------------------------------------------------------------------------- |
| 4 | + |
| 5 | + section .text |
| 6 | + |
| 7 | + public _strtol |
| 8 | + |
| 9 | +_strtol: |
| 10 | + call __strtol_common |
| 11 | + ; overflow occured if B is non-zero |
| 12 | + djnz .out_of_range |
| 13 | + ld a, e |
| 14 | + rla |
| 15 | + jr c, .maybe_out_of_range |
| 16 | + ret nz |
| 17 | + jp __lneg |
| 18 | + |
| 19 | +.maybe_out_of_range: |
| 20 | + ; greater than INT_MAX |
| 21 | + jr nz, .overflow |
| 22 | + ; negative |
| 23 | + ; check that the result is not an exact INT_MIN |
| 24 | + or a, a |
| 25 | + adc hl, hl |
| 26 | + jr nz, .underflow |
| 27 | + ld a, e |
| 28 | + adc a, a |
| 29 | + ret z ; exact INT_MIN |
| 30 | +.underflow: |
| 31 | + xor a, a ; set Z |
| 32 | +.out_of_range: |
| 33 | +.overflow: |
| 34 | + ld e, $80 |
| 35 | + ld hl, 5 ; ERANGE |
| 36 | + ld (_errno), hl |
| 37 | + ld l, h ; ld hl, 0 |
| 38 | + ret z ; underflow |
| 39 | + ; overflow |
| 40 | + dec hl |
| 41 | + dec e |
| 42 | + ret |
| 43 | + |
| 44 | +;------------------------------------------------------------------------------- |
| 45 | + |
| 46 | + section .text |
| 47 | + |
| 48 | + public _strtoul |
| 49 | + |
| 50 | +_strtoul: |
| 51 | + call __strtol_common |
| 52 | + ; overflow occured if B is non-zero |
| 53 | + djnz .out_of_range |
| 54 | + ret nz |
| 55 | + jp __lneg |
| 56 | + |
| 57 | +.out_of_range: |
| 58 | + ld hl, 5 ; ERANGE |
| 59 | + ld (_errno), hl |
| 60 | + ld l, h ; ld hl, 0 |
| 61 | + dec hl |
| 62 | + ld e, l |
| 63 | + ret |
| 64 | + |
| 65 | +;------------------------------------------------------------------------------- |
| 66 | + |
| 67 | + section .text |
| 68 | + |
| 69 | + private __strtol_common |
| 70 | + |
| 71 | +__strtol_common: |
| 72 | + ; output: E:UHL |
| 73 | + ; B = 1 if no overflow |
| 74 | + ; Z means that A is zero = negate return value |
| 75 | + ; NZ means that A is non-zero = positive return value |
| 76 | + push ix |
| 77 | + ld ix, 0 |
| 78 | + lea hl, ix - 37 ; ld hl, -37 |
| 79 | + add ix, sp |
| 80 | + |
| 81 | + ld bc, (ix + 15) ; base |
| 82 | + add hl, bc |
| 83 | + jr c, .invalid_base |
| 84 | + ; UBC is zero here |
| 85 | + ld b, c ; store the base in B to allow for djnz hax |
| 86 | + ld hl, (ix + 9) ; nptr |
| 87 | +;------------------------------------------------------------------------------- |
| 88 | +; consume whitespace (inlinsed isspace) |
| 89 | +.whitespace_loop: |
| 90 | + ld a, (hl) |
| 91 | + inc hl |
| 92 | + cp a, 32 |
| 93 | + jr z, .whitespace_loop |
| 94 | + sub a, 9 |
| 95 | + add a, -5 |
| 96 | + jr nc, .whitespace_loop |
| 97 | +; test for plus/minus signs |
| 98 | + ; A = (HL - 1) - 9 + -5 |
| 99 | + ; A = (HL - 1) - 14 |
| 100 | + xor a, '-' - 14 |
| 101 | + push af |
| 102 | + jr z, .minus_sign |
| 103 | + xor a, ('+' - 14) xor ('-' - 14) |
| 104 | + jr z, .plus_sign |
| 105 | + dec hl |
| 106 | + xor a, a |
| 107 | +.plus_sign: |
| 108 | +.minus_sign: |
| 109 | + ; A = 0, (HL) = start of number |
| 110 | +;------------------------------------------------------------------------------- |
| 111 | +; update the base if needed |
| 112 | + or a, b ; base |
| 113 | + jr z, .auto_base |
| 114 | + xor a, 16 |
| 115 | + jr z, .hex_base |
| 116 | + xor a, 2 xor 16 |
| 117 | + jr nz, .other_base |
| 118 | +.auto_base: ; test for 0* 0x* 0X* 0b* 0B* |
| 119 | +.bin_base: ; test for 0x* 0X* |
| 120 | +.hex_base: ; test for 0b* 0B* |
| 121 | + inc b ; djnz hax |
| 122 | + ld a, (hl) |
| 123 | + xor a, '0' |
| 124 | + jr nz, .maybe_decimal |
| 125 | + inc hl |
| 126 | + ld a, (hl) |
| 127 | + res 5, a ; upper case |
| 128 | + xor a, 'X' |
| 129 | + jr z, .maybe_hex |
| 130 | + xor a, 'B' xor 'X' |
| 131 | + jr z, .maybe_bin |
| 132 | + dec hl |
| 133 | + djnz .other_base |
| 134 | + ld b, 8 ; octal |
| 135 | + jr .save_new_base |
| 136 | + |
| 137 | +.maybe_bin: |
| 138 | + bit 4, b |
| 139 | + jr nz, .undo_inc ; hexadecimal |
| 140 | + ; base is 0 or 2 |
| 141 | + inc hl |
| 142 | + ld b, 2 |
| 143 | + jr .save_new_base |
| 144 | + |
| 145 | +.maybe_hex: |
| 146 | + bit 1, b |
| 147 | + jr nz, .undo_inc ; binary |
| 148 | + ; base is 0 or 16 |
| 149 | + inc hl |
| 150 | + ld b, 16 |
| 151 | + jr .save_new_base |
| 152 | + |
| 153 | +.undo_inc: |
| 154 | + dec hl |
| 155 | + ; dec b |
| 156 | + ; jr .other_base |
| 157 | +.maybe_decimal: |
| 158 | + ; set to decimal if base is not zero |
| 159 | + djnz .other_base |
| 160 | + ld b, 10 ; decimal |
| 161 | +.save_new_base: |
| 162 | +;------------------------------------------------------------------------------- |
| 163 | +.other_base: |
| 164 | + ld a, (hl) ; first digit of the number |
| 165 | + push hl |
| 166 | + pop iy |
| 167 | + ld d, b |
| 168 | +.invalid_base_hijack: |
| 169 | + ; or a, a ; carry is cleared here |
| 170 | + sbc hl, hl |
| 171 | + ld e, l |
| 172 | + ld b, l |
| 173 | + ; A = first digit of the number |
| 174 | + ; E:UHL = 0 |
| 175 | + ; D = base |
| 176 | + ; UBC = 0 when base is valid (otherwise unknown) |
| 177 | + ; B = 0 |
| 178 | + |
| 179 | + ; The strto* functions return nptr (not nptr + whitespace) if there are |
| 180 | + ; no digits in the string. Having a digit check here allows us to |
| 181 | + ; directly handle the case where the string has no digits. |
| 182 | + |
| 183 | + sub a, 48 |
| 184 | + cp a, 10 |
| 185 | + jr c, .check_digit |
| 186 | + ; Convert an alphabetic digit, case-insensitive |
| 187 | + sub a, 65 - 48 |
| 188 | + res 5, a |
| 189 | + add a, 10 |
| 190 | +.check_digit: |
| 191 | + ; End the loop when the digit is out of range for the base |
| 192 | + cp a, d |
| 193 | + jr c, .loop |
| 194 | +;------------------------------------------------------------------------------- |
| 195 | +; no digit found or invalid base |
| 196 | + ; set *endptr to nptr and return 0 |
| 197 | + ld iy, (ix + 9) ; nptr |
| 198 | + jr .write_endptr |
| 199 | +.invalid_base: |
| 200 | + xor a, a |
| 201 | + ld d, a |
| 202 | + ; Setting D (base) to zero ensures that cp a, d will never set carry. |
| 203 | + ; forcing the function to return. |
| 204 | + push af |
| 205 | + ; sets E:UHL to zero |
| 206 | + jr .invalid_base_hijack |
| 207 | +;------------------------------------------------------------------------------- |
| 208 | + ; common path : 30F + 1R + 3W + 5 + __lmulu_b |
| 209 | + ; decimal path : 6F + 0R + 0W + 1 |
| 210 | + ; other path : 12F + 0R + 0W + 1 |
| 211 | + ; |
| 212 | + ; decimal digit : 36F + 1R + 3W + 4 + __lmulu_b |
| 213 | + ; other digit : 42F + 1R + 3W + 4 + __lmulu_b |
| 214 | + ; __lmulu_b : 43F + 12R + 9W + 17 |
| 215 | + ; |
| 216 | + ; Total CC per digit: |
| 217 | + ; decimal digit : 79F + 13R + 12W + 21 |
| 218 | + ; other digit : 85F + 13R + 12W + 21 |
| 219 | +.check_decimal: |
| 220 | + cp a, d |
| 221 | + jr nc, .end_loop |
| 222 | +.loop: |
| 223 | + ld c, a ; UBC = digit if no carry, don't care otherwise |
| 224 | + ld a, d ; A = base |
| 225 | + ld d, e ; D = upper accumulator byte |
| 226 | + ld e, b ; E = 0 if no carry, don't care otherwise |
| 227 | + ; E:UHL = UHL * base |
| 228 | + call __lmulu_b |
| 229 | + ; Add digit to lower product bytes |
| 230 | + add hl, bc |
| 231 | + ld c, a ; C = base |
| 232 | + ld a, e ; A = upper product byte |
| 233 | + ld e, c ; E = base |
| 234 | + mlt de ; DE = upper accumulator byte * base |
| 235 | + ; Carry into upper product byte |
| 236 | + adc a, e |
| 237 | + ld e, a |
| 238 | + ; Set B != 0 if any carry from the upper byte or previous iterations |
| 239 | + sbc a, a |
| 240 | + or a, d |
| 241 | + or a, b |
| 242 | + ld b, a |
| 243 | + ld d, c ; D = base |
| 244 | + ; IY = str, D = base, E:UHL = accumulator, BCU = 0, B = 0 if no carry |
| 245 | +.next_digit: |
| 246 | + inc iy |
| 247 | + ; Convert a numerical digit |
| 248 | + ld a, (iy) |
| 249 | + sub a, 48 |
| 250 | + cp a, 10 |
| 251 | + jr c, .check_decimal |
| 252 | + ; Convert an alphabetic digit, case-insensitive |
| 253 | + sub a, 65 - 48 |
| 254 | + res 5, a |
| 255 | + add a, 10 |
| 256 | + ; End the loop when the digit is out of range for the base |
| 257 | + cp a, d |
| 258 | + jr c, .loop |
| 259 | +.end_loop: |
| 260 | +;------------------------------------------------------------------------------- |
| 261 | +.write_endptr: |
| 262 | + push hl |
| 263 | + ld hl, (ix + 12) ; endptr |
| 264 | + add hl, de |
| 265 | + or a, a |
| 266 | + sbc hl, de |
| 267 | + jr z, .endptr_null |
| 268 | + ld (hl), iy |
| 269 | +.endptr_null: |
| 270 | + pop hl |
| 271 | + inc b ; djnz hax |
| 272 | + pop af |
| 273 | + pop ix |
| 274 | + ret |
| 275 | + |
| 276 | + extern _errno |
| 277 | + extern __lneg |
| 278 | + extern __lmulu_b |
0 commit comments