From bf300d5566a7e0c1cf55def4bf300ccdf21bdc0c Mon Sep 17 00:00:00 2001 From: unknown <71151164+ZERICO2005@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:05:18 -0600 Subject: [PATCH 1/5] implemented atoi, atol, and atoll in assembly --- src/libc/atoi.src | 61 ++++++++++ src/libc/atol.src | 84 +++++++++++--- src/libc/atoll.src | 77 +++++++++++-- test/standalone/atol/autotest.json | 40 +++++++ test/standalone/atol/makefile | 19 ++++ test/standalone/atol/src/main.c | 173 +++++++++++++++++++++++++++++ 6 files changed, 428 insertions(+), 26 deletions(-) create mode 100644 src/libc/atoi.src create mode 100644 test/standalone/atol/autotest.json create mode 100644 test/standalone/atol/makefile create mode 100644 test/standalone/atol/src/main.c diff --git a/src/libc/atoi.src b/src/libc/atoi.src new file mode 100644 index 000000000..613472c04 --- /dev/null +++ b/src/libc/atoi.src @@ -0,0 +1,61 @@ + assume adl=1 + + section .text + + public _atoi + +_atoi: + pop de + ex (sp), hl + push de + ; inlined isspace +.whitespace_loop: + ld a, (hl) + inc hl + cp a, 32 + jr z, .whitespace_loop + sub a, 9 + add a, -5 + jr nc, .whitespace_loop + + ; A = (HL - 1) - 9 + -5 + ; A = (HL - 1) - 14 + xor a, '-' - 14 + push af + jr z, .minus_sign + xor a, ('+' - 14) xor ('-' - 14) + jr z, .plus_sign + dec hl +.plus_sign: +.minus_sign: + ; carry is cleared + ex de, hl + sbc hl, hl + ; DE = start of the digits + ; HL = 0 + jr .start +.loop: + ; 21F + 4R + 3W + 1 per digit + push hl + pop bc + add hl, hl ; *= 2 + add hl, hl ; *= 4 + add hl, bc ; *= 5 + add hl, hl ; *= 10 + ld bc, 0 + ld c, a + add hl, bc + inc de ; next digit +.start: + ld a, (de) + sub a, 48 + cp a, 10 + jr c, .loop +.finish: + pop af + ; carry is cleared + ret nz ; A != '-' positive + ; A == '-' negative + jp __ineg + + extern __ineg diff --git a/src/libc/atol.src b/src/libc/atol.src index ee1bc9435..8986947ab 100644 --- a/src/libc/atol.src +++ b/src/libc/atol.src @@ -1,21 +1,77 @@ assume adl=1 section .text - public _atoi, _atol -_atoi: + + public _atol + _atol: - pop bc - ex (sp),hl - push bc - ld bc,10 - push bc - ld c,b - push bc + pop de + ex (sp), hl + push de + ; inlined isspace +.whitespace_loop: + ld a, (hl) + inc hl + cp a, 32 + jr z, .whitespace_loop + sub a, 9 + add a, -5 + jr nc, .whitespace_loop + + ; A = (HL - 1) - 9 + -5 + ; A = (HL - 1) - 14 + xor a, '-' - 14 + push af + jr z, .minus_sign + xor a, ('+' - 14) xor ('-' - 14) + jr z, .plus_sign + dec hl +.plus_sign: +.minus_sign: + ; carry is cleared push hl - call _strtol - pop af - pop af + pop iy + sbc hl, hl + ld e, l + ; IY = start of the digits + ; E:UHL = 0 + jr .start +.loop: + ; 32F + 4R + 3W + 1 per digit + ld d, a + ld a, e + push hl + pop bc + ; *= 2 + add hl, hl + rla + ; *= 4 + add hl, hl + rla + ; *= 5 + add hl, bc + adc a, e + ; *= 10 + add hl, hl + rla + ; += digit + ld bc, 0 + ld c, d + add hl, bc + adc a, b + ld e, a + ; next digit + inc iy +.start: + ld a, (iy) + sub a, 48 + cp a, 10 + jr c, .loop +.finish: pop af - ret + ; carry is cleared + ret nz ; A != '-' positive + ; A == '-' negative + jp __lneg - extern _strtol + extern __lneg diff --git a/src/libc/atoll.src b/src/libc/atoll.src index d9f5f4903..346aade96 100644 --- a/src/libc/atoll.src +++ b/src/libc/atoll.src @@ -1,20 +1,73 @@ assume adl=1 section .text + public _atoll + _atoll: - pop bc - ex (sp),hl - push bc - ld bc,10 - push bc - ld c,b - push bc + push ix + ld ix, -3 + add ix, sp + ld hl, (ix + 9) + ; inlined isspace +.whitespace_loop: + ld a, (hl) + inc hl + cp a, 32 + jr z, .whitespace_loop + sub a, 9 + add a, -5 + jr nc, .whitespace_loop + + ; A = (HL - 1) - 9 + -5 + ; A = (HL - 1) - 14 + xor a, '-' - 14 + push af + jr z, .minus_sign + xor a, ('+' - 14) xor ('-' - 14) + jr z, .plus_sign + dec hl +.plus_sign: +.minus_sign: + ; carry is cleared push hl - call _strtoll - pop af - pop af + pop iy + sbc hl, hl + ex de, hl + sbc hl, hl + ld b, l + ld c, l + push hl + push hl + push hl + ; IY = start of the digits + ; BC:UDE:UHL = 0 + ; (ix - 9) = [0, 10] + jr .start +.loop: + ; loop : 27F + 1R + 8W + 1 + ; lladd : ? + ; llmulu: ? + ; total : a lot per digit + ld (ix - 9), 10 + call __llmulu ; BC:UDE:UHL *= 10 + ld (ix - 9), a + call __lladd + inc iy ; next digit +.start: + ld a, (iy) + sub a, 48 + cp a, 10 + jr c, .loop +.finish: + ld sp, ix pop af - ret + ; carry is cleared + pop ix + ret nz ; A != '-' positive + ; A == '-' negative + jp __llneg - extern _strtoll + extern __llneg + extern __lladd + extern __llmulu diff --git a/test/standalone/atol/autotest.json b/test/standalone/atol/autotest.json new file mode 100644 index 000000000..be5eeed3c --- /dev/null +++ b/test/standalone/atol/autotest.json @@ -0,0 +1,40 @@ +{ + "transfer_files": [ + "bin/DEMO.8xp" + ], + "target": { + "name": "DEMO", + "isASM": true + }, + "sequence": [ + "action|launch", + "delay|1000", + "hashWait|1", + "key|enter", + "delay|300", + "hashWait|2" + ], + "hashes": { + "1": { + "description": "All tests passed", + "timeout": 5000, + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "38E2AD5A" + ] + }, + "2": { + "description": "Exit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "FFAF89BA", + "101734A5", + "9DA19F44", + "A32840C8", + "349F4775" + ] + } + } +} diff --git a/test/standalone/atol/makefile b/test/standalone/atol/makefile new file mode 100644 index 000000000..e99218420 --- /dev/null +++ b/test/standalone/atol/makefile @@ -0,0 +1,19 @@ +# ---------------------------- +# Makefile Options +# ---------------------------- + +NAME = DEMO +ICON = icon.png +DESCRIPTION = "CE C Toolchain Demo" +COMPRESSED = NO +ARCHIVED = NO + +CFLAGS = -ffreestanding -Wall -Wextra -Wshadow -Wconversion -Wformat=2 -Wno-sign-conversion -Oz +CXXFLAGS = -ffreestanding -Wall -Wextra -Wshadow -Wconversion -Wformat=2 -Wno-sign-conversion -Oz + +PREFER_OS_LIBC = NO +PREFER_OS_CRT = NO + +# ---------------------------- + +include $(shell cedev-config --makefile) diff --git a/test/standalone/atol/src/main.c b/test/standalone/atol/src/main.c new file mode 100644 index 000000000..34ef2b5c5 --- /dev/null +++ b/test/standalone/atol/src/main.c @@ -0,0 +1,173 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Config +//------------------------------------------------------------------------------ + +// define to 0 or 1 +#define DEBUG_DIAGNOSTICS 0 + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +#ifndef DEBUG_DIAGNOSTICS +#error "DEBUG_DIAGNOSTICS needs to be defined to 0 or 1" +#endif + +#if DEBUG_DIAGNOSTICS +#define test_printf printf +#else +#define test_printf(...) +#endif + +#define C(expr) if (!(expr)) { return __LINE__; } + +#define T(val, expr) do { \ + long long temp = (long long)(expr); \ + if ((long long)(val) != temp) { \ + test_printf("E: %lld\n", temp); \ + return __LINE__; \ + } \ +} while (0); + +#define TEST(test) { ret = test; if (ret != 0) { return ret; }} + +int test_atoi(void) { + T( 0, atoi("" )); + T( 0, atoi("+" )); + T( 0, atoi("-" )); + T( 0, atoi("0" )); + T( 0, atoi("+0" )); + T( 0, atoi("-0" )); + T( 1, atoi("1" )); + T( 1, atoi("+1" )); + T( -1, atoi("-1" )); + T( 0, atoi("+-84" )); + T( 0, atoi("--84" )); + T( 0, atoi("-+84" )); + T( 0, atoi("++84" )); + T( 0, atoi("+ 84" )); + T( 0, atoi("- 84" )); + T( 100, atoi("100" )); + T( 100, atoi("+100" )); + T(-100, atoi("-100" )); + T(-123, atoi(" -123junk" )); + T( 321, atoi(" +321dust" )); + T( 99, atoi(" \f\n\r\t\v99")); + T( 42, atoi("0042" )); + T( 0, atoi("0x2A" )); + T( 0, atoi("junk" )); + T( 0, atoi("a701" )); + + T( INT_MIN, atoi("-8388608" )); + T( INT_MAX, atoi("8388607" )); + T( INT_MAX, atoi("+8388607" )); + T(-INT_MAX, atoi("-8388607" )); + return 0; +} + +int test_atol(void) { + T( 0, atol("" )); + T( 0, atol("+" )); + T( 0, atol("-" )); + T( 0, atol("0" )); + T( 0, atol("+0" )); + T( 0, atol("-0" )); + T( 1, atol("1" )); + T( 1, atol("+1" )); + T( -1, atol("-1" )); + T( 0, atol("+-84" )); + T( 0, atol("--84" )); + T( 0, atol("-+84" )); + T( 0, atol("++84" )); + T( 0, atol("+ 84" )); + T( 0, atol("- 84" )); + T( 100, atol("100" )); + T( 100, atol("+100" )); + T(-100, atol("-100" )); + T(-123, atol(" -123junk" )); + T( 321, atol(" +321dust" )); + T( 99, atol(" \f\n\r\t\v99")); + T( 42, atol("0042" )); + T( 0, atol("0x2A" )); + T( 0, atol("junk" )); + T( 0, atol("a701" )); + + T( LONG_MIN, atol("-2147483648")); + T( LONG_MAX, atol("2147483647")); + T( LONG_MAX, atol("+2147483647")); + T(-LONG_MAX, atol("-2147483647")); + return 0; +} + +int test_atoll(void) { + T( 0, atoll("" )); + T( 0, atoll("+" )); + T( 0, atoll("-" )); + T( 0, atoll("0" )); + T( 0, atoll("+0" )); + T( 0, atoll("-0" )); + T( 1, atoll("1" )); + T( 1, atoll("+1" )); + T( -1, atoll("-1" )); + T( 0, atoll("+-84" )); + T( 0, atoll("--84" )); + T( 0, atoll("-+84" )); + T( 0, atoll("++84" )); + T( 0, atoll("+ 84" )); + T( 0, atoll("- 84" )); + T( 100, atoll("100" )); + T( 100, atoll("+100" )); + T(-100, atoll("-100" )); + T(-123, atoll(" -123junk" )); + T( 321, atoll(" +321dust" )); + T( 99, atoll(" \f\n\r\t\v99")); + T( 42, atoll("0042" )); + T( 0, atoll("0x2A" )); + T( 0, atoll("junk" )); + T( 0, atoll("a701" )); + + T( LLONG_MIN, atoll("-9223372036854775808")); + T( LLONG_MAX, atoll("9223372036854775807")); + T( LLONG_MAX, atoll("+9223372036854775807")); + T(-LLONG_MAX, atoll("-9223372036854775807")); + return 0; +} + +int run_tests(void) { + int ret = 0; + + TEST(test_atoi()); + TEST(test_atol()); + TEST(test_atoll()); + + return ret; +} + +int main(void) { + os_ClrHome(); + int failed_test = run_tests(); + if (failed_test != 0) { + char buf[sizeof("Failed test L-8388608\n")]; + boot_sprintf(buf, "Failed test L%d\n", failed_test); + fputs(buf, stdout); + } else { + fputs("All tests passed", stdout); + } + + while (!os_GetCSC()); + + return 0; +} From 9a3d298ea8466ff3528001a2de13090901a9f4c3 Mon Sep 17 00:00:00 2001 From: unknown <71151164+ZERICO2005@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:54:56 -0600 Subject: [PATCH 2/5] implemented strto(u)l in assembly --- src/libc/strtol.c | 8 - src/libc/strtol.src | 278 +++++++++++ src/libc/strtoul.c | 8 - src/libc/strtoull.c | 2 +- test/standalone/atol/src/char_to_digit.asm | 27 ++ test/standalone/atol/src/main.c | 517 +++++++++++++++++---- 6 files changed, 724 insertions(+), 116 deletions(-) delete mode 100644 src/libc/strtol.c create mode 100644 src/libc/strtol.src delete mode 100644 src/libc/strtoul.c create mode 100644 test/standalone/atol/src/char_to_digit.asm diff --git a/src/libc/strtol.c b/src/libc/strtol.c deleted file mode 100644 index 50659a787..000000000 --- a/src/libc/strtol.c +++ /dev/null @@ -1,8 +0,0 @@ -#define STRTOX_TYPE long -#define STRTOX_MAX LONG_MAX -#define STRTOX_MIN LONG_MIN - -#define STRTOX_SIGNED 1 -#define STRTOX_NAME strtol - -#include "strtox.h" diff --git a/src/libc/strtol.src b/src/libc/strtol.src new file mode 100644 index 000000000..31d57f07a --- /dev/null +++ b/src/libc/strtol.src @@ -0,0 +1,278 @@ + assume adl=1 + +;------------------------------------------------------------------------------- + + section .text + + public _strtol + +_strtol: + call __strtol_common + ; overflow occured if B is non-zero + djnz .out_of_range + ld a, e + rla + jr c, .maybe_out_of_range + ret nz + jp __lneg + +.maybe_out_of_range: + ; greater than INT_MAX + jr nz, .overflow + ; negative + ; check that the result is not an exact INT_MIN + or a, a + adc hl, hl + jr nz, .underflow + ld a, e + adc a, a + ret z ; exact INT_MIN +.underflow: + xor a, a ; set Z +.out_of_range: +.overflow: + ld e, $80 + ld hl, 5 ; ERANGE + ld (_errno), hl + ld l, h ; ld hl, 0 + ret z ; underflow + ; overflow + dec hl + dec e + ret + +;------------------------------------------------------------------------------- + + section .text + + public _strtoul + +_strtoul: + call __strtol_common + ; overflow occured if B is non-zero + djnz .out_of_range + ret nz + jp __lneg + +.out_of_range: + ld hl, 5 ; ERANGE + ld (_errno), hl + ld l, h ; ld hl, 0 + dec hl + ld e, l + ret + +;------------------------------------------------------------------------------- + + section .text + + private __strtol_common + +__strtol_common: + ; output: E:UHL + ; B = 1 if no overflow + ; Z means that A is zero = negate return value + ; NZ means that A is non-zero = positive return value + push ix + ld ix, 0 + lea hl, ix - 37 ; ld hl, -37 + add ix, sp + + ld bc, (ix + 15) ; base + add hl, bc + jr c, .invalid_base + ; UBC is zero here + ld b, c ; store the base in B to allow for djnz hax + ld hl, (ix + 9) ; nptr +;------------------------------------------------------------------------------- +; consume whitespace (inlinsed isspace) +.whitespace_loop: + ld a, (hl) + inc hl + cp a, 32 + jr z, .whitespace_loop + sub a, 9 + add a, -5 + jr nc, .whitespace_loop +; test for plus/minus signs + ; A = (HL - 1) - 9 + -5 + ; A = (HL - 1) - 14 + xor a, '-' - 14 + push af + jr z, .minus_sign + xor a, ('+' - 14) xor ('-' - 14) + jr z, .plus_sign + dec hl + xor a, a +.plus_sign: +.minus_sign: + ; A = 0, (HL) = start of number +;------------------------------------------------------------------------------- +; update the base if needed + or a, b ; base + jr z, .auto_base + xor a, 16 + jr z, .hex_base + xor a, 2 xor 16 + jr nz, .other_base +.auto_base: ; test for 0* 0x* 0X* 0b* 0B* +.bin_base: ; test for 0x* 0X* +.hex_base: ; test for 0b* 0B* + inc b ; djnz hax + ld a, (hl) + xor a, '0' + jr nz, .maybe_decimal + inc hl + ld a, (hl) + res 5, a ; upper case + xor a, 'X' + jr z, .maybe_hex + xor a, 'B' xor 'X' + jr z, .maybe_bin + dec hl + djnz .other_base + ld b, 8 ; octal + jr .save_new_base + +.maybe_bin: + bit 4, b + jr nz, .undo_inc ; hexadecimal + ; base is 0 or 2 + inc hl + ld b, 2 + jr .save_new_base + +.maybe_hex: + bit 1, b + jr nz, .undo_inc ; binary + ; base is 0 or 16 + inc hl + ld b, 16 + jr .save_new_base + +.undo_inc: + dec hl + ; dec b + ; jr .other_base +.maybe_decimal: + ; set to decimal if base is not zero + djnz .other_base + ld b, 10 ; decimal +.save_new_base: +;------------------------------------------------------------------------------- +.other_base: + ld a, (hl) ; first digit of the number + push hl + pop iy + ld d, b +.invalid_base_hijack: + ; or a, a ; carry is cleared here + sbc hl, hl + ld e, l + ld b, l + ; A = first digit of the number + ; E:UHL = 0 + ; D = base + ; UBC = 0 when base is valid (otherwise unknown) + ; B = 0 + + ; The strto* functions return nptr (not nptr + whitespace) if there are + ; no digits in the string. Having a digit check here allows us to + ; directly handle the case where the string has no digits. + + sub a, 48 + cp a, 10 + jr c, .check_digit + ; Convert an alphabetic digit, case-insensitive + sub a, 65 - 48 + res 5, a + add a, 10 +.check_digit: + ; End the loop when the digit is out of range for the base + cp a, d + jr c, .loop +;------------------------------------------------------------------------------- +; no digit found or invalid base + ; set *endptr to nptr and return 0 + ld iy, (ix + 9) ; nptr + jr .write_endptr +.invalid_base: + xor a, a + ld d, a + ; Setting D (base) to zero ensures that cp a, d will never set carry. + ; forcing the function to return. + push af + ; sets E:UHL to zero + jr .invalid_base_hijack +;------------------------------------------------------------------------------- + ; common path : 30F + 1R + 3W + 5 + __lmulu_b + ; decimal path : 6F + 0R + 0W + 1 + ; other path : 12F + 0R + 0W + 1 + ; + ; decimal digit : 36F + 1R + 3W + 4 + __lmulu_b + ; other digit : 42F + 1R + 3W + 4 + __lmulu_b + ; __lmulu_b : 43F + 12R + 9W + 17 + ; + ; Total CC per digit: + ; decimal digit : 79F + 13R + 12W + 21 + ; other digit : 85F + 13R + 12W + 21 +.check_decimal: + cp a, d + jr nc, .end_loop +.loop: + ld c, a ; UBC = digit if no carry, don't care otherwise + ld a, d ; A = base + ld d, e ; D = upper accumulator byte + ld e, b ; E = 0 if no carry, don't care otherwise + ; E:UHL = UHL * base + call __lmulu_b + ; Add digit to lower product bytes + add hl, bc + ld c, a ; C = base + ld a, e ; A = upper product byte + ld e, c ; E = base + mlt de ; DE = upper accumulator byte * base + ; Carry into upper product byte + adc a, e + ld e, a + ; Set B != 0 if any carry from the upper byte or previous iterations + sbc a, a + or a, d + or a, b + ld b, a + ld d, c ; D = base + ; IY = str, D = base, E:UHL = accumulator, BCU = 0, B = 0 if no carry +.next_digit: + inc iy + ; Convert a numerical digit + ld a, (iy) + sub a, 48 + cp a, 10 + jr c, .check_decimal + ; Convert an alphabetic digit, case-insensitive + sub a, 65 - 48 + res 5, a + add a, 10 + ; End the loop when the digit is out of range for the base + cp a, d + jr c, .loop +.end_loop: +;------------------------------------------------------------------------------- +.write_endptr: + push hl + ld hl, (ix + 12) ; endptr + add hl, de + or a, a + sbc hl, de + jr z, .endptr_null + ld (hl), iy +.endptr_null: + pop hl + inc b ; djnz hax + pop af + pop ix + ret + + extern _errno + extern __lneg + extern __lmulu_b diff --git a/src/libc/strtoul.c b/src/libc/strtoul.c deleted file mode 100644 index 56742b4dd..000000000 --- a/src/libc/strtoul.c +++ /dev/null @@ -1,8 +0,0 @@ -#define STRTOX_TYPE long -#define STRTOX_MAX ULONG_MAX -#define STRTOX_MIN 0 - -#define STRTOX_SIGNED 0 -#define STRTOX_NAME strtoul - -#include "strtox.h" diff --git a/src/libc/strtoull.c b/src/libc/strtoull.c index 58c576c79..ea2550b29 100644 --- a/src/libc/strtoull.c +++ b/src/libc/strtoull.c @@ -1,6 +1,6 @@ #define STRTOX_TYPE long long #define STRTOX_MAX ULLONG_MAX -#define STRTOX_MIN 0 +#define STRTOX_MIN ULLONG_MAX #define STRTOX_SIGNED 0 #define STRTOX_NAME strtoull diff --git a/test/standalone/atol/src/char_to_digit.asm b/test/standalone/atol/src/char_to_digit.asm new file mode 100644 index 000000000..e3223262a --- /dev/null +++ b/test/standalone/atol/src/char_to_digit.asm @@ -0,0 +1,27 @@ + assume adl=1 + + section .text + + public _char_to_digit + +; char char_to_digit(char c) +_char_to_digit: + ; returns [0, 9] when ['0', '9'] + ; returns [10, 35] when ['A', 'Z'] or ['a', 'z'] + ; otherwise returns -1 + ld iy, 3 + add iy, sp +;------------------------------------------------------------------------------- + ld a, (iy) + sub a, 48 + cp a, 10 + jr c, .check_digit + ; Convert an alphabetic digit, case-insensitive + sub a, 65 - 48 + res 5, a + add a, 10 +.check_digit: + cp a, 36 + ret c + ld a, -1 + ret diff --git a/test/standalone/atol/src/main.c b/test/standalone/atol/src/main.c index 34ef2b5c5..66f61afad 100644 --- a/test/standalone/atol/src/main.c +++ b/test/standalone/atol/src/main.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include //------------------------------------------------------------------------------ @@ -19,7 +21,7 @@ #define DEBUG_DIAGNOSTICS 0 //------------------------------------------------------------------------------ -// Tests +// Utility //------------------------------------------------------------------------------ #ifndef DEBUG_DIAGNOSTICS @@ -32,126 +34,443 @@ #define test_printf(...) #endif -#define C(expr) if (!(expr)) { return __LINE__; } +#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) -#define T(val, expr) do { \ - long long temp = (long long)(expr); \ - if ((long long)(val) != temp) { \ - test_printf("E: %lld\n", temp); \ - return __LINE__; \ - } \ -} while (0); +#define C(expr) if (!(expr)) { return __LINE__; } #define TEST(test) { ret = test; if (ret != 0) { return ret; }} +#define TEST_NAME(test, name) { ret = test; if (ret != 0) { fputs(name "\n", stdout); return ret; }} + +#define NO_ERR 0 + +//------------------------------------------------------------------------------ +// Char to digit +//------------------------------------------------------------------------------ + +/** + * returns [0, 9] when ['0', '9'] + * returns [10, 35] when ['A', 'Z'] or ['a', 'z'] + * otherwise returns -1 + */ +char char_to_digit(char c); + +char char_to_digit_truth(char c) { + if (c >= '0' && c <= '9') { + return (c - '0'); + } + if (c >= 'A' && c <= 'Z') { + return (c - 'A') + 10; + } + if (c >= 'a' && c <= 'z') { + return (c - 'a') + 10; + } + return -1; +} + +int test_char_to_digit(void) { + for (int i = 0x00; i <= 0xFF; i++) { + char c = (char)i; + char guess = char_to_digit(c); + char truth = char_to_digit_truth(c); + if (guess != truth) { + test_printf("%02X: %d != %d\n", (uint8_t)c, guess, truth); + return __LINE__; + } + } + return 0; +} + +//------------------------------------------------------------------------------ +// atoint tests +//------------------------------------------------------------------------------ + +typedef long long (*atoint_func)(const char* nptr); + +long long atoint_atoi(const char* nptr) { + return (long long)atoi(nptr); +} + +long long atoint_atol(const char* nptr) { + return (long long)atol(nptr); +} + +long long atoint_atoll(const char* nptr) { + return (long long)atoll(nptr); +} + +long long atoint_strtol(const char* nptr) { + return (long long)strtol(nptr, NULL, 10); +} + +long long atoint_strtoul(const char* nptr) { + return (long long)((long)strtoul(nptr, NULL, 10)); +} + +long long atoint_strtoll(const char* nptr) { + return strtoll(nptr, NULL, 10); +} + +long long atoint_strtoull(const char* nptr) { + return (long long)strtoull(nptr, NULL, 10); +} + +bool atoint_verify(atoint_func func, long long truth, const char* nptr) { + long long guess = (func)(nptr); + if (guess == truth) { + return true; + } + test_printf("E: %llX\n", guess); + return false; +} + +int atoint_test(atoint_func const func) { + C(atoint_verify(func, 0, "" )); + C(atoint_verify(func, 0, "+" )); + C(atoint_verify(func, 0, "-" )); + C(atoint_verify(func, 0, "0" )); + C(atoint_verify(func, 0, "+0" )); + C(atoint_verify(func, 0, "-0" )); + C(atoint_verify(func, 1, "1" )); + C(atoint_verify(func, 1, "+1" )); + C(atoint_verify(func, -1, "-1" )); + C(atoint_verify(func, 0, "+-84" )); + C(atoint_verify(func, 0, "--84" )); + C(atoint_verify(func, 0, "-+84" )); + C(atoint_verify(func, 0, "++84" )); + C(atoint_verify(func, 0, "+ 84" )); + C(atoint_verify(func, 0, "- 84" )); + C(atoint_verify(func, 10000, "10000" )); + C(atoint_verify(func, 10000, "+10000" )); + C(atoint_verify(func, -10000, "-10000" )); + C(atoint_verify(func, -123, " -123junk" )); + C(atoint_verify(func, 321, " +321dust" )); + C(atoint_verify(func, 99, " \f\n\r\t\v99")); + C(atoint_verify(func, 42, "0042" )); + C(atoint_verify(func, 0, "0x2A" )); + C(atoint_verify(func, 0, "junk" )); + C(atoint_verify(func, 0, "a701" )); + C(atoint_verify(func, 8, "000000008" )); + return 0; +} + +//------------------------------------------------------------------------------ +// strtoint tests +//------------------------------------------------------------------------------ + +typedef long long (*strtoint_func)(const char* nptr, char** endptr, int base); + +long long strtoint_strtol(const char* nptr, char** endptr, int base) { + return (long long)strtol(nptr, endptr, base); +} + +long long strtoint_strtoul(const char* nptr, char** endptr, int base) { + return (long long)((long)strtoul(nptr, endptr, base)); +} + +long long strtoint_strtoll(const char* nptr, char** endptr, int base) { + return strtoll(nptr, endptr, base); +} + +long long strtoint_strtoull(const char* nptr, char** endptr, int base) { + return (long long)strtoull(nptr, endptr, base); +} + +bool strtoint_verify( + long long truth, + const char* nptr, + int endptr_offset, + strtoint_func func, + int base, + int errno_state +) { + char* endptr; + long long guess; + errno = 0; + guess = (func)(nptr, &endptr, base); + if (guess != truth) { + test_printf("E: %llX\n", guess); + return false; + } + if (endptr != nptr + endptr_offset) { + test_printf("%p - %p = %td\n", endptr, nptr, endptr - nptr); + return false; + } + if (errno != errno_state) { + test_printf("errno: G %d != T %d\n", errno, errno_state); + return false; + } + guess = (func)(nptr, NULL, base); + if (guess != truth) { + test_printf("NULL error: %llX\n", guess); + return false; + } + return true; +} + +int strtoint_test(strtoint_func const func) { + /* test base 10 */ + C(strtoint_verify( 0, "" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "+" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "-" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "0" , 1, func, 10, NO_ERR)); + C(strtoint_verify( 0, "+0" , 2, func, 10, NO_ERR)); + C(strtoint_verify( 0, "-0" , 2, func, 10, NO_ERR)); + C(strtoint_verify( 1, "1" , 1, func, 10, NO_ERR)); + C(strtoint_verify( 1, "+1" , 2, func, 10, NO_ERR)); + C(strtoint_verify( -1, "-1" , 2, func, 10, NO_ERR)); + C(strtoint_verify( 0, "+-84" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "--84" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "-+84" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "++84" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "+ 84" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "- 84" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 10000, "10000" , 5, func, 10, NO_ERR)); + C(strtoint_verify( 10000, "+10000" , 6, func, 10, NO_ERR)); + C(strtoint_verify( -10000, "-10000" , 6, func, 10, NO_ERR)); + C(strtoint_verify( -123, " -123junk" , 5, func, 10, NO_ERR)); + C(strtoint_verify( 321, " +321dust" , 5, func, 10, NO_ERR)); + C(strtoint_verify( 99, " \f\n\r\t\v99", 8, func, 10, NO_ERR)); + C(strtoint_verify( 42, "0042" , 4, func, 10, NO_ERR)); + C(strtoint_verify( 0, "0x2A" , 1, func, 10, NO_ERR)); + C(strtoint_verify( 0, "junk" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 0, "a701" , 0, func, 10, NO_ERR)); + C(strtoint_verify( 8, "000000008" , 9, func, 10, NO_ERR)); + + /* test other bases */ + C(strtoint_verify( 10, "1010" , 4, func, 2, NO_ERR)); + C(strtoint_verify( 10, "12" , 2, func, 8, NO_ERR)); + C(strtoint_verify( 10, "A" , 1, func, 16, NO_ERR)); + C(strtoint_verify( 0, "junk" , 0, func, 0, NO_ERR)); + C(strtoint_verify( 926192, "junk" , 4, func, 36, NO_ERR)); + C(strtoint_verify( -926192, "-JuNk" , 5, func, 36, NO_ERR)); + C(strtoint_verify( 10, "012" , 3, func, 0, NO_ERR)); + C(strtoint_verify( 10, "\f0xA" , 4, func, 0, NO_ERR)); + C(strtoint_verify( -83, "-0123ABC" , 5, func, 0, NO_ERR)); + C(strtoint_verify( 0xCAFE, "0XCAFE" , 6, func, 0, NO_ERR)); + C(strtoint_verify(0b1011100010, "0b1011100010", 12, func, 2, NO_ERR)); + C(strtoint_verify(0b0100011101, "0B0100011101", 12, func, 0, NO_ERR)); + C(strtoint_verify( 0, "000000000000", 12, func, 0, NO_ERR)); + + /* test base 1 */ + C(strtoint_verify( 0, "\n000?" , 4, func, 1, NO_ERR)); + C(strtoint_verify( 0, "\t111" , 0, func, 1, NO_ERR)); + + /* test invalid bases */ + C(strtoint_verify( 0, "\f12" , 0, func, 37, NO_ERR)); + C(strtoint_verify( 0, "\v34" , 0, func, -1, NO_ERR)); + C(strtoint_verify( 0, "\r56" , 0, func, INT_MAX, NO_ERR)); + C(strtoint_verify( 0, "\t78" , 0, func, INT_MIN, NO_ERR)); + + /** + * @remarks If the first digit is '0' and the base is 0, then the + * string should be treated as octal. This also implies that the string + * contains a number, even if there are no more digits after the '0'. + * Here we test that the string has been correctly classified as + * containing a number, meaning that nptr != endptr. + */ + C(strtoint_verify( 0, "0" , 1, func, 0, NO_ERR)); + C(strtoint_verify( 0, "\n09" , 2, func, 0, NO_ERR)); + + /** + * @remarks Make sure endptr is handled correctly when there is a + * prefix without digits. + */ + C(strtoint_verify( 0, "0x" , 0, func, 0, NO_ERR)); + C(strtoint_verify( 0, "0x" , 0, func, 16, NO_ERR)); + C(strtoint_verify( 0, "\v-0b", 0, func, 0, NO_ERR)); + C(strtoint_verify( 0, "\v-0b", 0, func, 2, NO_ERR)); + + return 0; +} + +//------------------------------------------------------------------------------ +// Specific tests +//------------------------------------------------------------------------------ + int test_atoi(void) { - T( 0, atoi("" )); - T( 0, atoi("+" )); - T( 0, atoi("-" )); - T( 0, atoi("0" )); - T( 0, atoi("+0" )); - T( 0, atoi("-0" )); - T( 1, atoi("1" )); - T( 1, atoi("+1" )); - T( -1, atoi("-1" )); - T( 0, atoi("+-84" )); - T( 0, atoi("--84" )); - T( 0, atoi("-+84" )); - T( 0, atoi("++84" )); - T( 0, atoi("+ 84" )); - T( 0, atoi("- 84" )); - T( 100, atoi("100" )); - T( 100, atoi("+100" )); - T(-100, atoi("-100" )); - T(-123, atoi(" -123junk" )); - T( 321, atoi(" +321dust" )); - T( 99, atoi(" \f\n\r\t\v99")); - T( 42, atoi("0042" )); - T( 0, atoi("0x2A" )); - T( 0, atoi("junk" )); - T( 0, atoi("a701" )); - - T( INT_MIN, atoi("-8388608" )); - T( INT_MAX, atoi("8388607" )); - T( INT_MAX, atoi("+8388607" )); - T(-INT_MAX, atoi("-8388607" )); + atoint_func func = atoint_atoi; + C(atoint_verify(func, INT_MIN, "-8388608")); + C(atoint_verify(func, INT_MAX, "8388607" )); + C(atoint_verify(func, INT_MAX, "+8388607")); + C(atoint_verify(func, -INT_MAX, "-8388607")); return 0; } int test_atol(void) { - T( 0, atol("" )); - T( 0, atol("+" )); - T( 0, atol("-" )); - T( 0, atol("0" )); - T( 0, atol("+0" )); - T( 0, atol("-0" )); - T( 1, atol("1" )); - T( 1, atol("+1" )); - T( -1, atol("-1" )); - T( 0, atol("+-84" )); - T( 0, atol("--84" )); - T( 0, atol("-+84" )); - T( 0, atol("++84" )); - T( 0, atol("+ 84" )); - T( 0, atol("- 84" )); - T( 100, atol("100" )); - T( 100, atol("+100" )); - T(-100, atol("-100" )); - T(-123, atol(" -123junk" )); - T( 321, atol(" +321dust" )); - T( 99, atol(" \f\n\r\t\v99")); - T( 42, atol("0042" )); - T( 0, atol("0x2A" )); - T( 0, atol("junk" )); - T( 0, atol("a701" )); - - T( LONG_MIN, atol("-2147483648")); - T( LONG_MAX, atol("2147483647")); - T( LONG_MAX, atol("+2147483647")); - T(-LONG_MAX, atol("-2147483647")); + atoint_func func = atoint_atol; + C(atoint_verify(func, LONG_MIN, "-2147483648")); + C(atoint_verify(func, LONG_MAX, "2147483647" )); + C(atoint_verify(func, LONG_MAX, "+2147483647")); + C(atoint_verify(func, -LONG_MAX, "-2147483647")); return 0; } int test_atoll(void) { - T( 0, atoll("" )); - T( 0, atoll("+" )); - T( 0, atoll("-" )); - T( 0, atoll("0" )); - T( 0, atoll("+0" )); - T( 0, atoll("-0" )); - T( 1, atoll("1" )); - T( 1, atoll("+1" )); - T( -1, atoll("-1" )); - T( 0, atoll("+-84" )); - T( 0, atoll("--84" )); - T( 0, atoll("-+84" )); - T( 0, atoll("++84" )); - T( 0, atoll("+ 84" )); - T( 0, atoll("- 84" )); - T( 100, atoll("100" )); - T( 100, atoll("+100" )); - T(-100, atoll("-100" )); - T(-123, atoll(" -123junk" )); - T( 321, atoll(" +321dust" )); - T( 99, atoll(" \f\n\r\t\v99")); - T( 42, atoll("0042" )); - T( 0, atoll("0x2A" )); - T( 0, atoll("junk" )); - T( 0, atoll("a701" )); - - T( LLONG_MIN, atoll("-9223372036854775808")); - T( LLONG_MAX, atoll("9223372036854775807")); - T( LLONG_MAX, atoll("+9223372036854775807")); - T(-LLONG_MAX, atoll("-9223372036854775807")); + atoint_func func = atoint_atoll; + C(atoint_verify(func, LLONG_MIN, "-9223372036854775808")); + C(atoint_verify(func, LLONG_MAX, "9223372036854775807" )); + C(atoint_verify(func, LLONG_MAX, "+9223372036854775807")); + C(atoint_verify(func, -LLONG_MAX, "-9223372036854775807")); + return 0; +} + +int test_strtol(void) { + strtoint_func func = strtoint_strtol; + + C(strtoint_verify( LONG_MIN, "-2147483648", 11, func, 10, NO_ERR)); + C(strtoint_verify( LONG_MAX, "2147483648" , 10, func, 10, ERANGE)); + C(strtoint_verify( LONG_MAX, "2147483647" , 10, func, 10, NO_ERR)); + C(strtoint_verify( LONG_MAX, "+2147483647", 11, func, 10, NO_ERR)); + C(strtoint_verify(-LONG_MAX, "-2147483647", 11, func, 10, NO_ERR)); + + C(strtoint_verify( LONG_MIN, "-4294967296", 11, func, 10, ERANGE)); + C(strtoint_verify( LONG_MAX, "4294967296" , 10, func, 10, ERANGE)); + C(strtoint_verify( LONG_MAX, "4294967295" , 10, func, 10, ERANGE)); + C(strtoint_verify( LONG_MAX, "+4294967295", 11, func, 10, ERANGE)); + C(strtoint_verify( LONG_MIN, "-4294967295", 11, func, 10, ERANGE)); + + C(strtoint_verify( LONG_MIN, "-10000000000000000000000000000000", 33, func, 2, NO_ERR)); + C(strtoint_verify( LONG_MAX, " 10000000000000000000000000000000", 33, func, 2, ERANGE)); + C(strtoint_verify( LONG_MAX, "0b1111111111111111111111111111111", 33, func, 0, NO_ERR)); + C(strtoint_verify(-LONG_MAX, " -1111111111111111111111111111111", 33, func, 2, NO_ERR)); + + C(strtoint_verify( LONG_MIN, "-100000000000000000000000000000000", 34, func, 2, ERANGE)); + C(strtoint_verify( LONG_MAX, " 100000000000000000000000000000000", 34, func, 2, ERANGE)); + C(strtoint_verify( LONG_MAX, "0b11111111111111111111111111111111", 34, func, 0, ERANGE)); + C(strtoint_verify( LONG_MIN, " -11111111111111111111111111111111", 34, func, 2, ERANGE)); + + C(strtoint_verify( LONG_MIN, "-1qaz2WSX3edc4RFV5tgb", 21, func, 36, ERANGE)); + C(strtoint_verify( LONG_MAX, "+1qaz2WSX3edc4RFV5tgb", 21, func, 36, ERANGE)); + + return 0; +} + +int test_strtoul(void) { + strtoint_func func = strtoint_strtoul; + + C(strtoint_verify( LONG_MIN, "-2147483648", 11, func, 10, NO_ERR)); + C(strtoint_verify( LONG_MIN, "2147483648" , 10, func, 10, NO_ERR)); + C(strtoint_verify( LONG_MAX, "2147483647" , 10, func, 10, NO_ERR)); + C(strtoint_verify( LONG_MAX, "+2147483647", 11, func, 10, NO_ERR)); + C(strtoint_verify(-LONG_MAX, "-2147483647", 11, func, 10, NO_ERR)); + + C(strtoint_verify( -1, "-4294967296", 11, func, 10, ERANGE)); + C(strtoint_verify( -1, "4294967296" , 10, func, 10, ERANGE)); + C(strtoint_verify( -1, "4294967295" , 10, func, 10, NO_ERR)); + C(strtoint_verify( -1, "+4294967295", 11, func, 10, NO_ERR)); + C(strtoint_verify( 1, "-4294967295", 11, func, 10, NO_ERR)); + + C(strtoint_verify( LONG_MIN, "-10000000000000000000000000000000", 33, func, 2, NO_ERR)); + C(strtoint_verify( LONG_MIN, " 10000000000000000000000000000000", 33, func, 2, NO_ERR)); + C(strtoint_verify( LONG_MAX, "0b1111111111111111111111111111111", 33, func, 0, NO_ERR)); + C(strtoint_verify(-LONG_MAX, " -1111111111111111111111111111111", 33, func, 2, NO_ERR)); + + C(strtoint_verify( -1, "-100000000000000000000000000000000", 34, func, 2, ERANGE)); + C(strtoint_verify( -1, " 100000000000000000000000000000000", 34, func, 2, ERANGE)); + C(strtoint_verify( -1, "0b11111111111111111111111111111111", 34, func, 0, NO_ERR)); + C(strtoint_verify( 1, " -11111111111111111111111111111111", 34, func, 2, NO_ERR)); + + C(strtoint_verify( -1, "-1qaz2WSX3edc4RFV5tgb", 21, func, 36, ERANGE)); + C(strtoint_verify( -1, "+1qaz2WSX3edc4RFV5tgb", 21, func, 36, ERANGE)); + return 0; +} + +int test_strtoll(void) { + strtoint_func func = strtoint_strtoll; + + C(strtoint_verify( LLONG_MIN, "-9223372036854775808", 20, func, 10, NO_ERR)); + C(strtoint_verify( LLONG_MAX, "9223372036854775808" , 19, func, 10, ERANGE)); + C(strtoint_verify( LLONG_MAX, "9223372036854775807" , 19, func, 10, NO_ERR)); + C(strtoint_verify( LLONG_MAX, "+9223372036854775807", 20, func, 10, NO_ERR)); + C(strtoint_verify(-LLONG_MAX, "-9223372036854775807", 20, func, 10, NO_ERR)); + + C(strtoint_verify( LLONG_MIN, "-18446744073709551616", 21, func, 10, ERANGE)); + C(strtoint_verify( LLONG_MAX, "18446744073709551616" , 20, func, 10, ERANGE)); + C(strtoint_verify( LLONG_MAX, "18446744073709551615" , 20, func, 10, ERANGE)); + C(strtoint_verify( LLONG_MAX, "+18446744073709551615", 21, func, 10, ERANGE)); + C(strtoint_verify( LLONG_MIN, "-18446744073709551615", 21, func, 10, ERANGE)); + + C(strtoint_verify( LLONG_MIN, "-1000000000000000000000000000000000000000000000000000000000000000", 65, func, 2, NO_ERR)); + C(strtoint_verify( LLONG_MAX, " 1000000000000000000000000000000000000000000000000000000000000000", 65, func, 2, ERANGE)); + C(strtoint_verify( LLONG_MAX, "0b111111111111111111111111111111111111111111111111111111111111111", 65, func, 0, NO_ERR)); + C(strtoint_verify(-LLONG_MAX, " -111111111111111111111111111111111111111111111111111111111111111", 65, func, 2, NO_ERR)); + + C(strtoint_verify( LLONG_MIN, "-10000000000000000000000000000000000000000000000000000000000000000", 66, func, 2, ERANGE)); + C(strtoint_verify( LLONG_MAX, " 10000000000000000000000000000000000000000000000000000000000000000", 66, func, 2, ERANGE)); + C(strtoint_verify( LLONG_MAX, "0b1111111111111111111111111111111111111111111111111111111111111111", 66, func, 0, ERANGE)); + C(strtoint_verify( LLONG_MIN, " -1111111111111111111111111111111111111111111111111111111111111111", 66, func, 2, ERANGE)); + + C(strtoint_verify( LLONG_MIN, "-1qaz2WSX3edc4RFV5tgb", 21, func, 36, ERANGE)); + C(strtoint_verify( LLONG_MAX, "+1qaz2WSX3edc4RFV5tgb", 21, func, 36, ERANGE)); + + return 0; +} + +int test_strtoull(void) { + strtoint_func func = strtoint_strtoull; + + C(strtoint_verify( LLONG_MIN, "-9223372036854775808", 20, func, 10, NO_ERR)); + C(strtoint_verify( LLONG_MIN, "9223372036854775808" , 19, func, 10, NO_ERR)); + C(strtoint_verify( LLONG_MAX, "9223372036854775807" , 19, func, 10, NO_ERR)); + C(strtoint_verify( LLONG_MAX, "+9223372036854775807", 20, func, 10, NO_ERR)); + C(strtoint_verify(-LLONG_MAX, "-9223372036854775807", 20, func, 10, NO_ERR)); + + C(strtoint_verify( -1, "-18446744073709551616", 21, func, 10, ERANGE)); + C(strtoint_verify( -1, "18446744073709551616" , 20, func, 10, ERANGE)); + C(strtoint_verify( -1, "18446744073709551615" , 20, func, 10, NO_ERR)); + C(strtoint_verify( -1, "+18446744073709551615", 21, func, 10, NO_ERR)); + C(strtoint_verify( 1, "-18446744073709551615", 21, func, 10, NO_ERR)); + + C(strtoint_verify( LLONG_MIN, "-1000000000000000000000000000000000000000000000000000000000000000", 65, func, 2, NO_ERR)); + C(strtoint_verify( LLONG_MIN, " 1000000000000000000000000000000000000000000000000000000000000000", 65, func, 2, NO_ERR)); + C(strtoint_verify( LLONG_MAX, "0b111111111111111111111111111111111111111111111111111111111111111", 65, func, 0, NO_ERR)); + C(strtoint_verify(-LLONG_MAX, " -111111111111111111111111111111111111111111111111111111111111111", 65, func, 2, NO_ERR)); + + C(strtoint_verify( -1, "-10000000000000000000000000000000000000000000000000000000000000000", 66, func, 2, ERANGE)); + C(strtoint_verify( -1, " 10000000000000000000000000000000000000000000000000000000000000000", 66, func, 2, ERANGE)); + C(strtoint_verify( -1, "0b1111111111111111111111111111111111111111111111111111111111111111", 66, func, 0, NO_ERR)); + C(strtoint_verify( 1, " -1111111111111111111111111111111111111111111111111111111111111111", 66, func, 2, NO_ERR)); + + C(strtoint_verify( -1, "-1qaz2WSX3edc4RFV5tgb", 21, func, 36, ERANGE)); + C(strtoint_verify( -1, "+1qaz2WSX3edc4RFV5tgb", 21, func, 36, ERANGE)); + return 0; } +//------------------------------------------------------------------------------ +// Program +//------------------------------------------------------------------------------ + int run_tests(void) { int ret = 0; + /* basic tests of ato* and strto* routines */ + TEST_NAME(atoint_test(atoint_atoi ), "atoi" ); + TEST_NAME(atoint_test(atoint_atol ), "atol" ); + TEST_NAME(atoint_test(atoint_atoll ), "atoll" ); + TEST_NAME(atoint_test(atoint_strtol ), "strtol" ); + TEST_NAME(atoint_test(atoint_strtoul ), "strtoul" ); + TEST_NAME(atoint_test(atoint_strtoll ), "strtoll" ); + TEST_NAME(atoint_test(atoint_strtoull), "strtoull"); + + /* basic tests of strto* routines */ + TEST_NAME(strtoint_test(strtoint_strtol ), "strtol" ); + TEST_NAME(strtoint_test(strtoint_strtoul ), "strtoul" ); + TEST_NAME(strtoint_test(strtoint_strtoll ), "strtoll" ); + TEST_NAME(strtoint_test(strtoint_strtoull), "strtoull"); + + /* specific tests for each routine */ TEST(test_atoi()); TEST(test_atol()); TEST(test_atoll()); + TEST(test_strtol()); + TEST(test_strtoul()); + TEST(test_strtoll()); + TEST(test_strtoull()); + + /* others */ + TEST(test_char_to_digit()); return ret; } From b7949c3d0ad3994e3b209b6d8b385a98c2a92122 Mon Sep 17 00:00:00 2001 From: unknown <71151164+ZERICO2005@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:58:27 -0600 Subject: [PATCH 3/5] optimize llmulu_b and fix clock cycle count for lmulu_b --- src/crt/llmulu_b.src | 55 ++++++++++++++----------- src/crt/lmulu_b.src | 6 +-- src/crt/lmulu_b_fast.src | 6 +-- test/standalone/mulu_b/src/crt_wrap.asm | 17 +++++++- test/standalone/mulu_b/src/main.c | 25 +++++++++++ 5 files changed, 78 insertions(+), 31 deletions(-) diff --git a/src/crt/llmulu_b.src b/src/crt/llmulu_b.src index a2c0b9fd3..506c0bc92 100644 --- a/src/crt/llmulu_b.src +++ b/src/crt/llmulu_b.src @@ -1,68 +1,75 @@ assume adl=1 section .text + public __llmulu_b + __llmulu_b: - push af +; Multiplies BC:UDE:UHL by (SP) and returns the 64-bit unsigned product bc:ude:uhl. +; I: (SP) = 8-bit multiplier, BC:UDE:UHL = multiplicand, ADL=1 +; O: bc:ude:uhl = BC:UDE:UHL * (SP) +; CC: 101*r(PC)+21*r(SPL)+18*w(SPL)+33 +; CC: 100 bytes | 101F + 21R + 18W + 33 push iy ld iy, 0 add iy, sp push de push hl - ld a, (iy + 9) - ld h, a + push bc + ld b, (iy + 6) + ld c, 0 + ld h, b mlt hl ld (iy - 6), l - ld d, a + ld d, b ld e, (iy - 5) mlt de ld l, h - ld h, 0 + ld h, c add hl, de ld (iy - 5), l - ld d, a + ld d, b ld e, (iy - 4) mlt de ld l, h - ld h, 0 + ld h, c add hl, de ld (iy - 4), l - ld d, a + ld d, b ld e, (iy - 3) mlt de ld l, h - ld h, 0 + ld h, c add hl, de ld (iy - 3), l - ld d, a + ld d, b ld e, (iy - 2) mlt de ld l, h - ld h, 0 + ld h, c add hl, de ld (iy - 2), l - ld d, a + ld d, b ld e, (iy - 1) mlt de ld l, h - ld h, 0 + ld h, c add hl, de ld (iy - 1), l - ld d, a - ld e, c - mlt de + + pop de ld l, h - ld h, 0 + ld c, d + ld d, b + mlt bc + ld h, c + mlt de + add hl, de + ld b, h ld c, l - ld d, a - ld e, b - mlt de - ld a, h - add a, e - ld b, a + pop hl pop de pop iy - pop af ret diff --git a/src/crt/lmulu_b.src b/src/crt/lmulu_b.src index 3533f96ac..e76769481 100644 --- a/src/crt/lmulu_b.src +++ b/src/crt/lmulu_b.src @@ -9,8 +9,8 @@ __lmulu_b: ; Multiplies EUHL by A and returns the 32-bit product euhl. ; I: A=multiplier, EUHL=multiplicand, ADL=1 ; O: euhl=EUHL*A -; CC: 43*r(PC)+12*r(SPL)+9*w(SPL)+13 -; CC: 42 bytes | 43F + 12R + 9W + 13 +; CC: 43*r(PC)+12*r(SPL)+9*w(SPL)+17 +; CC: 42 bytes | 43F + 12R + 9W + 17 Mul_EUHL_A_EUHL: push bc push de @@ -45,7 +45,7 @@ Mul_EUHL_A_EUHL: add hl, hl add hl, hl add hl, hl - + mlt de ; DE = A * L add hl, de ; UHL = AH.hi + AU.lo, AH.lo + AL.hi, AL.lo diff --git a/src/crt/lmulu_b_fast.src b/src/crt/lmulu_b_fast.src index eb93969a0..16f91b23c 100644 --- a/src/crt/lmulu_b_fast.src +++ b/src/crt/lmulu_b_fast.src @@ -9,8 +9,8 @@ __lmulu_b_fast: ; Multiplies EUHL by A and returns the 32-bit product euhl. ; I: A=multiplier, EUHL=multiplicand, ADL=1 ; O: euhl=EUHL*A -; CC: 37*r(PC)+6*r(SPL)+3*w(SPL)+13 -; CC: 36 bytes | 37F + 6R + 3W + 13 +; CC: 37*r(PC)+6*r(SPL)+3*w(SPL)+17 +; CC: 36 bytes | 37F + 6R + 3W + 17 Mul_EUHL_A_EUHL: dec sp push hl @@ -42,7 +42,7 @@ Mul_EUHL_A_EUHL: add hl, hl add hl, hl add hl, hl - + mlt de ; DE = A * L add hl, de ; UHL = AH.hi + AU.lo, AH.lo + AL.hi, AL.lo diff --git a/test/standalone/mulu_b/src/crt_wrap.asm b/test/standalone/mulu_b/src/crt_wrap.asm index 9b1bcad1d..4c29370db 100644 --- a/test/standalone/mulu_b/src/crt_wrap.asm +++ b/test/standalone/mulu_b/src/crt_wrap.asm @@ -90,11 +90,26 @@ _CRT_lmulu_b_fast: ld a, (iy + 9) jp __lmulu_b_fast + public _CRT_llmulu_b +_CRT_llmulu_b: + ld iy, 0 + add iy, sp + ld l, (iy + 12) + push hl + ld hl, (iy + 3) + ld de, (iy + 6) + ld bc, (iy + 9) + call __llmulu_b + ld sp, iy + ret + extern __smulu_b extern __smulu_b_fast - + extern __imulu_b extern __imulu_b_fast extern __lmulu_b extern __lmulu_b_fast + + extern __llmulu_b diff --git a/test/standalone/mulu_b/src/main.c b/test/standalone/mulu_b/src/main.c index e120881d6..bd56fdad2 100644 --- a/test/standalone/mulu_b/src/main.c +++ b/test/standalone/mulu_b/src/main.c @@ -113,6 +113,8 @@ uint24_t CRT_imulu_b_fast(uint24_t, uint8_t); uint32_t CRT_lmulu_b(uint32_t, uint8_t); uint32_t CRT_lmulu_b_fast(uint32_t, uint8_t); +uint64_t CRT_llmulu_b(uint64_t, uint8_t); + typedef struct reg_group { union { struct { @@ -177,6 +179,14 @@ static bool test_A_UBC_UD(void) { return false; } +static bool test_A(void) { + if (prev_reg.A == next_reg.A) { + return true; + } + print_reg(); + return false; +} + int test_smulu_b(void) { for (int i = 0; i < RANDOM_TEST_COUNT; i++) { uint16_t truth, guess, x; @@ -258,6 +268,20 @@ int test_lmulu_b_fast(void) { return 0; } +int test_llmulu_b(void) { + for (int i = 0; i < RANDOM_TEST_COUNT; i++) { + uint64_t truth, guess, x; + uint8_t y; + x = rand64(); + y = rand8(); + truth = x * (uint64_t)y; + guess = CRT_llmulu_b(x, y); + CMP("%016llX", x, y, truth, guess); + C((test_A())); + } + return 0; +} + int run_tests(void) { srand(AUTOTEST_SEED); int ret = 0; @@ -267,6 +291,7 @@ int run_tests(void) { TEST(test_imulu_b_fast()); TEST(test_lmulu_b()); TEST(test_lmulu_b_fast()); + TEST(test_llmulu_b()); return ret; } From 9294f894b5ba9ca0d73a089ffb807db213625e53 Mon Sep 17 00:00:00 2001 From: unknown <71151164+ZERICO2005@users.noreply.github.com> Date: Mon, 29 Sep 2025 11:47:54 -0600 Subject: [PATCH 4/5] implemented strto(u)ll in assembly --- src/libc/atoll.src | 77 ++------- src/libc/strtoll.c | 12 -- src/libc/strtoll.src | 381 +++++++++++++++++++++++++++++++++++++++++++ src/libc/strtoull.c | 13 -- src/libc/strtox.h | 217 ------------------------ 5 files changed, 393 insertions(+), 307 deletions(-) delete mode 100644 src/libc/strtoll.c create mode 100644 src/libc/strtoll.src delete mode 100644 src/libc/strtoull.c delete mode 100644 src/libc/strtox.h diff --git a/src/libc/atoll.src b/src/libc/atoll.src index 346aade96..d9f5f4903 100644 --- a/src/libc/atoll.src +++ b/src/libc/atoll.src @@ -1,73 +1,20 @@ assume adl=1 section .text - public _atoll - _atoll: - push ix - ld ix, -3 - add ix, sp - ld hl, (ix + 9) - ; inlined isspace -.whitespace_loop: - ld a, (hl) - inc hl - cp a, 32 - jr z, .whitespace_loop - sub a, 9 - add a, -5 - jr nc, .whitespace_loop - - ; A = (HL - 1) - 9 + -5 - ; A = (HL - 1) - 14 - xor a, '-' - 14 - push af - jr z, .minus_sign - xor a, ('+' - 14) xor ('-' - 14) - jr z, .plus_sign - dec hl -.plus_sign: -.minus_sign: - ; carry is cleared - push hl - pop iy - sbc hl, hl - ex de, hl - sbc hl, hl - ld b, l - ld c, l + pop bc + ex (sp),hl + push bc + ld bc,10 + push bc + ld c,b + push bc push hl - push hl - push hl - ; IY = start of the digits - ; BC:UDE:UHL = 0 - ; (ix - 9) = [0, 10] - jr .start -.loop: - ; loop : 27F + 1R + 8W + 1 - ; lladd : ? - ; llmulu: ? - ; total : a lot per digit - ld (ix - 9), 10 - call __llmulu ; BC:UDE:UHL *= 10 - ld (ix - 9), a - call __lladd - inc iy ; next digit -.start: - ld a, (iy) - sub a, 48 - cp a, 10 - jr c, .loop -.finish: - ld sp, ix + call _strtoll + pop af + pop af pop af - ; carry is cleared - pop ix - ret nz ; A != '-' positive - ; A == '-' negative - jp __llneg + ret - extern __llneg - extern __lladd - extern __llmulu + extern _strtoll diff --git a/src/libc/strtoll.c b/src/libc/strtoll.c deleted file mode 100644 index 91b383074..000000000 --- a/src/libc/strtoll.c +++ /dev/null @@ -1,12 +0,0 @@ -#define STRTOX_TYPE long long -#define STRTOX_MAX LLONG_MAX -#define STRTOX_MIN LLONG_MIN - -#define STRTOX_SIGNED 1 -#define STRTOX_NAME strtoll - -#include "strtox.h" - -#if __INTMAX_WIDTH__ == __LLONG_WIDTH__ -__INTMAX_TYPE__ strtoimax(const char *, char **, int) __attribute__((alias("strtoll"))); -#endif /* __INTMAX_WIDTH__ == __LLONG_WIDTH__ */ diff --git a/src/libc/strtoll.src b/src/libc/strtoll.src new file mode 100644 index 000000000..143518dd7 --- /dev/null +++ b/src/libc/strtoll.src @@ -0,0 +1,381 @@ + assume adl=1 + +;------------------------------------------------------------------------------- + + section .text + + public _strtoll + public _strtoimax + +_strtoimax: +_strtoll: + call __strtoll_common + ; overflow if Carry is set + jr c, .out_of_range + ld a, b + rla + jr c, .maybe_out_of_range + ret nz + jp __llneg + +.maybe_out_of_range: + ; greater than INT_MAX + jr nz, .overflow + ; negative + ; check that the result is not an exact INT_MIN + ld b, a ; B = (B << 1) + call __llcmpzero + set 7, b + ret z ; exact INT_MIN +.underflow: + xor a, a ; set Z +.out_of_range: +.overflow: + ld b, $80 + ld de, 0 + ld c, e + ld hl, 5 ; ERANGE + ld (_errno), hl + ld l, h ; ld hl, 0 + ret z ; underflow + ; overflow + dec bc + dec hl + dec de + ret + +;------------------------------------------------------------------------------- + + section .text + + public _strtoull + public _strtoumax + +_strtoumax: +_strtoull: + call __strtoll_common + ; overflow if Carry is set + jr c, .out_of_range + ret nz + jp __llneg + +.out_of_range: + sbc hl, hl + ex de, hl + ld hl, 5 ; ERANGE + ld (_errno), hl + ld l, h + dec hl + ld b, e + ld c, e + ret + +;------------------------------------------------------------------------------- + + section .text + + private __strtoll_common + +__strtoll_common: + ; output: BC:UDE:UHL + ; NC = no overflow for strtoull + ; C = overflow, raise ERANGE + ; Z means that A is zero = negate return value + ; NZ means that A is non-zero = positive return value + push ix + ld ix, 0 + lea hl, ix - 37 ; ld hl, -37 + add ix, sp + + ld bc, (ix + 15) ; base + add hl, bc + jr c, .invalid_base + ; UBC is zero here + ld b, c ; store the base in B to allow for djnz hax + ld hl, (ix + 9) ; nptr +;------------------------------------------------------------------------------- +; consume whitespace (inlinsed isspace) +.whitespace_loop: + ld a, (hl) + inc hl + cp a, 32 + jr z, .whitespace_loop + sub a, 9 + add a, -5 + jr nc, .whitespace_loop +; test for plus/minus signs + ; A = (HL - 1) - 9 + -5 + ; A = (HL - 1) - 14 + xor a, '-' - 14 + push af + jr z, .minus_sign + xor a, ('+' - 14) xor ('-' - 14) + jr z, .plus_sign + dec hl + xor a, a +.plus_sign: +.minus_sign: + ; A = 0, (HL) = start of number +;------------------------------------------------------------------------------- +; update the base if needed + or a, b ; base + jr z, .auto_base + xor a, 16 + jr z, .hex_base + xor a, 2 xor 16 + jr nz, .other_base +.auto_base: ; test for 0* 0x* 0X* 0b* 0B* +.bin_base: ; test for 0x* 0X* +.hex_base: ; test for 0b* 0B* + inc b ; djnz hax + ld a, (hl) + xor a, '0' + jr nz, .maybe_decimal + inc hl + ld a, (hl) + res 5, a ; upper case + xor a, 'X' + jr z, .maybe_hex + xor a, 'B' xor 'X' + jr z, .maybe_bin + dec hl + djnz .other_base + ld b, 8 ; octal + jr .save_new_base + +.maybe_bin: + bit 4, b + jr nz, .undo_inc ; hexadecimal + ; base is 0 or 2 + inc hl + ld b, 2 + jr .save_new_base + +.maybe_hex: + bit 1, b + jr nz, .undo_inc ; binary + ; base is 0 or 16 + inc hl + ld b, 16 + jr .save_new_base + +.undo_inc: + dec hl + ; dec b + ; jr .other_base +.maybe_decimal: + ; set to decimal if base is not zero + djnz .other_base + ld b, 10 ; decimal +.save_new_base: +;------------------------------------------------------------------------------- +.other_base: + ld a, (hl) ; first digit of the number + push hl + pop iy +.invalid_base_hijack: + ; or a, a ; carry is cleared here + sbc hl, hl + ex de, hl + sbc hl, hl + ld c, l + ; A = first digit of the number + ; BC:UDE:UHL = 0 + ; (ix - 1) = base + ; (ix - 2) = (first-non-whitespace) XOR '-' + ; 6, (ix - 3) = Z if result should be negative, NZ for positive + ; 0, (ix - 3) = overflow bit + + ; The strto* functions return nptr (not nptr + whitespace) if there are + ; no digits in the string. Having a digit check here allows us to + ; directly handle the case where the string has no digits. + + sub a, 48 + cp a, 10 + jr c, .check_digit + ; Convert an alphabetic digit, case-insensitive + sub a, 65 - 48 + res 5, a + add a, 10 +.check_digit: + ; End the loop when the digit is out of range for the base + cp a, b + ld (ix - 1), b ; store base + ld b, l ; now BC:UDE:UHL is zero + jr c, .loop +;------------------------------------------------------------------------------- +; no digit found or invalid base + ; set *endptr to nptr and return 0 + ld iy, (ix + 9) ; nptr + jr .write_endptr +.invalid_base: + xor a, a + ld b, a + ; Setting B (base) to zero ensures that cp a, b will never set carry. + ; forcing the function to return. + push af + ; sets E:UHL to zero + jr .invalid_base_hijack +;------------------------------------------------------------------------------- +; CC per non-decimal digit: +; minimum : 100F + 20R + 18W + 35 +; low average : 102F + 20R + 18W + 36 +; high average : 112F + 20R + 18W + 37 ; an over-estimate of the average CC +; maximum : 127F + 20R + 18W + 40 +; overflow max : 131F + 21R + 19W + 42 +.check_decimal: + cp a, (ix - 1) + jr nc, .end_loop +.loop: + call __llmul_add_b_overflow +.next_digit: + ; IY = str, BC:UDE:UHL = accumulator, 0, (ix - 3) = overflow bit + inc iy + ; Convert a numerical digit + ld a, (iy) + sub a, 48 + cp a, 10 + jr c, .check_decimal + ; Convert an alphabetic digit, case-insensitive + sub a, 65 - 48 + res 5, a + add a, 10 + ; End the loop when the digit is out of range for the base + cp a, (ix - 1) + jr c, .loop +.end_loop: +;------------------------------------------------------------------------------- +.write_endptr: + push hl + ld hl, (ix + 12) ; endptr + add hl, de + or a, a + sbc hl, de + jr z, .endptr_null + ld (hl), iy +.endptr_null: + pop hl + pop af ; overflow and sign flags + pop ix + ret + +;------------------------------------------------------------------------------- + + private __llmul_add_b_overflow +__llmul_add_b_overflow: +; BC:UDE:UHL = (BC:UDE:UHL * (ix - 1)) + A +; bit 0, (ix - 3) is set if overflow has occured +.__llmulu_b_overflow: +; inlined/modified __llmulu_b +; CC if no overflow: 70F + 15R + 15W + 32 + push de ; (ix - 9) + push hl ; (ix - 12) + push bc ; (ix - 15) + ld b, (ix - 1) + ld c, 0 + ld h, b + mlt hl + ld (ix - 12), l + ld d, b + ld e, (ix - 11) + mlt de + ld l, h + ld h, c + add hl, de + ld (ix - 11), l + ld d, b + ld e, (ix - 10) + mlt de + ld l, h + ld h, c + add hl, de + ld (ix - 10), l + ld d, b + ld e, (ix - 9) + mlt de + ld l, h + ld h, c + add hl, de + ld (ix - 9), l + ld d, b + ld e, (ix - 8) + mlt de + ld l, h + ld h, c + add hl, de + ld (ix - 8), l + ld d, b + ld e, (ix - 7) + mlt de + ld l, h + ld h, c + add hl, de + ld (ix - 7), l + + pop de + ld l, h + ld c, d + ld d, b + mlt bc + ld h, c + mlt de + + add.s hl, de + jr c, .set_overflow_bit + inc b + djnz .set_overflow_bit + ld b, h + ld c, l + pop hl + pop de +.__lladd_b_overflow: +; inlined/modified __lladd_b_fast +; NC L: 3F + 3R + 2 (49.8046875%) +; NC H: 5F + 3R + 3 (49.9992370605%) +; All other cases: (0.196075439453%) +; NC UHL: 15F + 3R + 0W + 4 +; NC E: 17F + 3R + 0W + 5 +; NC D: 19F + 3R + 0W + 6 +; NC UDE: 26F + 3R + 0W + 7 +; NC BC: 30F + 3R + 0W + 7 +; Overflow: 34F + 4R + 1W + 9 +; Average : 5F + 3R + 0W + 3 (Rounded up and assuming no overflow) + add a, l ; a=L+A + ld l, a ; l=L+A + ret nc ; cf=1 + inc h ; h=H+1 + ret nz ; h=0 + dec h + ld l, h ; uhl=(HLU<<16)+0xFFFF + inc hl ; uhl=HLU+1<<16 + add hl, bc + or a, a + sbc hl, bc + ld l, a ; uhl=(HLU+1<<16)+(L+A&0xFF) + ret nz ; uhl=L+A&0xFF, cf=0 + inc e ; e=E+1 + ret nz ; e=0 + inc d ; d=D+1 + ret nz ; d=0 + dec d + ld e, d ; ude=(DEU<<16)+0xFFFF + inc de ; ude=DEU+1<<16 + sbc hl, de + add hl, de + ret c ; ude=0 + inc bc ; ubc=UBC+1 + ; test for overflow + ld a, b + or a, c + ret nz +.finish_overflow: + set 0, (ix - 3) ; set carry + ret +.set_overflow_bit: + pop hl + pop de + jr .finish_overflow + + extern _errno + extern __llneg + extern __llcmpzero diff --git a/src/libc/strtoull.c b/src/libc/strtoull.c deleted file mode 100644 index ea2550b29..000000000 --- a/src/libc/strtoull.c +++ /dev/null @@ -1,13 +0,0 @@ -#define STRTOX_TYPE long long -#define STRTOX_MAX ULLONG_MAX -#define STRTOX_MIN ULLONG_MAX - -#define STRTOX_SIGNED 0 -#define STRTOX_NAME strtoull - -#include "strtox.h" - -/* not sure why __ULLONG_WIDTH__ isn't defined */ -#if __UINTMAX_WIDTH__ == __LLONG_WIDTH__ -__UINTMAX_TYPE__ strtoumax(const char *, char **, int) __attribute__((alias("strtoull"))); -#endif /* __UINTMAX_WIDTH__ == __LLONG_WIDTH__ */ diff --git a/src/libc/strtox.h b/src/libc/strtox.h deleted file mode 100644 index dbfebc61a..000000000 --- a/src/libc/strtox.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) 2015-2025 CE Programming - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * MODIFICATIONS - * Made generic interface - * Add back in errno range handling - * - */ -/* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. - * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ -/* Copyright (c) 1995 NeXT Computer, Inc. All rights reserved. - * - * strol.c - The functions strtol() & strtoul() are exported as public API - * via the header file ~driverkit/generalFuncs.h - * - * HISTORY - * 25-Oct-1995 Dean Reece at NeXT - * Created based on BSD4.4's strtol.c & strtoul.c. - * Removed dependency on _ctype_ by static versions of isupper()... - * Added support for "0b101..." binary constants. - * Commented out references to errno. - */ - -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifdef STRTOX_TYPE -#include -#include -#include -#include -#include -#include -#include - -#if !STRTOX_SIGNED -unsigned -#endif -STRTOX_TYPE STRTOX_NAME(const char *nptr, char **endptr, int base) -{ - unsigned STRTOX_TYPE acc; - unsigned STRTOX_TYPE cutoff; - unsigned char cutlim; - unsigned char c; - unsigned char b; - const char *s; - bool neg; - char any; - - if (base < 0 || base > 36) - { - if (endptr) - { - *endptr = (char *)nptr; - } - - return 0; - } - - b = base; - s = nptr; - neg = false; - - do - { - c = *s++; - } while (isspace(c)); - - if (c == '-') - { - neg = true; - c = *s++; - } - else if (c == '+') - { - c = *s++; - } - - if ((b == 0 || b == 16) && c == '0' && (*s == 'x' || *s == 'X')) - { - c = s[1]; - s += 2; - b = 16; - } - else if ((b == 0 || b == 2) && c == '0' && (*s == 'b' || *s == 'B')) - { - c = s[1]; - s += 2; - b = 2; - } - - if (b == 0) - { - b = c == '0' ? 8 : 10; - } - -#if STRTOX_SIGNED - cutoff = neg ? -(unsigned STRTOX_TYPE)(STRTOX_MIN) : STRTOX_MAX; - cutlim = cutoff % b; - cutoff /= b; -#else - cutoff = STRTOX_MAX / b; - cutlim = STRTOX_MAX % b; -#endif - - for (acc = 0, any = 0;; c = *s++) - { - if (isdigit(c)) - { - c -= '0'; - } - else if (isalpha(c)) - { - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - } - else - { - break; - } - if (c >= b) - { - break; - } - - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) - { - any = -1; - } - else - { - any = 1; - acc *= b; - acc += c; - } - } - - if (any < 0) - { - acc = neg ? STRTOX_MIN : STRTOX_MAX; - errno = ERANGE; - } - else if (neg) - { - acc = -acc; - } - - if (endptr) - { - *endptr = (char *)(any ? s - 1 : nptr); - } - - return acc; -} -#endif From 89f16fe496a2451f474d1d0d085a8cf8cbf5197b Mon Sep 17 00:00:00 2001 From: unknown <71151164+ZERICO2005@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:18:03 -0600 Subject: [PATCH 5/5] rename strtol folder --- test/standalone/{atol => strtol}/autotest.json | 0 test/standalone/{atol => strtol}/makefile | 0 test/standalone/{atol => strtol}/src/char_to_digit.asm | 0 test/standalone/{atol => strtol}/src/main.c | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename test/standalone/{atol => strtol}/autotest.json (100%) rename test/standalone/{atol => strtol}/makefile (100%) rename test/standalone/{atol => strtol}/src/char_to_digit.asm (100%) rename test/standalone/{atol => strtol}/src/main.c (100%) diff --git a/test/standalone/atol/autotest.json b/test/standalone/strtol/autotest.json similarity index 100% rename from test/standalone/atol/autotest.json rename to test/standalone/strtol/autotest.json diff --git a/test/standalone/atol/makefile b/test/standalone/strtol/makefile similarity index 100% rename from test/standalone/atol/makefile rename to test/standalone/strtol/makefile diff --git a/test/standalone/atol/src/char_to_digit.asm b/test/standalone/strtol/src/char_to_digit.asm similarity index 100% rename from test/standalone/atol/src/char_to_digit.asm rename to test/standalone/strtol/src/char_to_digit.asm diff --git a/test/standalone/atol/src/main.c b/test/standalone/strtol/src/main.c similarity index 100% rename from test/standalone/atol/src/main.c rename to test/standalone/strtol/src/main.c