From 84d69b415da0c8a4e28c3f6369c1bf91670b23bc Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Tue, 6 Jan 2026 12:54:23 -0700 Subject: [PATCH 01/15] UNSTABLE | added draft libc functions --- src/libc/memchr.src | 32 ++++++++++++++++++++ src/libc/memcmp.src | 42 +++++++++++++++++++++++++++ src/libc/os.src | 21 -------------- src/libc/strcat.src | 44 ++++++++++++++++++++++++++++ src/libc/strchr.src | 42 +++++++++++++++++++++++++++ src/libc/strcpy.src | 39 +++++++++++++++++++++++++ src/libc/strncpy.src | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/libc/strstr.src | 51 ++++++++++++++++++++++++++++++++ 8 files changed, 319 insertions(+), 21 deletions(-) create mode 100644 src/libc/memchr.src create mode 100644 src/libc/memcmp.src create mode 100644 src/libc/strcat.src create mode 100644 src/libc/strchr.src create mode 100644 src/libc/strcpy.src create mode 100644 src/libc/strncpy.src create mode 100644 src/libc/strstr.src diff --git a/src/libc/memchr.src b/src/libc/memchr.src new file mode 100644 index 000000000..08330d4bd --- /dev/null +++ b/src/libc/memchr.src @@ -0,0 +1,32 @@ + .assume adl=1 + + .section .text + + .global _memchr + .type _memchr, @function + +.ifdef PREFER_CE_LIBC + + .set _memchr, 0x00009C + +.else + +_memchr: + ld iy, -1 + add iy, sp + ld bc, (iy + 10) + sbc hl, hl + add hl, bc + jr nc, .L.ret_zero + ld hl, (iy + 4) + ld a, (iy + 7) + cpir + dec hl + ret z ; found match + or a, a +.L.ret_zero: + ; return NULL + sbc hl, hl + ret + +.endif diff --git a/src/libc/memcmp.src b/src/libc/memcmp.src new file mode 100644 index 000000000..71c0fc60f --- /dev/null +++ b/src/libc/memcmp.src @@ -0,0 +1,42 @@ + .assume adl=1 + + .section .text + + .global _memcmp + .type _memcmp, @function + +.ifdef PREFER_CE_LIBC + + .set _memcmp, 0x0000A0 + +.else + +_memcmp: + ld iy, 0 + add iy, sp + ld bc, (iy + 9) + sbc hl, hl + adc hl, bc + ld hl, (iy + 6) + ld de, (iy + 3) + call nz, .L.start ; z means BC was zero +.L.ret_zero: + sbc hl, hl + ret + +.L.loop: + ret po + inc de +.L.start: + ld a, (de) + cpi + jr z, .L.loop +.L.finish: + ld sp, iy ; ret + dec hl + sub a, (hl) + sbc hl, hl + ld l, a + ret + +.endif diff --git a/src/libc/os.src b/src/libc/os.src index c1df4c8a4..d4cab2a1d 100644 --- a/src/libc/os.src +++ b/src/libc/os.src @@ -4,33 +4,12 @@ .global _longjmp .type _longjmp, @function .set _longjmp, 0x000098 - .global _memchr - .type _memchr, @function - .set _memchr, 0x00009C - .global _memcmp - .type _memcmp, @function - .set _memcmp, 0x0000A0 .global _setjmp .type _setjmp, @function .set _setjmp, 0x0000B8 - .global _strcat - .type _strcat, @function - .set _strcat, 0x0000C0 - .global _strchr - .type _strchr, @function - .set _strchr, 0x0000C4 - .global _strcpy - .type _strcpy, @function - .set _strcpy, 0x0000CC .global _strncat .type _strncat, @function .set _strncat, 0x0000D8 - .global _strncpy - .type _strncpy, @function - .set _strncpy, 0x0000E0 - .global _strstr - .type _strstr, @function - .set _strstr, 0x0000F0 .global _strtok .type _strtok, @function .set _strtok, 0x0000F4 diff --git a/src/libc/strcat.src b/src/libc/strcat.src new file mode 100644 index 000000000..6fc046a9c --- /dev/null +++ b/src/libc/strcat.src @@ -0,0 +1,44 @@ + .assume adl=1 + + .section .text + + .global _strcat + .type _strcat, @function + +.ifdef PREFER_CE_LIBC + + .set _strcat, 0x0000C0 + +.else + +_strcat: + pop bc + pop hl + pop de + push de + push hl + push bc + + push hl ; return value + ; move to the end of dst + xor a, a + ld bc, 0 + cpir + dec hl + ex de, hl + ; move to the end of src + cpir + add hl, bc ; rewind to the beginning of src + push hl + sbc hl, hl + sbc hl, bc + ex (sp), hl + pop bc + ; DE = dst + strlen(dst) + 1 + ; HL = src + ; BC = strlen(src) + 1 + ldir + pop hl ; return dst + ret + +.endif diff --git a/src/libc/strchr.src b/src/libc/strchr.src new file mode 100644 index 000000000..9691fb33f --- /dev/null +++ b/src/libc/strchr.src @@ -0,0 +1,42 @@ + .assume adl=1 + + .section .text + + .global _strchr + .type _strchr, @function + +.ifdef PREFER_CE_LIBC + + .set _strchr, 0x0000C4 + +.else + +_strchr: + pop bc + pop hl + pop de + push de + push hl + push bc + xor a, a + ld bc, 0 + cpir + ld a, e + ex de, hl + ; calculate strlen(str) + 1 + sbc hl, hl + sbc hl, bc + ex de, hl + ; rewind HL to the beginning of str + add hl, bc + push de + pop bc + cpir + dec hl + ret z ; found match + ; return NULL + or a, a + sbc hl, hl + ret + +.endif diff --git a/src/libc/strcpy.src b/src/libc/strcpy.src new file mode 100644 index 000000000..4e547c848 --- /dev/null +++ b/src/libc/strcpy.src @@ -0,0 +1,39 @@ + .assume adl=1 + + .section .text + + .global _strcpy + .type _strcpy, @function + +.ifdef PREFER_CE_LIBC + + .set _strcpy, 0x0000CC + +.else + +_strcpy: + pop bc + pop de + ex (sp), hl + push de + push bc + + push de ; return value + xor a, a + ld bc, 0 + ; move to the end of src + cpir + add hl, bc ; rewind to the beginning of src + push hl + sbc hl, hl + sbc hl, bc + ex (sp), hl + pop bc + ; DE = dst + ; HL = src + ; BC = strlen(src) + 1 + ldir + pop hl ; return dst + ret + +.endif diff --git a/src/libc/strncpy.src b/src/libc/strncpy.src new file mode 100644 index 000000000..6102fdebc --- /dev/null +++ b/src/libc/strncpy.src @@ -0,0 +1,69 @@ + .assume adl=1 + + .section .text + + .global _strncpy + .type _strncpy, @function + +.ifdef PREFER_CE_LIBC + + .set _strncpy, 0x0000E0 + +.else + +; currently this just copies the stpncpy implementation, and could probably be optimized further +_strncpy: + ld iy, 0 + add iy, sp + ld bc, (iy + 9) ; max_len + + ; inlined strnlen + xor a, a + sbc hl, hl + sbc hl, bc + jr z, .L.zero_size + add hl, bc + ld de, (iy + 6) ; src + sbc hl, de + ex de, hl + cpir + jr z, .L.finish_strnlen + inc hl +.L.finish_strnlen: + xor a, a + adc hl, de +.L.zero_size: + + ; copy strnlen bytes from src + push hl + ld de, (iy + 3) ; dst + jr z, .L.zero_byte_copy + ld hl, (iy + 6) ; src + pop bc + push bc + ldir +.L.zero_byte_copy: + pop bc + + ; zero pad the remainder + ld hl, (iy + 9) ; max_len + scf + sbc hl, bc ; clear_size - 1 = max_len - src_len - 1 + ex de, hl + jr c, .L.finish ; clear_size <= 0 (or max_len <= src_len) + ; HL = dst + src_len + ; DE = clear_size - 1 + add hl, de + ld (hl), a + jr z, .L.finish ; clear_size == 1 + push de + pop bc + push hl + pop de + dec de + lddr +.L.finish: + ld hl, (iy + 3) + ret + +.endif diff --git a/src/libc/strstr.src b/src/libc/strstr.src new file mode 100644 index 000000000..74be8af41 --- /dev/null +++ b/src/libc/strstr.src @@ -0,0 +1,51 @@ + .assume adl=1 + + .section .text + + .global _strstr + .type _strstr, @function + +.ifdef PREFER_CE_LIBC + + .set _strstr, 0x0000F0 + +.else + +; strstr can probably be optimized further beyond just wrapping memmem +_strstr: + pop bc + pop de + ex (sp), hl + push de + push bc + + push hl + xor a, a + ld bc, 0 + cpir + sbc hl, hl + dec hl + sbc hl, bc ; carry will be cleared + ex (sp), hl ; strlen(needle) + push hl ; needle + + ex de, hl + push hl + ld bc, 0 + cpir + sbc hl, hl + dec hl + sbc hl, bc + ex (sp), hl ; strlen(haystack) + push hl ; haystack + + call _memmem + pop bc + pop bc + pop bc + pop bc + ret + + .extern _memmem + +.endif From df1b84557d165c41ff400f1ca480ae10feb363cc Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:40:15 -0700 Subject: [PATCH 02/15] fixup string.h tests --- test/standalone/asprintf_fprintf/src/main.c | 125 +++++++++++--- test/standalone/asprintf_fprintf/src/rename.s | 160 +++++++++++++++--- 2 files changed, 234 insertions(+), 51 deletions(-) diff --git a/test/standalone/asprintf_fprintf/src/main.c b/test/standalone/asprintf_fprintf/src/main.c index 181d5f8f7..6e12a771f 100644 --- a/test/standalone/asprintf_fprintf/src/main.c +++ b/test/standalone/asprintf_fprintf/src/main.c @@ -31,19 +31,24 @@ extern void* NULL_ptr; // prevents Clang from replacing function calls with builtins #if 1 -void *T_memcpy(void *__restrict dest, const void *__restrict src, size_t n) - __attribute__((nonnull(1, 2))); +void T_bzero(void* s, size_t n); -void *T_memmove(void *dest, const void *src, size_t n) +void *T_memccpy(void *__restrict dest, const void *__restrict src, int c, size_t n) __attribute__((nonnull(1, 2))); -void *T_memset(void *s, int c, size_t n) +void *T_memchr(const void *s, int c, size_t n) __attribute__((nonnull(1))); int T_memcmp(const void *s1, const void *s2, size_t n) __attribute__((nonnull(1, 2))); -void *T_memccpy(void *__restrict dest, const void *__restrict src, int c, size_t n) +void *T_memcpy(void *__restrict dest, const void *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +void *T_memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) + __attribute__((nonnull(1, 3))); + +void *T_memmove(void *dest, const void *src, size_t n) __attribute__((nonnull(1, 2))); void *T_mempcpy(void *__restrict dest, const void *__restrict src, size_t n) @@ -52,58 +57,97 @@ void *T_mempcpy(void *__restrict dest, const void *__restrict src, size_t n) void *T_memrchr(const void *s, int c, size_t n) __attribute__((nonnull(1))); -void *T_memmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) - __attribute__((nonnull(1, 3))); - void *T_memrmem(const void *haystack, size_t haystack_len, const void *needle, size_t needle_len) __attribute__((nonnull(1, 3))); +void *T_memset(void *s, int c, size_t n) + __attribute__((nonnull(1))); + char *T_stpcpy(char *__restrict dest, const char *__restrict src) __attribute__((nonnull(1, 2))); char *T_stpncpy(char *__restrict dest, const char *__restrict src, size_t n) __attribute__((nonnull(1, 2))); +char *T_strcat(char *__restrict dest, const char *__restrict src) + __attribute__((nonnull(1, 2))); + +char *T_strchr(const char *s, int c) + __attribute__((nonnull(1))); + +char *T_strchrnul(const char *s, int c) + __attribute__((nonnull(1))); + +int T_strcmp(const char *s1, const char *s2) + __attribute__((nonnull(1, 2))); + +char *T_strcpy(char *__restrict dest, const char *__restrict src) + __attribute__((nonnull(1, 2))); + size_t T_strlcat(void *__restrict dest, const void *__restrict src, size_t n) __attribute__((nonnull(1, 2))); size_t T_strlen(const char *s) __attribute__((nonnull(1))); -int T_strcmp(const char *s1, const char *s2) +char *T_strncat(char *__restrict dest, const char *__restrict src, size_t n) __attribute__((nonnull(1, 2))); int T_strncmp(const char *s1, const char *s2, size_t n) __attribute__((nonnull(1, 2))); -char *T_strchrnul(const char *s, int c) +char *T_strncpy(char *__restrict dest, const char *__restrict src, size_t n) + __attribute__((nonnull(1, 2))); + +size_t T_strnlen(const char *s, size_t maxlen) + __attribute__((nonnull(1))); + +char *T_strrchr(const char *s, int c) __attribute__((nonnull(1))); char *T_strrstr(const char *haystack, const char *needle) __attribute__((nonnull(1, 2))); -void T_bzero(void* s, size_t n); +char *T_strstr(const char *haystack, const char *needle) + __attribute__((nonnull(1, 2))); + +char *T_strtok(char *__restrict s, const char *__restrict delim) + __attribute__((nonnull(2))); + +char *T_strtok_r(char *__restrict s, const char *__restrict delim, char **__restrict save_ptr) + __attribute__((nonnull(2, 3))); #else +#define T_bzero bzero +#define T_memccpy memccpy +#define T_memchr memchr +#define T_memcmp memcmp #define T_memcpy memcpy +#define T_memmem memmem #define T_memmove memmove -#define T_memset memset -#define T_memcmp memcmp -#define T_memccpy memccpy #define T_mempcpy mempcpy #define T_memrchr memrchr -#define T_memmem memmem #define T_memrmem memrmem +#define T_memset memset #define T_stpcpy stpcpy #define T_stpncpy stpncpy +#define T_strcat strcat +#define T_strchr strchr +#define T_strchrnul strchrnul +#define T_strcmp strcmp +#define T_strcpy strcpy #define T_strlcat strlcat #define T_strlen strlen -#define T_strcmp strcmp +#define T_strncat strncat #define T_strncmp strncmp -#define T_strchrnul strchrnul +#define T_strncpy strncpy +#define T_strnlen strnlen +#define T_strrchr strrchr #define T_strrstr strrstr -#define T_bzero bzero +#define T_strstr strstr +#define T_strtok strtok +#define T_strtok_r strtok_r #endif @@ -671,12 +715,12 @@ int strlcat_test(void) { strcpy(dst, src1); C(T_strlcat(dst , src2, 8) == 6); C(strcmp_exact(dst, "FooBar")); strcpy(dst, src1); C(T_strlcat(dst , src2, 9) == 6); C(strcmp_exact(dst, "FooBar")); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 0) == 0); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 1) == 1); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 2) == 2); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 3) == 3); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 4) == 3); C(strcmp_exact(dst, src1)); - strcpy(dst, src1); C(T_strlcat(dst , SINK, 5) == 3); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 0) == 0); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 1) == 1); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 2) == 2); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 3) == 3); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 4) == 3); C(strcmp_exact(dst, src1)); + T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 5) == 3); C(strcmp_exact(dst, src1)); C(T_strlcat(NULL_ptr, SINK, 0) == 0); C(T_strlcat(NULL_ptr, src1, 0) == 3); @@ -952,6 +996,38 @@ int memrmem_test(void) { return 0; } +int strstr_test(void) { + const char str1[] = "abcdef123\0aababc123"; + + C(T_strstr(SINK, SINK) == SINK); + C(T_strstr(SINK, str1) == NULL); + C(T_strstr(str1, SINK) == str1); + C(T_strstr(str1, str1) == str1); + + C(T_strstr(str1 + 0, str1 + 6) == str1 + 6); + C(T_strstr(str1 + 9, str1 + 9) == str1 + 9); + C(T_strstr(str1 + 10, "ab") == str1 + 11); + C(T_strstr(str1 + 10, "aa") == str1 + 10); + C(T_strstr(str1 + 10, "b" ) == str1 + 12); + C(T_strstr(str1 + 10, "ba") == str1 + 12); + C(T_strstr(str1 + 10, "aac") == NULL); + + C(T_strstr(gnu_copypasta, "" ) == gnu_copypasta + 0); + C(T_strstr(gnu_copypasta, "a" ) == gnu_copypasta + 35); + C(T_strstr(gnu_copypasta, "by" ) == gnu_copypasta + 286); + C(T_strstr(gnu_copypasta, "GNU" ) == gnu_copypasta + 92); + C(T_strstr(gnu_copypasta, "full" ) == gnu_copypasta + 245); + C(T_strstr(gnu_copypasta, "linux" ) == NULL); + C(T_strstr(gnu_copypasta, "Linux" ) == gnu_copypasta + 73); + C(T_strstr(gnu_copypasta, "I would" ) == gnu_copypasta + 0); + C(T_strstr(gnu_copypasta, "POSIX" ) == gnu_copypasta + 386); + C(T_strstr(gnu_copypasta, "POsIX" ) == NULL); + C(T_strstr(gnu_copypasta, "system" ) == gnu_copypasta + 186); + C(T_strstr(gnu_copypasta, " component") == gnu_copypasta + 229); + + return 0; +} + int strrstr_test(void) { const char str1[] = "abcdef123\0aababc123"; @@ -968,6 +1044,7 @@ int strrstr_test(void) { C(T_strrstr(str1 + 10, "ba") == str1 + 12); C(T_strrstr(str1 + 10, "aac") == NULL); + C(T_strrstr(gnu_copypasta, "" ) == gnu_copypasta + 390); C(T_strrstr(gnu_copypasta, "a" ) == gnu_copypasta + 372); C(T_strrstr(gnu_copypasta, "by" ) == gnu_copypasta + 383); C(T_strrstr(gnu_copypasta, "GNU" ) == gnu_copypasta + 293); diff --git a/test/standalone/asprintf_fprintf/src/rename.s b/test/standalone/asprintf_fprintf/src/rename.s index aa23678af..a2cb56b5a 100644 --- a/test/standalone/asprintf_fprintf/src/rename.s +++ b/test/standalone/asprintf_fprintf/src/rename.s @@ -1,56 +1,162 @@ .assume adl = 1 + .section .rodata._NULL_ptr + + .global _NULL_ptr +_NULL_ptr: + db $00, $00, $00 + +;------------------------------------------------------------------------------- + .section .text - .global _T_memset, _T_memcpy, _T_memmove, _T_memcmp, _T_memccpy, _T_mempcpy, _T_memrchr, _T_memmem, _T_memrmem - .global _T_strlen, _T_strcmp, _T_strncmp, _T_stpcpy, _T_stpncpy, _T_strlcat, _T_strchrnul, _T_strrstr .global _T_bzero + .global _T_memccpy + .global _T_memchr + .global _T_memcmp + .global _T_memcpy + .global _T_memmem + .global _T_memmove + .global _T_mempcpy + .global _T_memrchr + .global _T_memrmem + .global _T_memset + .global _T_stpcpy + .global _T_stpncpy + .global _T_strcat + .global _T_strchr + .global _T_strchrnul + .global _T_strcmp + .global _T_strcpy + .global _T_strlcat + .global _T_strlen + .global _T_strncat + .global _T_strncmp + .global _T_strncpy + .global _T_strnlen + .global _T_strrchr + .global _T_strrstr + .global _T_strstr + .global _T_strtok + .global _T_strtok_r + +;------------------------------------------------------------------------------- + +_T_bzero: + jp _bzero + +_T_memccpy: + jp _memccpy + +_T_memchr: + jp _memchr + +_T_memcmp: + jp _memcmp -_T_memset: - jp _memset _T_memcpy: jp _memcpy + +_T_memmem: + jp _memmem + _T_memmove: jp _memmove -_T_memcmp: - jp _memcmp -_T_memccpy: - jp _memccpy + _T_mempcpy: jp _mempcpy + _T_memrchr: jp _memrchr -_T_memmem: - jp _memmem + _T_memrmem: jp _memrmem -_T_strlen: - jp _strlen -_T_strcmp: - jp _strcmp -_T_strncmp: - jp _strncmp +_T_memset: + jp _memset + _T_stpcpy: jp _stpcpy + _T_stpncpy: jp _stpncpy -_T_strlcat: - jp _strlcat + +_T_strcat: + jp _strcat + +_T_strchr: + jp _strchr + _T_strchrnul: jp _strchrnul + +_T_strcmp: + jp _strcmp + +_T_strcpy: + jp _strcpy + +_T_strlcat: + jp _strlcat + +_T_strlen: + jp _strlen + +_T_strncat: + jp _strncat + +_T_strncmp: + jp _strncmp + +_T_strncpy: + jp _strncpy + +_T_strnlen: + jp _strnlen + +_T_strrchr: + jp _strrchr + _T_strrstr: jp _strrstr -_T_bzero: - jp _bzero +_T_strstr: + jp _strstr - .section .rodata._NULL_ptr +_T_strtok: + jp _strtok - .global _NULL_ptr -_NULL_ptr: - db $00, $00, $00 +_T_strtok_r: + jp _strtok_r + +;------------------------------------------------------------------------------- - .extern _memset, _memcpy, _memmove, _memcmp, _memccpy, _mempcpy, _memrchr, _memmem, _memrmem - .extern _strlen, _strcmp, _strncmp, _stpcpy, _stpncpy, _strlcat, _strchrnul, _strrstr - .extern _bzero + .global _bzero + .global _memccpy + .global _memchr + .global _memcmp + .global _memcpy + .global _memmem + .global _memmove + .global _mempcpy + .global _memrchr + .global _memrmem + .global _memset + .global _stpcpy + .global _stpncpy + .global _strcat + .global _strchr + .global _strchrnul + .global _strcmp + .global _strcpy + .global _strlcat + .global _strlen + .global _strncat + .global _strncmp + .global _strncpy + .global _strnlen + .global _strrchr + .global _strrstr + .global _strstr + .global _strtok + .global _strtok_r From 293a7ee5577ae22590188c1d8b10cd63277fa5f7 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:49:33 -0700 Subject: [PATCH 03/15] add strtok(_r) implementations --- src/libc/include/string.h | 3 ++ src/libc/strtok.src | 95 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/libc/strtok.src diff --git a/src/libc/include/string.h b/src/libc/include/string.h index 67cc527ec..28160d7a6 100644 --- a/src/libc/include/string.h +++ b/src/libc/include/string.h @@ -86,6 +86,9 @@ char *strcasestr(const char *haystack, const char *needle) char *strtok(char *__restrict s, const char *__restrict delim) __attribute__((nonnull(2))); +char *T_strtok_r(char *__restrict s, const char *__restrict delim, char **__restrict save_ptr) + __attribute__((nonnull(2, 3))); + char *strdup(const char *s) __attribute__((__malloc__)) __attribute__((nonnull(1))); diff --git a/src/libc/strtok.src b/src/libc/strtok.src new file mode 100644 index 000000000..24a7c8833 --- /dev/null +++ b/src/libc/strtok.src @@ -0,0 +1,95 @@ + .assume adl=1 + +;------------------------------------------------------------------------------- + + .section .text + + .global _strtok + .type _strtok, @function + + + .equ strtokPtr, $D000FF + +; strtok_r(char *__restrict str, const char *__restrict delim) +_strtok: + pop bc + pop de + ex (sp), hl + push de + push bc + + ld bc, strtokPtr + push bc ; save_ptr + push hl ; delim + push de ; str + call _strtok_r + pop bc + pop bc + pop bc + ret + +;------------------------------------------------------------------------------- + + .section .text + + .global _strtok_r + .type _strtok_r, @function + +; strtok_r(char *__restrict str, const char *__restrict delim, char **__restrict save_ptr) +_strtok_r: + push ix + ld ix, 0 + add ix, sp + ld hl, (ix + 6) + add hl, de + xor a, a + sbc hl, de + jr nz, .L.str_non_null + ; use save_ptr + ld hl, (ix + 12) + ld hl, (hl) +.L.str_non_null: + cp a, (hl) + jr z, .L.end_of_str + ld de, (ix + 9) + push de + push hl + call _strspn + ld de, (ix + 6) + add hl, de + xor a, a + cp a, (hl) + jr z, .L.end_of_strspn + + push hl ; ld (ix - 9), hl + + ld de, (ix + 9) + push de + push hl + call _strcspn + + ld de, (ix - 9) + + add hl, de + xor a, a + cp a, (hl) + jr z, .L.finish + ; nul terminate + ld (hl), a + inc hl +.L.finish: + ld sp, ix + ld iy, (ix + 12) + ld (iy), hl + ex de, hl + pop ix + ret + +.L.end_of_str: +.L.end_of_strspn: + ; return NULL + ld de, 0 + jr .L.finish + + .extern _strspn + .extern _strcspn From ca6ab8d5835dd16b4e1a3414b441d84965cd4848 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:49:41 -0700 Subject: [PATCH 04/15] strcat bug fix --- src/libc/strcat.src | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libc/strcat.src b/src/libc/strcat.src index 6fc046a9c..c5a5ae8e4 100644 --- a/src/libc/strcat.src +++ b/src/libc/strcat.src @@ -27,6 +27,7 @@ _strcat: dec hl ex de, hl ; move to the end of src + ld bc, 0 cpir add hl, bc ; rewind to the beginning of src push hl From d4914e2a30f34028a57232cb9770a37312c524ae Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:51:35 -0700 Subject: [PATCH 05/15] strtok fix --- src/libc/os.src | 3 --- src/libc/strtok.src | 7 +++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libc/os.src b/src/libc/os.src index d4cab2a1d..4815ee1f7 100644 --- a/src/libc/os.src +++ b/src/libc/os.src @@ -10,6 +10,3 @@ .global _strncat .type _strncat, @function .set _strncat, 0x0000D8 - .global _strtok - .type _strtok, @function - .set _strtok, 0x0000F4 diff --git a/src/libc/strtok.src b/src/libc/strtok.src index 24a7c8833..fc6b8e4c9 100644 --- a/src/libc/strtok.src +++ b/src/libc/strtok.src @@ -7,6 +7,11 @@ .global _strtok .type _strtok, @function +.ifdef PREFER_CE_LIBC + + .set _strtok, 0x0000F4 + +.else .equ strtokPtr, $D000FF @@ -28,6 +33,8 @@ _strtok: pop bc ret +.endif + ;------------------------------------------------------------------------------- .section .text From 4e10d0589264f489e82a522d2d728d657c281902 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:59:03 -0700 Subject: [PATCH 06/15] fix bugs in strcat/strcpy --- src/libc/strcat.src | 3 +-- src/libc/strcpy.src | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libc/strcat.src b/src/libc/strcat.src index c5a5ae8e4..c1e1e86a1 100644 --- a/src/libc/strcat.src +++ b/src/libc/strcat.src @@ -28,9 +28,8 @@ _strcat: ex de, hl ; move to the end of src ld bc, 0 - cpir - add hl, bc ; rewind to the beginning of src push hl + cpir sbc hl, hl sbc hl, bc ex (sp), hl diff --git a/src/libc/strcpy.src b/src/libc/strcpy.src index 4e547c848..6570ca143 100644 --- a/src/libc/strcpy.src +++ b/src/libc/strcpy.src @@ -17,18 +17,20 @@ _strcpy: ex (sp), hl push de push bc + ; DE = dst + ; HL = src push de ; return value xor a, a ld bc, 0 ; move to the end of src - cpir - add hl, bc ; rewind to the beginning of src push hl + cpir sbc hl, hl sbc hl, bc ex (sp), hl pop bc + ; DE = dst ; HL = src ; BC = strlen(src) + 1 From 1de0aa024374066d3e68eea55ed3c3c428f8bd2c Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:59:39 -0700 Subject: [PATCH 07/15] fix more string tests --- test/standalone/asprintf_fprintf/src/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/standalone/asprintf_fprintf/src/main.c b/test/standalone/asprintf_fprintf/src/main.c index 6e12a771f..0233ddf76 100644 --- a/test/standalone/asprintf_fprintf/src/main.c +++ b/test/standalone/asprintf_fprintf/src/main.c @@ -715,7 +715,8 @@ int strlcat_test(void) { strcpy(dst, src1); C(T_strlcat(dst , src2, 8) == 6); C(strcmp_exact(dst, "FooBar")); strcpy(dst, src1); C(T_strlcat(dst , src2, 9) == 6); C(strcmp_exact(dst, "FooBar")); - T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 0) == 0); C(strcmp_exact(dst, src1)); + C(T_strcpy(dst, src1) == dst); + /* ============== */ C(T_strlcat(dst , SINK, 0) == 0); C(strcmp_exact(dst, src1)); T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 1) == 1); C(strcmp_exact(dst, src1)); T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 2) == 2); C(strcmp_exact(dst, src1)); T_strcpy(dst, src1); C(T_strlcat(dst , SINK, 3) == 3); C(strcmp_exact(dst, src1)); From 275d7889070c74c84b10f78274aa28c5840de088 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:14:32 -0700 Subject: [PATCH 08/15] added strncat --- src/libc/os.src | 3 --- src/libc/strncat.src | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/libc/strncat.src diff --git a/src/libc/os.src b/src/libc/os.src index 4815ee1f7..07c62e7d6 100644 --- a/src/libc/os.src +++ b/src/libc/os.src @@ -7,6 +7,3 @@ .global _setjmp .type _setjmp, @function .set _setjmp, 0x0000B8 - .global _strncat - .type _strncat, @function - .set _strncat, 0x0000D8 diff --git a/src/libc/strncat.src b/src/libc/strncat.src new file mode 100644 index 000000000..920386700 --- /dev/null +++ b/src/libc/strncat.src @@ -0,0 +1,50 @@ + .assume adl=1 + + .section .text + + .global _strncat + .type _strncat, @function + +.ifdef PREFER_OS_LIBC + + .set _strncat, 0x0000D8 + +.else + +_strncat: + ld iy, 0 + lea bc, iy + 0 + add iy, sp + xor a, a + ld hl, (iy + 3) ; dst + cpir + dec hl + push hl ; dst + strlen(dst) + + ; inlined strnlen + ld bc, (iy + 9) ; max_size + sbc hl, hl + sbc hl, bc + jr z, .zero_size + add hl, bc + ld de, (iy + 6) ; src + sbc hl, de + ex de, hl + cpir + jr z, .finish_strnlen + inc hl +.finish_strnlen: + xor a, a + adc hl, de + pop hl + jr z, .no_room + ld de, (iy + 6) ; src + ldir + ld (de), a ; nul terminate +.zero_size: + ld sp, iy +.no_room: + ld hl, (iy + 3) + ret + +.endif From 2e15dc959d1edc06b28ed244c79513c1b31c839e Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:29:59 -0700 Subject: [PATCH 09/15] added some more tests, still need str(n)cat tests --- test/standalone/asprintf_fprintf/src/main.c | 86 +++++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/test/standalone/asprintf_fprintf/src/main.c b/test/standalone/asprintf_fprintf/src/main.c index 0233ddf76..86bd6f593 100644 --- a/test/standalone/asprintf_fprintf/src/main.c +++ b/test/standalone/asprintf_fprintf/src/main.c @@ -641,6 +641,36 @@ int strncmp_test(void) { return 0; } +int memchr_test(void) { + C(T_memchr(NULL_ptr, 0x00, 0) == NULL_ptr); + C(T_memchr(NULL_ptr, 0xFF, 0) == NULL_ptr); + + C(T_memchr(SINK, 0x00, 0) == NULL); + C(T_memchr(SINK, 0x00, 1) == SINK); + C(T_memchr(SINK, 0xFF, 1) == NULL); + C(T_memchr(SINK, 0x00, 2) == SINK); + C(T_memchr(SINK, 0x00, 500) == SINK); + const char test0[] = "ABCDEFABCDEF\0G"; + char const * const test = &test0[0]; + + const size_t test_size = sizeof(test0) - 2; + const size_t test_strlen = sizeof(test0) - 3; + + C(T_memchr(test, '\0', sizeof(test0)) == &test[12]); + C(T_memchr(test, 'A', test_strlen) == &test[0]); + C(T_memchr(&test[7], 'A', 5) == NULL); + C(T_memchr(&test[6], 'A', 6) == &test[6]); + C(T_memchr(&test[5], 'A', 7) == &test[6]); + C(T_memchr(&test[7], 'B', 5) == &test[7]); + C(T_memchr(&test[8], 'C', 1) == &test[8]); + C(T_memchr(&test[8], 'C', 8) == &test[8]); + C(T_memchr(test, 'G', test_strlen) == NULL); + C(T_memchr(test, 'G', test_size) == NULL); + C(T_memchr(test0, 'G', sizeof(test0)) == &test[test_size]); + + return 0; +} + int memrchr_test(void) { C(T_memrchr(NULL_ptr, 0x00, 0) == NULL_ptr); C(T_memrchr(NULL_ptr, 0xFF, 0) == NULL_ptr); @@ -740,6 +770,49 @@ int strlcat_test(void) { return 0; } +int strncpy_test(void) { + char text[6]; + + C(T_strncpy(NULL_ptr, "", 0) == NULL_ptr + 0); + C(T_strncpy(NULL_ptr, "foobar", 0) == NULL_ptr + 0); + + memset(text, '\xee', 6); + + C(T_strncpy(text, "1", 5) == text); + C(memcmp(text, "1\0\0\0\0\xee", 6) == 0); + + C(T_strncpy(text, "1234", 5) == text); + C(memcmp(text, "1234\0\xee", 6) == 0); + + C(T_strncpy(text, "12345", 5) == text); + C(memcmp(text, "12345\xee", 6) == 0); + + C(T_strncpy(text, "123456", 5) == text); + C(memcmp(text, "12345\xee", 6) == 0); + + memset(text, '\xff', 6); + + C(T_strncpy(text, "", 0) == text); + C(memcmp(text, "\xff\xff\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "123456", 1) == text); + C(memcmp(text, "1\xff\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "6", 1) == text); + C(memcmp(text, "6\xff\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "", 1) == text); + C(memcmp(text, "\0\xff\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "a", 2) == text); + C(memcmp(text, "a\0\xff\xff\xff\xff", 6) == 0); + + C(T_strncpy(text, "", 5) == text); + C(memcmp(text, "\0\0\0\0\0\xff", 6) == 0); + + return 0; +} + int stpncpy_test(void) { char text[6]; @@ -766,19 +839,19 @@ int stpncpy_test(void) { C(memcmp(text, "\xff\xff\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "123456", 1) == text + 1); - C(memcmp(text, "1\xff\xff\xff\xff\xff", 1) == 0); + C(memcmp(text, "1\xff\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "6", 1) == text + 1); - C(memcmp(text, "6\xff\xff\xff\xff\xff", 1) == 0); + C(memcmp(text, "6\xff\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "", 1) == text + 0); - C(memcmp(text, "\0\xff\xff\xff\xff\xff", 1) == 0); + C(memcmp(text, "\0\xff\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "a", 2) == text + 1); - C(memcmp(text, "a\0\xff\xff\xff\xff", 1) == 0); + C(memcmp(text, "a\0\xff\xff\xff\xff", 6) == 0); C(T_stpncpy(text, "", 5) == text + 0); - C(memcmp(text, "\0\0\0\0\0\xff", 1) == 0); + C(memcmp(text, "\0\0\0\0\0\xff", 6) == 0); return 0; } @@ -1096,12 +1169,15 @@ int run_tests(void) { TEST(mempcpy_test()); TEST(bzero_test()); TEST(strncmp_test()); + TEST(memchr_test()); TEST(memrchr_test()); TEST(memmove_test()); TEST(strlcat_test()); + TEST(strncpy_test()); TEST(stpncpy_test()); TEST(memmem_test()); TEST(memrmem_test()); + TEST(strstr_test()); TEST(strrstr_test()); TEST(strchrnul_test()); From 75bfff5141918a8fb79179750b4a2c1e67099a48 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:32:12 -0700 Subject: [PATCH 10/15] fix strtok_r --- src/libc/include/string.h | 2 +- src/libcxx/include/cstring | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libc/include/string.h b/src/libc/include/string.h index 28160d7a6..850b9908a 100644 --- a/src/libc/include/string.h +++ b/src/libc/include/string.h @@ -86,7 +86,7 @@ char *strcasestr(const char *haystack, const char *needle) char *strtok(char *__restrict s, const char *__restrict delim) __attribute__((nonnull(2))); -char *T_strtok_r(char *__restrict s, const char *__restrict delim, char **__restrict save_ptr) +char *strtok_r(char *__restrict s, const char *__restrict delim, char **__restrict save_ptr) __attribute__((nonnull(2, 3))); char *strdup(const char *s) diff --git a/src/libcxx/include/cstring b/src/libcxx/include/cstring index 7adbf29d1..507f6bc97 100644 --- a/src/libcxx/include/cstring +++ b/src/libcxx/include/cstring @@ -36,6 +36,7 @@ using ::strstr; using ::strrstr; using ::strcasestr; using ::strtok; +using ::strtok_r; using ::strdup; using ::strndup; using ::strcspn; From 68ea5f87a2a9316fbf8d93157b1ad7689f7d3c59 Mon Sep 17 00:00:00 2001 From: ZERICO2005 <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:53:37 -0700 Subject: [PATCH 11/15] Forgot to set BC in strncat before calling LDIR :) --- src/libc/strncat.src | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libc/strncat.src b/src/libc/strncat.src index 920386700..9f291ddaf 100644 --- a/src/libc/strncat.src +++ b/src/libc/strncat.src @@ -36,7 +36,8 @@ _strncat: .finish_strnlen: xor a, a adc hl, de - pop hl + ex (sp), hl + pop bc jr z, .no_room ld de, (iy + 6) ; src ldir From 5058575817a3069d1b3c793095c6bd6cd092c4c1 Mon Sep 17 00:00:00 2001 From: ZERICO2005 <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:41:44 -0700 Subject: [PATCH 12/15] forgot an ex de, hl in strncat. Oops. Made other opts too --- src/libc/strncat.src | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libc/strncat.src b/src/libc/strncat.src index 9f291ddaf..6d0e31447 100644 --- a/src/libc/strncat.src +++ b/src/libc/strncat.src @@ -36,14 +36,14 @@ _strncat: .finish_strnlen: xor a, a adc hl, de - ex (sp), hl - pop bc +.zero_size: + pop de ; dst + strlen(dst) jr z, .no_room - ld de, (iy + 6) ; src + push hl + pop bc + ld hl, (iy + 6) ; src ldir ld (de), a ; nul terminate -.zero_size: - ld sp, iy .no_room: ld hl, (iy + 3) ret From 9d839b45a4ed8d8b225676d5e49439ac520f8e25 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:53:01 -0700 Subject: [PATCH 13/15] optimize strcat and fix strncat --- src/libc/strcat.src | 36 +++++++++++++++++++----------------- src/libc/strncat.src | 16 ++++++++-------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/libc/strcat.src b/src/libc/strcat.src index c1e1e86a1..8249fabda 100644 --- a/src/libc/strcat.src +++ b/src/libc/strcat.src @@ -12,33 +12,35 @@ .else _strcat: - pop bc - pop hl + pop iy pop de - push de - push hl - push bc + ex (sp), hl ; src + push de ; dst - push hl ; return value - ; move to the end of dst - xor a, a - ld bc, 0 - cpir - dec hl - ex de, hl ; move to the end of src + xor a, a ld bc, 0 - push hl + push hl ; src cpir sbc hl, hl - sbc hl, bc + sbc hl, bc ; carry is set ex (sp), hl + ex de, hl + ; move to the end of dst + + ; not needed unless the strlen(dst) + strlen(src) > 16777215 bytes, + ; which would imply that the resulting string would not fit into memory + ; ld bc, 0 + cpir + dec hl + pop bc - ; DE = dst + strlen(dst) + 1 + ex de, hl + ; DE = dst + strlen(dst) ; HL = src ; BC = strlen(src) + 1 ldir - pop hl ; return dst - ret + ex (sp), hl ; return value + jp (iy) .endif diff --git a/src/libc/strncat.src b/src/libc/strncat.src index 6d0e31447..96150994a 100644 --- a/src/libc/strncat.src +++ b/src/libc/strncat.src @@ -19,32 +19,32 @@ _strncat: ld hl, (iy + 3) ; dst cpir dec hl - push hl ; dst + strlen(dst) + push hl ; dst + strlen(dst) ; inlined strnlen ld bc, (iy + 9) ; max_size sbc hl, hl sbc hl, bc - jr z, .zero_size + jr z, .L.zero_size add hl, bc ld de, (iy + 6) ; src sbc hl, de ex de, hl cpir - jr z, .finish_strnlen + jr z, .L.finish_strnlen inc hl -.finish_strnlen: +.L.finish_strnlen: xor a, a adc hl, de -.zero_size: - pop de ; dst + strlen(dst) - jr z, .no_room +.L.zero_size: + pop de ; dst + strlen(dst) + jr z, .L.no_room push hl pop bc ld hl, (iy + 6) ; src ldir ld (de), a ; nul terminate -.no_room: +.L.no_room: ld hl, (iy + 3) ret From 90bc99043848198bda3b7d1aec973296ef7b5986 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Wed, 7 Jan 2026 23:03:41 -0700 Subject: [PATCH 14/15] optimize strcpy too --- src/libc/strcpy.src | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/libc/strcpy.src b/src/libc/strcpy.src index 6570ca143..7a3b7eeae 100644 --- a/src/libc/strcpy.src +++ b/src/libc/strcpy.src @@ -12,15 +12,11 @@ .else _strcpy: - pop bc + pop iy pop de - ex (sp), hl - push de - push bc - ; DE = dst - ; HL = src + ex (sp), hl ; src + push de ; dst - push de ; return value xor a, a ld bc, 0 ; move to the end of src @@ -35,7 +31,7 @@ _strcpy: ; HL = src ; BC = strlen(src) + 1 ldir - pop hl ; return dst - ret + ex (sp), hl ; return dst + jp (iy) .endif From ab0ba6d5fba3e6dfd4c289c02602d049391ef0c8 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Sat, 10 Jan 2026 13:19:50 -0700 Subject: [PATCH 15/15] clean asprintf_fprintf more and fix strtok(_r) from failed tests --- src/libc/strtok.src | 15 +- test/standalone/asprintf_fprintf/src/main.c | 319 ++++++++++++++++---- 2 files changed, 266 insertions(+), 68 deletions(-) diff --git a/src/libc/strtok.src b/src/libc/strtok.src index fc6b8e4c9..a08ff1627 100644 --- a/src/libc/strtok.src +++ b/src/libc/strtok.src @@ -47,30 +47,31 @@ _strtok_r: push ix ld ix, 0 add ix, sp - ld hl, (ix + 6) + ld hl, (ix + 6) ; str add hl, de xor a, a sbc hl, de jr nz, .L.str_non_null ; use save_ptr - ld hl, (ix + 12) + ld hl, (ix + 12) ; save_ptr ld hl, (hl) + ld (ix + 6), hl .L.str_non_null: cp a, (hl) jr z, .L.end_of_str - ld de, (ix + 9) + ld de, (ix + 9) ; delim push de push hl call _strspn - ld de, (ix + 6) + ld de, (ix + 6) ; str add hl, de xor a, a cp a, (hl) jr z, .L.end_of_strspn - push hl ; ld (ix - 9), hl + push hl ; ld (ix - 9), hl - ld de, (ix + 9) + ld de, (ix + 9) ; delim push de push hl call _strcspn @@ -85,10 +86,10 @@ _strtok_r: ld (hl), a inc hl .L.finish: - ld sp, ix ld iy, (ix + 12) ld (iy), hl ex de, hl + ld sp, ix pop ix ret diff --git a/test/standalone/asprintf_fprintf/src/main.c b/test/standalone/asprintf_fprintf/src/main.c index 86bd6f593..35041b81a 100644 --- a/test/standalone/asprintf_fprintf/src/main.c +++ b/test/standalone/asprintf_fprintf/src/main.c @@ -8,28 +8,88 @@ #include #include -/** -* @brief Tests the following functions/macros: -* boot_sprintf -* boot_snprintf -* boot_asprintf -* asprintf -* fprintf -* stpcpy -* memccpy -*/ +//------------------------------------------------------------------------------ +// Config +//------------------------------------------------------------------------------ + +// define to 0 or 1 +#define DEBUG_DIAGNOSTICS 0 + +// define to 0 or 1 (prevents Clang from replacing functions with builtins) +#define RENAME_FUNCTIONS 1 + +//------------------------------------------------------------------------------ +// Utilities +//------------------------------------------------------------------------------ #define C(expr) if (!(expr)) { return __LINE__; } -#define TEST(test) { ret = test; if (ret != 0) { return ret; }} +#define TEST(test) { ret = test; if (ret != 0) { return ret; } } #define SINK (char*)0xE40000 +#ifndef DEBUG_DIAGNOSTICS +#error "DEBUG_DIAGNOSTICS needs to be defined to 0 or 1" +#endif + +#ifndef RENAME_FUNCTIONS +#error "RENAME_FUNCTIONS needs to be defined to 0 or 1" +#endif + +#if DEBUG_DIAGNOSTICS +#define test_printf printf +#else +#define test_printf(...) +#endif + /* pass NULL into functions without triggering -Wnonnull */ extern void* NULL_ptr; +//------------------------------------------------------------------------------ +// Debug routines +//------------------------------------------------------------------------------ + +__attribute__((__unused__)) static void write_letter(char ch) { + if (isgraph(ch)) { + fputc(ch, stdout); + return; + } + fputc('\\', stdout); + switch (ch) { + case '\0': fputc('0', stdout); return; + case ' ' : fputc('s', stdout); return; + case '\n': fputc('n', stdout); return; + case '\t': fputc('t', stdout); return; + case '\v': fputc('v', stdout); return; + case '\r': fputc('r', stdout); return; + case '\f': fputc('f', stdout); return; + case '\b': fputc('b', stdout); return; + default: { + char buf[sizeof("xFF")]; + boot_sprintf(buf, "x%02X", (unsigned int)ch); + fputs(buf, stdout); + return; + } + } +} + +__attribute__((__unused__)) static void print_mem_string(const char *str, size_t length) { + for (size_t i = 0; i < length; i++) { + write_letter(*str++); + } + fputc('\n', stdout); +} + +__attribute__((__unused__)) static void print_string(const char* str) { + print_mem_string(str, strlen(str)); +} + +//------------------------------------------------------------------------------ +// Functions +//------------------------------------------------------------------------------ + // prevents Clang from replacing function calls with builtins -#if 1 +#if RENAME_FUNCTIONS void T_bzero(void* s, size_t n); @@ -151,6 +211,10 @@ char *T_strtok_r(char *__restrict s, const char *__restrict delim, char **__rest #endif +//------------------------------------------------------------------------------ +// Globals +//------------------------------------------------------------------------------ + const char gnu_copypasta[] = "I would just like to interject for a moment. What you're referring to "\ "as Linux, is in fact, GNU/Linux, or as I\'ve recently taken to calling "\ @@ -175,6 +239,10 @@ static const int pos_4 = 212; static char* buf = NULL; static FILE* file = NULL; +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + int boot_sprintf_tests(void) { int pos; int len = boot_asprintf( @@ -182,7 +250,7 @@ int boot_sprintf_tests(void) { 123, "asprintf", 076543, 0x9abcd, &pos, 0xFE1, 0 ); if (buf == NULL || len <= 0) { - printf("buf %p len %d\n", buf, len); + test_printf("buf %p len %d\n", buf, len); return __LINE__; } if (buf[len] != '\0') { @@ -190,27 +258,27 @@ int boot_sprintf_tests(void) { } size_t buf_len = T_strlen(buf); if (buf_len != T_strlen(test_1) || buf_len != (size_t)len) { - printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); + test_printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); return __LINE__; } if (pos != pos_1) { - printf("E: %d != %d\n", pos, pos_1); + test_printf("E: %d != %d\n", pos, pos_1); return __LINE__; } int cmp = strcmp(buf, test_1); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } char append[128]; int snprintf_test = boot_snprintf(append, 20, "%s", test_1); if (snprintf_test != (int)T_strlen(test_1)) { - printf("sprintf_test: %d\n", snprintf_test); + test_printf("sprintf_test: %d\n", snprintf_test); return __LINE__; } int len_2 = boot_snprintf(append, sizeof(append), "%s", test_1); if (len_2 != (int)T_strlen(test_1)) { - printf("E: %d != %zu\n", len_2, T_strlen(test_1)); + test_printf("E: %d != %zu\n", len_2, T_strlen(test_1)); return __LINE__; } char str2[128]; @@ -222,18 +290,18 @@ int boot_sprintf_tests(void) { return __LINE__; } if (end != &str2[pos_2]) { - printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); + test_printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); return __LINE__; } int cmp2 = T_strcmp(str2, test_2); if (cmp2 != 0) { - printf("cmp: %d\n", cmp2); + test_printf("cmp: %d\n", cmp2); return __LINE__; } char buf_3[30]; int len_3 = boot_snprintf(buf_3, sizeof(buf_3), test_3); if (len_3 != pos_3) { - printf("E: %d != %d\n", len_3, pos_3); + test_printf("E: %d != %d\n", len_3, pos_3); return __LINE__; } @@ -266,12 +334,12 @@ int boot_sprintf_tests(void) { 5, 10 ); if (len_4 != pos_4) { - printf("E: %d != %d\n", len_4, pos_4); + test_printf("E: %d != %d\n", len_4, pos_4); return __LINE__; } int len_5 = boot_snprintf(SINK, 10, ""); if (len_5 != 0) { - printf("E: %d != 0\n", len_5); + test_printf("E: %d != 0\n", len_5); return __LINE__; } @@ -288,7 +356,7 @@ int nano_tests(void) { 123, "asprintf", 076543, 0x9abcd, &pos, 0xFE1, 0 ); if (buf == NULL || len <= 0) { - printf("buf %p len %d\n", buf, len); + test_printf("buf %p len %d\n", buf, len); return __LINE__; } if (buf[len] != '\0') { @@ -296,27 +364,27 @@ int nano_tests(void) { } size_t buf_len = T_strlen(buf); if (buf_len != T_strlen(test_1) || buf_len != (size_t)len) { - printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); + test_printf("E: %zu != %zu != %d\n", T_strlen(test_1), buf_len, len); return __LINE__; } if (pos != pos_1) { - printf("E: %d != %d\n", pos, pos_1); + test_printf("E: %d != %d\n", pos, pos_1); return __LINE__; } int cmp = strcmp(buf, test_1); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } char append[128]; int snprintf_test = snprintf(append, 20, "%s", test_1); if (snprintf_test != (int)T_strlen(test_1)) { - printf("sprintf_test: %d\n", snprintf_test); + test_printf("sprintf_test: %d\n", snprintf_test); return __LINE__; } int len_2 = snprintf(append, sizeof(append), "%s", test_1); if (len_2 != (int)T_strlen(test_1)) { - printf("E: %d != %zu\n", len_2, T_strlen(test_1)); + test_printf("E: %d != %zu\n", len_2, T_strlen(test_1)); return __LINE__; } char str2[128]; @@ -328,24 +396,24 @@ int nano_tests(void) { return __LINE__; } if (end != &str2[pos_2]) { - printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); + test_printf("diff %p - %p = %td\n", end, str2, (ptrdiff_t)(end - str2)); return __LINE__; } int cmp2 = T_strcmp(str2, test_2); if (cmp2 != 0) { - printf("cmp: %d\n", cmp2); + test_printf("cmp: %d\n", cmp2); return __LINE__; } char buf_30[30]; int len_3sn = snprintf(buf_30, sizeof(buf_30), test_3); if (len_3sn != pos_3) { - printf("E: %d != %d\n", len_3sn, pos_3); + test_printf("E: %d != %d\n", len_3sn, pos_3); return __LINE__; } int len_3s = sprintf(buf_30, test_3); if (len_3s != pos_3) { - printf("E: %d != %d\n", len_3s, pos_3); + test_printf("E: %d != %d\n", len_3s, pos_3); return __LINE__; } @@ -378,12 +446,12 @@ int nano_tests(void) { 5, 10 ); if (len_4 != pos_4) { - printf("E: %d != %d\n", len_4, pos_4); + test_printf("E: %d != %d\n", len_4, pos_4); return __LINE__; } int len_5 = snprintf(SINK, 10, ""); if (len_5 != 0) { - printf("E: %d != 0\n", len_5); + test_printf("E: %d != 0\n", len_5); return __LINE__; } @@ -433,7 +501,7 @@ int memccpy_tests(void) { // test zero byte case void* ptr = T_memccpy((void*)0xC0FFEE, (void*)0x123456, 123, 0); if (ptr != NULL) { - printf("%p != NULL\n", ptr); + test_printf("%p != NULL\n", ptr); return __LINE__; } file = fopen(file_name, "wb"); @@ -534,7 +602,7 @@ int mempcpy_test(void) { // test zero byte case void* ptr = T_mempcpy((void*)0xC0FFEE, (void*)0x123456, 0); if (ptr != (void*)0xC0FFEE) { - printf("%p != %p\n", ptr, (void*)0xC0FFEE); + test_printf("%p != %p\n", ptr, (void*)0xC0FFEE); return __LINE__; } char data[192 + 1]; @@ -545,7 +613,7 @@ int mempcpy_test(void) { T_memset(append, 0x34, 64); char* res = T_mempcpy(&data[64], append, 64); if (res != &data[128]) { - printf("%p != %p\n", res, &data[128]); + test_printf("%p != %p\n", res, &data[128]); return __LINE__; } @@ -556,7 +624,7 @@ int mempcpy_test(void) { int cmp = T_memcmp(data, truth, 192); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } return 0; @@ -594,7 +662,7 @@ int bzero_test(void) { T_bzero(&data[8], 17); int cmp = T_memcmp(data, truth, 32); if (cmp != 0) { - printf("cmp: %d\n", cmp); + test_printf("cmp: %d\n", cmp); return __LINE__; } return 0; @@ -1145,6 +1213,155 @@ int strchrnul_test(void) { return 0; } +int strtok_test(void) { + const char input_empty[] = "\x00\xff"; + const char truth_empty[] = "\x00\xff"; + const char input_text[] = "?a???b,,,#c"; + const char truth_text[] = "?a\0??b\0,,#c"; + const char input_str[] = "A bird came... down the walk"; + const char truth_str[] = "A\0bird\0came\0.. down\0the\0walk"; + const char delim[] = " .,"; + char empty[sizeof(input_empty)]; + char text[sizeof(input_text)]; + char str[sizeof(input_str)]; + + /* strtok */ { + char *token; + memcpy(empty, input_empty, sizeof(input_empty)); + strcpy(text, input_text); + strcpy(str, input_str); + + token = T_strtok(empty, ""); + C(token == NULL); + + token = T_strtok(NULL, ""); + C(token == NULL); + + C(T_memcmp(empty, truth_empty, sizeof(truth_empty)) == 0); + + // token points to the token "a" + token = T_strtok(text, "?"); + C(token == text + 1); + C(strcmp_exact(token, "a")); + + // token points to the token "??b" + token = T_strtok(NULL, ","); + C(token == text + 3); + C(strcmp_exact(token, "??b")); + + // token points to the token "c" + token = T_strtok(NULL, "#,"); + C(token == text + 10); + C(strcmp_exact(token, "c")); + + // token is a null pointer + token = T_strtok(NULL, "?"); + C(token == NULL); + + C(T_memcmp(text, truth_text, sizeof(truth_text)) == 0); + + strcpy(str, input_str); + + token = strtok(str , delim); + C(token == str + 0); + + token = strtok(NULL, delim); + C(token == str + 2); + + token = strtok(NULL, delim); + C(token == str + 7); + + token = strtok(NULL, delim); + C(token == str + 15); + + token = strtok(NULL, delim); + C(token == str + 20); + + token = strtok(NULL, delim); + C(token == str + 24); + + token = strtok(NULL, delim); + C(token == NULL); + + C(T_memcmp(str, truth_str, sizeof(truth_str)) == 0); + } + + /* strtok_r */ { + char *save_ptr; + char *token; + memcpy(empty, input_empty, sizeof(input_empty)); + strcpy(text, input_text); + strcpy(str, input_str); + + token = T_strtok_r(empty, "", &save_ptr); + C(token == NULL); + C(save_ptr == empty); + + token = T_strtok_r(NULL, "", &save_ptr); + C(token == NULL); + C(save_ptr == empty); + + C(T_memcmp(empty, truth_empty, sizeof(truth_empty)) == 0); + + // token points to the token "a" + token = T_strtok_r(text, "?", &save_ptr); + C(token == text + 1); + C(strcmp_exact(token, "a")); + C(save_ptr == text + 3); + + // token points to the token "??b" + token = T_strtok_r(NULL, ",", &save_ptr); + C(token == text + 3); + C(strcmp_exact(token, "??b")); + C(save_ptr == text + 7); + + // token points to the token "c" + token = T_strtok_r(NULL, "#,", &save_ptr); + C(token == text + 10); + C(strcmp_exact(token, "c")); + C(save_ptr == text + 11); + + // token is a null pointer + token = T_strtok_r(NULL, "?", &save_ptr); + C(token == NULL); + C(save_ptr == text + 11); + + C(T_memcmp(text, truth_text, sizeof(truth_text)) == 0); + + token = T_strtok_r(str , delim, &save_ptr); + C(token == str + 0); + C(save_ptr == str + 2); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 2); + C(save_ptr == str + 7); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 7); + C(save_ptr == str + 12); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 15); + C(save_ptr == str + 20); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 20); + C(save_ptr == str + 24); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == str + 24); + C(save_ptr == str + 28); + + token = T_strtok_r(NULL, delim, &save_ptr); + C(token == NULL); + C(save_ptr == str + 28); + + C(T_memcmp(str, truth_str, sizeof(truth_str)) == 0); + } + + return 0; +} + int run_tests(void) { int ret = 0; /* boot_asprintf */ @@ -1180,31 +1397,11 @@ int run_tests(void) { TEST(strstr_test()); TEST(strrstr_test()); TEST(strchrnul_test()); + TEST(strtok_test()); return 0; } -#if 0 -static void write_letter(char c) { - if (isgraph(c)) { - fputc(c, stdout); - return; - } - fputc('\\', stdout); - switch (c) { - case '\0': fputc('0', stdout); return; - case ' ': fputc('s', stdout); return; - case '\n': fputc('n', stdout); return; - case '\t': fputc('t', stdout); return; - case '\v': fputc('v', stdout); return; - case '\r': fputc('r', stdout); return; - case '\f': fputc('f', stdout); return; - case '\b': fputc('b', stdout); return; - default: printf("x%02X", (unsigned int)c); return; - } -} -#endif - int main(void) { os_ClrHome();