From a8a302299a7c7eab0dccf605bb73807971c75faf Mon Sep 17 00:00:00 2001 From: Brendan Fletcher Date: Sat, 4 Oct 2025 15:11:38 -0400 Subject: [PATCH] Implement strspn, strcspn, strpbrk in place of broken OS functions. Fixes #646 --- src/libc/os.src | 6 ---- src/libc/strcspn.src | 50 ++++++++++++++++++++++++++ src/libc/strspn.src | 38 ++++++++++++++++++++ test/issues/646/autotest.json | 36 +++++++++++++++++++ test/issues/646/makefile | 15 ++++++++ test/issues/646/src/main.c | 67 +++++++++++++++++++++++++++++++++++ 6 files changed, 206 insertions(+), 6 deletions(-) create mode 100644 src/libc/strcspn.src create mode 100644 src/libc/strspn.src create mode 100644 test/issues/646/autotest.json create mode 100644 test/issues/646/makefile create mode 100644 test/issues/646/src/main.c diff --git a/src/libc/os.src b/src/libc/os.src index 0eea559ee..19b6b3be8 100644 --- a/src/libc/os.src +++ b/src/libc/os.src @@ -16,16 +16,10 @@ _strcat := 0000C0h _strchr := 0000C4h public _strcpy _strcpy := 0000CCh - public _strcspn -_strcspn := 0000D0h public _strncat _strncat := 0000D8h public _strncpy _strncpy := 0000E0h - public _strpbrk -_strpbrk := 0000E4h - public _strspn -_strspn := 0000ECh public _strstr _strstr := 0000F0h public _strtok diff --git a/src/libc/strcspn.src b/src/libc/strcspn.src new file mode 100644 index 000000000..10c09ca56 --- /dev/null +++ b/src/libc/strcspn.src @@ -0,0 +1,50 @@ + assume adl=1 + + section .text + public _strcspn +_strcspn: + call __strcspn_strpbrk_common + sbc hl, de ; Calculate number of mismatching characters + ret + + section .text + public _strpbrk +_strpbrk: + call __strcspn_strpbrk_common + ret nz ; Return pointer if not end of string + sbc hl, hl ; Return NULL + ret + + section .text + private __strcspn_strpbrk_common +__strcspn_strpbrk_common: + ld hl, 6 + add hl, sp + ld de, (hl) ; DE = str + ld bc, 3 + add hl, bc ; Always resets carry + ld hl, (hl) ; HL = reject + + ; Calculate strlen(reject) + push hl + xor a, a + ld c, a + cpir + sbc hl, hl + sbc hl, bc + ex (sp), hl + pop iy ; IY = strlen(reject) + 1 + dec hl ; HL = reject - 1 + push de +.loop: + ld a, (de) ; A = *str++ + inc de + lea bc, iy ; BC = strlen(reject) + 1 + add hl, bc ; HL = reject + strlen(reject) + cpdr ; Find A in reject, including null terminator + jr nz, .loop ; Loop if no match + ex de, hl + dec hl ; HL = pointer to matching character + pop de ; DE = str + or a, a ; Check if A is null terminator and reset carry + ret diff --git a/src/libc/strspn.src b/src/libc/strspn.src new file mode 100644 index 000000000..ee15a5ac5 --- /dev/null +++ b/src/libc/strspn.src @@ -0,0 +1,38 @@ + assume adl=1 + + section .text + public _strspn +_strspn: + xor a, a + sbc hl, hl + add hl, sp + ld bc, 3 + add hl, bc + ld de, (hl) ; DE = str + add hl, bc ; Always resets carry + ld iy, (hl) ; IY = accept + + ; Calculate strlen(accept) + ld c, a + lea hl, iy + cpir + sbc hl, hl + scf + sbc hl, bc ; Always sets carry + ret z ; Return 0 if accept is empty + + push hl + ex (sp), ix ; IX = strlen(accept) + push de +.loop: + ld a, (de) ; A = *str++ + inc de + lea hl, iy ; HL = accept + lea bc, ix ; BC = strlen(accept) + cpir ; Find A in accept + jr z, .loop ; Loop if match + ex de, hl ; Calculate number of matching characters + pop de + sbc hl, de ; Input carry is set + pop ix + ret diff --git a/test/issues/646/autotest.json b/test/issues/646/autotest.json new file mode 100644 index 000000000..c98ab5038 --- /dev/null +++ b/test/issues/646/autotest.json @@ -0,0 +1,36 @@ +{ + "transfer_files": + [ + "bin/DEMO.8xp" + ], + "target": + { + "name": "DEMO", + "isASM": true + }, + "sequence": + [ + "action|launch", + "delay|100", + "hashWait|1", + "key|enter", + "hashWait|2" + ], + "hashes": + { + "1": + { + "description": "Test functions", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ "36DD9C48" ] + }, + "2": + { + "description": "Test program exit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ "FFAF89BA", "101734A5", "9DA19F44", "A32840C8", "349F4775" ] + } + } +} diff --git a/test/issues/646/makefile b/test/issues/646/makefile new file mode 100644 index 000000000..8c3d9e447 --- /dev/null +++ b/test/issues/646/makefile @@ -0,0 +1,15 @@ +# ---------------------------- +# Makefile Options +# ---------------------------- + +NAME = DEMO +ICON = icon.png +DESCRIPTION = "CE C Toolchain Demo" +COMPRESSED = NO + +CFLAGS = -Wall -Wextra -Oz -fno-builtin-strspn -fno-builtin-strcspn -fno-builtin-strpbrk +CXXFLAGS = -Wall -Wextra -Oz -fno-builtin-strspn -fno-builtin-strcspn -fno-builtin-strpbrk + +# ---------------------------- + +include $(shell cedev-config --makefile) diff --git a/test/issues/646/src/main.c b/test/issues/646/src/main.c new file mode 100644 index 000000000..2731fa7ed --- /dev/null +++ b/test/issues/646/src/main.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +#define C(expr) if (!(expr)) { return __LINE__; } +static int libc_test(void) { + + char const * str = "abcdef"; + char const * empty = ""; + + C(strspn(str, "abc") == 3); + C(strspn(str, "cba") == 3); + C(strspn(str, "def") == 0); + C(strspn(str, "fed") == 0); + C(strspn(str, "ABCDEF") == 0); + C(strspn(str, "bbeebe") == 0); + C(strspn(str, "eebbeb") == 0); + C(strspn(str, "aaffaf") == 1); + C(strspn(str, "ffaafa") == 1); + C(strspn(str, str) == 6); + C(strspn(str, empty) == 0); + C(strspn(empty, str) == 0); + C(strspn(empty, empty) == 0); + + C(strcspn(str, "abc") == 0); + C(strcspn(str, "cba") == 0); + C(strcspn(str, "def") == 3); + C(strcspn(str, "fed") == 3); + C(strcspn(str, "ABCDEF") == 6); + C(strcspn(str, "bbeebe") == 1); + C(strcspn(str, "eebbeb") == 1); + C(strcspn(str, "aaffaf") == 0); + C(strcspn(str, "ffaafa") == 0); + C(strcspn(str, str) == 0); + C(strcspn(str, empty) == 6); + C(strcspn(empty, str) == 0); + C(strcspn(empty, empty) == 0); + + C(strpbrk(str, "abc") == str + 0); + C(strpbrk(str, "cba") == str + 0); + C(strpbrk(str, "def") == str + 3); + C(strpbrk(str, "fed") == str + 3); + C(strpbrk(str, "ABCDEF") == NULL); + C(strpbrk(str, "bbeebe") == str + 1); + C(strpbrk(str, "eebbeb") == str + 1); + C(strpbrk(str, "aaffaf") == str + 0); + C(strpbrk(str, "ffaafa") == str + 0); + C(strpbrk(str, str) == str); + C(strpbrk(str, empty) == NULL); + C(strpbrk(empty, str) == NULL); + C(strpbrk(empty, empty) == NULL); + + return 0; +} +#undef C + +int main(void) +{ + os_ClrHome(); + + printf("%d\n", libc_test()); + + os_GetKey(); + + return 0; +}