Skip to content

Commit d663153

Browse files
committed
implemented strto(u)l in assembly
1 parent bf96486 commit d663153

File tree

6 files changed

+724
-116
lines changed

6 files changed

+724
-116
lines changed

src/libc/strtol.c

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/libc/strtol.src

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
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

src/libc/strtoul.c

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/libc/strtoull.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#define STRTOX_TYPE long long
22
#define STRTOX_MAX ULLONG_MAX
3-
#define STRTOX_MIN 0
3+
#define STRTOX_MIN ULLONG_MAX
44

55
#define STRTOX_SIGNED 0
66
#define STRTOX_NAME strtoull
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
assume adl=1
2+
3+
section .text
4+
5+
public _char_to_digit
6+
7+
; char char_to_digit(char c)
8+
_char_to_digit:
9+
; returns [0, 9] when ['0', '9']
10+
; returns [10, 35] when ['A', 'Z'] or ['a', 'z']
11+
; otherwise returns -1
12+
ld iy, 3
13+
add iy, sp
14+
;-------------------------------------------------------------------------------
15+
ld a, (iy)
16+
sub a, 48
17+
cp a, 10
18+
jr c, .check_digit
19+
; Convert an alphabetic digit, case-insensitive
20+
sub a, 65 - 48
21+
res 5, a
22+
add a, 10
23+
.check_digit:
24+
cp a, 36
25+
ret c
26+
ld a, -1
27+
ret

0 commit comments

Comments
 (0)