From bc1092d4b58db7a1fabae09fdcb1e779e3fba334 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Sun, 1 Mar 2026 17:15:33 -0700 Subject: [PATCH 1/4] implemented strerror and perror in asm --- src/libc/errno_str.cpp | 146 ----------------------------- src/libc/errno_str.src | 202 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 146 deletions(-) delete mode 100644 src/libc/errno_str.cpp create mode 100644 src/libc/errno_str.src diff --git a/src/libc/errno_str.cpp b/src/libc/errno_str.cpp deleted file mode 100644 index e8dc7ed69..000000000 --- a/src/libc/errno_str.cpp +++ /dev/null @@ -1,146 +0,0 @@ -#include -#include -#include -#include - -static constexpr char errno_strings[] = { - "no error" - "\0" "EPERM" - "\0" "EINVAL" - "\0" "EIO" - "\0" "EDOM" - "\0" "ERANGE" - "\0" "EILSEQ" - /* C++ errno */ - "\0" "E2BIG" - "\0" "EACCES" - "\0" "EADDRINUSE" - "\0" "EADDRNOTAVAIL" - "\0" "EAFNOSUPPORT" - "\0" "EAGAIN" - "\0" "EALREADY" - "\0" "EBADF" - "\0" "EBADMSG" - "\0" "EBUSY" - "\0" "ECANCELED" - "\0" "ECHILD" - "\0" "ECONNABORTED" - "\0" "ECONNREFUSED" - "\0" "ECONNRESET" - "\0" "EDEADLK" - "\0" "EDESTADDRREQ" - "\0" "EEXIST" - "\0" "EFAULT" - "\0" "EFBIG" - "\0" "EHOSTUNREACH" - "\0" "EIDRM" - "\0" "EINPROGRESS" - "\0" "EINTR" - "\0" "EISCONN" - "\0" "EISDIR" - "\0" "ELOOP" - "\0" "EMFILE" - "\0" "EMLINK" - "\0" "EMSGSIZE" - "\0" "ENAMETOOLONG" - "\0" "ENETDOWN" - "\0" "ENETRESET" - "\0" "ENETUNREACH" - "\0" "ENFILE" - "\0" "ENOBUFS" - "\0" "ENODATA" - "\0" "ENODEV" - "\0" "ENOENT" - "\0" "ENOEXEC" - "\0" "ENOLCK" - "\0" "ENOLINK" - "\0" "ENOMEM" - "\0" "ENOMSG" - "\0" "ENOPROTOOPT" - "\0" "ENOSPC" - "\0" "ENOSR" - "\0" "ENOSTR" - "\0" "ENOSYS" - "\0" "ENOTCONN" - "\0" "ENOTDIR" - "\0" "ENOTEMPTY" - "\0" "ENOTRECOVERABLE" - "\0" "ENOTSOCK" - "\0" "ENOTSUP" - "\0" "ENOTTY" - "\0" "ENXIO" - "\0" "EOPNOTSUPP" - "\0" "EOVERFLOW" - "\0" "EOWNERDEAD" - "\0" "EPIPE" - "\0" "EPROTO" - "\0" "EPROTONOSUPPORT" - "\0" "EPROTOTYPE" - "\0" "EROFS" - "\0" "ESPIPE" - "\0" "ESRCH" - "\0" "ETIME" - "\0" "ETIMEDOUT" - "\0" "ETXTBSY" - "\0" "EWOULDBLOCK" - "\0" "EXDEV" -}; - -static constexpr size_t count_errno_strings(const char *str, size_t size) -{ - size_t count = 0; - for (size_t i = 0; i < size; i++) { - if (str[i] == '\0') { - ++count; - } - } - return count; -} - -static constexpr int errno_strings_count = count_errno_strings(errno_strings, sizeof(errno_strings)); - -static constexpr size_t unknown_errno_number_offset = 14; -#define UNKNOWN_ERRNO_STRING "unknown error -8388608" -static char unknown_errno_string[] = UNKNOWN_ERRNO_STRING; - -static_assert( - UNKNOWN_ERRNO_STRING[unknown_errno_number_offset + 0] == '-' && - UNKNOWN_ERRNO_STRING[unknown_errno_number_offset + 8] == '\0', - "the string for unknown errno numbers has been changed" -); - -char *strerror(int errnum) -{ - if (errnum < 0 || errnum >= errno_strings_count) { - boot_sprintf(&unknown_errno_string[unknown_errno_number_offset], "%d", errnum); - return const_cast(unknown_errno_string); - } - const char* ret = errno_strings; - // worst case retrieval takes ~0.8ms (79 strings) - for (int i = 0; i < errnum; i++) { - // skips to the start of the next string - ret += strlen(ret) + 1; - } - return const_cast(ret); -} - -static void errchar_puts(const char *str) -{ - while (*str) { - errchar(*str++); - } -} - -void perror(const char *str) -{ - /* Normally this would print to stderr, but since they are handled the same and pulling */ - /* in fputs would create a dependency on fileioc, just use puts/errchar rather than fputs here */ - - if (str != nullptr && *str != '\0') { - errchar_puts(str); - errchar(':'); - errchar(' '); - } - errchar_puts(strerror(errno)); - errchar('\n'); -} diff --git a/src/libc/errno_str.src b/src/libc/errno_str.src new file mode 100644 index 000000000..eaa3be2d0 --- /dev/null +++ b/src/libc/errno_str.src @@ -0,0 +1,202 @@ + .assume adl=1 + +;------------------------------------------------------------------------------- + + .section .text + .global _perror + .type _perror, @function + +; void perror(const char*) +_perror: + pop de + ex (sp), hl + push de + add hl, bc + xor a, a + sbc hl, bc + jr z, .L.no_string + cp a, (hl) + jr z, .L.no_string +.L.str_loop: + call _perror_errchar_ld_a_inc_hl + jr nz, .L.str_loop + ld a, ':' + call _perror_errchar + ld a, ' ' + call _perror_errchar + +.L.no_string: + ld hl, (_errno) + call _strerror.hijack_perror +.L.errstr_loop: + call _perror_errchar_ld_a_inc_hl + jr nz, .L.errstr_loop + or a, $0A ; '\n' + .db $CA ; jp z, * +_perror_errchar_ld_a_inc_hl: + ld a, (hl) + inc hl +_perror_errchar: + or a, a + push af + push hl + ld l, a + push hl + call nz, _errchar + pop hl + pop hl + pop af + ret + + .extern _errno + .extern _errchar + +;------------------------------------------------------------------------------- + + .section .text + .global _strerror + .type _strerror, @function + + ; supports a maximum of 255 strings + .equ errno_strings_count, 79 + +_strerror: + pop de + ex (sp), hl + push de + .local _strerror.hijack_perror +_strerror.hijack_perror: + ; HL = errnum + ld bc, -errno_strings_count + push hl + add hl, bc + jr c, .L.unknown_errnum + pop de + ld hl, _errno_strings + xor a, a + ; test if errnum is zero + cp a, e + ret z +.L.loop: + ; skip to the start of the next string + cpir + dec e + jr nz, .L.loop + ret + +.L.unknown_errnum: + ; errnum < 0 || errnum >= errno_strings_count + ld hl, _percent_d + push hl + ; overwrites the numeric portion of "unknown error -8388608" + ld hl, _unknown_errno_string + 14 + push hl + call _boot_sprintf + pop hl + pop hl + pop hl + ld hl, _unknown_errno_string + ret + + .extern _boot_sprintf + +;------------------------------------------------------------------------------- + + .section .data._unknown_errno_string + .local _unknown_errno_string +_unknown_errno_string: + .asciz "unknown error -8388608" + +;------------------------------------------------------------------------------- + + .section .rodata._percent_d + .local _percent_d +_percent_d: + .asciz "%d" + +;------------------------------------------------------------------------------- + + .section .rodata._errno_strings + .local _errno_strings + +; make sure to update errno_strings_count +_errno_strings: + .db "no error", 0 + .db "EPERM", 0 + .db "EINVAL", 0 + .db "EIO", 0 + .db "EDOM", 0 + .db "ERANGE", 0 + .db "EILSEQ", 0 + .db "E2BIG", 0 + .db "EACCES", 0 + .db "EADDRINUSE", 0 + .db "EADDRNOTAVAIL", 0 + .db "EAFNOSUPPORT", 0 + .db "EAGAIN", 0 + .db "EALREADY", 0 + .db "EBADF", 0 + .db "EBADMSG", 0 + .db "EBUSY", 0 + .db "ECANCELED", 0 + .db "ECHILD", 0 + .db "ECONNABORTED", 0 + .db "ECONNREFUSED", 0 + .db "ECONNRESET", 0 + .db "EDEADLK", 0 + .db "EDESTADDRREQ", 0 + .db "EEXIST", 0 + .db "EFAULT", 0 + .db "EFBIG", 0 + .db "EHOSTUNREACH", 0 + .db "EIDRM", 0 + .db "EINPROGRESS", 0 + .db "EINTR", 0 + .db "EISCONN", 0 + .db "EISDIR", 0 + .db "ELOOP", 0 + .db "EMFILE", 0 + .db "EMLINK", 0 + .db "EMSGSIZE", 0 + .db "ENAMETOOLONG", 0 + .db "ENETDOWN", 0 + .db "ENETRESET", 0 + .db "ENETUNREACH", 0 + .db "ENFILE", 0 + .db "ENOBUFS", 0 + .db "ENODATA", 0 + .db "ENODEV", 0 + .db "ENOENT", 0 + .db "ENOEXEC", 0 + .db "ENOLCK", 0 + .db "ENOLINK", 0 + .db "ENOMEM", 0 + .db "ENOMSG", 0 + .db "ENOPROTOOPT", 0 + .db "ENOSPC", 0 + .db "ENOSR", 0 + .db "ENOSTR", 0 + .db "ENOSYS", 0 + .db "ENOTCONN", 0 + .db "ENOTDIR", 0 + .db "ENOTEMPTY", 0 + .db "ENOTRECOVERABLE", 0 + .db "ENOTSOCK", 0 + .db "ENOTSUP", 0 + .db "ENOTTY", 0 + .db "ENXIO", 0 + .db "EOPNOTSUPP", 0 + .db "EOVERFLOW", 0 + .db "EOWNERDEAD", 0 + .db "EPIPE", 0 + .db "EPROTO", 0 + .db "EPROTONOSUPPORT", 0 + .db "EPROTOTYPE", 0 + .db "EROFS", 0 + .db "ESPIPE", 0 + .db "ESRCH", 0 + .db "ETIME", 0 + .db "ETIMEDOUT", 0 + .db "ETXTBSY", 0 + .db "EWOULDBLOCK", 0 + .db "EXDEV", 0 From ea158771d93c581b3789608338b2e2d02b7a3965 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Sun, 1 Mar 2026 19:20:54 -0700 Subject: [PATCH 2/4] additional perror size optimizations --- src/libc/errno_str.src | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/libc/errno_str.src b/src/libc/errno_str.src index eaa3be2d0..261cb034d 100644 --- a/src/libc/errno_str.src +++ b/src/libc/errno_str.src @@ -14,38 +14,42 @@ _perror: add hl, bc xor a, a sbc hl, bc - jr z, .L.no_string + jr z, .L.null_string cp a, (hl) - jr z, .L.no_string -.L.str_loop: - call _perror_errchar_ld_a_inc_hl - jr nz, .L.str_loop + jr z, .L.empty_string + ; fprintf(stderr, "%s: ", str) + call .L.errchar_puts ld a, ':' - call _perror_errchar + call .L.errchar_putchar ld a, ' ' - call _perror_errchar + call .L.errchar_putchar -.L.no_string: +.L.null_string: +.L.empty_string: ld hl, (_errno) call _strerror.hijack_perror -.L.errstr_loop: - call _perror_errchar_ld_a_inc_hl - jr nz, .L.errstr_loop - or a, $0A ; '\n' - .db $CA ; jp z, * -_perror_errchar_ld_a_inc_hl: - ld a, (hl) - inc hl -_perror_errchar: - or a, a + call .L.errchar_puts + ld a, $0A ; '\n' +.L.errchar_putchar: + ; input: + ; - Z for single character + ; - NZ to loop until '\0' + ; output: + ; - Z flag set push af push hl ld l, a push hl - call nz, _errchar + call _errchar pop hl pop hl pop af + ret z +.L.errchar_puts: + ld a, (hl) + inc hl + or a, a + jr nz, .L.errchar_putchar ret .extern _errno From 1fc7cc88fca65dab8f4036d7c6154384515e532b Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Sun, 1 Mar 2026 19:36:34 -0700 Subject: [PATCH 3/4] optimize perror print loop Co-authored-by: Brendan Fletcher --- src/libc/errno_str.src | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/libc/errno_str.src b/src/libc/errno_str.src index 261cb034d..cd5826c3f 100644 --- a/src/libc/errno_str.src +++ b/src/libc/errno_str.src @@ -32,25 +32,22 @@ _perror: ld a, $0A ; '\n' .L.errchar_putchar: ; input: - ; - Z for single character - ; - NZ to loop until '\0' + ; - A = character to display + ; - HL = nul terminated string to display afterward ; output: - ; - Z flag set - push af + ; - HL points to nul terminator push hl ld l, a push hl call _errchar pop hl pop hl - pop af - ret z .L.errchar_puts: ld a, (hl) - inc hl or a, a - jr nz, .L.errchar_putchar - ret + ret z + inc hl + jr .L.errchar_putchar .extern _errno .extern _errchar From b9aa934e1891129ba2e8b997690377e79f0042c9 Mon Sep 17 00:00:00 2001 From: zerico <71151164+ZERICO2005@users.noreply.github.com> Date: Sun, 1 Mar 2026 19:53:42 -0700 Subject: [PATCH 4/4] merged two errchar_putchar calls into one errchar_puts call to save a byte Co-authored-by: Brendan Fletcher --- src/libc/errno_str.src | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libc/errno_str.src b/src/libc/errno_str.src index cd5826c3f..0261aacf2 100644 --- a/src/libc/errno_str.src +++ b/src/libc/errno_str.src @@ -19,10 +19,8 @@ _perror: jr z, .L.empty_string ; fprintf(stderr, "%s: ", str) call .L.errchar_puts - ld a, ':' - call .L.errchar_putchar - ld a, ' ' - call .L.errchar_putchar + ld hl, _perror_colon_space_str + call .L.errchar_puts .L.null_string: .L.empty_string: @@ -52,6 +50,13 @@ _perror: .extern _errno .extern _errchar +;------------------------------------------------------------------------------- + + .section .rodata._perror_colon_space_str + .local _perror_colon_space_str +_perror_colon_space_str: + .asciz ": " + ;------------------------------------------------------------------------------- .section .text