From 49df2a5fb45405bf3a55644e237c1df01641c121 Mon Sep 17 00:00:00 2001 From: lephe Date: Sun, 22 Jan 2017 18:35:02 +0100 Subject: [PATCH] Added a --extended-libc option and some headers to allow porting external programs. --- .gitignore | 2 + Makefile | 15 +++--- TODO | 2 + configure | 50 ++++++++++++----- include/ctype.h | 89 ++++++++++++++++++++++++------- include/extended/endian.h | 46 ++++++++++++++++ include/{ => extended}/inttypes.h | 0 include/string.h | 45 +++++++++++++--- src/string/ctype.c | 64 ++++++++++++++++++++++ src/string/memchr.c | 20 +++++++ src/string/memcmp.c | 84 +++++++++++++++++++++++++++++ src/string/memcpy.c | 2 +- src/string/memset.c | 20 +++---- src/string/strcmp.c | 18 +++++++ src/string/strnlen.c | 13 +++++ 15 files changed, 412 insertions(+), 58 deletions(-) create mode 100644 include/extended/endian.h rename include/{ => extended}/inttypes.h (100%) create mode 100644 src/string/ctype.c create mode 100644 src/string/memchr.c create mode 100644 src/string/memcmp.c create mode 100644 src/string/strcmp.c create mode 100644 src/string/strnlen.c diff --git a/.gitignore b/.gitignore index 41fea35..06e0ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ gintdemo.g1a # Configuration files gcc.cfg +Makefile.cfg + diff --git a/Makefile b/Makefile index 0960d86..ab90ea1 100755 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ # #--- - +include Makefile.cfg #--- # Project variables. @@ -48,7 +48,10 @@ obj-lib-spec = build/display_font_system.bmp.o obj-std-spec = # Configuration files -config = gcc.cfg +config = gcc.cfg +ifeq ($(wildcard $(config)),) +$(error "Configuration files are missing. Did you ./configure ?") +endif # Target folder folder := $(shell fxsdk --folder) @@ -127,16 +130,13 @@ endef all: $(config) $(target-std) $(target-lib) $(target-g1a) @ printf '\e[32;1mmsg \u00bb\e[0m All done!\n' -$(config): - $(error "Configuration files are missing. Did you ./configure?") - build: $(if $(VERBOSE),,@ printf '\e[35;1mdir \u00bb\e[0m mkdir $@\n') $(if $(VERBOSE),,@) mkdir -p $@ $(obj-std) $(obj-lib) $(demo-obj): | build -$(target-std): $(config) $(obj-std) +$(target-std): $(obj-std) $(if $(VERBOSE),,@ printf '\e[35;1mlib \u00bb\e[0m ar $@\n') $(if $(VERBOSE),,@) $(ar) rcs $@ $(obj-std) @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built libc (' @@ -217,6 +217,9 @@ install: $(target-std) $(target-lib) install -m 644 -T demo/gintdemo.ld $(folder)/linker.ld mkdir -p $(folder)/gint install -m 644 include/*.h $(folder)/gint +ifdef config_ext + install -m 644 include/extended/*.h $(folder)/gint +endif @ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n' install_demo: diff --git a/TODO b/TODO index 5e0a027..a4ed30e 100644 --- a/TODO +++ b/TODO @@ -12,6 +12,8 @@ Simple improvements: - core: Add VBR handlers debugging information (if possible) - events: Introduce KeyRepeat events - library: Implement C99's inttypes.h for Cake's UpdateExe +- string: Use cmp/str to implement memchr() (assembler examples) +- string: Do some tests for memcmp() Larger improvements: - errno: Introduce errno and use it more or less everywhere - bopti: Monochrome bitmaps blending modes diff --git a/configure b/configure index 4f70807..8fb9110 100755 --- a/configure +++ b/configure @@ -5,8 +5,16 @@ conf[ATEXIT_MAX]=16 conf[RTC_CB_ARRAY_SIZE]=5 conf[EVENTS_QUEUE_SIZE]=64 conf[GINT_NO_SYSCALLS]= +conf[GINT_EXTENDED_LIBC]= fail=false -output="gcc.cfg" +output_gcc="gcc.cfg" +output_make="Makefile.cfg" + +error="\e[31;1merror:\e[0m" +Cr="$(tput setaf 1)$(tput bold)" +Cg="$(tput setaf 2)$(tput bold)" +Cp="$(tput setaf 5)$(tput setaf 62)$(tput bold)" +C0="$(tput setaf 0)$(tput sgr 0)" help() { @@ -14,16 +22,19 @@ help() Configuration script for the gint library. Options that affect the behavior of the library: - --no-syscalls [default: false] + $Cr--no-syscalls$C0 [default:$Cp false$C0] Never use syscalls. Expect some trouble with the malloc() function... - Do not trigger this option unless you know what you are doing. + Do not enable this option unless you know what you are doing. + $Cr--extended-libc$C0 [default:$Cp false$C0] + Enable specific C99 headers/features that are normally not required by + calculator programs. May allow porting programs from other platforms. Options that customize size limits: - --atexit-max= [default: 16] + $Cr--atexit-max=$C0$Cg$C0 [default:$Cp 16$C0] Number of exit handlers that can be registered by atexit(). - --rtc-callbacks= [default: 5] + $Cr--rtc-callbacks=$C0$Cg$C0 [default:$Cp 5$C0] Number of RTC callbacks that can be registered. - --events-queue-size= [default: 64] + $Cr--events-queue-size=$C0$Cg$C0 [default:$Cp 64$C0] Number of events simultaneously stored in the event queue. EOF @@ -33,34 +44,36 @@ EOF for arg; do case "$arg" in -h | --help) help;; --no-syscalls) conf[GINT_NO_SYSCALLS]=true;; + --extended-libc) conf[GINT_EXTENDED_LIBC]=true;; --atexit-max=*) size=${arg#*=} if [[ $size == +([0-9]) ]]; then conf[ATEXIT_MAX]=$size - else echo "error: --atexit-max expects an integer value" + else echo -e "$error --atexit-max expects an integer value" fail=true; fi;; --rtc-callbacks=*) size=${arg#*=} if [[ $size == +([0-9]) ]]; then conf[RTC_CB_ARRAY_SIZE]=$size - else echo "error: --rtc-callbacks expects an integer value" + else echo -e "$error --rtc-callbacks expects an integer value" fail=true; fi;; --events-queue-size=*) size=${arg#*=} if [[ $size == +([0-9]) ]]; then conf[EVENTS_QUEUE_SIZE]=$size - else echo "error: --events-queue-size expects an integer value" + else echo -e "$error --events-queue-size expects an integer"\ + "value" fail=true; fi;; --atexit-max | --rtc-callbacks | --events-queue-size) - echo "error: syntax for $arg is $arg=";; + echo -e "$error syntax for $arg is $arg=";; *) - echo "error: unrecognized argument '$arg'"; fail=true;; + echo -e "$error unrecognized argument '$arg'"; fail=true;; esac; done -output_config() +output_config_gcc() { echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}" echo "-D RTC_CB_ARRAY_SIZE=${conf[RTC_CB_ARRAY_SIZE]}" @@ -68,11 +81,20 @@ output_config() if [ "${conf[GINT_NO_SYSCALLS]}" != "" ]; then echo "-D GINT_NO_SYSCALLS" fi + if [ "${conf[GINT_EXTENDED_LIBC]}" != "" ]; then + echo "-D GINT_EXTENDED_LIBC" + fi +} + +output_config_make() +{ + [ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "config_ext=true" } if $fail; then echo "Configuration has not been modified." else - output_config > $output - echo "Configuration details have been output to file $output." + output_config_gcc > $output_gcc + output_config_make > $output_make + echo "Configuration saved in $output_gcc and $output_make." fi diff --git a/include/ctype.h b/include/ctype.h index 8c7ee2d..a325f23 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -9,27 +9,76 @@ #ifndef _CTYPE_H #define _CTYPE_H 1 -// Character definition macros. -#define isalnum(c) (isdigit(c) || isalpha(c)) -#define isalpha(c) (islower(c) || isupper(c)) +#include -#define iscntrl(c) ((c) <= 0x1f || (c) == 0x7f) -#define isdigit(c) ((c) >= '0' && (c) <= '9') -#define isgraph(c) ((c) > ' ' && (c) < 0x7f) -#define islower(c) ((c) >= 'a' && (c) <= 'z') -#define isprint(c) ((c) >= ' ' && (c) < 0x7f) -#define ispunct(c) (((c) >= '!' && (c) <= '/') || \ - ((c) >= ':' && (c) <= '@') || \ - ((c) >= '[' && (c) <= '`') || \ - ((c) >= '{' && (c) <= '~')) -#define isspace(c) (((c) >= '\t' && (c) <= '\r') || (c) == ' ') -#define isupper(c) ((c) >= 'A' && (c) <= 'Z') -#define isxdigit(c) (((c) >= '0' && (c) <= '9') || \ - ((c) >= 'A' && (c) <= 'F') || \ - ((c) >= 'a' && (c) <= 'f')) +//--- +// Character classes. +//--- -// Character manipulation macros. -#define tolower(c) (isupper(c) ? (c) | 32 : (c)) -#define toupper(c) (islower(c) ? (c) & ~32 : (c)) +extern uint8_t ctype_classes[0x80]; + +__attribute__((always_inline)) static inline int isalnum(int c) { + return ctype_classes[c] & 0xf0; +} + +__attribute__((always_inline)) static inline int isalpha(int c) { + return ctype_classes[c] & 0x30; +} + +__attribute__((always_inline)) static inline int iscntrl(int c) { + return ctype_classes[c] & 0x01; +} + +__attribute__((always_inline)) static inline int isdigit(int c) { + return ctype_classes[c] & 0x40; +} + +__attribute__((always_inline)) static inline int isgraph(int c) { + return ctype_classes[c] & 0xf4; +} + +__attribute__((always_inline)) static inline int islower(int c) { + return ctype_classes[c] & 0x10; +} + +__attribute__((always_inline)) static inline int isprint(int c) { + return ctype_classes[c] & 0x08; +} + +__attribute__((always_inline)) static inline int ispunct(int c) { + return ctype_classes[c] & 0x04; +} + +__attribute__((always_inline)) static inline int isspace(int c) { + return ctype_classes[c] & 0x02; +} + +__attribute__((always_inline)) static inline int isupper(int c) { + return ctype_classes[c] & 0x20; +} + +__attribute__((always_inline)) static inline int isxdigit(int c) { + return ctype_classes[c] & 0x80; +} + +__attribute__((always_inline)) static inline int isascii(int c) { + return (c >= 0 && c <= 0x7f); +} + +__attribute__((always_inline)) static inline int isblank(int c) { + return (c == '\t' || c == ' '); +} + +//--- +// Character manipulation. +//--- + +__attribute__((always_inline)) static inline int tolower(int c) { + return c | isupper(c); +} + +__attribute__((always_inline)) static inline int toupper(int c) { + return c & ~(islower(c) << 1); +} #endif // _CTYPE_H diff --git a/include/extended/endian.h b/include/extended/endian.h new file mode 100644 index 0000000..8474a81 --- /dev/null +++ b/include/extended/endian.h @@ -0,0 +1,46 @@ +#ifndef _ENDIAN_H +#define _ENDIAN_H + +#include + +//--- +// Assembler-optimized byte-ordering functions. +//--- + +__attribute__((always_inline)) static inline uint16_t swap16(uint16_t word) +{ + uint16_t result; + __asm__("swap.b %1, %0": "=r"(result): "r"(word)); + return result; +} + +__attribute__((always_inline)) static inline uint32_t swap32(uint32_t longw) +{ + uint32_t result; + __asm__( + "swap.b %1, r0 \n\t" + "swap.w r0, r0 \n\t" + "swap.b r0, %0 \n\t" + : "=r"(result) + : "r"(longw) + ); + return result; +} + + + +//--- +// Conversion of values of different endianness. +//--- + +#define htobe16(host16) (host16) +#define htole16(host16) (swap16(host16)) +#define be16toh(be16) (be16) +#define le16toh(le16) (swap16(le16)) + +#define htobe32(host32) (host32) +#define htole32(host32) (swap32(host32)) +#define be32toh(be32) (be32) +#define le32toh(le32) (swap32(le32)) + +#endif // _ENDIAN_H diff --git a/include/inttypes.h b/include/extended/inttypes.h similarity index 100% rename from include/inttypes.h rename to include/extended/inttypes.h diff --git a/include/string.h b/include/string.h index dc0ac1c..9fdbf2a 100644 --- a/include/string.h +++ b/include/string.h @@ -17,18 +17,34 @@ //--- /* - memcpy() O(byte_number) + memcpy() O(byte_count) Copies a memory area. The two areas must not overlap (if they do, use memmove()). A smart copy is performed when possible. To enhance performance, make sure than destination and source are both 4-aligned. */ -void *memcpy(void *destination, const void *source, size_t byte_number); +void *memcpy(void *destination, const void *source, size_t byte_count); /* - memset() O(byte_number) + memset() O(byte_count) Sets the contents of a memory area. A smart copy is performed. */ -void *memset(void *destination, int byte, size_t byte_number); +void *memset(void *destination, int byte, size_t byte_count); + +/* + memchr() O(byte_count) + Looks for a byte in a memory area. Returns the address of the first + occurrence if found, NULL otherwise. +*/ +void *memchr(const void *area, int byte, size_t byte_count); + +/* + memcmp() O(byte_count) + Compares two memory areas. Returns 0 if all bytes are equal in both + areas, a negative number if the first unequal byte is lower in the + first area, and a positive number otherwise. + A smart comparison is performed when possible. +*/ +int memcmp(const void *area1, const void *area2, size_t byte_count); @@ -42,12 +58,25 @@ void *memset(void *destination, int byte, size_t byte_number); */ size_t strlen(const char *str); +/* + strnlen() O(len(str)) + Returns the minimum of the length of the string and n. This function + never access more than n bytes at the beginning of the string. +*/ +size_t strnlen(const char *str, size_t n); + /* strcpy() O(len(source)) Copies a string to another. */ char *strcpy(char *destination, const char *source); +/* + strncpy() O(min(len(source), size)) + Copies part of a string to another. +*/ +char *strncpy(char *destination, const char *source, size_t size); + /* strchr() O(len(str)) Searches a character in a string. @@ -55,9 +84,11 @@ char *strcpy(char *destination, const char *source); const char *strchr(const char *str, int value); /* - strncpy() O(min(len(source), size)) - Copies part of a string to another. + strcmp() O(max(len(str1), len(str2))) + Compares two strings. Returns 0 if they are identical, a negative + number if the first unequal byte is lower in str1 than in str2, and a + positive number otherwise. */ -char *strncpy(char *destination, const char *source, size_t size); +int strcmp(const char *str1, const char *str2); #endif // _STRING_H diff --git a/src/string/ctype.c b/src/string/ctype.c new file mode 100644 index 0000000..6b55781 --- /dev/null +++ b/src/string/ctype.c @@ -0,0 +1,64 @@ +#include + +enum { + cntrl = 0x01, + space = 0x02, + punct = 0x04, + print = 0x08, + upper = 0x20, + lower = 0x10, + digit = 0x40, + xdigt = 0x80, +}; + +uint8_t ctype_classes[0x80] = { + // Control characters. + cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, + cntrl | space, // Tabulation + cntrl | space, cntrl | space, cntrl | space, cntrl | space, + cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, + cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, cntrl, + + // Space and some punctuation. + space | print, + punct | print, punct | print, punct | print, punct | print, + punct | print, punct | print, punct | print, punct | print, + punct | print, punct | print, punct | print, punct | print, + punct | print, punct | print, punct | print, + + // Decimal digits. + digit | xdigt | print, digit | xdigt | print, digit | xdigt | print, + digit | xdigt | print, digit | xdigt | print, digit | xdigt | print, + digit | xdigt | print, digit | xdigt | print, digit | xdigt | print, + digit | xdigt | print, + + // Some punctuation. + punct | print, punct | print, punct | print, punct | print, + punct | print, punct | print, punct | print, + + // Uppercase alphabet. + upper | xdigt | print, upper | xdigt | print, upper | xdigt | print, + upper | xdigt | print, upper | xdigt | print, upper | xdigt | print, + upper | print, upper | print, upper | print, upper | print, + upper | print, upper | print, upper | print, upper | print, + upper | print, upper | print, upper | print, upper | print, + upper | print, upper | print, upper | print, upper | print, + upper | print, upper | print, upper | print, upper | print, + + // Other punctuation symbols. + punct | print, punct | print, punct | print, punct | print, + punct | print, punct | print, + + // Lowercase alphabet. + lower | xdigt | print, lower | xdigt | print, lower | xdigt | print, + lower | xdigt | print, lower | xdigt | print, lower | xdigt | print, + lower | print, lower | print, lower | print, lower | print, + lower | print, lower | print, lower | print, lower | print, + lower | print, lower | print, lower | print, lower | print, + lower | print, lower | print, lower | print, lower | print, + lower | print, lower | print, lower | print, lower | print, + + // Last punctuation characters and DEL. + punct | print, punct | print, punct | print, punct | print, + cntrl +}; diff --git a/src/string/memchr.c b/src/string/memchr.c new file mode 100644 index 0000000..e7ad19a --- /dev/null +++ b/src/string/memchr.c @@ -0,0 +1,20 @@ +#include +#include + +/* + memchr() O(byte_count) + Looks for a byte in a memory area. Returns the address of the first + occurrence if found, NULL otherwise. +*/ +void *memchr(const void *area, int byte, size_t byte_count) +{ + const uint8_t *mem = area; + + while(byte_count--) + { + if(*mem == byte) return (void *)mem; + mem++; + } + + return NULL; +} diff --git a/src/string/memcmp.c b/src/string/memcmp.c new file mode 100644 index 0000000..a862d3b --- /dev/null +++ b/src/string/memcmp.c @@ -0,0 +1,84 @@ +#include +#include + +int raw_cmp(const uint8_t *mem1, const uint8_t *mem2, size_t byte_count); + +/* + memcmp() O(byte_count) + Compares two memory areas. Returns 0 if all bytes are equal in both + areas, a negative number if the first unequal byte is lower in the + first area, and a positive number otherwise. + A smart comparison is performed when possible. +*/ +int memcmp(const void *area1, const void *area2, size_t byte_count) +{ + const uint8_t *mem1 = (const uint8_t *)area1; + const uint8_t *mem2 = (const uint8_t *)area2; + + // Trying to do long-based comparisons. + if(((intptr_t)area1 & 3) == ((intptr_t)area2 & 3)) + { + // Getting to a 4-byte offset. + while((intptr_t)mem1 & 3) + { + if(*mem1 != *mem2) return *mem1 - *mem2; + mem1++, mem2++; + byte_count--; + } + + // Testing groups of four bytes. + while(byte_count >= 4) + { + uint32_t long1 = *((uint32_t *)mem1); + uint32_t long2 = *((uint32_t *)mem2); + if(long1 != long2) return raw_cmp(mem1, mem2, 4); + mem1 += 4, mem2 += 4; + byte_count -= 4; + } + + // Testing the last bytes. + return raw_cmp(mem1, mem2, byte_count); + } + + // Well, maybe we can do a word-based operation? + else if(((intptr_t)area1 & 1) == ((intptr_t)area2 & 3)) + { + // Getting to the word offset. + if((intptr_t)mem1 & 1) + { + if(*mem1 != *mem2) return *mem1 - *mem2; + mem1++, mem2++; + byte_count--; + } + + // Testing groups of two bytes. + while(byte_count >= 2) + { + uint16_t word1 = *((uint16_t *)mem1); + uint16_t word2 = *((uint16_t *)mem2); + if(word1 != word2) return raw_cmp(mem1, mem2, 2); + mem1 += 2, mem2 += 2; + byte_count -= 2; + } + + // Testing the last byte. + if(!byte_count) return 0; + return *mem1 - *mem2; + } + + // That's too bad, we'll have to compare everything manually. + else return raw_cmp(mem1, mem2, byte_count); +} + +int raw_cmp(const uint8_t *mem1, const uint8_t *mem2, size_t byte_count) +{ + while(byte_count) + { + if(*mem1 != *mem2) return *mem1 - *mem2; + mem1++; + mem2++; + byte_count--; + } + + return 0; +} diff --git a/src/string/memcpy.c b/src/string/memcpy.c index d78d1f6..28a5da1 100644 --- a/src/string/memcpy.c +++ b/src/string/memcpy.c @@ -2,7 +2,7 @@ #include /* - memcpy() + memcpy() O(n) Copies a memory area. A smart copy is performed if possible. */ void *memcpy(void *d, const void *s, size_t n) diff --git a/src/string/memset.c b/src/string/memset.c index 7236516..4843e3a 100644 --- a/src/string/memset.c +++ b/src/string/memset.c @@ -2,10 +2,10 @@ #include /* - memset() + memset() O(byte_count) Sets the contents of a memory area. A smart copy is performed. */ -void *memset(void *d, int byte, size_t byte_number) +void *memset(void *d, int byte, size_t byte_count) { uint8_t *dest = (uint8_t *)d; unsigned short word = (byte << 8) | byte; @@ -13,12 +13,12 @@ void *memset(void *d, int byte, size_t byte_number) // When the area is small, simply copying using byte operations. The // minimum length used for long operations must be at least 3. - if(byte_number < 8) + if(byte_count < 8) { - while(byte_number) + while(byte_count) { *dest++ = byte; - byte_number--; + byte_count--; } return d; @@ -28,20 +28,20 @@ void *memset(void *d, int byte, size_t byte_number) while((intptr_t)dest & 3) { *dest++ = byte; - byte_number--; + byte_count--; } // Copying using long operations. - while(byte_number >= 4) + while(byte_count >= 4) { *((uint32_t *)dest) = longword; dest += 4; - byte_number -= 4; + byte_count -= 4; } // Ending the copy. - while(byte_number) + while(byte_count) { *dest++ = byte; - byte_number--; + byte_count--; } return d; diff --git a/src/string/strcmp.c b/src/string/strcmp.c new file mode 100644 index 0000000..36971a4 --- /dev/null +++ b/src/string/strcmp.c @@ -0,0 +1,18 @@ +#include + +/* + strcmp() O(max(len(str1), len(str2))) + Compares two strings. Returns 0 if they are identical, a negative + number if the first unequal byte is lower in str1 than in str2, and a + positive number otherwise. +*/ +int strcmp(const char *str1, const char *str2) +{ + while(*str1 && *str2 && *str1 == *str2) + { + str1++; + str2++; + } + + return *str1 - *str2; +} diff --git a/src/string/strnlen.c b/src/string/strnlen.c new file mode 100644 index 0000000..57d1344 --- /dev/null +++ b/src/string/strnlen.c @@ -0,0 +1,13 @@ +#include + +/* + strnlen() O(len(str)) + Returns the minimum of the length of the string and n. This function + never access more than n bytes at the beginning of the string. +*/ +size_t strnlen(const char *str, size_t n) +{ + size_t len = 0; + while(len < n && str[len]) len++; + return len; +}