From 8679cfebcbef0a794b8cb277f75d3133d7c7657f Mon Sep 17 00:00:00 2001 From: selmentdev Date: Tue, 4 Jul 2017 21:19:14 +0200 Subject: [PATCH 1/4] Ported functions from SelmentOS --- scripts/build.py | 5 + src/os/common.h | 10 + src/os/crt.c | 33 -- src/os/crt.h | 126 +++++++- src/os/crt/ctype.c | 368 +++++++++++++++++++++ src/os/crt/format.c | 766 ++++++++++++++++++++++++++++++++++++++++++++ src/os/crt/memory.c | 119 +++++++ src/os/crt/scan.c | 583 +++++++++++++++++++++++++++++++++ src/os/crt/stdlib.c | 13 + src/os/crt/string.c | 279 ++++++++++++++++ src/os/crt/strtox.c | 598 ++++++++++++++++++++++++++++++++++ 11 files changed, 2865 insertions(+), 35 deletions(-) delete mode 100644 src/os/crt.c create mode 100644 src/os/crt/ctype.c create mode 100644 src/os/crt/format.c create mode 100644 src/os/crt/memory.c create mode 100644 src/os/crt/scan.c create mode 100644 src/os/crt/stdlib.c create mode 100644 src/os/crt/string.c create mode 100644 src/os/crt/strtox.c diff --git a/scripts/build.py b/scripts/build.py index ee27dab..52edc56 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -31,6 +31,11 @@ def build(): objfiles = [] + # LIBC files + for fname in glob("../src/os/crt/*.c"): + cmds_to_run.append("gcc %s %s" % (fname, cc_flags)) + objfiles.append("%s.o" % os.path.basename(os.path.splitext(fname)[0])) + for fname in glob("../src/os/*.c"): cmds_to_run.append("gcc %s %s" % (fname, cc_flags)) objfiles.append("%s.o" % os.path.basename(os.path.splitext(fname)[0])) diff --git a/src/os/common.h b/src/os/common.h index 210b336..71f000f 100644 --- a/src/os/common.h +++ b/src/os/common.h @@ -2,3 +2,13 @@ #define UNUSED(a) (void)(a) typedef unsigned long long size_t; + +#ifdef __cplusplus +#define STATIC_ASSERT(_Expression) static_assert(_Expression, #_Expression) +#else +#if __STDC_VERSION__ == 201112L +#define STATIC_ASSERT(_Expression) _Static_assert(_Expression, #_Expression) +#else +#define STATIC_ASSERT(_Expression) typedef char __STATIC_ASSERT__[(_Expression) ? 1 : -1] +#endif +#endif \ No newline at end of file diff --git a/src/os/crt.c b/src/os/crt.c deleted file mode 100644 index 8c715fd..0000000 --- a/src/os/crt.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include "crt.h" - -void *memset(void *dst, int c, size_t n) { - uint8_t *dst_byte = (uint8_t*)dst; - for (size_t i = 0; i < n; i++) { - dst_byte[i] = (uint8_t)c; - } - - return dst; -} - -void *memmove(void *dst, const void *src, size_t n) { - uint8_t *dst_byte = (uint8_t*)dst; - const uint8_t *src_byte = (const uint8_t*)src; - if (dst < src) { - for (size_t i = 0; i < n; i++) { - dst_byte[i] = src_byte[i]; - } - } else { - size_t i = n - 1; - for (;;) { - dst_byte[i] = src_byte[i]; - if (i == 0) { - break; - } - i--; - } - } - - return dst; -} - diff --git a/src/os/crt.h b/src/os/crt.h index ab1da0f..b923740 100644 --- a/src/os/crt.h +++ b/src/os/crt.h @@ -1,5 +1,127 @@ #pragma once #include "common.h" -void *memset(void *dst, int c, size_t n); -void *memmove(void *dst, const void *src, size_t n); +// + +#define CTYPE_NONE 0x0000 +#define CTYPE_ALPHA 0x0001 +#define CTYPE_BLANK 0x0002 +#define CTYPE_CNTRL 0x0004 +#define CTYPE_GRAPH 0x0008 +#define CTYPE_PUNCT 0x0010 +#define CTYPE_SPACE 0x0020 +#define CTYPE_LOWER 0x0040 +#define CTYPE_UPPER 0x0080 +#define CTYPE_DIGIT 0x0100 +#define CTYPE_XDIGIT 0x0200 +#define CTYPE_CBYTE 0x0400 +#define CTYPE_LEADBYTE 0x8000 + +extern int isalnum(int c); +extern int isalpha(int c); +extern int isblank(int c); +extern int iscntrl(int c); +extern int isctype(int c, int desc); +extern int isdigit(int c); +extern int isgraph(int c); +extern int islower(int c); +extern int isprint(int c); +extern int ispunct(int c); +extern int isspace(int c); +extern int isupper(int c); +extern int isxdigit(int c); +extern int tolower(int c); +extern int toupper(int c); + + +// + +typedef int errno_t; + +// Error Codes: 7.5 p.2, 7.5 p.4 + +#define EPERM 1 +#define ENOENT 2 +#define ESRCH 3 +#define EINTR 4 +#define EIO 5 +#define ENXIO 6 +#define E2BIG 7 +#define ENOEXEC 8 +#define EBADF 9 +#define ECHILD 10 +#define EAGAIN 11 +#define ENOMEM 12 +#define EACCES 13 +#define EFAULT 14 +#define EBUSY 16 +#define EEXIST 17 +#define EXDEV 18 +#define ENODEV 19 +#define ENOTDIR 20 +#define EISDIR 21 +#define EINVAL 22 +#define ENFILE 23 +#define EMFILE 24 +#define ENOTTY 25 +#define EFBIG 27 +#define ENOSPC 28 +#define ESPIPE 29 +#define EROFS 30 +#define EMLINK 31 +#define EPIPE 32 +#define EDOM 33 +#define ERANGE 34 +#define EDEADLK 36 +#define ENAMETOOLONG 38 +#define ENOLCK 39 +#define ENOSYS 40 +#define ENOTEMPTY 41 +#define EILSEQ 42 + + +// + +#define EOF -1 + +extern int snprintf(char *string, size_t size, const char *format, ...); +extern int sprintf(char *string, const char *format, ...); +extern int vsnprintf(char *string, size_t size, const char *format, va_list arglist); +extern int vsprintf(char *string, const char *format, va_list arglist); +extern int sscanf(const char* restrict str, const char* restrict format, ...); +extern int vsscanf(const char* restrict str, const char* restrict format, va_list arglist); + + +// + +extern void* memchr(const void* ptr, int ch, size_t count); +extern int memcmp(const void* lhs, const void* rhs, size_t count); +extern void* memcpy(void *restrict dest, const void *restrict src, size_t count); +extern void* memmove(void* dest, const void* src, size_t count); +extern void* memset(void *dest, int ch, size_t count); +extern char* strcat(char* restrict dest, const char* restrict src); +extern char* strchr(const char* str, int ch); +extern int strcmp(const char* lhs, const char* rhs); +extern char* strcpy(char* restrict dest, const char* restrict src); +extern size_t strcspn(const char* dest, const char* src); +extern size_t strlen(const char* str); +extern char* strncat(char* restrict dest, const char* restrict src, size_t count); +extern int strncmp(const char* lhs, const char* rhs, size_t count); +extern char* strncpy(char* restrict dest, const char* restrict src, size_t count); +extern char* strpbrk(const char* dest, const char* breakset); +extern char* strrchr(const char* str, int ch); +extern size_t strspn(const char* dest, const char* src); +extern char* strstr(const char* str, const char* substr); +extern size_t strxfrm(char* restrict dest, const char* restrict src, size_t count); + + +// + +extern int (abs)(int n); +extern long int (labs)(long int n); +extern long long int (llabs)(long long int n); + +extern long int (strtol)(const char* restrict str, char** restrict endstr, int base); +extern long long int (strtoll)(const char* restrict str, char** restrict endstr, int base); +extern unsigned long int (strtoul)(const char* restrict str, char** restrict endstr, int base); +extern unsigned long long int (strtoull)(const char* restrict str, char** restrict endstr, int base); diff --git a/src/os/crt/ctype.c b/src/os/crt/ctype.c new file mode 100644 index 0000000..10e9fc0 --- /dev/null +++ b/src/os/crt/ctype.c @@ -0,0 +1,368 @@ +#include "../crt.h" + +const char __libc_ctype_digits_lowercase[] = "0123456789abcdefghijklmnopqrstuvwxyz"; +const char __libc_ctype_digits_uppercase[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +typedef struct __libc_ascii_data_entry_t { + uint16_t flags; + unsigned char upper; + unsigned char lower; + unsigned char collation; +} __libc_ascii_data_entry_t; + +static const __libc_ascii_data_entry_t __libc_ascii_data_table[] = { + { /* EOF */ 0, 0, 0, 0 }, + { /* NUL */ CTYPE_CNTRL, 0x00, 0x00, 0x00 }, + { /* SOH */ CTYPE_CNTRL, 0x01, 0x01, 0x01 }, + { /* STX */ CTYPE_CNTRL, 0x02, 0x02, 0x02 }, + { /* ETX */ CTYPE_CNTRL, 0x03, 0x03, 0x03 }, + { /* EOT */ CTYPE_CNTRL, 0x04, 0x04, 0x04 }, + { /* ENQ */ CTYPE_CNTRL, 0x05, 0x05, 0x05 }, + { /* ACK */ CTYPE_CNTRL, 0x06, 0x06, 0x06 }, + { /* BEL */ CTYPE_CNTRL, 0x07, 0x07, 0x07 }, + { /* BS */ CTYPE_CNTRL, 0x08, 0x08, 0x08 }, + { /* HT */ CTYPE_CNTRL | CTYPE_BLANK | CTYPE_SPACE, 0x09, 0x09, 0x09 }, + { /* LF */ CTYPE_CNTRL | CTYPE_SPACE, 0x0A, 0x0A, 0x0A }, + { /* VT */ CTYPE_CNTRL | CTYPE_SPACE, 0x0B, 0x0B, 0x0B }, + { /* FF */ CTYPE_CNTRL | CTYPE_SPACE, 0x0C, 0x0C, 0x0C }, + { /* CR */ CTYPE_CNTRL | CTYPE_SPACE, 0x0D, 0x0D, 0x0D }, + { /* SO */ CTYPE_CNTRL, 0x0E, 0x0E, 0x0E }, + { /* SI */ CTYPE_CNTRL, 0x0F, 0x0F, 0x0F }, + { /* DLE */ CTYPE_CNTRL, 0x10, 0x10, 0x10 }, + { /* DC1 */ CTYPE_CNTRL, 0x11, 0x11, 0x11 }, + { /* DC2 */ CTYPE_CNTRL, 0x12, 0x12, 0x12 }, + { /* DC3 */ CTYPE_CNTRL, 0x13, 0x13, 0x13 }, + { /* DC4 */ CTYPE_CNTRL, 0x14, 0x14, 0x14 }, + { /* NAK */ CTYPE_CNTRL, 0x15, 0x15, 0x15 }, + { /* SYN */ CTYPE_CNTRL, 0x16, 0x16, 0x16 }, + { /* ETB */ CTYPE_CNTRL, 0x17, 0x17, 0x17 }, + { /* CAN */ CTYPE_CNTRL, 0x18, 0x18, 0x18 }, + { /* EM */ CTYPE_CNTRL, 0x19, 0x19, 0x19 }, + { /* SUB */ CTYPE_CNTRL, 0x1A, 0x1A, 0x1A }, + { /* ESC */ CTYPE_CNTRL, 0x1B, 0x1B, 0x1B }, + { /* FS */ CTYPE_CNTRL, 0x1C, 0x1C, 0x1C }, + { /* GS */ CTYPE_CNTRL, 0x1D, 0x1D, 0x1D }, + { /* RS */ CTYPE_CNTRL, 0x1E, 0x1E, 0x1E }, + { /* US */ CTYPE_CNTRL, 0x1F, 0x1F, 0x1F }, + { /* SP */ CTYPE_BLANK | CTYPE_SPACE, 0x20, 0x20, 0x20 }, + { /* '!' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x21, 0x21, 0x21 }, + { /* '"' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x22, 0x22, 0x22 }, + { /* '#' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x23, 0x23, 0x23 }, + { /* '$' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x24, 0x24, 0x24 }, + { /* '%' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x25, 0x25, 0x25 }, + { /* '&' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x26, 0x26, 0x26 }, + { /* ''' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x27, 0x27, 0x27 }, + { /* '(' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x28, 0x28, 0x28 }, + { /* ')' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x29, 0x29, 0x29 }, + { /* '*' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x2A, 0x2A, 0x2A }, + { /* '+' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x2B, 0x2B, 0x2B }, + { /* ',' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x2C, 0x2C, 0x2C }, + { /* '-' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x2D, 0x2D, 0x2D }, + { /* '.' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x2E, 0x2E, 0x2E }, + { /* '/' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x2F, 0x2F, 0x2F }, + { /* '0' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x30, 0x30, 0x30 }, + { /* '1' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x31, 0x31, 0x31 }, + { /* '2' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x32, 0x32, 0x32 }, + { /* '3' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x33, 0x33, 0x33 }, + { /* '4' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x34, 0x34, 0x34 }, + { /* '5' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x35, 0x35, 0x35 }, + { /* '6' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x36, 0x36, 0x36 }, + { /* '7' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x37, 0x37, 0x37 }, + { /* '8' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x38, 0x38, 0x38 }, + { /* '9' */ CTYPE_GRAPH | CTYPE_DIGIT | CTYPE_XDIGIT, 0x39, 0x39, 0x39 }, + { /* ':' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x3A, 0x3A, 0x3A }, + { /* ';' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x3B, 0x3B, 0x3B }, + { /* '<' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x3C, 0x3C, 0x3C }, + { /* '=' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x3D, 0x3D, 0x3D }, + { /* '>' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x3E, 0x3E, 0x3E }, + { /* '?' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x3F, 0x3F, 0x3F }, + { /* '@' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x40, 0x40, 0x40 }, + { /* 'A' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER | CTYPE_XDIGIT, 0x41, 0x61, 0x41 }, + { /* 'B' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER | CTYPE_XDIGIT, 0x42, 0x62, 0x42 }, + { /* 'C' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER | CTYPE_XDIGIT, 0x43, 0x63, 0x43 }, + { /* 'D' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER | CTYPE_XDIGIT, 0x44, 0x64, 0x44 }, + { /* 'E' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER | CTYPE_XDIGIT, 0x45, 0x65, 0x45 }, + { /* 'F' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER | CTYPE_XDIGIT, 0x46, 0x66, 0x46 }, + { /* 'G' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x47, 0x67, 0x47 }, + { /* 'H' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x48, 0x68, 0x48 }, + { /* 'I' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x49, 0x69, 0x49 }, + { /* 'J' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x4A, 0x6A, 0x4A }, + { /* 'K' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x4B, 0x6B, 0x4B }, + { /* 'L' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x4C, 0x6C, 0x4C }, + { /* 'M' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x4D, 0x6D, 0x4D }, + { /* 'N' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x4E, 0x6E, 0x4E }, + { /* 'O' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x4F, 0x6F, 0x4F }, + { /* 'P' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x50, 0x70, 0x50 }, + { /* 'Q' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x51, 0x71, 0x51 }, + { /* 'R' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x52, 0x72, 0x52 }, + { /* 'S' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x53, 0x73, 0x53 }, + { /* 'T' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x54, 0x74, 0x54 }, + { /* 'U' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x55, 0x75, 0x55 }, + { /* 'V' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x56, 0x76, 0x56 }, + { /* 'W' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x57, 0x77, 0x57 }, + { /* 'X' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x58, 0x78, 0x58 }, + { /* 'Y' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x59, 0x79, 0x59 }, + { /* 'Z' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_UPPER, 0x5A, 0x7A, 0x5A }, + { /* '[' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x5B, 0x5B, 0x5B }, + { /* '\' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x5C, 0x5C, 0x5C }, + { /* ']' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x5D, 0x5D, 0x5D }, + { /* '^' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x5E, 0x5E, 0x5E }, + { /* '_' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x5F, 0x5F, 0x5F }, + { /* '`' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x60, 0x60, 0x60 }, + { /* 'a' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER | CTYPE_XDIGIT, 0x41, 0x61, 0x61 }, + { /* 'b' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER | CTYPE_XDIGIT, 0x42, 0x62, 0x62 }, + { /* 'c' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER | CTYPE_XDIGIT, 0x43, 0x63, 0x63 }, + { /* 'd' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER | CTYPE_XDIGIT, 0x44, 0x64, 0x64 }, + { /* 'e' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER | CTYPE_XDIGIT, 0x45, 0x65, 0x65 }, + { /* 'f' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER | CTYPE_XDIGIT, 0x46, 0x66, 0x66 }, + { /* 'g' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x47, 0x67, 0x67 }, + { /* 'h' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x48, 0x68, 0x68 }, + { /* 'i' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x49, 0x69, 0x69 }, + { /* 'j' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x4A, 0x6A, 0x6A }, + { /* 'k' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x4B, 0x6B, 0x6B }, + { /* 'l' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x4C, 0x6C, 0x6C }, + { /* 'm' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x4D, 0x6D, 0x6D }, + { /* 'n' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x4E, 0x6E, 0x6E }, + { /* 'o' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x4F, 0x6F, 0x6F }, + { /* 'p' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x50, 0x70, 0x70 }, + { /* 'q' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x51, 0x71, 0x71 }, + { /* 'r' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x52, 0x72, 0x72 }, + { /* 's' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x53, 0x73, 0x73 }, + { /* 't' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x54, 0x74, 0x74 }, + { /* 'u' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x55, 0x75, 0x75 }, + { /* 'v' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x56, 0x76, 0x76 }, + { /* 'w' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x57, 0x77, 0x77 }, + { /* 'x' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x58, 0x78, 0x78 }, + { /* 'y' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x59, 0x79, 0x79 }, + { /* 'z' */ CTYPE_GRAPH | CTYPE_ALPHA | CTYPE_LOWER, 0x5A, 0x7A, 0x7A }, + { /* '{' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x7B, 0x7B, 0x7B }, + { /* '|' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x7C, 0x7C, 0x7C }, + { /* '}' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x7D, 0x7D, 0x7D }, + { /* '~' */ CTYPE_GRAPH | CTYPE_PUNCT, 0x7E, 0x7E, 0x7E }, + { /* DEL */ CTYPE_CNTRL, 0x7F, 0x7F, 0x7F }, + { CTYPE_CBYTE, 0x80, 0x80, 0x80 }, + { CTYPE_CBYTE, 0x81, 0x81, 0x81 }, + { CTYPE_CBYTE, 0x82, 0x82, 0x82 }, + { CTYPE_CBYTE, 0x83, 0x83, 0x83 }, + { CTYPE_CBYTE, 0x84, 0x84, 0x84 }, + { CTYPE_CBYTE, 0x85, 0x85, 0x85 }, + { CTYPE_CBYTE, 0x86, 0x86, 0x86 }, + { CTYPE_CBYTE, 0x87, 0x87, 0x87 }, + { CTYPE_CBYTE, 0x88, 0x88, 0x88 }, + { CTYPE_CBYTE, 0x89, 0x89, 0x89 }, + { CTYPE_CBYTE, 0x8A, 0x8A, 0x8A }, + { CTYPE_CBYTE, 0x8B, 0x8B, 0x8B }, + { CTYPE_CBYTE, 0x8C, 0x8C, 0x8C }, + { CTYPE_CBYTE, 0x8D, 0x8D, 0x8D }, + { CTYPE_CBYTE, 0x8E, 0x8E, 0x8E }, + { CTYPE_CBYTE, 0x8F, 0x8F, 0x8F }, + { CTYPE_CBYTE, 0x90, 0x90, 0x90 }, + { CTYPE_CBYTE, 0x91, 0x91, 0x91 }, + { CTYPE_CBYTE, 0x92, 0x92, 0x92 }, + { CTYPE_CBYTE, 0x93, 0x93, 0x93 }, + { CTYPE_CBYTE, 0x94, 0x94, 0x94 }, + { CTYPE_CBYTE, 0x95, 0x95, 0x95 }, + { CTYPE_CBYTE, 0x96, 0x96, 0x96 }, + { CTYPE_CBYTE, 0x97, 0x97, 0x97 }, + { CTYPE_CBYTE, 0x98, 0x98, 0x98 }, + { CTYPE_CBYTE, 0x99, 0x99, 0x99 }, + { CTYPE_CBYTE, 0x9A, 0x9A, 0x9A }, + { CTYPE_CBYTE, 0x9B, 0x9B, 0x9B }, + { CTYPE_CBYTE, 0x9C, 0x9C, 0x9C }, + { CTYPE_CBYTE, 0x9D, 0x9D, 0x9D }, + { CTYPE_CBYTE, 0x9E, 0x9E, 0x9E }, + { CTYPE_CBYTE, 0x9F, 0x9F, 0x9F }, + { CTYPE_CBYTE, 0xA0, 0xA0, 0xA0 }, + { CTYPE_CBYTE, 0xA1, 0xA1, 0xA1 }, + { CTYPE_CBYTE, 0xA2, 0xA2, 0xA2 }, + { CTYPE_CBYTE, 0xA3, 0xA3, 0xA3 }, + { CTYPE_CBYTE, 0xA4, 0xA4, 0xA4 }, + { CTYPE_CBYTE, 0xA5, 0xA5, 0xA5 }, + { CTYPE_CBYTE, 0xA6, 0xA6, 0xA6 }, + { CTYPE_CBYTE, 0xA7, 0xA7, 0xA7 }, + { CTYPE_CBYTE, 0xA8, 0xA8, 0xA8 }, + { CTYPE_CBYTE, 0xA9, 0xA9, 0xA9 }, + { CTYPE_CBYTE, 0xAA, 0xAA, 0xAA }, + { CTYPE_CBYTE, 0xAB, 0xAB, 0xAB }, + { CTYPE_CBYTE, 0xAC, 0xAC, 0xAC }, + { CTYPE_CBYTE, 0xAD, 0xAD, 0xAD }, + { CTYPE_CBYTE, 0xAE, 0xAE, 0xAE }, + { CTYPE_CBYTE, 0xAF, 0xAF, 0xAF }, + { CTYPE_CBYTE, 0xB0, 0xB0, 0xB0 }, + { CTYPE_CBYTE, 0xB1, 0xB1, 0xB1 }, + { CTYPE_CBYTE, 0xB2, 0xB2, 0xB2 }, + { CTYPE_CBYTE, 0xB3, 0xB3, 0xB3 }, + { CTYPE_CBYTE, 0xB4, 0xB4, 0xB4 }, + { CTYPE_CBYTE, 0xB5, 0xB5, 0xB5 }, + { CTYPE_CBYTE, 0xB6, 0xB6, 0xB6 }, + { CTYPE_CBYTE, 0xB7, 0xB7, 0xB7 }, + { CTYPE_CBYTE, 0xB8, 0xB8, 0xB8 }, + { CTYPE_CBYTE, 0xB9, 0xB9, 0xB9 }, + { CTYPE_CBYTE, 0xBA, 0xBA, 0xBA }, + { CTYPE_CBYTE, 0xBB, 0xBB, 0xBB }, + { CTYPE_CBYTE, 0xBC, 0xBC, 0xBC }, + { CTYPE_CBYTE, 0xBD, 0xBD, 0xBD }, + { CTYPE_CBYTE, 0xBE, 0xBE, 0xBE }, + { CTYPE_CBYTE, 0xBF, 0xBF, 0xBF }, + { 0x00, 0xC0, 0xC0, 0xC0 }, + { 0x00, 0xC1, 0xC1, 0xC1 }, + { 0x00, 0xC2, 0xC2, 0xC2 }, + { 0x00, 0xC3, 0xC3, 0xC3 }, + { 0x00, 0xC4, 0xC4, 0xC4 }, + { 0x00, 0xC5, 0xC5, 0xC5 }, + { 0x00, 0xC6, 0xC6, 0xC6 }, + { 0x00, 0xC7, 0xC7, 0xC7 }, + { 0x00, 0xC8, 0xC8, 0xC8 }, + { 0x00, 0xC9, 0xC9, 0xC9 }, + { 0x00, 0xCA, 0xCA, 0xCA }, + { 0x00, 0xCB, 0xCB, 0xCB }, + { 0x00, 0xCC, 0xCC, 0xCC }, + { 0x00, 0xCD, 0xCD, 0xCD }, + { 0x00, 0xCE, 0xCE, 0xCE }, + { 0x00, 0xCF, 0xCF, 0xCF }, + { 0x00, 0xD0, 0xD0, 0xD0 }, + { 0x00, 0xD1, 0xD1, 0xD1 }, + { 0x00, 0xD2, 0xD2, 0xD2 }, + { 0x00, 0xD3, 0xD3, 0xD3 }, + { 0x00, 0xD4, 0xD4, 0xD4 }, + { 0x00, 0xD5, 0xD5, 0xD5 }, + { 0x00, 0xD6, 0xD6, 0xD6 }, + { 0x00, 0xD7, 0xD7, 0xD7 }, + { 0x00, 0xD8, 0xD8, 0xD8 }, + { 0x00, 0xD9, 0xD9, 0xD9 }, + { 0x00, 0xDA, 0xDA, 0xDA }, + { 0x00, 0xDB, 0xDB, 0xDB }, + { 0x00, 0xDC, 0xDC, 0xDC }, + { 0x00, 0xDD, 0xDD, 0xDD }, + { 0x00, 0xDE, 0xDE, 0xDE }, + { 0x00, 0xDF, 0xDF, 0xDF }, + { 0x00, 0xE0, 0xE0, 0xE0 }, + { 0x00, 0xE1, 0xE1, 0xE1 }, + { 0x00, 0xE2, 0xE2, 0xE2 }, + { 0x00, 0xE3, 0xE3, 0xE3 }, + { 0x00, 0xE4, 0xE4, 0xE4 }, + { 0x00, 0xE5, 0xE5, 0xE5 }, + { 0x00, 0xE6, 0xE6, 0xE6 }, + { 0x00, 0xE7, 0xE7, 0xE7 }, + { 0x00, 0xE8, 0xE8, 0xE8 }, + { 0x00, 0xE9, 0xE9, 0xE9 }, + { 0x00, 0xEA, 0xEA, 0xEA }, + { 0x00, 0xEB, 0xEB, 0xEB }, + { 0x00, 0xEC, 0xEC, 0xEC }, + { 0x00, 0xED, 0xED, 0xED }, + { 0x00, 0xEE, 0xEE, 0xEE }, + { 0x00, 0xEF, 0xEF, 0xEF }, + { 0x00, 0xF0, 0xF0, 0xF0 }, + { 0x00, 0xF1, 0xF1, 0xF1 }, + { 0x00, 0xF2, 0xF2, 0xF2 }, + { 0x00, 0xF3, 0xF3, 0xF3 }, + { 0x00, 0xF4, 0xF4, 0xF4 }, + { 0x00, 0xF5, 0xF5, 0xF5 }, + { 0x00, 0xF6, 0xF6, 0xF6 }, + { 0x00, 0xF7, 0xF7, 0xF7 }, + { 0x00, 0xF8, 0xF8, 0xF8 }, + { 0x00, 0xF9, 0xF9, 0xF9 }, + { 0x00, 0xFA, 0xFA, 0xFA }, + { 0x00, 0xFB, 0xFB, 0xFB }, + { 0x00, 0xFC, 0xFC, 0xFC }, + { 0x00, 0xFD, 0xFD, 0xFD }, + { 0x00, 0xFE, 0xFE, 0xFE }, + { 0x00, 0xFF, 0xFF, 0xFF }, +}; + +static const __libc_ascii_data_entry_t* __libc_get_ascii_data(int character) { + // + // We also accept EOF, for that case table is moved in single row away. + // + unsigned int index = (unsigned int)(character + 1); + + if (index <= 256) { + // + // Return item if index is valid. + // + return &__libc_ascii_data_table[index]; + } + + // + // No data available. + // + return NULL; +} + +int (isalnum)(int n) { + return isctype(n, CT_ALPHA | CT_DIGIT); +} + +int (isalpha)(int n) { + return isctype(n, CT_ALPHA); +} + +int (isblank)(int n) { + return isctype(n, CT_BLANK); +} + +int (iscntrl)(int n) { + return isctype(n, CT_CNTRL); +} + +int (isctype)(int n, int desc) { + const __libc_ascii_data_entry_t* data = __libc_get_ascii_data(n); + + if (data != NULL) { + return (data->flags & desc) != 0; + } + + return 0; +} + +int (isdigit)(int n) { + return isctype(n, CT_DIGIT); +} + +int (isgraph)(int n) { + return isctype(n, CT_PUNCT | CT_ALPHA | CT_DIGIT | CT_GRAPH); +} + +int (islower)(int n) { + return isctype(n, CT_LOWER); +} + +int (isprint)(int n) { + return isctype(n, CT_BLANK | CT_PUNCT | CT_ALPHA | CT_DIGIT); +} + +int (ispunct)(int n) { + return isctype(n, CT_PUNCT); +} + +int (isspace)(int n) { + return isctype(n, CT_SPACE); +} + +int (isupper)(int n) { + return isctype(n, CT_UPPER); +} + +int (isxdigit)(int n) { + return isctype(n, CT_XDIGIT); +} + +int (tolower)(int n) { + const __libc_ascii_data_entry_t* data = __libc_get_ascii_data(n); + + if (data != NULL) { + return data->lower; + } + + return n; +} + +int (toupper)(int n) { + const __libc_ascii_data_entry_t* data = __libc_get_ascii_data(n); + + if (data != NULL) { + return data->upper; + } + + return n; +} diff --git a/src/os/crt/format.c b/src/os/crt/format.c new file mode 100644 index 0000000..484f341 --- /dev/null +++ b/src/os/crt/format.c @@ -0,0 +1,766 @@ +#include "../crt.h" + +#define FORMAT_FLAGS_NONE (0) +#define FORMAT_FLAGS_MINUS (1 << 0) +#define FORMAT_FLAGS_PLUS (1 << 1) +#define FORMAT_FLAGS_ALT (1 << 2) +#define FORMAT_FLAGS_SPACE (1 << 3) +#define FORMAT_FLAGS_ZERO (1 << 4) +#define FORMAT_FLAGS_DONE (1 << 5) +#define FORMAT_FLAGS_LOWERCASE (1 << 6) +#define FORMAT_FLAGS_UNSIGNED (1 << 7) +#define FORMAT_FLAGS_WIDE (1 << 8) + +#define FORMAT_TYPE_NONE (0) +#define FORMAT_TYPE_CHAR (1) +#define FORMAT_TYPE_SHORT (2) +#define FORMAT_TYPE_LONG (3) +#define FORMAT_TYPE_LLONG (4) +#define FORMAT_TYPE_INTMAX (5) +#define FORMAT_TYPE_SIZE (6) +#define FORMAT_TYPE_UINTPTR (7) +#define FORMAT_TYPE_PTRDIFF (8) +#define FORMAT_TYPE_LDOUBLE (9) + +// TODO: Implement file streams. +typedef void* FILE; + +typedef struct __libc_format_status_t { + int32_t base; + uint32_t flags; + uint32_t type; + size_t max_count; + size_t processed; + size_t current; + int32_t width; + int32_t precision; + char* string; + FILE* stream; + va_list args; +} __libc_format_status_t; + +static int __libc_putc_unlocked(int character, FILE* stream) { + __libc_unused__(character); + __libc_unused__(stream); + return 0; + //return fputc(character, stream); +} + +static int __libc_format_worker_emit_char_to_string(__libc_format_status_t* status, int character, size_t count) { + size_t processed = 0; + for (size_t i = 0; i < count; ++i) { + if (status->processed < status->max_count) { + ++processed; + status->string[status->processed] = character; + } + ++(status->processed); + } + + status->current += processed; + + return processed == count; +} + +static int __libc_format_worker_emit_char_to_stream(__libc_format_status_t* status, int character, size_t count) { + size_t processed = 0; + + // TODO: optimize for small strings? For i.e. count > 16, replicate it over array and write in single batch? + for (size_t i = 0; i < count; ++i) { + if (status->processed < status->max_count) { + ++processed; + __libc_putc_unlocked(character, status->stream); + } + ++(status->processed); + } + + status->current += processed; + + return processed == count; +} + +static int __libc_format_worker_emit_string_to_string(__libc_format_status_t* status, const char* string, size_t length) { + size_t processed = 0; + // TODO: use strncpy? + for (size_t i = 0; i < length; ++i) { + if (status->processed < status->max_count) { + ++processed; + status->string[status->processed] = string[i]; + } + ++(status->processed); + } + + status->current += processed; + + return processed == length; +} + +static int __libc_format_worker_emit_string_to_stream(__libc_format_status_t* status, const char* string, size_t length) { + size_t processed = 0; + // TODO: use __libc_fwrite_unlocked + for (size_t i = 0; i < length; ++i) { + if (status->processed < status->max_count) { + ++processed; + __libc_putc_unlocked(string[i], status->stream); + } + ++(status->processed); + } + + status->current += processed; + + return processed == length; +} + +static int __libc_format_worker_emit_char(__libc_format_status_t* status, const char character, size_t count) { + if (status->stream != NULL) { + return __libc_format_worker_emit_char_to_stream(status, character, count); + } else { + return __libc_format_worker_emit_char_to_string(status, character, count); + } +} + +static int __libc_format_worker_emit_string(__libc_format_status_t* status, const char* string, size_t length) { + if (status->stream != NULL) { + return __libc_format_worker_emit_string_to_stream(status, string, length); + } else { + return __libc_format_worker_emit_string_to_string(status, string, length); + } +} + +static int __libc_format_worker_write_string(__libc_format_status_t* status, const char* string) { + size_t length = status->precision >= 0 ? strnlen_s(string, status->precision) : strlen(string); + + if (status->width == 0 || __libc_flag_has(status->flags, FORMAT_FLAGS_MINUS)) { + if (status->precision > 0) { + size_t precision = (size_t)status->precision; + length = precision < length ? precision : length; + } + + if (!__libc_format_worker_emit_string(status, string, length)) { + return 0; + } + + if ((size_t)status->width > status->current) { + length = status->width - status->current; + + if (!__libc_format_worker_emit_char(status, ' ', length)) { + return 0; + } + } + } else { + if ((size_t)status->width > length) { + size_t padding = status->width - length; + + if (!__libc_format_worker_emit_char(status, ' ', padding)) { + return 0; + } + } + + if (!__libc_format_worker_emit_string(status, string, length)) { + return 0; + } + } + + return 1; +} + +static int __libc_format_worker_write_char(__libc_format_status_t* status, const char value) { + if (__libc_flag_has(status->flags, FORMAT_FLAGS_MINUS)) { + if (!__libc_format_worker_emit_char(status, value, 1)) { + return 0; + } + + if ((size_t)(status->width) > status->current) { + if (!__libc_format_worker_emit_char(status, ' ', status->width - status->current)) { + return 0; + } + } + } else { + if (status->width != 0) { + int adjust = status->width - status->current - 1; + if (!__libc_format_worker_emit_char(status, ' ', adjust)) { + return 0; + } + } + + if (!__libc_format_worker_emit_char(status, value, 1)) { + return 0; + } + } + + return 1; +} + +static int __libc_format_worker_write_integer(__libc_format_status_t* status, uintmax_t value) { + char sign = '\0'; + + if (!__libc_flag_has(status->flags, FORMAT_FLAGS_UNSIGNED)) { + const intmax_t signvalue = (intmax_t)value; + const int is_negative= signvalue < 0; + + value = is_negative ? (uintmax_t)-signvalue : (uintmax_t)signvalue; + + if (is_negative) { + sign = '-'; + } else if (__libc_flag_has(status->flags, FORMAT_FLAGS_PLUS)) { + sign = '+'; + } else if (__libc_flag_has(status->flags, FORMAT_FLAGS_SPACE)) { + sign = ' '; + } + } + + enum { + max_int_length = 96 + }; + + char output_buffer[max_int_length]; + char* output_buffer_end = output_buffer + max_int_length; + + ptrdiff_t written = 0; + + const char* digits = __libc_flag_has(status->flags, FORMAT_FLAGS_LOWERCASE) ? __libc_ctype_digits_lowercase : __libc_ctype_digits_uppercase; // ->is_lower_case_specifier() ? libcrt_ctype::array_dec_digits : libcrt_ctype::array_hex_digits; + + // + // Write number in specified base. + // + uintmax_t remaining = value; + + if (status->precision != 0 || remaining != 0) { + do { + uintmax_t digit = remaining % status->base; + remaining /= status->base; + output_buffer_end[-++written] = digits[digit]; + } while (remaining != 0); + } + + while ((int32_t)written < status->precision) { + output_buffer_end[-++written] = '0'; + } + + unsigned padding = 0; + + if (!__libc_flag_has(status->flags, FORMAT_FLAGS_MINUS) && __libc_flag_has(status->flags, FORMAT_FLAGS_ZERO)) { + while (written < (ptrdiff_t)status->width) { + output_buffer_end[-++written] = '0'; + ++padding; + } + } + + + if (sign != 0) { + if (padding == 0) { + ++written; + } + + output_buffer_end[-written] = sign; + } else if (__libc_flag_has(status->flags, FORMAT_FLAGS_ALT)) { + // + // Write alternative number prefix. + // + switch (status->base) { + case 2: { + written += padding < 2 ? 2 - padding : 0; + output_buffer_end[-written + 0] = '0'; + output_buffer_end[-written + 1] = __libc_flag_has(status->flags, FORMAT_FLAGS_LOWERCASE) ? 'b' : 'B'; + break; + } + case 8: { + if (output_buffer_end[-written] != '0') { + output_buffer_end[-++written] = '0'; + } + + break; + } + case 16: { + written += padding < 2 ? 2 - padding : 0; + output_buffer_end[-written + 0] = '0'; + output_buffer_end[-written + 1] = __libc_flag_has(status->flags, FORMAT_FLAGS_LOWERCASE) ? 'x' : 'X'; + break; + } + default: { + break; + } + } + } + + + if (!__libc_flag_has(status->flags, FORMAT_FLAGS_MINUS)) { + char leading_character = (!__libc_flag_has(status->flags, FORMAT_FLAGS_MINUS) && __libc_flag_has(status->flags, FORMAT_FLAGS_ZERO)) ? '0' : ' '; + + // note: this may be replaced with batched write + while (written < (ptrdiff_t)status->width) { + output_buffer_end[-++written] = leading_character; + } + } + + return __libc_format_worker_emit_string(status, output_buffer_end - written, (size_t)written); +} + +static ptrdiff_t __libc_format_worker_process(__libc_format_status_t* status, const char* specifier) { + const char* original_specifier = specifier; + + if (*(++specifier) == '%') { + if (!__libc_format_worker_emit_char(status, *specifier, 1)) { + return -1; + } + + ++specifier; + return specifier - original_specifier; + } + + status->flags = FORMAT_FLAGS_NONE; + status->base = 0; + status->type = FORMAT_TYPE_NONE; + status->width = 0; + status->precision = -1; + status->current = 0; + + do { + switch (*specifier) { + case '-': { + status->flags |= FORMAT_FLAGS_MINUS; + ++specifier; + break; + } + case '+': { + status->flags |= FORMAT_FLAGS_PLUS; + ++specifier; + break; + } + case '#': { + status->flags |= FORMAT_FLAGS_ALT; + ++specifier; + break; + } + case ' ': { + status->flags |= FORMAT_FLAGS_SPACE; + ++specifier; + break; + } + case '0': { + status->flags |= FORMAT_FLAGS_ZERO; + ++specifier; + break; + } + default: { + status->flags |= FORMAT_FLAGS_DONE; + } + } + } while (!__libc_flag_has(status->flags, FORMAT_FLAGS_DONE)); + + if (*specifier == '*') { + int width = va_arg(status->args, int); + + if (width < 0) { + status->flags |= FORMAT_FLAGS_MINUS; + width = -width; + } + + status->width = width; + ++specifier; + } else { + int width = (int)strtol(specifier, (char**)&specifier, 10); + if (width < 0) { + status->flags |= FORMAT_FLAGS_MINUS; + width = -width; + } + + status->width = width; + } + + if (*specifier == '.') { + ++specifier; + + if (*specifier == '*') { + status->precision = va_arg(status->args, int); + ++specifier; + } else { + status->precision = (int)strtol(specifier, (char**)&specifier, 10); + } + + status->flags = __libc_flag_unset(status->flags, FORMAT_FLAGS_ZERO); + } + + switch (*(specifier++)) { + case 'h': { + if (*specifier == 'h') { + status->type = FORMAT_TYPE_CHAR; + ++specifier; + } else { + status->type = FORMAT_TYPE_SHORT; + } + + break; + } + case 'l': { + if (*specifier == 'l') { + status->type = FORMAT_TYPE_LLONG; + ++specifier; + } else { + status->type = FORMAT_TYPE_LONG; + } + break; + } + case 'j': { + status->type = FORMAT_TYPE_INTMAX; + break; + } + case 'z': { + status->type = FORMAT_TYPE_SIZE; + break; + } + case 't': { + status->type = FORMAT_TYPE_PTRDIFF; + break; + } + case 'L': { + status->type = FORMAT_TYPE_LDOUBLE; + break; + } + default: { + --specifier; + break; + } + } + + switch (*specifier) { + case 'b': { + status->flags |= FORMAT_FLAGS_LOWERCASE; + } + case 'B': { + status->flags |= FORMAT_FLAGS_UNSIGNED; + status->base = 2; + break; + } + case 'o': { + status->flags |= FORMAT_FLAGS_UNSIGNED; + status->base = 8; + break; + } + case 'u': { + status->flags |= FORMAT_FLAGS_UNSIGNED; + } + case 'd': + case 'i': { + status->base = 10; + break; + } + case 'x': { + status->flags |= FORMAT_FLAGS_LOWERCASE; + } + case 'X': { + status->flags |= FORMAT_FLAGS_UNSIGNED; + status->base = 16; + break; + } + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': { + break; + } + case 'c': { + char value = (char)va_arg(status->args, int); + + if (!__libc_format_worker_write_char(status, value)) { + return -1; + } + + ++specifier; + return specifier - original_specifier; + } + case 's': { + char* value = (char*)va_arg(status->args, char*); + + if (value == NULL) { + if (!__libc_format_worker_write_string(status, "(null)")) { + return -1; + } + } else { + if (!__libc_format_worker_write_string(status, value)) { + return -1; + } + } + + ++specifier; + return specifier - original_specifier; + } + case 'p': { + status->flags |= FORMAT_FLAGS_LOWERCASE; + } + case 'P': { + status->flags |= FORMAT_FLAGS_UNSIGNED | FORMAT_FLAGS_ALT; + status->base = 16; + status->type = FORMAT_TYPE_UINTPTR; + status->precision = sizeof(uintptr_t) * 2; + break; + } + case 'n': { + int* output = va_arg(status->args, int*); + (*output) = (int)status->processed; + ++specifier; + return specifier - original_specifier; + } + default: { + return 0; + } + } + + if (status->base != 0) { + if (__libc_flag_has(status->flags, FORMAT_FLAGS_UNSIGNED)) { + uintmax_t value; + + switch (status->type) { + case FORMAT_TYPE_CHAR: { + value = (uintmax_t)(unsigned char)va_arg(status->args, int); + break; + } + case FORMAT_TYPE_SHORT: { + value = (uintmax_t)(unsigned short)va_arg(status->args, int); + break; + } + case FORMAT_TYPE_NONE: { + value = (uintmax_t)(unsigned int)va_arg(status->args, unsigned int); + break; + } + case FORMAT_TYPE_LONG: { + value = (uintmax_t)(unsigned long)va_arg(status->args, unsigned long); + break; + } + case FORMAT_TYPE_LLONG: { + value = (uintmax_t)(unsigned long long)va_arg(status->args, unsigned long long); + break; + } + case FORMAT_TYPE_SIZE: { + value = (uintmax_t)(size_t)va_arg(status->args, size_t); + break; + } + case FORMAT_TYPE_UINTPTR: { + value = (uintmax_t)(uintptr_t)va_arg(status->args, uintptr_t); + break; + } + case FORMAT_TYPE_PTRDIFF: { + value = (uintmax_t)(ptrdiff_t)va_arg(status->args, ptrdiff_t); + break; + } + case FORMAT_TYPE_INTMAX: { + value = (uintmax_t)(intmax_t)va_arg(status->args, intmax_t); + break; + } + default: { + return -1; + } + } + + if (!__libc_format_worker_write_integer(status, value)) { + return -1; + } + } else { + uintmax_t value; + + switch (status->type) { + case FORMAT_TYPE_CHAR: { + value = (uintmax_t)(intmax_t)(char)va_arg(status->args, int); + break; + } + case FORMAT_TYPE_SHORT: { + value = (uintmax_t)(intmax_t)(short)va_arg(status->args, int); + break; + } + case FORMAT_TYPE_NONE: { + value = (uintmax_t)(intmax_t)(int)va_arg(status->args, int); + break; + } + case FORMAT_TYPE_LONG: { + value = (uintmax_t)(intmax_t)(long)va_arg(status->args, long); + break; + } + case FORMAT_TYPE_LLONG: { + value = (uintmax_t)(intmax_t)(long long)va_arg(status->args, long long); + break; + } + case FORMAT_TYPE_SIZE: { + value = (uintmax_t)(intmax_t)(size_t)va_arg(status->args, size_t); + break; + } + case FORMAT_TYPE_UINTPTR: { + value = (uintmax_t)(intmax_t)(uintptr_t)va_arg(status->args, uintptr_t); + break; + } + case FORMAT_TYPE_PTRDIFF: { + value = (uintmax_t)(intmax_t)(ptrdiff_t)va_arg(status->args, ptrdiff_t); + break; + } + case FORMAT_TYPE_INTMAX: { + value = (uintmax_t)(intmax_t)va_arg(status->args, intmax_t); + break; + } + default: { + return -1; + } + } + + if (!__libc_format_worker_write_integer(status, value)) { + return -1; + } + } + + if (__libc_flag_has(status->flags, FORMAT_FLAGS_MINUS) && (status->current - status->width) != 0) { + __libc_format_worker_emit_char(status, ' ', status->width - status->current); + } + } + + ++specifier; + return specifier - original_specifier; +} + +static errno_t __libc_format_worker(__libc_format_status_t* status, const char* format) { + while (*format != '\0') { + const char* mark = format; + + while (*format != '\0' && *format != '%') { + ++format; + } + + if (mark != format) { + if (!__libc_format_worker_emit_string(status, mark, format - mark)) { + return EINVAL; + } + } + + if (*format == '%') { + ptrdiff_t consumed = __libc_format_worker_process(status, format); + if (consumed > 0) { + format += consumed; + } else if (consumed == 0) { + ++format; + if (!__libc_format_worker_emit_char(status, *format, 1)) { + return EINVAL; + } + } else { + return EINVAL; + } + } + } + + // + // Put NUL character at the end of string / buffer. + // + if (status->string != NULL) { + if (status->processed < status->max_count) { + status->string[status->processed] = '\0'; + } else { + status->string[status->max_count - 1] = '\0'; + } + } + + va_end(status->args); + + return 0; +} + + +// LIBC public functions + +int (snprintf)(char *string, size_t size, const char *format, ...) { + __libc_format_status_t status = { + .base = 0, + .flags = 0, + .type = 0, + .max_count = size, + .processed = 0, + .current = 0, + .width = 0, + .precision = 0, + .string = string, + .stream = NULL + }; + + va_start(status.args, format); + errno_t result = __libc_format_worker(&status, format); + va_end(status.args); + + if (result == 0) { + return (int)status.processed; + } + + return -1; +} + +int (sprintf)(char *string, const char *format, ...) { + __libc_format_status_t status = { + .base = 0, + .flags = 0, + .type = 0, + .max_count = SIZE_MAX, + .processed = 0, + .current = 0, + .width = 0, + .precision = 0, + .string = string, + .stream = NULL + }; + + va_start(status.args, format); + errno_t result = __libc_format_worker(&status, format); + va_end(status.args); + + if (result == 0) { + return (int)status.processed; + } + + return -1; +} + +int (vsnprintf)(char *string, size_t size, const char *format, va_list arglist) { + __libc_format_status_t status = { + .base = 0, + .flags = 0, + .type = 0, + .max_count = size, + .processed = 0, + .current = 0, + .width = 0, + .precision = 0, + .string = string, + .stream = NULL + }; + + va_copy(status.args, arglist); + errno_t result = __libc_format_worker(&status, format); + va_end(status.args); + + if (result == 0) { + return (int)status.processed; + } + + return -1; +} + +int (vsprintf)(char *string, const char *format, va_list arglist) { + __libc_format_status_t status = { + .base = 0, + .flags = 0, + .type = 0, + .max_count = SIZE_MAX, + .processed = 0, + .current = 0, + .width = 0, + .precision = 0, + .string = string, + .stream = NULL + }; + + va_copy(status.args, arglist); + errno_t result = __libc_format_worker(&status, format); + va_end(status.args); + + if (result == 0) { + return (int)status.processed; + } + + return -1; +} diff --git a/src/os/crt/memory.c b/src/os/crt/memory.c new file mode 100644 index 0000000..08a8cd6 --- /dev/null +++ b/src/os/crt/memory.c @@ -0,0 +1,119 @@ +#include "../crt.h" + +// +// Some of these functions may be optimized by compiler to recursive call. +// In OSDev this often ends in triple fault due to code or data corruption +// due to stack overflow :) +// +// In general, compiler may generate similar assembly for i.e. memcpy function: +// +// memcpy(void*, void const*, unsigned long): +// test rdx, rdx +// je .L3 +// push rbx +// mov rbx, rdx +// call memcpy <-- which is recursive call :) +// lea rax, [rax+rbx] +// pop rbx +// ret +// .L3: +// mov rax, rdi +// ret +// +// Compiler expects these functions to be implemented by standard library. +// Since we are in freestanding environment, it's free to optimize any loop in +// these functions to extern calls. +// +// There are three general solutions to that: +// +// - implement these functions in assembly, +// - tag these functions by __optimize__ attribute. +// +// Optionally, we may explicitely don't use any C standard functions, but with +// `-fno-builtin` in CFLAGS. +// +// With that flag, compiler cannot use these functions for example in context of +// assignment operator. +// However, we still can use `__builtin_` prefix to order compiler to +// try to optimize function call for few cases anyway. +// + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +void* (memchr)(const void* ptr, int ch, size_t count) { + const unsigned char* pointer = (const unsigned char*)ptr; + const unsigned char value = (const unsigned char)(unsigned int)ch; + + while (count != 0 && (*pointer != value)) { + ++pointer; + --count; + } + + return (void*)(char*)((count != 0) ? pointer : NULL); +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +int (memcmp)(const void* lhs, const void* rhs, size_t count) { + if (count == 0) { + return 0; + } + + const signed char* source1 = (const signed char*)lhs; + const signed char* source2 = (const signed char*)rhs; + + int result = 0; + + while (count--) { + result = (int)(*source1++) - (int)(*source2++); + + if (result != 0) { + break; + } + } + + return result; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +void* (memcpy)(void *restrict dest, const void *restrict src, size_t count) { + unsigned char* destination = (unsigned char*)dest; + const unsigned char* source = (const unsigned char*)src; + + for (; count != 0; --count) { + (*destination++) = (*source++); + } + + return destination; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +void* (memmove)(void* dest, const void* src, size_t count) { + unsigned char* destination = (unsigned char*)dest; + const unsigned char* source = (const unsigned char*)src; + + if ((destination <= source) && (destination >= (source + count))) { + for (; count != 0; --count) { + (*destination++) = (*source++); + } + } else { + destination += count - 1; + source += count - 1; + + for (; count != 0; --count) { + (*destination--) = (*source--); + } + } + + return destination; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +void* (memset)(void *dest, int ch, size_t count) { + const unsigned char value = (unsigned char)(unsigned int)ch; + unsigned char* destination = (unsigned char*)destination; + + for (; count != 0; --count) { + (*destination++) = value; + } + + return destination; +} diff --git a/src/os/crt/scan.c b/src/os/crt/scan.c new file mode 100644 index 0000000..1a570e3 --- /dev/null +++ b/src/os/crt/scan.c @@ -0,0 +1,583 @@ +#include "../crt.h" + + +#define SCAN_FLAGS_NONE (0) +#define SCAN_FLAGS_SUPPRESSED (1 << 0) +#define SCAN_FLAGS_TYPE_CHAR (1 << 1) +#define SCAN_FLAGS_TYPE_SHORT (1 << 2) +#define SCAN_FLAGS_TYPE_LONG (1 << 3) +#define SCAN_FLAGS_TYPE_LLONG (1 << 4) +#define SCAN_FLAGS_TYPE_SIZE (1 << 5) +#define SCAN_FLAGS_TYPE_PTRDIFF (1 << 6) +#define SCAN_FLAGS_TYPE_INTPTR (1 << 7) +#define SCAN_FLAGS_TYPE_LDOUBLE (1 << 8) +#define SCAN_FLAGS_TYPE_INTMAX (1 << 9) +#define SCAN_FLAGS_TYPE_UNSIGNED (1 << 31) +#define SCAN_FLAGS_ALL_TYPES (SCAN_FLAGS_TYPE_CHAR | SCAN_FLAGS_TYPE_SHORT | SCAN_FLAGS_TYPE_LONG | SCAN_FLAGS_TYPE_LLONG | SCAN_FLAGS_TYPE_SIZE | SCAN_FLAGS_TYPE_PTRDIFF | SCAN_FLAGS_TYPE_INTPTR | SCAN_FLAGS_TYPE_LDOUBLE | SCAN_FLAGS_TYPE_INTMAX | SCAN_FLAGS_TYPE_UNSIGNED) +#define SCAN_FLAGS_TYPE_UCHAR (SCAN_FLAGS_TYPE_UNSIGNED | SCAN_FLAGS_TYPE_CHAR) +#define SCAN_FLAGS_TYPE_USHORT (SCAN_FLAGS_TYPE_UNSIGNED | SCAN_FLAGS_TYPE_SHORT) +#define SCAN_FLAGS_TYPE_ULONG (SCAN_FLAGS_TYPE_UNSIGNED | SCAN_FLAGS_TYPE_LONG) +#define SCAN_FLAGS_TYPE_ULLONG (SCAN_FLAGS_TYPE_UNSIGNED | SCAN_FLAGS_TYPE_LLONG) +#define SCAN_FLAGS_TYPE_UINTMAX (SCAN_FLAGS_TYPE_UNSIGNED | SCAN_FLAGS_TYPE_INTMAX) +#define SCAN_FLAGS_TYPE_UINTPTR (SCAN_FLAGS_TYPE_UNSIGNED | SCAN_FLAGS_TYPE_INTPTR) + +// TODO: Implemement streams to use this function. +typedef void* FILE; + +typedef struct __libc_scan_status_t { + FILE* stream; + const char* string; + int matched; + int flags; + int base; + int precision; + size_t processed; + size_t current; + unsigned int width; + va_list arg; +} __libc_scan_status_t; + +static int __libc_getc_unlocked(FILE* stream) { + (void)stream; + return EOF; +} + +static int __libc_ungetc_unlocked(int character, FILE* stream) { + (void)character; + (void)stream; + return EOF; +} + +static int __libc_scan_worker_get_char(__libc_scan_status_t* status) { + int result = EOF; + + if (status->stream != NULL) { + result = __libc_getc_unlocked(status->stream); + } else { + result = (*status->string == '\0') ? EOF : (int)(unsigned char)*((status->string)++); + } + + if (result != EOF) { + ++(status->processed); + ++(status->current); + } + + return result; +} + +static int __libc_scan_in_scanset(const char* scanlist, const char* endscanlist, int character) { + int previous = -1; + + while (scanlist != endscanlist) { + if ((*scanlist == '-') && (previous != -1)) { + if (++scanlist == endscanlist) { + return character == '-'; + } + + while (++previous <= (unsigned char)*scanlist) { + if (previous == character) { + return 1; + } + + previous = -1; + } + } else { + if (character == (int)(unsigned char)(*scanlist)) { + return 1; + } + + previous = (unsigned char)(*scanlist++); + } + } + + return 0; +} + +static void __libc_scan_worker_unget_char(__libc_scan_status_t* status, int character) { + if (status->stream != NULL) { + __libc_ungetc_unlocked(character, status->stream); + } else { + --(status->string); + } + + --(status->processed); + --(status->current); +} + +static const char* __libc_scan_worker_process(__libc_scan_status_t* status, const char* specifier) { + int character = EOF; + + const char* original_specifier = specifier; + + if (*(++specifier) == '%') { + character = __libc_scan_worker_get_char(status); + + switch (character) { + case EOF: { + if (status->matched == 0) { + status->matched = -1; + } + + return NULL; + } + case '%': { + return ++specifier; + } + default: { + __libc_scan_worker_unget_char(status, character); + break; + } + } + } + + status->flags = 0; + status->base = -1; + status->current = 0; + status->width = 0; + status->precision = 0; + + if (*specifier == '*') { + status->flags |= SCAN_FLAGS_SUPPRESSED; + ++specifier; + } + + // + // Parse width specifier. + // + const char* previous_specifier = specifier; + status->width = (int)strtol(specifier, (char**)&specifier, 10); + + if (specifier == previous_specifier) { + status->width = UINT_MAX; + } + + // + // Parse specifier. + // + switch (*(specifier++)) { + case 'h': { + if (*specifier == 'h') { + status->flags |= SCAN_FLAGS_TYPE_CHAR; + ++specifier; + } else { + status->flags |= SCAN_FLAGS_TYPE_SHORT; + } + break; + } + case 'l': { + if (*specifier == 'l') { + status->flags |= SCAN_FLAGS_TYPE_LLONG; + ++specifier; + } else { + status->flags |= SCAN_FLAGS_TYPE_LONG; + } + break; + } + case 'j': { + status->flags |= SCAN_FLAGS_TYPE_INTMAX; + break; + } + case 'z': { + status->flags |= SCAN_FLAGS_TYPE_SIZE; + break; + } + case 't': { + status->flags |= SCAN_FLAGS_TYPE_PTRDIFF; + break; + } + case 'L': { + status->flags |= SCAN_FLAGS_TYPE_LDOUBLE; + break; + } + default: { + --specifier; + break; + } + } + + // + // Parse base specifier. + // + int value_parsed = 0; + + switch (*specifier) { + case 'd': { + status->base = 10; + break; + } + case 'i': { + status->base = 0; + break; + } + case 'o': { + status->base = 8; + status->flags |= SCAN_FLAGS_TYPE_UNSIGNED; + break; + } + case 'u': { + status->base = 10; + status->flags |= SCAN_FLAGS_TYPE_UNSIGNED; + break; + } + case 'x': { + status->base = 16; + status->flags |= SCAN_FLAGS_TYPE_UNSIGNED; + break; + } + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': { + break; + } + case 'c': { + char* ch = va_arg(status->arg, char*); + if (status->width == UINT_MAX) { + status->width = 1; + } + + while ((status->current < status->width) && ((character = __libc_scan_worker_get_char(status)) != EOF)) { + *(ch++) = character; + value_parsed = 1; + } + + if (value_parsed) { + ++status->matched; + return ++specifier; + } else { + if (status->matched == 0) { + status->matched = -1; + } + + return NULL; + } + } + case 's': { + char* ch = va_arg(status->arg, char*); + + while ((status->current < status->width) && ((character = __libc_scan_worker_get_char(status)) != EOF)) { + if (isspace(character)) { + __libc_scan_worker_unget_char(status, character); + + if (value_parsed) { + (*ch) = '\0'; + ++status->matched; + return ++specifier; + } else { + return NULL; + } + } else { + value_parsed = 1; + *(ch++) = character; + } + } + + if (value_parsed) { + (*ch) = '\0'; + ++status->matched; + return ++specifier; + } else { + if (status->matched == 0) { + status->matched = -1; + } + return NULL; + } + } + case '[': { + const char* end_specifier = specifier; + int negative_scanlist = 0; + if (*(++end_specifier) == '^') { + negative_scanlist = 1; + ++end_specifier; + } + + specifier = end_specifier; + + do { + ++end_specifier; + } while (*end_specifier != ']'); + + char* ch = va_arg(status->arg, char*); + + while ((status->current < status->width) && ((character = __libc_scan_worker_get_char(status)) != EOF)) { + if (negative_scanlist) { + if (__libc_scan_in_scanset(specifier, end_specifier, character)) { + __libc_scan_worker_unget_char(status, character); + break; + } + } else { + if (!__libc_scan_in_scanset(specifier, end_specifier, character)) { + __libc_scan_worker_unget_char(status, character); + break; + } + } + + value_parsed = 1; + *(ch++) = character; + } + + if (value_parsed) { + (*ch) = '\0'; + ++status->matched; + return ++end_specifier; + } else { + if (character == EOF) { + status->matched = -1; + } + + return NULL; + } + } + case 'p': { + status->base = 16; + status->flags = SCAN_FLAGS_TYPE_INTPTR | SCAN_FLAGS_TYPE_UNSIGNED; + break; + } + case 'n': { + int* value = va_arg(status->arg, int*); + (*value) = status->processed; + return ++specifier; + } + default: { + return original_specifier; + } + } + + if (status->base != -1) { + uintmax_t value = 0; + int prefix_parsed = 0; + int sign = 0; + + while ((status->current < status->width) && ((character = __libc_scan_worker_get_char(status)) != EOF)) { + if (isspace(character)) { + if (sign) { + __libc_scan_worker_unget_char(status, character); + break; + } else { + --status->current; + } + } else if (!sign) { + switch (character) { + case '-': { + sign = -1; + break; + } + case '+': { + sign = 1; + break; + } + default: { + sign = 1; + __libc_scan_worker_unget_char(status, character); + break; + } + } + } else if (!prefix_parsed) { + prefix_parsed = 1; + + if (character != '0') { + if (status->base == 0) { + status->base = 10; + } + + __libc_scan_worker_unget_char(status, character); + } else { + if ((status->current < status->width) && ((character = __libc_scan_worker_get_char(status)) != EOF)) { + if (character == 'x' || character == 'X') { + if (status->base == 0 || status->base == 16) { + status->base = 16; + } else { + __libc_scan_worker_unget_char(status, character); + value_parsed = 1; + } + } else if (character == 'b' || character == 'B') { + if (status->base == 0 || status->base == 2) { + status->base = 2; + } else { + __libc_scan_worker_unget_char(status, character); + value_parsed = 1; + } + } else { + __libc_scan_worker_unget_char(status, character); + if (status->base == 0) { + status->base = 8; + } + + value_parsed = 1; + } + } else { + value_parsed = 1; + break; + } + } + } else { + // TODO: possible sign bug + const char* digits = memchr(&__libc_ctype_digits_lowercase[0], tolower(character), status->base); + if (digits == NULL) { + __libc_scan_worker_unget_char(status, character); + break; + } + + value *= status->base; + value += digits - &__libc_ctype_digits_lowercase[0]; + value_parsed = 1; + } + } + + if (!value_parsed) { + if ((status->matched == 0) && (character == EOF)) { + status->matched = -1; + } + + return NULL; + } + + if (!__libc_flag_has(status->flags, SCAN_FLAGS_SUPPRESSED)) { + switch (status->flags & SCAN_FLAGS_ALL_TYPES) { + case SCAN_FLAGS_TYPE_CHAR: { + *(va_arg(status->arg, char*)) = (char)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_UCHAR: { + *(va_arg(status->arg, unsigned char*)) = (unsigned char)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_SHORT: { + *(va_arg(status->arg, short*)) = (short)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_USHORT: { + *(va_arg(status->arg, unsigned short*)) = (unsigned short)(value * sign); + break; + } + case SCAN_FLAGS_NONE: { + *(va_arg(status->arg, int*)) = (int)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_UNSIGNED: { + *(va_arg(status->arg, unsigned int*)) = (unsigned int)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_LONG: { + *(va_arg(status->arg, long*)) = (long)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_ULONG: { + *(va_arg(status->arg, unsigned long*)) = (unsigned long)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_LLONG: { + *(va_arg(status->arg, long long*)) = (long long)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_ULLONG: { + *(va_arg(status->arg, unsigned long long*)) = (unsigned long long)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_INTMAX: { + *(va_arg(status->arg, intmax_t*)) = (intmax_t)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_UINTMAX: { + *(va_arg(status->arg, uintmax_t*)) = (uintmax_t)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_SIZE: { + *(va_arg(status->arg, size_t*)) = (size_t)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_PTRDIFF: { + *(va_arg(status->arg, ptrdiff_t*)) = (ptrdiff_t)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_INTPTR: { + *(va_arg(status->arg, intptr_t*)) = (intptr_t)(value * sign); + break; + } + case SCAN_FLAGS_TYPE_UINTPTR: { + *(va_arg(status->arg, uintptr_t*)) = (uintptr_t)(value * sign); + break; + } + default: { + // TODO: assert that state. + return NULL; + } + } + ++status->matched; + } + return ++specifier; + } + + return NULL; +} + + +// LIBC public functions + +int (vsscanf)(const char* restrict str, const char* restrict format, va_list arglist) { + __libc_scan_status_t status = { + .stream = NULL, + .string = str, + .matched = 0, + .flags = 0, + .base = 0, + .precision = 0, + .processed = 0, + .current = 0, + .width = 0, + }; + va_copy(status.arg, arglist); + + while (*format != '\0') { + const char* rc; + if ((*format != '%') || ((rc = __libc_scan_worker_process(&status, format)) == format)) { + if (isspace(*format)) { + while (isspace(*status.string)) { + ++status.string; + ++status.processed; + } + } else { + if (*status.string != *format) { + if (*status.string == '\0' && status.matched == 0) { + status.matched = EOF; + break; + } + + //return status.matched; + break; + } else { + ++status.string; + ++status.processed; + } + } + + ++format; + } else { + if (rc == NULL) { + if ((*status.string == '\n') && (status.matched == 0)) { + status.matched = EOF; + } + + break; + } + + format = rc; + } + } + + va_end(status.arg); + + return status.matched; +} + +int (sscanf)(const char* restrict str, const char* restrict format, ...) { + va_list args; + va_start(args, format); + int result = vsscanf(str, format, args); + va_end(args); + return result; +} diff --git a/src/os/crt/stdlib.c b/src/os/crt/stdlib.c new file mode 100644 index 0000000..ce670c7 --- /dev/null +++ b/src/os/crt/stdlib.c @@ -0,0 +1,13 @@ +#include "../crt.h" + +int (abs)(int n) { + return (n < 0) ? (-n) : (n); +} + +long int (labs)(long int n) { + return (n < 0) ? (-n) : (n); +} + +long long int (llabs)(long long int n) { + return (n < 0) ? (-n) : (n); +} diff --git a/src/os/crt/string.c b/src/os/crt/string.c new file mode 100644 index 0000000..678bf33 --- /dev/null +++ b/src/os/crt/string.c @@ -0,0 +1,279 @@ +#include "../crt.h" + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +char* (strcat)(char* restrict dest, const char* restrict src) { + char* destination = dest; + + // Skip string in dest. + while (*destination != '\0') { + ++destination; + } + + // Copy string from src. + while ((*destination++ = *src++)) { + ; + } + + return dest; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +char* (strchr)(const char* str, int ch) { + char value = (char)ch; + + while (*str != '\0' && *str != value) { + ++str; + } + + if (*str == value) { + return str; + } + + return NULL; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +int (strcmp)(const char* lhs, const char* rhs) { + int result = 0; + + const unsigned char* str1 = (const unsigned char*)lhs; + const unsigned char* str2 = (const unsigned char*)rhs; + + unsigned char c1; + unsigned char c2; + + do { + c1 = (unsigned char)(*str1++); + c2 = (unsigned char)(*str2++); + + result = (c1 - c2); + } while (result == 0 && c1 != '\0'); + + return result; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +char* (strcpy)(char* restrict dest, const char* restrict src) { + char* destination = dest; + + while ((*destination++ = *src++)) { + ; + } + + return dest; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +size_t (strcspn)(const char* dest, const char* src) { + const unsigned char* str = (const unsigned char*)dest; + const unsigned char* ctrl = (const unsigned char*)src; + + // Check for other archs. + STATIC_ASSERT(CHAR_BITS == 8); + + unsigned char map[32]; + + for (size_t i = 0; i < 32; ++i) { + map[i] = 0; + } + + while (*ctrl != '\0') { + map[*ctrl >> 3] = (unsigned char)(map[*ctrl >> 3] | (1 << (*ctrl & 7))); + ++ctrl; + } + + size_t count = 0; + map[0] |= 1; // We encountered NUL char at end of loop. + + while (!(map[*str >> 3] & (1 << (*str & 7)))) { + ++count; + ++str; + } + + return count; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +size_t (strlen)(const char* str) { + const char* eos = str; + + while (*eos != '\0') { + ++eos; + } + + return eos - str; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +char* (strncat)(char* restrict dest, const char* restrict src, size_t count) { + char* start = dest; + + while (*dest != '\0') { + ++dest; + } + + --dest; + + while (count != '\0') { + --count; + + if (!(*dest++ = *src++)) { + return start; + } + } + + *dest = '\0'; + + return start; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +int (strncmp)(const char* lhs, const char* rhs, size_t count) { + if (count != 0) { + int s1 = 0; + int s2 = 0; + + do { + s1 = (unsigned char)(*lhs++); + s2 = (unsigned char)(*rhs++); + } while (--count != 0 && s1 != 0 && (s1 == s2)); + + return s1 - s2; + } + + return 0; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +char* (strncpy)(char* restrict dest, const char* restrict src, size_t count) { + char* it = dest; + + while (count != 0 && (*it++ = *src++)) { + --count; + } + + if (count != 0) { + while (--count != 0) { + *it++ = '\0'; + } + } + + return dest; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +char* (strpbrk)(const char* dest, const char* breakset) { + const unsigned char* str = (const unsigned char*)dest; + const unsigned char* ctrl = (const unsigned char*)breakset; + + // Check for other archs. + STATIC_ASSERT(CHAR_BITS == 8); + + unsigned char map[32]; + + for (size_t i = 0; i < 32; ++i) { + map[i] = 0; + } + + while (*ctrl != '\0') { + map[*ctrl >> 3] = (unsigned char)(map[*ctrl >> 3] | (1 << (*ctrl & 7))); + ++ctrl; + } + + while (*str != '\0') { + if (map[*str >> 3] & (1 << (*str & 7))) { + return (char*)(unsigned char*)str; + } + + ++str; + } + + return NULL; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +char* (strrchr)(const char* str, int ch) { + char* start = (char*)str; + char value = (char)ch; + + while (*str != '\0') { + ++str; + } + + while (--str != start && *str != value) { + ; + } + + if (*str == value) { + return (char*)str; + } + + return NULL; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +size_t (strspn)(const char* dest, const char* src) { + const unsigned char* str = (const unsigned char*)dest; + const unsigned char* ctrl = (const unsigned char*)src; + + // Check for other archs. + STATIC_ASSERT(CHAR_BITS == 8); + + unsigned char map[32]; + + for (size_t i = 0; i < 32; ++i) { + map[i] = 0; + } + + while (*ctrl != '\0') { + map[*ctrl >> 3] = (unsigned char)(map[*ctrl >> 3] | (1 << (*ctrl & 7))); + ++ctrl; + } + + if (*str != '\0') { + size_t count = 0; + + while (map[*str >> 3] & (1 << (*str & 7))) { + ++count; + ++str; + } + + return count; + } + + return 0; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +char* (strstr)(const char* str, const char* substr) { + char character; + + if ((character = (*substr++)) != '\0') { + size_t length = strlen(substr); + + do { + char sc; + do { + if ((sc = (*str++)) == '\0') { + return NULL; + } + } while (sc != character); + } while (strncmp(str, substr, length) != 0); + --str; + } + + return (char*)str; +} + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +size_t (strxfrm)(char* restrict dest, const char* restrict src, size_t count) { + size_t length = strlen(src); + + if (count != 0) { + size_t copy_size = length < count ? length : count - 1; + (void)memcpy(dest, src, copy_size); + dest[copy_size] = '\0'; + } + + return length; +} diff --git a/src/os/crt/strtox.c b/src/os/crt/strtox.c new file mode 100644 index 0000000..e719991 --- /dev/null +++ b/src/os/crt/strtox.c @@ -0,0 +1,598 @@ +#include "../crt.h" + +static errno_t __libc_string_to_uint32(const char* restrict str, char** restrict endstr, int base, uint32_t* restrict result) { + // + // Check base param values. + // + if (base < 0 || base == 1 || base > 36) { + (*endstr) = (char*)str; + (*result) = 0; + return EINVAL; + } + + const char* it = str; + int character; + + // + // Skip any whitespaces. + // + do { + character = (int)(unsigned char)(*it++); + } while (isspace(character)); + + // + // Check potential sign of value. + // + int is_negative; + if (character == '-') { + is_negative = 1; + character = (int)(unsigned char)(*it++); + } else { + is_negative = 0; + if (character == '+') { + character = (int)(unsigned char)(*it++); + } + } + + // + // Check string prefix. + // + if ((base == 0 || base == 16) && (character == '0' && (*it == 'x' || *it == 'X'))) { + // + // We have hex string. + // + character = (int)(unsigned char)it[1]; + it += 2; + base = 16; + } else if ((base == 0 || base == 2) && (character == '0' && (*it == 'b' || *it == 'B'))) { + // + // We have binary string. + // + character = (int)(unsigned char)it[1]; + it += 2; + base = 2; + } + + if (base == 0) { + // + // Either dec or oct. + // + base = (character == '0') ? 8 : 10; + } + + uint32_t cutoff = UINT32_MAX / (uint32_t)(unsigned int)base; + uint32_t cutlim = UINT32_MAX % (uint32_t)(unsigned int)base; + + uint32_t accumulator = 0; + + int direction = 0; + + errno_t status = 0; + for (;; character = (int)(unsigned char)(*it++)) { + if (isdigit(character)) { + character -= '0'; + } else if (isalpha(character)) { + character -= (isupper(character) ? ('A' - 10) : ('a' - 10)); + } else { + break; + } + + if (character >= base) { + break; + } + + if (direction < 0) { + continue; + } + + if (accumulator > cutoff || (accumulator == cutoff && (unsigned)character > cutlim)) { + direction = -1; + accumulator = UINT32_MAX; + status = ERANGE; + } else { + direction = 1; + accumulator *= (uint32_t)base; + accumulator += character; + } + } + + if (is_negative && direction > 0) { + // + // Update accumulator value + // + accumulator = (uint32_t)-(int32_t)accumulator; + } + + (*endstr) = (char*)(direction ? (it - 1) : str); + (*result) = accumulator; + + return status; +} + +static errno_t __libc_string_to_int32(const char* restrict str, char** restrict endstr, int base, int32_t* restrict result) { + // + // Check base param values. + // + if (base < 0 || base == 1 || base > 36) { + (*endstr) = (char*)str; + (*result) = 0; + return EINVAL; + } + + const char* it = str; + int character; + + // + // Skip any whitespaces. + // + do { + character = (int)(unsigned char)(*it++); + } while (isspace(character)); + + + // + // Check potential sign of value. + // + int is_negative; + if (character == '-') { + is_negative = 1; + character = (int)(unsigned char)(*it++); + } else { + is_negative = 0; + if (character == '+') { + character = (int)(unsigned char)(*it++); + } + } + + // + // Check string prefix. + // + if ((base == 0 || base == 16) && (character == '0' && (*it == 'x' || *it == 'X'))) { + // + // We have hex string. + // + character = (int)(unsigned char)it[1]; + it += 2; + base = 16; + } else if ((base == 0 || base == 2) && (character == '0' && (*it == 'b' || *it == 'B'))) { + // + // We have binary string. + // + character = (int)(unsigned char)it[1]; + it += 2; + base = 2; + } + + if (base == 0) { + // + // Either dec or oct. + // + base = (character == '0') ? 8 : 10; + } + + // + // Compute parsing cutoff bounds. + // + int32_t cutoff = (is_negative ? INT32_MIN : INT32_MAX); + int32_t cutlim = cutoff % base; + cutoff /= base; + + // + // Adjust limits for negative ranges. + // + if (is_negative) { + if (cutlim > 0) { + cutlim -= base; + cutoff += 1; + } + + cutlim = -cutlim; + } + + int32_t accumulator = 0; + + int direction = 0; + + errno_t status = 0; + + // + // Parse string. + // + for (;; character = (int)(unsigned char)(*it++)) { + if (isdigit(character)) { + character -= '0'; + } else if (isalpha(character)) { + character -= (isupper(character) ? 'A' - 10 : 'a' - 10); + } else { + // + // Invalid digit range. + // + break; + } + + if (character >= base) { + // + // Disallow alpha digits larger than base. + // + break; + } + + if (direction < 0) { + // + // Skip parsing until end of invalid / out of range number. + // + continue; + } + + if (is_negative) { + if (accumulator < cutoff || (accumulator == cutoff && character > cutlim)) { + // + // Value out of valid range. + // + direction = -1; + accumulator = INT32_MIN; + status = ERANGE; + } else { + // + // Update accumulator. + // + direction = 1; + accumulator *= base; + accumulator -= character; + } + } else { + if (accumulator > cutoff || (accumulator == cutoff && character > cutlim)) { + // + // Value out of valid range. + // + direction = -1; + accumulator = INT32_MAX; + status = ERANGE; + } else { + // + // Update accumulator. + // + direction = 1; + accumulator *= base; + accumulator += character; + } + } + } + + (*endstr) = (char*)(direction ? (it - 1) : str); + (*result) = accumulator; + + return status; +} + + +static __libc_string_to_uint64(const char* restrict str, char** restrict endstr, int base, uint64_t* restrict result) { + // + // Check base param values. + // + if (base < 0 || base == 1 || base > 36) { + (*endstr) = (char*)str; + (*result) = 0; + return EINVAL; + } + + const char* it = str; + int character; + + // + // Skip any whitespaces. + // + do { + character = (int)(unsigned char)(*it++); + } while (isspace(character)); + + // + // Check potential sign of value. + // + int is_negative; + if (character == '-') { + is_negative = 1; + character = (int)(unsigned char)(*it++); + } else { + is_negative = 0; + if (character == '+') + { + character = (int)(unsigned char)(*it++); + } + } + + // + // Check string prefix. + // + if ((base == 0 || base == 16) && (character == '0' && (*it == 'x' || *it == 'X'))) { + // + // We have hex string. + // + character = (int)(unsigned char)it[1]; + it += 2; + base = 16; + } else if ((base == 0 || base == 2) && (character == '0' && (*it == 'b' || *it == 'B'))) { + // + // We have binary string. + // + character = (int)(unsigned char)it[1]; + it += 2; + base = 2; + } + + if (base == 0) { + // + // Either dec or oct. + // + base = (character == '0') ? 8 : 10; + } + + uint64_t cutoff = UINT64_MAX / (uint64_t)(unsigned int)base; + uint64_t cutlim = UINT64_MAX % (uint64_t)(unsigned int)base; + + uint64_t accumulator = 0; + + int direction = 0; + + errno_t status = 0; + for (;; character = (int)(unsigned char)(*it++)) { + if (isdigit(character)) { + character -= '0'; + } else if (isalpha(character)) { + character -= (isupper(character) ? ('A' - 10) : ('a' - 10)); + } else { + break; + } + + if (character >= base) { + break; + } + + if (direction < 0) { + continue; + } + + if (accumulator > cutoff || (accumulator == cutoff && (unsigned)character > cutlim)) { + direction = -1; + accumulator = UINT64_MAX; + status = ERANGE; + } else { + direction = 1; + accumulator *= (uint64_t)base; + accumulator += character; + } + } + + if (is_negative && direction > 0) { + // + // Update accumulator value + // + accumulator = (uint64_t)-(int64_t)accumulator; + } + + (*endstr) = (char*)(direction ? (it - 1) : str); + (*result) = accumulator; + + return status; +} + +static errno_t __libc_string_to_int64(const char* restrict str, char** restrict endstr, int base, int64_t* restrict result) { + // + // Check base param values. + // + if (base < 0 || base == 1 || base > 36) { + (*endstr) = (char*)str; + (*result) = 0; + return EINVAL; + } + + const char* it = str; + int character; + + // + // Skip any whitespaces. + // + do { + character = (int)(unsigned char)(*it++); + } while (isspace(character)); + + + // + // Check potential sign of value. + // + int is_negative; + if (character == '-') { + is_negative = 1; + character = (int)(unsigned char)(*it++); + } else { + is_negative = 0; + if (character == '+') + { + character = (int)(unsigned char)(*it++); + } + } + + // + // Check string prefix. + // + if ((base == 0 || base == 16) && (character == '0' && (*it == 'x' || *it == 'X'))) { + // + // We have hex string. + // + character = (int)(unsigned char)it[1]; + it += 2; + base = 16; + } else if ((base == 0 || base == 2) && (character == '0' && (*it == 'b' || *it == 'B'))) { + // + // We have binary string. + // + character = (int)(unsigned char)it[1]; + it += 2; + base = 2; + } + + if (base == 0) { + // + // Either dec or oct. + // + base = (character == '0') ? 8 : 10; + } + + // + // Compute parsing cutoff bounds. + // + int64_t cutoff = (is_negative ? INT64_MIN : INT64_MAX); + int64_t cutlim = cutoff % base; + cutoff /= base; + + // + // Adjust limits for negative ranges. + // + if (is_negative) { + if (cutlim > 0) + { + cutlim -= base; + cutoff += 1; + } + + cutlim = -cutlim; + } + + int64_t accumulator = 0; + + int direction = 0; + + errno_t status = 0; + + // + // Parse string. + // + for (;; character = (int)(unsigned char)(*it++)) { + if (isdigit(character)) { + character -= '0'; + } else if (isalpha(character)) { + character -= (isupper(character) ? 'A' - 10 : 'a' - 10); + } else { + // + // Invalid digit range. + // + break; + } + + if (character >= base) { + // + // Disallow alpha digits larger than base. + // + break; + } + + if (direction < 0) { + // + // Skip parsing until end of invalid / out of range number. + // + continue; + } + + if (is_negative) { + if (accumulator < cutoff || (accumulator == cutoff && character > cutlim)) { + // + // Value out of valid range. + // + direction = -1; + accumulator = INT64_MIN; + status = ERANGE; + } else { + // + // Update accumulator. + // + direction = 1; + accumulator *= base; + accumulator -= character; + } + } else { + if (accumulator > cutoff || (accumulator == cutoff && character > cutlim)) { + // + // Value out of valid range. + // + direction = -1; + accumulator = INT64_MAX; + status = ERANGE; + } else { + // + // Update accumulator. + // + direction = 1; + accumulator *= base; + accumulator += character; + } + } + } + + (*endstr) = (char*)(direction ? (it - 1) : str); + (*result) = accumulator; + + return status; +} + + +// LIBC public functions + +long int (strtol)(const char* restrict str, char** restrict endstr, int base) { + char* end = NULL; + +#if __SIZEOF_LONG__ == __SIZEOF_LONG_LONG__ + int64_t result = 0; + errno = __libc_string_to_int64(str, &end, base, &result); +#else + int32_t result = 0; + errno = __libc_string_to_int32(str, &end, base, &result); +#endif + + if (endstr != NULL) { + (*endstr) = end; + } + + return (long int)result; +} + +long long int (strtoll)(const char* restrict str, char** restrict endstr, int base) { + char* end = NULL; + int64_t result = 0; + errno = __libc_string_to_int64(str, &end, base, &result); + + if (endstr != NULL) { + (*endstr) = end; + } + + return (long long int)result; +} + +unsigned long int (strtoul)(const char* restrict str, char** restrict endstr, int base) { + char* end = NULL; + +#if __SIZEOF_LONG__ == __SIZEOF_LONG_LONG__ + uint64_t result = 0; + errno = __libc_string_to_uint64(str, &end, base, &result); +#else + uint32_t result = 0; + errno = __libc_string_to_uint32(str, &end, base, &result); +#endif + + if (endstr != NULL) { + (*endstr) = end; + } + + return (unsigned long int)result; +} + +unsigned long long int (strtoull)(const char* restrict str, char** restrict endstr, int base) { + char* end = NULL; + uint64_t result = 0; + errno = __libc_string_to_uint64(str, &end, base, &result); + + if (endstr != NULL) { + (*endstr) = end; + } + + return (unsigned long long int)result; +} From 5621d71839df31c374609dd86edc095fbe1e0719 Mon Sep 17 00:00:00 2001 From: selmentdev Date: Tue, 4 Jul 2017 21:46:27 +0200 Subject: [PATCH 2/4] Porting on Windows machine, building on Linux machine. Classic commit :) --- src/os/common.h | 2 -- src/os/crt.h | 26 +++++++++++++++++++++++++- src/os/crt/ctype.c | 2 ++ src/os/crt/format.c | 7 +++++-- src/os/crt/memory.c | 2 +- src/os/crt/scan.c | 2 ++ src/os/crt/string.c | 32 ++++++++++++++++++++++---------- src/os/crt/strtox.c | 16 +++++++++------- src/os/kernel.c | 7 +++++++ src/os/terminal.c | 1 + src/os/terminal.h | 1 + src/os/terminal_backend_b8000.c | 1 + 12 files changed, 76 insertions(+), 23 deletions(-) diff --git a/src/os/common.h b/src/os/common.h index 71f000f..cd81e27 100644 --- a/src/os/common.h +++ b/src/os/common.h @@ -1,8 +1,6 @@ #pragma once #define UNUSED(a) (void)(a) -typedef unsigned long long size_t; - #ifdef __cplusplus #define STATIC_ASSERT(_Expression) static_assert(_Expression, #_Expression) #else diff --git a/src/os/crt.h b/src/os/crt.h index b923740..d482c24 100644 --- a/src/os/crt.h +++ b/src/os/crt.h @@ -1,5 +1,9 @@ #pragma once #include "common.h" +#include +#include +#include +#include // @@ -17,6 +21,19 @@ #define CTYPE_CBYTE 0x0400 #define CTYPE_LEADBYTE 0x8000 +#define CT_UPPER CTYPE_UPPER +#define CT_LOWER CTYPE_LOWER +#define CT_DIGIT CTYPE_DIGIT +#define CT_SPACE CTYPE_SPACE +#define CT_PUNCT CTYPE_PUNCT +#define CT_CNTRL CTYPE_CNTRL +#define CT_BLANK CTYPE_BLANK +#define CT_XDIGIT CTYPE_XDIGIT +#define CT_GRAPH CTYPE_GRAPH +#define CT_LEADBYTE CTYPE_LEADBYTE +#define CT_CBYTE CTYPE_CBYTE +#define CT_ALPHA (CTYPE_ALPHA | CTYPE_UPPER | CTYPE_LOWER) + extern int isalnum(int c); extern int isalpha(int c); extern int isblank(int c); @@ -113,7 +130,7 @@ extern char* strrchr(const char* str, int ch); extern size_t strspn(const char* dest, const char* src); extern char* strstr(const char* str, const char* substr); extern size_t strxfrm(char* restrict dest, const char* restrict src, size_t count); - +extern size_t strnlen_s(const char* str, size_t max_count); // @@ -125,3 +142,10 @@ extern long int (strtol)(const char* restrict str, char** restrict endstr, int b extern long long int (strtoll)(const char* restrict str, char** restrict endstr, int base); extern unsigned long int (strtoul)(const char* restrict str, char** restrict endstr, int base); extern unsigned long long int (strtoull)(const char* restrict str, char** restrict endstr, int base); + +// Additional LIBC macros. +#define __libc_flag_set(__Value, __Mask) ((__Value) | (__Mask)) +#define __libc_flag_unset(__Value, __Mask) ((__Value) & (~(__Mask))) +#define __libc_flag_has(__Value, __Mask) (((__Value) & (__Mask)) == (__Mask)) +#define __libc_flag_any(__Value, __Mask) (((__Value) & (__Mask)) != 0) +#define __libc_flag_none(__Value, __Mask) (((__Value) & (__Mask)) == 0) \ No newline at end of file diff --git a/src/os/crt/ctype.c b/src/os/crt/ctype.c index 10e9fc0..c9942e9 100644 --- a/src/os/crt/ctype.c +++ b/src/os/crt/ctype.c @@ -1,4 +1,6 @@ #include "../crt.h" +#include +#include const char __libc_ctype_digits_lowercase[] = "0123456789abcdefghijklmnopqrstuvwxyz"; const char __libc_ctype_digits_uppercase[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; diff --git a/src/os/crt/format.c b/src/os/crt/format.c index 484f341..4977704 100644 --- a/src/os/crt/format.c +++ b/src/os/crt/format.c @@ -1,5 +1,8 @@ #include "../crt.h" +extern const char __libc_ctype_digits_lowercase[]; +extern const char __libc_ctype_digits_uppercase[]; + #define FORMAT_FLAGS_NONE (0) #define FORMAT_FLAGS_MINUS (1 << 0) #define FORMAT_FLAGS_PLUS (1 << 1) @@ -40,8 +43,8 @@ typedef struct __libc_format_status_t { } __libc_format_status_t; static int __libc_putc_unlocked(int character, FILE* stream) { - __libc_unused__(character); - __libc_unused__(stream); + (void)character; + (void)stream; return 0; //return fputc(character, stream); } diff --git a/src/os/crt/memory.c b/src/os/crt/memory.c index 08a8cd6..d72cbc7 100644 --- a/src/os/crt/memory.c +++ b/src/os/crt/memory.c @@ -109,7 +109,7 @@ void* (memmove)(void* dest, const void* src, size_t count) { __attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) void* (memset)(void *dest, int ch, size_t count) { const unsigned char value = (unsigned char)(unsigned int)ch; - unsigned char* destination = (unsigned char*)destination; + unsigned char* destination = (unsigned char*)dest; for (; count != 0; --count) { (*destination++) = value; diff --git a/src/os/crt/scan.c b/src/os/crt/scan.c index 1a570e3..5b30628 100644 --- a/src/os/crt/scan.c +++ b/src/os/crt/scan.c @@ -1,5 +1,7 @@ #include "../crt.h" +extern const char __libc_ctype_digits_lowercase[]; +extern const char __libc_ctype_digits_uppercase[]; #define SCAN_FLAGS_NONE (0) #define SCAN_FLAGS_SUPPRESSED (1 << 0) diff --git a/src/os/crt/string.c b/src/os/crt/string.c index 678bf33..a9bdcbd 100644 --- a/src/os/crt/string.c +++ b/src/os/crt/string.c @@ -1,5 +1,7 @@ #include "../crt.h" +STATIC_ASSERT(CHAR_BIT == 8); + __attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) char* (strcat)(char* restrict dest, const char* restrict src) { char* destination = dest; @@ -26,7 +28,7 @@ char* (strchr)(const char* str, int ch) { } if (*str == value) { - return str; + return (char*)str; } return NULL; @@ -68,9 +70,6 @@ size_t (strcspn)(const char* dest, const char* src) { const unsigned char* str = (const unsigned char*)dest; const unsigned char* ctrl = (const unsigned char*)src; - // Check for other archs. - STATIC_ASSERT(CHAR_BITS == 8); - unsigned char map[32]; for (size_t i = 0; i < 32; ++i) { @@ -166,9 +165,6 @@ char* (strpbrk)(const char* dest, const char* breakset) { const unsigned char* str = (const unsigned char*)dest; const unsigned char* ctrl = (const unsigned char*)breakset; - // Check for other archs. - STATIC_ASSERT(CHAR_BITS == 8); - unsigned char map[32]; for (size_t i = 0; i < 32; ++i) { @@ -216,9 +212,6 @@ size_t (strspn)(const char* dest, const char* src) { const unsigned char* str = (const unsigned char*)dest; const unsigned char* ctrl = (const unsigned char*)src; - // Check for other archs. - STATIC_ASSERT(CHAR_BITS == 8); - unsigned char map[32]; for (size_t i = 0; i < 32; ++i) { @@ -277,3 +270,22 @@ size_t (strxfrm)(char* restrict dest, const char* restrict src, size_t count) { return length; } + +__attribute__((__optimize__("-fno-tree-loop-distribute-patterns"))) +size_t (strnlen_s)(const char* str, size_t max_count) +{ + if (str != NULL) + { + const char* last = str + max_count; + const char* it = str; + + while (it != last && *it != '\0') + { + ++it; + } + + return (size_t)(it - str); + } + + return 0; +} diff --git a/src/os/crt/strtox.c b/src/os/crt/strtox.c index e719991..fe0f887 100644 --- a/src/os/crt/strtox.c +++ b/src/os/crt/strtox.c @@ -266,7 +266,7 @@ static errno_t __libc_string_to_int32(const char* restrict str, char** restrict } -static __libc_string_to_uint64(const char* restrict str, char** restrict endstr, int base, uint64_t* restrict result) { +static errno_t __libc_string_to_uint64(const char* restrict str, char** restrict endstr, int base, uint64_t* restrict result) { // // Check base param values. // @@ -542,10 +542,10 @@ long int (strtol)(const char* restrict str, char** restrict endstr, int base) { #if __SIZEOF_LONG__ == __SIZEOF_LONG_LONG__ int64_t result = 0; - errno = __libc_string_to_int64(str, &end, base, &result); + __libc_string_to_int64(str, &end, base, &result); #else int32_t result = 0; - errno = __libc_string_to_int32(str, &end, base, &result); + __libc_string_to_int32(str, &end, base, &result); #endif if (endstr != NULL) { @@ -558,7 +558,8 @@ long int (strtol)(const char* restrict str, char** restrict endstr, int base) { long long int (strtoll)(const char* restrict str, char** restrict endstr, int base) { char* end = NULL; int64_t result = 0; - errno = __libc_string_to_int64(str, &end, base, &result); + + __libc_string_to_int64(str, &end, base, &result); if (endstr != NULL) { (*endstr) = end; @@ -572,10 +573,10 @@ unsigned long int (strtoul)(const char* restrict str, char** restrict endstr, in #if __SIZEOF_LONG__ == __SIZEOF_LONG_LONG__ uint64_t result = 0; - errno = __libc_string_to_uint64(str, &end, base, &result); + __libc_string_to_uint64(str, &end, base, &result); #else uint32_t result = 0; - errno = __libc_string_to_uint32(str, &end, base, &result); + __libc_string_to_uint32(str, &end, base, &result); #endif if (endstr != NULL) { @@ -588,7 +589,8 @@ unsigned long int (strtoul)(const char* restrict str, char** restrict endstr, in unsigned long long int (strtoull)(const char* restrict str, char** restrict endstr, int base) { char* end = NULL; uint64_t result = 0; - errno = __libc_string_to_uint64(str, &end, base, &result); + + __libc_string_to_uint64(str, &end, base, &result); if (endstr != NULL) { (*endstr) = end; diff --git a/src/os/kernel.c b/src/os/kernel.c index 2b43b36..2273ac0 100644 --- a/src/os/kernel.c +++ b/src/os/kernel.c @@ -2,6 +2,7 @@ #include "idt.h" #include "terminal.h" #include "terminal_backend_b8000.h" +#include "crt.h" int div(int a, int b) { return a/b; @@ -14,6 +15,7 @@ void _start(void* kernel_location) { TerminalBackend *con = TerminalBackendB8000(); T_ClearScreen(con); + T_SetColor(con, COLOR_WHITE, 0, 0); T_PutText(con, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\t\tXXXX\n"); T_Printf(con, "%p %x %i %s %c %u\n", con, 0x41424344, -1234, "alamakota", 'X', 1234567890123LL); @@ -25,6 +27,11 @@ void _start(void* kernel_location) { // addr <<= 4; //} + char buffer[128]; + snprintf(buffer, sizeof(buffer), "Hello from %p", _start); + T_Printf(con, "%s", buffer); + for (;;) { ; } + int a = 0, b = 0; __asm__ volatile("div %2\n" : "=d" (a), "=a" (b) diff --git a/src/os/terminal.c b/src/os/terminal.c index a4ad573..eef3669 100644 --- a/src/os/terminal.c +++ b/src/os/terminal.c @@ -1,4 +1,5 @@ #include +#include #include "common.h" #include "terminal.h" diff --git a/src/os/terminal.h b/src/os/terminal.h index 5242905..d2dde2b 100644 --- a/src/os/terminal.h +++ b/src/os/terminal.h @@ -40,3 +40,4 @@ void T_PutCharacter(TerminalBackend *tb, uint32_t ch); void T_GetSize(TerminalBackend *tb, uint16_t *w, uint16_t *h); void T_Printf(TerminalBackend *tb, const char *fmt, ...); void T_ScrollLine(TerminalBackend *tb); +void T_SetColor(TerminalBackend *tb, unsigned char fgColor, unsigned char bgColor, bool blink); \ No newline at end of file diff --git a/src/os/terminal_backend_b8000.c b/src/os/terminal_backend_b8000.c index af8a909..698cb67 100644 --- a/src/os/terminal_backend_b8000.c +++ b/src/os/terminal_backend_b8000.c @@ -24,6 +24,7 @@ static struct B8000_ContextStruct { static void B8000_ScrollLine(TerminalBackend *tb); static void B8000_SetColor(TerminalBackend *tb, unsigned char fgColor, unsigned char bgColor, bool blink) { + UNUSED(tb); B8000_Context.fgColor = fgColor; B8000_Context.bgColor = bgColor; B8000_Context.blink = blink; From f9cf374fb5292a2812c5939d235f19f6475ea8ca Mon Sep 17 00:00:00 2001 From: selmentdev Date: Tue, 4 Jul 2017 21:56:06 +0200 Subject: [PATCH 3/4] Added stack protector and CPU parking (don't burns VM and host CPU by idle loop) --- scripts/build.py | 2 +- src/os/hal.c | 9 +++++ src/os/hal.h | 1 + src/os/int_handlers.c | 78 ++++++++++++++----------------------------- src/os/int_handlers.h | 36 +++++++++++++++++++- src/os/kernel.c | 3 +- src/os/ssp.c | 16 +++++++++ 7 files changed, 88 insertions(+), 57 deletions(-) create mode 100644 src/os/ssp.c diff --git a/scripts/build.py b/scripts/build.py index 52edc56..d16900f 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -26,7 +26,7 @@ def build(): """ Build disk image with OS """ cmds_to_run = [] - cc_flags = "-std=c99 -nostdlib -c -O0 -Wall -Wextra -masm=intel -ggdb" + cc_flags = "-std=c99 -nostdlib -c -O0 -Wall -Wextra -masm=intel -ggdb -fstack-protector-strong" ld_flags = "-std=c99 -nostdlib -o kernel64 -O0 -Wall -Wextra -masm=intel -ggdb" objfiles = [] diff --git a/src/os/hal.c b/src/os/hal.c index dc842ec..c31254f 100644 --- a/src/os/hal.c +++ b/src/os/hal.c @@ -11,3 +11,12 @@ void HAL_PortOutWord(int port, unsigned short v) { void HAL_PortOutDword(int port, unsigned int v) { __asm("out dx, eax\n" : : "a" (v), "d" (port)); } + +void HAL_PauseKernel() { + // Disable interrupts, park CPU and wait for interrupt. Illogical, but doesn't burn VMs ;) + __asm__ __volatile__( + "cli\n" + "pause\n" + "hlt\n" + ); +} \ No newline at end of file diff --git a/src/os/hal.h b/src/os/hal.h index db9c608..1f5055f 100644 --- a/src/os/hal.h +++ b/src/os/hal.h @@ -3,3 +3,4 @@ void HAL_PortOutByte(int port, unsigned char v); void HAL_PortOutWord(int port, unsigned short v); void HAL_PortOutDword(int port, unsigned int v); +void HAL_PauseKernel(); \ No newline at end of file diff --git a/src/os/int_handlers.c b/src/os/int_handlers.c index 85982bc..45c6c33 100644 --- a/src/os/int_handlers.c +++ b/src/os/int_handlers.c @@ -1,37 +1,8 @@ +#include "int_handlers.h" #include "common.h" #include "terminal.h" #include "terminal_backend_b8000.h" -// Thanks to Karol Grzybowski -typedef struct TrapFrame { - uint64_t r15; - uint64_t r14; - uint64_t r13; - uint64_t r12; - uint64_t r11; - uint64_t r10; - uint64_t r9; - uint64_t r8; - uint64_t rbp; - uint64_t rdi; - uint64_t rsi; - uint64_t rdx; - uint64_t rcx; - uint64_t rbx; - uint64_t rax; - uint64_t rsp; - - /*uint64_t trap_number; - uint64_t error_code;*/ - - uint64_t rip; - uint64_t segment_cs; - /*uint64_t rflags; - uint64_t rsp; - uint64_t segment_ss;*/ - -} TrapFrame; - void GuruPanicOfDeath(const char *reason, TrapFrame *frame) { TerminalBackend *con = TerminalBackendB8000(); T_ClearScreen(con); @@ -43,29 +14,30 @@ void GuruPanicOfDeath(const char *reason, TrapFrame *frame) { T_SetColor(con, COLOR_GRAY, 0, 0); T_PutText(con, " Something went wrong!\n\n\n"); - T_SetColor(con, COLOR_YELLOW, 0, 0); - T_PutText(con, " State:\n\n"); - - T_Printf(con, " RAX: %x\t\t R8: %x\n", frame->rax, frame->r8); - T_Printf(con, " RBX: %x\t\t R9: %x\n", frame->rbx, frame->r9); - T_Printf(con, " RCX: %x\t\t R10: %x\n", frame->rcx, frame->r10); - T_Printf(con, " RDX: %x\t\t R11: %x\n", frame->rdx, frame->r11); - T_Printf(con, " RSI: %x\t\t R12: %x\n", frame->rsi, frame->r12); - T_Printf(con, " RDI: %x\t\t R13: %x\n", frame->rdi, frame->r13); - T_Printf(con, " RBP: %x\t\t R14: %x\n", frame->rbp, frame->r14); - T_Printf(con, " RSP: %x\t\t R15: %x\n", frame->rsp, frame->r15); - - T_SetColor(con, COLOR_RED, 0, 0); - T_PutText(con, "\n CS:RIP: "); - T_SetColor(con, COLOR_LIGHT_RED, 0, 0); - T_Printf(con, "%x:%x\n\n", frame->segment_cs, frame->rip); - - T_SetColor(con, COLOR_LIGHT_RED, 0, true); - T_PutText(con, "\n\n Reason: "); - T_Printf(con, "%s\n\n", reason); - - for (;;) - ; + if (frame != NULL) { + T_SetColor(con, COLOR_YELLOW, 0, 0); + T_PutText(con, " State:\n\n"); + + T_Printf(con, " RAX: %x\t\t R8: %x\n", frame->rax, frame->r8); + T_Printf(con, " RBX: %x\t\t R9: %x\n", frame->rbx, frame->r9); + T_Printf(con, " RCX: %x\t\t R10: %x\n", frame->rcx, frame->r10); + T_Printf(con, " RDX: %x\t\t R11: %x\n", frame->rdx, frame->r11); + T_Printf(con, " RSI: %x\t\t R12: %x\n", frame->rsi, frame->r12); + T_Printf(con, " RDI: %x\t\t R13: %x\n", frame->rdi, frame->r13); + T_Printf(con, " RBP: %x\t\t R14: %x\n", frame->rbp, frame->r14); + T_Printf(con, " RSP: %x\t\t R15: %x\n", frame->rsp, frame->r15); + + T_SetColor(con, COLOR_RED, 0, 0); + T_PutText(con, "\n CS:RIP: "); + T_SetColor(con, COLOR_LIGHT_RED, 0, 0); + T_Printf(con, "%x:%x\n\n", frame->segment_cs, frame->rip); + + T_SetColor(con, COLOR_LIGHT_RED, 0, true); + T_PutText(con, "\n\n Reason: "); + T_Printf(con, "%s\n\n", reason); + } + + HAL_PauseKernel(); } void Int_DE(TrapFrame *frame) { diff --git a/src/os/int_handlers.h b/src/os/int_handlers.h index a28321a..969f38e 100644 --- a/src/os/int_handlers.h +++ b/src/os/int_handlers.h @@ -1,3 +1,37 @@ #pragma once +#include +#include -void Int_DE(void); +// Thanks to Karol Grzybowski +typedef struct TrapFrame { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rbp; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t rax; + uint64_t rsp; + + /*uint64_t trap_number; + uint64_t error_code;*/ + + uint64_t rip; + uint64_t segment_cs; + /*uint64_t rflags; + uint64_t rsp; + uint64_t segment_ss;*/ + +} TrapFrame; + +void Int_DE(TrapFrame *frame); + +void GuruPanicOfDeath(const char *reason, TrapFrame *frame); diff --git a/src/os/kernel.c b/src/os/kernel.c index 2273ac0..2fd5f51 100644 --- a/src/os/kernel.c +++ b/src/os/kernel.c @@ -30,7 +30,6 @@ void _start(void* kernel_location) { char buffer[128]; snprintf(buffer, sizeof(buffer), "Hello from %p", _start); T_Printf(con, "%s", buffer); - for (;;) { ; } int a = 0, b = 0; __asm__ volatile("div %2\n" @@ -39,5 +38,5 @@ void _start(void* kernel_location) { T_PutText(con, "It came back!"); - for(;;); + HAL_PauseKernel(); } diff --git a/src/os/ssp.c b/src/os/ssp.c new file mode 100644 index 0000000..ff2fc4e --- /dev/null +++ b/src/os/ssp.c @@ -0,0 +1,16 @@ +#include "common.h" +#include + +#include "int_handlers.h" + +void* __stack_chk_guard = NULL; + +void __stack_chk_fail(void) +{ + GuruPanicOfDeath("STACK_FRAME_SMASHED", NULL); +} + +void __chk_fail(void) +{ + GuruPanicOfDeath("STACK_FRAME_SMASHED", NULL); +} \ No newline at end of file From ebea5293f36c25528f47dcf1e42def35c0f42175 Mon Sep 17 00:00:00 2001 From: selmentdev Date: Sat, 8 Jul 2017 21:26:32 +0200 Subject: [PATCH 4/4] Implemented UART16550 serial device terminal backend. --- src/os/hal.c | 18 +++ src/os/hal.h | 3 + src/os/int_handlers.c | 3 +- src/os/kernel.c | 7 +- src/os/terminal.c | 6 + src/os/terminal.h | 24 ++- src/os/terminal_backend_b8000.c | 5 + src/os/terminal_backend_b8000.h | 17 -- src/os/terminal_backend_uart16550.c | 231 ++++++++++++++++++++++++++++ src/os/terminal_backend_uart16550.h | 4 + 10 files changed, 298 insertions(+), 20 deletions(-) create mode 100644 src/os/terminal_backend_uart16550.c create mode 100644 src/os/terminal_backend_uart16550.h diff --git a/src/os/hal.c b/src/os/hal.c index c31254f..d1f42d4 100644 --- a/src/os/hal.c +++ b/src/os/hal.c @@ -12,6 +12,24 @@ void HAL_PortOutDword(int port, unsigned int v) { __asm("out dx, eax\n" : : "a" (v), "d" (port)); } +unsigned char HAL_PortInByte(int port) { + unsigned char result; + __asm__("in %b[result], %w[port]" : [result]"=a"(result) : [port]"Nd"(port)); + return result; +} + +unsigned short HAL_PortInWord(int port) { + unsigned short result; + __asm__("in %w[result], %w[port]" : [result]"=a"(result) : [port]"Nd"(port)); + return result; +} + +unsigned int HAL_PortInDword(int port) { + unsigned int result; + __asm__("in %k[result], %w[port]" : [result]"=a"(result) : [port]"Nd"(port)); + return result; +} + void HAL_PauseKernel() { // Disable interrupts, park CPU and wait for interrupt. Illogical, but doesn't burn VMs ;) __asm__ __volatile__( diff --git a/src/os/hal.h b/src/os/hal.h index 1f5055f..f4fe779 100644 --- a/src/os/hal.h +++ b/src/os/hal.h @@ -3,4 +3,7 @@ void HAL_PortOutByte(int port, unsigned char v); void HAL_PortOutWord(int port, unsigned short v); void HAL_PortOutDword(int port, unsigned int v); +unsigned char HAL_PortInByte(int port); +unsigned short HAL_PortInWord(int port); +unsigned int HAL_PortInDword(int port); void HAL_PauseKernel(); \ No newline at end of file diff --git a/src/os/int_handlers.c b/src/os/int_handlers.c index 45c6c33..3259b4f 100644 --- a/src/os/int_handlers.c +++ b/src/os/int_handlers.c @@ -2,9 +2,10 @@ #include "common.h" #include "terminal.h" #include "terminal_backend_b8000.h" +#include "hal.h" void GuruPanicOfDeath(const char *reason, TrapFrame *frame) { - TerminalBackend *con = TerminalBackendB8000(); + TerminalBackend *con = Console; T_ClearScreen(con); T_SetColor(con, COLOR_WHITE, 0, 0); diff --git a/src/os/kernel.c b/src/os/kernel.c index 2fd5f51..9841cbe 100644 --- a/src/os/kernel.c +++ b/src/os/kernel.c @@ -2,7 +2,10 @@ #include "idt.h" #include "terminal.h" #include "terminal_backend_b8000.h" +#include "terminal_backend_uart16550.h" #include "crt.h" +#include "hal.h" + int div(int a, int b) { return a/b; @@ -12,8 +15,10 @@ void _start(void* kernel_location) { UNUSED(kernel_location); SetIDTR(); - TerminalBackend *con = TerminalBackendB8000(); + Console = TerminalBackendUART16550(); + TerminalBackend *con = Console; + T_Initialize(con); T_ClearScreen(con); T_SetColor(con, COLOR_WHITE, 0, 0); T_PutText(con, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\t\tXXXX\n"); diff --git a/src/os/terminal.c b/src/os/terminal.c index eef3669..93492c1 100644 --- a/src/os/terminal.c +++ b/src/os/terminal.c @@ -3,6 +3,12 @@ #include "common.h" #include "terminal.h" +TerminalBackend* Console = NULL; + +void T_Initialize(TerminalBackend *tb) { + tb->func_initialize(tb); +} + void T_SetCursorPosition( TerminalBackend *tb, uint16_t x, uint16_t y) { tb->func_set_cursor_position(tb, x, y); diff --git a/src/os/terminal.h b/src/os/terminal.h index d2dde2b..956b57d 100644 --- a/src/os/terminal.h +++ b/src/os/terminal.h @@ -7,6 +7,24 @@ typedef struct TerminalBackend TerminalBackend; // XXX on creation limit size to 32000x32000 +#define COLOR_BLACK 0 +#define COLOR_BLUE 1 +#define COLOR_GREEN 2 +#define COLOR_CYAN 3 +#define COLOR_RED 4 +#define COLOR_MAGENTA 5 +#define COLOR_BROWN 6 +#define COLOR_GRAY 7 +#define COLOR_DARK_GRAY 8 +#define COLOR_LIGHT_BLUE 9 +#define COLOR_LIGHT_GREEN 10 +#define COLOR_LIGHT_CYAN 11 +#define COLOR_LIGHT_RED 12 +#define COLOR_LIGHT_MAGENTA 13 +#define COLOR_YELLOW 14 +#define COLOR_WHITE 15 + +typedef void (*TBfunc_Initialize)(TerminalBackend *tb); typedef void (*TBfunc_SetCursorPosition)( TerminalBackend *tb, uint16_t x, uint16_t y); typedef void (*TBfunc_GetCursorPosition)( @@ -20,6 +38,7 @@ typedef void (*TBfunc_SetColor)(TerminalBackend *tb, unsigned char fgColor, unsigned char bgColor, bool blink); struct TerminalBackend { + TBfunc_Initialize func_initialize; TBfunc_SetCursorPosition func_set_cursor_position; TBfunc_GetCursorPosition func_get_cursor_position; TBfunc_ClearScreen func_clear_screen; @@ -30,6 +49,7 @@ struct TerminalBackend { void *backend_data; }; +void T_Initialize(TerminalBackend *tb); void T_SetCursorPosition( TerminalBackend *tb, uint16_t x, uint16_t y); void T_GetCursorPosition( @@ -40,4 +60,6 @@ void T_PutCharacter(TerminalBackend *tb, uint32_t ch); void T_GetSize(TerminalBackend *tb, uint16_t *w, uint16_t *h); void T_Printf(TerminalBackend *tb, const char *fmt, ...); void T_ScrollLine(TerminalBackend *tb); -void T_SetColor(TerminalBackend *tb, unsigned char fgColor, unsigned char bgColor, bool blink); \ No newline at end of file +void T_SetColor(TerminalBackend *tb, unsigned char fgColor, unsigned char bgColor, bool blink); + +extern TerminalBackend* Console; diff --git a/src/os/terminal_backend_b8000.c b/src/os/terminal_backend_b8000.c index 698cb67..90581cc 100644 --- a/src/os/terminal_backend_b8000.c +++ b/src/os/terminal_backend_b8000.c @@ -94,6 +94,10 @@ static void B8000_PutCharacter(TerminalBackend *tb, uint32_t ch) { B8000_SetCursorPosition(tb, x, y); } +static void B8000_Initialize(TerminalBackend *tb) { + UNUSED(tb); +} + static void B8000_GetCursorPosition( TerminalBackend *tb, uint16_t *x, uint16_t *y) { UNUSED(tb); @@ -121,6 +125,7 @@ static void B8000_ScrollLine(TerminalBackend *tb) { } static const TerminalBackend B8000_Functions = { + .func_initialize = B8000_Initialize, .func_set_cursor_position = B8000_SetCursorPosition, .func_get_cursor_position = B8000_GetCursorPosition, .func_clear_screen = B8000_ClearScreen, diff --git a/src/os/terminal_backend_b8000.h b/src/os/terminal_backend_b8000.h index 6995895..defca3e 100644 --- a/src/os/terminal_backend_b8000.h +++ b/src/os/terminal_backend_b8000.h @@ -1,21 +1,4 @@ #pragma once #include "terminal.h" -#define COLOR_BLACK 0 -#define COLOR_BLUE 1 -#define COLOR_GREEN 2 -#define COLOR_CYAN 3 -#define COLOR_RED 4 -#define COLOR_MAGENTA 5 -#define COLOR_BROWN 6 -#define COLOR_GRAY 7 -#define COLOR_DARK_GRAY 8 -#define COLOR_LIGHT_BLUE 9 -#define COLOR_LIGHT_GREEN 10 -#define COLOR_LIGHT_CYAN 11 -#define COLOR_LIGHT_RED 12 -#define COLOR_LIGHT_MAGENTA 13 -#define COLOR_YELLOW 14 -#define COLOR_WHITE 15 - TerminalBackend *TerminalBackendB8000(void); diff --git a/src/os/terminal_backend_uart16550.c b/src/os/terminal_backend_uart16550.c new file mode 100644 index 0000000..552b141 --- /dev/null +++ b/src/os/terminal_backend_uart16550.c @@ -0,0 +1,231 @@ +#include "common.h" +#include "crt.h" +#include "hal.h" +#include "terminal.h" + +#define UART_CLOCK_RATE ((uint32_t)115200) + +enum { + kUARTAddressCOM1 = 0x3F8, + kUARTAddressCOM2 = 0x2F8, + kUARTAddressCOM3 = 0x3E8, + kUARTAddressCOM4 = 0x2E8, +}; + +enum { + kUARTRegisterDTX = 0, + kUARTRegisterDRX = 0, + kUARTRegisterDLL = 0, + kUARTRegisterDLH = 1, + kUARTRegisterIER = 1, + kUARTRegisterIIR = 2, + kUARTRegisterFCR = 2, + kUARTRegisterLCR = 3, + kUARTRegisterMCR = 4, + kUARTRegisterLSR = 5, + kUARTRegisterMSR = 6, + kUARTRegisterSCR = 7, +}; + +enum { + kUARTLCRBits5D1S = 0x00, + kUARTLCRBits6D1S = 0x01, + kUARTLCRBits7D1S = 0x02, + kUARTLCRBits8D1S = 0x03, + kUARTLCRBits5D15S = 0x04, + kUARTLCRBits6D2S = 0x05, + kUARTLCRBits7D2S = 0x06, + kUARTLCRBits8D2S = 0x07, + kUARTLCRParityNone = 0x00, + kUARTLCRParityOdd = 0x08, + kUARTLCRParityEven = 0x18, + kUARTLCRParityMark = 0x28, + kUARTLCRParitySpace = 0x38, + kUARTLCRDivisorLatch = 0x80, +}; + +enum { + kUARTLSRReceiverDataReady = 0x01, + kUARTLSREmptyTransmitter = 0x20, + kUARTLSRErrorMask = 0x8E, +}; + +static struct UART16550_Context { + uint16_t port; + uint32_t baud_rate; + uint16_t cursor_position_x; + uint16_t cursor_position_y; +} UART16550_Context; + +static uint8_t UART16550_ReadRegister(uint16_t port, uint16_t offset) { + return HAL_PortInByte(port + offset); +} + +static void UART16550_WriteRegister(uint16_t port, uint16_t offset, uint8_t value) { + HAL_PortOutByte(port + offset, value); +} + +#if defined(BONUS_FUNCTION) +static bool UART16550_RecvByte(uint8_t* value) { + UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterMSR); + uint8_t status = 0; + + do { + status = UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterLSR); + if (status & kUARTLSRErrorMask) { + return false; + } + } while ((status & kUARTLSRReceiverDataReady) == 0); + + uint8_t received = UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterDRX); + status = UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterLSR); + + if (status & kUARTLSRErrorMask) { + return false; + } + + (*value) = received; + return true; +} +#endif + +static bool UART16550_SendByte(uint8_t value) { + uint8_t status = 0; + do { + status = UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterLSR); + if (status & kUARTLSRErrorMask) { + return false; + } + } while ((status & kUARTLSREmptyTransmitter) == 0); + + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterDTX, value); + + status = UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterLSR); + return (status & kUARTLSRErrorMask) == 0; +} + +// Public Interface + +static void UART16550_Initialize(TerminalBackend *tb) { + UNUSED(tb); + + // + // Initialize UART controller context structure. + // + UART16550_Context.port = kUARTAddressCOM1; + UART16550_Context.baud_rate = 112500; + + // + // Disable interrupts. + // + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterLCR, 0); + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterIER, 0); + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterFCR, 0); + + // + // Set baud rate. + // + uint32_t const divisor = (uint32_t)(UART_CLOCK_RATE / UART16550_Context.baud_rate); + uint8_t const divisor_lo = (uint8_t)(divisor); + uint8_t const divisor_hi = (uint8_t)(divisor >> 8); + + // Enable divisor latch. + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterLCR, + UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterLCR) | kUARTLCRDivisorLatch); + + // Set divisor value. + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterDLL, divisor_lo); + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterDLH, divisor_hi); + + // Disable divisor latch. + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterLCR, + UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterLCR) & ~kUARTLCRDivisorLatch); + + // Configure data transfer. + UART16550_WriteRegister(UART16550_Context.port, kUARTRegisterLCR, kUARTLCRBits8D1S | kUARTLCRParityNone); + + // Clear data buffer. + (void)UART16550_ReadRegister(UART16550_Context.port, kUARTRegisterDRX); + + UART16550_SendByte(033); + UART16550_SendByte('c'); + UART16550_SendByte(033); + UART16550_SendByte('['); + UART16550_SendByte('2'); + UART16550_SendByte('J'); +} + +static void UART16550_SetColor(TerminalBackend *tb, unsigned char fgColor, unsigned char bgColor, bool blink) { + UNUSED(tb); + UART16550_SendByte(033); + UART16550_SendByte('['); + UART16550_SendByte('0' + (blink ? 5 : 0)); + UART16550_SendByte(';'); + UART16550_SendByte('3'); + UART16550_SendByte('0' + (fgColor & 0x7)); + UART16550_SendByte(';'); + UART16550_SendByte('4'); + UART16550_SendByte('0' + (bgColor & 0x7)); + UART16550_SendByte('m'); +} + +static void UART16550_SetCursorPosition(TerminalBackend *tb, uint16_t x, uint16_t y) { + UNUSED(tb); + char tmp[16]; + snprintf(tmp, sizeof(tmp), "[%u;%uf", y, x); + + UART16550_SendByte(033); + for (char* ptr = &tmp[0]; *ptr != '\0'; ++ptr) { + UART16550_SendByte(*ptr); + } + + UART16550_Context.cursor_position_x = x; + UART16550_Context.cursor_position_y = y; +} + +static void UART16550_ClearScreen(TerminalBackend *tb) { + UNUSED(tb); + UART16550_SendByte(033); + UART16550_SendByte('['); + UART16550_SendByte('2'); + UART16550_SendByte('J'); +} +static void UART16550_PutCharacter(TerminalBackend *tb, uint32_t ch) { + UNUSED(tb); + UART16550_SendByte((uint8_t)ch); +} +static void UART16550_GetCursorPosition(TerminalBackend *tb, uint16_t *x, uint16_t *y) { + UNUSED(tb); + (*x) = UART16550_Context.cursor_position_x; + (*y) = UART16550_Context.cursor_position_y; +} + +static void UART16550_GetSize(TerminalBackend *tb, uint16_t *w, uint16_t *h) { + UNUSED(tb); + (*w) = 80; + (*h) = 25; +} + +static void UART16550_ScrollLine(TerminalBackend *tb) { + UNUSED(tb); + UART16550_SendByte('\n'); + UART16550_SendByte(033); + UART16550_SendByte('E'); + UART16550_Context.cursor_position_y -= 1; +} + +static const TerminalBackend UART16550_Functions = { + .func_initialize = UART16550_Initialize, + .func_set_cursor_position = UART16550_SetCursorPosition, + .func_get_cursor_position = UART16550_GetCursorPosition, + .func_clear_screen = UART16550_ClearScreen, + .func_put_character = UART16550_PutCharacter, + .func_get_size = UART16550_GetSize, + .func_scroll_line = UART16550_ScrollLine, + .func_set_color = UART16550_SetColor}; + +TerminalBackend *TerminalBackendUART16550(void) { + // It's just a function table, so we declared it as const. + // But we pass it as non-const to the functions. + return (TerminalBackend *)&UART16550_Functions; +} diff --git a/src/os/terminal_backend_uart16550.h b/src/os/terminal_backend_uart16550.h new file mode 100644 index 0000000..b134586 --- /dev/null +++ b/src/os/terminal_backend_uart16550.h @@ -0,0 +1,4 @@ +#pragma once +#include "terminal.h" + +TerminalBackend* TerminalBackendUART16550(void);