From d9137ce13818e800749898863d2e0924f1695bea Mon Sep 17 00:00:00 2001 From: lephe Date: Fri, 23 Dec 2016 22:32:04 +0100 Subject: [PATCH 01/24] Added a continuous integration file. --- .gitlab-ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..f2d6a20 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,7 @@ +# I know there actually are a lot of dependencies but building them is already +# long enough, and I'll be managing the runner for now. So let's KISS. + +build_all: + script: + - ./configure + - make From 7a888fc90a76a297516d9683d03c0e2ab5b02a08 Mon Sep 17 00:00:00 2001 From: lephe Date: Sun, 22 Jan 2017 18:35:02 +0100 Subject: [PATCH 02/24] 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; +} From 77ffc24e94bac2a9dc88056026558bff25e5d901 Mon Sep 17 00:00:00 2001 From: lephe Date: Sat, 25 Feb 2017 19:02:07 +0100 Subject: [PATCH 03/24] Huh... debug app for crashes & whole new interrupt system. Minor edits. --- .gitignore | 3 +- Makefile | 106 +++++--- README.md | 13 +- TODO | 26 +- configure | 76 ++++-- debug/addin.ld | 31 +++ debug/crt0.s | 172 ++++++++++++ debug/gintdbg.c | 178 ++++++++++++ debug/icon.bmp | Bin 0 -> 1870 bytes debug/include/dispbios.h | 97 +++++++ debug/include/endian.h | 31 +++ debug/include/filebios.h | 116 ++++++++ debug/include/fxlib.h | 100 +++++++ debug/include/keybios.h | 158 +++++++++++ debug/include/timer.h | 31 +++ debug/libfx.a | Bin 0 -> 410910 bytes debug/vsprintf.c | 160 +++++++++++ demo/gintdemo.c | 52 ++-- demo/gintdemo.ld | 8 +- demo/test_bopti.c | 4 + demo/test_tales.c | 2 +- demo/test_timer.c | 2 +- include/clock.h | 88 ++---- include/ctype.h | 2 +- include/display.h | 1 + include/gint.h | 376 ++++++++++++++------------ include/gray.h | 19 -- include/internals/clock.h | 21 ++ include/internals/exceptions.h | 66 +++++ include/internals/gint.h | 176 +++++++++++- include/internals/interrupt_maps.h | 100 +++++++ include/internals/interrupts.h | 18 ++ include/internals/keyboard.h | 7 +- include/keyboard.h | 8 +- include/mpu.h | 51 ++-- include/rtc.h | 6 +- include/tales.h | 1 + include/timer.h | 2 +- src/bopti/bopti_internals.c | 20 +- src/bopti/dimage.c | 49 +++- src/bopti/dimage_part.c | 65 ----- src/bopti/gimage.c | 48 +++- src/bopti/gimage_part.c | 85 ------ src/clock/clock.c | 61 ++--- src/core/crt0.c | 137 ++++++++-- src/core/exceptions.c | 172 ++++++++++++ src/core/gint.c | 368 ++++++++++--------------- src/{mpu => core}/gint_sh7305.c | 94 +------ src/{mpu => core}/gint_sh7705.c | 99 +------ src/core/gint_str.c | 37 --- src/core/gint_vbr.s | 14 +- src/core/init_quit.c | 73 +++++ src/core/interrupt_maps_7305.c | 53 ++++ src/core/interrupt_maps_7705.c | 65 +++++ src/core/interrupts.c | 21 ++ src/{mpu => core}/mpu.c | 18 +- src/core/syscalls.s | 58 ++++ src/core/vbr_space.c | 98 +++++++ src/display/dupdate.c | 2 - src/gray/gray_engine.c | 64 ++--- src/rtc/rtc_callback.c | 5 +- src/tales/{tales_print.c => dprint.c} | 15 - src/tales/{tales_text.c => dtext.c} | 9 - src/tales/gprint.c | 18 ++ src/tales/gtext.c | 11 + src/tales/tales_internals.c | 3 +- 66 files changed, 2883 insertions(+), 1187 deletions(-) create mode 100644 debug/addin.ld create mode 100644 debug/crt0.s create mode 100644 debug/gintdbg.c create mode 100644 debug/icon.bmp create mode 100644 debug/include/dispbios.h create mode 100644 debug/include/endian.h create mode 100644 debug/include/filebios.h create mode 100644 debug/include/fxlib.h create mode 100644 debug/include/keybios.h create mode 100644 debug/include/timer.h create mode 100644 debug/libfx.a create mode 100644 debug/vsprintf.c create mode 100644 include/internals/clock.h create mode 100644 include/internals/exceptions.h create mode 100644 include/internals/interrupt_maps.h create mode 100644 include/internals/interrupts.h delete mode 100644 src/bopti/dimage_part.c delete mode 100644 src/bopti/gimage_part.c create mode 100644 src/core/exceptions.c rename src/{mpu => core}/gint_sh7305.c (66%) rename src/{mpu => core}/gint_sh7705.c (51%) delete mode 100644 src/core/gint_str.c create mode 100644 src/core/init_quit.c create mode 100644 src/core/interrupt_maps_7305.c create mode 100644 src/core/interrupt_maps_7705.c create mode 100644 src/core/interrupts.c rename src/{mpu => core}/mpu.c (85%) create mode 100644 src/core/vbr_space.c rename src/tales/{tales_print.c => dprint.c} (56%) rename src/tales/{tales_text.c => dtext.c} (60%) create mode 100644 src/tales/gprint.c create mode 100644 src/tales/gtext.c diff --git a/.gitignore b/.gitignore index 06e0ba1..9a73a45 100644 --- a/.gitignore +++ b/.gitignore @@ -9,12 +9,13 @@ build/** *.o # Some notes. -LIBC +notes/** # Output files libc.a libgint.a gintdemo.g1a +gintdbg.g1a # Configuration files gcc.cfg diff --git a/Makefile b/Makefile index ab90ea1..3c2f7a8 100755 --- a/Makefile +++ b/Makefile @@ -1,25 +1,25 @@ #! /usr/bin/make -f -#--- # -# gint project Makefile. +# gint project Makefile # #--- include Makefile.cfg #--- -# Project variables. +# Project variables #--- # Modules -modules-gint = core clock keyboard mmu mpu rtc screen timer \ - bopti display gray tales events -modules-libc = setjmp string stdio stdlib time +modules-gint = bopti clock core display events gray keyboard mmu rtc \ + screen tales timer +modules-libc = setjmp stdio stdlib string time # Targets -target-g1a = gintdemo.g1a target-lib = libgint.a target-std = libc.a +target-g1a = gintdemo.g1a +target-dbg = gintdbg.g1a # Tools cc = sh3eb-elf-gcc @@ -28,27 +28,40 @@ ar = sh3eb-elf-ar ob = sh3eb-elf-objcopy wr = g1a-wrapper -# Flags -cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ - -W -Wall -Wextra -pedantic @gcc.cfg +# Flags for gint +lib-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ + -Wall -Wextra @gcc.cfg # Demo application (could be done better) demo-src = $(notdir $(wildcard demo/*.[cs])) demo-dep = $(wildcard demo/*.h) -demo-ld = demo/gintdemo.ld demo-icon = demo/icon.bmp demo-res = $(notdir $(wildcard demo/resources/*)) demo-obj = $(patsubst %,build/demo_%.o,$(demo-src) $(demo-res)) demo-elf = build/gintdemo.elf demo-bin = build/gintdemo.bin -demo-libs = -L. -lgint -lc -lgcc +demo-ldflags = $(demo-cflags) -T demo/gintdemo.ld -L. -lgint -lc -lgcc +demo-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ + -Wall -Wextra + +# Debugger application (displays past diagnostics without running gint) +debug-src = $(notdir $(wildcard debug/*.[cs])) +debug-dep = $(wildcard debug/*.h) +debug-icon = debug/icon.bmp +debug-obj = $(debug-src:%=build/debug_%.o) +debug-elf = build/gintdbg.elf +debug-bin = build/gintdbg.bin +debug-ldflags = $(debug-cflags) -T debug/addin.ld -L debug -lfx -lgcc +debug-cflags = -m3 -mb -nostdlib -ffreestanding -I debug/include -I \ + include -std=c11 -Os -Wall -Wextra # Specific objects -obj-lib-spec = build/display_font_system.bmp.o +obj-lib-spec = build/display_font_system.bmp.o \ + build/display_font_terminal.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 @@ -63,7 +76,7 @@ endif #--- -# Automatic variables. +# Automatic variables #--- # Modules are subfolders of src/. @@ -86,7 +99,7 @@ $(foreach mod, $(modules), $(eval \ obj-std = $(foreach mod,$(modules-libc),$(mod-$(mod)-obj)) $(obj-std-spec) obj-lib = $(foreach mod,$(modules-gint),$(mod-$(mod)-obj)) $(obj-lib-spec) -# Dependencies +# Dependencies. hdr-dep = $(wildcard include/*.h include/internals/*.h) @@ -95,11 +108,6 @@ hdr-dep = $(wildcard include/*.h include/internals/*.h) # Rule templates. #--- -#ifndef VERBOSE -#$(note "default full log") -#VERBOSE = -#endif - # C source file template: # $1 module name # $2 filename @@ -107,10 +115,10 @@ hdr-dep = $(wildcard include/*.h include/internals/*.h) define rule-c-source build/$1_$2.o: src/$1/$2 $3 $(config) $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $$<\n') - $(if $(VERBOSE),,@) $(cc) -c $$< -o $$@ $(cflags) -I src/$1 + $(if $(VERBOSE),,@) $(cc) -c $$< -o $$@ $(lib-cflags) endef -# asm source file template: +# Asm source file template: # $1 module name # $2 filename define rule-asm-source @@ -127,8 +135,9 @@ endef # Generic rules -all: $(config) $(target-std) $(target-lib) $(target-g1a) - @ printf '\e[32;1mmsg \u00bb\e[0m All done!\n' +all-lib: $(config) $(target-std) $(target-lib) + +all: $(config) $(target-std) $(target-lib) $(target-g1a) $(target-dbg) build: $(if $(VERBOSE),,@ printf '\e[35;1mdir \u00bb\e[0m mkdir $@\n') @@ -152,7 +161,7 @@ $(target-lib): $(config) $(target-std) $(obj-lib) $(target-g1a): $(config) $(target-std) $(target-lib) $(demo-obj) $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m ld -o $(demo-elf)\n') - $(if $(VERBOSE),,@) $(cc) -o $(demo-elf) $(cflags) -T $(demo-ld) $(demo-obj) $(demo-libs) + $(if $(VERBOSE),,@) $(cc) -o $(demo-elf) $(demo-obj) $(demo-ldflags) $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m objcopy -o $(demo-bin)\n') $(if $(VERBOSE),,@) $(ob) -R .comment -R .bss -O binary $(demo-elf) $(demo-bin) $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m g1a-wrapper -o $@\n') @@ -161,6 +170,17 @@ $(target-g1a): $(config) $(target-std) $(target-lib) $(demo-obj) @ printf $$(stat -c %s $@) @ printf ' bytes)\n\n' +$(target-dbg): $(config) $(debug-obj) + $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m ld -o $(debug-elf)\n') + $(if $(VERBOSE),,@) $(cc) -o $(debug-elf) $(debug-obj) $(debug-ldflags) + $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m objcopy -o $(debug-bin)\n') + $(if $(VERBOSE),,@) $(ob) -R .comment -R .bss -O binary $(debug-elf) $(debug-bin) + $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m g1a-wrapper -o $@\n') + $(if $(VERBOSE),,@) $(wr) $(debug-bin) -o $@ -i $(debug-icon) + @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built debug application (' + @ printf $$(stat -c %s $@) + @ printf ' bytes)\n\n' + # Automated rules $(foreach mod,$(modules), \ @@ -172,21 +192,19 @@ $(foreach mod,$(modules), \ # Specific rules -# This one should not be optimized. It makes __attribute__((interrupt_handler)) -# buggy... maybe. Anyway there's some bug in this file that I can't fix now. -build/core_gint.c.o: src/core/gint.c $(mod-core-dep) $(config) - $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $<\n') - $(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(cflags) -I src/core -O0 +# Optimizing this one makes the interrupt handler raise illegal slot exception +# on rte; lds.l @r15+, mach. This is totally weird but I haven't understood +# why for now. -build/display_font_system.bmp.o: src/display/font_system.bmp +build/display_font_%.bmp.o: src/display/font_%.bmp $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') - $(if $(VERBOSE),,@) fxconv $< -o $@ --font -n gint_font_system + $(if $(VERBOSE),,@) fxconv $< -o $@ --font -n $(<:src/display/font_%.bmp=gint_font_%) # Demo application build/demo_%.c.o: demo/%.c $(hdr-dep) $(demo-dep) $(config) $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $<\n') - $(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(cflags) + $(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(demo-cflags) build/demo_font_%.bmp.o: demo/resources/font_%.bmp $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') @@ -196,10 +214,20 @@ build/demo_%.bmp.o: demo/resources/%.bmp $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') $(if $(VERBOSE),,@) fxconv $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) +# Debug application + +build/debug_%.s.o: debug/%.s $(config) + $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m as $<\n') + $(if $(VERBOSE),,@) $(as) -c $< -o $@ + +build/debug_%.c.o: debug/%.c $(hdr-dep) $(debug-dep) $(config) + $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $<\n') + $(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(debug-cflags) + #--- -# Cleaning and others. +# Cleaning and install #--- clean: @@ -222,7 +250,11 @@ ifdef config_ext endif @ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n' -install_demo: +install-demo: p7 send -f $(target-g1a) +install-debug: + p7 send -f $(target-dbg) -.PHONY: all clean mrproper distclean install install_demo help +.PHONY: all-lib all help +.PHONY: clean mrproper distclean +.PHONY: install install-demo install-debug diff --git a/README.md b/README.md index 64a94c6..79e9845 100644 --- a/README.md +++ b/README.md @@ -59,13 +59,14 @@ The classical way to build gint is to enter a terminal and use the usual: $ make # make install -This will build and install the following components in the storage folder of -the fxSDK: +This will build the `all-lib` target and install the following components in +the storage folder of the fxSDK: * `libgint.a`, the gint library * `libc.a`, the partial standard library * The libgint headers for development -The following additional files will be generated in the working directory: +When explicitly running target `all`, the following additional files will be +generated in the working directory: * `gintdemo.g1a`, a test application The usual `clean`, `mrproper`, and `distclean` rules will clean the directory. @@ -79,8 +80,8 @@ allocation...). Source organization ------------------- -gint is made of *modules*. Each module may have any of the following -components: +gint is made of *modules*. Each module has one or more of the following +component files: * A header file in `/include` * An internal header file in `/include/internals` * Single-function source files in `/src/module`: to avoid linking against the @@ -91,4 +92,4 @@ components: often begin with `module_`. * Other files in `/src/module`: the `display` module contains a font. -The demo application is in the `demo` directory. +The demo application is in the `demo` folder. diff --git a/TODO b/TODO index a4ed30e..3f7b8de 100644 --- a/TODO +++ b/TODO @@ -9,30 +9,28 @@ Simple improvements: - demo: Try 284x124 at (-60, -28) (all disadvantages) - display: Rectangle-based drawing functions - time: Compute CLOCKS_PER_SEC -- 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() +- core: Register more interrupts (and understand their parameters) +- rtc: Take care of carry when reading time Larger improvements: - errno: Introduce errno and use it more or less everywhere - bopti: Monochrome bitmaps blending modes - bopti: Handle partial transparency - core: Implement all callbacks and a complete user API -Other whole modules: -- Serial communication through 3-pin -- USB communication -- Sound playback and synthesizing (if possible) -- Overclock (relaunch clocks when overclocking) +* core: Better save registers +* core: Allow return to menu +- serial: Implement a driver +- usb: Implement a driver +- esper: Cleaner playback, synthetizing +- clock: Handle overclocking (relaunch clocks when overclocking) Things to investigate: - Packed bit fields alignment - Registers that may need to be saved within setjmp() - Registers that may need to be saved and restored by gint -- Possible bug when optimizing __attribute__((interrupt_handler)) - -Configuration: -- ATEXIT_MAX (16) -- RTC_CB_ARRAY_SIZE (5) -- EVENTS_QUEUE_SIZE (64) -- GINT_NO_SYSCALLS (undefined) +- Optimizing core/gint.c leads to raising of an illegal slot exception when + running the interrupt handler, although it ends on rte; lds.l @r15+, mach, + which is totally not an illegal slot. +- Check version registers on SH7705 diff --git a/configure b/configure index 8fb9110..5bd34db 100755 --- a/configure +++ b/configure @@ -1,18 +1,33 @@ #! /bin/bash +# +# Basic configuration +# + declare -A conf + +# Behavior +conf[GINT_DIAGNOSTICS]= +conf[GINT_NO_SYSCALLS]= +conf[GINT_EXTENDED_LIBC]= + +# Size limits 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 files output_gcc="gcc.cfg" output_make="Makefile.cfg" +# +# Help screen and output util +# + error="\e[31;1merror:\e[0m" +Cg="$(tput setaf 8)$(tput bold)" Cr="$(tput setaf 1)$(tput bold)" -Cg="$(tput setaf 2)$(tput bold)" +Cy="$(tput setaf 2)$(tput bold)" Cp="$(tput setaf 5)$(tput setaf 62)$(tput bold)" C0="$(tput setaf 0)$(tput sgr 0)" @@ -22,28 +37,37 @@ help() Configuration script for the gint library. Options that affect the behavior of the library: - $Cr--no-syscalls$C0 [default:$Cp false$C0] - Never use syscalls. Expect some trouble with the malloc() function... - 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. + $Cr--diagnostics $Cg[default:$Cp false$Cg]$C0 + Use gint in debug mode, where the library outputs some diagnostics in + memory or briefly on screen to diagnose incompatibilites or crashes. + $Cr--no-syscalls $Cg[default:$Cp false$Cg]$C0 + Never use syscalls. Expect some trouble with the malloc() function... do + not trigger this switch unless you know what you are doing. + $Cr--extended-libc $Cg[default:$Cp false$Cg]$C0 + Enable specific C99 headers/features that are normally not required by + calculator programs. This may allow porting programs from other platforms. Options that customize size limits: - $Cr--atexit-max=$C0$Cg$C0 [default:$Cp 16$C0] - Number of exit handlers that can be registered by atexit(). - $Cr--rtc-callbacks=$C0$Cg$C0 [default:$Cp 5$C0] - Number of RTC callbacks that can be registered. - $Cr--events-queue-size=$C0$Cg$C0 [default:$Cp 64$C0] - Number of events simultaneously stored in the event queue. + $Cr--atexit-max$C0=$Cy$Cg [default:$Cp 16$Cg]$C0 + Number of exit handlers that can be registered by atexit(). + $Cr--rtc-callbacks$C0=$Cy$Cg [default:$Cp 5$Cg]$C0 + Number of RTC callbacks that can be registered. + $Cr--events-queue-size$C0=$Cy$Cg [default:$Cp 64$Cg]$C0 + Number of events simultaneously stored in the event queue. EOF exit 0 } +# +# Parsing arguments +# + +fail=false for arg; do case "$arg" in -h | --help) help;; - --no-syscalls) conf[GINT_NO_SYSCALLS]=true;; + --diagnostics) conf[GINT_DIAGNOSTICS]=true;; + --no-syscalls) conf[GINT_NO_SYSCALLS]=true;; --extended-libc) conf[GINT_EXTENDED_LIBC]=true;; --atexit-max=*) @@ -73,17 +97,19 @@ for arg; do case "$arg" in echo -e "$error unrecognized argument '$arg'"; fail=true;; esac; done +# +# Output config routines +# + output_config_gcc() { + [ "${conf[GINT_DIAGNOSTICS]}" != "" ] && echo "-D GINT_DIAGNOSTICS" + [ "${conf[GINT_NO_SYSCALLS]}" != "" ] && echo "-D GINT_NO_SYSCALLS" + [ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "-D GINT_EXTENDED_LIBC" + echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}" echo "-D RTC_CB_ARRAY_SIZE=${conf[RTC_CB_ARRAY_SIZE]}" echo "-D EVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}" - 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() @@ -91,6 +117,10 @@ output_config_make() [ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "config_ext=true" } +# +# Output config +# + if $fail; then echo "Configuration has not been modified." else diff --git a/debug/addin.ld b/debug/addin.ld new file mode 100644 index 0000000..b712b1d --- /dev/null +++ b/debug/addin.ld @@ -0,0 +1,31 @@ +OUTPUT_ARCH(sh3) +ENTRY(initialize) +MEMORY +{ + rom : o = 0x00300200, l = 512k + ram : o = 0x08100000, l = 64k /* pretty safe guess */ +} +SECTIONS +{ + .text : { + *(.pretext) /* init stuff */ + *(.text) + } > rom + .rodata : { + *(.rodata) + *(.rodata.str1.4) + _romdata = . ; /* symbol for initialization data */ + } > rom + .bss : { + _bbss = . ; + _bssdatasize = . ; + LONG(0); /* bssdatasize */ + *(.bss) *(COMMON); + _ebss = . ; + } > ram + .data : AT(_romdata) { + _bdata = . ; + *(.data); + _edata = . ; + } > ram +} diff --git a/debug/crt0.s b/debug/crt0.s new file mode 100644 index 0000000..93000cf --- /dev/null +++ b/debug/crt0.s @@ -0,0 +1,172 @@ + .section .pretext + .global initialize +initialize: + sts.l pr, @-r15 + + ! set up TLB + mov.l Hmem_SetMMU, r3 + mov.l address_one, r4 ! 0x8102000 + mov.l address_two, r5 ! 0x8801E000 + jsr @r3 ! _Hmem_SetMMU + mov #108, r6 + + ! clear the BSS + mov.l bbss, r4 ! start + mov.l ebss, r5 ! end + bra L_check_bss + mov #0, r6 +L_zero_bss: + mov.l r6, @r4 ! zero and advance + add #4, r4 +L_check_bss: + cmp/hs r5, r4 + bf L_zero_bss + + ! Copy the .data + mov.l bdata, r4 ! dest + mov.l edata, r5 ! dest limit + mov.l romdata, r6 ! source + bra L_check_data + nop +L_copy_data: + mov.l @r6+, r3 + mov.l r3, @r4 + add #4, r4 +L_check_data: + cmp/hs r5, r4 + bf L_copy_data + + mov.l bbss, r4 + mov.l edata, r5 + sub r4, r5 ! size of .bss and .data sections + add #4, r5 + mov.l bssdatasize, r4 + mov.l r5, @r4 + + mov.l GLibAddinAplExecutionCheck, r2 + mov #0, r4 + mov #1, r5 + jsr @r2 ! _GLibAddinAplExecutionCheck(0,1,1); + mov r5, r6 + + mov.l CallbackAtQuitMainFunction, r3 + mov.l exit_handler, r4 + jsr @r3 ! _CallbackAtQuitMainFunction(&exit_handler) + nop + mov.l main, r3 + jmp @r3 ! _main() + lds.l @r15+, pr + +_exit_handler: + mov.l r14, @-r15 + mov.l r13, @-r15 + mov.l r12, @-r15 + sts.l pr, @-r15 + + mov.l Bdel_cychdr, r14 + jsr @r14 ! _Bdel_cychdr + mov #6, r4 + jsr @r14 ! _Bdel_cychdr + mov #7, r4 + jsr @r14 ! _Bdel_cychdr + mov #8, r4 + jsr @r14 ! _Bdel_cychdr + mov #9, r4 + jsr @r14 ! _Bdel_cychdr + mov #10, r4 + + mov.l BfileFLS_CloseFile, r12 + mov #4, r14 + mov #0, r13 +L_close_files: + jsr @r12 ! _BfileFLS_CloseFile + mov r13, r4 + add #1, r13 + cmp/ge r14, r13 + bf L_close_files + + mov.l flsFindClose, r12 + mov #0, r13 +L_close_finds: + jsr @r12 ! _flsFindClose + mov r13, r4 + add #1, r13 + cmp/ge r14, r13 + bf L_close_finds + + lds.l @r15+, pr + mov.l @r15+, r12 + mov.l @r15+, r13 + mov.l Bkey_Set_RepeatTime_Default, r2 + jmp @r2 ! _Bkey_Set_RepeatTime_Default + mov.l @r15+, r14 + +.align 4 +address_two: .long 0x8801E000 +address_one: .long 0x8102000 +Hmem_SetMMU: .long _Hmem_SetMMU +GLibAddinAplExecutionCheck: .long _GLibAddinAplExecutionCheck +CallbackAtQuitMainFunction: .long _CallbackAtQuitMainFunction +Bdel_cychdr: .long _Bdel_cychdr +BfileFLS_CloseFile: .long _BfileFLS_CloseFile +flsFindClose: .long _flsFindClose +Bkey_Set_RepeatTime_Default: .long _Bkey_Set_RepeatTime_Default +bbss: .long _bbss +ebss: .long _ebss +edata: .long _edata +bdata: .long _bdata +romdata: .long _romdata +bssdatasize: .long _bssdatasize + +exit_handler: .long _exit_handler +main: .long _main + +_Hmem_SetMMU: + mov.l sc_addr, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x3FA + +_Bdel_cychdr: + mov.l sc_addr, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x119 + +_BfileFLS_CloseFile: + mov.l sc_addr, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x1E7 + +_Bkey_Set_RepeatTime_Default: + mov.l sc_addr, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x244 + +_CallbackAtQuitMainFunction: + mov.l sc_addr, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x494 + +_flsFindClose: + mov.l sc_addr, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x218 + +_GLibAddinAplExecutionCheck: + mov.l sc_addr, r2 + mov #0x13, r0 + jmp @r2 + nop +sc_addr: .long 0x80010070 +.end diff --git a/debug/gintdbg.c b/debug/gintdbg.c new file mode 100644 index 0000000..61c70af --- /dev/null +++ b/debug/gintdbg.c @@ -0,0 +1,178 @@ +//--- +// +// gintdbg +// +// A simple debugger for gint applications, providing diagnoses to +// determine what went bad. +// +//--- + +// Just for structure definitions, gint does not run here. +#define GINT_DIAGNOSTICS +#include +#include + +#include +#include +#include + +// Some functions from other files... +int vsprintf(char *buffer, const char *format, va_list args); +int sprintf(char *buffer, const char *format, ...); + +//--- +// Some util... +//--- + +static int print_row = 1; + +void print(int col, const char *format, ...) +{ + int row = print_row; + print_row++; + if(row < 1 || row > 8) return; + + char buffer[256]; + va_list args; + va_start(args, format); + vsprintf(buffer, format, args); + va_end(args); + + locate(col, row); + Print((unsigned char *)buffer); +} + +void newline(void) +{ + print_row++; +} + +void nothing_found(void) +{ + unsigned int key; + + Bdisp_AllClr_VRAM(); + PopUpWin(6); + print_row = 2; + print(3, "Apparently there"); + print(3, "is no diagnostic."); + print(3, ""); + print(3, "Show anyway?"); + print(3, " Yes:[F1]"); + print(3, " No :[MENU]"); + + do GetKey(&key); + while(key != KEY_CTRL_F1); +} + +void show_diagnostics(void) +{ + volatile gint_diagnostics_t *dg = gint_diagnostics(); + + const char *stages[] = { + "Startup", "Sections", "MMU", "Gint", "Clock", "Constructors", + "Running", "Leaving", "Destructors", "Terminated", + }; + const char *mpus[] = { + "Unknown", "SH7337", "SH7355", "SH7305", "SH7724", + }; + + print(1, "Gint debugger (%d)", dg->counter); + newline(); + + print(1, "General information"); + print(2, "Magic 0x%02x", dg->magic); + print(2, "Stage %s", dg->stage <= 9 ? stages[dg->stage] : "-"); + if(dg->stage >= stage_gint) + { + print(2, "MPU %s", dg->mpu <= 4 ? mpus[dg->mpu] : "-"); + } + print(2, "Version %08x", dg->version); + newline(); + + print(1, "Memory map"); + print(2, "%08x romdata", dg->romdata); + print(2, "%08x vbr", dg->vbr_address); + print(2, "%08x:%05x text", dg->section_text.address, + dg->section_text.length); + print(2, "%08x:%05x data", dg->section_data.address, + dg->section_data.length); + print(2, "%08x:%05x bss", dg->section_bss.address, + dg->section_bss.length); + + print(2, "%08x:%05x gint", dg->section_gint.address, + dg->section_gint.length); + newline(); + + print(1, "Exception records"); + size_t len = sizeof dg->except_vect; + char line[19]; + for(size_t i = 0; i < len; i += 6) + { + for(size_t n = 0; n < 6 && i + n < len; n++) + { + size_t index = (dg->excepts + i + n) % len; + sprintf(line + 3 * n, " %02x", dg->except_vect[index]); + } + + print(1, "%s", line); + } + print(2, "SPC %08x", dg->spc); + print(2, "SSR %08x", dg->ssr); + print(2, "EXPEVT %08x", dg->expevt); + print(2, "TEA %08x", dg->tea); + newline(); + + if(dg->stage >= stage_clock) + { + print(1, "Clock frequencies"); + print(2, "Bus clock %d MHz", dg->Bphi_f); + print(2, "Peripheral %d MHz", dg->Pphi_f); + print(2, "Processor %d MHz", dg->Iphi_f); + newline(); + } +} + +/* + main() + Let's do this! +*/ +int main(void) +{ + volatile gint_diagnostics_t *dg = gint_diagnostics(); + unsigned int key; + + if(dg->magic != GINT_DIAGNOSTICS_MAGIC + || dg->stage > 9 + || dg->mpu > 4 + ) nothing_found(); + + int total_height = -1; + int y = 0; + + while(1) + { + Bdisp_AllClr_VRAM(); + print_row = 1 - y; + + show_diagnostics(); + if(total_height < 0) total_height = print_row - 1; + + // Drawing a scrollbar. + if(total_height > 8) + { + int base = (64 * y) / total_height; + int height = (64 * 8) / total_height; + Bdisp_DrawLineVRAM(127, base, 127, base + height); + } + Bdisp_PutDisp_DD(); + + do GetKey(&key); + while(key != KEY_CTRL_UP && key != KEY_CTRL_DOWN); + + if(key == KEY_CTRL_UP && y > 0) y--; + else if(key == KEY_CTRL_DOWN && y + 8 < total_height) y++; + } + + return 1; +} diff --git a/debug/icon.bmp b/debug/icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..6240f6142013afb0b62eb2e7a8d0c96b8cdb2702 GIT binary patch literal 1870 zcmb_bu@S;R3^Nok01Xwxq@jX>Iq2w^fC1SjI4@qWIG-mR`0kA?%d+8)?sVQ-io55l zzk|P>zm@Ghn>{yX4=09~qdQ)1887`@2Vvj$f%#wQ-oMApIl-7JjX?CnBofwin2LIJz^Ta(a3l_XCO0G1FlJD> 8)) +#define UtlSwapDword(l) (unsigned long)((((l) & 0x000000ff) << 24) | (((l) & 0x0000ff00) << 8) | (((l) & 0xff000000) >> 24) | (((l) & 0x00ff0000) >> 8)) +#define UtlSwapInteger(i) UtlSwapDword(i) +#define UtlSwapPointer(p) (void*)((((unsigned long)(p) & 0x000000ff) << 24) | (((unsigned long)(p) & 0x0000ff00) << 8) | (((unsigned long)(p) & 0xff000000) >> 24) | (((unsigned long)(p) & 0x00ff0000) >> 8)) + + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/debug/include/filebios.h b/debug/include/filebios.h new file mode 100644 index 0000000..276d9ce --- /dev/null +++ b/debug/include/filebios.h @@ -0,0 +1,116 @@ +/*****************************************************************/ +/* */ +/* CASIO fx-9860G SDK Library */ +/* */ +/* File name : filebios.h */ +/* */ +/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */ +/* */ +/*****************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __FILEBIOS_H__ +#define __FILEBIOS_H__ + + +// Defines + +#define FONTCHARACTER unsigned short + +#define _OPENMODE_READ 0x01 +#define _OPENMODE_READ_SHARE 0x80 +#define _OPENMODE_WRITE 0x02 +#define _OPENMODE_READWRITE 0x03 +#define _OPENMODE_READWRITE_SHARE 0x83 + +#define _CREATEMODE_BINARY 1 +#define _CREATEMODE_DIRECTORY 5 + +enum DEVICE_TYPE{ + DEVICE_MAIN_MEMORY, + DEVICE_STORAGE, + DEVICE_SD_CARD, // only fx-9860G SD model +}; + + +// File system standard error code +#define IML_FILEERR_NOERROR 0 +#define IML_FILEERR_ENTRYNOTFOUND -1 +#define IML_FILEERR_ILLEGALPARAM -2 +#define IML_FILEERR_ILLEGALPATH -3 +#define IML_FILEERR_DEVICEFULL -4 +#define IML_FILEERR_ILLEGALDEVICE -5 +#define IML_FILEERR_ILLEGALFILESYS -6 +#define IML_FILEERR_ILLEGALSYSTEM -7 +#define IML_FILEERR_ACCESSDENYED -8 +#define IML_FILEERR_ALREADYLOCKED -9 +#define IML_FILEERR_ILLEGALTASKID -10 +#define IML_FILEERR_PERMISSIONERROR -11 +#define IML_FILEERR_ENTRYFULL -12 +#define IML_FILEERR_ALREADYEXISTENTRY -13 +#define IML_FILEERR_READONLYFILE -14 +#define IML_FILEERR_ILLEGALFILTER -15 +#define IML_FILEERR_ENUMRATEEND -16 +#define IML_FILEERR_DEVICECHANGED -17 +//#define IML_FILEERR_NOTRECORDFILE -18 // Not used +#define IML_FILEERR_ILLEGALSEEKPOS -19 +#define IML_FILEERR_ILLEGALBLOCKFILE -20 +//#define IML_FILEERR_DEVICENOTEXIST -21 // Not used +//#define IML_FILEERR_ENDOFFILE -22 // Not used +#define IML_FILEERR_NOTMOUNTDEVICE -23 +#define IML_FILEERR_NOTUNMOUNTDEVICE -24 +#define IML_FILEERR_CANNOTLOCKSYSTEM -25 +#define IML_FILEERR_RECORDNOTFOUND -26 +//#define IML_FILEERR_NOTDUALRECORDFILE -27 // Not used +#define IML_FILEERR_NOTALARMSUPPORT -28 +#define IML_FILEERR_CANNOTADDALARM -29 +#define IML_FILEERR_FILEFINDUSED -30 +#define IML_FILEERR_DEVICEERROR -31 +#define IML_FILEERR_SYSTEMNOTLOCKED -32 +#define IML_FILEERR_DEVICENOTFOUND -33 +#define IML_FILEERR_FILETYPEMISMATCH -34 +#define IML_FILEERR_NOTEMPTY -35 +#define IML_FILEERR_BROKENSYSTEMDATA -36 +#define IML_FILEERR_MEDIANOTREADY -37 +#define IML_FILEERR_TOOMANYALARMITEM -38 +#define IML_FILEERR_SAMEALARMEXIST -39 +#define IML_FILEERR_ACCESSSWAPAREA -40 +#define IML_FILEERR_MULTIMEDIACARD -41 +#define IML_FILEERR_COPYPROTECTION -42 +#define IML_FILEERR_ILLEGALFILEDATA -43 + +// FILE_INFO.type +#define DT_DIRECTORY 0x0000 // Directory +#define DT_FILE 0x0001 // File +#define DT_ADDIN_APP 0x0002 // Add-In application +#define DT_EACT 0x0003 // eActivity +#define DT_LANGUAGE 0x0004 // Language +#define DT_BITMAP 0x0005 // Bitmap +#define DT_MAINMEM 0x0006 // Main Memory data +#define DT_TEMP 0x0007 // Temporary data +#define DT_DOT 0x0008 // . (Current directory) +#define DT_DOTDOT 0x0009 // .. (Parent directory) +#define DT_VOLUME 0x000A // Volume label + + +// Structs + +typedef struct tag_FILE_INFO +{ + unsigned short id; + unsigned short type; + unsigned long fsize; // File size + unsigned long dsize; // Data size + unsigned int property; // The file has not been completed, except when property is 0. + unsigned long address; +} FILE_INFO; + + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/debug/include/fxlib.h b/debug/include/fxlib.h new file mode 100644 index 0000000..dc4534a --- /dev/null +++ b/debug/include/fxlib.h @@ -0,0 +1,100 @@ +/*****************************************************************/ +/* */ +/* CASIO fx-9860G SDK Library */ +/* */ +/* File name : fxlib.h */ +/* */ +/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */ +/* */ +/*****************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __FXLIB_H__ +#define __FXLIB_H__ + +#include "dispbios.h" +#include "filebios.h" +#include "keybios.h" + + +// Prototypes + +void Bdisp_AllClr_DD(void); +void Bdisp_AllClr_VRAM(void); +void Bdisp_AllClr_DDVRAM(void); +void Bdisp_AreaClr_DD(const DISPBOX *pArea); +void Bdisp_AreaClr_VRAM(const DISPBOX *pArea); +void Bdisp_AreaClr_DDVRAM(const DISPBOX *pArea); +void Bdisp_AreaReverseVRAM(int x1, int y1, int x2, int y2); +void Bdisp_GetDisp_DD(unsigned char *pData); +void Bdisp_GetDisp_VRAM(unsigned char *pData); +void Bdisp_PutDisp_DD(void); +void Bdisp_PutDispArea_DD(const DISPBOX *PutDispArea); +void Bdisp_SetPoint_DD(int x, int y, unsigned char point); +void Bdisp_SetPoint_VRAM(int x, int y, unsigned char point); +void Bdisp_SetPoint_DDVRAM(int x, int y, unsigned char point); +int Bdisp_GetPoint_VRAM(int x, int y); +void Bdisp_WriteGraph_DD(const DISPGRAPH *WriteGraph); +void Bdisp_WriteGraph_VRAM(const DISPGRAPH *WriteGraph); +void Bdisp_WriteGraph_DDVRAM(const DISPGRAPH *WriteGraph); +void Bdisp_ReadArea_DD(const DISPBOX *ReadArea, unsigned char *ReadData); +void Bdisp_ReadArea_VRAM(const DISPBOX *ReadArea, unsigned char *ReadData); +void Bdisp_DrawLineVRAM(int x1, int y1, int x2, int y2); +void Bdisp_ClearLineVRAM(int x1, int y1, int x2, int y2); + +void locate(int x, int y); +void Print(const unsigned char *str); +void PrintRev(const unsigned char *str); +void PrintC(const unsigned char *c); +void PrintRevC(const unsigned char *str); +void PrintLine(const unsigned char *str, int max); +void PrintRLine(const unsigned char *str, int max); +void PrintXY(int x, int y, const unsigned char *str, int type); +int PrintMini(int x, int y, const unsigned char *str, int type); +void SaveDisp(unsigned char num); +void RestoreDisp(unsigned char num); +void PopUpWin(int n); + +int Bfile_OpenFile(const FONTCHARACTER *filename, int mode); +int Bfile_OpenMainMemory(const unsigned char *name); +int Bfile_ReadFile(int HANDLE, void *buf, int size, int readpos); +int Bfile_WriteFile(int HANDLE, const void *buf, int size); +int Bfile_SeekFile(int HANDLE, int pos); +int Bfile_CloseFile(int HANDLE); +int Bfile_GetMediaFree(enum DEVICE_TYPE devicetype, int *freebytes); +int Bfile_GetFileSize(int HANDLE); +int Bfile_CreateFile(const FONTCHARACTER *filename, int size); +int Bfile_CreateDirectory(const FONTCHARACTER *pathname); +int Bfile_CreateMainMemory(const unsigned char *name); +int Bfile_RenameMainMemory(const unsigned char *oldname, const unsigned char *newname); +int Bfile_DeleteFile(const FONTCHARACTER *filename); +int Bfile_DeleteDirectory(const FONTCHARACTER *pathname); +int Bfile_DeleteMainMemory(const unsigned char *name); +int Bfile_FindFirst(const FONTCHARACTER *pathname, int *FindHandle, FONTCHARACTER *foundfile, FILE_INFO *fileinfo); +int Bfile_FindNext(int FindHandle, FONTCHARACTER *foundfile, FILE_INFO *fileinfo); +int Bfile_FindClose(int FindHandle); + +void Bkey_Set_RepeatTime(long FirstCount, long NextCount); +void Bkey_Get_RepeatTime(long *FirstCount, long *NextCount); +void Bkey_Set_RepeatTime_Default(void); +int GetKeyWait(int sel, int time, int menu, unsigned int *keycode); +int IsKeyDown(int keycode); +int IsKeyUp(int keycode); +int GetKey(unsigned int *keycode); + +int SetTimer(int ID, int elapse, void (*hander)(void)); +int KillTimer(int ID); +void Sleep(int millisecond); + +void SetQuitHandler(void (*callback)(void)); +int INIT_ADDIN_APPLICATION(int isAppli, unsigned short OptionNum); + + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/debug/include/keybios.h b/debug/include/keybios.h new file mode 100644 index 0000000..848fa5f --- /dev/null +++ b/debug/include/keybios.h @@ -0,0 +1,158 @@ +/*****************************************************************/ +/* */ +/* CASIO fx-9860G SDK Library */ +/* */ +/* File name : keybios.h */ +/* */ +/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */ +/* */ +/*****************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __KEYBIOS_H__ +#define __KEYBIOS_H__ + + +// Defines + +// Character codes +#define KEY_CHAR_0 0x30 +#define KEY_CHAR_1 0x31 +#define KEY_CHAR_2 0x32 +#define KEY_CHAR_3 0x33 +#define KEY_CHAR_4 0x34 +#define KEY_CHAR_5 0x35 +#define KEY_CHAR_6 0x36 +#define KEY_CHAR_7 0x37 +#define KEY_CHAR_8 0x38 +#define KEY_CHAR_9 0x39 +#define KEY_CHAR_DP 0x2e +#define KEY_CHAR_EXP 0x0f +#define KEY_CHAR_PMINUS 0x87 +#define KEY_CHAR_PLUS 0x89 +#define KEY_CHAR_MINUS 0x99 +#define KEY_CHAR_MULT 0xa9 +#define KEY_CHAR_DIV 0xb9 +#define KEY_CHAR_FRAC 0xbb +#define KEY_CHAR_LPAR 0x28 +#define KEY_CHAR_RPAR 0x29 +#define KEY_CHAR_COMMA 0x2c +#define KEY_CHAR_STORE 0x0e +#define KEY_CHAR_LOG 0x95 +#define KEY_CHAR_LN 0x85 +#define KEY_CHAR_SIN 0x81 +#define KEY_CHAR_COS 0x82 +#define KEY_CHAR_TAN 0x83 +#define KEY_CHAR_SQUARE 0x8b +#define KEY_CHAR_POW 0xa8 +#define KEY_CHAR_IMGNRY 0x7f50 +#define KEY_CHAR_LIST 0x7f51 +#define KEY_CHAR_MAT 0x7f40 +#define KEY_CHAR_EQUAL 0x3d +#define KEY_CHAR_PI 0xd0 +#define KEY_CHAR_ANS 0xc0 +#define KEY_CHAR_LBRCKT 0x5b +#define KEY_CHAR_RBRCKT 0x5d +#define KEY_CHAR_LBRACE 0x7b +#define KEY_CHAR_RBRACE 0x7d +#define KEY_CHAR_CR 0x0d +#define KEY_CHAR_CUBEROOT 0x96 +#define KEY_CHAR_RECIP 0x9b +#define KEY_CHAR_ANGLE 0x7f54 +#define KEY_CHAR_EXPN10 0xb5 +#define KEY_CHAR_EXPN 0xa5 +#define KEY_CHAR_ASIN 0x91 +#define KEY_CHAR_ACOS 0x92 +#define KEY_CHAR_ATAN 0x93 +#define KEY_CHAR_ROOT 0x86 +#define KEY_CHAR_POWROOT 0xb8 +#define KEY_CHAR_SPACE 0x20 +#define KEY_CHAR_DQUATE 0x22 +#define KEY_CHAR_VALR 0xcd +#define KEY_CHAR_THETA 0xce +#define KEY_CHAR_A 0x41 +#define KEY_CHAR_B 0x42 +#define KEY_CHAR_C 0x43 +#define KEY_CHAR_D 0x44 +#define KEY_CHAR_E 0x45 +#define KEY_CHAR_F 0x46 +#define KEY_CHAR_G 0x47 +#define KEY_CHAR_H 0x48 +#define KEY_CHAR_I 0x49 +#define KEY_CHAR_J 0x4a +#define KEY_CHAR_K 0x4b +#define KEY_CHAR_L 0x4c +#define KEY_CHAR_M 0x4d +#define KEY_CHAR_N 0x4e +#define KEY_CHAR_O 0x4f +#define KEY_CHAR_P 0x50 +#define KEY_CHAR_Q 0x51 +#define KEY_CHAR_R 0x52 +#define KEY_CHAR_S 0x53 +#define KEY_CHAR_T 0x54 +#define KEY_CHAR_U 0x55 +#define KEY_CHAR_V 0x56 +#define KEY_CHAR_W 0x57 +#define KEY_CHAR_X 0x58 +#define KEY_CHAR_Y 0x59 +#define KEY_CHAR_Z 0x5a + + +// Control codes +#define KEY_CTRL_NOP 0 +#define KEY_CTRL_EXE 30004 +#define KEY_CTRL_DEL 30025 +#define KEY_CTRL_AC 30015 +#define KEY_CTRL_FD 30046 +#define KEY_CTRL_XTT 30001 +#define KEY_CTRL_EXIT 30002 +#define KEY_CTRL_SHIFT 30006 +#define KEY_CTRL_ALPHA 30007 +#define KEY_CTRL_OPTN 30008 +#define KEY_CTRL_VARS 30016 +#define KEY_CTRL_UP 30018 +#define KEY_CTRL_DOWN 30023 +#define KEY_CTRL_LEFT 30020 +#define KEY_CTRL_RIGHT 30021 +#define KEY_CTRL_F1 30009 +#define KEY_CTRL_F2 30010 +#define KEY_CTRL_F3 30011 +#define KEY_CTRL_F4 30012 +#define KEY_CTRL_F5 30013 +#define KEY_CTRL_F6 30014 +#define KEY_CTRL_CATALOG 30100 +#define KEY_CTRL_CAPTURE 30055 +#define KEY_CTRL_CLIP 30050 +#define KEY_CTRL_PASTE 30036 +#define KEY_CTRL_INS 30033 +#define KEY_CTRL_MIXEDFRAC 30054 +#define KEY_CTRL_FRACCNVRT 30026 +#define KEY_CTRL_QUIT 30029 +#define KEY_CTRL_PRGM 30028 +#define KEY_CTRL_SETUP 30037 +#define KEY_CTRL_PAGEUP 30052 +#define KEY_CTRL_PAGEDOWN 30053 +#define KEY_CTRL_MENU 30003 +#define KEY_CTRL_RESERVE1 30060 +#define KEY_CTRL_RESERVE2 30061 +#define KEY_CTRL_RESERVE3 30062 + + +// in Bkey_GetKeyWait function +#define KEYWAIT_HALTON_TIMEROFF 0 +#define KEYWAIT_HALTOFF_TIMEROFF 1 +#define KEYWAIT_HALTON_TIMERON 2 + +#define KEYREP_NOEVENT 0 +#define KEYREP_KEYEVENT 1 +#define KEYREP_TIMEREVENT 2 + + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/debug/include/timer.h b/debug/include/timer.h new file mode 100644 index 0000000..286f665 --- /dev/null +++ b/debug/include/timer.h @@ -0,0 +1,31 @@ +/*****************************************************************/ +/* */ +/* CASIO fx-9860G SDK Library */ +/* */ +/* File name : timer.h */ +/* */ +/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */ +/* */ +/*****************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __TIMER_H__ +#define __TIMER_H__ + + +// Defines + +#define ID_USER_TIMER1 1 +#define ID_USER_TIMER2 2 +#define ID_USER_TIMER3 3 +#define ID_USER_TIMER4 4 +#define ID_USER_TIMER5 5 + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/debug/libfx.a b/debug/libfx.a new file mode 100644 index 0000000000000000000000000000000000000000..5ef92aaf7eaf3afca041f8f64511d0caf86c42e8 GIT binary patch literal 410910 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 z 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 s0r9OE?c!NrzMIs{8f91wQ* -#include - -void vsprintf_int(char **buffer_ptr, int n) -{ - char *buffer = *buffer_ptr; - if(!n) - { - *buffer++ = '0'; - *buffer_ptr = buffer; - return; - } - if(n < 0) - { - *buffer++ = '-'; - n = -n; - } - - int digits = 0, x = n, copy; - while(x) digits++, x /= 10; - copy = digits; - - while(digits) - { - buffer[--digits] = n % 10 + '0'; - n /= 10; - } - - *buffer_ptr = buffer + copy; -} - -void vsprintf_hexa(char **buffer_ptr, uint32_t val, int digits, int zero) -{ - char *buffer = *buffer_ptr; - if(!val) - { - while(digits-- > 1) *buffer++ = (zero) ? '0' : ' '; - *buffer++ = '0'; - *buffer_ptr = buffer; - return; - } - - if(digits <= 0) - { - uint32_t x = val; - while(x) digits++, x >>= 4; - } - int copy = digits; - - while(val && digits) - { - buffer[--digits] = (val & 15) + '0' + 39 * ((val & 15) > 9); - val >>= 4; - } - while(digits) - { - buffer[--digits] = (zero) ? '0': ' '; - } - - *buffer_ptr = buffer + copy; -} - -void vsprintf_ptr(char **buffer_ptr, void *ptr) -{ - vsprintf_hexa(buffer_ptr, (uint32_t)ptr, 8, 1); -} - -void vsprintf_char(char **buffer_ptr, int c) -{ - char *buffer = *buffer_ptr; - *buffer++ = c; - *buffer_ptr = buffer; -} - -void vsprintf_str(char **buffer_ptr, const char *str) -{ - char *buffer = *buffer_ptr; - while(*str) *buffer++ = *str++; - *buffer_ptr = buffer; -} - -int vsprintf(char *buffer, const char *format, va_list args) -{ - char *save = buffer; - int zero, count; - - while(*format) - { - if(*format != '%') - { - *buffer++ = *format++; - continue; - } - if(!*++format) break; - - zero = 0; - count = 0; - - if(*format == '0') zero = 1, format++; - while(*format >= '0' && *format <= '9') - { - count *= 10; - count += (*format++ - '0'); - } - if(!*format) break; - - switch(*format) - { - case 'd': - vsprintf_int(&buffer, va_arg(args, int)); - break; - case 'x': - vsprintf_hexa(&buffer, va_arg(args, uint32_t), count, - zero); - break; - case 'p': - vsprintf_ptr(&buffer, va_arg(args, void *)); - break; - case 'c': - vsprintf_char(&buffer, va_arg(args, int)); - break; - case 's': - vsprintf_str(&buffer, va_arg(args, const char *)); - break; - default: - *buffer++ = *format; - break; - } - - format++; - } - - *buffer = 0; - return buffer - save; -} - -int sprintf(char *buffer, const char *format, ...) -{ - va_list args; - va_start(args, format); - int x = vsprintf(buffer, format, args); - va_end(args); - - return x; -} diff --git a/demo/gintdemo.c b/demo/gintdemo.c index cfc653d..4655bf6 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -277,8 +277,7 @@ void tlb_debug(void) } */ -#include -#include +#include /* main_menu() @@ -435,14 +434,11 @@ void main_menu(int *category, int *app) index = 0; scroll = 0; break; - case KEY_F5: - gint_switch(); - break; - case KEY_F6:; +/* case KEY_F6:; void screen(void); screen(); break; - +*/ case KEY_UP: if(list && list_len > 1) { @@ -484,11 +480,6 @@ void main_menu(int *category, int *app) if(app) *app = index + 1; return; -/* case KEY_MENU: - if(category) *category = 0; - if(app) *app = 0; - return; -*/ default: leave = 0; } @@ -533,7 +524,7 @@ int main(void) return 0; } -void crash(void) +static void crash(void) { __asm__( "mov #0, r0 \n\t" @@ -542,7 +533,7 @@ void crash(void) ); } -void screen(void) +static void screen(void) { enum { File = 1, Folder = 5 }; enum { Read = 0x01, Write = 0x02, ReadWrite = Read | Write }; diff --git a/demo/gintdemo.ld b/demo/gintdemo.ld index 7a7d11e..5df3f76 100644 --- a/demo/gintdemo.ld +++ b/demo/gintdemo.ld @@ -13,36 +13,32 @@ ENTRY(_start) MEMORY { + /* System-managed mappings map this area into the add-in data */ rom : o = 0x00300200, l = 512k - /* 0x0810000 is apparently mapped to 0x8801c0000. */ + /* 0x0810000 is apparently mapped to 0x8801c0000 */ ram : o = 0x08100000, l = 8k + /* RAM section from P1 area, no MMU involved */ realram : o = 0x8800d000, l = 12k } SECTIONS { - /* - ROM sections : binary code and read-only data. - */ + /* ROM sections: binary code and read-only data */ .text : { - _btext = . ; - /* Initialization code. */ *(.pretext.entry) *(.pretext) - _bctors = . ; + _bctors = ABSOLUTE(.); *(.ctors) - _ectors = . ; - _bdtors = . ; + _ectors = ABSOLUTE(.); + _bdtors = ABSOLUTE(.); *(.dtors) - _edtors = . ; + _edtors = ABSOLUTE(.); *(.text) *(.text.*) - - _etext = . ; } > rom .rodata : { @@ -55,26 +51,23 @@ SECTIONS _romdata = ALIGN(4) ; } > rom - - - /* - RAM sections : bss section and read/write data. - The BSS section is meant to be stripped from the ELF file (to - reduce the binary size) and initialized with zeros in the - initialization routine, therefore its location is undefined. - */ + /* RAM sections: bss section and read/write data + The .bss section is to be stripped from the ELF file and wiped at + startup, hence its location is undefined */ .bss : { - _bbss = . ; + _bbss = ABSOLUTE(.); + _sbss = ABSOLUTE(SIZEOF(.bss)); + *(.bss) - _ebss = . ; } > ram .data : AT(_romdata) ALIGN(4) { - _bdata = . ; + _bdata = ABSOLUTE(.); + _sdata = ABSOLUTE(SIZEOF(.data)); + *(.data) *(.data.*) - _edata = . ; } > ram .cc : AT(_romdata + SIZEOF(.data)) ALIGN(4) { @@ -82,33 +75,39 @@ SECTIONS *(.jcr) . = ALIGN(4); - _gint_data = _romdata + SIZEOF(.data) + SIZEOF(.cc) ; } > ram + /* Real RAM sections: interrupt handlers and some *uninitialized* gint + data */ - - /* - Real RAM : interrupt, exception and TLB miss handlers. - */ + _gint_data = _romdata + SIZEOF(.data) + SIZEOF(.cc) ; .gint : AT(_gint_data) ALIGN(4) { - /* The vbr needs to be 0x100-aligned because of an ld issue. */ + /* The vbr needs to be 0x100-aligned because of an ld issue */ . = ALIGN(0x100) ; - _gint_vbr = . ; - _bgint = . ; - /* Exception handler. */ + _gint_vbr = . ; + _bgint = ABSOLUTE(.) ; + + /* Exception handler */ . = _gint_vbr + 0x100 ; *(.gint.exc) - /* TLB miss handler. */ + /* TLB miss handler */ . = _gint_vbr + 0x400 ; *(.gint.tlb) - /* Interrupt handler. */ + /* Interrupt handler */ . = _gint_vbr + 0x600 ; *(.gint.int) - _egint = . ; + . = ALIGN(4); + _egint = ABSOLUTE(.) ; + } > realram + + .gint_bss : AT(_gint_data + SIZEOF(.gint)) ALIGN(4) { + _bgbss = ABSOLUTE(.) ; + *(.gint.bss) + _egbss = ABSOLUTE(.) ; } > realram } diff --git a/demo/resources/font_modern.bmp b/demo/resources/font_modern.bmp index 0340f76caa3294d8fa52e91b46611365384cad91..1286f301f05bad4f17e1def00e15eb2592050cba 100644 GIT binary patch delta 126 zcmex$fbrJ>#to_h6DM4qoDd*1d4eg=#0Iv>a)Mox=LfS*5)|5eKrn-G@&i|)$p@5$ zCKs5CPIk}}n!Esr6GVXgpk$yn(a8elijxgQN`dM)Cm+y}2I-wVL0Nq=wrEJ6-?)6E6M$ delta 143 zcmex$fbrJ>#to_hlQ;NrPBxI`nf$^1z~qDgp~(|Wc_ucnO_mevn!JIZZE}JL&*X#{ zw#^3wGZ-g3xEV~IAIvtn0Vn~aCy7e~<%K2}1SJEticS_VSDb7hQVLcw!B=SV0c9Qt eTUmWFw41@#KO3=}8o^7aTI0qfwRvd8e#Ey30)5y*~ksy>jDbCm)kAIG@r^o5~ zPO|U%KJ^{+-A&K3ddfdN_;X;IjxTQ?Klr!*`%e7&+v|ekNbff0_w0WtoY%fS%%X_4 zGan$=n8>V4m!yGHL_t3Xg{d{@*}^G=lBE~=CS+CTxHWd|`Ohe<1MC8jPR?JpAWJ*Q zq+CLL)}`E4N$xsUfQgGpZvi>g61yV`JGsB7N}Dqqm&ub%N}yo7*YLMtcl)^6vGCIs zri8d_opLZaU7742?q9~XTV}>(a;ivqapD8lA||+;D%@M;X3i=g0J&5T=0R#ZqW+%e79)rskkrDaJ6KC8t_qlTq0G{!FqIaEG!HakjRyX(gMr;LaQ? ckTsAkQV_cGhWBTfMWF1ssMh3NDQtEB3%4y6QUCw| literal 3194 zcmd6jv2Fq}5JZnukw}*&-$Rp%hVP(DN5u#7PhrHs)5>dGcZZO%bY^z^c36A2KYdNN zb<%#+ch>i*Z!`U9*V6g-)}M`O+Wj8>9(wNYM+X0&TRE`r;+R4mJs~3`xJ_#Q&Y1$=*zd!R>%-KDECgsbtk9K*p@0;5rKua{Mm?Nr3g-Iz9 z|Gh{U`~Jnnnc048HBiEmvkNoX;BKhD`A#rOyTDM7;6_h&W>reeY<(8gRAyGk7E zYDq$i>HBfc7AeL@2uKMMhQB|PEEU{>YdQg6WuY@^Ct^jHORz-=z(u=CkE(s(`{$|V PI7~hVt}<_xFzWrU> 8; - vl[o + 1] = light << 8; - vd[o] = dark >> 8; - vd[o + 1] = dark << 8; + int offset = (i << 2) + 3; + vl[offset] = -(!(i & 16)); + vd[offset] = -(i < 32); } - locate(3, 3, "light"); - print(4, 4, "%d", delay1); + text_configure(&res_font_modern, color_black); + gtext(13, 17, "light"); + gtext(13, 31, "dark"); - locate(3, 5, "dark"); + text_configure(NULL, color_black); + print(4, 4, "%d", delay1); print(4, 6, "%d", delay2); locate(3, selected ? 6 : 4, "\x02"); diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c index 02ea379..59f0471 100644 --- a/demo/test_keyboard.c +++ b/demo/test_keyboard.c @@ -4,9 +4,8 @@ #include #include -static int draw_keyboard(volatile uint8_t *state) +static void draw_keyboard(volatile uint8_t *state) { - int pressed_keys = 0; int i, j, k, l; int x, y; @@ -30,7 +29,6 @@ static int draw_keyboard(volatile uint8_t *state) // Drawing a filled shape when the key is pressed. if(state[i] & (0x80 >> j)) { - pressed_keys++; for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++) if(abs(k) + abs(l) <= 2) dpixel(x + k, y + l, color_black); @@ -51,8 +49,6 @@ static int draw_keyboard(volatile uint8_t *state) // An horizontal line to separate parts of the keyboard. dline(5, 28, 32, 28, color_black); - - return pressed_keys; } typedef struct { @@ -66,7 +62,7 @@ typedef struct { int pressed; } history_t; -static int pressed_keys = -1; +static int pressed_keys = 0; static int releases = 0; static void history_push(history_t *h, event_t event) @@ -120,19 +116,25 @@ static void draw_events(history_t *h) }; int y = 3; + int y_limit = 8 - (h->pressed > h->size); - print(8, 2, "%d %d %d", pressed_keys, h->pressed, releases); + extern font_t res_font_modern; + text_configure(&res_font_modern, color_black); + dtext(49, 12, "Key"); + dtext(89, 12, "Rep."); + // dprint(49, 7, "%d %d %d", h->pressed, pressed_keys, releases); + text_configure(NULL, color_black); + dline(45, 18, 120, 18, color_black); - for(int i = 0; i < h->size; i++) + for(int i = 0; i < h->size && y < y_limit; i++) if(h->data[i].key) { - if(!h->data[i].key) continue; - print(8, y, "%s", key_names[key_id(h->data[i].key)]); + dtext(49, y * 8 - 4, key_names[key_id(h->data[i].key)]); if(h->data[i].repeats > 1) - print(18, y, "%d", h->data[i].repeats); + dprint(89, y * 8 - 4, "%d", h->data[i].repeats); y++; } - if(h->pressed > h->size) print(8, 8, "(more)"); + if(h->pressed > h->size) dtext(49, 52, "(more)"); } /* @@ -149,15 +151,37 @@ void test_keyboard_events(void) }; event_t event; + // There may be keys pressed when this test starts. We need to detect + // which and create a valid history for them. + volatile const uint8_t *buffer = keyboard_stateBuffer(); + + // For the purpose of this function we don't need to calculate the + // other fields. + event_t push_event = { + .type = event_key_press, + .key.code = KEY_AC_ON, + }; + + // Finding all the pressed keys and pushing them in the history. + if(buffer[0] & 1) history_push(&history, push_event); + for(int row = 1; row <= 9; row++) for(int col = 0; col < 8; col++) + { + // Eliminate non-existing keys. + if(col > 6 || col <= (row <= 4)) continue; + + if(buffer[row] & (1 << col)) + { + push_event.key.code = (col << 4) | row; + history_push(&history, push_event); + } + } + while(1) { dclear(); locate(1, 1, "Keyboard and events"); - // There may be more than zero keys pressed when this test - // starts. We need to detect this count automatically. - int x = draw_keyboard(keyboard_stateBuffer()); - if(pressed_keys < 0) pressed_keys = x; + draw_keyboard(keyboard_stateBuffer()); draw_events(&history); dupdate(); diff --git a/include/events.h b/include/events.h index a30729a..b4bce55 100644 --- a/include/events.h +++ b/include/events.h @@ -49,14 +49,14 @@ typedef struct { // This is the key code as defined in (a matrix code), and // probably what you need. - uint32_t code; + uint16_t code; // This is a "compact id" which can be used for array subscript. There // are only a few holes in the "compact id" numbering. - uint32_t id; + uint16_t id; // Character associated with the event key. int character; -} key_event_t; +} __attribute__((packed, aligned(4))) key_event_t; /* event_t @@ -77,7 +77,7 @@ typedef struct timer_t *timer; }; -} event_t; +} __attribute__((packed, aligned(4))) event_t; diff --git a/include/internals/gint.h b/include/internals/gint.h index fd51e06..6e7ed83 100644 --- a/include/internals/gint.h +++ b/include/internals/gint.h @@ -2,6 +2,7 @@ #define _INTERNALS_GINT_H #include +#include #include //--- @@ -45,7 +46,7 @@ void gint_setvbr(uint32_t vbr, void (*setup)(void)); Initializes gint. Loads the interrupt handler into the memory and sets the new vbr address. */ -void gint_init(void); +void gint_init(mpu_t mpu); /* gint_quit() @@ -135,101 +136,4 @@ void gint_restore_and_unlock_7305(environment_7305_t *env); volatile void *gint_reg_7705(gint_register_t reg); volatile void *gint_reg_7305(gint_register_t reg); - - -//--- -// Diagnostics -// When diagnostics are enabled, gint saves runtime information to a -// buffer in RAM, which by chance is held if the application crashes (I -// don't really know why). This allows deeper debugging. -//--- - -#ifdef GINT_DIAGNOSTICS - -#include - -// Determining whether the SaveDisp() buffer actually contains gint diagnostics -// is performed by checking a magic number (1/256 chance of failure) and the -// validity of all fields in the diagnostic information. Formally, a picture -// taken by SaveDisp() could fool the checks but this is *very* unlikely and -// the diagnostics should only be read just after gint crashes or stops anyway. -#define GINT_DIAGNOSTICS_MAGIC 0xb7 - -typedef enum -{ - stage_startup = 0, - stage_sections = 1, - stage_mmu = 2, - stage_gint = 3, - stage_clock = 4, - stage_ctors = 5, - stage_running = 6, - stage_leaving = 7, - stage_dtors = 8, - stage_terminated = 9, - -} gint_stage_t; - -typedef struct -{ - uint32_t address; - uint32_t length; - -} gint_memsection_t; - -typedef struct -{ - // Magic number to check whether there is a diagnostic. - uint8_t magic :8; - // Unique counter that is incremented at each execution, allowing to - // distinguish diagnostics output at different times if the application - // crashes repeatedly. - uint8_t counter :8; - // How many work of initialization had been successfully done before - // the application crashed. - gint_stage_t stage :8; - // What kind of MPU the library detected (undefined if the - // initialization stage does not reach stage_gint). - mpu_t mpu :8; - - // Frequency of the main clocks, in MHz. - uint8_t Bphi_f :8; - uint8_t Iphi_f :8; - uint8_t Pphi_f :8; - - // What kind of exceptions occurred last. - uint8_t excepts :8; - uint8_t except_vect[12]; - // Last values held by registers SPC, SSR, EXPEVT / INTEVT2 / INTEVT, - // and TEA. - uint32_t spc; - uint32_t ssr; - uint32_t expevt; - uint32_t tea; - - // Gint version number, on the form 0xMMmmbbbb, where MM is the major - // version, mm the minor version and bbbb the build number. - uint32_t version; - // Location of the VBR at the time of execution. - uint32_t vbr_address; - - // Memory map. - uint32_t romdata; - gint_memsection_t section_text; - gint_memsection_t section_data; - gint_memsection_t section_bss; - gint_memsection_t section_gint; - -} gint_diagnostics_t; - -// This is somewhere inside the buffers of SaveDisp(), 3 bytes inside the first -// buffer to be exact (that's to be 4-aligned). -// This buffer is 1024-byte long so the logs must fit in 1021 bytes to be safe. -// It looks like that this RAM area is generally not cleared when the -// calculator reboots after a crash, even though it does not seem to work with -// manual resets. Maybe it can provide useful information in some cases. -#define gint_diagnostics() ((volatile gint_diagnostics_t *)0x88004d90) - -#endif // GINT_DIAGNOSTICS - #endif // _INTERNALS_GINT_H diff --git a/include/internals/init.h b/include/internals/init.h new file mode 100644 index 0000000..66b3b29 --- /dev/null +++ b/include/internals/init.h @@ -0,0 +1,37 @@ +//--- +// gint core module: init +// Program initialization and display manipulation for the startup logs. +//--- + +#ifndef _INTERNALS_INIT_H +#define _INTERNALS_INIT_H + +#include + +struct qdiv +{ + uint32_t q, r; +}; + +/* qdiv10() -- quickly divide by 10 */ +struct qdiv qdiv10(uint32_t n); + +/* init_version() -- get a version string */ +const char *init_version(void); + +/* init_stage() -- change the current init stage */ +void init_stage(const char *name); + +/* init_halt() -- halt the program */ +void init_halt(void); + +/* print() -- print text on a 21*8 grid */ +#define print(x, y, str) dtext((x) * 6 - 5, (y) * 8 - 8, (str)) + +/* print_dec() -- print a number in base 10 */ +void print_dec(int x, int y, int n, int digits); + +/* print_hex() -- print a number in base 16 */ +void print_hex(int x, int y, uint32_t n, int digits); + +#endif // _INTERNALS_INIT_H diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h index 2ed7f9e..01d24eb 100644 --- a/include/internals/keyboard.h +++ b/include/internals/keyboard.h @@ -39,4 +39,11 @@ int getPressedKeys(volatile uint8_t *keyboard_state, int *keys, int count); void keyboard_updateState_7705(volatile uint8_t *state); void keyboard_updateState_7305(volatile uint8_t *state); +/* + keyboard_interrupt() + Answers an interrupt event by updating the keyboard state and + generating the associated keyboard events. +*/ +void keyboard_interrupt(void); + #endif // _INTERNALS_KEYBOARD_H diff --git a/include/internals/syscalls.h b/include/internals/syscalls.h new file mode 100644 index 0000000..ae05b78 --- /dev/null +++ b/include/internals/syscalls.h @@ -0,0 +1,26 @@ +//--- +// gint core module: syscalls +// Some of the functionality still requires interacting with the system. +//--- + +#ifndef _INTERNALS_SYSCALLS_H +#define _INTERNALS_SYSCALLS_H + +#include + +/* malloc() -- allocate data in heap */ +void *__malloc(size_t size); + +/* free() -- free data allocated by malloc(), calloc() or realloc() */ +void __free(void *ptr); + +/* realloc() -- reallocate a chunk of memory */ +void *__realloc(void *chunk, size_t new_size); + +/* get_os_version() -- write the OS version in format MM.mm.pppp to a string */ +void __get_os_version(char *str); + +/* system_menu() -- go back to menu, assuming the system has the control */ +void __system_menu(const void *vram); + +#endif // _INTERNALS_SYSCALLS_H diff --git a/include/internals/tales.h b/include/internals/tales.h index a599d4c..455246d 100644 --- a/include/internals/tales.h +++ b/include/internals/tales.h @@ -10,13 +10,6 @@ extern font_t *font; extern color_t operator; -/* - tales_init() - Configures tales with the default font (which is part of gint). -*/ -__attribute__((constructor)) -void tales_init(void); - /* getCharacterIndex() Returns the index of a character in a font data area depending on the diff --git a/include/internals/timer.h b/include/internals/timer.h index c169a6e..8e4feec 100644 --- a/include/internals/timer.h +++ b/include/internals/timer.h @@ -11,11 +11,9 @@ */ typedef struct timer_t { - // Current delay, how much time elapsed since last interrupt occurred, - // and how many repeats are left. - int ms_delay; - int ms_elapsed; - int repeats_left; + // Current delay, how much time elapsed since last interrupt occurred. + uint32_t ms_delay; + uint32_t ms_elapsed; // Is the virtual slot free? Is the virtual timer active? uint8_t used :1; @@ -25,12 +23,14 @@ typedef struct timer_t uint8_t vsupport :1; // How many events do I have received but not executed? uint8_t events :4; + // How many repeats are left. + uint32_t repeats_left :24; // Callback function (NULL for event-firing timers) and its argument. void *callback; void *argument; -} timer_t; +} __attribute__((packed, aligned(4))) timer_t; // Hardware timers. extern timer_t htimers[3]; diff --git a/include/mpu.h b/include/mpu.h index d62919c..8b7219d 100644 --- a/include/mpu.h +++ b/include/mpu.h @@ -12,8 +12,8 @@ // care of assuming unknown MPUs are SH4, which is the more reasonable // option. // -// In a general way, it is advised to always use the following -// alternative (which gint does): +// It is advised to always use the following alternative (which gint +// does): // // if(isSH3()) // { @@ -54,7 +54,7 @@ extern const mpu_t MPU_CURRENT; // Quick SH3 test. It is safer to assume that an unknown model is SH4 because // SH3-based models are not produced anymore. #define isSH3() (MPU_CURRENT == mpu_sh7337 || MPU_CURRENT == mpu_sh7355) -#define isSH4() !isSH3() +#define isSH4() (!isSH3()) diff --git a/src/core/crt0.c b/src/core/crt0.c deleted file mode 100644 index 618af26..0000000 --- a/src/core/crt0.c +++ /dev/null @@ -1,253 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -int main(void); - -static void init(void); -static void fini(void); - -// Symbols provided by the linker script. -extern uint32_t - etext, btext, // Location of .text section - bdata, edata, // Location of .data section - bbss, ebss, // Location of .bss section - bgint, egint, // Location of interrupt handler and gint data - gint_vbr, // VBR address - romdata; // ROM address of .data section contents - -// This variable should be overwritten before being returned, so the default -// value doesn't matter much. -static int exit_code = EXIT_SUCCESS; -static jmp_buf env; - -// Exit handlers. -void (*atexit_handlers[ATEXIT_MAX])(void); -int atexit_index = 0; - - - -/* - start() - Program entry point. Loads the data section into the memory and invokes - main(). Also prepares the execution environment by initializing all the - modules. -*/ -__attribute__((section(".pretext.entry"))) int start(void) -{ - #ifdef GINT_DIAGNOSTICS - - // OK, so fill as much information as possible so that in case anything - // goes wrong we can try to analyze what's been going on. - volatile gint_diagnostics_t *dg = gint_diagnostics(); - - // All that follows is "safe" information that can be saved immediately - // because it will never change anyway. - // Basic environment description. - dg->magic = GINT_DIAGNOSTICS_MAGIC; - dg->counter = dg->counter + 1; - dg->mpu = mpu_unknown; - dg->version = (uint32_t)&GINT_VERSION; - dg->stage = stage_startup; - - // Exception records: what kind of exceptions / TLB faults / interrupts - // occurred last to get some trace in case of crash. - dg->excepts = 0; - for(size_t i = 0; i < sizeof dg->except_vect; i++) - dg->except_vect[i] = 0; - dg->spc = 0x00000000; - dg->ssr = 0x00000000; - dg->expevt = 0x00000000; - dg->tea = 0x00000000; - - // Memory map: provides information about alignment and the relative - // size and position of each section, as well as read-only / read-write - // types of addresses. - dg->section_text.address = (uint32_t)&btext; - dg->section_text.length = (uint32_t)&etext - (uint32_t)&btext; - dg->section_data.address = (uint32_t)&bdata; - dg->section_data.length = (uint32_t)&edata - (uint32_t)&bdata; - dg->section_bss.address = (uint32_t)&bbss; - dg->section_bss.length = (uint32_t)&ebss - (uint32_t)&bbss; - dg->romdata = (uint32_t)&romdata; - - // Basic information about the running library. - dg->vbr_address = (uint32_t)&gint_vbr; - dg->section_gint.address = (uint32_t)&bgint; - dg->section_gint.length = (uint32_t)&egint - (uint32_t)&bgint; - #endif - - // Clearing the .bss section. - uint32_t *bss = &bbss; - while(bss < &ebss) *bss++ = 0; - - // Copying the .data section. - uint32_t *data = &bdata, *src = &romdata; - while(data < &edata) *data++ = *src++; - - #ifdef GINT_DIAGNOSTICS - dg->stage = stage_sections; - #endif - - // Trying to get the system to fill the TLB without editing it - // directly, so that it does not go on rampage when finding out. - mmu_pseudoTLBInit(); - - #ifdef GINT_DIAGNOSTICS - // At this point it would be wise to include a copy of the TLB, but - // it's already a huge array. Maybe later... - /* TODO Debug TLB? */ - dg->stage = stage_mmu; - #endif - - // Initializing gint, which does several things: - // - Detect the MPU and platform - // - Initialize register addresses in a platform-independent way - // - Save the current environment information in a large buffer - // - Set up the interrupt handler and configure everything gint needs - gint_init(); - - #ifdef GINT_DIAGNOSTICS - dg->mpu = MPU_CURRENT; - dg->stage = stage_gint; - #endif - - // Measuring clock frequencies. - clock_measure(); - clock_measure_end(); - - #ifdef GINT_DIAGNOSTICS - clock_config_t clock = clock_config(); - dg->Bphi_f = clock.Bphi_f / 1000000; - dg->Iphi_f = clock.Iphi_f / 1000000; - dg->Pphi_f = clock.Pphi_f / 1000000; - dg->stage = stage_clock; - #endif - - // Calling global constructors. - init(); - - #ifdef GINT_DIAGNOSTICS - dg->stage = stage_ctors; - dg->stage = stage_running; - #endif - - // Saving the execution state there. - int x = setjmp(env); - // If the program has just started, executing main(). Otherwise, the - // exit code has already been set by abort() or similar. - if(!x) exit_code = main(); - - - - #ifdef GINT_DIAGNOSTICS - dg->stage = stage_leaving; - #endif - - /* TODO Flush and close opened streams. */ - - // Calling exit handlers and destructors. - while(atexit_index > 0) (*atexit_handlers[--atexit_index])(); - fini(); - - #ifdef GINT_DIAGNOSTICS - dg->stage = stage_dtors; - #endif - - // Un-initializing gint. - gint_quit(); - - #ifdef GINT_DIAGNOSTICS - dg->stage = stage_terminated; - #endif - - return exit_code; -} - -/* - init() - Calls the constructors. -*/ -static void init(void) -{ - extern void - (*bctors)(void), - (*ectors)(void); - void (**func)(void) = &bctors; - - while(func < &ectors) - { - (*(*func))(); - func++; - } -} - -/* - fini() - Calls the destructors. -*/ -static void fini(void) -{ - extern void - (*bdtors)(void), - (*edtors)(void); - void (**func)(void) = &bdtors; - - while(func < &edtors) - { - (*(*func))(); - func++; - } -} - - - -/* - abort() - Immediately ends the program without invoking the exit handlers. -*/ -void abort(void) -{ - exit_code = EXIT_FAILURE; - - // Avoiding any exit handler call. - atexit_index = 0; - - longjmp(env, 1); -} - -/* - exit() - Ends the program and returns the given exit code status. Calls exit - handlers before returning. - Usually exit() would ask the operating system to stop the process but - the fx-9860G executes only one program at a time and calls it as a - function. Reaching the program end point is therefore an efficient way - of achieving this goal while minimizing interaction with the operating - system. -*/ -void exit(int status) -{ - exit_code = status; - longjmp(env, 1); -} - -/* - atexit() - Registers a function to be called at normal program termination. -*/ -int atexit(void (*function)(void)) -{ - if(atexit_index >= ATEXIT_MAX) return 1; - - atexit_handlers[atexit_index++] = function; - return 0; -} diff --git a/src/core/init_quit.c b/src/core/init_quit.c index 6103f41..4d92d5c 100644 --- a/src/core/init_quit.c +++ b/src/core/init_quit.c @@ -21,11 +21,11 @@ static void setup(void) isSH3() ? gint_lock_and_setup_7705() : gint_lock_and_setup_7305(); } -void gint_init(void) +void gint_init(mpu_t mpu) { - // Detecting the MPU type. I don't like const-casting but this is still + // Setting the MPU type. I don't like const-casting but this is still // better than allowing the user to change the variable by mistake. - *((mpu_t *)&MPU_CURRENT) = getMPU(); + *((mpu_t *)&MPU_CURRENT) = mpu; // Loading the register addresses of the current platform. mod_init(); @@ -89,23 +89,29 @@ void gint_quit(void) #include #include +/* + __system_menu() + Updates the system's vram and triggers the calculator's main menu. +*/ +void __system_menu(void *vram); + +// Stores gint's configuration while the user visits the main menu. +static environment_t switch_env; + /* gint_switch() Temporarily returns to the system's main menu. */ -static environment_t switch_env; -static void switch_(void) +static void restore(void) { isSH3() ? gint_restore_and_unlock_7705(&switch_env.env_7705) : gint_restore_and_unlock_7305(&switch_env.env_7305); } void gint_switch(void) { - // Save the current environment in a special buffer, so that we can - // restore it if we ever come back to gint. isSH3() ? gint_save_7705(&switch_env.env_7705) : gint_save_7305(&switch_env.env_7305); - // Give the control back to the system. + gint_setvbr(gint.system_vbr, stop); // When returning to the add-in from the menu, the system displays the @@ -120,5 +126,5 @@ void gint_switch(void) __system_menu(vram); // If the user came back, restore the gint working environment. - gint_setvbr(gint.gint_vbr, switch_); + gint_setvbr(gint.gint_vbr, restore); } diff --git a/src/core/mpu.c b/src/core/mpu.c index 889e5a7..73d0a46 100644 --- a/src/core/mpu.c +++ b/src/core/mpu.c @@ -9,7 +9,8 @@ #include #include -const mpu_t MPU_CURRENT; +/* Located in gint's uninitialized bss section */ +__attribute__((section(".gint.bss"))) const mpu_t MPU_CURRENT; /* getMPU() diff --git a/src/core/syscalls.s b/src/core/syscalls.s index 960c2de..ad7f984 100644 --- a/src/core/syscalls.s +++ b/src/core/syscalls.s @@ -1,14 +1,25 @@ /* gint core module: syscalls - All the system calls used by the library. Somehow "the less, the - better". - We have finally gotten rid of every obscure system-related syscalls! + System calls (and the like) used by the library. The library should + rely the least possible on the system, but sometimes using the syscalls + is nothing of a nuisance. + + For instance, using the malloc()-family syscalls is a bit annoying + because it "locks" many functionalities. On the other hand, getting the + SaveDisp() buffer addresses to store data here is not a problem since + such data can very easily be relocated to static RAM. */ + /* Dynamic allocation */ .global ___malloc .global ___free .global ___realloc + + /* OS version */ + .global ___get_os_version + + /* Return to menu */ .global ___system_menu @@ -34,6 +45,13 @@ ___realloc: nop 1: .long 0xe6d +___get_os_version: + mov.l syscall_table, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x02ee + /* __system_menu() Brings one back to the system menu by putting KEY_MENU in the system's diff --git a/src/display/display_vram.c b/src/display/display_vram.c index 34ab168..c0c48ed 100644 --- a/src/display/display_vram.c +++ b/src/display/display_vram.c @@ -1,9 +1,8 @@ #include -// Program video ram. It resides in BSS section, therefore it is cleared at -// program initialization and stripped from the executable file. -static uint32_t local_vram[256]; -uint32_t *vram = local_vram; +/* Add-in monochrome vram in gint's uninitialized bss section */ +__attribute__((section(".gint.bss"))) static uint32_t vram_local[256]; +__attribute__((section(".gint.bss"))) uint32_t *vram; /* display_getLocalVRAM() @@ -16,7 +15,7 @@ uint32_t *vram = local_vram; */ inline uint32_t *display_getLocalVRAM(void) { - return local_vram; + return vram_local; } /* @@ -43,6 +42,6 @@ inline uint32_t *display_getCurrentVRAM(void) */ inline void display_useVRAM(uint32_t *ptr) { - if((intptr_t)ptr & 3) return; + if((uintptr_t)ptr & 3) return; vram = ptr; } diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index 8e9d207..666b725 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -10,9 +10,12 @@ #include #include #include +#include // Additional video rams used by the gray engine. -static uint32_t internal_vrams[3][256]; +#ifdef GINT_STATIC_GRAY +static uint32_t internals_vrams[3][256]; +#endif static uint32_t *vrams[4]; // Current vram set (0 or 1), delays of the light and dark frames respectively. @@ -31,10 +34,7 @@ static timer_t *gray_timer = NULL; // Interrupt control and initialization. //--- -/* - gray_interrupt() - Answers a timer interrupt. Swaps the buffers. -*/ +/* gray_interrupt() -- switch buffers and update the screen */ void gray_interrupt(void) { htimer_reload(timer_gray, delays[(~current) & 1]); @@ -43,21 +43,33 @@ void gray_interrupt(void) current ^= 1; } -/* - gray_init() - Initializes the gray engine. -*/ -__attribute__((constructor)) void gray_init(void) +/* gray_init() -- setup the video ram buffers and timer delays */ +__attribute__((constructor)) static void gray_init(void) { vrams[0] = display_getLocalVRAM(); +#ifdef GINT_STATIC_GRAY vrams[1] = internal_vrams[0]; vrams[2] = internal_vrams[1]; vrams[3] = internal_vrams[2]; - +#else + vrams[1] = NULL; + vrams[2] = NULL; + vrams[3] = NULL; +#endif delays[0] = 912; delays[1] = 1343; } +/* gray_quit() -- Free the gray engine's heap-allocated video rams */ +__attribute__((destructor)) static void gray_quit(void) +{ +#ifndef GINT_STATIC_GRAY + free(vrams[1]); + free(vrams[2]); + free(vrams[3]); +#endif +} + //--- @@ -71,6 +83,14 @@ __attribute__((constructor)) void gray_init(void) */ void gray_start(void) { +#ifndef GINT_STATIC_GRAY + for(int i = 0; i < 4; i++) + { + if(!vrams[i]) vrams[i] = malloc(1024); + /* Don't continue if any of the buffer is missing */ + if(!vrams[i]) return; + } +#endif if(runs) return; gray_timer = htimer_setup(timer_gray, delays[0], timer_Po_64, 0); diff --git a/src/init/crt0.c b/src/init/crt0.c new file mode 100644 index 0000000..92ccc69 --- /dev/null +++ b/src/init/crt0.c @@ -0,0 +1,306 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include + +/* We need some more functionality to generate these */ +#ifdef GINT_STARTUP_LOG + #include + #include + #include + #include + #include + + #ifndef GINT_NO_SYSCALLS + #include + #endif +#endif + +int main(void); + +static void init(void); +static void fini(void); + +// Symbols provided by the linker script. +extern uint32_t + bdata, sdata, // Location of .data section + bbss, sbss, // Location of .bss section + bgint, egint, // Location of interrupt handler and gint data + bgbss, egbss, // Location of interrupt handler and gint data + gint_vbr, // VBR address + romdata, // ROM address of .data section contents + gint_data; // ROM address of .gint section contents +extern void + (*bctors)(void), (*ectors)(void), // Constructors + (*bdtors)(void), (*edtors)(void); // Destructors + +// This variable should be overwritten before being returned, so the default +// value doesn't matter much. +static int exit_code = EXIT_SUCCESS; +static jmp_buf env; + +// Exit handlers. +static void (*atexit_handlers[ATEXIT_MAX])(void); +static int atexit_index = 0; + +#include + +/* + start() + Program entry point. Loads the data section into the memory and invokes + main(). Also prepares the execution environment by initializing all the + modules. +*/ +__attribute__((section(".pretext.entry"))) int start(void) +{ + /* Configure the display output or we won't get anywhere - this is also + needed for the add-in even if the startup logs are disabled */ + display_useVRAM(display_getLocalVRAM()); + text_configure(NULL, color_black); + +#ifdef GINT_STARTUP_LOG + /* Start outputting information on the screen so that we can quickly + debug anything that would fail */ + uint32_t rom_size = ((uint32_t)&romdata - 0x00300000) >> 10; + uint32_t ram_size = ((uint32_t)&gint_data - (uint32_t)&romdata) + + ((uint32_t)&sbss); + uint32_t rram_size = ((uint32_t)&egbss - (uint32_t)&gint_vbr); + + dclear(); + init_stage("Startup"); + dupdate(); + + print(1, 1, init_version()); + print(1, 2, "ROM RAM RRAM"); + print(4, 3, "k c d"); + print_dec(1, 3, rom_size, 3); + print_dec(6, 3, ram_size, 4); + print_dec(11, 3, rram_size, 4); + print_dec(17, 3, &ectors - &bctors, 2); + print_dec(20, 3, &edtors - &bdtors, 2); + dupdate(); +#endif + + /* Determining the processor type */ + mpu_t mpu = getMPU(); + +#ifdef GINT_STARTUP_LOG + const char *mpu_names[] = { "SH7337", "SH7355", "SH7305", "SH7724" }; + if(mpu >= 1 && mpu <= 4) print(16, 2, mpu_names[mpu - 1]); + else print_dec(16, 2, mpu, 6); + dupdate(); +#endif + + /* Make sure the MPU is of a valid type */ + if(!mpu || mpu > 4) init_halt(); + +#ifdef GINT_STARTUP_LOG + init_stage(" MMU"); + dupdate(); +#endif + + /* Try to get the TLB filled by the system by accessing all the pages + while the system still answers TLB misses */ + mmu_pseudoTLBInit(); + +#ifdef GINT_STARTUP_LOG + /* Read the TLB and count how much memory has been mapped */ + /* TODO: Debug TLB at startup */ + print(1, 4, "MMU ROM:???k RAM:???k"); + + init_stage("Section"); + dupdate(); +#endif + + /* Copying the data section, then clearing the bss section. This + doesn't affect the previously used variables, which are stored in + another section for this purpose */ + volatile uint32_t *data = &bdata; + volatile uint32_t *data_end = (void *)&bdata + (int)&sdata; + volatile uint32_t *src = &romdata; + while(data < data_end) *data++ = *src++; + volatile uint32_t *bss = &bbss; + volatile uint32_t *bss_end = (void *)&bbss + (int)&sbss; + while(bss < bss_end) *bss++ = 0; + +#ifdef GINT_STARTUP_LOG + init_stage("Handler"); + dupdate(); +#endif + + /* Initialize gint and debug all the interrupt priorities */ + gint_init(mpu); + +#ifdef GINT_STARTUP_LOG + if(isSH3()) + { + print(1, 5, "ABCD"); + print_hex( 6, 5, INTC._7705.iprs.IPRA->word, 4); + print_hex(10, 5, INTC._7705.iprs.IPRB->word, 4); + print_hex(14, 5, INTC._7705.iprs.IPRC->word, 4); + print_hex(18, 5, INTC._7705.iprs.IPRD->word, 4); + print(1, 6, "EFGH"); + print_hex( 6, 6, INTC._7705.iprs.IPRE->word, 4); + print_hex(10, 6, INTC._7705.iprs.IPRF->word, 4); + print_hex(14, 6, INTC._7705.iprs.IPRG->word, 4); + print_hex(18, 6, INTC._7705.iprs.IPRH->word, 4); + } + else + { + print(1, 5, "ACFG"); + print_hex( 6, 5, INTC._7305.iprs->IPRA.word, 4); + print_hex(10, 5, INTC._7305.iprs->IPRC.word, 4); + print_hex(14, 5, INTC._7305.iprs->IPRF.word, 4); + print_hex(18, 5, INTC._7305.iprs->IPRG.word, 4); + print(1, 6, "HJKL"); + print_hex( 6, 6, INTC._7305.iprs->IPRH.word, 4); + print_hex(10, 6, INTC._7305.iprs->IPRJ.word, 4); + print_hex(14, 6, INTC._7305.iprs->IPRK.word, 4); + print_hex(18, 6, INTC._7305.iprs->IPRL.word, 4); + } +#endif + + /* Checking that at least the required interrupts are enabled */ + if(isSH3() + ? (!INTC._7705.iprs.IPRA->word) + : (!INTC._7305.iprs->IPRA.word || !INTC._7305.iprs->IPRK.word) + ) init_halt(); + +#ifdef GINT_STARTUP_LOG + init_stage(" Clocks"); + dupdate(); +#endif + + /* Measuring the clock speed */ + clock_measure(); + clock_measure_end(); + +#ifdef GINT_STARTUP_LOG + clock_config_t clock = clock_config(); + print(1, 7, "Clock I B P"); + print_dec( 8, 7, clock.Iphi_f / 1000000, 2); + print_dec(12, 7, clock.Bphi_f / 1000000, 2); + print_dec(16, 7, clock.Pphi_f / 1000000, 2); + + init_stage("Boot OK"); + dupdate(); + + /* Displaying some interrupt controller config */ + if(isSH3()) + { + print(1, 8, "INTC:0"); + print_hex(7, 8, INTC._7705.ICR1->word, 4); + } + else + { + print(1, 8, "INTC:"); + print_hex(6, 8, INTC._7305.ICR0->word, 4); + print_hex(10, 8, (INTC._7305.USERIMASK->lword >> 4) & 0xf, 1); + } + + #ifndef GINT_NO_SYSCALLS + /* Print the OS version */ + char version[11]; + __get_os_version(version); + version[2] = version[3]; + version[3] = version[4]; + version[4] = version[6]; + version[5] = version[7]; + version[6] = version[8]; + version[7] = version[9]; + version[8] = 0; + print(12, 8, "OS"); + print(14, 8, version); + #endif + + dupdate(); +#endif + + /* Call the global constructors */ + init(); + +#ifdef GINT_STARTUP_LOG + /* Keep this visible for a second or so */ + keyboard_interrupt(); + if(pollevent().type != event_none) getkey(); +#endif + + /* Otherwise, call main() and get the show on the road! */ + int x = setjmp(env); + if(!x) exit_code = main(); + + while(atexit_index > 0) (*atexit_handlers[--atexit_index])(); + fini(); + + gint_quit(); + + return exit_code; +} + +/* init() -- call the constructors */ +static void init(void) +{ + extern void + (*bctors)(void), + (*ectors)(void); + void (**func)(void) = &bctors; + + while(func < &ectors) (*(*func++))(); +} + +/* fini() -- call the destructors */ +static void fini(void) +{ + extern void + (*bdtors)(void), + (*edtors)(void); + void (**func)(void) = &bdtors; + + while(func < &edtors) (*(*func++))(); +} + + + +/* + abort() + Immediately ends the program without invoking the exit handlers. +*/ +void abort(void) +{ + exit_code = EXIT_FAILURE; + // Avoiding any exit handler call. + atexit_index = 0; + longjmp(env, 1); +} + +/* + exit() + Ends the program and returns the given exit code status. Calls exit + handlers before returning. + Usually exit() would ask the operating system to stop the process but + the fx-9860G executes only one program at a time and calls it as a + function. Reaching the program end point thus efficiently exits. +*/ +void exit(int status) +{ + exit_code = status; + longjmp(env, 1); +} + +/* + atexit() + Registers a function to be called at normal program termination. +*/ +int atexit(void (*function)(void)) +{ + if(atexit_index >= ATEXIT_MAX) return 1; + + atexit_handlers[atexit_index++] = function; + return 0; +} diff --git a/src/init/util.c b/src/init/util.c new file mode 100644 index 0000000..e930875 --- /dev/null +++ b/src/init/util.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include + +/* qdiv10() -- quickly divide by 10 */ +struct qdiv qdiv10(uint32_t n) +{ + uint32_t magic10 = 0x1999999a; + struct qdiv result; + + __asm__( + "dmuls.l %1, %2 \n\t" + "sts mach, %0 " + : "=r"(result.q) + : "r"(n), "r"(magic10) + : "macl", "mach" + ); + + result.r = n - 10 * result.q; + return result; +} + +/* init_version() -- get a version string */ +const char *init_version(void) +{ + static char data[14]; + uint32_t s = (uint32_t)&GINT_VERSION; + + /* Force the string constant to reside in ROM because we haven't + initialized the data section yet */ + memcpy(data, "gint #0.0-000", 14); + + /* Quickly get the three digits of the build number */ + struct qdiv x = qdiv10(s & 0xffff); + struct qdiv y = qdiv10(x.q); + + data[5] = (s & 0xff000000) >> 24; + data[6] += ((s & 0x00f00000) >> 20); + data[8] += ((s & 0x000f0000) >> 16); + data[10] += y.q; + data[11] += y.r; + data[12] += x.r; + + return data; +} + +/* init_stage() -- change the current init stage */ +void init_stage(const char *name) +{ + drect(85, 0, 127, 7, color_white); + print(15, 1, name); +} + +/* init_halt() -- halt the program */ +void init_halt(void) +{ + while(1) sleep(); +} + +/* print_dec() -- print a number in base 10 */ +void print_dec(int x, int y, int n, int digits) +{ + char str[20]; + str[digits] = 0; + + while(--digits >= 0) + { + struct qdiv d = qdiv10(n); + str[digits] = '0' + d.r; + n = d.q; + } + + print(x, y, str); +} + +/* print_hex() -- print a number in base 16 */ +void print_hex(int x, int y, uint32_t n, int digits) +{ + char str[20]; + str[digits] = 0; + + while(--digits >= 0) + { + str[digits] = (n & 0xf) + '0' + 39 * ((n & 0xf) > 9); + n >>= 4; + } + + print(x, y, str); +} diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c index 1fc3eb1..f75d086 100644 --- a/src/keyboard/getkey.c +++ b/src/keyboard/getkey.c @@ -16,7 +16,9 @@ int getkey(void) getkey_shift_modifier | getkey_alpha_modifier | getkey_manage_backlight | +#ifndef GINT_NO_SYSCALLS getkey_task_switch | +#endif getkey_repeat_arrow_keys, 0 @@ -105,12 +107,14 @@ int getkey_opt(getkey_option_t options, int delay_ms) break; case event_key_release: +#ifndef GINT_NO_SYSCALLS if((options & getkey_task_switch) && event.key.code == KEY_MENU && !modifier) { gint_switch(); continue; } +#endif if((int)event.key.code != last_key) break; last_key = KEY_NONE; diff --git a/src/keyboard/keyboard_core.c b/src/keyboard/keyboard_core.c index 6e956f4..530b5fc 100644 --- a/src/keyboard/keyboard_core.c +++ b/src/keyboard/keyboard_core.c @@ -34,45 +34,6 @@ timer_t *vtimer = NULL; // Interrupt management. //--- -static inline void push_press(int keycode) -{ - uint32_t id = key_id(keycode); - - event_t event = { - .type = event_key_press, - .key.code = keycode, - .key.id = id, - .key.character = key_char(keycode), - }; - event_push(event); -} - -static inline void push_repeat(int keycode) -{ - uint32_t id = key_id(keycode); - - event_t event = { - .type = event_key_repeat, - .key.code = keycode, - .key.id = id, - .key.character = key_char(keycode), - }; - event_push(event); -} - -static inline void push_release(int keycode) -{ - uint32_t id = key_id(keycode); - - event_t event = { - .type = event_key_release, - .key.code = keycode, - .key.id = id, - .key.character = key_char(keycode), - }; - event_push(event); -} - /* keyboard_interrupt() Callback function for keyboard update; called by the timer manager when diff --git a/src/keyboard/multigetkey.c b/src/keyboard/multigetkey.c deleted file mode 100644 index 74f9b6c..0000000 --- a/src/keyboard/multigetkey.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include - -/* - multigetkey() - Listens the keyboard for simultaneous key hits. Uses the same options - as getkey_opt(). - multigetkey() fills the 'keys' array with 'count' key codes, adding - KEY_NOKEY if less than 'count' keys are pressed. - Be aware that rectangle and column effects can make multigetkey() read - unpressed keys as pressed (see documentation for more information). - Setting count = 3 is generally safe. - The function returns after 'max_cycles' if no key is pressed. -*/ -static void multigetkey_wait(int *cycles) -{ - while(!interrupt_flag) sleep(); - interrupt_flag = 0; - - if(*cycles > 0) (*cycles)--; -} -void multigetkey(int *keys, int count, int cycles) -{ - event_t event; - int number = 0; - - if(count <= 0) return; - if(cycles <= 0) cycles = -1; - - while(cycles != 0) - { - number = getPressedKeys(keyboard_state, keys, count); - - // We want to update the last key data when multigetkey() - // returns a single key, because getkey() could be called a - // short time after we return, and send a new event for this - // key. - if(number == 1) - { - last_key = keys[0]; - last_repeats = 0; - last_time = 0; - } - - if(number) break; - multigetkey_wait(&cycles); - } - - do event = pollevent(); - while(event.type != event_none); - - return; -} diff --git a/src/stdlib/free.c b/src/stdlib/free.c index b51d876..b967fda 100644 --- a/src/stdlib/free.c +++ b/src/stdlib/free.c @@ -3,7 +3,7 @@ #pragma GCC diagnostic ignored "-Wunused-parameter" #ifndef GINT_NO_SYSCALLS -void __free(void *ptr); +#include #endif /* @@ -12,6 +12,9 @@ void __free(void *ptr); */ void free(void *ptr) { + // Just to be sure. + if(!ptr) return; + #ifndef GINT_NO_SYSCALLS __free(ptr); #endif diff --git a/src/stdlib/malloc.c b/src/stdlib/malloc.c index 92366bc..3b7724a 100644 --- a/src/stdlib/malloc.c +++ b/src/stdlib/malloc.c @@ -7,8 +7,8 @@ */ #ifndef GINT_NO_SYSCALLS +#include -void *__malloc(size_t size); void *malloc(size_t size) { return __malloc(size); diff --git a/src/stdlib/realloc.c b/src/stdlib/realloc.c index b98530e..7c30537 100644 --- a/src/stdlib/realloc.c +++ b/src/stdlib/realloc.c @@ -6,8 +6,8 @@ */ #ifndef GINT_NO_SYSCALLS +#include -void *__realloc(void *ptr, size_t size); void *realloc(void *ptr, size_t size) { return __realloc(ptr, size); diff --git a/src/tales/tales_internals.c b/src/tales/tales_internals.c index 889fd7a..3607d0d 100644 --- a/src/tales/tales_internals.c +++ b/src/tales/tales_internals.c @@ -4,17 +4,10 @@ #include #include -font_t *font = NULL; -color_t operator; - -/* - tales_init() - Configures tales with the default font (which is part of gint). -*/ -void tales_init(void) -{ - text_configure(NULL, color_black); -} +/* Put these in gint's uninitialized bss section so that text rendering can be + used before the library is fully initialized */ +__attribute__((section(".gint.bss"))) font_t *font = NULL; +__attribute__((section(".gint.bss"))) color_t operator; /* getCharacterIndex() diff --git a/version b/version index 18bb01c..96648ab 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-410 +beta-0.9-566 From 89d0c4672752cd86fa57c50430e9486b0bd7660b Mon Sep 17 00:00:00 2001 From: lephe Date: Sat, 8 Jul 2017 17:22:42 +0200 Subject: [PATCH 17/24] Small Makefile adjustments, preparing fusion with fxSDK --- Makefile | 68 +++++++++++++++++++++---------------- TODO | 1 - include/internals/init.h | 8 ----- include/math.h | 41 ++++++++++++++++++++++ src/init/util.c | 25 +++----------- src/math/math_qdiv.c | 23 +++++++++++++ src/tales/tales_internals.c | 2 +- version | 2 +- 8 files changed, 108 insertions(+), 62 deletions(-) create mode 100644 include/math.h create mode 100644 src/math/math_qdiv.c diff --git a/Makefile b/Makefile index 5924ace..d3804c7 100755 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ include Makefile.cfg # Modules modules-gint = bopti clock core display events gray init keyboard mmu rtc \ screen tales timer -modules-libc = ctype setjmp stdio stdlib string time +modules-libc = ctype math setjmp stdio stdlib string time # Targets target-lib = libgint.a @@ -44,8 +44,8 @@ demo-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ -Wall -Wextra # Specific objects -obj-lib-spec = build/display_font_system.bmp.o obj-std-spec = +obj-lib-spec = build/display_font_system.bmp.o # Configuration files config = gcc.cfg @@ -101,7 +101,7 @@ hdr-dep = $(wildcard include/*.h include/*/*.h) # $3 dependencies define rule-c-source build/$1_$2.o: src/$1/$2 $3 $(config) - $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $$<\n') + $(if $(VERBOSE),,@ printf '\e[34;1m gcc\e[0m $$<\n') $(if $(VERBOSE),,@) $(cc) -c $$< -o $$@ $(lib-cflags) endef @@ -110,7 +110,7 @@ endef # $2 filename define rule-asm-source build/$1_$2.o: src/$1/$2 $(config) - $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m as $$<\n') + $(if $(VERBOSE),,@ printf '\e[34;1m as\e[0m $$<\n') $(if $(VERBOSE),,@) $(as) -c $$< -o $$@ endef @@ -135,7 +135,6 @@ version_symbol = $(shell printf '0x%02x%01x%01x%04x' "'$(version_letter)'" \ # Tell the linker to define the version symbol. demo-ldflags += -Wl,--defsym,_GINT_VERSION=$(version_symbol) -debug-ldflags += -Wl,--defsym,_GINT_VERSION=$(version_symbol) @@ -150,37 +149,37 @@ all-lib: $(config) $(target-std) $(target-lib) all: $(config) $(target-std) $(target-lib) $(target-g1a) build: - $(if $(VERBOSE),,@ printf '\e[35;1mdir \u00bb\e[0m mkdir $@\n') + $(if $(VERBOSE),,@ printf '\e[35;1m mkdir\e[0m $@\n') $(if $(VERBOSE),,@) mkdir -p $@ -version: $(obj-std) $(obj-lib) +version: src include @ echo '$(version_type)-$(version_major).$(version_minor)-$(version_build_n)' > $@ $(obj-std) $(obj-lib) $(demo-obj): | build $(target-std): $(obj-std) version - $(if $(VERBOSE),,@ printf '\e[35;1mlib \u00bb\e[0m ar $@\n') + $(if $(VERBOSE),,@ printf '\e[35;1m ar\e[0m ar $@\n') $(if $(VERBOSE),,@) $(ar) rcs $@ $(obj-std) - @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built libc (' + @ printf '\n\e[32;1m\u00bb\e[0m Succesfully built libc (' @ printf $$(stat -c %s $@) @ printf ' bytes)\n\n' $(target-lib): $(config) $(target-std) $(obj-lib) version - $(if $(VERBOSE),,@ printf '\e[35;1mlib \u00bb\e[0m ar $@\n') + $(if $(VERBOSE),,@ printf '\e[35;1m ar\e[0m $@\n') $(if $(VERBOSE),,@) $(ar) rcs $@ $(obj-lib) - @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built libgint (' + @ printf '\n\e[32;1m\u00bb\e[0m Succesfully built libgint (' @ printf $$(stat -c %s $@) @ printf ' bytes)\n\n' $(target-g1a): $(config) $(target-std) $(target-lib) $(demo-obj) - $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m ld -o $(demo-elf)\n') + $(if $(VERBOSE),,@ printf '\e[35;1m ld\e[0m $(demo-elf)\n') $(if $(VERBOSE),,@) $(cc) -o $(demo-elf) $(demo-obj) $(demo-ldflags) - $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m objcopy -o $(demo-bin)\n') + $(if $(VERBOSE),,@ printf '\e[35;1mobjcopy\e[0m $(demo-bin)\n') $(if $(VERBOSE),,@) $(ob) -R .comment -R .bss -O binary $(demo-elf) $(demo-bin) - $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m g1a-wrapper -o $@\n') + $(if $(VERBOSE),,@ printf '\e[35;1m fxg1a\e[0m $@\n') $(if $(VERBOSE),,@) $(wr) $(demo-bin) -o $@ -i $(demo-icon) - @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built demo application (' + @ printf '\n\e[32;1m\u00bb\e[0m Succesfully built demo application (' @ printf $$(stat -c %s $@) @ printf ' bytes)\n\n' @@ -200,26 +199,26 @@ $(foreach mod,$(modules), \ # why for now. build/display_font_%.bmp.o: src/display/font_%.bmp - $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') - $(if $(VERBOSE),,@) fxconv $< -o $@ --font -n $(<:src/display/font_%.bmp=gint_font_%) + $(if $(VERBOSE),,@ printf '\e[30;1m fxconv\e[0m -font $<\n') + $(if $(VERBOSE),,@) fxconv -font $< -o $@ -font -n $(<:src/display/font_%.bmp=gint_font_%) # Demo application build/demo_%.c.o: demo/%.c $(hdr-dep) $(demo-dep) $(config) - $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $<\n') + $(if $(VERBOSE),,@ printf '\e[34;1m gcc\e[0m $<\n') $(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(demo-cflags) build/demo_%.s.o: demo/%.s $(config) - $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m as $<\n') + $(if $(VERBOSE),,@ printf '\e[34;1m as\e[0m $<\n') $(if $(VERBOSE),,@) $(as) -c $< -o $@ build/demo_font_%.bmp.o: demo/resources/font_%.bmp - $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') - $(if $(VERBOSE),,@) fxconv $< -o $@ --font -n $(patsubst demo/resources/%.bmp,res_%,$<) + $(if $(VERBOSE),,@ printf '\e[30;1m fxconv\e[0m -font $<\n') + $(if $(VERBOSE),,@) fxconv -font $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) build/demo_%.bmp.o: demo/resources/%.bmp - $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') - $(if $(VERBOSE),,@) fxconv $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) + $(if $(VERBOSE),,@ printf '\e[30;1m fxconv\e[0m -image $<\n') + $(if $(VERBOSE),,@) fxconv -image $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) @@ -237,15 +236,24 @@ mrproper: clean distclean: mrproper install: $(target-std) $(target-lib) - mkdir -p $(folder) - install -m 644 $^ $(folder) - install -m 644 -T demo/gintdemo.ld $(folder)/linker.ld - mkdir -p $(folder)/gint - install -m 644 include/*.h $(folder)/gint + $(if $(VERBOSE),,@ printf '\e[35;1m mkdir\e[0m $(folder)\n') + $(if $(VERBOSE),,@) mkdir -p $(folder) + + $(if $(VERBOSE),,@ printf '\e[33;1minstall\e[0m 644 $^\n') + $(if $(VERBOSE),,@) install -m 644 $^ $(folder) + $(if $(VERBOSE),,@ printf '\e[33;1minstall\e[0m 644 demo/gintdemo.ld\n') + $(if $(VERBOSE),,@) install -m 644 -T demo/gintdemo.ld $(folder)/linker.ld + + $(if $(VERBOSE),,@ printf '\e[35;1m mkdir\e[0m $(folder)/gint/modules\n') + $(if $(VERBOSE),,@) mkdir -p $(folder)/gint/modules + + $(if $(VERBOSE),,@ printf '\e[33;1minstall\e[0m 644 include/**.h\n') + $(if $(VERBOSE),,@) install -m 644 include/*.h $(folder)/gint + $(if $(VERBOSE),,@) install -m 644 include/modules/*.h $(folder)/gint/modules ifdef config_ext - install -m 644 include/extended/*.h $(folder)/gint + $(if $(VERBOSE),,@) install -m 644 include/extended/*.h $(folder)/gint endif - @ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n' + @ printf "\n\033[32;1m\u00bb\033[0m Successfully installed gint\n\n" install-demo: all p7 send -f $(target-g1a) diff --git a/TODO b/TODO index 7a2c0ff..ee2bab3 100644 --- a/TODO +++ b/TODO @@ -5,7 +5,6 @@ Bugs to fix: - Ensure heap data is freed when a task-switch results in leaving the app Things to do before 1.0: -- init: Move qdiv10() somewhere else - bopti: Test partial transparency - demo: Try 284x124 at (-60, -28) (all disadvantages) - project: Check size of *all* library structures diff --git a/include/internals/init.h b/include/internals/init.h index 66b3b29..f6b7f47 100644 --- a/include/internals/init.h +++ b/include/internals/init.h @@ -8,14 +8,6 @@ #include -struct qdiv -{ - uint32_t q, r; -}; - -/* qdiv10() -- quickly divide by 10 */ -struct qdiv qdiv10(uint32_t n); - /* init_version() -- get a version string */ const char *init_version(void); diff --git a/include/math.h b/include/math.h new file mode 100644 index 0000000..1bcbbcb --- /dev/null +++ b/include/math.h @@ -0,0 +1,41 @@ +//--- +// +// gint libc module: math +// +// Provides mathematical functions as well as a few useful extensions. +// +//--- + +#ifndef _MATH_H +#define _MATH_H + +#include + +//--- +// Function extensions +//--- + +/* + qdiv() + Quickly divides by predefined integers using a 64-bit multiplication + technique. These functions should be ~10 times faster than dividing + using opeator "/". +*/ + +typedef struct qdiv_t +{ + uint32_t q; /* Quotient */ + uint32_t r; /* Remainer */ + +} __attribute__((packed, aligned(4))) qdiv_t; + +qdiv_t qdiv(uint32_t n, uint32_t divider, uint32_t reciprocal); + +/* Predefined magic numbers */ +#define qdiv_3(n) qdiv(n, 3, 0x55555556) +#define qdiv_5(n) qdiv(n, 5, 0x33333334) +#define qdiv_10(n) qdiv(n, 10, 0x1999999a) +#define qdiv_100(n) qdiv(n, 100, 0x028f5c29) +#define qdiv_1000(n) qdiv(n, 1000, 0x00418938) + +#endif // _MATH_H diff --git a/src/init/util.c b/src/init/util.c index e930875..bb2ad40 100644 --- a/src/init/util.c +++ b/src/init/util.c @@ -2,24 +2,7 @@ #include #include #include - -/* qdiv10() -- quickly divide by 10 */ -struct qdiv qdiv10(uint32_t n) -{ - uint32_t magic10 = 0x1999999a; - struct qdiv result; - - __asm__( - "dmuls.l %1, %2 \n\t" - "sts mach, %0 " - : "=r"(result.q) - : "r"(n), "r"(magic10) - : "macl", "mach" - ); - - result.r = n - 10 * result.q; - return result; -} +#include /* init_version() -- get a version string */ const char *init_version(void) @@ -32,8 +15,8 @@ const char *init_version(void) memcpy(data, "gint #0.0-000", 14); /* Quickly get the three digits of the build number */ - struct qdiv x = qdiv10(s & 0xffff); - struct qdiv y = qdiv10(x.q); + qdiv_t x = qdiv_10(s & 0xffff); + qdiv_t y = qdiv_10(x.q); data[5] = (s & 0xff000000) >> 24; data[6] += ((s & 0x00f00000) >> 20); @@ -66,7 +49,7 @@ void print_dec(int x, int y, int n, int digits) while(--digits >= 0) { - struct qdiv d = qdiv10(n); + qdiv_t d = qdiv_10(n); str[digits] = '0' + d.r; n = d.q; } diff --git a/src/math/math_qdiv.c b/src/math/math_qdiv.c new file mode 100644 index 0000000..be7b59c --- /dev/null +++ b/src/math/math_qdiv.c @@ -0,0 +1,23 @@ +#include + +/* + Quickly divides by predefined integers using a 64-bit multiplication + technique. These functions should be ~10 times faster than dividing + using opeator "/". +*/ +__attribute__((always_inline)) +inline qdiv_t qdiv(uint32_t n, uint32_t divider, uint32_t magic) +{ + qdiv_t result; + + __asm__( + "dmuls.l %1, %2 \n\t" + "sts mach, %0 " + : "=r"(result.q) + : "r"(n), "r"(magic) + : "macl", "mach" + ); + + result.r = n - divider * result.q; + return result; +} diff --git a/src/tales/tales_internals.c b/src/tales/tales_internals.c index 3607d0d..f1d6a99 100644 --- a/src/tales/tales_internals.c +++ b/src/tales/tales_internals.c @@ -202,7 +202,7 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph) */ void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS)) { - if(!font) return; + if(!font || font->magic != 0x01) return; // Operator data, and number of available bits in the operators (which // is the same for all operators, since they are treated equally). diff --git a/version b/version index 96648ab..8cc1a27 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-566 +beta-0.9-581 From 54fe9e7ccdf94641856b7272f5f3691ff4de9b50 Mon Sep 17 00:00:00 2001 From: lephe Date: Tue, 11 Jul 2017 13:51:56 +0200 Subject: [PATCH 18/24] Hopefully the last adjustments before the first release. --- TODO | 7 +- configure | 6 +- demo/asm_syscalls.s | 56 ------- demo/gintdemo.c | 267 +++++++++------------------------- demo/perf_bopti.c | 179 +++++++++++++++++++++++ demo/resources/opt_bitmap.bmp | Bin 3194 -> 3194 bytes demo/resources/opt_gray.bmp | Bin 3194 -> 3194 bytes demo/resources/opt_menu.bmp | Bin 3194 -> 3194 bytes demo/resources/screen.bmp | Bin 32906 -> 13570 bytes demo/resources/town.bmp | Bin 0 -> 105770 bytes demo/test_bopti.c | 61 +++++--- demo/test_gray.c | 49 ++++--- gscreen.bmp | Bin 0 -> 1150 bytes gscreend.bmp | Bin 0 -> 1150 bytes gscreenl.bmp | Bin 0 -> 1150 bytes include/bfile.h | 74 ++++++++++ include/internals/syscalls.h | 3 + include/keyboard.h | 3 + include/modules/interrupts.h | 6 +- src/core/syscalls.s | 64 ++++++++ src/gray/grect.c | 2 +- src/init/crt0.c | 4 +- src/keyboard/getkey.c | 12 +- version | 2 +- 24 files changed, 480 insertions(+), 315 deletions(-) delete mode 100644 demo/asm_syscalls.s create mode 100644 demo/perf_bopti.c create mode 100644 demo/resources/town.bmp create mode 100644 gscreen.bmp create mode 100644 gscreend.bmp create mode 100644 gscreenl.bmp create mode 100644 include/bfile.h diff --git a/TODO b/TODO index ee2bab3..47e0637 100644 --- a/TODO +++ b/TODO @@ -14,11 +14,14 @@ Things to do before 1.0: Things to do later: - bopti: Implement blending modes for monochrome bitmaps +- clock: Only measure if requires as option, otherwise trust {FTune} - clock: Handle overclock (relaunch clocks when overclocking) +- clock: Split code into several files, change clock_config_t type - core: Change interrupt priority using the gint API - core: Register more interrupts (and understand their parameters) - core: Remove redundant code linked to environment saves - core: Review interrupt system (again) - this one is too slow +- display: Try to make this module lighter (lots of code in text section) - errno: Introduce errno and use it more or less everywhere - esper: Cleaner playback, synthesizing - events: Allow customization of keyboard event system (option to return @@ -26,10 +29,12 @@ Things to do later: - events: Generate keyboard events on-the-fly by reading state arrays, | allowing both a faster interrupt and avoiding supressing other | events inside getkey() and multigetkey() +- gray: Same as display, it's quite heavy - serial: Implement a driver -- stdio: More serious formatted printing functions +- stdio: More serious formatted printing functions and headers - string: Use cmp/str to implement memchr() (assembler examples) - string: Do some tests for memcmp() and memcpy() +- string: Lighter functions? - usb: Implement a driver Things to investigate: diff --git a/configure b/configure index 83c842f..bb6ae65 100755 --- a/configure +++ b/configure @@ -52,11 +52,11 @@ Options that affect the behavior of the library: use this option when using both the gray engine and --no-syscalls. Options that customize size limits: - $Cr--atexit-max$C0=$Cy$Cg [default:$Cp 16$Cg]$C0 + $Cr--atexit-max=$Cy$Cg [default:$Cp 16$Cg]$C0 Number of exit handlers that can be registered by atexit(). - $Cr--timer-slots$C0=$Cy$Cg [default:$Cp 16$Cg]$C0 + $Cr--timer-slots=$Cy$Cg [default:$Cp 16$Cg]$C0 Number of virtual timers that may be registered at the same time. - $Cr--events-queue-size$C0=$Cy$Cg [default:$Cp 64$Cg]$C0 + $Cr--events-queue-size=$Cy$Cg [default:$Cp 64$Cg]$C0 Number of events simultaneously stored in the event queue. EOF diff --git a/demo/asm_syscalls.s b/demo/asm_syscalls.s deleted file mode 100644 index 69b4f10..0000000 --- a/demo/asm_syscalls.s +++ /dev/null @@ -1,56 +0,0 @@ - - # int BFile_Remove(const uint16_t *file) - .global _BFile_Remove - -_BFile_Remove: - mov.l 1f, r0 - mov.l 2f, r1 - jmp @r1 - mov #0, r5 -1: .long 0x0439 - - # int BFile_Create(const uint16_t *file, enum { file = 1, folder = 5 }, - # int *size) - .global _BFile_Create - -_BFile_Create: - mov.l 1f, r0 - mov.l 2f, r1 - jmp @r1 - nop -1: .long 0x0434 - - # int BFile_Open(const uint16_t *file, int mode) - .global _BFile_Open - -_BFile_Open: - mov.l 1f, r0 - mov.l 2f, r1 - jmp @r1 - mov #0, r6 -1: .long 0x042c - - # int BFile_Close(int handle) - .global _BFile_Close - -_BFile_Close: - mov.l 1f, r0 - mov.l 2f, r1 - jmp @r1 - nop -1: .long 0x042d - - # int BFile_Write(int handle, void *ram_buffer, int even_size) - .global _BFile_Write - -_BFile_Write: - mov.l 1f, r0 - mov.l 2f, r1 - jmp @r1 - nop -1: .long 0x0435 - - .align 4 - -# Syscall branch address -2: .long 0x80010070 diff --git a/demo/gintdemo.c b/demo/gintdemo.c index 4655bf6..16ffaf5 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -54,154 +54,6 @@ void printf_test(void) } */ -/* -static const unsigned char screen[1024] = { -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 7, 159, 0, 0, 1, 192, 0, 0, 0, 0, 0, 121, 240, 0, 0, 0, -31, 191, 192, 0, 3, 224, 27, 216, 0, 0, 1, 251, 252, 0, 0, 0, 57, 247, 222, -30, 7, 240, 36, 36, 62, 25, 131, 159, 24, 255, 129, 224, 0, 227, 142, 126, 1, -192, 45, 172, 127, 127, 192, 14, 1, 255, 199, 224, 0, 227, 140, 240, 1, 192, -26, 88, 115, 127, 224, 14, 57, 221, 207, 0, 0, 227, 13, 192, 1, 192, 34, 68, -120, 30, 0, 14, 25, 156, 220, 0, 0, 227, 253, 252, 1, 192, 36, 36, 126, 28, -0, 14, 219, 156, 223, 192, 0, 227, 253, 252, 1, 192, 36, 36, 31, 12, 0, 46, -27, 140, 223, 192, 0, 227, 141, 193, 193, 192, 40, 20, 7, 140, 0, 206, 25, 140, -220, 28, 0, 227, 140, 225, 129, 199, 24, 24, 99, 156, 1, 14, 25, 204, 206, 24, -0, 227, 142, 127, 1, 195, 39, 228, 255, 156, 2, 14, 24, 237, 199, 240, 1, 247, -222, 62, 1, 198, 44, 44, 223, 30, 2, 31, 28, 237, 131, 224, 1, 224, 0, 0, 3, -254, 27, 216, 0, 0, 4, 30, 0, 0, 0, 0, 3, 192, 0, 0, 7, 252, 0, 0, 0, 0, 4, -60, 1, 249, 240, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 4, 0, 97, 240, 56, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 224, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, 47, 192, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 32, 255, 128, 63, 128, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 32, 255, 0, 48, 78, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 15, 176, 255, 0, 112, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 8, 56, 255, 0, -96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 8, 60, 255, 0, 224, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 130, 56, 126, 255, 3, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 192, -62, 255, 15, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 14, 191, 255, 192, 0, -0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 6, 129, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 6, 0, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 7, 128, 63, 192, -0, 0, 96, 1, 224, 1, 0, 0, 0, 2, 0, 0, 7, 0, 31, 192, 0, 0, 95, 1, 11, 68, 88, -0, 0, 4, 0, 0, 7, 128, 31, 192, 0, 1, 192, 129, 204, 85, 100, 0, 0, 8, 0, 0, -15, 128, 63, 224, 0, 0, 95, 1, 8, 85, 68, 0, 1, 144, 0, 0, 31, 128, 143, 224, -64, 0, 96, 1, 232, 41, 68, 0, 2, 96, 0, 31, 255, 129, 7, 248, 96, 0, 0, 0, 0, -0, 0, 0, 4, 0, 0, 96, 254, 129, 7, 254, 96, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 128, -254, 131, 135, 255, 224, 0, 0, 1, 192, 64, 16, 0, 8, 0, 7, 0, 254, 131, 255, -63, 224, 0, 0, 1, 38, 113, 208, 0, 8, 0, 13, 0, 222, 147, 254, 31, 224, 0, 0, -1, 41, 74, 80, 0, 8, 0, 25, 0, 222, 67, 254, 31, 160, 0, 0, 1, 41, 74, 80, 0, -12, 0, 49, 0, 222, 19, 254, 62, 48, 0, 0, 1, 198, 113, 208, 0, 2, 0, 32, 128, -222, 195, 255, 252, 56, 0, 0, 0, 0, 0, 0, 0, 2, 0, 124, 64, 220, 151, 135, 248, -127, 0, 0, 0, 0, 0, 0, 0, 2, 0, 66, 32, 221, 223, 7, 240, 255, 0, 0, 0, 0, 0, -0, 0, 2, 0, 129, 23, 93, 159, 15, 241, 131, 0, 0, 0, 0, 0, 0, 0, 4, 0, 128, -136, 217, 95, 3, 226, 9, 0, 0, 1, 240, 0, 0, 0, 4, 0, 128, 72, 89, 95, 129, -228, 18, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 72, 73, 127, 128, 224, 36, 0, 0, 0, 0, -0, 0, 0, 28, 1, 0, 76, 129, 127, 192, 96, 8, 0, 0, 0, 0, 0, 0, 0, 16, 1, 0, -231, 203, 124, 96, 64, 0, 0, 0, 0, 0, 0, 0, 0, 16, 1, 1, 28, 123, 240, 12, 64, -1, 0, 0, 0, 0, 0, 0, 0, 16, 1, 2, 28, 143, 128, 15, 192, 7, 0, 0, 0, 0, 0, 0, -0, 16, 1, 4, 17, 143, 24, 15, 192, 14, 0, 0, 0, 0, 0, 0, 0, 28, 1, 4, 1, 135, -24, 31, 192, 24, 0, 0, 0, 0, 0, 0, 0, 18, 1, 62, 1, 135, 248, 63, 224, 192, -0, 0, 0, 0, 0, 0, 0, 35, 1, 195, 1, 135, 128, 254, 126, 1, 0, 0, 0, 0, 0, 0, -0, 35, 193, 131, 195, 135, 255, 248, 112, 1, 0, 0, 0, 0, 0, 0, 0, 67, 241, 131, -14, 207, 255, 192, 224, 3, 0, 0, 0, 0, 0, 0, 3, 67, 15, 143, 56, 255, 7, 1, -224, 7, 0, 0, 0, 0, 0, 0, 28, 130, 7, 255, 112, 204, 7, 131, 224, 31, 0, 0, -0, 0, 0, 0, 32, 134, 30, 29, 120, 156, 7, 255, 224, 127, 0, 0, 0, 0, 0, 63, -197, 206, 60, 56, 192, 248, 15, 255, 248, 255, 0, 0, 0, 0, 0, 120, 5, 227, 248, -56, 195, 248, 127, 191, 254, 63, 0, 0, 0, 0, 7, 254, 255, 193, 255, 15, 193, -255, 15, 31, 252, 31 }; - -void ML_bmp_or_cl(const unsigned char *bmp, int x, int y, int width, int height) -{ - unsigned short line; - char shift, *screen, *p; - int i, j, real_width, begin_x, end_x, begin_y, end_y; - char bool1=1, bool2=1, bool3; - if(!bmp || x<1-width || x>127 || y<1-height || y>63 || height<1 || width<1) return; - p = (char*)&line; - real_width = (width-1>>3<<3)+8; - if(y < 0) begin_y = -y; - else begin_y = 0; - if(y+height > 64) end_y = 64-y; - else end_y = height; - shift = 8-(x&7); - if(x<0) - { - begin_x = -x>>3; - if(shift != 8) bool1 = 0; - } else begin_x = 0; - if(x+real_width > 128) end_x = 15-(x>>3), bool2 = 0; - else end_x = real_width-1>>3; - bool3 = (end_x == real_width-1>>3); - screen = display_getCurrentVRAM()+(y+begin_y<<4)+(x>>3); - - for(i=begin_y ; i>3)+begin_x] << shift; - if(bool1) screen[begin_x] |= *p; - if(shift!=8) screen[begin_x+1] |= *(p+1); - for(j=begin_x+1 ; j>3)+j] << shift; - screen[j] |= *p; - if(shift!=8) screen[j+1] |= *(p+1); - } - } - line = bmp[i*(real_width>>3)+end_x]; - if(bool3) line &= -1< -void debug(void) -{ - extern Image res_screen_start; - struct mod_tmu *timer; - int time1, time2; - int i; - - timer_get(TIMER_USER, &timer, NULL); - - dclear(); - ML_bmp_or_cl(screen, 1, 1, 128, 64); - dupdate(); - getkey(); - - dclear(); - dimage(&res_screen_start, 1, 1); - dupdate(); - getkey(); - - dclear(); - dtext("ML...", 2, 2); - dupdate(); - - timer_start2(TIMER_USER, 0x0fffffff, TIMER_Po_4, NULL, 0); - for(i = 0; i < 1000; i++) ML_bmp_or_cl(screen, 1, 1, 128, 64); - time1 = timer->TCNT; - timer_stop(TIMER_USER); - time1 = 0x0fffffff - time1; - - dclear(); - dtext("gint...", 2, 2); - dupdate(); - - timer_start2(TIMER_USER, 0x0fffffff, TIMER_Po_4, NULL, 0); - for(i = 0; i < 1000; i++) dimage(&res_screen_start, 1, 1); - time2 = timer->TCNT; - timer_stop(TIMER_USER); - time2 = 0x0fffffff - time2; - - dclear(); - print_hex(time1, 2, 2); - print_hex(time2, 2, 9); - dupdate(); - while(getkey() != KEY_EXIT); -} -*/ - /* tlb_debug() Displays the TLB contents and some information. Only available for @@ -311,15 +163,13 @@ void main_menu(int *category, int *app) "Clocks and timers", NULL }; +/* const char *list_perfs[] = { "Image rendering", "Text rendering", NULL }; - const char *list_debug[] = { - "View TLB (SH3 only)", - NULL - }; +*/ const char **list = NULL; int list_len = 0; @@ -367,22 +217,17 @@ void main_menu(int *category, int *app) locate(1, 1, "Test list"); list = list_tests; break; - +/* case 2: locate(1, 1, "Performance"); list = list_perfs; break; - - case 3: - locate(1, 1, "Debug"); - list = list_debug; - break; - +*/ default: print(1, 1, "Tab %d", tab); break; } - dimage(0, 56, &res_opt_menu); + dimage_part(0, 56, &res_opt_menu, 0, 0, 42, 8); if(list) { @@ -422,22 +267,17 @@ void main_menu(int *category, int *app) index = 0; scroll = 0; break; - case KEY_F3: +/* case KEY_F3: + *category = 2; + *app = 1; + return; +*/ +/* case KEY_F3: if(tab == 2) break; tab = 2; index = 0; scroll = 0; break; - case KEY_F4: - if(tab == 3) break; - tab = 3; - index = 0; - scroll = 0; - break; -/* case KEY_F6:; - void screen(void); - screen(); - break; */ case KEY_UP: if(list && list_len > 1) @@ -514,36 +354,18 @@ int main(void) case 0x0105: test_rtc(); break; case 0x0106: test_timer(); break; - case 0x0201: /* perf_bopti(); */ break; + case 0x0201: perf_bopti(); break; case 0x0202: /* perf_tales(); */ break; - - case 0x0301: /* if(isSH3()) debug_tlb(); */ break; } } return 0; } -static void crash(void) +/* +#include +void screen(void) { - __asm__( - "mov #0, r0 \n\t" - "ldc r0, vbr \n\t" - "trapa #1 " - ); -} - -static void screen(void) -{ - enum { File = 1, Folder = 5 }; - enum { Read = 0x01, Write = 0x02, ReadWrite = Read | Write }; - - int BFile_Remove(const uint16_t *file); - int BFile_Create(const uint16_t *file, int type, int *size); - int BFile_Open(const uint16_t *file, int mode); - int BFile_Close(int handle); - int BFile_Write(int handle, const void *ram_buffer, int even_size); - const uint8_t bmp_header[0x7e] = { 0x42, 0x4d, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6c, 0x00, @@ -567,8 +389,8 @@ static void screen(void) int size = 0x7e + 1024; BFile_Remove(file); - BFile_Create(file, File, &size); - int handle = BFile_Open(file, Write); + BFile_Create(file, BFile_File, &size); + int handle = BFile_Open(file, BFile_WriteOnly); BFile_Write(handle, bmp_header, 0x7e); void *vram = (void *)display_getCurrentVRAM() + 1024; for(int i = 1; i <= 64; i++) @@ -577,3 +399,58 @@ static void screen(void) } BFile_Close(handle); } +void screengray(void) +{ + const uint8_t bmp_header[0x7e] = { + 0x42, 0x4d, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6c, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x13, 0x0b, + 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x47, + 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + }; + uint16_t file[] = { '\\', '\\', 'f', 'l', 's', '0', '\\', 'g', 's', + 'c', 'r', 'e', 'e', 'n', '#', '.', 'b', 'm', 'p', 0x00 }; + int size = 0x7e + 1024; + void *vram; + int handle; + + gupdate(); + + file[14] = 'l'; + BFile_Remove(file); + BFile_Create(file, BFile_File, &size); + handle = BFile_Open(file, BFile_WriteOnly); + BFile_Write(handle, bmp_header, 0x7e); + vram = (void *)gray_lightVRAM() + 1024; + for(int i = 1; i <= 64; i++) + { + BFile_Write(handle, vram - 16 * i, 16); + } + BFile_Close(handle); + + file[14] = 'd'; + BFile_Remove(file); + BFile_Create(file, BFile_File, &size); + handle = BFile_Open(file, BFile_WriteOnly); + BFile_Write(handle, bmp_header, 0x7e); + vram = (void *)gray_darkVRAM() + 1024; + for(int i = 1; i <= 64; i++) + { + BFile_Write(handle, vram - 16 * i, 16); + } + BFile_Close(handle); + + gupdate(); +} +*/ diff --git a/demo/perf_bopti.c b/demo/perf_bopti.c new file mode 100644 index 0000000..94a9907 --- /dev/null +++ b/demo/perf_bopti.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include + +/* +static const uint8_t screen[1024] = { +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, 7, 159, 0, 0, 1, 192, 0, 0, 0, 0, 0, 121, 240, 0, 0, 0, +31, 191, 192, 0, 3, 224, 27, 216, 0, 0, 1, 251, 252, 0, 0, 0, 57, 247, 222, +30, 7, 240, 36, 36, 62, 25, 131, 159, 24, 255, 129, 224, 0, 227, 142, 126, 1, +192, 45, 172, 127, 127, 192, 14, 1, 255, 199, 224, 0, 227, 140, 240, 1, 192, +26, 88, 115, 127, 224, 14, 57, 221, 207, 0, 0, 227, 13, 192, 1, 192, 34, 68, +120, 30, 0, 14, 25, 156, 220, 0, 0, 227, 253, 252, 1, 192, 36, 36, 126, 28, +0, 14, 219, 156, 223, 192, 0, 227, 253, 252, 1, 192, 36, 36, 31, 12, 0, 46, +27, 140, 223, 192, 0, 227, 141, 193, 193, 192, 40, 20, 7, 140, 0, 206, 25, 140, +220, 28, 0, 227, 140, 225, 129, 199, 24, 24, 99, 156, 1, 14, 25, 204, 206, 24, +0, 227, 142, 127, 1, 195, 39, 228, 255, 156, 2, 14, 24, 237, 199, 240, 1, 247, +222, 62, 1, 198, 44, 44, 223, 30, 2, 31, 28, 237, 131, 224, 1, 224, 0, 0, 3, +254, 27, 216, 0, 0, 4, 30, 0, 0, 0, 0, 3, 192, 0, 0, 7, 252, 0, 0, 0, 0, 4, +60, 1, 249, 240, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 4, 0, 97, 240, 56, 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 224, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +4, 0, 47, 192, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 32, 255, 128, 63, 128, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 32, 255, 0, 48, 78, 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, 15, 176, 255, 0, 112, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 8, 56, 255, 0, +96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 8, 60, 255, 0, 224, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 130, 56, 126, 255, 3, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 192, +62, 255, 15, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 14, 191, 255, 192, 0, +0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 6, 129, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, +1, 0, 0, 6, 0, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 7, 128, 63, 192, +0, 0, 96, 1, 224, 1, 0, 0, 0, 2, 0, 0, 7, 0, 31, 192, 0, 0, 95, 1, 11, 68, 88, +0, 0, 4, 0, 0, 7, 128, 31, 192, 0, 1, 192, 129, 204, 85, 100, 0, 0, 8, 0, 0, +15, 128, 63, 224, 0, 0, 95, 1, 8, 85, 68, 0, 1, 144, 0, 0, 31, 128, 143, 224, +64, 0, 96, 1, 232, 41, 68, 0, 2, 96, 0, 31, 255, 129, 7, 248, 96, 0, 0, 0, 0, +0, 0, 0, 4, 0, 0, 96, 254, 129, 7, 254, 96, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 128, +254, 131, 135, 255, 224, 0, 0, 1, 192, 64, 16, 0, 8, 0, 7, 0, 254, 131, 255, +63, 224, 0, 0, 1, 38, 113, 208, 0, 8, 0, 13, 0, 222, 147, 254, 31, 224, 0, 0, +1, 41, 74, 80, 0, 8, 0, 25, 0, 222, 67, 254, 31, 160, 0, 0, 1, 41, 74, 80, 0, +12, 0, 49, 0, 222, 19, 254, 62, 48, 0, 0, 1, 198, 113, 208, 0, 2, 0, 32, 128, +222, 195, 255, 252, 56, 0, 0, 0, 0, 0, 0, 0, 2, 0, 124, 64, 220, 151, 135, 248, +127, 0, 0, 0, 0, 0, 0, 0, 2, 0, 66, 32, 221, 223, 7, 240, 255, 0, 0, 0, 0, 0, +0, 0, 2, 0, 129, 23, 93, 159, 15, 241, 131, 0, 0, 0, 0, 0, 0, 0, 4, 0, 128, +136, 217, 95, 3, 226, 9, 0, 0, 1, 240, 0, 0, 0, 4, 0, 128, 72, 89, 95, 129, +228, 18, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 72, 73, 127, 128, 224, 36, 0, 0, 0, 0, +0, 0, 0, 28, 1, 0, 76, 129, 127, 192, 96, 8, 0, 0, 0, 0, 0, 0, 0, 16, 1, 0, +231, 203, 124, 96, 64, 0, 0, 0, 0, 0, 0, 0, 0, 16, 1, 1, 28, 123, 240, 12, 64, +1, 0, 0, 0, 0, 0, 0, 0, 16, 1, 2, 28, 143, 128, 15, 192, 7, 0, 0, 0, 0, 0, 0, +0, 16, 1, 4, 17, 143, 24, 15, 192, 14, 0, 0, 0, 0, 0, 0, 0, 28, 1, 4, 1, 135, +24, 31, 192, 24, 0, 0, 0, 0, 0, 0, 0, 18, 1, 62, 1, 135, 248, 63, 224, 192, +0, 0, 0, 0, 0, 0, 0, 35, 1, 195, 1, 135, 128, 254, 126, 1, 0, 0, 0, 0, 0, 0, +0, 35, 193, 131, 195, 135, 255, 248, 112, 1, 0, 0, 0, 0, 0, 0, 0, 67, 241, 131, +14, 207, 255, 192, 224, 3, 0, 0, 0, 0, 0, 0, 3, 67, 15, 143, 56, 255, 7, 1, +224, 7, 0, 0, 0, 0, 0, 0, 28, 130, 7, 255, 112, 204, 7, 131, 224, 31, 0, 0, +0, 0, 0, 0, 32, 134, 30, 29, 120, 156, 7, 255, 224, 127, 0, 0, 0, 0, 0, 63, +197, 206, 60, 56, 192, 248, 15, 255, 248, 255, 0, 0, 0, 0, 0, 120, 5, 227, 248, +56, 195, 248, 127, 191, 254, 63, 0, 0, 0, 0, 7, 254, 255, 193, 255, 15, 193, +255, 15, 31, 252, 31 }; +*/ + +static const uint8_t town[4464] = { 4, 0, 0, 16, 2, 115, 130, 6, 243, 130, 6, 243, 130, 6, 243, 130, 4, 0, 0, 16, 2, 115, 130, 6, 243, 130, 6, 243, 130, 6, 243, 130, 4, 0, 0, 16, 255, 255, 255, 240, 3, 255, 198, 15, 127, 198, 15, 127, 198, 15, 127, 198, 255, 255, 255, 240, 3, 255, 198, 15, 127, 198, 15, 127, 198, 15, 127, 198, 255, 255, 255, 240, 0, 144, 3, 192, 0, 112, 255, 30, 112, 255, 30, 112, 255, 30, 112, 255, 0, 144, 3, 192, 0, 112, 255, 30, 112, 255, 30, 112, 255, 30, 112, 255, 0, 144, 3, 192, 0, 144, 2, 192, 15, 240, 195, 255, 240, 195, 255, 240, 195, 255, 240, 195, 0, 144, 2, 192, 15, 240, 195, 255, 240, 195, 255, 240, 195, 255, 240, 195, 0, 144, 2, 192, 0, 144, 2, 64, 112, 15, 129, 240, 15, 129, 240, 15, 129, 240, 15, 129, 0, 144, 2, 64, 112, 15, 129, 240, 15, 129, 240, 15, 129, 240, 15, 129, 0, 144, 2, 64, 0, 208, 3, 64, 128, 1, 193, 128, 1, 193, 128, 1, 193, 128, 1, 193, 0, 208, 3, 64, 128, 1, 193, 128, 1, 193, 128, 1, 193, 128, 1, 193, 0, 208, 3, 64, 255, 223, 255, 67, 0, 0, 227, 0, 0, 227, 0, 0, 227, 0, 0, 227, 255, 223, 255, 67, 0, 0, 227, 0, 0, 227, 0, 0, 227, 0, 0, 227, 255, 223, 255, 64, 112, 195, 129, 194, 0, 0, 126, 0, 0, 126, 0, 0, 126, 0, 0, 126, 112, 195, 129, 194, 0, 0, 126, 0, 0, 126, 0, 0, 126, 0, 0, 126, 112, 195, 129, 192, 247, 255, 254, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 247, 255, 254, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 247, 255, 254, 192, 72, 0, 2, 98, 240, 113, 202, 240, 113, 202, 240, 113, 202, 240, 113, 202, 72, 0, 2, 98, 240, 113, 202, 240, 113, 202, 240, 113, 202, 240, 113, 202, 72, 0, 2, 96, 240, 0, 2, 70, 25, 255, 46, 25, 255, 46, 25, 255, 46, 25, 255, 46, 240, 0, 2, 70, 25, 255, 46, 25, 255, 46, 25, 255, 46, 25, 255, 46, 240, 0, 2, 64, 96, 1, 226, 198, 30, 48, 110, 30, 48, 110, 30, 48, 110, 30, 48, 110, 96, 1, 226, 198, 30, 48, 110, 30, 48, 110, 30, 48, 110, 30, 48, 110, 96, 1, 226, 192, 64, 2, 19, 199, 56, 32, 111, 56, 32, 111, 56, 32, 111, 56, 32, 111, 64, 2, 19, 199, 56, 32, 111, 56, 32, 111, 56, 32, 111, 56, 32, 111, 64, 2, 19, 192, 128, 2, 19, 71, 252, 96, 247, 252, 96, 247, 252, 96, 247, 252, 96, 247, 128, 2, 19, 71, 252, 96, 247, 252, 96, 247, 252, 96, 247, 252, 96, 247, 128, 2, 19, 64, 0, 2, 19, 71, 15, 241, 231, 15, 241, 231, 15, 241, 231, 15, 241, 231, 0, 2, 19, 71, 15, 241, 231, 15, 241, 231, 15, 241, 231, 15, 241, 231, 0, 2, 19, 64, 3, 194, 18, 71, 12, 63, 255, 12, 63, 231, 12, 63, 231, 12, 63, 255, 3, 194, 18, 71, 12, 63, +255, 12, 63, 231, 12, 63, 231, 12, 63, 255, 3, 194, 18, 64, 6, 99, 242, 195, 152, 31, 0, 248, 31, 195, 152, 31, 195, 152, 31, 0, 6, 99, 242, 195, 152, 31, 0, 248, 31, 195, 152, 31, 195, 152, 31, 0, 6, 99, 242, 192, 10, 84, 10, 193, 252, 24, 0, 28, 31, 129, 252, 31, 129, 252, 24, 0, 10, 84, 10, 193, 252, 24, 0, 28, 31, 129, 252, 31, 129, 252, 24, 0, 10, 84, 10, 192, 10, 87, 251, 192, 126, 48, 0, 14, 62, 0, 126, 62, 0, 126, 48, 0, 10, 87, 251, 192, 126, 48, 0, 14, 62, 0, 126, 62, 0, 126, 48, 0, 10, 87, 251, 192, 10, 80, 3, 192, 31, 224, 0, 7, 252, 3, 255, 255, 255, 255, 224, 0, 10, 80, 3, 192, 31, 224, 0, 7, 252, 3, 255, 255, 255, 255, 224, 0, 10, 80, 3, 192, 10, 208, 3, 64, 12, 56, 0, 12, 32, 6, 16, 64, 0, 2, 56, 0, 10, 208, 3, 64, 12, 56, 0, 12, 32, 6, 16, 64, 0, 2, 56, 0, 10, 208, 3, 64, 250, 95, 254, 64, 56, 175, 7, 28, 144, 7, 255, 252, 0, 2, 175, 7, 250, 95, 254, 64, 56, 175, 7, 28, 144, 7, 255, 252, 0, 2, 175, 7, 250, 95, 254, 64, 106, 86, 219, 64, 34, 225, 159, 242, 200, 4, 130, 8, 0, 2, 225, 159, 106, 86, 219, 64, 34, 225, 159, 242, 200, 4, 130, 8, 0, 2, 225, 159, 106, 86, 219, 64, 10, 80, 3, 64, 62, 225, 227, 6, 228, 15, 255, 240, 0, 1, 225, 227, 10, 80, 3, 64, 62, 225, 227, 6, 228, 15, 255, 240, 0, 1, 225, 227, 10, 80, 3, 64, 255, 255, 255, 192, 4, 243, 130, 6, 156, 10, 16, 64, 0, 1, 243, 130, 255, 255, 255, 192, 4, 243, 130, 6, 156, 10, 16, 64, 0, 1, 243, 130, 255, 255, 255, 192, 0, 0, 3, 192, 7, 127, 198, 15, 0, 15, 255, 255, 255, 255, 127, 198, 0, 0, 3, 192, 7, 127, 198, 15, 0, 15, 255, 255, 255, 255, 127, 198, 0, 0, 3, 192, 0, 0, 0, 0, 0, 112, 255, 30, 0, 3, 192, 0, 0, 60, 112, 255, 0, 0, 0, 0, 0, 112, 255, 30, 0, 3, 192, 0, 0, 60, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 195, 254, 0, 2, 198, 0, 220, 44, 112, 195, 0, 0, 0, 0, 0, 112, 195, 254, 0, 2, 198, 0, 220, 44, 112, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 129, 252, 0, 2, 69, 0, 168, 36, 57, 129, 0, 0, 0, 0, 0, 57, 129, 252, 0, 2, 69, 0, 168, 36, 57, 129, 0, 0, 0, 0, 0, 0, 0, 0, 128, 31, 193, 248, 0, 3, 117, 62, 87, 244, 31, 193, 0, 0, 0, 0, 128, 31, 193, 248, 0, 3, 117, 62, 87, 244, 31, 193, 0, 0, 0, 0, 0, 0, 0, 2, 160, 7, 227, 224, 0, 3, 72, 128, 168, 52, 7, 227, 0, 0, 0, 2, 160, 7, 227, 224, 0, 3, 72, 128, 168, 52, 7, 227, 0, 0, 0, 0, 0, 0, 0, 1, 64, 1, 255, 192, 0, 3, 232, 246, 245, 252, 1, 254, 0, 0, 0, 1, 64, 1, 255, 192, 0, 3, 232, 246, 245, 252, 1, 254, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 194, 0, 16, 2, 199, 40, 138, 44, 0, 195, 0, 0, 0, 128, 0, 0, 194, 0, 16, 2, 199, 40, 138, 44, 0, 195, 0, 0, 0, 128, 0, 0, 2, 160, 0, 3, 137, 0, 40, 2, 64, 24, 6, 36, 3, 138, 0, 0, 2, 160, 0, 3, 137, 0, 40, 2, 64, 24, 6, 36, 3, 138, 0, 0, 2, 160, 0, 0, 1, 64, 0, 2, 44, 128, 40, 2, 64, 0, 0, 36, 2, 46, 0, 0, 1, 64, 0, 2, 44, 128, 40, 2, 64, 0, 0, 36, 2, 46, 0, 0, 1, 64, 0, 0, 0, 0, 0, 3, 238, 64, 68, 2, 192, 0, 0, 44, 3, 238, 0, 0, 0, 0, 0, 3, 238, 64, 68, 2, 192, 0, 0, 44, 3, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 192, 68, 3, 192, 63, 192, 60, 0, 79, 0, 0, 0, 0, 0, 0, 73, 192, 68, 3, 192, 63, 192, 60, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 56, 3, 64, 124, 32, 52, 0, 119, 0, 0, 0, 0, 0, 0, 112, 0, 56, 3, 64, 124, 32, 52, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 64, 80, 32, 52, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 64, 80, 32, 52, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 64, 248, 16, 36, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 64, 248, 16, 36, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 192, 192, 16, 44, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 192, 192, 16, 44, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 192, 255, 240, 44, 8, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 192, 255, 240, 44, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 3, 192, 41, 64, 60, 48, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 3, 192, 41, 64, 60, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 3, 192, 41, 64, 60, 32, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 3, 192, 41, 64, 60, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 16, 3, 64, 47, 64, 52, 56, 0, 0, 0, 0, 0, 8, 0, 0, 8, 16, 3, 64, 47, 64, 52, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 42, 40, 2, 127, 224, 127, 230, 47, 7, 0, 0, 0, 0, 42, 0, 0, 42, 40, 2, 127, 224, 127, 230, 47, 7, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 20, 40, 3, 118, 227, 109, 180, 97, 159, 0, 0, 0, 0, 20, 0, 0, 20, 40, 3, 118, 227, 109, 180, 97, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 3, 64, 32, 64, 52, 97, 227, 0, 0, 0, 0, 0, 0, 0, 0, 68, 3, 64, 32, 64, 52, 97, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 3, 255, 255, 255, 252, 115, 130, 0, 0, 0, 0, 0, 0, 0, 0, 124, 3, 255, 255, 255, 252, 115, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 0, 60, 127, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 0, 60, 127, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 132, 0, 0, 112, 195, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 132, 0, 0, 112, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 126, 0, 0, 120, 0, 0, 57, 129, 0, 0, 0, 0, 0, 0, 0, 40, 126, 0, 0, 120, 0, 0, 57, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 252, 0, 0, 3, 240, 0, 31, 193, 0, 0, 0, 0, 0, 0, 0, 43, 252, 0, 0, 3, 240, 0, 31, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 134, 0, 0, 2, 16, 0, 7, 227, 0, 0, 0, 0, 0, 0, 0, 21, 134, 0, 0, 2, 16, 0, 7, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 6, 0, 0, 1, 224, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 23, 6, 0, 0, 1, 224, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 62, 0, 0, 252, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 15, 62, 0, 0, 252, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 250, 0, 0, 132, 0, 0, 3, 138, 0, 0, 0, 0, 0, 0, 0, 14, 250, 0, 0, 132, 0, 0, 3, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 190, 0, 0, 120, 0, 0, 2, 46, 0, 0, 0, 0, 0, 0, 0, 6, 190, 0, 0, 120, 0, 0, 2, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 156, 0, 0, 3, 240, 0, 3, 238, 0, 0, 0, 0, 0, 0, 0, 3, 156, 0, 0, 3, 240, 0, 3, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 36, 0, 0, 2, 16, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 1, 36, 0, 0, 2, 16, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 1, 224, 0, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 1, 224, 0, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 4, 0, 0, 16, 2, 115, 130, 6, 243, 130, 6, 243, 130, 6, 243, 130, 4, 0, 0, 16, 2, 115, 130, 6, 243, 130, 6, 243, 130, 6, 243, 130, 4, 0, 0, 16, 255, 255, 255, 240, 3, 255, 198, 15, 127, 198, 15, 127, 198, 15, 127, 198, 255, 255, 255, 240, 3, 255, 198, 15, 127, 198, 15, 127, 198, 15, 127, 198, 255, 255, 255, 240, 0, 144, 3, 192, 0, 112, 255, 30, 112, 255, 30, 112, 255, 30, 112, 255, 0, 144, 3, 192, 0, 112, 255, 30, 112, 255, 30, 112, 255, 30, 112, 255, 0, 144, 3, 192, 0, 144, 2, 192, 15, 240, 195, 255, 240, 195, 255, 240, 195, 255, 240, 195, 0, 144, 2, 192, 15, 240, 195, 255, 240, 195, 255, 240, 195, 255, 240, 195, 0, 144, 2, 192, 0, 144, 2, 64, 112, 15, 129, 240, 15, 129, 240, 15, 129, 240, 15, 129, 0, 144, 2, 64, 112, 15, 129, 240, 15, 129, 240, 15, 129, 240, 15, 129, 0, 144, 2, 64, 0, 208, 3, 64, 128, 1, 193, 128, 1, 193, 128, 1, 193, 128, 1, 193, 0, 208, 3, 64, 128, 1, 193, 128, 1, 193, 128, 1, 193, 128, 1, 193, 0, 208, 3, 64, 255, 223, 255, 67, 0, 0, 227, 0, 0, 227, 0, 0, 227, 0, 0, 227, 255, 223, 255, 67, 0, 0, 227, 0, 0, 227, 0, 0, 227, 0, 0, 227, 255, 223, 255, 64, 112, 195, 129, 194, 0, 0, 126, 0, 0, 126, 0, 0, 126, 0, 0, 126, 112, 195, 129, 194, 0, 0, 126, 0, 0, 126, 0, 0, 126, 0, 0, 126, 112, 195, 129, 192, 247, 255, 254, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 247, 255, 254, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 247, 255, 254, 192, 72, 0, 2, 98, 240, 113, 202, 240, 113, 202, 240, 113, 202, 240, 113, 202, 72, 0, 2, 98, 240, 113, 202, 240, 113, 202, 240, 113, 202, 240, 113, 202, 72, 0, 2, 96, 240, 0, 2, 70, 25, 255, 46, 25, 255, 46, 25, 255, 46, 25, 255, 46, 240, 0, 2, 70, 25, 255, 46, 25, 255, 46, 25, 255, 46, 25, 255, 46, 240, 0, 2, 64, 96, 1, 226, 198, 30, 48, 110, 30, 48, 110, 30, 48, 110, 30, 48, 110, 96, 1, 226, 198, 30, 48, 110, 30, 48, 110, 30, 48, 110, 30, 48, 110, 96, 1, 226, 192, 64, 2, 19, 199, 56, 32, 111, 56, 32, 111, 56, 32, 111, 56, 32, 111, 64, 2, 19, 199, 56, 32, 111, 56, 32, 111, 56, 32, 111, 56, 32, 111, 64, 2, 19, 192, 128, 2, 19, 71, 252, 96, 247, 252, 96, 247, 252, 96, 247, 252, 96, 247, 128, 2, 19, 71, 252, 96, 247, 252, 96, 247, 252, 96, 247, 252, 96, 247, 128, 2, 19, 64, 0, 2, 19, 71, 15, 241, 231, 15, 241, 231, 15, 241, 231, 15, 241, 231, 0, 2, 19, 71, 15, 241, 231, 15, 241, 231, 15, 241, 231, 15, 241, 231, 0, 2, 19, 64, 3, 194, 18, 71, 12, 63, 255, 12, 63, 231, 12, 63, 231, 12, 63, 255, 3, 194, 18, 71, 12, 63, 255, 12, 63, 231, 12, 63, 231, 12, 63, 255, 3, 194, 18, 64, 6, 99, 242, 195, 152, 31, 0, 248, 31, 195, 152, 31, 195, 152, 31, 0, 6, 99, 242, 195, 152, 31, 0, 248, 31, 195, 152, 31, 195, 152, 31, 0, 6, 99, 242, 192, 10, 84, 10, 193, 252, 24, 0, 28, 31, 129, 252, 31, 129, 252, 24, 0, 10, 84, 10, 193, 252, 24, 0, 28, 31, 129, 252, 31, 129, 252, 24, 0, 10, 84, 10, 192, 10, 87, 251, 192, 126, 48, 0, 14, 62, 0, 126, 62, 0, 126, 48, 0, 10, 87, 251, 192, 126, 48, 0, 14, 62, 0, 126, 62, 0, 126, 48, 0, 10, 87, 251, 192, 10, 80, 3, 192, 31, 224, 0, 7, 252, 3, 255, 255, 255, 255, 224, 0, 10, 80, 3, 192, 31, 224, 0, 7, 252, 3, 255, 255, 255, 255, 224, 0, 10, 80, 3, 192, 10, 208, 3, 64, 12, 56, 0, 12, 32, 6, 16, 64, 0, 2, 56, 0, 10, 208, 3, 64, 12, 56, 0, 12, 32, 6, 16, 64, 0, 2, 56, 0, 10, 208, 3, 64, 250, 95, 254, 64, 56, 175, 7, 28, 144, 7, 255, 252, 0, 2, 175, 7, 250, 95, 254, 64, 56, 175, 7, 28, 144, 7, 255, 252, 0, 2, 175, 7, 250, 95, 254, 64, 106, 86, 219, 64, 34, 225, 159, 242, 200, 4, 130, 8, 0, 2, 225, 159, 106, 86, 219, 64, 34, 225, 159, 242, 200, 4, 130, 8, 0, 2, 225, 159, 106, 86, 219, 64, 10, 80, 3, 64, 62, 225, 227, 6, 228, 15, 255, 240, 0, 1, 225, 227, 10, 80, 3, 64, 62, 225, 227, 6, 228, 15, 255, 240, 0, 1, 225, 227, 10, 80, 3, 64, 255, 255, 255, 192, 4, 243, 130, 6, 156, 10, 16, 64, 0, 1, 243, 130, 255, 255, 255, 192, 4, 243, 130, 6, 156, 10, 16, 64, 0, 1, 243, 130, 255, 255, 255, 192, 0, 0, 3, 192, 7, 127, 198, 15, 0, 15, 255, 255, 255, 255, 127, 198, 0, 0, 3, 192, 7, 127, 198, 15, 0, 15, 255, 255, 255, 255, 127, 198, 0, 0, 3, 192, 0, 0, 0, 0, 0, 112, 255, 30, 0, 3, 192, 0, 0, 60, 112, 255, 0, 0, 0, 0, 0, 112, 255, 30, 0, 3, 192, 0, 0, 60, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 195, 254, 0, 2, 198, 0, 220, 44, 112, 195, 0, 0, 0, 0, 0, 112, 195, 254, 0, 2, 198, 0, 220, 44, 112, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 129, 252, 0, 2, 69, 0, 168, 36, 57, 129, 0, 0, 0, 0, 0, 57, 129, 252, 0, 2, 69, 0, 168, 36, 57, 129, 0, 0, 0, 0, 0, 0, 0, 0, 128, 31, 193, 248, 0, 3, 117, 62, 87, 244, 31, 193, 0, 0, 0, 0, 128, 31, 193, 248, 0, 3, 117, 62, 87, 244, 31, 193, 0, 0, 0, 0, 0, 0, 0, 2, 160, 7, 227, 224, 0, 3, 72, 128, 168, 52, 7, 227, 0, 0, 0, 2, 160, 7, 227, 224, 0, 3, 72, 128, 168, 52, 7, 227, 0, 0, 0, 0, 0, 0, 0, 1, 64, 1, 255, 192, 0, 3, 232, 246, 245, 252, 1, 254, 0, 0, 0, 1, 64, 1, 255, 192, 0, 3, 232, 246, 245, 252, 1, 254, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 194, 0, 16, 2, 199, 40, 138, 44, 0, 195, 0, 0, 0, 128, 0, 0, 194, 0, 16, 2, 199, 40, 138, 44, 0, 195, 0, 0, 0, 128, 0, 0, 2, 160, 0, 3, 137, 0, 40, 2, 64, 24, 6, 36, 3, 138, 0, 0, 2, 160, 0, 3, 137, 0, 40, 2, 64, 24, 6, 36, 3, 138, 0, 0, 2, 160, 0, 0, 1, 64, 0, 2, 44, 128, 40, 2, 64, 0, 0, 36, 2, 46, 0, 0, 1, 64, 0, 2, 44, 128, 40, 2, 64, 0, 0, 36, 2, 46, 0, 0, 1, 64, 0, 0, 0, 0, 0, 3, 238, 64, 68, 2, 192, 0, 0, 44, 3, 238, 0, 0, 0, 0, 0, 3, 238, 64, 68, 2, 192, 0, 0, 44, 3, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 192, 68, 3, 192, 63, 192, 60, 0, 79, 0, 0, 0, 0, 0, 0, 73, 192, 68, 3, 192, 63, 192, 60, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 56, 3, 64, 124, 32, 52, 0, 119, 0, 0, 0, 0, 0, 0, 112, 0, 56, 3, 64, 124, 32, 52, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 64, 80, 32, 52, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 64, 80, 32, 52, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 64, 248, 16, 36, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 64, 248, 16, 36, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 192, 192, 16, 44, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 192, 192, 16, 44, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 192, 255, 240, 44, 8, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 192, 255, 240, 44, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 3, 192, 41, 64, 60, 48, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 3, 192, 41, 64, 60, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 3, 192, 41, 64, 60, 32, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 3, 192, 41, 64, 60, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 16, 3, 64, 47, 64, 52, 56, 0, 0, 0, 0, 0, 8, 0, 0, 8, 16, 3, 64, 47, 64, 52, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 42, 40, 2, 127, 224, 127, 230, 47, 7, 0, 0, 0, 0, 42, 0, 0, 42, 40, 2, 127, 224, 127, 230, 47, 7, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 20, 40, 3, 118, 227, 109, 180, 97, 159, 0, 0, 0, 0, 20, 0, 0, 20, 40, 3, 118, 227, 109, 180, 97, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 3, 64, 32, 64, 52, 97, 227, 0, 0, 0, 0, 0, 0, 0, 0, 68, 3, 64, 32, 64, 52, 97, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 3, 255, 255, 255, 252, 115, 130, 0, 0, 0, 0, 0, 0, 0, 0, 124, 3, 255, 255, 255, 252, 115, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 0, 60, 127, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 0, 60, 127, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 132, 0, 0, 112, 195, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 132, 0, 0, 112, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 126, 0, 0, 120, 0, 0, 57, 129, 0, 0, 0, 0, 0, 0, 0, 40, 126, 0, 0, 120, 0, 0, 57, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 252, 0, 0, 3, 240, 0, 31, 193, 0, 0, 0, 0, 0, 0, 0, 43, 252, 0, 0, 3, 240, 0, 31, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 134, 0, 0, 2, 16, 0, 7, 227, 0, 0, 0, 0, 0, 0, 0, 21, 134, 0, 0, 2, 16, 0, 7, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 6, 0, 0, 1, 224, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 23, 6, 0, 0, 1, 224, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 62, 0, 0, 252, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 15, 62, 0, 0, 252, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 250, 0, 0, 132, 0, 0, 3, 138, 0, 0, 0, 0, 0, 0, 0, 14, 250, 0, 0, 132, 0, 0, 3, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 190, 0, 0, 120, 0, 0, 2, 46, 0, 0, 0, 0, 0, 0, 0, 6, 190, 0, 0, 120, 0, 0, 2, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 156, 0, 0, 3, 240, 0, 3, 238, 0, 0, 0, 0, 0, 0, 0, 3, 156, 0, 0, 3, 240, 0, 3, 238, 0, 0, 0, 0 }; + +static const uint8_t screen[460] = { 0, 0, 0, 2, 0, 23, 224, 15, 0, 0, 0, 0, 0, 2, 16, 127, 192, 31, 192, 0, 0, 0, 0, 1, 144, 127, 128, 24, 39, 0, 0, 0, 0, 7, 216, 127, 128, 56, 24, 128, 0, 0, 14, 4, 28, 127, 128, 48, 0, 0, 0, 0, 49, 4, 30, 127, 128, 112, 0, 0, 0, 0, 65, 28, 63, 127, 129, 240, 0, 0, 0, 0, 128, 224, 31, 127, 135, 240, 0, 0, 0, 1, 0, 0, 7, 95, 255, 224, 0, 0, 0, 1, 0, 0, 3, 64, 255, 224, 0, 0, 0, 0, 128, 0, 3, 0, 127, 224, 0, 0, 0, 0, 128, 0, 3, 192, 31, 224, 0, 0, 0, 1, 0, 0, 3, 128, 15, 224, 0, 0, 0, 2, 0, 0, 3, 192, 15, 224, 0, 0, 0, 4, 0, 0, 7, 192, 31, 240, 0, 0, 0, 200, 0, 0, 15, 192, 71, 240, 32, 0, 1, 48, 0, 15, 255, 192, 131, 252, 48, 0, 2, 0, 0, 48, 127, 64, 131, 255, 48, 0, 2, 0, 0, 192, 127, 65, 195, 255, 240, 0, 4, 0, 3, 128, 127, 65, 255, 159, 240, 0, 4, 0, 6, 128, 111, 73, 255, 15, 240, 0, 4, 0, 12, 128, 111, 33, 255, 15, 208, 0, 6, 0, 24, 128, 111, 9, 255, 31, 24, 0, 1, 0, 16, 64, 111, 97, 255, 254, 28, 0, 1, 0, 62, 32, 110, 75, 195, 252, 63, 128, 1, 0, 33, 16, 110, 239, 131, 248, 127, 128, 1, 0, 64, 139, 174, 207, 135, 248, 193, 128, 2, 0, 64, 68, 108, 175, 129, 241, 4, 128, 2, 0, 64, 36, 44, 175, 192, 242, 9, 0, 2, 0, 128, 36, 36, 191, 192, 112, 18, 0, 14, 0, 128, 38, 64, 191, 224, 48, 4, 0, 8, 0, 128, 115, 229, 190, 48, 32, 0, 0, 8, 0, 128, 142, 61, 248, 6, 32, 0, 128, 8, 0, 129, 14, 71, 192, 7, 224, 3, 128, 8, 0, 130, 8, 199, 140, 7, 224, 7, 0, 14, 0, 130, 0, 195, 140, 15, 224, 12, 0, 9, 0, 159, 0, 195, 252, 31, 240, 96, 0, 17, 128, 225, 128, 195, 192, 127, 63, 0, 128, 17, 224, 193, 225, 195, 255, 252, 56, 0, 128, 33, 248, 193, 135, 103, 255, 224, 112, 1, 128, 161, 135, 199, 156, 127, 131, 128, 240, 3, 128, 65, 3, 255, 184, 102, 3, 193, 240, 15, 128, 67, 15, 14, 188, 78, 3, 255, 240, 63, 128, 231, 30, 28, 96, 124, 7, 255, 252, 127, 128, 241, 252, 28, 97, 252, 63, 223, 255, 31, 128, 224, 255, 135, 224, 255, 135, 143, 254, 15, 128 }; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wparentheses" +#pragma GCC diagnostic ignored "-Wshift-negative-value" +#pragma GCC push_options +#pragma GCC optimize 0 + +#define ML_vram_adress display_getCurrentVRAM +__attribute__((optimize("0"))) +void ML_bmp_or_cl(const unsigned char *bmp, int x, int y, int width, int height) +{ + uint16_t line; + char shift, *screen; + uint8_t *p; + int i, j, real_width, begin_x, end_x, begin_y, end_y; + int bool1=1, bool2=1, bool3; + if(!bmp || x<1-width || x>127 || y<1-height || y>63 || height<1 + || width<1) return; + p = (uint8_t *)&line; + real_width = (width-1>>3<<3)+8; + if(y < 0) begin_y = -y; + else begin_y = 0; + if(y+height > 64) end_y = 64-y; + else end_y = height; + shift = 8-(x&7); + if(x<0) + { + begin_x = -x>>3; + if(shift != 8) bool1 = 0; + } else begin_x = 0; + if(x+real_width > 128) end_x = 15-(x>>3), bool2 = 0; + else end_x = real_width-1>>3; + bool3 = (end_x == real_width-1>>3); + screen = (char *)ML_vram_adress()+(y+begin_y<<4)+(x>>3); + + for(i=begin_y ; i>3)+begin_x] << shift; + if(bool1) screen[begin_x] |= *p; + if(shift!=8) screen[begin_x+1] |= *(p+1); + for(j=begin_x+1 ; j>3)+j] << shift; + screen[j] |= *p; + if(shift!=8) screen[j+1] |= *(p+1); + } + } + line = bmp[i*(real_width>>3)+end_x]; + if(bool3) line &= -1<TCNT; + timer_stop(t); + time1 = 0xffffffff - time1; + + dclear(); + dtext(2, 2, "gint..."); + dupdate(); + + t = htimer_setup(timer_user, 0xffffffff, timer_Po_4, 1); + timer_start(t); + for(i = 0; i < 1000; i++) dimage(x, y, img); + time2 = TMU.timers[timer_user]->TCNT; + timer_stop(t); + time2 = 0xffffffff - time2; + + dclear(); + print(1, 1, " ML: %08x", time1); + print(1, 2, "gint: %08x", time2); + print(1, 3, ">> \x04%d", time1 / time2); + dupdate(); + getkey(); +} diff --git a/demo/resources/opt_bitmap.bmp b/demo/resources/opt_bitmap.bmp index c776021b3647f47a5038217020d133593164f535..d8107170fec8ce5d1c9fd0c222710acad8248e15 100644 GIT binary patch literal 3194 zcmd6ju@1s83`7GH5^OAdPZuT@zJrae6Cc=rf-9#!SxJQmVTh%)=d*o@Z@7$y^o~*A zo6bXLr?W{XeHE%7_dX_48qU}Ig1`N5%f@G0l!Bj`^0w*MK5mFXRRMYBegjgG zU;)Rf@=~1tG(eV2Y;&~-_uAjUrd0qp3`ixCF|ib{5KnkYX&fh|%?u*#yZ4~LI#mEG za4A|BF`b#dc*6ZkZ5JcBRmP|(C^Ee?Q;T&XSj(l@d@)J+3*$*rOtXtZ4O?6FmjZZJ<$Kh7z z^tXJMeEWR6;h3Jv{OQ04F$|~Y*Y{8S-QS0?@Y+{n!)2y#+h5&Y&ODy}R*pDh1@IVB z96(Jj%}B8_0V6bAuXxgM6Vg;Bhw;cm=!*NJYUMgT8%Cu9K$U%oBcK|S&5ucy47E3} zGqxH)XL3!nZDPo)Y&I&GyC@Hco@#uyJtmSlCvnXTuFQ|av#YL#(OkjY3iZ`w4r8nN zoag`ZKGSS4c6px0k!ep_9RMELf^$e}a#2R|;DwG4-COqn7BVNEvB3?~lb^l)4(4xw oQC(5rBDp#6f2i>)<{@;%OgodiVl8m*+KcLSxCdaWwV*Tn2U6elvj6}9 diff --git a/demo/resources/opt_gray.bmp b/demo/resources/opt_gray.bmp index 6d60c629e12bd18b8ed326cf8b6053caad073652..80e5c36689700cd2f51ee19e19572b287b034678 100644 GIT binary patch delta 360 zcmZvYv1$TA5Qf>k+dXI9u;Jk3AP-<;AwrsvLaq`5M_xcHTfr`nO2UC43b*_(Vj-jw z?+F5VgH&nLUzIscKtqa!-TC*Qf4-^HI^7NhUPOl4^f4E)FMGZ;sH8^6IE41@#KO3=}8o^7aTI0qfwRvd8e#Ey30)5y*~ksy>jDbCm)kAIG@r^o5~ zPO|U%KJ^{+-A&K3ddfdN_;X;IjxTQ?Klr!*`%e7&+v|ekNbff0_w0WtoY%fS%%X_4 zGan$=n8>V4m!yGHL_t3Xg{d{@*}^G=lBE~=CS+CTxHWd|`Ohe<1MC8jPR?JpAWJ*Q zq+CLL)}`E4N$xsUfQgGpZvi>g61yV`JGsB7N}Dqqm&ub%N}yo7*YLMtcl)^6vGCIs zri8d_opLZaU7742?q9~XTV}>(a;ivqapD8lA||+;D%@M;X3i=g0J&5T=0R#ZqW+%e79)rskkrDaJ6KC8t_qlTq0G{!FqIaEG!HakjRyX(gMr;LaQ? ckTsAkQV_cGhWBTfMWF1ssMh3NDQtEB3%4y6QUCw| diff --git a/demo/resources/opt_menu.bmp b/demo/resources/opt_menu.bmp index 4d682e41f8ba32f159a4d11eed322f74220e798b..09fd37d17ac8c7d8a6219ed15f477fe9819ab291 100644 GIT binary patch delta 78 ucmew*@k?TZ1LNc-MyJh9jCK$P6El?2!~zpxgNd-iL^xq0To4A(hzbDZIT$zq delta 274 zcmaLTF$%&!5Cu>+*&Q=+L0AxS1_cpM5K>xs0k0xk*eDiG{xNJkLJlO@rwDFLLSkd_ zndb3lm^!PoXRf3<%XKGrjZ;KrRJ8-13A5DU%?_)`Ay>tS;NuESOt_H4jTRw{R~(d{ zsM3Tg!#(KGF|pzam61TF N@OQ&$UZ6|=fe*(LUBv(Z diff --git a/demo/resources/screen.bmp b/demo/resources/screen.bmp index 050825a995dc236695e5926389a04c0d10130388..c6fb2c3ef7332f413ba5e729e523f6c9f6dcf001 100644 GIT binary patch delta 821 zcmX|9&ui3B5N>LJY};s3n%ZFFCTm~Wutpn}t+r(`t5!v*#A1=PENs={7F~?uuL^~S zo}_qLJ`WxW-9MlZAs)Q##e-lW|A3H-_guWk*>q{(W!}siX1;Ic+x^i0@hQ7@r+>aL z{dVcUL+3V~i*$rOqwfUS?NW<1Frlm=b~VfuEI36I^&ty<$bkOPMt#dhGHJsp+=c&! zK~gk|Y493Kzz1akaXtg*f*A1OV@F13QiGncFgGA0mQ-}!u~s_+eYASB8p?6b*bm|lM%M!vx0wr5Wq_5)!*OV?w}Zy1!Xd0M9MMJ4q#}be#VIK2u43S5t`{ zG0Td?P(f!?rEzc5yN&%Z1K|}K_jAf=$kawe^wxxHRgWo1qXhLatwP#?zf?gyCBr9Z z@!U7K*)r9Z;W(_AB5Jwu?Vif|+Y{z`*$bAcXc7c`?fBV|?p1)Yl3-k2T6=&3!e$ z)8TUlzQPr6UwQ1$KKwJ7=g^O`c%A`$Pwo?3wK_h$ zp0ibFf8L8ceD2|mctC5^XhyUS?WOL#kIwskQRijd8oY6&qs{v5i@I9&R63k}-7LID z=gdEAUA>Lpz_s51-W+wO!3V~o&pG&6B%jgwtc`kdBhNEwabGX)4_c$1(K-)&U|!bt z)T4P>8}-%MM*c_E@?ClN(Vg(*Y-G@8^4UWc%({C#72>P3;I`I`XKThI>AtV)?hf`u zoqt^p9zQ(@-yuF@=QVFXH9Gw(BX1EnvpQeL=b7x+@p(??Cy!nso)MB8d7fE|`>(9d zKg(C|4F~!>6=ywaJQ=gE>z(U7&&tQ$SJud1Y1P|ipFPy8XCi0ZJ7XpH=-zm*m9^#1 zdiOodlgS+Y*7+(QGq+K9)-e=}!?>_QnjXscHyy1vP!$%Ix-vGWt`|+5g9%*=<8RP!5tMX2WH(po+Gf^2cA9Y z1HCezb$eIq)mfdt<2g_0c&NefxF%yBeHA?`&&%3TeVmy^4F*pQu5ip<#n{XH&E~7z z%pB*a!MwNU;TfUxm9Nsx9bM=9#D2Hp_MYAQug>6&$<54ih8i5r!81bgVCM}zw0ke_ znd_=I$C-nJ2d}xuwKG=Fll}CZ1LVHk&AzV23wAzueD^~34IHg4o$Ra+e}%W25p!l{ z;j1vt*UhN>6~`SbEpvEZ9phbYv3TFnEIbuPt~-nUdA_xkI?s94zI*e%@4+{y@@N%? ze{_~kt7cUk=Ru=i#l3lQVC)-pwctT}&6%&}%yOO?wfor_*~gq!%ya0wGi}W$58|g^ zMb5z-<7gH+dqx~uhs;5z--|KFvs~|;Im)qL#;uI?oON%X>+tMZ*Uql;a6S*`8zEZc zTzk$f>LX`!_SnO}nRDCU)iZPUcw)a;)xM*7xmH`3^HPbB3NQy%Fse=($nfbLG9OKA#6q@YJBWoV#@&dsd6HfR*Pnah%f_ z=W|b8@xBW_qxGupyztHh7pv0XLA<#qYYY#|yl0>rD?R74$2zkzM<2Rt{2-bUZ3Vmh z3U6<&M`y7&`_SN{XQ5@Z?mGK&UwPp=j2YGP?5v}ihhrXnc&O3Hna?$zS#;i+Ego;@ z3mpw_M)W|hE#6+&(HaGeq=#=;8rouppQ9K9UKe2;+(gSni;D|-g|nlJP*G;Ih!+G&#akz_PBS=xx8oS z{#kQvPo8PtsP>HPLxb;n?`;pYb!P3wZsr!xe)g@N%UR})5Px{Q7qxZIfyYOl-ukHa z%&vCco{G=hR_}A;^&Wd_<^+1J@bJF=U*KpN7Tl2_-j~X<$HRo13duQ}nvPOTaGwhke89-*x8_|xyd+l?? a**(+uh?ZK$*atGklY(*doH`hr|$Y&XaP1GJ!c~ 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 F{vUZB$Z!Av literal 0 HcmV?d00001 diff --git a/demo/test_bopti.c b/demo/test_bopti.c index 9b137c8..de1c083 100644 --- a/demo/test_bopti.c +++ b/demo/test_bopti.c @@ -19,7 +19,7 @@ --------------------------------------------------------- zelda.bmp 86 * 280 Mono - isometric.bmp 37 * 27 Mono Full - Mono Greater + - - Mono Greater --------------------------------------------------------- */ @@ -60,6 +60,7 @@ static image_t *select(image_t *current) res_swords, res_zelda, res_isometric; + extern image_t res_screen; struct { image_t *img; @@ -71,6 +72,7 @@ static image_t *select(image_t *current) { &res_swords, "Swords", "Gray Alpha" }, { &res_zelda, "Zelda", "Mono" }, { &res_isometric, "Isometric", "Mono Alpha" }, + { &res_screen, "TLT", "Mono" }, { NULL, NULL, NULL } }; @@ -81,6 +83,7 @@ static image_t *select(image_t *current) while(images[items].img) items++; + keyboard_setRepeatRate(625, 125); gray_start(); while(1) @@ -141,8 +144,10 @@ void test_bopti(void) int black_bg = 0; int x = 0, y = 0; + while(1) { + keyboard_setRepeatRate(25, 25); if(img && (img->format & channel_light)) { gray_start(); @@ -152,8 +157,7 @@ void test_bopti(void) if(img) gimage(x, y, img); grect(0, 55, 127, 63, color_white); - gimage(0, 56, &res_opt_bitmap); - gupdate(); + gimage_part(0, 56, &res_opt_bitmap, 0, 0, 96, 8); } else if(img) { @@ -164,8 +168,7 @@ void test_bopti(void) if(img) dimage(x, y, img); drect(0, 55, 127, 63, color_white); - dimage(0, 56, &res_opt_bitmap); - dupdate(); + dimage_part(0, 56, &res_opt_bitmap, 0, 0, 96, 8); } else { @@ -174,10 +177,36 @@ void test_bopti(void) dclear(); locate(3, 3, "No image selected"); - dimage(0, 56, &res_opt_bitmap); + dimage_part(0, 56, &res_opt_bitmap, 0, 0, 96, 8); dupdate(); } + if(img) + { + int width, height; + getwh(img, &width, &height); + + if(x < 0) print(1, 4, "\x01"); + if(x + width > 128) print(21, 4, "\x02"); + + if(img->format & channel_light) + { + if(y < 0) gimage_part(61, 0, &res_opt_bitmap, + 122, 0, 6, 8); + if(y + height > 64) gimage_part(61, 48, + &res_opt_bitmap, 116, 0, 6, 8); + gupdate(); + } + else + { + if(y < 0) dimage_part(61, 0, &res_opt_bitmap, + 122, 0, 6, 8); + if(y + height > 64) dimage_part(61, 48, + &res_opt_bitmap, 116, 0, 6, 8); + dupdate(); + } + } + do { leave = 1; @@ -185,6 +214,7 @@ void test_bopti(void) switch(getkey()) { case KEY_EXIT: + keyboard_setRepeatRate(625, 125); gray_stop(); return; @@ -192,22 +222,14 @@ void test_bopti(void) img = select(img); getxy(img, &x, &y); break; - case KEY_F5: + case KEY_F2: black_bg = !black_bg; break; - case KEY_UP: - y--; - break; - case KEY_DOWN: - y++; - break; - case KEY_LEFT: - x--; - break; - case KEY_RIGHT: - x++; - break; + case KEY_UP: y++; break; + case KEY_DOWN: y--; break; + case KEY_LEFT: x++; break; + case KEY_RIGHT: x--; break; default: leave = 0; @@ -216,6 +238,7 @@ void test_bopti(void) while(!leave); } + keyboard_setRepeatRate(625, 125); gray_stop(); return; } diff --git a/demo/test_gray.c b/demo/test_gray.c index 894aa93..86d7f35 100644 --- a/demo/test_gray.c +++ b/demo/test_gray.c @@ -8,10 +8,9 @@ Lets the user set the gray delays and see the results. */ -static void draw(int delay1, int delay2, int selected) +static void draw(int delay1, int delay2, int selected, int gran) { extern image_t res_opt_gray; - extern font_t res_font_modern; uint32_t *vl = gray_lightVRAM(); uint32_t *vd = gray_darkVRAM(); @@ -25,15 +24,16 @@ static void draw(int delay1, int delay2, int selected) vd[offset] = -(i < 32); } - text_configure(&res_font_modern, color_black); - gtext(13, 17, "light"); - gtext(13, 31, "dark"); + print(2, 3, "light %d", delay1); + print(2, 4, " dark %d", delay2); + print(2, 6, "Mode: \x04%d", gran); - text_configure(NULL, color_black); - print(4, 4, "%d", delay1); - print(4, 6, "%d", delay2); - - locate(3, selected ? 6 : 4, "\x02"); + int lengths[2] = { + 4 - (delay1 < 1000), + 4 - (delay2 < 1000) + }; + print(8, 3 + selected, "\x01"); + print(9 + lengths[selected], 3 + selected, "\x02"); gimage(0, 56, &res_opt_gray); gupdate(); @@ -44,6 +44,7 @@ void test_gray(void) int delays[2]; // { light, dark } int key, changed = 1; int selected = 0; + int gran = 1; gray_getDelays(delays, delays + 1); gray_start(); @@ -53,11 +54,11 @@ void test_gray(void) if(changed) { gray_setDelays(delays[0], delays[1]); - draw(delays[0], delays[1], selected); + draw(delays[0], delays[1], selected, gran); } changed = 0; - key = getkey_opt(getkey_repeat_arrow_keys, 25); + key = getkey_opt(getkey_default, 25); if(key == KEY_EXIT) break; changed = 1; @@ -65,31 +66,33 @@ void test_gray(void) switch(key) { case KEY_F1: - selected = !selected; - break; - case KEY_F2: delays[0] = 912; delays[1] = 1343; break; - case KEY_F3: + case KEY_F2: delays[0] = 993; delays[1] = 1609; break; - case KEY_F4: + case KEY_F3: delays[0] = 860; delays[1] = 1298; break; + case KEY_F5: + if(gran >= 100) gran = 1; + else gran *= 10; case KEY_UP: - delays[selected] += 10; + selected = 0; break; case KEY_DOWN: - if(delays[selected] >= 110) delays[selected] -= 10; - break; - case KEY_RIGHT: - delays[selected]++; + selected = 1; break; case KEY_LEFT: - if(delays[selected] >= 101) delays[selected]--; + if(delays[selected] - gran >= 100) + delays[selected] -= gran; + break; + case KEY_RIGHT: + if(delays[selected] + gran < 10000) + delays[selected] += gran; break; default: changed = 0; diff --git a/gscreen.bmp b/gscreen.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4665fe5e4194ebc789476010d2b7dfa864babd78 GIT binary patch literal 1150 zcmeH_L2DC16vyA|jAqxCx=WW-F<>P181T4f$L~<#5kopazvK6l>RrDZ^@68HgPaZw^cHd)v^PfMn^JeNN7bMtNpn8Qe zp;VMej}6i%M)Twm&ib)4*K_gj>&T9&#u!>lnD2YdaM}#4`rygpg&U*Y=atg=oS)8I zTXp*{tFv>KANFT1uXZ0VS7+xfKO+5RxBseo;9EZb#p2bk%)j&mMkoXdsz>__oqM~VqN+{46x;wMcW@*F2;cV{<4&=XZa)P z+r_eYYyHxd>+H)94q!~S6&!ZUw5C?F&I_Qw2ke!Wz74>$rNrcx7ZbX#hU%UJFzAdE8bz^voW;Kv0wU*eh>I8#sLxg<B<>v%J-Z8i90OO&6OOdIjC2zx-$c8WgqV*}H<}nUcYRSuho`uk j(b^JbOQ9U-Lds_bP(mnyRGRw1s2l_bL=qO(xr;vmxEO@6 literal 0 HcmV?d00001 diff --git a/gscreend.bmp b/gscreend.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2a4f7c913571047f9d0b215096599188b163d2af GIT binary patch literal 1150 zcma)4O=whC6#mY=^JX${CX>uhrvouzz!(yh@{rU{z|w0PTD57bsVxzTJT!zZicZzG z11)lJ<)$DdmLXOg+mNKQF;I{wgC#QBZsuX1?pEO#9PpZ1fz;ovLSv>(c#@d+msN8Vl+^SOrcBS zG&qPY+%|6FT*wY0V{p|D!qIy}wt&R<_@rE@sdHA?b=T8}dB}77Y1ACz!ak=$J5BMt zvkTvuA>)pz85GK?G4(elOtI|D(J5p?^C@GJn|G(A1EqFFl z(pfx2r7C6lheyhpf_{KMxH3pSYTV~{4b$&@i(Wv4|5$Tbjii zc(viI;6uEQtfk$wiYtC%5uf2Q4;WR`Mf}wm`Ar66#_m|owD2RoGTA1Qe9AA?I_7DJ z4>VF25wqmst@wNTwte|vm&>XuPkv5!SoxX7q+_Upadbv*Qiul5V*puU#ceyN8!dT4 z`sj&U7Px?6sf?C+l0lLZRgp#lG7420B-uPmbV+s)k7v;Vi3hyo;QTEzwUn$vt&7k0YG(V|5z9 q{?qt`^Xg<0d*-aJ=VnVS=8JVXd6B8)XVdJ#lxsIGbSYPzedYSwP( zwNR2?S@(&us4GhHltlQdxuFK0K-aG8I2e-uzp6h%eczX}wErXY3jzi!1=9e^ct5Oqjx>v!i4Z49tyC0-}noU-V@AFw|0j7_$wP7S}nX z2T(Wc+|;jd(9{jvFX=m?!;vlY7;VP!^nDB=ZFCzxNg3vG)%(a9N-%l)C>(C(T5rxX zDwjOe9l7svcfo}E8r-u;UC>-@5?RWH1NIceaYTXffd4I0qulTJzgwxtLu8GU)D@CP zu3Iy1aDx|1skgYo%W~vl8z;Q-HrkFirg>JHyM$w`p3`kKxpR->4vryis8_nY!n4Pu zeVb6>(n5NPb47a)2QbMijk>N+;lpstQ)%|B+xpFUgfIAP>aL=U4`h;i%0_6Kj}@JB zuw9yuPWy#^;RKDd$~c2j%g{LTG9gwFwjQd8K`ch4uoAWu)Q(x1y)+A( z#{!@q%~BbeoPN{?CF({Tb}$M-JLTnHqLUJvEyhtAoGjFRd_;ribn8n}uNFu%P9jmX z$7vEr`t2}n0#= 0, it is considered as the absolute location (in + bytes) of the requested data in the file; + - If `whence` == -1, BFile_Read() reads from the current virtual + position in the file. +*/ +int BFile_Read(int handle, void *ram_buffer, int size, int whence); + +#endif // _BFILE_H diff --git a/include/internals/syscalls.h b/include/internals/syscalls.h index ae05b78..594c50b 100644 --- a/include/internals/syscalls.h +++ b/include/internals/syscalls.h @@ -1,6 +1,9 @@ //--- +// // gint core module: syscalls +// // Some of the functionality still requires interacting with the system. +// //--- #ifndef _INTERNALS_SYSCALLS_H diff --git a/include/keyboard.h b/include/keyboard.h index ea5cd7f..4acb419 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -177,6 +177,9 @@ typedef enum // Shorthand for the four previous properties. getkey_repeat_all_keys = 0xf0, + // Default combination of getkey(). + getkey_default = 0x1f, + } getkey_option_t; /* diff --git a/include/modules/interrupts.h b/include/modules/interrupts.h index 54b172e..3ffd9d1 100644 --- a/include/modules/interrupts.h +++ b/include/modules/interrupts.h @@ -1,5 +1,5 @@ -#ifndef _MODULES_INTERRUPTS -#define _MODULES_INTERRUPTS +#ifndef _MODULES_INTERRUPTS_H +#define _MODULES_INTERRUPTS_H #include #include @@ -685,4 +685,4 @@ typedef union // Here's what you'll need to use. extern mod_intc_t INTC; -#endif // _MODULE_INTERRUPTS +#endif // _MODULE_INTERRUPTS_H diff --git a/src/core/syscalls.s b/src/core/syscalls.s index ad7f984..0ebf0d2 100644 --- a/src/core/syscalls.s +++ b/src/core/syscalls.s @@ -22,8 +22,18 @@ /* Return to menu */ .global ___system_menu + /* Storage memory filesystem */ + .global _BFile_Remove + .global _BFile_Create + .global _BFile_Open + .global _BFile_Close + .global _BFile_Write + .global _BFile_Read + +/* Dynamic memory allocation */ + ___malloc: mov.l syscall_table, r2 mov.l 1f, r0 @@ -45,6 +55,8 @@ ___realloc: nop 1: .long 0xe6d +/* OS version access */ + ___get_os_version: mov.l syscall_table, r2 mov.l 1f, r0 @@ -126,6 +138,58 @@ ___system_menu: .memcpy: .long _memcpy +/* BFile driver */ + + +# int BFile_Remove(const uint16_t *file) +_BFile_Remove: + mov.l 1f, r0 + mov.l syscall_table, r1 + jmp @r1 + mov #0, r5 +1: .long 0x0439 + +# int BFile_Create(const uint16_t *file, enum { file = 1, folder = 5 }, +# int *size) +_BFile_Create: + mov.l 1f, r0 + mov.l syscall_table, r1 + jmp @r1 + nop +1: .long 0x0434 + +# int BFile_Open(const uint16_t *file, int mode) +_BFile_Open: + mov.l 1f, r0 + mov.l syscall_table, r1 + jmp @r1 + mov #0, r6 +1: .long 0x042c + +# int BFile_Close(int handle) +_BFile_Close: + mov.l 1f, r0 + mov.l syscall_table, r1 + jmp @r1 + nop +1: .long 0x042d + +# int BFile_Write(int handle, const void *ram_buffer, int even_size) +_BFile_Write: + mov.l 1f, r0 + mov.l syscall_table, r1 + jmp @r1 + nop +1: .long 0x0435 + +# int BFile_Read(int handle, void *ram_buffer, int size, int whence) +_BFile_Read: + mov.l 1f, r0 + mov.l syscall_table, r1 + jmp @r1 + nop +1: .long 0x0432 + .align 4 diff --git a/src/gray/grect.c b/src/gray/grect.c index 0edfb1d..a123d94 100644 --- a/src/gray/grect.c +++ b/src/gray/grect.c @@ -20,7 +20,7 @@ void grect(int x1, int y1, int x2, int y2, color_t operator) // Doing things in this order will be slower, but man, I can't stand // writing that many lines of code for such a simple task. It will be // terribly heavy in the binary file... - while(lvideo > lbase) for(int i = 0; i < 4; i++) switch(operator) + while(lvideo > lbase) for(int i = 3; i >= 0; i--) switch(operator) { case color_white: *--lvideo &= ~masks[i]; diff --git a/src/init/crt0.c b/src/init/crt0.c index 92ccc69..712fd3c 100644 --- a/src/init/crt0.c +++ b/src/init/crt0.c @@ -8,6 +8,7 @@ #include #include #include +#include /* We need some more functionality to generate these */ #ifdef GINT_STARTUP_LOG @@ -15,7 +16,6 @@ #include #include #include - #include #ifndef GINT_NO_SYSCALLS #include @@ -226,7 +226,7 @@ __attribute__((section(".pretext.entry"))) int start(void) init(); #ifdef GINT_STARTUP_LOG - /* Keep this visible for a second or so */ + /* Keep this visible if a key is kept pressed */ keyboard_interrupt(); if(pollevent().type != event_none) getkey(); #endif diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c index f75d086..9b1f38b 100644 --- a/src/keyboard/getkey.c +++ b/src/keyboard/getkey.c @@ -12,17 +12,7 @@ */ int getkey(void) { - return getkey_opt( - getkey_shift_modifier | - getkey_alpha_modifier | - getkey_manage_backlight | -#ifndef GINT_NO_SYSCALLS - getkey_task_switch | -#endif - getkey_repeat_arrow_keys, - - 0 - ); + return getkey_opt(getkey_default, 0); } /* diff --git a/version b/version index 8cc1a27..6b8dc6a 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-581 +beta-0.9-584 From a4b3aaa7daf7a3bddb6eaf82cafb656a0920ca3f Mon Sep 17 00:00:00 2001 From: lephe Date: Tue, 11 Jul 2017 14:02:50 +0200 Subject: [PATCH 19/24] Not enough, apparently. --- Makefile | 8 ++++++-- gscreen.bmp | Bin 1150 -> 0 bytes gscreend.bmp | Bin 1150 -> 0 bytes gscreenl.bmp | Bin 1150 -> 0 bytes 4 files changed, 6 insertions(+), 2 deletions(-) delete mode 100644 gscreen.bmp delete mode 100644 gscreend.bmp delete mode 100644 gscreenl.bmp diff --git a/Makefile b/Makefile index d3804c7..e14989b 100755 --- a/Makefile +++ b/Makefile @@ -47,18 +47,22 @@ demo-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ obj-std-spec = obj-lib-spec = build/display_font_system.bmp.o -# Configuration files +# Configuration files, require them only if we're not cleaning config = gcc.cfg +ifeq "$(filter clean mrproper distclean,$(MAKECMDGOALS))" "" ifeq ($(wildcard $(config)),) $(error "Configuration files are missing. Did you ./configure ?") endif +endif -# Target folder +# Target folder, require it only if we want to install +ifneq "$(filter install,$(MAKECMDGOALS))" "" folder := $(shell fxsdk --folder) ifndef folder $(error "Could not get the fxSDK storage folder. Did you install fxSDK \ properly?") endif +endif diff --git a/gscreen.bmp b/gscreen.bmp deleted file mode 100644 index 4665fe5e4194ebc789476010d2b7dfa864babd78..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmeH_L2DC16vyA|jAqxCx=WW-F<>P181T4f$L~<#5kopazvK6l>RrDZ^@68HgPaZw^cHd)v^PfMn^JeNN7bMtNpn8Qe zp;VMej}6i%M)Twm&ib)4*K_gj>&T9&#u!>lnD2YdaM}#4`rygpg&U*Y=atg=oS)8I zTXp*{tFv>KANFT1uXZ0VS7+xfKO+5RxBseo;9EZb#p2bk%)j&mMkoXdsz>__oqM~VqN+{46x;wMcW@*F2;cV{<4&=XZa)P z+r_eYYyHxd>+H)94q!~S6&!ZUw5C?F&I_Qw2ke!Wz74>$rNrcx7ZbX#hU%UJFzAdE8bz^voW;Kv0wU*eh>I8#sLxg<B<>v%J-Z8i90OO&6OOdIjC2zx-$c8WgqV*}H<}nUcYRSuho`uk j(b^JbOQ9U-Lds_bP(mnyRGRw1s2l_bL=qO(xr;vmxEO@6 diff --git a/gscreend.bmp b/gscreend.bmp deleted file mode 100644 index 2a4f7c913571047f9d0b215096599188b163d2af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcma)4O=whC6#mY=^JX${CX>uhrvouzz!(yh@{rU{z|w0PTD57bsVxzTJT!zZicZzG z11)lJ<)$DdmLXOg+mNKQF;I{wgC#QBZsuX1?pEO#9PpZ1fz;ovLSv>(c#@d+msN8Vl+^SOrcBS zG&qPY+%|6FT*wY0V{p|D!qIy}wt&R<_@rE@sdHA?b=T8}dB}77Y1ACz!ak=$J5BMt zvkTvuA>)pz85GK?G4(elOtI|D(J5p?^C@GJn|G(A1EqFFl z(pfx2r7C6lheyhpf_{KMxH3pSYTV~{4b$&@i(Wv4|5$Tbjii zc(viI;6uEQtfk$wiYtC%5uf2Q4;WR`Mf}wm`Ar66#_m|owD2RoGTA1Qe9AA?I_7DJ z4>VF25wqmst@wNTwte|vm&>XuPkv5!SoxX7q+_Upadbv*Qiul5V*puU#ceyN8!dT4 z`sj&U7Px?6sf?C+l0lLZRgp#lG7420B-uPmbV+s)k7v;Vi3hyo;QTEzwUn$vt&7k0YG(V|5z9 q{?qt`^Xg<0d*-aJ=VnVS=8JVXd6B8)XVdJ#lxsIGbSYPzedYSwP( zwNR2?S@(&us4GhHltlQdxuFK0K-aG8I2e-uzp6h%eczX}wErXY3jzi!1=9e^ct5Oqjx>v!i4Z49tyC0-}noU-V@AFw|0j7_$wP7S}nX z2T(Wc+|;jd(9{jvFX=m?!;vlY7;VP!^nDB=ZFCzxNg3vG)%(a9N-%l)C>(C(T5rxX zDwjOe9l7svcfo}E8r-u;UC>-@5?RWH1NIceaYTXffd4I0qulTJzgwxtLu8GU)D@CP zu3Iy1aDx|1skgYo%W~vl8z;Q-HrkFirg>JHyM$w`p3`kKxpR->4vryis8_nY!n4Pu zeVb6>(n5NPb47a)2QbMijk>N+;lpstQ)%|B+xpFUgfIAP>aL=U4`h;i%0_6Kj}@JB zuw9yuPWy#^;RKDd$~c2j%g{LTG9gwFwjQd8K`ch4uoAWu)Q(x1y)+A( z#{!@q%~BbeoPN{?CF({Tb}$M-JLTnHqLUJvEyhtAoGjFRd_;ribn8n}uNFu%P9jmX z$7vEr`t2}n0# Date: Thu, 13 Jul 2017 21:39:51 +0200 Subject: [PATCH 20/24] Define GINT_VERSION at library link time instead of add-in link time. --- Makefile | 17 ++++++++++------- version | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index e14989b..beabd19 100755 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ target-g1a = gintdemo.g1a # Tools cc = sh3eb-elf-gcc +ld = sh3eb-elf-ld as = sh3eb-elf-as ar = sh3eb-elf-ar ob = sh3eb-elf-objcopy @@ -45,7 +46,7 @@ demo-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ # Specific objects obj-std-spec = -obj-lib-spec = build/display_font_system.bmp.o +obj-lib-spec = build/display_font_system.bmp.o build/version.o # Configuration files, require them only if we're not cleaning config = gcc.cfg @@ -137,9 +138,6 @@ version_letter = $(shell echo -n $(version_type) | sed -r 's/^(.).*/\1/') version_symbol = $(shell printf '0x%02x%01x%01x%04x' "'$(version_letter)'" \ $(version_major) $(version_minor) $(version_build)) -# Tell the linker to define the version symbol. -demo-ldflags += -Wl,--defsym,_GINT_VERSION=$(version_symbol) - #--- @@ -198,9 +196,14 @@ $(foreach mod,$(modules), \ # Specific rules -# Optimizing this one makes the interrupt handler raise illegal slot exception -# on rte; lds.l @r15+, mach. This is totally weird but I haven't understood -# why for now. +# Define the version symbol in a specific object file. ld generates a .stack +# section on sh3eb-elf, which it didn't on x86_64. I don't understand the +# details of why, so I just fall back to removing it afterwards. +build/version.o: + @ echo "_GINT_VERSION = $(version_symbol);" > $@.txt + $(if $(VERBOSE),,@ printf '\e[35;1m ld\e[0m $@\n') + $(if $(VERBOSE),,@) $(ld) -r -R $@.txt -o $@ + $(if $(VERBOSE),,@) $(ob) -R .stack $@ $@ build/display_font_%.bmp.o: src/display/font_%.bmp $(if $(VERBOSE),,@ printf '\e[30;1m fxconv\e[0m -font $<\n') diff --git a/version b/version index 6b8dc6a..3bada75 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-584 +beta-0.9-585 From 1d107a1d64fa2c77218436fcf224920de0434ef7 Mon Sep 17 00:00:00 2001 From: lephe Date: Mon, 9 Apr 2018 08:31:12 +0200 Subject: [PATCH 21/24] Make room for an fx-cg50 compatible version. Enhanced build system. --- .gitignore | 13 +- Makefile | 332 +++------ README.md | 2 + configure | 105 ++- demo/gintdemo.c | 456 ------------ demo/gintdemo.h | 114 --- demo/gintdemo.ld | 113 --- demo/icon.bmp | Bin 1870 -> 0 bytes demo/perf_bopti.c | 179 ----- demo/resources/bopti_thumbs.bmp | Bin 1250 -> 0 bytes demo/resources/clock_7305.bmp | Bin 12410 -> 0 bytes demo/resources/clock_7705.bmp | Bin 12410 -> 0 bytes demo/resources/clock_chars.bmp | Bin 1082 -> 0 bytes demo/resources/font_modern.bmp | Bin 24698 -> 0 bytes demo/resources/isometric.bmp | Bin 4134 -> 0 bytes demo/resources/items.bmp | Bin 99322 -> 0 bytes demo/resources/opt_bitmap.bmp | Bin 3194 -> 0 bytes demo/resources/opt_gray.bmp | Bin 3194 -> 0 bytes demo/resources/opt_menu.bmp | Bin 3194 -> 0 bytes demo/resources/opt_rtc.bmp | Bin 20474 -> 0 bytes demo/resources/opt_tales.bmp | Bin 3194 -> 0 bytes demo/resources/opt_timer.bmp | Bin 3194 -> 0 bytes demo/resources/rtc_segments.bmp | Bin 6962 -> 0 bytes demo/resources/screen.bmp | Bin 13570 -> 0 bytes demo/resources/sprites.bmp | Bin 8850 -> 0 bytes demo/resources/swords.bmp | Bin 5770 -> 0 bytes demo/resources/town.bmp | Bin 105770 -> 0 bytes demo/resources/zelda.bmp | Bin 72922 -> 0 bytes demo/test_bopti.c | 244 ------ demo/test_gray.c | 104 --- demo/test_keyboard.c | 195 ----- demo/test_rtc.c | 260 ------- demo/test_tales.c | 158 ---- demo/test_timer.c | 273 ------- include/7305.h | 673 ----------------- include/7705.h | 1099 ---------------------------- include/alloca.h | 25 - include/bfile.h | 74 -- include/bopti.h | 73 -- include/clock.h | 121 --- include/ctype.h | 35 - include/display.h | 156 ---- include/events.h | 113 --- include/extended/endian.h | 47 -- include/extended/inttypes.h | 116 --- include/gint.h | 251 ------- include/gray.h | 139 ---- include/internals/bopti.h | 162 ---- include/internals/clock.h | 21 - include/internals/display.h | 49 -- include/internals/events.h | 19 - include/internals/exceptions.h | 66 -- include/internals/gint.h | 139 ---- include/internals/init.h | 29 - include/internals/interrupt_maps.h | 100 --- include/internals/interrupts.h | 18 - include/internals/keyboard.h | 49 -- include/internals/mmu.h | 21 - include/internals/modules.h | 12 - include/internals/rtc.h | 41 -- include/internals/stdio.h | 23 - include/internals/syscalls.h | 29 - include/internals/tales.h | 50 -- include/internals/time.h | 16 - include/internals/timer.h | 76 -- include/keyboard.h | 329 --------- include/math.h | 41 -- include/modules/interrupts.h | 688 ----------------- include/modules/macros.h | 57 -- include/modules/rtc.h | 131 ---- include/modules/timer.h | 64 -- include/mpu.h | 71 -- include/rtc.h | 105 --- include/screen.h | 35 - include/setjmp.h | 37 - include/stdio.h | 44 -- include/stdlib.h | 149 ---- include/string.h | 94 --- include/tales.h | 128 ---- include/time.h | 126 ---- include/timer.h | 177 ----- src/bopti/bopti_internals.c | 331 --------- src/bopti/dimage.c | 73 -- src/bopti/gimage.c | 76 -- src/clock/clock.c | 272 ------- src/core/exceptions.c | 172 ----- src/core/gint.c | 180 ----- src/core/gint_sh7305.c | 136 ---- src/core/gint_sh7705.c | 130 ---- src/core/gint_vbr.s | 66 -- src/core/init_quit.c | 130 ---- src/core/interrupt_maps_7305.c | 53 -- src/core/interrupt_maps_7705.c | 65 -- src/core/interrupts.c | 21 - src/core/modules.c | 91 --- src/core/mpu.c | 70 -- src/core/syscalls.s | 198 ----- src/core/vbr_space.c | 98 --- src/ctype/ctype_classes.c | 43 -- src/ctype/ctype_functions.c | 73 -- src/display/adjustRectangle.c | 29 - src/display/dclear.c | 32 - src/display/display_vram.c | 47 -- src/display/dline.c | 134 ---- src/display/dpixel.c | 25 - src/display/drect.c | 59 -- src/display/dupdate.c | 12 - src/display/font_system.bmp | Bin 32762 -> 0 bytes src/display/getMasks.c | 33 - src/events/event_get.c | 38 - src/events/event_push.c | 26 - src/gray/gclear.c | 33 - src/gray/gline.c | 25 - src/gray/gpixel.c | 59 -- src/gray/gray_engine.c | 197 ----- src/gray/grect.c | 70 -- src/init/crt0.c | 306 -------- src/init/util.c | 73 -- src/keyboard/getPressedKeys.c | 53 -- src/keyboard/getkey.c | 123 ---- src/keyboard/key_char.c | 36 - src/keyboard/key_id.c | 19 - src/keyboard/key_type.c | 24 - src/keyboard/keyboard_core.c | 178 ----- src/keyboard/keyboard_sh7305.c | 146 ---- src/keyboard/keyboard_sh7705.c | 135 ---- src/math/math_qdiv.c | 23 - src/mmu/pseudoTLBInit.c | 29 - src/rtc/rtc_callback.c | 152 ---- src/rtc/rtc_getTime.c | 42 -- src/rtc/rtc_interrupt.c | 15 - src/rtc/rtc_setTime.c | 44 -- src/screen/screen_backlight.c | 41 -- src/screen/screen_display.c | 55 -- src/setjmp/setjmp.s | 76 -- src/stdio/snprintf.c | 19 - src/stdio/sprintf.c | 19 - src/stdio/stdio_format.c | 895 ---------------------- src/stdio/vsnprintf.c | 15 - src/stdio/vsprintf.c | 15 - src/stdlib/calloc.c | 13 - src/stdlib/free.c | 23 - src/stdlib/malloc.c | 27 - src/stdlib/realloc.c | 25 - src/stdlib/stdlib_abs.c | 13 - src/stdlib/stdlib_div.c | 29 - src/stdlib/stdlib_rand.c | 19 - src/string/memchr.c | 20 - src/string/memcmp.c | 84 --- src/string/memcpy.c | 76 -- src/string/memset.c | 48 -- src/string/strchr.c | 11 - src/string/strcmp.c | 18 - src/string/strcpy.c | 11 - src/string/strlen.c | 12 - src/string/strncpy.c | 20 - src/string/strnlen.c | 13 - src/tales/dprint.c | 18 - src/tales/dtext.c | 11 - src/tales/gprint.c | 18 - src/tales/gtext.c | 11 - src/tales/tales_configuration.c | 15 - src/tales/tales_gray.c | 67 -- src/tales/tales_internals.c | 283 ------- src/tales/text_length.c | 21 - src/time/asctime.c | 57 -- src/time/ctime.c | 13 - src/time/gmtime.c | 45 -- src/time/mktime.c | 68 -- src/time/time.c | 30 - src/time/time_misc.c | 21 - src/time/time_util.c | 28 - src/timer/common_api.c | 112 --- src/timer/hardware_timers.c | 63 -- src/timer/virtual_timers.c | 241 ------ version | 1 - 176 files changed, 166 insertions(+), 15715 deletions(-) delete mode 100644 demo/gintdemo.c delete mode 100644 demo/gintdemo.h delete mode 100644 demo/gintdemo.ld delete mode 100644 demo/icon.bmp delete mode 100644 demo/perf_bopti.c delete mode 100644 demo/resources/bopti_thumbs.bmp delete mode 100644 demo/resources/clock_7305.bmp delete mode 100644 demo/resources/clock_7705.bmp delete mode 100644 demo/resources/clock_chars.bmp delete mode 100644 demo/resources/font_modern.bmp delete mode 100644 demo/resources/isometric.bmp delete mode 100644 demo/resources/items.bmp delete mode 100644 demo/resources/opt_bitmap.bmp delete mode 100644 demo/resources/opt_gray.bmp delete mode 100644 demo/resources/opt_menu.bmp delete mode 100644 demo/resources/opt_rtc.bmp delete mode 100644 demo/resources/opt_tales.bmp delete mode 100644 demo/resources/opt_timer.bmp delete mode 100644 demo/resources/rtc_segments.bmp delete mode 100644 demo/resources/screen.bmp delete mode 100644 demo/resources/sprites.bmp delete mode 100644 demo/resources/swords.bmp delete mode 100644 demo/resources/town.bmp delete mode 100644 demo/resources/zelda.bmp delete mode 100644 demo/test_bopti.c delete mode 100644 demo/test_gray.c delete mode 100644 demo/test_keyboard.c delete mode 100644 demo/test_rtc.c delete mode 100644 demo/test_tales.c delete mode 100644 demo/test_timer.c delete mode 100644 include/7305.h delete mode 100644 include/7705.h delete mode 100644 include/alloca.h delete mode 100644 include/bfile.h delete mode 100644 include/bopti.h delete mode 100644 include/clock.h delete mode 100644 include/ctype.h delete mode 100644 include/display.h delete mode 100644 include/events.h delete mode 100644 include/extended/endian.h delete mode 100644 include/extended/inttypes.h delete mode 100644 include/gint.h delete mode 100644 include/gray.h delete mode 100644 include/internals/bopti.h delete mode 100644 include/internals/clock.h delete mode 100644 include/internals/display.h delete mode 100644 include/internals/events.h delete mode 100644 include/internals/exceptions.h delete mode 100644 include/internals/gint.h delete mode 100644 include/internals/init.h delete mode 100644 include/internals/interrupt_maps.h delete mode 100644 include/internals/interrupts.h delete mode 100644 include/internals/keyboard.h delete mode 100644 include/internals/mmu.h delete mode 100644 include/internals/modules.h delete mode 100644 include/internals/rtc.h delete mode 100644 include/internals/stdio.h delete mode 100644 include/internals/syscalls.h delete mode 100644 include/internals/tales.h delete mode 100644 include/internals/time.h delete mode 100644 include/internals/timer.h delete mode 100644 include/keyboard.h delete mode 100644 include/math.h delete mode 100644 include/modules/interrupts.h delete mode 100644 include/modules/macros.h delete mode 100644 include/modules/rtc.h delete mode 100644 include/modules/timer.h delete mode 100644 include/mpu.h delete mode 100644 include/rtc.h delete mode 100644 include/screen.h delete mode 100644 include/setjmp.h delete mode 100644 include/stdio.h delete mode 100644 include/stdlib.h delete mode 100644 include/string.h delete mode 100644 include/tales.h delete mode 100644 include/time.h delete mode 100644 include/timer.h delete mode 100644 src/bopti/bopti_internals.c delete mode 100644 src/bopti/dimage.c delete mode 100644 src/bopti/gimage.c delete mode 100644 src/clock/clock.c delete mode 100644 src/core/exceptions.c delete mode 100644 src/core/gint.c delete mode 100644 src/core/gint_sh7305.c delete mode 100644 src/core/gint_sh7705.c delete mode 100644 src/core/gint_vbr.s delete mode 100644 src/core/init_quit.c delete mode 100644 src/core/interrupt_maps_7305.c delete mode 100644 src/core/interrupt_maps_7705.c delete mode 100644 src/core/interrupts.c delete mode 100644 src/core/modules.c delete mode 100644 src/core/mpu.c delete mode 100644 src/core/syscalls.s delete mode 100644 src/core/vbr_space.c delete mode 100644 src/ctype/ctype_classes.c delete mode 100644 src/ctype/ctype_functions.c delete mode 100644 src/display/adjustRectangle.c delete mode 100644 src/display/dclear.c delete mode 100644 src/display/display_vram.c delete mode 100644 src/display/dline.c delete mode 100644 src/display/dpixel.c delete mode 100644 src/display/drect.c delete mode 100644 src/display/dupdate.c delete mode 100644 src/display/font_system.bmp delete mode 100644 src/display/getMasks.c delete mode 100644 src/events/event_get.c delete mode 100644 src/events/event_push.c delete mode 100644 src/gray/gclear.c delete mode 100644 src/gray/gline.c delete mode 100644 src/gray/gpixel.c delete mode 100644 src/gray/gray_engine.c delete mode 100644 src/gray/grect.c delete mode 100644 src/init/crt0.c delete mode 100644 src/init/util.c delete mode 100644 src/keyboard/getPressedKeys.c delete mode 100644 src/keyboard/getkey.c delete mode 100644 src/keyboard/key_char.c delete mode 100644 src/keyboard/key_id.c delete mode 100644 src/keyboard/key_type.c delete mode 100644 src/keyboard/keyboard_core.c delete mode 100644 src/keyboard/keyboard_sh7305.c delete mode 100644 src/keyboard/keyboard_sh7705.c delete mode 100644 src/math/math_qdiv.c delete mode 100644 src/mmu/pseudoTLBInit.c delete mode 100644 src/rtc/rtc_callback.c delete mode 100644 src/rtc/rtc_getTime.c delete mode 100644 src/rtc/rtc_interrupt.c delete mode 100644 src/rtc/rtc_setTime.c delete mode 100644 src/screen/screen_backlight.c delete mode 100644 src/screen/screen_display.c delete mode 100644 src/setjmp/setjmp.s delete mode 100644 src/stdio/snprintf.c delete mode 100644 src/stdio/sprintf.c delete mode 100644 src/stdio/stdio_format.c delete mode 100644 src/stdio/vsnprintf.c delete mode 100644 src/stdio/vsprintf.c delete mode 100644 src/stdlib/calloc.c delete mode 100644 src/stdlib/free.c delete mode 100644 src/stdlib/malloc.c delete mode 100644 src/stdlib/realloc.c delete mode 100644 src/stdlib/stdlib_abs.c delete mode 100644 src/stdlib/stdlib_div.c delete mode 100644 src/stdlib/stdlib_rand.c delete mode 100644 src/string/memchr.c delete mode 100644 src/string/memcmp.c delete mode 100644 src/string/memcpy.c delete mode 100644 src/string/memset.c delete mode 100644 src/string/strchr.c delete mode 100644 src/string/strcmp.c delete mode 100644 src/string/strcpy.c delete mode 100644 src/string/strlen.c delete mode 100644 src/string/strncpy.c delete mode 100644 src/string/strnlen.c delete mode 100644 src/tales/dprint.c delete mode 100644 src/tales/dtext.c delete mode 100644 src/tales/gprint.c delete mode 100644 src/tales/gtext.c delete mode 100644 src/tales/tales_configuration.c delete mode 100644 src/tales/tales_gray.c delete mode 100644 src/tales/tales_internals.c delete mode 100644 src/tales/text_length.c delete mode 100644 src/time/asctime.c delete mode 100644 src/time/ctime.c delete mode 100644 src/time/gmtime.c delete mode 100644 src/time/mktime.c delete mode 100644 src/time/time.c delete mode 100644 src/time/time_misc.c delete mode 100644 src/time/time_util.c delete mode 100644 src/timer/common_api.c delete mode 100644 src/timer/hardware_timers.c delete mode 100644 src/timer/virtual_timers.c delete mode 100644 version diff --git a/.gitignore b/.gitignore index 9a73a45..5b8a11e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,19 +5,12 @@ build/** *.sublime-project *.sublime-workspace -# Object files. -*.o - -# Some notes. +# Lots of unordered project notes notes/** # Output files -libc.a -libgint.a -gintdemo.g1a -gintdbg.g1a +bin/** # Configuration files -gcc.cfg -Makefile.cfg +config/** diff --git a/Makefile b/Makefile index beabd19..f577bc5 100755 --- a/Makefile +++ b/Makefile @@ -4,267 +4,125 @@ # #--- -include Makefile.cfg +# +# Build configuration +# -#--- -# Project variables -#--- +# Some rules don't care if config files are missing (eg. clean, etc) +cfg := config/Makefile.cfg +-include $(cfg) -# Modules -modules-gint = bopti clock core display events gray init keyboard mmu rtc \ - screen tales timer -modules-libc = ctype math setjmp stdio stdlib string time +# Those that do may use this special dependency +cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg)) -# Targets -target-lib = libgint.a -target-std = libc.a -target-g1a = gintdemo.g1a +# Compiler flags, assembler flags, dependency generation, archiving +cflags := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -O2 \ + -I include $(cfg_defs) +dflags = -MMD -MT $@ -MF $(@:.o=.d) -MP +arflags := -# Tools -cc = sh3eb-elf-gcc -ld = sh3eb-elf-ld -as = sh3eb-elf-as -ar = sh3eb-elf-ar -ob = sh3eb-elf-objcopy -wr = g1a-wrapper +# +# File listings +# -# Flags for gint -lib-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ - -Wall -Wextra @gcc.cfg -g0 +# Target file +target := bin/libgint.a -# Demo application (could be done better) -demo-src = $(notdir $(wildcard demo/*.[cs])) -demo-dep = $(wildcard demo/*.h) -demo-icon = demo/icon.bmp -demo-res = $(notdir $(wildcard demo/resources/*)) -demo-obj = $(patsubst %,build/demo_%.o,$(demo-src) $(demo-res)) -demo-elf = build/gintdemo.elf -demo-bin = build/gintdemo.bin -demo-ldflags = $(demo-cflags) -T demo/gintdemo.ld -L. -lgint -lc -lgcc -demo-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ - -Wall -Wextra +# Automatic names for object and dependency files +src2obj = build/$(1:src/%=%).o +src2dep = build/$(1:src/%=%).d -# Specific objects -obj-std-spec = -obj-lib-spec = build/display_font_system.bmp.o build/version.o +# Source files +src := $(shell find src -name '*.[cs]') +src_obj := $(foreach s,$(src),$(call src2obj,$s)) -# Configuration files, require them only if we're not cleaning -config = gcc.cfg -ifeq "$(filter clean mrproper distclean,$(MAKECMDGOALS))" "" -ifeq ($(wildcard $(config)),) -$(error "Configuration files are missing. Did you ./configure ?") -endif -endif +# Files with special handling +spe := src/display/font.bmp +spe_obj := $(foreach s,$(spe),$(call src2obj,$s)) -# Target folder, require it only if we want to install -ifneq "$(filter install,$(MAKECMDGOALS))" "" -folder := $(shell fxsdk --folder) -ifndef folder -$(error "Could not get the fxSDK storage folder. Did you install fxSDK \ -properly?") -endif -endif +# All object files +obj := $(src_obj) $(spe_obj) +# +# Toolchain +# +gcc = sh3eb-elf-gcc +as = sh3eb-elf-as +ld = sh3eb-elf-ld +ar = sh3eb-elf-ar +conv = fxconv -#--- -# Automatic variables -#--- +# +# Build rules +# -# Modules are subfolders of src/. -modules = $(modules-gint) $(modules-libc) +all: $(target) -define n -# This is a newline character. +$(target): $(obj) | $(dir $(target)) + $(call cmd_l,ar,$@) $(ar) -crs $(arflags) $@ $(obj) -endef +# Assembler sources +build/%.s.o: src/%.s build/%.s.d + @ mkdir -p $(dir $@) + $(call cmd_b,as,$<) $(as) -c $< -o $@ $(sflags) -# Module-scope variables. -$(foreach mod, $(modules), $(eval \ - mod-$(mod)-c = $(notdir $(wildcard src/$(mod)/*.c)) $n\ - mod-$(mod)-asm = $(notdir $(wildcard src/$(mod)/*.s)) $n\ - mod-$(mod)-src = $$(mod-$(mod)-c)$$(mod-$(mod)-asm) $n\ - mod-$(mod)-obj = $$(patsubst %,build/$(mod)_%.o,$$(mod-$(mod)-src)) \ -)) +# C sources +build/%.c.o: src/%.c build/%.c.d $(cfgdep) + @ mkdir -p $(dir $@) + $(call cmd_b,gcc,$<) $(gcc) -c $< -o $@ $(dflags) $(cflags) -# Target-scope variables. -obj-std = $(foreach mod,$(modules-libc),$(mod-$(mod)-obj)) $(obj-std-spec) -obj-lib = $(foreach mod,$(modules-gint),$(mod-$(mod)-obj)) $(obj-lib-spec) +# Special files +$(call src2obj,src/display/font.bmp): src/display/font.bmp + @ mkdir -p $(dir $@) + $(call cmd_m,fxconv,$<) $(conv) -font $< -n gint_font -o $@ -# Dependencies. -hdr-dep = $(wildcard include/*.h include/*/*.h) - - - -#--- -# Rule templates -#--- - -# C source file template: -# $1 module name -# $2 filename -# $3 dependencies -define rule-c-source -build/$1_$2.o: src/$1/$2 $3 $(config) - $(if $(VERBOSE),,@ printf '\e[34;1m gcc\e[0m $$<\n') - $(if $(VERBOSE),,@) $(cc) -c $$< -o $$@ $(lib-cflags) -endef - -# Asm source file template: -# $1 module name -# $2 filename -define rule-asm-source -build/$1_$2.o: src/$1/$2 $(config) - $(if $(VERBOSE),,@ printf '\e[34;1m as\e[0m $$<\n') - $(if $(VERBOSE),,@) $(as) -c $$< -o $$@ -endef - - - -#--- -# Version management -#--- - -# Retrieve version information. -version_string = $(shell cat version | sed 's/[-.]/ /g') -version_type = $(word 1,$(version_string)) -version_major = $(word 2,$(version_string)) -version_minor = $(word 3,$(version_string)) -version_build = $(word 4,$(version_string)) - -# Make up the new version integer. -version_build_n = $(shell echo $$(($(version_build) + 1))) -version_letter = $(shell echo -n $(version_type) | sed -r 's/^(.).*/\1/') -version_symbol = $(shell printf '0x%02x%01x%01x%04x' "'$(version_letter)'" \ - $(version_major) $(version_minor) $(version_build)) - - - -#--- -# Building -#--- - -# Generic rules - -all-lib: $(config) $(target-std) $(target-lib) - -all: $(config) $(target-std) $(target-lib) $(target-g1a) - -build: - $(if $(VERBOSE),,@ printf '\e[35;1m mkdir\e[0m $@\n') - $(if $(VERBOSE),,@) mkdir -p $@ - -version: src include - @ echo '$(version_type)-$(version_major).$(version_minor)-$(version_build_n)' > $@ - - -$(obj-std) $(obj-lib) $(demo-obj): | build - -$(target-std): $(obj-std) version - $(if $(VERBOSE),,@ printf '\e[35;1m ar\e[0m ar $@\n') - $(if $(VERBOSE),,@) $(ar) rcs $@ $(obj-std) - @ printf '\n\e[32;1m\u00bb\e[0m Succesfully built libc (' - @ printf $$(stat -c %s $@) - @ printf ' bytes)\n\n' - -$(target-lib): $(config) $(target-std) $(obj-lib) version - $(if $(VERBOSE),,@ printf '\e[35;1m ar\e[0m $@\n') - $(if $(VERBOSE),,@) $(ar) rcs $@ $(obj-lib) - @ printf '\n\e[32;1m\u00bb\e[0m Succesfully built libgint (' - @ printf $$(stat -c %s $@) - @ printf ' bytes)\n\n' - -$(target-g1a): $(config) $(target-std) $(target-lib) $(demo-obj) - $(if $(VERBOSE),,@ printf '\e[35;1m ld\e[0m $(demo-elf)\n') - $(if $(VERBOSE),,@) $(cc) -o $(demo-elf) $(demo-obj) $(demo-ldflags) - $(if $(VERBOSE),,@ printf '\e[35;1mobjcopy\e[0m $(demo-bin)\n') - $(if $(VERBOSE),,@) $(ob) -R .comment -R .bss -O binary $(demo-elf) $(demo-bin) - $(if $(VERBOSE),,@ printf '\e[35;1m fxg1a\e[0m $@\n') - $(if $(VERBOSE),,@) $(wr) $(demo-bin) -o $@ -i $(demo-icon) - @ printf '\n\e[32;1m\u00bb\e[0m Succesfully built demo application (' - @ printf $$(stat -c %s $@) - @ printf ' bytes)\n\n' - -# Automated rules - -$(foreach mod,$(modules), \ - $(foreach source,$(mod-$(mod)-c), $(eval \ - $(call rule-c-source,$(mod),$(source),$(hdr-dep)))) \ - $(foreach source,$(mod-$(mod)-asm), $(eval \ - $(call rule-asm-source,$(mod),$(source)))) \ -) - -# Specific rules - -# Define the version symbol in a specific object file. ld generates a .stack -# section on sh3eb-elf, which it didn't on x86_64. I don't understand the -# details of why, so I just fall back to removing it afterwards. -build/version.o: - @ echo "_GINT_VERSION = $(version_symbol);" > $@.txt - $(if $(VERBOSE),,@ printf '\e[35;1m ld\e[0m $@\n') - $(if $(VERBOSE),,@) $(ld) -r -R $@.txt -o $@ - $(if $(VERBOSE),,@) $(ob) -R .stack $@ $@ - -build/display_font_%.bmp.o: src/display/font_%.bmp - $(if $(VERBOSE),,@ printf '\e[30;1m fxconv\e[0m -font $<\n') - $(if $(VERBOSE),,@) fxconv -font $< -o $@ -font -n $(<:src/display/font_%.bmp=gint_font_%) - -# Demo application - -build/demo_%.c.o: demo/%.c $(hdr-dep) $(demo-dep) $(config) - $(if $(VERBOSE),,@ printf '\e[34;1m gcc\e[0m $<\n') - $(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(demo-cflags) - -build/demo_%.s.o: demo/%.s $(config) - $(if $(VERBOSE),,@ printf '\e[34;1m as\e[0m $<\n') - $(if $(VERBOSE),,@) $(as) -c $< -o $@ - -build/demo_font_%.bmp.o: demo/resources/font_%.bmp - $(if $(VERBOSE),,@ printf '\e[30;1m fxconv\e[0m -font $<\n') - $(if $(VERBOSE),,@) fxconv -font $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) - -build/demo_%.bmp.o: demo/resources/%.bmp - $(if $(VERBOSE),,@ printf '\e[30;1m fxconv\e[0m -image $<\n') - $(if $(VERBOSE),,@) fxconv -image $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) - - - -#--- -# Cleaning and install -#--- +# +# Cleaning +# clean: - @ rm -rf build + @ rm -rf build/ +distclean: clean + @ rm -rf bin/ + @ rm -f $(cfg) -mrproper: clean - @ rm -f $(target-g1a) $(target-lib) $(target-std) - @ rm -f $(config) +# +# Utilities +# -distclean: mrproper +# Evaluated when a rule requires the configuration file but it doesn't exist +CFGDEP: + @ echo "Configuration file $(cfg) is missing. Have you configured?" + @ echo "See \`./configure --help\` for details." + @ false -install: $(target-std) $(target-lib) - $(if $(VERBOSE),,@ printf '\e[35;1m mkdir\e[0m $(folder)\n') - $(if $(VERBOSE),,@) mkdir -p $(folder) +# Make directories: make conveniently leaves a '/' at the end of $(dir ...) +%/: + @ mkdir -p $@ +# Don't try to unlink directories once they're built (that wouldn't work =p) +.PRECIOUS: %/ - $(if $(VERBOSE),,@ printf '\e[33;1minstall\e[0m 644 $^\n') - $(if $(VERBOSE),,@) install -m 644 $^ $(folder) - $(if $(VERBOSE),,@ printf '\e[33;1minstall\e[0m 644 demo/gintdemo.ld\n') - $(if $(VERBOSE),,@) install -m 644 -T demo/gintdemo.ld $(folder)/linker.ld +# Dependency information +-include $(shell [ -d build ] && find build -name *.d) +build/%.d: ; +.PRECIOUS: build/%.d - $(if $(VERBOSE),,@ printf '\e[35;1m mkdir\e[0m $(folder)/gint/modules\n') - $(if $(VERBOSE),,@) mkdir -p $(folder)/gint/modules +# Do not output full commands by default +VERBOSE ?= - $(if $(VERBOSE),,@ printf '\e[33;1minstall\e[0m 644 include/**.h\n') - $(if $(VERBOSE),,@) install -m 644 include/*.h $(folder)/gint - $(if $(VERBOSE),,@) install -m 644 include/modules/*.h $(folder)/gint/modules -ifdef config_ext - $(if $(VERBOSE),,@) install -m 644 include/extended/*.h $(folder)/gint -endif - @ printf "\n\033[32;1m\u00bb\033[0m Successfully installed gint\n\n" +# Simple command output method +# $1 Program name +# $2 Argument string to display +# $3 Command color +define cmd +@ echo -e "\e[""$3"";1m>\e[0;1m $1\e[0m $2" +$(if $(VERBOSE),,@) +endef -install-demo: all - p7 send -f $(target-g1a) - -.PHONY: all-lib all help -.PHONY: clean mrproper distclean -.PHONY: install install-demo +# Some pre-colored command kinds: misc, build, link, clean, install +cmd_m = $(call cmd,$1,$2,30) +cmd_b = $(call cmd,$1,$2,32) +cmd_l = $(call cmd,$1,$2,36) +cmd_c = $(call cmd,$1,$2,31) +cmd_i = $(call cmd,$1,$2,33) diff --git a/README.md b/README.md index e6f748b..d5b21b9 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ target) and the [fxSDK](http://git.planet-casio.com/lephe/fxsdk). gint is free software: you may use it for any purpose, share it, modify it and share your changes. No credit of any kind is required, though appreciated. +**TODO: Update this file for everything related to project organization** + Programming interface --------------------- diff --git a/configure b/configure index bb6ae65..e99fe35 100755 --- a/configure +++ b/configure @@ -6,8 +6,11 @@ declare -A conf +# Target platform +conf_target= + # Behavior -conf[GINT_STARTUP_LOG]= +conf[GINT_BOOT_LOG]= conf[GINT_NO_SYSCALLS]= conf[GINT_EXTENDED_LIBC]= conf[GINT_STATIC_GRAY]= @@ -18,45 +21,50 @@ conf[TIMER_SLOTS]=16 conf[EVENTS_QUEUE_SIZE]=64 # Output files -output_gcc="gcc.cfg" -output_make="Makefile.cfg" +output="config/Makefile.cfg" # # Help screen and output util # error="\e[31;1merror:\e[0m" -Cg="$(tput setaf 8)$(tput bold)" -Cr="$(tput setaf 1)$(tput bold)" -Cy="$(tput setaf 2)$(tput bold)" -Cp="$(tput setaf 5)$(tput setaf 62)$(tput bold)" -C0="$(tput setaf 0)$(tput sgr 0)" +C_="$(echo -e '\e[30;1m')" +Cr="$(echo -e '\e[31;1m')" +Cg="$(echo -e '\e[32;1m')" +Cp="$(echo -e '\e[34;1m')" +C0="$(echo -e '\e[0m')" help() { cat << EOF Configuration script for the gint library. +Usage: $0 [options...] + +Required settings: + $Cr--target$C0=${Cg}fx9860g$C0,${Cg}fxcg50$C0 + Select the target platform, either ${Cg}fx9860g$C0 for monochrome + calculators, or ${Cg}fxcg50$C0 for Prizm calculators. Options that affect the behavior of the library: - $Cr--startup-log $Cg[default:$Cp false$Cg]$C0 - Enable a on-screen log at startup if a key is kept pressed while launching + $Cr--boot-log $C_[default:$Cp not specified$C_]$C0 + Enable an on-screen log at startup if a key is kept pressed while launching the add-in, allowing easy debug and crash diagnoses. - $Cr--no-syscalls $Cg[default:$Cp false$Cg]$C0 + $Cr--no-syscalls $C_[default:$Cp not specified$C_]$C0 Never use syscalls. Expect trouble with malloc() and the gray engine. Do not trigger this switch unless you know what you are doing. - $Cr--extended-libc $Cg[default:$Cp false$Cg]$C0 + $Cr--extended-libc $C_[default:$Cp not specified$C_]$C0 Enable specific C99 headers/features that are normally not required by calculator programs. This may allow porting programs from other platforms. - $Cr--static-gray-engine $Cg[default:$Cp false$Cg]$C0 + $Cr--static-gray-engine $C_[default:$Cp not specified$C_]$C0 Place the gray engine vram in static ram instead of using the heap. Always use this option when using both the gray engine and --no-syscalls. Options that customize size limits: - $Cr--atexit-max=$Cy$Cg [default:$Cp 16$Cg]$C0 + $Cr--atexit-max$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0 Number of exit handlers that can be registered by atexit(). - $Cr--timer-slots=$Cy$Cg [default:$Cp 16$Cg]$C0 + $Cr--timer-slots$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0 Number of virtual timers that may be registered at the same time. - $Cr--events-queue-size=$Cy$Cg [default:$Cp 64$Cg]$C0 + $Cr--events-queue-size$C0=${Cg}integer$C_ [default:$Cp 64$C_]$C0 Number of events simultaneously stored in the event queue. EOF @@ -69,8 +77,20 @@ EOF fail=false for arg; do case "$arg" in - -h | --help) help;; - --startup-log) conf[GINT_STARTUP_LOG]=true;; + -h | -? | --help) help;; + + --target=*) + conf_target=${arg#*=} + if [[ $conf_target = "fx9860g" ]]; then + conf_target="GINT_FX9860G" + else if [[ $conf_target = "fxcg50" ]]; then + conf_target="GINT_FXCG50" + else + echo -e "$error Invalid target. See $0 --help." + fail=true; + fi; fi;; + + --boot-log) conf[GINT_BOOT_LOG]=true;; --no-syscalls) conf[GINT_NO_SYSCALLS]=true;; --extended-libc) conf[GINT_EXTENDED_LIBC]=true;; --static-gray-engine) conf[GINT_STATIC_GRAY]=true;; @@ -103,34 +123,41 @@ for arg; do case "$arg" in esac; done # -# Output config routines +# Checking mandatory arguments # -output_config_gcc() -{ - [ "${conf[GINT_STARTUP_LOG]}" != "" ] && echo "-D GINT_STARTUP_LOG" - [ "${conf[GINT_NO_SYSCALLS]}" != "" ] && echo "-D GINT_NO_SYSCALLS" - [ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "-D GINT_EXTENDED_LIBC" - [ "${conf[GINT_STATIC_GRAY]}" != "" ] && echo "-D GINT_STATIC_GRAY" - - echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}" - echo "-D TIMER_SLOTS=${conf[TIMER_SLOTS]}" - echo "-D EVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}" -} - -output_config_make() -{ - [ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "config_ext=true" -} +if [[ ! $conf_target ]]; then + echo -e "$error No target specified. See $0 --help." + fail=true; +fi # # Output config # +output_config() +{ + echo -n "cfg_defs =" + echo -n " -D$conf_target" + + [ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG" + [ "${conf[GINT_NO_SYSCALLS]}" ] && echo -n " -DGINT_NO_SYSCALLS" + [ "${conf[GINT_EXTENDED_LIBC]}" ] && echo -n " -DGINT_EXTENDED_LIBC" + [ "${conf[GINT_STATIC_GRAY]}" ] && echo -n " -DGINT_STATIC_GRAY" + + echo -n " -DATEXIT_MAX=${conf[ATEXIT_MAX]}" + echo -n " -DTIMER_SLOTS=${conf[TIMER_SLOTS]}" + echo -n " -DEVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}" + + echo "" + + [ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "cfg_ext = true" +} + if $fail; then - echo "Configuration has not been modified." + echo "Output file $output has not been modified." else - output_config_gcc > $output_gcc - output_config_make > $output_make - echo "Configuration saved in $output_gcc and $output_make." + mkdir -p config + output_config > $output + echo "Configuration saved in $output, ready to make!" fi diff --git a/demo/gintdemo.c b/demo/gintdemo.c deleted file mode 100644 index 16ffaf5..0000000 --- a/demo/gintdemo.c +++ /dev/null @@ -1,456 +0,0 @@ -#include "gintdemo.h" - -#include -#include -#include -#include -#include - -#include -#include - - - -//--- -// A few procedures for displaying text aligned on a 21*8 grid. -// Not really beautiful... but this will do. -//--- - -void locate(int x, int y, const char *str) -{ - if(x < 1 || x > 21 || y < 1 || y > 8) return; - if(gray_runs()) gtext(x * 6 - 5, y * 8 - 8, str); - else dtext(x * 6 - 5, y * 8 - 8, str); -} - -void print(int x, int y, const char *format, ...) -{ - va_list args; - - va_start(args, format); - __printf(0, format, args); - va_end(args); - - locate(x, y, __stdio_buffer); -} - -/* - printf_test() - Tests formatting functions. - -void printf_test(void) -{ - dclear(); - locate(1, 1, "Formatted printing"); - - print(2, 3, "%%4.2d 5 :\"%4.2d\"", 5); - print(2, 4, "%%-3c '&':\"%-3c\"", '&'); - print(2, 5, "%%#05x 27 :\"%#05x\"", 27); - print(2, 6, "%%1s \"tr\":\"%1s\"", "tr"); - print(2, 7, "%%6p NULL :\"%6p\"", NULL); - - dupdate(); - while(getkey() != KEY_EXIT); -} -*/ - -/* - tlb_debug() - Displays the TLB contents and some information. Only available for - SH7705, because the SH7305's TLB is much more complicated. - -void tlb_debug(void) -{ - // Entry address address (pointer in the address array), entry data - // address (pointer in the data address), and their contents. - unsigned int address, data, a, d; - // Virtual page number and physical page number. - unsigned int vpn, ppn; - // Contents of register MMUCR. - unsigned mmucr; - - int i, r, key = 0; - int way = 0, entry = 0; - int pointer_base; - - const char *protection[] = { "pr", "prw", "ar", "arw" }; - mmucr = *((volatile unsigned int *)gint_reg(Register_MMUCR)); - - dclear(); - locate("MMU register info", 1, 1); - locate("MMUCR.IX = ", 2, 3); - locate(mmucr & 0x02 ? "1" : "0", 13, 3); - dupdate(); - getkey(); - - while(key != KEY_EXIT && way < 4) - { - dclear(); - - print(1, 1, "TLB way=%d %d-%d", way, entry, - entry > 29 ? 31 : entry + 2); - - for(i = 0; i < 3 && entry < 32; i++, entry++) - { - address = 0xf2000000 | (entry << 12) | (way << 8); - data = 0xf3000000 | (entry << 12) | (way << 8); - - a = *((volatile unsigned int *)address); - d = *((volatile unsigned int *)data); - - ppn = (d >> 10) & 0x00007ffff; - // 4-kbyte page - if(d & 0x08) - { - vpn = (a >> 12) | entry; - pointer_base = vpn << 12; - } - // 1-kbyte page - else - { - vpn = (a >> 10) | (entry << 2); - pointer_base = vpn << 10; - } - - r = 2 * i + 3; - print(1, r, "%08x :%08x", pointer_base, ppn << 10); - - r++; - locate((d & 0x08) ? "4k" : "1k", 1, r); - print(5, r, "pr=%s", protection[(d >> 5) & 3]); - locate((d & 0x02) ? "shared" : "exclusive", 13, r); - } - - if(entry == 32) entry = 0, way++; - - dupdate(); - key = getkey(); - } -} -*/ - -#include - -/* - main_menu() - Displays the main menu and returns user's choice: 0 for [EXIT], - application numbers otherwise. Sets the ctaegory and application - number. -*/ -void main_menu(int *category, int *app) -{ - //--- - // Quite a few things to declare... - //--- - - extern image_t res_opt_menu; - - const char *mpu, *mpu_names[] = { - "Unknown", - "SH7337", - "SH7355", - "SH7305", - "SH7724", - "[error]" - }; - - const char *list_tests[] = { - "Keyboard & events", - "Gray engine", - "Image rendering", - "Text rendering", - "Real-time clock", - "Clocks and timers", - NULL - }; -/* - const char *list_perfs[] = { - "Image rendering", - "Text rendering", - NULL - }; -*/ - const char **list = NULL; - int list_len = 0; - - extern unsigned int bgint, egint; - extern unsigned int romdata; - int gint_size = (char *)&egint - (char *)&bgint; - - // Building a version string. - char gint_version[20]; - uint32_t v = (uint32_t)&GINT_VERSION; - sprintf(gint_version, "%s-%d.%d-%d", - (v >> 24 == 'a') ? "alpha" : (v >> 24 == 'b') ? "beta" : - (v >> 24 == 'd') ? "dev" : (v >> 24 == 'r') ? "release" : "?", - (v >> 20) & 0x0f, (v >> 16) & 0x0f, v & 0xffff); - - static int tab = 0, index = 0, scroll = 0; - // Set to 1 when interface has to be redrawn. - int leave = 1; - int i; - - mpu = mpu_names[MPU_CURRENT < 5 ? MPU_CURRENT : 5]; - text_configure(NULL, color_black); - - while(1) - { - //--- - // Displaying the current tab. - //--- - - dclear(); - - switch(tab) - { - case 0: - print(1, 1, "gint %s", gint_version); - print(2, 3, "MPU type %7s", mpu); - print(2, 4, "Add-in size %3dk", - ((uint32_t)&romdata - 0x00300000) >> 10); - print(2, 5, "gint size %5do", gint_size); - - list = NULL; - break; - - case 1: - locate(1, 1, "Test list"); - list = list_tests; - break; -/* - case 2: - locate(1, 1, "Performance"); - list = list_perfs; - break; -*/ - default: - print(1, 1, "Tab %d", tab); - break; - } - dimage_part(0, 56, &res_opt_menu, 0, 0, 42, 8); - - if(list) - { - list_len = 0; - while(list[list_len]) list_len++; - - for(i = scroll; list[i] && i < scroll + 6; i++) - locate(2, i - scroll + 2, list[i]); - - if(scroll > 0) locate(20, 2, "\x0d"); - if(scroll + 6 < list_len) locate(20, 7, "\x0e"); - - drect(0, 8 * (index - scroll) + 8, 127, - 8 * (index - scroll) + 15, color_invert); - } - - dupdate(); - - //--- - // Waiting for events. - //--- - - do - { - leave = 1; - - switch(getkey()) - { - case KEY_F1: - if(!tab) break; - tab = 0; - index = 0; - break; - case KEY_F2: - if(tab == 1) break; - tab = 1; - index = 0; - scroll = 0; - break; -/* case KEY_F3: - *category = 2; - *app = 1; - return; -*/ -/* case KEY_F3: - if(tab == 2) break; - tab = 2; - index = 0; - scroll = 0; - break; -*/ - case KEY_UP: - if(list && list_len > 1) - { - if(index) - { - index--; - if(index < scroll) scroll--; - } - else - { - index = list_len - 1; - scroll = list_len - 6; - if(scroll < 0) scroll = 0; - } - } - else leave = 0; - break; - case KEY_DOWN: - if(list && list_len > 1) - { - if(list[index + 1]) - { - index++; - if(index >= scroll + 6) - scroll++; - } - else - { - index = 0; - scroll = 0; - } - } - else leave = 0; - break; - - case KEY_EXE: - if(!tab) break; - if(category) *category = tab; - if(app) *app = index + 1; - return; - - default: - leave = 0; - } - } - while(!leave); - } - - if(category) *category = 0; - if(app) *app = 0; - return; -} - -/* - main() - No need for description. -*/ -int main(void) -{ - int category, app; - - while(1) - { - main_menu(&category, &app); - if(!category) break; - - switch((category << 8) | app) - { - case 0x0101: test_keyboard_events(); break; - case 0x0102: test_gray(); break; - case 0x0103: test_bopti(); break; - case 0x0104: test_tales(); break; - case 0x0105: test_rtc(); break; - case 0x0106: test_timer(); break; - - case 0x0201: perf_bopti(); break; - case 0x0202: /* perf_tales(); */ break; - } - } - - return 0; -} - -/* -#include -void screen(void) -{ - const uint8_t bmp_header[0x7e] = { - 0x42, 0x4d, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6c, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x13, 0x0b, - 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x47, - 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, - }; - const uint16_t file[] = { '\\', '\\', 'f', 'l', 's', '0', '\\', 'g', - 's', 'c', 'r', 'e', 'e', 'n', '.', 'b', 'm', 'p', 0x00 }; - int size = 0x7e + 1024; - - BFile_Remove(file); - BFile_Create(file, BFile_File, &size); - int handle = BFile_Open(file, BFile_WriteOnly); - BFile_Write(handle, bmp_header, 0x7e); - void *vram = (void *)display_getCurrentVRAM() + 1024; - for(int i = 1; i <= 64; i++) - { - BFile_Write(handle, vram - 16 * i, 16); - } - BFile_Close(handle); -} -void screengray(void) -{ - const uint8_t bmp_header[0x7e] = { - 0x42, 0x4d, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6c, 0x00, - 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x13, 0x0b, - 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x47, - 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, - }; - uint16_t file[] = { '\\', '\\', 'f', 'l', 's', '0', '\\', 'g', 's', - 'c', 'r', 'e', 'e', 'n', '#', '.', 'b', 'm', 'p', 0x00 }; - int size = 0x7e + 1024; - void *vram; - int handle; - - gupdate(); - - file[14] = 'l'; - BFile_Remove(file); - BFile_Create(file, BFile_File, &size); - handle = BFile_Open(file, BFile_WriteOnly); - BFile_Write(handle, bmp_header, 0x7e); - vram = (void *)gray_lightVRAM() + 1024; - for(int i = 1; i <= 64; i++) - { - BFile_Write(handle, vram - 16 * i, 16); - } - BFile_Close(handle); - - file[14] = 'd'; - BFile_Remove(file); - BFile_Create(file, BFile_File, &size); - handle = BFile_Open(file, BFile_WriteOnly); - BFile_Write(handle, bmp_header, 0x7e); - vram = (void *)gray_darkVRAM() + 1024; - for(int i = 1; i <= 64; i++) - { - BFile_Write(handle, vram - 16 * i, 16); - } - BFile_Close(handle); - - gupdate(); -} -*/ diff --git a/demo/gintdemo.h b/demo/gintdemo.h deleted file mode 100644 index 2b4fc24..0000000 --- a/demo/gintdemo.h +++ /dev/null @@ -1,114 +0,0 @@ -//--- -// -// gint demo application -// -// Displays some tests cases for many features of the library. -// -//--- - -#ifndef _GINTDEMO_H -#define _GINTDEMO_H - -//--- -// Main routines and common functions. -//--- - -/* - main() - No need for description. -*/ -int main(void); - -/* - main_menu() - Displays the main menu and returns user's choice by setting the - category and application. Category is 0 when the user leaves the - application. -*/ -void main_menu(int *category, int *app); - -/* - locate() - Displays text using a system-like monospaced font on a 21*8 grid. -*/ -void locate(int x, int y, const char *str); - -/* - print() - Locates a string using formatted printing. -*/ -void print(int x, int y, const char *format, ...); - - - -//--- -// Test applications. -//--- - -/* - test_keyboard_events() - Real-time keyboard management with events. -*/ -void test_keyboard_events(void); - -/* - test_gray() - Lets the user set the gray delays and see the results. -*/ -void test_gray(void); - -/* - test_bopti() - Displays and moves many kinds of bitmaps. -*/ -void test_bopti(void); - -/* - test_tales() - Displays some text using different modes and clipping options. -*/ -void test_tales(void); - -/* - test_rtc() - Just a clock. -*/ -void test_rtc(void); - -/* - test_timer() - Clock timer and timer precision. -*/ -void test_timer(void); - - - -//--- -// Performance applications. -//--- - -/* - perf_bopti() - Compares bopti and MonochromeLib. -*/ -void perf_bopti(void); - -/* - perf_tales() - Compares tales and the system's text rendering functions. -*/ -void perf_tales(void); - - - -//--- -// Debug applications. -//--- - -/* - debug_tlb() - On SH7705, displays the TLB contents. Does nothing on SH7305. -*/ -void debug_tlb(void); - -#endif // _GINTDEMO_H diff --git a/demo/gintdemo.ld b/demo/gintdemo.ld deleted file mode 100644 index 5df3f76..0000000 --- a/demo/gintdemo.ld +++ /dev/null @@ -1,113 +0,0 @@ -/* - This linker script links the object files when generating the ELF - output. Note how symbols romdata, bbss, ebss, bdata and edata are used - in the initialization routine (crt0.c) to initialize the application. - - Two ram areas are specified. The "real ram" is accessed directly while - the other area is virtualized. It is not possible to execute code in - virtualized ram. -*/ - -OUTPUT_ARCH(sh3) -ENTRY(_start) - -MEMORY -{ - /* System-managed mappings map this area into the add-in data */ - rom : o = 0x00300200, l = 512k - /* 0x0810000 is apparently mapped to 0x8801c0000 */ - ram : o = 0x08100000, l = 8k - /* RAM section from P1 area, no MMU involved */ - realram : o = 0x8800d000, l = 12k -} - -SECTIONS -{ - /* ROM sections: binary code and read-only data */ - - .text : { - /* Initialization code. */ - *(.pretext.entry) - *(.pretext) - - _bctors = ABSOLUTE(.); - *(.ctors) - _ectors = ABSOLUTE(.); - _bdtors = ABSOLUTE(.); - *(.dtors) - _edtors = ABSOLUTE(.); - - *(.text) - *(.text.*) - } > rom - - .rodata : { - *(.rodata.fxconv); - *(.rodata.paradox); - *(.rodata) - . = ALIGN(4); - *(.rodata.*) - - _romdata = ALIGN(4) ; - } > rom - - /* RAM sections: bss section and read/write data - The .bss section is to be stripped from the ELF file and wiped at - startup, hence its location is undefined */ - - .bss : { - _bbss = ABSOLUTE(.); - _sbss = ABSOLUTE(SIZEOF(.bss)); - - *(.bss) - } > ram - - .data : AT(_romdata) ALIGN(4) { - _bdata = ABSOLUTE(.); - _sdata = ABSOLUTE(SIZEOF(.data)); - - *(.data) - *(.data.*) - } > ram - - .cc : AT(_romdata + SIZEOF(.data)) ALIGN(4) { - *(.eh_frame) - *(.jcr) - - . = ALIGN(4); - } > ram - - /* Real RAM sections: interrupt handlers and some *uninitialized* gint - data */ - - _gint_data = _romdata + SIZEOF(.data) + SIZEOF(.cc) ; - - .gint : AT(_gint_data) ALIGN(4) { - /* The vbr needs to be 0x100-aligned because of an ld issue */ - . = ALIGN(0x100) ; - - _gint_vbr = . ; - _bgint = ABSOLUTE(.) ; - - /* Exception handler */ - . = _gint_vbr + 0x100 ; - *(.gint.exc) - - /* TLB miss handler */ - . = _gint_vbr + 0x400 ; - *(.gint.tlb) - - /* Interrupt handler */ - . = _gint_vbr + 0x600 ; - *(.gint.int) - - . = ALIGN(4); - _egint = ABSOLUTE(.) ; - } > realram - - .gint_bss : AT(_gint_data + SIZEOF(.gint)) ALIGN(4) { - _bgbss = ABSOLUTE(.) ; - *(.gint.bss) - _egbss = ABSOLUTE(.) ; - } > realram -} diff --git a/demo/icon.bmp b/demo/icon.bmp deleted file mode 100644 index 27f0e48206978289bfc171e9aa03a16e1d448acc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1870 zcmchTI}*Y$3`7kD9Ds(3!=#~tf^*Q(aRLs=eS+ehtT&2do6q1GD{E=>B -#include -#include -#include -#include - -/* -static const uint8_t screen[1024] = { -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 0, 0, 0, 7, 159, 0, 0, 1, 192, 0, 0, 0, 0, 0, 121, 240, 0, 0, 0, -31, 191, 192, 0, 3, 224, 27, 216, 0, 0, 1, 251, 252, 0, 0, 0, 57, 247, 222, -30, 7, 240, 36, 36, 62, 25, 131, 159, 24, 255, 129, 224, 0, 227, 142, 126, 1, -192, 45, 172, 127, 127, 192, 14, 1, 255, 199, 224, 0, 227, 140, 240, 1, 192, -26, 88, 115, 127, 224, 14, 57, 221, 207, 0, 0, 227, 13, 192, 1, 192, 34, 68, -120, 30, 0, 14, 25, 156, 220, 0, 0, 227, 253, 252, 1, 192, 36, 36, 126, 28, -0, 14, 219, 156, 223, 192, 0, 227, 253, 252, 1, 192, 36, 36, 31, 12, 0, 46, -27, 140, 223, 192, 0, 227, 141, 193, 193, 192, 40, 20, 7, 140, 0, 206, 25, 140, -220, 28, 0, 227, 140, 225, 129, 199, 24, 24, 99, 156, 1, 14, 25, 204, 206, 24, -0, 227, 142, 127, 1, 195, 39, 228, 255, 156, 2, 14, 24, 237, 199, 240, 1, 247, -222, 62, 1, 198, 44, 44, 223, 30, 2, 31, 28, 237, 131, 224, 1, 224, 0, 0, 3, -254, 27, 216, 0, 0, 4, 30, 0, 0, 0, 0, 3, 192, 0, 0, 7, 252, 0, 0, 0, 0, 4, -60, 1, 249, 240, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 4, 0, 97, 240, 56, 0, 0, -0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 224, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4, 0, 47, 192, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 32, 255, 128, 63, 128, -0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 32, 255, 0, 48, 78, 0, 0, 0, 0, 0, 0, 0, 0, -0, 0, 15, 176, 255, 0, 112, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 8, 56, 255, 0, -96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 8, 60, 255, 0, 224, 0, 0, 0, 0, 0, 0, -0, 0, 0, 0, 130, 56, 126, 255, 3, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 192, -62, 255, 15, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 14, 191, 255, 192, 0, -0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 6, 129, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 6, 0, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 7, 128, 63, 192, -0, 0, 96, 1, 224, 1, 0, 0, 0, 2, 0, 0, 7, 0, 31, 192, 0, 0, 95, 1, 11, 68, 88, -0, 0, 4, 0, 0, 7, 128, 31, 192, 0, 1, 192, 129, 204, 85, 100, 0, 0, 8, 0, 0, -15, 128, 63, 224, 0, 0, 95, 1, 8, 85, 68, 0, 1, 144, 0, 0, 31, 128, 143, 224, -64, 0, 96, 1, 232, 41, 68, 0, 2, 96, 0, 31, 255, 129, 7, 248, 96, 0, 0, 0, 0, -0, 0, 0, 4, 0, 0, 96, 254, 129, 7, 254, 96, 0, 0, 0, 0, 0, 0, 0, 4, 0, 1, 128, -254, 131, 135, 255, 224, 0, 0, 1, 192, 64, 16, 0, 8, 0, 7, 0, 254, 131, 255, -63, 224, 0, 0, 1, 38, 113, 208, 0, 8, 0, 13, 0, 222, 147, 254, 31, 224, 0, 0, -1, 41, 74, 80, 0, 8, 0, 25, 0, 222, 67, 254, 31, 160, 0, 0, 1, 41, 74, 80, 0, -12, 0, 49, 0, 222, 19, 254, 62, 48, 0, 0, 1, 198, 113, 208, 0, 2, 0, 32, 128, -222, 195, 255, 252, 56, 0, 0, 0, 0, 0, 0, 0, 2, 0, 124, 64, 220, 151, 135, 248, -127, 0, 0, 0, 0, 0, 0, 0, 2, 0, 66, 32, 221, 223, 7, 240, 255, 0, 0, 0, 0, 0, -0, 0, 2, 0, 129, 23, 93, 159, 15, 241, 131, 0, 0, 0, 0, 0, 0, 0, 4, 0, 128, -136, 217, 95, 3, 226, 9, 0, 0, 1, 240, 0, 0, 0, 4, 0, 128, 72, 89, 95, 129, -228, 18, 0, 0, 0, 0, 0, 0, 0, 4, 1, 0, 72, 73, 127, 128, 224, 36, 0, 0, 0, 0, -0, 0, 0, 28, 1, 0, 76, 129, 127, 192, 96, 8, 0, 0, 0, 0, 0, 0, 0, 16, 1, 0, -231, 203, 124, 96, 64, 0, 0, 0, 0, 0, 0, 0, 0, 16, 1, 1, 28, 123, 240, 12, 64, -1, 0, 0, 0, 0, 0, 0, 0, 16, 1, 2, 28, 143, 128, 15, 192, 7, 0, 0, 0, 0, 0, 0, -0, 16, 1, 4, 17, 143, 24, 15, 192, 14, 0, 0, 0, 0, 0, 0, 0, 28, 1, 4, 1, 135, -24, 31, 192, 24, 0, 0, 0, 0, 0, 0, 0, 18, 1, 62, 1, 135, 248, 63, 224, 192, -0, 0, 0, 0, 0, 0, 0, 35, 1, 195, 1, 135, 128, 254, 126, 1, 0, 0, 0, 0, 0, 0, -0, 35, 193, 131, 195, 135, 255, 248, 112, 1, 0, 0, 0, 0, 0, 0, 0, 67, 241, 131, -14, 207, 255, 192, 224, 3, 0, 0, 0, 0, 0, 0, 3, 67, 15, 143, 56, 255, 7, 1, -224, 7, 0, 0, 0, 0, 0, 0, 28, 130, 7, 255, 112, 204, 7, 131, 224, 31, 0, 0, -0, 0, 0, 0, 32, 134, 30, 29, 120, 156, 7, 255, 224, 127, 0, 0, 0, 0, 0, 63, -197, 206, 60, 56, 192, 248, 15, 255, 248, 255, 0, 0, 0, 0, 0, 120, 5, 227, 248, -56, 195, 248, 127, 191, 254, 63, 0, 0, 0, 0, 7, 254, 255, 193, 255, 15, 193, -255, 15, 31, 252, 31 }; -*/ - -static const uint8_t town[4464] = { 4, 0, 0, 16, 2, 115, 130, 6, 243, 130, 6, 243, 130, 6, 243, 130, 4, 0, 0, 16, 2, 115, 130, 6, 243, 130, 6, 243, 130, 6, 243, 130, 4, 0, 0, 16, 255, 255, 255, 240, 3, 255, 198, 15, 127, 198, 15, 127, 198, 15, 127, 198, 255, 255, 255, 240, 3, 255, 198, 15, 127, 198, 15, 127, 198, 15, 127, 198, 255, 255, 255, 240, 0, 144, 3, 192, 0, 112, 255, 30, 112, 255, 30, 112, 255, 30, 112, 255, 0, 144, 3, 192, 0, 112, 255, 30, 112, 255, 30, 112, 255, 30, 112, 255, 0, 144, 3, 192, 0, 144, 2, 192, 15, 240, 195, 255, 240, 195, 255, 240, 195, 255, 240, 195, 0, 144, 2, 192, 15, 240, 195, 255, 240, 195, 255, 240, 195, 255, 240, 195, 0, 144, 2, 192, 0, 144, 2, 64, 112, 15, 129, 240, 15, 129, 240, 15, 129, 240, 15, 129, 0, 144, 2, 64, 112, 15, 129, 240, 15, 129, 240, 15, 129, 240, 15, 129, 0, 144, 2, 64, 0, 208, 3, 64, 128, 1, 193, 128, 1, 193, 128, 1, 193, 128, 1, 193, 0, 208, 3, 64, 128, 1, 193, 128, 1, 193, 128, 1, 193, 128, 1, 193, 0, 208, 3, 64, 255, 223, 255, 67, 0, 0, 227, 0, 0, 227, 0, 0, 227, 0, 0, 227, 255, 223, 255, 67, 0, 0, 227, 0, 0, 227, 0, 0, 227, 0, 0, 227, 255, 223, 255, 64, 112, 195, 129, 194, 0, 0, 126, 0, 0, 126, 0, 0, 126, 0, 0, 126, 112, 195, 129, 194, 0, 0, 126, 0, 0, 126, 0, 0, 126, 0, 0, 126, 112, 195, 129, 192, 247, 255, 254, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 247, 255, 254, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 247, 255, 254, 192, 72, 0, 2, 98, 240, 113, 202, 240, 113, 202, 240, 113, 202, 240, 113, 202, 72, 0, 2, 98, 240, 113, 202, 240, 113, 202, 240, 113, 202, 240, 113, 202, 72, 0, 2, 96, 240, 0, 2, 70, 25, 255, 46, 25, 255, 46, 25, 255, 46, 25, 255, 46, 240, 0, 2, 70, 25, 255, 46, 25, 255, 46, 25, 255, 46, 25, 255, 46, 240, 0, 2, 64, 96, 1, 226, 198, 30, 48, 110, 30, 48, 110, 30, 48, 110, 30, 48, 110, 96, 1, 226, 198, 30, 48, 110, 30, 48, 110, 30, 48, 110, 30, 48, 110, 96, 1, 226, 192, 64, 2, 19, 199, 56, 32, 111, 56, 32, 111, 56, 32, 111, 56, 32, 111, 64, 2, 19, 199, 56, 32, 111, 56, 32, 111, 56, 32, 111, 56, 32, 111, 64, 2, 19, 192, 128, 2, 19, 71, 252, 96, 247, 252, 96, 247, 252, 96, 247, 252, 96, 247, 128, 2, 19, 71, 252, 96, 247, 252, 96, 247, 252, 96, 247, 252, 96, 247, 128, 2, 19, 64, 0, 2, 19, 71, 15, 241, 231, 15, 241, 231, 15, 241, 231, 15, 241, 231, 0, 2, 19, 71, 15, 241, 231, 15, 241, 231, 15, 241, 231, 15, 241, 231, 0, 2, 19, 64, 3, 194, 18, 71, 12, 63, 255, 12, 63, 231, 12, 63, 231, 12, 63, 255, 3, 194, 18, 71, 12, 63, -255, 12, 63, 231, 12, 63, 231, 12, 63, 255, 3, 194, 18, 64, 6, 99, 242, 195, 152, 31, 0, 248, 31, 195, 152, 31, 195, 152, 31, 0, 6, 99, 242, 195, 152, 31, 0, 248, 31, 195, 152, 31, 195, 152, 31, 0, 6, 99, 242, 192, 10, 84, 10, 193, 252, 24, 0, 28, 31, 129, 252, 31, 129, 252, 24, 0, 10, 84, 10, 193, 252, 24, 0, 28, 31, 129, 252, 31, 129, 252, 24, 0, 10, 84, 10, 192, 10, 87, 251, 192, 126, 48, 0, 14, 62, 0, 126, 62, 0, 126, 48, 0, 10, 87, 251, 192, 126, 48, 0, 14, 62, 0, 126, 62, 0, 126, 48, 0, 10, 87, 251, 192, 10, 80, 3, 192, 31, 224, 0, 7, 252, 3, 255, 255, 255, 255, 224, 0, 10, 80, 3, 192, 31, 224, 0, 7, 252, 3, 255, 255, 255, 255, 224, 0, 10, 80, 3, 192, 10, 208, 3, 64, 12, 56, 0, 12, 32, 6, 16, 64, 0, 2, 56, 0, 10, 208, 3, 64, 12, 56, 0, 12, 32, 6, 16, 64, 0, 2, 56, 0, 10, 208, 3, 64, 250, 95, 254, 64, 56, 175, 7, 28, 144, 7, 255, 252, 0, 2, 175, 7, 250, 95, 254, 64, 56, 175, 7, 28, 144, 7, 255, 252, 0, 2, 175, 7, 250, 95, 254, 64, 106, 86, 219, 64, 34, 225, 159, 242, 200, 4, 130, 8, 0, 2, 225, 159, 106, 86, 219, 64, 34, 225, 159, 242, 200, 4, 130, 8, 0, 2, 225, 159, 106, 86, 219, 64, 10, 80, 3, 64, 62, 225, 227, 6, 228, 15, 255, 240, 0, 1, 225, 227, 10, 80, 3, 64, 62, 225, 227, 6, 228, 15, 255, 240, 0, 1, 225, 227, 10, 80, 3, 64, 255, 255, 255, 192, 4, 243, 130, 6, 156, 10, 16, 64, 0, 1, 243, 130, 255, 255, 255, 192, 4, 243, 130, 6, 156, 10, 16, 64, 0, 1, 243, 130, 255, 255, 255, 192, 0, 0, 3, 192, 7, 127, 198, 15, 0, 15, 255, 255, 255, 255, 127, 198, 0, 0, 3, 192, 7, 127, 198, 15, 0, 15, 255, 255, 255, 255, 127, 198, 0, 0, 3, 192, 0, 0, 0, 0, 0, 112, 255, 30, 0, 3, 192, 0, 0, 60, 112, 255, 0, 0, 0, 0, 0, 112, 255, 30, 0, 3, 192, 0, 0, 60, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 195, 254, 0, 2, 198, 0, 220, 44, 112, 195, 0, 0, 0, 0, 0, 112, 195, 254, 0, 2, 198, 0, 220, 44, 112, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 129, 252, 0, 2, 69, 0, 168, 36, 57, 129, 0, 0, 0, 0, 0, 57, 129, 252, 0, 2, 69, 0, 168, 36, 57, 129, 0, 0, 0, 0, 0, 0, 0, 0, 128, 31, 193, 248, 0, 3, 117, 62, 87, 244, 31, 193, 0, 0, 0, 0, 128, 31, 193, 248, 0, 3, 117, 62, 87, 244, 31, 193, 0, 0, 0, 0, 0, 0, 0, 2, 160, 7, 227, 224, 0, 3, 72, 128, 168, 52, 7, 227, 0, 0, 0, 2, 160, 7, 227, 224, 0, 3, 72, 128, 168, 52, 7, 227, 0, 0, 0, 0, 0, 0, 0, 1, 64, 1, 255, 192, 0, 3, 232, 246, 245, 252, 1, 254, 0, 0, 0, 1, 64, 1, 255, 192, 0, 3, 232, 246, 245, 252, 1, 254, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 194, 0, 16, 2, 199, 40, 138, 44, 0, 195, 0, 0, 0, 128, 0, 0, 194, 0, 16, 2, 199, 40, 138, 44, 0, 195, 0, 0, 0, 128, 0, 0, 2, 160, 0, 3, 137, 0, 40, 2, 64, 24, 6, 36, 3, 138, 0, 0, 2, 160, 0, 3, 137, 0, 40, 2, 64, 24, 6, 36, 3, 138, 0, 0, 2, 160, 0, 0, 1, 64, 0, 2, 44, 128, 40, 2, 64, 0, 0, 36, 2, 46, 0, 0, 1, 64, 0, 2, 44, 128, 40, 2, 64, 0, 0, 36, 2, 46, 0, 0, 1, 64, 0, 0, 0, 0, 0, 3, 238, 64, 68, 2, 192, 0, 0, 44, 3, 238, 0, 0, 0, 0, 0, 3, 238, 64, 68, 2, 192, 0, 0, 44, 3, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 192, 68, 3, 192, 63, 192, 60, 0, 79, 0, 0, 0, 0, 0, 0, 73, 192, 68, 3, 192, 63, 192, 60, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 56, 3, 64, 124, 32, 52, 0, 119, 0, 0, 0, 0, 0, 0, 112, 0, 56, 3, 64, 124, 32, 52, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 64, 80, 32, 52, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 64, 80, 32, 52, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 64, 248, 16, 36, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 64, 248, 16, 36, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 192, 192, 16, 44, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 192, 192, 16, 44, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 192, 255, 240, 44, 8, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 192, 255, 240, 44, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 3, 192, 41, 64, 60, 48, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 3, 192, 41, 64, 60, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 3, 192, 41, 64, 60, 32, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 3, 192, 41, 64, 60, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 16, 3, 64, 47, 64, 52, 56, 0, 0, 0, 0, 0, 8, 0, 0, 8, 16, 3, 64, 47, 64, 52, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 42, 40, 2, 127, 224, 127, 230, 47, 7, 0, 0, 0, 0, 42, 0, 0, 42, 40, 2, 127, 224, 127, 230, 47, 7, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 20, 40, 3, 118, 227, 109, 180, 97, 159, 0, 0, 0, 0, 20, 0, 0, 20, 40, 3, 118, 227, 109, 180, 97, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 3, 64, 32, 64, 52, 97, 227, 0, 0, 0, 0, 0, 0, 0, 0, 68, 3, 64, 32, 64, 52, 97, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 3, 255, 255, 255, 252, 115, 130, 0, 0, 0, 0, 0, 0, 0, 0, 124, 3, 255, 255, 255, 252, 115, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 0, 60, 127, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 0, 60, 127, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 132, 0, 0, 112, 195, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 132, 0, 0, 112, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 126, 0, 0, 120, 0, 0, 57, 129, 0, 0, 0, 0, 0, 0, 0, 40, 126, 0, 0, 120, 0, 0, 57, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 252, 0, 0, 3, 240, 0, 31, 193, 0, 0, 0, 0, 0, 0, 0, 43, 252, 0, 0, 3, 240, 0, 31, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 134, 0, 0, 2, 16, 0, 7, 227, 0, 0, 0, 0, 0, 0, 0, 21, 134, 0, 0, 2, 16, 0, 7, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 6, 0, 0, 1, 224, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 23, 6, 0, 0, 1, 224, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 62, 0, 0, 252, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 15, 62, 0, 0, 252, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 250, 0, 0, 132, 0, 0, 3, 138, 0, 0, 0, 0, 0, 0, 0, 14, 250, 0, 0, 132, 0, 0, 3, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 190, 0, 0, 120, 0, 0, 2, 46, 0, 0, 0, 0, 0, 0, 0, 6, 190, 0, 0, 120, 0, 0, 2, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 156, 0, 0, 3, 240, 0, 3, 238, 0, 0, 0, 0, 0, 0, 0, 3, 156, 0, 0, 3, 240, 0, 3, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 36, 0, 0, 2, 16, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 1, 36, 0, 0, 2, 16, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 1, 224, 0, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 216, 0, 0, 1, 224, 0, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 4, 0, 0, 16, 2, 115, 130, 6, 243, 130, 6, 243, 130, 6, 243, 130, 4, 0, 0, 16, 2, 115, 130, 6, 243, 130, 6, 243, 130, 6, 243, 130, 4, 0, 0, 16, 255, 255, 255, 240, 3, 255, 198, 15, 127, 198, 15, 127, 198, 15, 127, 198, 255, 255, 255, 240, 3, 255, 198, 15, 127, 198, 15, 127, 198, 15, 127, 198, 255, 255, 255, 240, 0, 144, 3, 192, 0, 112, 255, 30, 112, 255, 30, 112, 255, 30, 112, 255, 0, 144, 3, 192, 0, 112, 255, 30, 112, 255, 30, 112, 255, 30, 112, 255, 0, 144, 3, 192, 0, 144, 2, 192, 15, 240, 195, 255, 240, 195, 255, 240, 195, 255, 240, 195, 0, 144, 2, 192, 15, 240, 195, 255, 240, 195, 255, 240, 195, 255, 240, 195, 0, 144, 2, 192, 0, 144, 2, 64, 112, 15, 129, 240, 15, 129, 240, 15, 129, 240, 15, 129, 0, 144, 2, 64, 112, 15, 129, 240, 15, 129, 240, 15, 129, 240, 15, 129, 0, 144, 2, 64, 0, 208, 3, 64, 128, 1, 193, 128, 1, 193, 128, 1, 193, 128, 1, 193, 0, 208, 3, 64, 128, 1, 193, 128, 1, 193, 128, 1, 193, 128, 1, 193, 0, 208, 3, 64, 255, 223, 255, 67, 0, 0, 227, 0, 0, 227, 0, 0, 227, 0, 0, 227, 255, 223, 255, 67, 0, 0, 227, 0, 0, 227, 0, 0, 227, 0, 0, 227, 255, 223, 255, 64, 112, 195, 129, 194, 0, 0, 126, 0, 0, 126, 0, 0, 126, 0, 0, 126, 112, 195, 129, 194, 0, 0, 126, 0, 0, 126, 0, 0, 126, 0, 0, 126, 112, 195, 129, 192, 247, 255, 254, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 247, 255, 254, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 128, 0, 195, 247, 255, 254, 192, 72, 0, 2, 98, 240, 113, 202, 240, 113, 202, 240, 113, 202, 240, 113, 202, 72, 0, 2, 98, 240, 113, 202, 240, 113, 202, 240, 113, 202, 240, 113, 202, 72, 0, 2, 96, 240, 0, 2, 70, 25, 255, 46, 25, 255, 46, 25, 255, 46, 25, 255, 46, 240, 0, 2, 70, 25, 255, 46, 25, 255, 46, 25, 255, 46, 25, 255, 46, 240, 0, 2, 64, 96, 1, 226, 198, 30, 48, 110, 30, 48, 110, 30, 48, 110, 30, 48, 110, 96, 1, 226, 198, 30, 48, 110, 30, 48, 110, 30, 48, 110, 30, 48, 110, 96, 1, 226, 192, 64, 2, 19, 199, 56, 32, 111, 56, 32, 111, 56, 32, 111, 56, 32, 111, 64, 2, 19, 199, 56, 32, 111, 56, 32, 111, 56, 32, 111, 56, 32, 111, 64, 2, 19, 192, 128, 2, 19, 71, 252, 96, 247, 252, 96, 247, 252, 96, 247, 252, 96, 247, 128, 2, 19, 71, 252, 96, 247, 252, 96, 247, 252, 96, 247, 252, 96, 247, 128, 2, 19, 64, 0, 2, 19, 71, 15, 241, 231, 15, 241, 231, 15, 241, 231, 15, 241, 231, 0, 2, 19, 71, 15, 241, 231, 15, 241, 231, 15, 241, 231, 15, 241, 231, 0, 2, 19, 64, 3, 194, 18, 71, 12, 63, 255, 12, 63, 231, 12, 63, 231, 12, 63, 255, 3, 194, 18, 71, 12, 63, 255, 12, 63, 231, 12, 63, 231, 12, 63, 255, 3, 194, 18, 64, 6, 99, 242, 195, 152, 31, 0, 248, 31, 195, 152, 31, 195, 152, 31, 0, 6, 99, 242, 195, 152, 31, 0, 248, 31, 195, 152, 31, 195, 152, 31, 0, 6, 99, 242, 192, 10, 84, 10, 193, 252, 24, 0, 28, 31, 129, 252, 31, 129, 252, 24, 0, 10, 84, 10, 193, 252, 24, 0, 28, 31, 129, 252, 31, 129, 252, 24, 0, 10, 84, 10, 192, 10, 87, 251, 192, 126, 48, 0, 14, 62, 0, 126, 62, 0, 126, 48, 0, 10, 87, 251, 192, 126, 48, 0, 14, 62, 0, 126, 62, 0, 126, 48, 0, 10, 87, 251, 192, 10, 80, 3, 192, 31, 224, 0, 7, 252, 3, 255, 255, 255, 255, 224, 0, 10, 80, 3, 192, 31, 224, 0, 7, 252, 3, 255, 255, 255, 255, 224, 0, 10, 80, 3, 192, 10, 208, 3, 64, 12, 56, 0, 12, 32, 6, 16, 64, 0, 2, 56, 0, 10, 208, 3, 64, 12, 56, 0, 12, 32, 6, 16, 64, 0, 2, 56, 0, 10, 208, 3, 64, 250, 95, 254, 64, 56, 175, 7, 28, 144, 7, 255, 252, 0, 2, 175, 7, 250, 95, 254, 64, 56, 175, 7, 28, 144, 7, 255, 252, 0, 2, 175, 7, 250, 95, 254, 64, 106, 86, 219, 64, 34, 225, 159, 242, 200, 4, 130, 8, 0, 2, 225, 159, 106, 86, 219, 64, 34, 225, 159, 242, 200, 4, 130, 8, 0, 2, 225, 159, 106, 86, 219, 64, 10, 80, 3, 64, 62, 225, 227, 6, 228, 15, 255, 240, 0, 1, 225, 227, 10, 80, 3, 64, 62, 225, 227, 6, 228, 15, 255, 240, 0, 1, 225, 227, 10, 80, 3, 64, 255, 255, 255, 192, 4, 243, 130, 6, 156, 10, 16, 64, 0, 1, 243, 130, 255, 255, 255, 192, 4, 243, 130, 6, 156, 10, 16, 64, 0, 1, 243, 130, 255, 255, 255, 192, 0, 0, 3, 192, 7, 127, 198, 15, 0, 15, 255, 255, 255, 255, 127, 198, 0, 0, 3, 192, 7, 127, 198, 15, 0, 15, 255, 255, 255, 255, 127, 198, 0, 0, 3, 192, 0, 0, 0, 0, 0, 112, 255, 30, 0, 3, 192, 0, 0, 60, 112, 255, 0, 0, 0, 0, 0, 112, 255, 30, 0, 3, 192, 0, 0, 60, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 195, 254, 0, 2, 198, 0, 220, 44, 112, 195, 0, 0, 0, 0, 0, 112, 195, 254, 0, 2, 198, 0, 220, 44, 112, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 129, 252, 0, 2, 69, 0, 168, 36, 57, 129, 0, 0, 0, 0, 0, 57, 129, 252, 0, 2, 69, 0, 168, 36, 57, 129, 0, 0, 0, 0, 0, 0, 0, 0, 128, 31, 193, 248, 0, 3, 117, 62, 87, 244, 31, 193, 0, 0, 0, 0, 128, 31, 193, 248, 0, 3, 117, 62, 87, 244, 31, 193, 0, 0, 0, 0, 0, 0, 0, 2, 160, 7, 227, 224, 0, 3, 72, 128, 168, 52, 7, 227, 0, 0, 0, 2, 160, 7, 227, 224, 0, 3, 72, 128, 168, 52, 7, 227, 0, 0, 0, 0, 0, 0, 0, 1, 64, 1, 255, 192, 0, 3, 232, 246, 245, 252, 1, 254, 0, 0, 0, 1, 64, 1, 255, 192, 0, 3, 232, 246, 245, 252, 1, 254, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 194, 0, 16, 2, 199, 40, 138, 44, 0, 195, 0, 0, 0, 128, 0, 0, 194, 0, 16, 2, 199, 40, 138, 44, 0, 195, 0, 0, 0, 128, 0, 0, 2, 160, 0, 3, 137, 0, 40, 2, 64, 24, 6, 36, 3, 138, 0, 0, 2, 160, 0, 3, 137, 0, 40, 2, 64, 24, 6, 36, 3, 138, 0, 0, 2, 160, 0, 0, 1, 64, 0, 2, 44, 128, 40, 2, 64, 0, 0, 36, 2, 46, 0, 0, 1, 64, 0, 2, 44, 128, 40, 2, 64, 0, 0, 36, 2, 46, 0, 0, 1, 64, 0, 0, 0, 0, 0, 3, 238, 64, 68, 2, 192, 0, 0, 44, 3, 238, 0, 0, 0, 0, 0, 3, 238, 64, 68, 2, 192, 0, 0, 44, 3, 238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 192, 68, 3, 192, 63, 192, 60, 0, 79, 0, 0, 0, 0, 0, 0, 73, 192, 68, 3, 192, 63, 192, 60, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 56, 3, 64, 124, 32, 52, 0, 119, 0, 0, 0, 0, 0, 0, 112, 0, 56, 3, 64, 124, 32, 52, 0, 119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 64, 80, 32, 52, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 16, 3, 64, 80, 32, 52, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 64, 248, 16, 36, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 64, 248, 16, 36, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 192, 192, 16, 44, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 2, 192, 192, 16, 44, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 192, 255, 240, 44, 8, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 2, 192, 255, 240, 44, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 3, 192, 41, 64, 60, 48, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 3, 192, 41, 64, 60, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 3, 192, 41, 64, 60, 32, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 3, 192, 41, 64, 60, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 16, 3, 64, 47, 64, 52, 56, 0, 0, 0, 0, 0, 8, 0, 0, 8, 16, 3, 64, 47, 64, 52, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 42, 40, 2, 127, 224, 127, 230, 47, 7, 0, 0, 0, 0, 42, 0, 0, 42, 40, 2, 127, 224, 127, 230, 47, 7, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 20, 40, 3, 118, 227, 109, 180, 97, 159, 0, 0, 0, 0, 20, 0, 0, 20, 40, 3, 118, 227, 109, 180, 97, 159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68, 3, 64, 32, 64, 52, 97, 227, 0, 0, 0, 0, 0, 0, 0, 0, 68, 3, 64, 32, 64, 52, 97, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 3, 255, 255, 255, 252, 115, 130, 0, 0, 0, 0, 0, 0, 0, 0, 124, 3, 255, 255, 255, 252, 115, 130, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 0, 60, 127, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 192, 0, 0, 60, 127, 198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 0, 0, 112, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 132, 0, 0, 112, 195, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 132, 0, 0, 112, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 126, 0, 0, 120, 0, 0, 57, 129, 0, 0, 0, 0, 0, 0, 0, 40, 126, 0, 0, 120, 0, 0, 57, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 252, 0, 0, 3, 240, 0, 31, 193, 0, 0, 0, 0, 0, 0, 0, 43, 252, 0, 0, 3, 240, 0, 31, 193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 134, 0, 0, 2, 16, 0, 7, 227, 0, 0, 0, 0, 0, 0, 0, 21, 134, 0, 0, 2, 16, 0, 7, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 6, 0, 0, 1, 224, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 23, 6, 0, 0, 1, 224, 0, 1, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 62, 0, 0, 252, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 15, 62, 0, 0, 252, 0, 0, 0, 195, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 250, 0, 0, 132, 0, 0, 3, 138, 0, 0, 0, 0, 0, 0, 0, 14, 250, 0, 0, 132, 0, 0, 3, 138, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 190, 0, 0, 120, 0, 0, 2, 46, 0, 0, 0, 0, 0, 0, 0, 6, 190, 0, 0, 120, 0, 0, 2, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 156, 0, 0, 3, 240, 0, 3, 238, 0, 0, 0, 0, 0, 0, 0, 3, 156, 0, 0, 3, 240, 0, 3, 238, 0, 0, 0, 0 }; - -static const uint8_t screen[460] = { 0, 0, 0, 2, 0, 23, 224, 15, 0, 0, 0, 0, 0, 2, 16, 127, 192, 31, 192, 0, 0, 0, 0, 1, 144, 127, 128, 24, 39, 0, 0, 0, 0, 7, 216, 127, 128, 56, 24, 128, 0, 0, 14, 4, 28, 127, 128, 48, 0, 0, 0, 0, 49, 4, 30, 127, 128, 112, 0, 0, 0, 0, 65, 28, 63, 127, 129, 240, 0, 0, 0, 0, 128, 224, 31, 127, 135, 240, 0, 0, 0, 1, 0, 0, 7, 95, 255, 224, 0, 0, 0, 1, 0, 0, 3, 64, 255, 224, 0, 0, 0, 0, 128, 0, 3, 0, 127, 224, 0, 0, 0, 0, 128, 0, 3, 192, 31, 224, 0, 0, 0, 1, 0, 0, 3, 128, 15, 224, 0, 0, 0, 2, 0, 0, 3, 192, 15, 224, 0, 0, 0, 4, 0, 0, 7, 192, 31, 240, 0, 0, 0, 200, 0, 0, 15, 192, 71, 240, 32, 0, 1, 48, 0, 15, 255, 192, 131, 252, 48, 0, 2, 0, 0, 48, 127, 64, 131, 255, 48, 0, 2, 0, 0, 192, 127, 65, 195, 255, 240, 0, 4, 0, 3, 128, 127, 65, 255, 159, 240, 0, 4, 0, 6, 128, 111, 73, 255, 15, 240, 0, 4, 0, 12, 128, 111, 33, 255, 15, 208, 0, 6, 0, 24, 128, 111, 9, 255, 31, 24, 0, 1, 0, 16, 64, 111, 97, 255, 254, 28, 0, 1, 0, 62, 32, 110, 75, 195, 252, 63, 128, 1, 0, 33, 16, 110, 239, 131, 248, 127, 128, 1, 0, 64, 139, 174, 207, 135, 248, 193, 128, 2, 0, 64, 68, 108, 175, 129, 241, 4, 128, 2, 0, 64, 36, 44, 175, 192, 242, 9, 0, 2, 0, 128, 36, 36, 191, 192, 112, 18, 0, 14, 0, 128, 38, 64, 191, 224, 48, 4, 0, 8, 0, 128, 115, 229, 190, 48, 32, 0, 0, 8, 0, 128, 142, 61, 248, 6, 32, 0, 128, 8, 0, 129, 14, 71, 192, 7, 224, 3, 128, 8, 0, 130, 8, 199, 140, 7, 224, 7, 0, 14, 0, 130, 0, 195, 140, 15, 224, 12, 0, 9, 0, 159, 0, 195, 252, 31, 240, 96, 0, 17, 128, 225, 128, 195, 192, 127, 63, 0, 128, 17, 224, 193, 225, 195, 255, 252, 56, 0, 128, 33, 248, 193, 135, 103, 255, 224, 112, 1, 128, 161, 135, 199, 156, 127, 131, 128, 240, 3, 128, 65, 3, 255, 184, 102, 3, 193, 240, 15, 128, 67, 15, 14, 188, 78, 3, 255, 240, 63, 128, 231, 30, 28, 96, 124, 7, 255, 252, 127, 128, 241, 252, 28, 97, 252, 63, 223, 255, 31, 128, 224, 255, 135, 224, 255, 135, 143, 254, 15, 128 }; - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wparentheses" -#pragma GCC diagnostic ignored "-Wshift-negative-value" -#pragma GCC push_options -#pragma GCC optimize 0 - -#define ML_vram_adress display_getCurrentVRAM -__attribute__((optimize("0"))) -void ML_bmp_or_cl(const unsigned char *bmp, int x, int y, int width, int height) -{ - uint16_t line; - char shift, *screen; - uint8_t *p; - int i, j, real_width, begin_x, end_x, begin_y, end_y; - int bool1=1, bool2=1, bool3; - if(!bmp || x<1-width || x>127 || y<1-height || y>63 || height<1 - || width<1) return; - p = (uint8_t *)&line; - real_width = (width-1>>3<<3)+8; - if(y < 0) begin_y = -y; - else begin_y = 0; - if(y+height > 64) end_y = 64-y; - else end_y = height; - shift = 8-(x&7); - if(x<0) - { - begin_x = -x>>3; - if(shift != 8) bool1 = 0; - } else begin_x = 0; - if(x+real_width > 128) end_x = 15-(x>>3), bool2 = 0; - else end_x = real_width-1>>3; - bool3 = (end_x == real_width-1>>3); - screen = (char *)ML_vram_adress()+(y+begin_y<<4)+(x>>3); - - for(i=begin_y ; i>3)+begin_x] << shift; - if(bool1) screen[begin_x] |= *p; - if(shift!=8) screen[begin_x+1] |= *(p+1); - for(j=begin_x+1 ; j>3)+j] << shift; - screen[j] |= *p; - if(shift!=8) screen[j+1] |= *(p+1); - } - } - line = bmp[i*(real_width>>3)+end_x]; - if(bool3) line &= -1<TCNT; - timer_stop(t); - time1 = 0xffffffff - time1; - - dclear(); - dtext(2, 2, "gint..."); - dupdate(); - - t = htimer_setup(timer_user, 0xffffffff, timer_Po_4, 1); - timer_start(t); - for(i = 0; i < 1000; i++) dimage(x, y, img); - time2 = TMU.timers[timer_user]->TCNT; - timer_stop(t); - time2 = 0xffffffff - time2; - - dclear(); - print(1, 1, " ML: %08x", time1); - print(1, 2, "gint: %08x", time2); - print(1, 3, ">> \x04%d", time1 / time2); - dupdate(); - getkey(); -} diff --git a/demo/resources/bopti_thumbs.bmp b/demo/resources/bopti_thumbs.bmp deleted file mode 100644 index 923b77ea1d10832b19397c57db82f58ce1376d0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1250 zcmc(bF;2rk5JeY+L_tZzDaaktq@qFc9a7LyAaNwFl*Ynxj#6MoE_(`y>Pd7r2hQmnFy}^;r(;QfBoJsgqO8IvqoS3kw3kD^}p;B zz}n!X0X?76RXvBVp0n$>{~!9Qj3cT;dJ*)?!&Ix7#AlVM)y1sn0m>>P89u!-m5(wc z%+~(HFl5Tl6sFu{nL-*jfsGLeAOR_9qG1H4n8$NbGUZkv*xElPSzdj3fFVDQBdJgG zzLi^J?aKQKsy>Q#BBVZ~Vd4{j#(>${pNUj)wrd)4!jV26UQg%O&EWwE3)k=O_#$yQ9G_p`2R!%vHWYr=sst*5-6w#b?e0CE4YpSG%*px) z9GEigPq!@aeW5PRx4Lu2sy6`d)ntNp_u@WG^}gQFCAZ?mI7n24kS0qs5zuOo2*-tT zxziS!>1bcyf39Ngv^GG@=i;T|l=c_k!&~)>7>pXN7f;~(X#IQ=qqyd`sB&nH?L z1u=o|s_6!l?{4=-`E(N4dCEBxiZ4ATjvAG24d*K6P8)Ry^Ff$Dzp<^vs6dJ#X!lTm=@A3DE^S2p&>G);l&+D)k&W8DxDQjJuQznjo|9r6 zBq~BnlU+9p(`t|i$AwzC(-xZPXfOXAp;b#_0{#sI=ED2|#y^Eb>6B6MsX0?v1zQ1y zDIh1#?AE{Bwt${}XN`ZBwrf8Od~(4OfQ&g7Ss)~E?J1T2a6{|3asvJh1m=Q=0scIZ zRyHmg;lT>P?!R8;>>RS%WVs$JUicZ?T5Mc{I^Z=TJG{xO67klnc`PVfR7-^ z`Ll>edGMNWNi~}Q{{^*}%oM+xVHgNY(%^r=yc%l!sNSuBn`tqbDSj)@K^OMukh`w> c@w?)5r+imsfl8nfs01p3N}v*`1UeG<0zD$}@c;k- diff --git a/demo/resources/clock_7705.bmp b/demo/resources/clock_7705.bmp deleted file mode 100644 index d711160c78d3deba622ae8da1a26f87b2d49db83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12410 zcmeHJyHdkI5HqxtG<*i1z@(v~;XCN)^79~~SZMYfCq8c;Fncq(=ybbUSp>r7ZTmdz z))xB*UN8IC-S7m2h5N?`zDNwi=Jow^#HYUBhr+K~l|Us>2~+}=KqXKK%qD<;9%k=v z=(4q{|9rAsQZPmBs9P5JzH%~29u38%)^tpI7p3=SoT)dsWh=J5(y?6rOC+klqA;ooK z%UC}5-b$gGB!Ta3@lu9XmUd0n%V)TCp5vSe#Y>NgQj?=w!?_c4)g~X|tYrB=YrG|X zGy(qv0&}5jgg<{g9Um3EZ~?q&;vvjOP}${y0u+!3XKvQN+_r$8eP>O07C*KkS7cy} z3myRE#BsP%O@WZop9`e?Fa43_CYGxfo(1Au;z#25*IZY9An5mrunSV5T?|3Hhx(;Q z2&OOhAaRm70{HI5eV8NqdP5hDIVr|L;zZ1nvd_)Jv>HZ)9cq6ggFZ;;PTl1-N z)j8;F;;b3**+ra=KH1P%ZjaYb&%U=KFr`Gaq7^yy^h%`xD!=LnlRwh*3uqrwHwb72 zTSO&>b_!DoewI9@#c28hZR)Dhr<{mXVqQ=7fYDri7eJz`^QHd&J6D+>t>Bk%sl9Gz heL@QBl3l;rg5hEo_!gP~=n*sH`xxKXT diff --git a/demo/resources/font_modern.bmp b/demo/resources/font_modern.bmp deleted file mode 100644 index 1286f301f05bad4f17e1def00e15eb2592050cba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24698 zcmeI0J(d(l5QJB9G3DqudN^1(Ispg3lZVAAxGuy@s6S$}x@vls?wQ%8>b)%@A|rn) z^%?>1KYsc2bUt5nf7Z`e{rsb!Kc4>9t@G*W>92SGIdfg_Km7aOg17$qXEA=CXKr9_ zU~XV;U~XV;U~XV;U~XV;U~XV;U~b?y*ns{zoUbf&V<>GCU$ud$Z12H z(khvo*hRRVXhYf(+bqS$DTrC2h&T&aDxH2Uq2p-j1>FL2D*O_`7eO(Yu8Q;`YNgQZ zKEPB<8j5ESN~dkQ4YGLJtRH&k0B(IA>e&Zq z1*bvUT8!DlLklQ$)!ceg5%ytAq8G6YWZ`cCb&dlx-Lb_HHMfta?hKFs+$(iTTTah2 z^|^t$fgLxX4{pb5FDT_N{$k=r+r>JMW*G0^uf|iT-(rz>wVu|186XeU1jZCYu@$`+ zr4a@zcew{M5ZVHGR0~KiA*c*^3<|YVck1AxLosXtw74-~Hu|Dm1)SbXNnIGKEMt^a z+MBIpm1-I7mBmAg8-p@HOQj$hLp`cR#%?{W0drfSn2QrC8(08JIttNLEEXjT-VB!+ zM2nJ>3_@uW9y?Dxo-dH}qW5S|r_Ka(19Jn9zk%{0KmNcso>2SAvwL?H&zgl2@(iYU z5~Lxsz~r`0>R`5#dJiilL#9%EvMI#@@{A2Qj|0q*ldOu&;HMQ2>DUB{2MWN`Q+oQA zE_kv?Ph9n2J`R+=(5ryPlb;l(Eef;+uHZyR^X?TSi-K9H!wGF#0K)aJEH!qIo?3KeJTX{bp`v|sFfE)8K3|r8SB}%I47MOm>am`26~_H9Y?yh^{akD8UEXHyab|) z{Eg)}gh9G6r`Q~|XiqAbQNrgH2K#BP- zXbZ6QLg;cPZYib4pcEOKZxbDlMAQJP*;>V-bf3Ze#UP3q4CwVDDw0CFCw}e>&TS6(IP%iuk=&- mje*l@mOlZu*dgmJ&Q+wR=%UUoAVsI!u&QUxiRT7(*}xC{|A@o@ diff --git a/demo/resources/isometric.bmp b/demo/resources/isometric.bmp deleted file mode 100644 index 5241b601dab9bab756a145bb1902c33287acb879..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4134 zcmd5%J5EGF3>%=N0Ev=@1`e_f6%ALR3Y)uyC&`jxW(2t6qBaw>JFL!&;#YRGYpZO6BUH4GQz?sK+ap#_J)xbXU z3oL1HtX%|W-ujN(S%SCVB3~}%)tl^7O>$K`-;+RmQ^q-?k1}O_*Xwe(v;>D#HKZ z!17Ek?|J7;K83tDI?fYXUCC99cUm>wHJdvRj*oQpKHGO^ac@Rx`pj$*tC<>MJ1ls2 z-!A&jXpQbD%j$h*wunWhEl=R61>U0dkT&$7e+}YUI4dUiq1gan28DUg@^v=iH;lUy z{XWvwJND4UjPe_>T)eC0);()r4nAh_3ydb@q#YVs%{%hh?EyGi%{wFTN$uEsw@gw| z*}ERX$?J+=mt!IkD+^9$W}8qdwBi>Wdl54Nao;l8iH8UE`SILyC#HR;=N3J_ReiW8 WfrjgO6XmSq9zSx=8ek&U^ZNqrBFN|f diff --git a/demo/resources/items.bmp b/demo/resources/items.bmp deleted file mode 100644 index c57f9345638707c12ffc0d6b4cba05269bbbc3db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 99322 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 diff --git a/demo/resources/opt_bitmap.bmp b/demo/resources/opt_bitmap.bmp deleted file mode 100644 index d8107170fec8ce5d1c9fd0c222710acad8248e15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3194 zcmd6ju@1s83`7GH5^OAdPZuT@zJrae6Cc=rf-9#!SxJQmVTh%)=d*o@Z@7$y^o~*A zo6bXLr?W{XeHE%7_dX_48qU}Ig1`N5%f@G0l!Bj`^0w*MK5mFXRRMYBegjgG zU;)Rf@=~1tG(eV2Y;&~-_uAjUrd0qp3`ixCF|ib{5KnkYX&fh|%?u*#yZ4~LI#mEG za4A|BF`b#dc*6Zk?^+2_fg+neK*Tf#X5EWc+Us1EW78|_Z9#3_jVM1UUx>r^UT=oynehL9S*ZctFg;@ z#=btwmX;g%@^yR_8W2Glnm=e$J#)B4gYW4fiu?D`LyWEaM0y(B3?Z}~LHzH~I= z*&4MJu_glfVmOV#oVZGU{|Sl#W2_QtiJR|#?3hdroMTL29^!LMlMy>ZHmchcsDU@a zn`VimngoSOGg5NA`40cNf5ym}9b*Ep2O`c!+`KxOBGyD8Ukv9Yqi1PSAO;xa^(>~& zfA0V1J?jqxOvmxzWwm;r-U%gkL~Iqn^rV#~m|&)-H*%-z$CpS0j_~%DH65DooegFK!xBl;E;dk%MhSxK5x9j?uogGelk5+pPPCD!Z znT|rhrK=hRwo8VjK(RndV0l0-2U}QK@V^fI?e!ga^J;Wnmw>}?W%f$h$b}4mt3DNg ztAoz7%uF=F9?Jvz+v_{j_uP&%4q||@0&!K^BCG;HsB`W&cojfY2PmUTG*v%1Bj|6h z@4!n|W5#jD04F6c3ssfip~NbDhO1)O>Ht-v8cl|B@|g6u*LUD0tI;Xym`qAc9?I*` zLcsv2A~{pASgE9ijjrT1#G${vz5_2=jdc&JwdkwEfV~P*63nq(u3CvvUdj&+{q6M~ a9A^nI$c|{29ks*u`VRf=^&MQu68Hf_0tz_* diff --git a/demo/resources/opt_rtc.bmp b/demo/resources/opt_rtc.bmp deleted file mode 100644 index 7fdd7641babf2196187112d263c9cbe484278547..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20474 zcmeI0L2e{P6hsHMEZJ}oE-)Kb2#IsBW5cZHvT#~aR;zvYg zWqW3%`TX-=zkIqpe`NhFKY!-u$NYTr>4&W0nScKsKNlg(=bwK4{R!Xt`R!`_``oh@ zURT!LuKU;N8*IoS*Hhi15Hv0g5T^&+z;Z`So9d6)k68et#!*&!y&-;k1d1w_(C-?( zaZ^h$Y&HykSBWqWSx0L_8`_4mr(krq2pErsp-Z1N=-dOG#OWw(iqo|t z+Qt+*oWqkTam@loJzX*7=;EebB94K_2;k`YRCD;#jSeTD0`IBe>75RLdI=xkRc;_( z*vWm5e&y?S@1WuCYb0n~8X(S%fg5;@+8zS+uX)pf%>ta#56!9#tgT0f8b+ZsVBMm3 zaci7SDQWXs+seQMjSm|0P;}a(AwcYuh8~Ian7RV&X>eZy2Vk$!FepxTWbG4nLt{%i z(qdx^+Z+)c=N|6qCGnWTk78|$j%+|_k3qnoXBym#?L5QS;ySgYZs0XujE`R;*r0%5Q{#4;#ipUD9;X02 zJ#A%dA?=bYerib#n+@nv!Ja-0+U&n>a2jA_06jwwMcFhs1q{k|ihB*%oDOR9o?+9k z*?2&U;s)zcdm2oM>j0P$SF92P4~4-4464}%K-(OxZN$;FnbofX7?hHTJf;NE^(Z!Q ziu1AP>kmAnzMed?E{r7AJe0L6iVqE;1K6yNClrNs?LoP?9-VqN8@kQT7D-w1PH}K|6S@%tsX>((5K$~r)VYGa@=d+tl)JS+y&IUeqOCi-1hc^_ep3R? z#skK~zC&#Ktf9@Dm}7Jg3TE*D!{5z4ir$nGS5tUe2ahu}Z3DYdGVmVlz3^nc@wd?M<=%(eyZagm8kYu$vjw<;-B^kEi&Q*3Y((D7`;=#1Z>piAz({x2T8+r)hg#ReF(4I1ZAbXuYzfTu@MD0v!A z&zM4Oa4N88^)SMzf0OF8jQj5rU6bj7TpDYz9Hx~GFZ6=>a#eT-!t7>%VyM4-sm7^|Ya}&LOG;ng;W9B)F*t#LIC0^U;0`t)yYl z$6fdJVK5Gm+N>g28!}?*DxgOS=!P*0RWXa`Y8=Df+J3Nwqao8CwRzQ@QbM*1)%Qjk z0`S-=XwQmUAz}}mdvxvBxdL{vknGxkVXR~|4-krh6jV=-Rs%L01f~GPHm20F($K37 z+8Qub>7ZDk4G^6XCDKjM5e%-xG3>2v9{r)+03DvLZ38wGJ+=vKQyIWcbg4m=7!XnH zkq&^F)Lywd*^a2tz-X+*F|=(}Y-<3fgl02$b<`=bQ-abf4R~x2&>&qK7`esmpsNmHw>Xq)^LuLO_d0~Vgb(m4 zH;^yu2ob;)6ab#!sZzPN7E;FjeWG9z}tE;{oSy?|eg_ z=XUIyCKAlkILe?XZlM818X%^jdyJ2fx^WExMwH_}e|zT}JozEeZ*eH7`7Jb@zrFJf cUdSQf@6u7=E=Pm<|HFp!w|Bn5*Et0K0nW?|&j0`b diff --git a/demo/resources/opt_tales.bmp b/demo/resources/opt_tales.bmp deleted file mode 100644 index 8f34e47611f921da87495803af7ba6fc505c87c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3194 zcmd6ju};H45JU}CBS5mev*tB@ie;4o$T01KIztTv$L}!AK%Z< z)9*Q}e(HYf9&{h3S5+PLU!HtTOw;l0*XKij({$ zPVzn6^S9nhvo^fPMWQk2t((c?h928&3HwQaMsI&pQLrdB*nP9GCV7m0`@K)M32KB0I8OOmEo^x$1QvS diff --git a/demo/resources/opt_timer.bmp b/demo/resources/opt_timer.bmp deleted file mode 100644 index 1d7cb9c5bbca8614c0a4c3e9f6933410cbd7e88a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3194 zcmeH_u?oUK5JXQaK|342Crv6Fzr)ThRsLL;3_gYp0mGH+!ChwOb_epf;XYm3vZgqn zvFF&Q*oStDGgjPR&)PCt8^*`$tAG6-*NyMK6@h&LrKd&D8M+f%3r497KOEgAK}nBh zbrM)yh*DxjiGXl&V7nIHDeX>i?EF4tfP*8X$#>2Q*=k5Rq|vYO;-^z`o?E^!Iy87D z5v3d#KKR$b6rzMnx6q?Z+C|y;`|!F@9}#ZzgL%cab7Mz-^cw;{+Wof%zM(DowmGP zZHW11&)|sKC-R!-UIcdgeUSYo{S{u18Z@j7BoE zL*!K)AOb_woHXM!udv?}t&LolH{R55at3*byov)vV2GNNW}M~~_IskWk?ZotoBB=8 zAP(jH_8)22&9|0LX!+MZ z;i-{68yODuzjKC#BH$AzY@D-Yq7Fnr1VkVsr+I~;PP@A^EEECHIAP40YPwonfH}c*Y4E=WLm%0}&7b5y;4CUSX)y?(Pf=MZhyo*f?j)L>-8L2#7#N zPV)*wopyI;SSSLXal*zqTPEs21VlgtGIE+%80xgUJHtW|@Qf2S&e<|i2O=N>B9IZG zUUA65D%YSox)s`<8evXMu6?LEx)pSDPmM4q!MwuqTT!WI-H@Cq?G$MFn9X@@Nq!|a PY@C1jt<8I0?!<;)xdY_V diff --git a/demo/resources/screen.bmp b/demo/resources/screen.bmp deleted file mode 100644 index c6fb2c3ef7332f413ba5e729e523f6c9f6dcf001..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13570 zcmd6jF|r&*5JXqP#N+~;K$f_G90CJ}tKeYh1iG}?lHw6BGOBudMmrK6e}7hGW@T5; zhdJ^z1v{{DFW-aUVB9=|?*dg4FtACF&t{`z~ioX?NPe}9h;zyI|&!Y;lI z<1?)izy3Zq2W^EpHhI%m{p2;DbJnW%Np018j(X)s!&?Z~p?%c$!4<-rGmmaO`bffjG?Ymk#^C+Xd<;la`#P8@eOE!Mcxp?q*9{baW ze+M&%el^Q|c#SnTomFOPm0>P3(crU(H{t=UQKK2rCbZXe`K!-^XX4Rw*1R?I&NScE z_EOsi*P(q??;g)F)6LuGnwdg)bJUXtUs#7e=iqmd`9#mBw(6O)r#K%xpf&0pZSueu z=CO9AUgf2>>T|cQPcHMk|D+}Fo_TXs!-J{2$I~IcPAl%{bRKiAr!0G_&fhc#kDnfd zpAesM@|w4w8lC8mayR*x8JHdg@?>OsK z<4H{4)O)V;lz!%{{GHa_pFVr2&+f#W@oGlSRpyy<!w=%QisXRKftwKTq)&V|SKQI@95tA;&cm}p&v(8~H#gPZ zWAaQqd!X-Rd4?KX<=|N%^Wfwyy|mqP#@l7GM`=4*>8EEF%sl(1I`^A=@adoW@_*~$ z&YdiKv#vd+jPy=DZ<%!Scz3YoUU9g8m4&Cnnwxg9Kl81fsWWHQPkXIqRvLSD9<&b2 zzuINex~z_4A2j-1^!-;e@-A}DV&>3$N4UbHe0%Lrey8UyywgtT--_IqxXR6)a@PHn zlNpn5mw$C$-s?9}r>E=juCSZCi>p2EJo(PuyK>I`ZpJHpcYddI` zmG|XL_GjMpO7Gp@XlK{_7E`~I^=i*u->3HEoz&Cr_;zs0xRbx@8~MHdM&_^7cYcdi z_R)9pTUI%I3;W4VF8j<)-FyWpp5`X+_u$#xcNN#26;F;m z=DNDO!<=W#|EPLrpYn8Pnl<0mT~>0;CI5fz+1FUR(&ygr6Q;lGJ=^KU;jv!ak36~4 zv=6=sci+H#=l9+C%ylz&bKU(NS9^HpsovRlnw{?5$-L8ZWiMu?x9;qlvl}!20-<@& A>Hq)$ diff --git a/demo/resources/sprites.bmp b/demo/resources/sprites.bmp deleted file mode 100644 index 2cbe277077d5bb0d2d3cd8e7333500260b42b549..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8850 zcmbuF@!0?T(|^x#R3DGW|NkB@zyJBgVi&(3 z#E-6H_dEtYy?=jy|L|nrJ-!*8d7t%=Cdil08HXmAooc=rja{GhkcKDBy!)JK%!9MX z2|qRWJ$n+451QuMWo92#&0Y23(XIAZzWDJ--)Fw7d1kM#uTQ?a!%t0*)r^Ms+uPem z9%uU?eRg=bGCWDg2i@f2+qW-&9?{NO_;cTFz(C;g+J2JDoku^l<}^Do!e-m45eW?p`|%Y#EB$BK&+4Ly`2XP!Z|>&v^} zsXN*`zCBOh8QdG>+5FDfefZ5we^#y2Pp$b~-!mV6aFuOpacFjsW(RltaCZD~Xm*h1 zF3xwAb64NJ;`W|y#j}I0Tk(5O6I^k7Pq*UPLAr2f%^A&{N9QgaUT0kUAk7)qKB&&P zJ*Vqg>+Pp+^V9b^`tVot%>2#d-S_F?%D27yZ&|oJgDDyCf}O!VblEi*-@bkM^N4oN z!k=^JaNEDN&C$c~rmxI6L7AtoJ2Ox9`CEK*rzgI$q@TWc`M;3r{}VaW;=$gfKl5$< z*?ZtQGklG&TzQu%D93Yne8`e{b7XTFz(C;g+ zJ2JDoku^l<}^D?O diff --git a/demo/resources/swords.bmp b/demo/resources/swords.bmp deleted file mode 100644 index 8aa6416275a5725e7cf1e5efc4d602c0c54acc27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5770 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 F{vUZB$Z!Av diff --git a/demo/resources/zelda.bmp b/demo/resources/zelda.bmp deleted file mode 100644 index 03440dd6ce9cb1a51f34232d1d2270348f3be255..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72922 zcmeHJy_zJuZadehLnjXWG&gbN#DQZ!fHP-)RKJXk7FuW&DXLWcQ`NgW`o5zEK@cQ0 zJ+tTU|MtKC^0&Wk=YR0uf8oFXz<>V?>TiGjGtggu{U`GO^pAhF{~Ceo@BjMm|K0KG z=RXAD|D^*#;!Y*P03p;UM|l+nkk+k?L*1uCjUf;^9_2&1hv^MGc_HSCPb^xMOm?w1 zc%ph@JWrw5q@AZ6)00faLz7b)2C1CwZ;&&pdoN(4+a&E|bI^fwW(A|%WpV?_69h&} z`%&Oi2zy769ukwxsX6aHQ1XBj=;kgw0f+v%BLtZQbBM3RbPh8V4Tn>_i`*SBH>ygG z@_GYram5>;e>HnC;~TyiS~|DB=nXVc1vIHZP#)z&xrg8V22U(nmH5&Df8`ta9*#LX znTm%dr^3=9{Q5TtJNbSqIIKGie}y;DGx*ZnhD_i?INaOGcf0|5>TJD5Z~m}9*im<6 z-9-2hKNMGei-zjF3qm)A0(4L)ka?s+R^5;CC~RgA!{DH>$<*fwASbVFu7jWDQC^^*^pM?TCdbbyqH8CQb9hfqFq z9@*Cp`^ZCW0RqX;1;R)PXHaD`pBcK%d1PNZY62ULFHCP~Bv=Qs3MCaEWb=?FHuzjV z%xVyUEK>?#cu+Fovmx~rpRIQNTS@Sf%`OimH3Dqtg-X%ZQ^L}S5y&YrLq3!Ane)iL zcIHixg}pSwWOP@&3WrLsB6db40COJM*G?YOn|puu3EACp7r-HulMyd4lm!x@$^l+D zG={;XgET{#X2}lDtkkY(vL~uuJG6uc0B>*NB>RLc!(pK-D?#8xD2F+Z>}zLt3D`>` ztm%{rjEC!0>q_s7s@IO2`QXB^-_=N*fyzUw;)84+(xeYK*H&2#B9N6z0SpgHCVV!e zzT&ghuKx$#08{vY3OSvpgryN9kW*xad?x8L=eP5!^CFP9KrU1YKsd-Mo-)tQv1h%- zoc|Tx0NZehyh`A+AirXMn@_$$9`NXld0uDoenS1L6{L5%CdjOOIZm5AHv1*Pks;H z^#<#ktt)-DbKgKen00V;t#xN8X@2e-pl3durQ(M>L0s{bPsO+HL$gIvvk-)kCQb?m z|B8n!r^pQXOokbTeTxST+A35}fvi*tKsd-Mo-)tQv1h%-oPXaNggy*$6JR38J|I(p z2_a2nrEK7{fXX@l);9>P5b|gcgc{#L#jj|**7166F_arju=fNW9+fkz&;e2&W?TsZ zA42)e`Mu_{83K4$_CZf&C9ZgyRN2gDX4e5Fue|{}G>@e#yvkx&O<3`Zwvx5c6`cT< z-s(gVS5Du9&rN`aG|%8FzE8rp;H?L6@^viRZw0=hW_0p(EL(u=#_Uym>+a1ptdp-} z*#gK-nAxlN)?Lj3G!DVLZaKq{ShrGQs5y;qJEEJ8W^~Y0n+y6)+=GfoUMT|HI;=)W z*%iEc67+o@U)mrz8oD93)UFBzGy?$0PxHNt@5HwG?%J5Cc2UDdgUbVXdDK7;WQ^yAp`66&n;Os`hRebC2&3#_*9y+d46(4HZ5u|`H z9nR$2Y;5RlIWor~4tJHRcrT|$w@So749t+&@3-@9w$|@{HR67$o0G&61_Jpigy#jAE#Amyb1W9s-IB3}gV37p+1xQcJxy}8c|-b2S#s^UW} zJAxDtro)-M&z3O9N%W1VQWfte)aX`;82X>a_u1626SvPHNkNsW_-qTMj(ds(IEQat za$7a`)YF)5y15+PKr$3KpVwe}K$8Yx#Y?s;kn-z6wMxZnp`k#e0QCw5&fzuf6ojFq zt@w3;l$QeRq2q&yd=a=OaCW2MD!z}0{vAF(hot6x=nPFtq`GR?s&5}`IQx)fMU|@f z-skoSO*rzrgzvMVd54eBA*p#EIzy8ZsluWvxr%Rtd2>w);QTAtJ%4cV0(jtIhWzmP zp|ALCHTTc@&okBRL!RS@6d!=Mr#QPa!dc7|ONv|7Zvoz9XjsjtNmb>_jGa*lz=TI? z`=x5c_-p2tPhdKScJitCAjmQ@;ssPlFyWDE2f_|F4hY2}5NaAmw4eRW|dP zq1Bwl1ywcv(DPmr?O_IX6`d<2Hb4?99t z@n&#`yZ)(!-rmIVu=AW$Ia>|I(&TJTks0!tq~V-Lc6%m85c-TEL@F~*vX46m4-1D` zS7z*t$^qtlu~t>&#*0{10K%cR;w7UX%L1PTbeQwVwr6OccZ@oc8etKHDuyBfgo9zl zyR6bU?|8iyRrMRVrDNFN%KGtc8AC;q@+<%&%`iI=gu)QYZO$WGzkxggXhr**v5Nk{ zUX=|4l){Qb4m_Kd6w9A z^=cQ&fZ%45ssiIBN)hM=Aa*fuCY@QqLlcrHr^EL45tykwq`dj`XK*|$bR`93Sg5R< z3k#@348fdtv9}{eR^`ez;NI>Luos1tLUkackU+8ks_Y@Y5^E9)`t2jAv?m{b6;$o| z6&yTKB4$`V%nr$kRi$S~WeNIMdYBx}c$5qcL!+UaO6KAqlU2oMH!AxZ^yu+_KC*lf zXd;0XLx}}K)loi_Go(p>1LuIN4nwCoDJy}wp4mQK{AKj)(C4Wxa8g|W3PZPD-UY$} z!7YcbbWA0giiakra*M5%=T$8*%sL4?6icJ5fKn9*N)M0Xss(wv)xQhgZSFM8c6)pt z{u`qbIm=B9!I018p-N#d7XhGZ03OdC#oZ&mfp~!IIV63yP%_k4BzPX00MohCo*ZSi zy5&eCqk^_#Jd~ejg;}dmpyDA2hC#`&-||zPGw;G-BDZDAnSeQIAQ4u4P*(or(@o<< zzlR8;Er#elR95^@JR#d|ll&V~&y8GFRs0hJr#BdSH`U;W;o?fepFPIENS;6@b9*RM_k2H<^I+uY}+(LoA;7FvM|9s#26B2q)=G#K{gCo zd&>8~_wZSO8FE)*zx#}jKM&HggaXH24$P1OV!v5_(Hn%JG?$Oe0sEM@XHZrt6$XZ4 zWdjIS?|1`r(9tXcNC~Ss$@P>9-HLeK#G886cEH?WmvJ+Q6$0=Chd?5vK!7G5x@~sL zjMoYaWdA-wa9RxASS28byaWO4>MOU3KkgqYzAda4t@tPAfA)Lu#t|k{S6wQ8Vl_#@ zZS)L{-ux|dk70=m)-fAR+T~tkDn7KPGw+a6*s_RZNx&kJlDpE$=Wy^3p*)>m!NWq3 zUMo%GnN%Ktn1Fa&QGP#MR$Zwp2QY)bb@wtF?XoAq)La0>c#SrKQ;K}}tm!$w`;SA>4 ztPhOJTl39Dr3AxJZb6p$jdcr#hSOc;nnBe+D~C*;&KWPM8dW@N-7RDH2fpGBG(M;E zsf7bzs9O$V94Nc%&#I^1Kux^!B33QIJcOqN@(+a-A7sOjwHLjCCL-XjA{mC|`n-=n zRH}7-Fp&m;*zfMCR_lN^>v~tJ7JxVd=$TTXlwt@nuR;Lg41zxe+iyeXZl9rNsLt74 zhMs<_#L3(`Gb{}J#H32~E#)K-I)LZV$j^abf-p2f7&K&t?NqL8sFgq<`}YxYrg9bk zE)b^9wm!aRE8aE03;vMsmxHO!CWldvWhYYt|5eeZZE@177Rn^<%jT43^M!o!Bj!e>HDW=b4rZMGIp6T zfs5xUC?RmY80Y6Eo@IGshOQ*#O`y=5H;*`4_LMA}xSI-HMlv%)-etlB3cVx9;$>Y$ z+_Vop3rPlnlmZgSR=|DarYrytWj%U)W?l?|ZU7!P+;R_F`^t66Ks%p*AFO z04BLXWL+=}NoHB*gL(ER)7#2`AXhu%V)0Ho9tuPbK`8f77=^+a3~fcazL8E4%brCd zop+M5;SHJdsuFNd#}W{PTgt$PDz+A>DhEvyY&?(DZrD7^iw;V!7;>J_m!zj8P>PKB z#SpUejq=i#fBcSy6q-{oxr2tG-}IEsQ@}jZAl$_i=$0OUINY7?&2FER5J(Ad3=jq3xhoGU(*&%-|3Ts6;j zkMa}co~ztk^9j7yx5|_EiL%nOLyVFkpUI$Pm^;oH*baSiyDc+urKJQR4BfV__b6y& z6@Z*s#;nL7o`fn{^r5WX?rQW6&QmUSrQ<~nO4N?viQ7wLC|jS37^!xo6)i3DPAXIh z9e9&;CSy4V0Hw+a6lRk-qeAxq9-eN6BuAi1m_RoGkIOoD`=pOEhp@^C6dp41F^>31 z3&2As;M{*A80xcwGhRYxzg@YG-GMmQg5Q!7di7QkBPl&0GJvBR*S`ygkrZ}+5{Nt_ zQat721-i}n6EaVP&{H49dwrS5&4XDS?1uB%Vda7lPEr%4S7jUu83r;}^`#-C)^Erg zeP6{>#3R247c?P!=u(%7(yKB?p=yQ2WPN9-^$HaY0ZFB3gK&6yliTM|0Fo&Q6apMQ zaua~Vq;L0k2`l_o)=6Oi9`@|f(x;aQ6DV9VGLZ@=KGJOKK=Y99(fucc5WJHN&EuB! za0PpbFoD7qAJ?dG;v-EjlonukD4e7u=$5OW@RT(BD|HC0_;}xD&LY5Ez03G?mH4Q} z_3y%avoVium2kozoe@??OWva2>&tw#sE?2}o?XF{Swc#o0R0sTtnfiR8Wob^f!6sD zA|*4IW$ZHH09Zxx?HGokT?&2bkesJ{WLIW9S0alN-U2FlozL@-VT*RVL=82IKu$SB zS@U~b!TFJB$V-F?LJ0YbytkIQY>O|v@?JsG#{uT?CXRc4@NGm&N@0Q!vajw8cJ*rH zwH>P`01q?3UPqh-Vq7pJhi)QS;&(Q5TUV~|Y6Jki!iHr)pirs|q@*80YUaGek_qG% z?`6~|0FKmu5qv;l4#K*CMFtR>Aapl|wDS8Zp0o8YhGx$rGu7qye=S!+FXWcIQ5()x zJjQ6PH64I(Pu^w~FEYp!QWzAJy-FVqP1p!+&rX#Ipq+L)a@Ezo^F4$ai-_L z;?suoc|?QM_!Q)n1OPn2%E>Nye6qzO?&wUu_429JTqMRK7H~}_`x=i=w|cM*$?8JB z&7c`8zyXKZNh;{WmMZ)XJ`8&_94;{=k2;^DZ==4_YPjnU`AfdQTfPUcm|R}l|Li|E z2!H6$)qYRa%WvST{F1*1Zx~u{;9eqs3D73Tq`rah3ePRQM0rH;mz>O_otzCO8ec#uI*NB7 zjg!FV@O_EUd@4^Y ztW-)MYp!BA5^g(~#i9qScqll&K;A~Fhus!c=I8LKOCs*mR=n>A4UfnW(yOaBpP@v# zb9fHcZ#3_4^I7o_ab5w9FQ611#XFG3N#Jw%zC>ug7-~j=T&NU)aFAWg_qp0AoDkYFLS1}w3_iKma!=u_msj3eOhpBV;s>^Vs zg5>H*669Mc8e{<&X+riKzMegdGYKcl`F<1GIlJZ+*!Sf(2%F_?!~~^JB#o2)ejnvI z*lTwfP$4(der%$w18JNDK8Jt#dl071q4xNt-au1_g(^PCLa3^*e*;ZgZLr7(8A3S= zWX)9!M>_p?0d2$KI|UNqIehEFR=D%wAW!gKC=4JRJg?ApG@j5C(iM z9jH2k`!rz}6lL|K9v|>EC--{YVA4@6Q|D{sQPx0Dis)KW}jRKKy(S{I}vy z!uZMe!|y?l9-n^z?>})Y?xFDS&2N|fy?51*RDk>byur`^AKZTTf4&DlZ;-zS`eX4& x0sLj`r~PL1z&`%~-hbrS+(Y5to8K<|d+(|rsQ~x=d4r#S7xKRgf9mhT{{uY0TloM0 diff --git a/demo/test_bopti.c b/demo/test_bopti.c deleted file mode 100644 index de1c083..0000000 --- a/demo/test_bopti.c +++ /dev/null @@ -1,244 +0,0 @@ -#include "gintdemo.h" -#include -#include -#include - -#include - -#include - -/* - test_bopti() - Displays and moves many kinds of bitmaps. Here are the images used: - - Name Size Color Alpha - --------------------------------------------------------- - items.bmp 266 * 124 Gray - - sprites.bmp 66 * 33 Gray - - swords.bmp 88 * 16 Gray Full - --------------------------------------------------------- - zelda.bmp 86 * 280 Mono - - isometric.bmp 37 * 27 Mono Full - - - Mono Greater - --------------------------------------------------------- -*/ - -static void getwh(image_t *img, int *width, int *height) -{ - const uint8_t *data; - - if(!img) - { - *width = 0; - *height = 0; - return; - } - *width = img->width; - *height = img->height; - if(*width && *height) return; - - data = (uint8_t *)img->data; - *width = (data[0] << 8) | data[1]; - *height = (data[2] << 8) | data[3]; -} - -static void getxy(image_t *img, int *x, int *y) -{ - int width, height; - - getwh(img, &width, &height); - *x = 64 - (width >> 1); - *y = 28 - (height >> 1); -} - -static image_t *select(image_t *current) -{ - extern image_t res_bopti_thumbs; - extern image_t - res_items, - res_sprites, - res_swords, - res_zelda, - res_isometric; - extern image_t res_screen; - - struct { - image_t *img; - const char *name; - const char *info; - } images[] = { - { &res_items, "Items", "Gray" }, - { &res_sprites, "Sprites", "Gray" }, - { &res_swords, "Swords", "Gray Alpha" }, - { &res_zelda, "Zelda", "Mono" }, - { &res_isometric, "Isometric", "Mono Alpha" }, - { &res_screen, "TLT", "Mono" }, - { NULL, NULL, NULL } - }; - - image_t *thumbs = &res_bopti_thumbs; - int items = 0; - static int row = 0; - int leave = 1, i; - - while(images[items].img) items++; - - keyboard_setRepeatRate(625, 125); - gray_start(); - - while(1) - { - gclear(); - locate(1, 1, "Select an image:"); - - for(i = 0; i < items && i < 7; i++) - { - locate(2, 2 + i + (i > row), images[i].name); - gimage_part(100, 8 + 8 * (i + (i > row)), thumbs, 0, - 8 * i, 7, 7); - if(i == row) - { - int width, height; - getwh(images[i].img, &width, &height); - print(2, 2 + i + 1, "%d\x04%d", width, height); - locate(10, 2 + i + 1, images[i].info); - } - } - - grect(0, 8 * row + 8, 128, 8 * row + 23, color_invert); - gupdate(); - - do - { - leave = 1; - - switch(getkey()) - { - case KEY_UP: - row = (row + items - 1) % items; - break; - case KEY_DOWN: - row = (row + 1) % items; - break; - case KEY_EXE: - return images[row].img; - case KEY_EXIT: - return current; - default: - leave = 0; - break; - } - } - while(!leave); - } - - gray_stop(); -} - -void test_bopti(void) -{ - extern image_t res_opt_bitmap; - image_t *img = NULL; - - int leave = 1; - int black_bg = 0; - int x = 0, y = 0; - - - while(1) - { - keyboard_setRepeatRate(25, 25); - if(img && (img->format & channel_light)) - { - gray_start(); - gclear(); - - if(black_bg) grect(0, 0, 127, 63, color_invert); - if(img) gimage(x, y, img); - - grect(0, 55, 127, 63, color_white); - gimage_part(0, 56, &res_opt_bitmap, 0, 0, 96, 8); - } - else if(img) - { - gray_stop(); - dclear(); - - if(black_bg) drect(0, 0, 127, 63, color_invert); - if(img) dimage(x, y, img); - - drect(0, 55, 127, 63, color_white); - dimage_part(0, 56, &res_opt_bitmap, 0, 0, 96, 8); - } - else - { - gray_stop(); - - dclear(); - locate(3, 3, "No image selected"); - - dimage_part(0, 56, &res_opt_bitmap, 0, 0, 96, 8); - dupdate(); - } - - if(img) - { - int width, height; - getwh(img, &width, &height); - - if(x < 0) print(1, 4, "\x01"); - if(x + width > 128) print(21, 4, "\x02"); - - if(img->format & channel_light) - { - if(y < 0) gimage_part(61, 0, &res_opt_bitmap, - 122, 0, 6, 8); - if(y + height > 64) gimage_part(61, 48, - &res_opt_bitmap, 116, 0, 6, 8); - gupdate(); - } - else - { - if(y < 0) dimage_part(61, 0, &res_opt_bitmap, - 122, 0, 6, 8); - if(y + height > 64) dimage_part(61, 48, - &res_opt_bitmap, 116, 0, 6, 8); - dupdate(); - } - } - - do - { - leave = 1; - - switch(getkey()) - { - case KEY_EXIT: - keyboard_setRepeatRate(625, 125); - gray_stop(); - return; - - case KEY_F1: - img = select(img); - getxy(img, &x, &y); - break; - case KEY_F2: - black_bg = !black_bg; - break; - - case KEY_UP: y++; break; - case KEY_DOWN: y--; break; - case KEY_LEFT: x++; break; - case KEY_RIGHT: x--; break; - - default: - leave = 0; - } - } - while(!leave); - } - - keyboard_setRepeatRate(625, 125); - gray_stop(); - return; -} diff --git a/demo/test_gray.c b/demo/test_gray.c deleted file mode 100644 index 86d7f35..0000000 --- a/demo/test_gray.c +++ /dev/null @@ -1,104 +0,0 @@ -#include "gintdemo.h" -#include -#include -#include - -/* - test_gray() - Lets the user set the gray delays and see the results. -*/ - -static void draw(int delay1, int delay2, int selected, int gran) -{ - extern image_t res_opt_gray; - uint32_t *vl = gray_lightVRAM(); - uint32_t *vd = gray_darkVRAM(); - - gclear(); - locate(1, 1, "Gray engine"); - - for(int i = 0; i < 64; i++) - { - int offset = (i << 2) + 3; - vl[offset] = -(!(i & 16)); - vd[offset] = -(i < 32); - } - - print(2, 3, "light %d", delay1); - print(2, 4, " dark %d", delay2); - print(2, 6, "Mode: \x04%d", gran); - - int lengths[2] = { - 4 - (delay1 < 1000), - 4 - (delay2 < 1000) - }; - print(8, 3 + selected, "\x01"); - print(9 + lengths[selected], 3 + selected, "\x02"); - - gimage(0, 56, &res_opt_gray); - gupdate(); -} - -void test_gray(void) -{ - int delays[2]; // { light, dark } - int key, changed = 1; - int selected = 0; - int gran = 1; - - gray_getDelays(delays, delays + 1); - gray_start(); - - while(1) - { - if(changed) - { - gray_setDelays(delays[0], delays[1]); - draw(delays[0], delays[1], selected, gran); - } - changed = 0; - - key = getkey_opt(getkey_default, 25); - if(key == KEY_EXIT) break; - - changed = 1; - - switch(key) - { - case KEY_F1: - delays[0] = 912; - delays[1] = 1343; - break; - case KEY_F2: - delays[0] = 993; - delays[1] = 1609; - break; - case KEY_F3: - delays[0] = 860; - delays[1] = 1298; - break; - case KEY_F5: - if(gran >= 100) gran = 1; - else gran *= 10; - case KEY_UP: - selected = 0; - break; - case KEY_DOWN: - selected = 1; - break; - case KEY_LEFT: - if(delays[selected] - gran >= 100) - delays[selected] -= gran; - break; - case KEY_RIGHT: - if(delays[selected] + gran < 10000) - delays[selected] += gran; - break; - default: - changed = 0; - break; - } - } - - gray_stop(); -} diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c deleted file mode 100644 index 59f0471..0000000 --- a/demo/test_keyboard.c +++ /dev/null @@ -1,195 +0,0 @@ -#include "gintdemo.h" -#include -#include -#include -#include - -static void draw_keyboard(volatile uint8_t *state) -{ - int i, j, k, l; - int x, y; - - for(i = 0; i < 10; i++) for(j = 1; j < 8; j++) - { - // Eliminating keys that do not exist. - if(!i && j != 7) continue; - if(i && j == 7) continue; - if(i <= 4 && j == 6) continue; - if(i == 4 && j == 5) continue; - - x = 5 * j + 1; - y = 59 - 5 * i; - - // Space for the horizontal line. - y += 3 * (i < 7); - - // Moving the [AC/ON] key. - if(!i) x = 5 * (5) + 1, y = 61 - 5 * (4) + 1; - - // Drawing a filled shape when the key is pressed. - if(state[i] & (0x80 >> j)) - { - for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++) - if(abs(k) + abs(l) <= 2) - dpixel(x + k, y + l, color_black); - } - // Drawing a square border otherwise. - else - { - for(k = -1; k <= 1; k++) for(l = -1; l <= 1; l++) - if(k || l) dpixel(x + k, y + l, color_black); - } - } - - // Binding the arrow keys together for a more visual thing. - dpixel(28, 19, color_black); dpixel(29, 19, color_black); - dpixel(28, 24, color_black); dpixel(29, 24, color_black); - dpixel(26, 21, color_black); dpixel(26, 22, color_black); - dpixel(31, 21, color_black); dpixel(31, 22, color_black); - - // An horizontal line to separate parts of the keyboard. - dline(5, 28, 32, 28, color_black); -} - -typedef struct { - uint32_t key; - int repeats; -} history_key_t; - -typedef struct { - history_key_t *data; - int size; - int pressed; -} history_t; - -static int pressed_keys = 0; -static int releases = 0; - -static void history_push(history_t *h, event_t event) -{ - // Only reset the number of pressed keys in the history when the actual - // number of pressed keys reaches 0. - - if(event.type == event_key_release) - { - pressed_keys--; - if(!pressed_keys) h->pressed = 0; - return; - } - - // Now we're sure a key was pressed or repeated. Check if it's in the - // last pressed elements of the history array, if so, update it. - // Otherwise make space for a new entry and add it. - - if(event.type == event_key_press) pressed_keys++; - - for(int i = h->size - h->pressed; i < h->size; i++) - { - if(h->data[i].key == event.key.code) - { - h->data[i].repeats++; - return; - } - } - - // If there are already h->size keys pressed, do not add more. - if(event.type == event_key_press) h->pressed++; - if(h->pressed > h->size) return; - - for(int i = 0; i < h->size - 1; i++) h->data[i] = h->data[i + 1]; - h->data[h->size - 1].key = event.key.code; - h->data[h->size - 1].repeats = 1; -} - -static void draw_events(history_t *h) -{ - const char *key_names[] = { - "F1", "F2", "F3", "F4", "F5", "F6", - "SHIFT", "OPTN", "VARS", "MENU", "Left", "Up", - "ALPHA", "x^2", "^", "EXIT", "Down", "Right", - "X,\x1d,T", "log", "ln", "sin", "cos", "tan", - "[frac]", "F\x0f\x09" "D", "(", ")", ",", "\x09", - "7", "8", "9", "DEL", "AC/ON", NULL, - "4", "5", "6", "\x04", "\x05", NULL, - "1", "2", "3", "+", "-", NULL, - "0", ".", "\x08", "(-)", "EXE", NULL - }; - - int y = 3; - int y_limit = 8 - (h->pressed > h->size); - - extern font_t res_font_modern; - text_configure(&res_font_modern, color_black); - dtext(49, 12, "Key"); - dtext(89, 12, "Rep."); - // dprint(49, 7, "%d %d %d", h->pressed, pressed_keys, releases); - text_configure(NULL, color_black); - dline(45, 18, 120, 18, color_black); - - for(int i = 0; i < h->size && y < y_limit; i++) if(h->data[i].key) - { - dtext(49, y * 8 - 4, key_names[key_id(h->data[i].key)]); - if(h->data[i].repeats > 1) - dprint(89, y * 8 - 4, "%d", h->data[i].repeats); - y++; - } - - if(h->pressed > h->size) dtext(49, 52, "(more)"); -} - -/* - test_keyboard_events() - Real-time keyboard management with events. -*/ -void test_keyboard_events(void) -{ - history_key_t history_data[5] = { 0 }; - history_t history = { - .data = history_data, - .size = 5, - .pressed = 0, - }; - event_t event; - - // There may be keys pressed when this test starts. We need to detect - // which and create a valid history for them. - volatile const uint8_t *buffer = keyboard_stateBuffer(); - - // For the purpose of this function we don't need to calculate the - // other fields. - event_t push_event = { - .type = event_key_press, - .key.code = KEY_AC_ON, - }; - - // Finding all the pressed keys and pushing them in the history. - if(buffer[0] & 1) history_push(&history, push_event); - for(int row = 1; row <= 9; row++) for(int col = 0; col < 8; col++) - { - // Eliminate non-existing keys. - if(col > 6 || col <= (row <= 4)) continue; - - if(buffer[row] & (1 << col)) - { - push_event.key.code = (col << 4) | row; - history_push(&history, push_event); - } - } - - while(1) - { - dclear(); - locate(1, 1, "Keyboard and events"); - - draw_keyboard(keyboard_stateBuffer()); - - draw_events(&history); - dupdate(); - - event = waitevent(); - if(event.type == event_key_release) releases++; - if(event.type == event_key_press && event.key.code == KEY_EXIT) - break; - history_push(&history, event); - } -} diff --git a/demo/test_rtc.c b/demo/test_rtc.c deleted file mode 100644 index 4b8fa65..0000000 --- a/demo/test_rtc.c +++ /dev/null @@ -1,260 +0,0 @@ -#include "gintdemo.h" -#include -#include -#include - -#include -#include - -/* - test_rtc() - Just a clock. Of course using all this RTCTime conversion and this / 10 - is awfully un-optimized, but it's a test case so it's made to check the - values in the structure are correct. -*/ - -#include -#include -#include - -static void draw(rtc_time_t *time) -{ - extern image_t res_rtc_segments; - - const char *days[7] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }, *months[12] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", - "Oct", "Nov", "Dec" - }; - int x[6] = { 20, 33, 52, 65, 84, 97 }; - int digits[6]; - int i; - - digits[0] = time->hours / 10; - digits[1] = time->hours % 10; - digits[2] = time->minutes / 10; - digits[3] = time->minutes % 10; - digits[4] = time->seconds / 10; - digits[5] = time->seconds % 10; - - // Drawing digits. - for(i = 0; i < 6; i++) dimage_part(x[i], 8, &res_rtc_segments, - 12 * digits[i], 0, 11, 19); - // Drawing ':' between pairs of digits. - for(i = 0; i < 16; i++) dpixel(47 + 32 * (i >= 8) + (i & 1), - 14 + 5 * !!(i & 4) + !!(i & 2), color_black); - - // This should print time.year + 1900 but for the sake of this demo we - // have tweaked the field so that it already contains time.year + 1900. - print(4, 6, "%s %s %02d %4d", days[time->week_day], - months[time->month], time->month_day, time->year); -} - -static void callback(void) -{ - extern image_t res_opt_rtc; - rtc_time_t time; - rtc_getTime(&time); - - dclear(); - draw(&time); - dimage_part(0, 56, &res_opt_rtc, 0, 0, 19, 8); - dupdate(); -} - -static void set_region(rtc_time_t *time, int region, int value) -{ - switch(region) - { - case 0: - time->hours = 10 * value + (time->hours % 10); - break; - case 1: - time->hours = time->hours - (time->hours % 10) + value; - break; - case 2: - time->minutes = 10 * value + (time->minutes % 10); - break; - case 3: - time->minutes = time->minutes - (time->minutes % 10) + value; - break; - case 4: - time->seconds = 10 * value + (time->seconds % 10); - break; - case 5: - time->seconds = time->seconds - (time->seconds % 10) + value; - break; - case 6: - time->week_day = value; - break; - case 7: - time->month = value; - break; - case 8: - time->month_day = 10 * value + (time->month_day % 10); - break; - case 9: - time->month_day = time->month_day - (time->month_day % 10) - + value; - break; - case 10: - time->year = 1000 * value + (time->year % 1000); - break; - case 11: - time->year = time->year - (time->year % 1000) + 100 * value - + (time->year % 100); - break; - case 12: - time->year = time->year - (time->year % 100) + 10 * value - + (time->year % 10); - break; - case 13: - time->year = time->year - (time->year % 10) + value; - break; - } -} - -static void set(void) -{ - extern image_t res_opt_rtc; - image_t *opt = &res_opt_rtc; - - struct { - int x, y; - int w, h; - } regions[] = { - { 19, 7, 13, 21 }, { 32, 7, 13, 21 }, { 51, 7, 13, 21 }, - { 64, 7, 13, 21 }, { 83, 7, 13, 21 }, { 96, 7, 13, 21 }, - { 18, 39, 19, 9 }, { 42, 39, 19, 9 }, { 66, 39, 7, 9 }, - { 72, 39, 7, 9 }, { 84, 39, 7, 9 }, { 90, 39, 7, 9 }, - { 96, 39, 7, 9 }, { 102, 39, 7, 9 }, - }; - rtc_time_t time; - int region_count = 14; - int n = 0, slide = 0, key, leave; - - rtc_getTime(&time); - - while(1) - { - dclear(); - draw(&time); - drect(regions[n].x, regions[n].y, regions[n].x + regions[n].w - - 1, regions[n].y + regions[n].h - 1, color_invert); - - if(n == 6) dimage_part(0, 56, opt, 0, 9 * (1 + slide), 128, 8); - if(n == 7) dimage_part(0, 56, opt, 0, 9 * (3 + slide), 128, 8); - else dimage_part(0, 56, opt, 22 + 22 * (n == region_count - 1), - 0, 19, 8); - - dupdate(); - - do - { - leave = 1; - key = getkey(); - if(key == KEY_EXIT) return; - - else if(key == KEY_F1 || key == KEY_EXE) - { - slide = 0; - if(++n == region_count) - { - rtc_setTime(&time); - return; - } - } - - else if(key == KEY_F6) - { - if(n == 6) slide = (slide + 1) % 2; - if(n == 7) slide = (slide + 1) % 3; - } - - else if((key & 0x0f) == 9) // Other F-keys - { - int k = 7 - (key >> 4); // Number of F-key - - if(n == 7) - { - int month = k + 4 * slide - 2; - set_region(&time, n, month); - n++; - slide = 0; - } - - else if(n == 6 && (slide != 1 || k != 5)) - { - int day = (k + 4 * slide - 1) % 7; - set_region(&time, n, day); - n++; - slide = 0; - } - - else leave = 0; - } - - else if(isdigit(key_char(key))) // Numbers - { - int val = key_char(key) - '0'; - int ok = 1; - - if(n == 0) ok = (val <= 2); - if(n == 1) - { - int max = time.hours >= 20 ? 3 : 9; - ok = (val <= max); - } - if(n == 2 || n == 4) ok = (val <= 5); - if(n == 8) ok = (val <= 3); - if(n == 9) - { - int max = time.month_day >= 30 ? 1 : 9; - ok = (val <= max); - } - - if(ok) - { - set_region(&time, n, val); - n++; - if(n == region_count) - { - rtc_setTime(&time); - return; - } - slide = 0; - } - else leave = 0; - } - - else leave = 0; - } while(!leave); - } - - while(getkey() != KEY_EXIT); -} - -void test_rtc(void) -{ - int key, cb_id; - - cb_id = rtc_cb_add(rtc_freq_1Hz, callback, 0); - callback(); - - while(1) - { - key = getkey(); - - if(key == KEY_EXIT) break; - if(key == KEY_F1) - { - rtc_cb_edit(cb_id, rtc_freq_none, NULL); - set(); - callback(); - rtc_cb_edit(cb_id, rtc_freq_1Hz, callback); - } - } - - rtc_cb_end(cb_id); -} diff --git a/demo/test_tales.c b/demo/test_tales.c deleted file mode 100644 index 1ad6a48..0000000 --- a/demo/test_tales.c +++ /dev/null @@ -1,158 +0,0 @@ -#include "gintdemo.h" -#include -#include -#include - -#include - -/* - test_tales() - Displays some text using different modes and clipping options. -*/ - -static font_t *select(font_t *current) -{ - extern font_t res_font_modern; - struct { - font_t *font; - const char *name; - } fonts[] = { - { NULL, "gint default" }, - { &res_font_modern, "Modern" }, - }; - int font_number = 2; - - static int row = 0; - int i, leave; - - while(1) - { - text_configure(NULL, color_black); - - dclear(); - locate(1, 1, "Select a font:"); - - for(i = 0; i < font_number && i < 6; i++) - { - if(fonts[i].font) - { - int height = fonts[i].font->line_height; - int y = (i + 2) * 8 - 8 + ((7 - height) >> 1); - - text_configure(fonts[i].font, color_black); - dtext(7, y, fonts[i].name); - } - else - { - text_configure(NULL, color_black); - locate(2, i + 2, fonts[i].name); - } - - } - - drect(0, 8 * row + 8, 128, 8 * row + 15, color_invert); - dupdate(); - - do - { - leave = 1; - - switch(getkey()) - { - case KEY_UP: - row = (row + font_number - 1) % font_number; - break; - case KEY_DOWN: - row = (row + 1) % font_number; - break; - case KEY_EXE: - return fonts[row].font; - case KEY_EXIT: - return current; - default: - leave = 0; - break; - } - } - while(!leave); - } -} - -void test_tales(void) -{ - color_t colors[] = { color_black, color_dark, color_light, color_white, - color_invert }; - extern image_t res_opt_tales; - font_t *font = NULL; - - int black_bg = 0; - int color = 0; - int i, x, height; - int leave; - - gray_start(); - while(1) - { - gclear(); - if(black_bg) grect(0, 0, 127, 54, color_invert); - - if(font) - { - text_configure(font, colors[color]); - height = font->line_height + 1; - } - else - { - text_configure(NULL, colors[color]); - height = 8; - } - - for(i = 0; i < 6 && 2 + (i + 1) * height < 56; i++) - { - char str[17]; - for(int j = 0; j < 16; j++) str[j] = 32 + (i << 4) + j; - str[16] = 0; - - gtext(1, 1 + i * height, str); - } - - gimage(0, 56, &res_opt_tales); - - x = 45 + 8 * color; - gline(x, 57, x + 5, 57, color_black); - gline(x, 57, x, 62, color_black); - gline(x + 5, 57, x + 5, 62, color_black); - gline(x, 62, x + 5, 62, color_black); - - gupdate(); - - do - { - leave = 1; - - switch(getkey()) - { - case KEY_F1: - gray_stop(); - font = select(font); - gray_start(); - break; - case KEY_F2: - color = (color + 1) % 5; - break; - case KEY_F5: - black_bg = !black_bg; - break; - - case KEY_EXIT: - gray_stop(); - text_configure(NULL, color_black); - return; - default: - leave = 0; - break; - } - } - while (!leave); - } -} diff --git a/demo/test_timer.c b/demo/test_timer.c deleted file mode 100644 index 011f365..0000000 --- a/demo/test_timer.c +++ /dev/null @@ -1,273 +0,0 @@ -#include "gintdemo.h" -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -static void draw(int new_tab); -static clock_config_t conf; - -//--- -// Timer-RTC comparison. -// The precision of the timer is measured by comparing it to the RTC. -//--- - -static volatile int elapsed_timer = -1; -static volatile int elapsed_rtc = -1; -static int cb_id = -1; -static timer_t *htimer = NULL; - -static void timing_rtc(void) -{ - elapsed_rtc++; -} - -static void timing_timer(void) -{ - elapsed_timer++; -} - -static void timing_start(void) -{ - uint32_t delay = clock_setting(64, clock_Hz); - htimer = htimer_setup(timer_user, delay, timer_Po_4, 0); - timer_attach(htimer, timing_timer, NULL); - timer_start(htimer); - - rtc_cb_edit(cb_id, rtc_freq_64Hz, timing_rtc); - - elapsed_timer = 0; - elapsed_rtc = 0; -} - - - -//--- -// Drawing. -//--- - -/* - small_text() - Renders small text using a minimalist bitmap-based font. -*/ -static void small_text(int x, int y, const char *text, int alignment) -{ - extern image_t res_clock_chars; - image_t *chars = &res_clock_chars; - const char *table = "0123456789kMHz*/"; - - if(alignment) x -= 2 * strlen(text) - 1, y -= 2; - int c; - - while(*text) - { - const char *ptr = strchr(table, *text++); - if(!ptr) continue; - c = ptr - table; - - dimage_part(x, y, chars, c << 2, 0, 3, 5); - x += 4; - } -} - -/* - getFreq() - Prints the given frequency in a string on the form: - 332kHz - There are 1, 2 or 3 characters for the value, and 2 or 3 - characters for the unit. The string is compacted. -*/ -void getFreq(char *str, int freq) -{ - if(freq < 1000) sprintf(str, "%dHz", freq); - else if(freq < 1000000) sprintf(str, "%dkHz", (freq + 500) / 1000); - else sprintf(str, "%dMHz", (freq + 500000) / 1000000); -} - -/* - dislay_freq() - Displays a frequency value a unit, in an simple form. -*/ -static void display_freq(int x, int y, int freq) -{ - int ratio, letter, dot, i; - char buffer[10]; - - if(freq <= 0) - { - dtext(x, y, "Unknown"); - return; - } - if(freq < 10000) - { - dprint(x, y, "%5d", freq); - small_text(x + 31, y + 2, "Hz", 0); - return; - } - - if(freq < 10000000) ratio = 1, letter = 'k'; - else ratio = 1000, letter = 'M'; - - dot = 1 + (freq >= 10000 * ratio) + (freq >= 100000 * ratio); - freq += (ratio * (1 + 9 * (dot >= 2) + 90 * (dot >= 3))) / 2; - snprintf(buffer, 6, "%d", freq); - - for(i = 4; i > dot; i--) buffer[i] = buffer[i - 1]; - buffer[dot] = '.'; - - dprint(x, y, buffer); - sprintf(buffer, "%cHz", letter); - small_text(x + 31, y + 2, buffer, 0); -} - -/* - draw() - Draws the test interface. -*/ -static void draw(int tab) -{ - extern image_t res_opt_timer; - extern image_t res_clock_7705; - extern image_t res_clock_7305; - - char buffer[16]; - - dclear(); - dimage(0, 56, &res_opt_timer); - - if(!tab) - { - locate(1, 1, "Clock frequency"); - dtext(7, 20, "B\x1e"); - display_freq(24, 20, conf.Bphi_f); - dtext(7, 28, "I\x1e"); - display_freq(24, 28, conf.Iphi_f); - dtext(7, 36, "P\x1e"); - display_freq(24, 36, conf.Pphi_f); - - if(isSH3()) - { - dimage(64, 0, &res_clock_7705); - - getFreq(buffer, conf.CKIO_f); - small_text(84, 16, buffer, 1); - - sprintf(buffer, "*%d", conf.PLL1); - small_text(84, 34, buffer, 1); - - if(conf.Iphi_div1 == 1) - dline(85, 43, 99, 43, color_black); - else - { - sprintf(buffer, "/%d", conf.Iphi_div1); - small_text(89, 41, buffer, 0); - } - if(conf.Pphi_div1 == 1) - dline(85, 50, 99, 50, color_black); - else - { - sprintf(buffer, "/%d", conf.Pphi_div1); - small_text(89, 48, buffer, 0); - } - } - else - { - dimage(64, 0, &res_clock_7305); - - getFreq(buffer, conf.RTCCLK_f); - small_text(82, 13, buffer, 1); - - sprintf(buffer, "*%d", conf.FLL); - small_text(82, 23, buffer, 1); - - sprintf(buffer, "*%d", conf.PLL); - small_text(82, 33, buffer, 1); - - sprintf(buffer, "/%d", conf.Bphi_div1); - small_text(87, 40, buffer, 0); - sprintf(buffer, "/%d", conf.Iphi_div1); - small_text(87, 47, buffer, 0); - sprintf(buffer, "/%d", conf.Pphi_div1); - small_text(87, 54, buffer, 0); - } - } - else - { - int timer = elapsed_timer, rtc = elapsed_rtc; // just in case - - locate(1, 1, "Timer/RTC comparison"); - - locate(2, 3, "Timer"); - if(timer >= 0) print(12, 3, "%04x", timer); - else locate(12, 3, "..."); - - locate(2, 4, "RTC"); - if(rtc >= 0) print(12, 4, "%04x", rtc); - else locate(12, 4, "..."); - - // We define the accuracy of the timer as the ratio between the - // two counters. - locate(2, 5, "Accuracy"); - if(rtc > 0 && timer > 0) - { - int ratio; - if(timer <= rtc) ratio = (10000 * timer) / rtc; - else ratio = (10000 * rtc) / timer; - - print(12, 5, "%d.%02d%%", ratio / 100, ratio % 100); - } - else locate(12, 5, "..."); - } - - dupdate(); -} - - - -//--- -// Timer/clock test. -//--- - -/* - test_timer() - Clock timer and timer precision. -*/ -void test_timer(void) -{ - int tab = 0; - - conf = clock_config(); - - elapsed_timer = -1; - elapsed_rtc = -1; - cb_id = rtc_cb_add(rtc_freq_64Hz, timing_start, 0); - - text_configure(NULL, color_black); - - while(1) - { - draw(tab); - - switch(getkey_opt(getkey_none, 25)) - { - case KEY_EXIT: - rtc_cb_end(cb_id); - timer_stop(htimer); - return; - - case KEY_F1: - tab = 0; - break; - case KEY_F2: - tab = 1; - break; - } - } -} diff --git a/include/7305.h b/include/7305.h deleted file mode 100644 index a34420f..0000000 --- a/include/7305.h +++ /dev/null @@ -1,673 +0,0 @@ -#ifndef _7305_H -#define _7305_H 1 - -/* - Double-underscore prefixed structures (e.g. __st_rtc_counter) are used - internally but are not meant to be used in user programs. - - Underscore-prefixed names (e.g. _R64CNT) are used to avoid name - conflicts (e.g. STRUCTURE.RTC would expand to STRUCTURE.((T *)0x...)). -*/ - -#pragma pack(push, 1) -#define gap(n) unsigned: n << 3 - -//--- -// Interrupt controller, part 1. -//--- - -struct _st_intc -{ - union { - unsigned short WORD; - struct { - unsigned const NMIL :1; - unsigned MAI :1; - unsigned :4; - unsigned NMIB :1; - unsigned NMIE :1; - unsigned :2; - unsigned LVLMODE :1; - unsigned :5; - }; - } ICR0; - - char gap1[14]; - - union { - unsigned int LONG; - struct { - unsigned IRQ0 :4; - unsigned IRQ1 :4; - unsigned IRQ2 :4; - unsigned IRQ3 :4; - unsigned IRQ4 :4; - unsigned IRQ5 :4; - unsigned IRQ6 :4; - unsigned IRQ7 :4; - }; - } INTPRI00; - - char gap2[8]; - - union { - unsigned short WORD; - struct { - unsigned IRQ0S :2; - unsigned IRQ1S :2; - unsigned IRQ2S :2; - unsigned IRQ3S :2; - unsigned IRQ4S :2; - unsigned IRQ5S :2; - unsigned IRQ6S :2; - unsigned IRQ7S :2; - }; - } ICR1; - - char gap3[6]; - - union { - unsigned char BYTE; - struct { - unsigned IRQ0 :1; - unsigned IRQ1 :1; - unsigned IRQ2 :1; - unsigned IRQ3 :1; - unsigned IRQ4 :1; - unsigned IRQ5 :1; - unsigned IRQ6 :1; - unsigned IRQ7 :1; - }; - } INTREQ00; - - char gap4[31]; - - union { - unsigned char BYTE; - struct { - unsigned IRQ0 :1; - unsigned IRQ1 :1; - unsigned IRQ2 :1; - unsigned IRQ3 :1; - unsigned IRQ4 :1; - unsigned IRQ5 :1; - unsigned IRQ6 :1; - unsigned IRQ7 :1; - }; - } INTMSK00; - - char gap5[31]; - - union { - unsigned char BYTE; - struct { - unsigned _IRQ0 :1; - unsigned _IRQ1 :1; - unsigned _IRQ2 :1; - unsigned _IRQ3 :1; - unsigned _IRQ4 :1; - unsigned _IRQ5 :1; - unsigned _IRQ6 :1; - unsigned _IRQ7 :1; - }; - } INTMSKCLR00; - - char gap6[91]; - - union { - unsigned short WORD; - struct { - unsigned const NMIL :1; - unsigned :14; - unsigned NMIFL :1; - }; - } NMIFCR; - - char gap7[6029118]; - - union { - unsigned int LONG; - struct { - unsigned HEXA_A5 :8; - unsigned :16; - unsigned UIMASK :4; - unsigned :4; - }; - } USERIMSK; - -} __attribute__((packed)); - - - -//--- -// Interrupt controller, part 2. -//--- - -struct _st_intx -{ - union { - unsigned short WORD; - struct { - unsigned TMU0_0 :4; - unsigned TMU0_1 :4; - unsigned TMU0_2 :4; - unsigned IrDA :4; - }; - } IPRA; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned JPU :4; - unsigned LCDC :4; - unsigned DMAC1A :4; - unsigned BEU2_1 :4; - }; - } IPRB; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned TMU1_0 :4; - unsigned TMU1_1 :4; - unsigned TMU1_2 :4; - unsigned SPU :4; - }; - } IPRC; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned :4; - unsigned MMCIF :4; - unsigned :4; - unsigned ATAPI :4; - }; - } IPRD; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned DMAC0A :4; - unsigned VARIOUS :4; - unsigned SCIFA3 :4; - unsigned VPU5F :4; - }; - } IPRE; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned _KEYSC :4; - unsigned DMAC0B :4; - unsigned USB01 :4; - unsigned CMT :4; - }; - } IPRF; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned SCIF0 :4; - unsigned SCIF1 :4; - unsigned SCIF2 :4; - unsigned VEU3F0 :4; - }; - } IPRG; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned MSIOF0 :4; - unsigned MSIOF1 :4; - unsigned I2C1 :4; - unsigned I2C0 :4; - }; - } IPRH; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned SCIFA4 :4; - unsigned ICB :4; - unsigned TSIF :4; - unsigned _2DG_ICB :4; - }; - } IPRI; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned CEU2_1 :4; - unsigned EtherMAC :4; - unsigned FSI :4; - unsigned SDHI1 :4; - }; - } IPRJ; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned _RTC :4; - unsigned DMAC1B :4; - unsigned ICB :4; - unsigned SDHI0 :4; - }; - } IPRK; - gap(2); - - union { - unsigned short WORD; - struct { - unsigned SCIFA5 :4; - unsigned :4; - unsigned TPU :4; - unsigned _2DDMAC :4; - }; - } IPRL; - char gap1[82]; - - union - { - unsigned char BYTE; - struct { - unsigned :1; - unsigned TUNI2 :1; - unsigned TUNI1 :1; - unsigned TUNI0 :1; - unsigned SDHII3 :1; - unsigned SDHII2 :1; - unsigned SDHII1 :1; - unsigned SDHII0 :1; - }; - } IMR0; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned VOUI :1; - unsigned VEU1I :1; - unsigned BEU0I :1; - unsigned CEUOI :1; - unsigned DEI3 :1; - unsigned DEI2 :1; - unsigned DEI1 :1; - unsigned DEI0 :1; - }; - } IMR1; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :3; - unsigned VPUI :1; - unsigned ATAPI :1; - unsigned EtherMAC :1; - unsigned :1; - unsigned SCIFA0 :1; - }; - } IMR2; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned DEI3 :1; - unsigned DEI2 :1; - unsigned DEI1 :1; - unsigned DEI0 :1; - unsigned :3; - unsigned IRDAI :1; - }; - } IMR3; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :1; - unsigned TUNI2 :1; - unsigned TUNI1 :1; - unsigned TUNI0 :1; - unsigned JPUI :1; - unsigned :2; - unsigned LCDCI :1; - }; - } IMR4; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned KEYI :1; - unsigned DADERR :1; - unsigned DEI5 :1; - unsigned DEI4 :1; - unsigned VEU0I :1; - unsigned SCIF2 :1; - unsigned SCIF1 :1; - unsigned SCIF0 :1; - }; - } IMR5; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :2; - unsigned ICBI :1; - unsigned SCIFA4 :1; - unsigned CEU1I :1; - unsigned :1; - unsigned MSIOFI0 :1; - unsigned MSIOFI1 :1; - }; - } IMR6; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned DTE0I :1; - unsigned WAITOI :1; - unsigned TACK0I :1; - unsigned AL0I :1; - unsigned DTE1I :1; - unsigned WAIT1I :1; - unsigned TACK1I :1; - unsigned AL1I :1; - }; - } IMR7; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned SDHII3 :1; - unsigned SDHII2 :1; - unsigned SDHII1 :1; - unsigned SDHII0 :1; - unsigned :2; - unsigned SCFIA5 :1; - unsigned FSI :1; - }; - } IMR8; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :3; - unsigned CMTI :1; - unsigned :1; - unsigned USI1 :1; - unsigned USI0 :1; - unsigned :1; - }; - } IMR9; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :1; - unsigned DADERR :1; - unsigned DEI5 :1; - unsigned DEI4 :1; - unsigned :1; - unsigned ATI :1; - unsigned PRI :1; - unsigned CUI :1; - }; - } IMR10; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned BRK :1; - unsigned CEI :1; - unsigned INI :1; - unsigned TRI :1; - unsigned :1; - unsigned TPUI :1; - unsigned LMBI :1; - unsigned TSIFI :1; - }; - } IMR11; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :7; - unsigned _2DDMAC :1; - }; - } IMR12; - char gap2[15]; - - union - { - unsigned char BYTE; - struct { - unsigned :1; - unsigned TUNI2 :1; - unsigned TUNI1 :1; - unsigned TUNI0 :1; - unsigned SDHII3 :1; - unsigned SDHII2 :1; - unsigned SDHII1 :1; - unsigned SDHII0 :1; - }; - } _IMCR0; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned VOUI :1; - unsigned VEU1I :1; - unsigned BEU0I :1; - unsigned CEUOI :1; - unsigned DEI3 :1; - unsigned DEI2 :1; - unsigned DEI1 :1; - unsigned DEI0 :1; - }; - } _IMCR1; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :3; - unsigned VPUI :1; - unsigned ATAPI :1; - unsigned EtherMAC :1; - unsigned :1; - unsigned SCIFA0 :1; - }; - } _IMCR2; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned DEI3 :1; - unsigned DEI2 :1; - unsigned DEI1 :1; - unsigned DEI0 :1; - unsigned :3; - unsigned IRDAI :1; - }; - } _IMCR3; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :1; - unsigned TUNI2 :1; - unsigned TUNI1 :1; - unsigned TUNI0 :1; - unsigned JPUI :1; - unsigned :2; - unsigned LCDCI :1; - }; - } _IMCR4; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned KEYI :1; - unsigned DADERR :1; - unsigned DEI5 :1; - unsigned DEI4 :1; - unsigned VEU0I :1; - unsigned SCIF2 :1; - unsigned SCIF1 :1; - unsigned SCIF0 :1; - }; - } _IMCR5; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :2; - unsigned ICBI :1; - unsigned SCIFA4 :1; - unsigned CEU1I :1; - unsigned :1; - unsigned MSIOFI0 :1; - unsigned MSIOFI1 :1; - }; - } _IMCR6; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned DTE0I :1; - unsigned WAITOI :1; - unsigned TACK0I :1; - unsigned AL0I :1; - unsigned DTE1I :1; - unsigned WAIT1I :1; - unsigned TACK1I :1; - unsigned AL1I :1; - }; - } _IMCR7; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned SDHII3 :1; - unsigned SDHII2 :1; - unsigned SDHII1 :1; - unsigned SDHII0 :1; - unsigned :2; - unsigned SCFIA5 :1; - unsigned FSI :1; - }; - } _IMCR8; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :3; - unsigned CMTI :1; - unsigned :1; - unsigned USI1 :1; - unsigned USI0 :1; - unsigned :1; - }; - } _IMCR9; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :1; - unsigned DADERR :1; - unsigned DEI5 :1; - unsigned DEI4 :1; - unsigned :1; - unsigned ATI :1; - unsigned PRI :1; - unsigned CUI :1; - }; - } _IMCR10; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned BRK :1; - unsigned CEI :1; - unsigned INI :1; - unsigned TRI :1; - unsigned :1; - unsigned TPUI :1; - unsigned LMBI :1; - unsigned TSIFI :1; - }; - } _IMCR11; - gap(3); - - union - { - unsigned char BYTE; - struct { - unsigned :7; - unsigned _2DDMAC :1; - }; - } _IMCR12; - -} __attribute__((packed)); - - - -#define INTC (*(volatile struct _st_intc *)0xa4140000) -#define INTX (*(volatile struct _st_intx *)0xa4080000) - -#pragma pack(pop) -#endif // _7305_H diff --git a/include/7705.h b/include/7705.h deleted file mode 100644 index 3282d34..0000000 --- a/include/7705.h +++ /dev/null @@ -1,1099 +0,0 @@ -#ifndef _7705_H -#define _7705_H 1 - - -// Internal Control Registers definition : - -struct st_cpg { /* struct CPG */ - union { /* FRQCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :3; /* */ - unsigned short CKOEN:1; /* CKOEN */ - unsigned short :2; /* */ - unsigned short STC :2; /* STC */ - unsigned short :2; /* */ - unsigned short IFC :2; /* IFC */ - unsigned short :2; /* */ - unsigned short _PFC :2; /* PFC */ - } BIT; /* */ - } FRQCR; /* */ -}; /* */ -struct st_wdt { /* struct WDT */ - union { /* WTCNT */ - unsigned char READ; /* Read Access*/ - unsigned short WRITE; /* Write Access*/ - } WTCNT; /* */ - union { /* WTCSR */ - union { /* Read Access*/ - unsigned char BYTE; /* Byte Access*/ - struct { /* Bit Access*/ - unsigned char TME :1; /* TME */ - unsigned char WTIT:1; /* WT/IT */ - unsigned char RSTS:1; /* RSTS */ - unsigned char WOVF:1; /* WOVF */ - unsigned char IOVF:1; /* IOVF */ - unsigned char CKS :3; /* CKS */ - } BIT; /* */ - } READ; /* */ - unsigned short WRITE; /* Write Access*/ - } WTCSR; /* */ -}; - -struct st_pa { /* struct PA */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pb { /* struct PB */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pc { /* struct PC */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pd { /* struct PD */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pe { /* struct PE */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pf { /* struct PF */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pg { /* struct PG */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_ph { /* struct PH */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pj { /* struct PJ */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pk { /* struct PK */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pl { /* struct PL */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :4; /* Bit 7-4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_scp { /* struct SCP */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :2; /* Bit 7,6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pm { /* struct PM */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char :1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ -struct st_pn { /* struct PN */ - union { /* */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char B7:1; /* Bit 7 */ - unsigned char B6:1; /* Bit 6 */ - unsigned char B5:1; /* Bit 5 */ - unsigned char B4:1; /* Bit 4 */ - unsigned char B3:1; /* Bit 3 */ - unsigned char B2:1; /* Bit 2 */ - unsigned char B1:1; /* Bit 1 */ - unsigned char B0:1; /* Bit 0 */ - } BIT; /* */ - } DR; /* */ -}; /* */ - -struct st_usb { /* struct USB */ - union { /* UCLKCR */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char USSCS:2; /* USSCS */ - unsigned char USBEN:1; /* USBEN */ - } BIT; /* */ - } UCLKCR; /* */ - char wk1[4063223]; /* */ - unsigned char EPDR0i; /* EPDR0i */ - char wk2[3]; /* */ - unsigned char EPDR0o; /* EPDR0o */ - char wk3[3]; /* */ - unsigned char EPDR0s; /* EPDR0s */ - char wk4[3]; /* */ - unsigned char EPDR1; /* EPDR1 */ - char wk5[3]; /* */ - unsigned char EPDR2; /* EPDR2 */ - char wk6[3]; /* */ - unsigned char EPDR3; /* EPDR3 */ - char wk7[3]; /* */ - union { /* IFR0 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char BRST :1; /* BRST */ - unsigned char EP1FULL :1; /* EP1FULL */ - unsigned char EP2TR :1; /* EP2TR */ - unsigned char EP2EMPTY:1; /* EP2EMPTY */ - unsigned char SETUPTS :1; /* SETUPTS */ - unsigned char EP0oTS :1; /* EP0oTS */ - unsigned char EP0iTR :1; /* EP0iTR */ - unsigned char EP0iTS :1; /* EP0iTS */ - } BIT; /* */ - } IFR0; /* */ - char wk8[3]; /* */ - union { /* IFR1 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :4; /* */ - unsigned char VBUSMN:1; /* VBUSMN */ - unsigned char EP3TS :1; /* EP3TS */ - unsigned char EP3TR :1; /* EP3TR */ - unsigned char VBUS :1; /* VBUS */ - } BIT; /* */ - } IFR1; /* */ - char wk9[3]; /* */ - union { /* TRG */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :1; /* */ - unsigned char EP3PKTE :1; /* EP3PKTE */ - unsigned char EP1RDFN :1; /* EP1RDFN */ - unsigned char EP2PKTE :1; /* EP2PKTE */ - unsigned char :1; /* */ - unsigned char EP0sRDFN:1; /* EP0sRDFN */ - unsigned char EP0oRDFN:1; /* EP0oRDFN */ - unsigned char EP0iPKTE:1; /* EP0iPKTE */ - } BIT; /* */ - } TRG; /* */ - char wk10[3]; /* */ - union { /* FCLR */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :1; /* */ - unsigned char EP3CLR :1; /* EP3CLR */ - unsigned char EP1CLR :1; /* EP1CLR */ - unsigned char EP2CLR :1; /* EP2CLR */ - unsigned char :2; /* */ - unsigned char EP0oCLR:1; /* EP0oCLR */ - unsigned char EP0iCLR:1; /* EP0iCLR */ - } BIT; /* */ - } FCLR; /* */ - char wk11[3]; /* */ - unsigned char EPSZ0o; /* EPSZ0o */ - char wk12[3]; /* */ - union { /* DASTS */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :2; /* */ - unsigned char EP3DE :1; /* EP3DE */ - unsigned char EP2DE :1; /* EP2DE */ - unsigned char :3; /* */ - unsigned char EP0iDE:1; /* EP0iDE */ - } BIT; /* */ - } DASTS; /* */ - char wk13[3]; /* */ - union { /* EPSTL */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :4; /* */ - unsigned char EP3STL:1; /* EP3STL */ - unsigned char EP2STL:1; /* EP2STL */ - unsigned char EP1STL:1; /* EP1STL */ - unsigned char EP0STL:1; /* EP0STL */ - } BIT; /* */ - } EPSTL; /* */ - char wk14[3]; /* */ - union { /* IER0 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char BRST :1; /* BRST */ - unsigned char EP1FULL :1; /* EP1FULL */ - unsigned char EP2TR :1; /* EP2TR */ - unsigned char EP2EMPTY:1; /* EP2EMPTY */ - unsigned char SETUPTS :1; /* SETUPTS */ - unsigned char EP0oTS :1; /* EP0oTS */ - unsigned char EP0iTR :1; /* EP0iTR */ - unsigned char EP0iTS :1; /* EP0iTS */ - } BIT; /* */ - } IER0; /* */ - char wk15[3]; /* */ - union { /* IER1 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :5; /* */ - unsigned char EP3TS:1; /* EP3TS */ - unsigned char EP3TR:1; /* EP3TR */ - unsigned char VBUS :1; /* VBUS */ - } BIT; /* */ - } IER1; /* */ - char wk16[3]; /* */ - unsigned char EPSZ1; /* EPSZ1 */ - char wk17[3]; /* */ - union { /* DMAR */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :6; /* */ - unsigned char EP2DMAE:1; /* EP2DMAE */ - unsigned char EP1DMAE:1; /* EP1DMAE */ - } BIT; /* */ - } DMAR; /* */ - char wk18[3]; /* */ - union { /* ISR0 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char BRST :1; /* BRST */ - unsigned char EP1FULL :1; /* EP1FULL */ - unsigned char EP2TR :1; /* EP2TR */ - unsigned char EP2EMPTY:1; /* EP2EMPTY */ - unsigned char SETUPTS :1; /* SETUPTS */ - unsigned char EP0oTS :1; /* EP0oTS */ - unsigned char EP0iTR :1; /* EP0iTR */ - unsigned char EP0iTS :1; /* EP0iTS */ - } BIT; /* */ - } ISR0; /* */ - char wk19[3]; /* */ - union { /* ISR1 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :5; /* */ - unsigned char EP3TS:1; /* EP3TS */ - unsigned char EP3TR:1; /* EP3TR */ - unsigned char VBUS :1; /* VBUS */ - } BIT; /* */ - } ISR1; /* */ - char wk20[23]; /* */ - union { /* XVERCR */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :6; /* */ - unsigned char XVEROFF:1; /* XVEROFF */ - } BIT; /* */ - } XVERCR; /* */ -}; -struct st_intc { /* struct INTC */ - union { /* ICR0 */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short NMIL:1; /* NMIL */ - unsigned short :6; /* */ - unsigned short NMIE:1; /* NMIE */ - } BIT; /* */ - } ICR0; /* */ - union { /* IPRA */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short _TMU0:4; /* TMU0 */ - unsigned short _TMU1:4; /* TMU1 */ - unsigned short _TMU2:4; /* TMU2 */ - unsigned short _RTC :4; /* RTC */ - } BIT; /* */ - } IPRA; /* */ - union { /* IPRB */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short _WDT:4; /* WDT */ - unsigned short _REF:4; /* REF */ - } BIT; /* */ - } IPRB; /* */ - char wk[234]; /* */ - unsigned int TRA; /* TRA */ - unsigned int EXPEVT; /* EXPEVT */ - unsigned int INTEVT; /* INTEVT */ -}; -struct st_intx { /* struct INTX */ - unsigned int INTEVT2; /* INTEVT2 */ - union { /* IRR0 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char PINT0R:1; /* PINT0R */ - unsigned char PINT1R:1; /* PINT1R */ - unsigned char IRQ5R :1; /* IRQ5R */ - unsigned char IRQ4R :1; /* IRQ4R */ - unsigned char IRQ3R :1; /* IRQ3R */ - unsigned char IRQ2R :1; /* IRQ2R */ - unsigned char IRQ1R :1; /* IRQ1R */ - unsigned char IRQ0R :1; /* IRQ0R */ - } BIT; /* */ - } IRR0; /* */ - char wk1; /* */ - union { /* IRR1 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char TXI0R:1; /* TXI0R */ - unsigned char :1; /* */ - unsigned char RXI0R:1; /* RXI0R */ - unsigned char ERI0R:1; /* ERI0R */ - unsigned char DEI3R:1; /* DEI3R */ - unsigned char DEI2R:1; /* DEI2R */ - unsigned char DEI1R:1; /* DEI1R */ - unsigned char DEI0R:1; /* DEI0R */ - } BIT; /* */ - } IRR1; /* */ - char wk2; /* */ - union { /* IRR2 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :3; /* */ - unsigned char ADIR :1; /* ADIR */ - unsigned char TXI2R:1; /* TXI2R */ - unsigned char :1; /* */ - unsigned char RXI2R:1; /* RXI2R */ - unsigned char ERI2R:1; /* ERI2R */ - } BIT; /* */ - } IRR2; /* */ - char wk3[7]; /* */ - union { /* ICR1 */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short MAI :1; /* MAI */ - unsigned short IRQLVL:1; /* IRQLVL */ - unsigned short BLMSK :1; /* BLMSK */ - unsigned short :1; /* */ - unsigned short IRQ5S :2; /* IRQ5S */ - unsigned short IRQ4S :2; /* IRQ4S */ - unsigned short IRQ3S :2; /* IRQ3S */ - unsigned short IRQ2S :2; /* IRQ2S */ - unsigned short IRQ1S :2; /* IRQ1S */ - unsigned short IRQ0S :2; /* IRQ0S */ - } BIT; /* */ - } ICR1; /* */ - union { /* ICR2 */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PINT15S:1; /* PINT15S */ - unsigned short PINT14S:1; /* PINT14S */ - unsigned short PINT13S:1; /* PINT13S */ - unsigned short PINT12S:1; /* PINT12S */ - unsigned short PINT11S:1; /* PINT11S */ - unsigned short PINT10S:1; /* PINT10S */ - unsigned short PINT9S :1; /* PINT9S */ - unsigned short PINT8S :1; /* PINT8S */ - unsigned short PINT7S :1; /* PINT7S */ - unsigned short PINT6S :1; /* PINT6S */ - unsigned short PINT5S :1; /* PINT5S */ - unsigned short PINT4S :1; /* PINT4S */ - unsigned short PINT3S :1; /* PINT3S */ - unsigned short PINT2S :1; /* PINT2S */ - unsigned short PINT1S :1; /* PINT1S */ - unsigned short PINT0S :1; /* PINT0S */ - } BIT; /* */ - } ICR2; /* */ - union { /* PINTER */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PINT15E:1; /* PINT15E */ - unsigned short PINT14E:1; /* PINT14E */ - unsigned short PINT13E:1; /* PINT13E */ - unsigned short PINT12E:1; /* PINT12E */ - unsigned short PINT11E:1; /* PINT11E */ - unsigned short PINT10E:1; /* PINT10E */ - unsigned short PINT9E :1; /* PINT9E */ - unsigned short PINT8E :1; /* PINT8E */ - unsigned short PINT7E :1; /* PINT7E */ - unsigned short PINT6E :1; /* PINT6E */ - unsigned short PINT5E :1; /* PINT5E */ - unsigned short PINT4E :1; /* PINT4E */ - unsigned short PINT3E :1; /* PINT3E */ - unsigned short PINT2E :1; /* PINT2E */ - unsigned short PINT1E :1; /* PINT1E */ - unsigned short PINT0E :1; /* PINT0E */ - } BIT; /* */ - } PINTER; /* */ - union { /* IPRC */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short _IRQ3:4; /* IRQ3 */ - unsigned short _IRQ2:4; /* IRQ2 */ - unsigned short _IRQ1:4; /* IRQ1 */ - unsigned short _IRQ0:4; /* IRQ0 */ - } BIT; /* */ - } IPRC; /* */ - union { /* IPRD */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short _PINT0_7 :4; /* PINT0-7 */ - unsigned short _PINT8_15:4; /* PINT8-15 */ - unsigned short _IRQ5 :4; /* IRQ5 */ - unsigned short _IRQ4 :4; /* IRQ4 */ - } BIT; /* */ - } IPRD; /* */ - union { /* IPRE */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short _DMAC :4; /* DMAC */ - unsigned short _SCIF0:4; /* SCIF0 */ - unsigned short _SCIF2:4; /* SCIF2 */ - unsigned short _ADC :4; /* ADC */ - } BIT; /* */ - } IPRE; /* */ - char wk4[524260]; /* */ - union { /* IPRF */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :8; /* */ - unsigned short _USB:4; /* USB */ - } BIT; /* */ - } IPRF; /* */ - union { /* IPRG */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short _TPU0:4; /* TPU0 */ - unsigned short _TPU1:4; /* TPU1 */ - } BIT; /* */ - } IPRG; /* */ - union { /* IPRH */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short _TPU2:4; /* TPU2 */ - unsigned short _TPU3:4; /* TPU3 */ - } BIT; /* */ - } IPRH; /* */ -}; -struct st_scif { /* struct SCIF */ - union { /* SCSMR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :5; /* */ - unsigned short SRC :3; /* SRC */ - unsigned short CA :1; /* CA */ - unsigned short CHR :1; /* CHR */ - unsigned short _PE :1; /* PE */ - unsigned short OE :1; /* O/E */ - unsigned short STOP:1; /* STOP */ - unsigned short :1; /* */ - unsigned short CKS :2; /* CKS */ - } BIT; /* */ - } SCSMR; /* */ - char wk1[2]; /* */ - unsigned char SCBRR; /* SCBRR */ - char wk2[3]; /* */ - union { /* SCSCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :4; /* */ - unsigned short TSIE:1; /* TSIE */ - unsigned short ERIE:1; /* ERIE */ - unsigned short BRIE:1; /* BRIE */ - unsigned short DRIE:1; /* DRIE */ - unsigned short TIE :1; /* TIE */ - unsigned short RIE :1; /* RIE */ - unsigned short TE :1; /* TE */ - unsigned short RE :1; /* RE */ - unsigned short :2; /* */ - unsigned short CKE :2; /* CKE */ - } BIT; /* */ - } SCSCR; /* */ - char wk3[2]; /* */ - unsigned char SCTDSR; /* SCTDSR */ - char wk4[3]; /* */ - union { /* SCFER */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :2; /* */ - unsigned short PER:6; /* PER */ - unsigned short :2; /* */ - unsigned short FER:6; /* FER */ - } BIT; /* */ - } SCFER; /* */ - char wk5[2]; /* */ - union { /* SCSSR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :6; /* */ - unsigned short ORER:1; /* ORER */ - unsigned short TSF :1; /* TSF */ - unsigned short ER :1; /* ER */ - unsigned short TEND:1; /* TEND */ - unsigned short TDFE:1; /* TDFE */ - unsigned short BRK :1; /* BRK */ - unsigned short FER :1; /* FER */ - unsigned short PER :1; /* PER */ - unsigned short RDF :1; /* RDF */ - unsigned short DR :1; /* DR */ - } BIT; /* */ - } SCSSR; /* */ - char wk6[2]; /* */ - union { /* SCFCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short TSE :1; /* TSE */ - unsigned short TCRST:1; /* TCRST */ - unsigned short :3; /* */ - unsigned short RSTRG:3; /* RSTRG */ - unsigned short RTRG :2; /* RTRG */ - unsigned short TTRG :2; /* TTRG */ - unsigned short MCE :1; /* MCE */ - unsigned short TFRST:1; /* TFRST */ - unsigned short RFRST:1; /* RFRST */ - unsigned short LOOP :1; /* LOOP */ - } BIT; /* */ - } SCFCR; /* */ - char wk7[2]; /* */ - union { /* SCFDR */ - unsigned short WORD; /* Word Access */ - struct { /* Byte Access */ - unsigned short :1; /* */ - unsigned short T:7; /* T */ - unsigned short :1; /* */ - unsigned short R:7; /* R */ - } BIT; /* */ - } SCFDR; /* */ - char wk8[2]; /* */ - unsigned char SCFTDR; /* SCFTDR */ - char wk9[3]; /* */ - unsigned char SCFRDR; /* SCFRDR */ -}; /* */ -union un_stbcr3 { /* union STBCR3 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char _USB :1; /* USB */ - unsigned char :1; /* */ - unsigned char _CMT :1; /* CMT */ - unsigned char _TPU :1; /* TPU */ - unsigned char _ADC :1; /* ADC */ - unsigned char _IrDA :1; /* IrDA */ - unsigned char _SCIF2:1; /* SCIF2 */ - unsigned char _SCIF0:1; /* SCIF0 */ - } BIT; /* */ -}; /* */ -struct st_pfc { /* struct PFC */ - union { /* PACR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PA7MD:2; /* PA7MD */ - unsigned short PA6MD:2; /* PA6MD */ - unsigned short PA5MD:2; /* PA5MD */ - unsigned short PA4MD:2; /* PA4MD */ - unsigned short PA3MD:2; /* PA3MD */ - unsigned short PA2MD:2; /* PA2MD */ - unsigned short PA1MD:2; /* PA1MD */ - unsigned short PA0MD:2; /* PA0MD */ - } BIT; /* */ - } PACR; /* */ - union { /* PBCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PB7MD:2; /* PB7MD */ - unsigned short PB6MD:2; /* PB6MD */ - unsigned short PB5MD:2; /* PB5MD */ - unsigned short PB4MD:2; /* PB4MD */ - unsigned short PB3MD:2; /* PB3MD */ - unsigned short PB2MD:2; /* PB2MD */ - unsigned short PB1MD:2; /* PB1MD */ - unsigned short PB0MD:2; /* PB0MD */ - } BIT; /* */ - } PBCR; /* */ - union { /* PCCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PC7MD:2; /* PC7MD */ - unsigned short PC6MD:2; /* PC6MD */ - unsigned short PC5MD:2; /* PC5MD */ - unsigned short PC4MD:2; /* PC4MD */ - unsigned short PC3MD:2; /* PC3MD */ - unsigned short PC2MD:2; /* PC2MD */ - unsigned short PC1MD:2; /* PC1MD */ - unsigned short PC0MD:2; /* PC0MD */ - } BIT; /* */ - } PCCR; /* */ - union { /* PDCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PD7MD:2; /* PD7MD */ - unsigned short PD6MD:2; /* PD6MD */ - unsigned short PD5MD:2; /* PD5MD */ - unsigned short PD4MD:2; /* PD4MD */ - unsigned short PD3MD:2; /* PD3MD */ - unsigned short PD2MD:2; /* PD2MD */ - unsigned short PD1MD:2; /* PD1MD */ - unsigned short PD0MD:2; /* PD0MD */ - } BIT; /* */ - } PDCR; /* */ - union { /* PECR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PE7MD:2; /* PE7MD */ - unsigned short PE6MD:2; /* PE6MD */ - unsigned short PE5MD:2; /* PE5MD */ - unsigned short PE4MD:2; /* PE4MD */ - unsigned short PE3MD:2; /* PE3MD */ - unsigned short PE2MD:2; /* PE2MD */ - unsigned short PE1MD:2; /* PE1MD */ - unsigned short PE0MD:2; /* PE0MD */ - } BIT; /* */ - } PECR; /* */ - union { /* PFCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PF7MD:2; /* PF7MD */ - unsigned short PF6MD:2; /* PF6MD */ - unsigned short PF5MD:2; /* PF5MD */ - unsigned short PF4MD:2; /* PF4MD */ - unsigned short PF3MD:2; /* PF3MD */ - unsigned short PF2MD:2; /* PF2MD */ - unsigned short PF1MD:2; /* PF1MD */ - unsigned short PF0MD:2; /* PF0MD */ - } BIT; /* */ - } PFCR; /* */ - union { /* PGCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PG7MD:2; /* PG7MD */ - unsigned short PG6MD:2; /* PG6MD */ - unsigned short PG5MD:2; /* PG5MD */ - unsigned short PG4MD:2; /* PG4MD */ - unsigned short PG3MD:2; /* PG3MD */ - unsigned short PG2MD:2; /* PG2MD */ - unsigned short PG1MD:2; /* PG1MD */ - unsigned short PG0MD:2; /* PG0MD */ - } BIT; /* */ - } PGCR; /* */ - union { /* PHCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :2; /* */ - unsigned short PH6MD:2; /* PH6MD */ - unsigned short PH5MD:2; /* PH5MD */ - unsigned short PH4MD:2; /* PH4MD */ - unsigned short PH3MD:2; /* PH3MD */ - unsigned short PH2MD:2; /* PH2MD */ - unsigned short PH1MD:2; /* PH1MD */ - unsigned short PH0MD:2; /* PH0MD */ - } BIT; /* */ - } PHCR; /* */ - union { /* PJCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PJ7MD:2; /* PJ7MD */ - unsigned short PJ6MD:2; /* PJ6MD */ - unsigned short PJ5MD:2; /* PJ5MD */ - unsigned short PJ4MD:2; /* PJ4MD */ - unsigned short PJ3MD:2; /* PJ3MD */ - unsigned short PJ2MD:2; /* PJ2MD */ - unsigned short PJ1MD:2; /* PJ1MD */ - unsigned short PJ0MD:2; /* PJ0MD */ - } BIT; /* */ - } PJCR; /* */ - union { /* PKCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PK7MD:2; /* PK7MD */ - unsigned short PK6MD:2; /* PK6MD */ - unsigned short PK5MD:2; /* PK5MD */ - unsigned short PK4MD:2; /* PK4MD */ - unsigned short PK3MD:2; /* PK3MD */ - unsigned short PK2MD:2; /* PK2MD */ - unsigned short PK1MD:2; /* PK1MD */ - unsigned short PK0MD:2; /* PK0MD */ - } BIT; /* */ - } PKCR; /* */ - union { /* PLCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :8; /* */ - unsigned short PL3MD:2; /* PL3MD */ - unsigned short PL2MD:2; /* PL2MD */ - unsigned short PL1MD:2; /* PL1MD */ - unsigned short PL0MD:2; /* PL0MD */ - } BIT; /* */ - } PLCR; /* */ - union { /* SCPCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :4; /* */ - unsigned short SCP5MD:2; /* SCP5MD */ - unsigned short SCP4MD:2; /* SCP4MD */ - unsigned short SCP3MD:2; /* SCP3MD */ - unsigned short SCP2MD:2; /* SCP2MD */ - unsigned short SCP1MD:2; /* SCP1MD */ - unsigned short SCP0MD:2; /* SCP0MD */ - } BIT; /* */ - } SCPCR; /* */ - union { /* PMCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short :2; /* */ - unsigned short PM6MD:2; /* PM6MD */ - unsigned short :2; /* */ - unsigned short PM4MD:2; /* PM4MD */ - unsigned short PM3MD:2; /* PM3MD */ - unsigned short PM2MD:2; /* PM2MD */ - unsigned short PM1MD:2; /* PM1MD */ - unsigned short PM0MD:2; /* PM0MD */ - } BIT; /* */ - } PMCR; /* */ - union { /* PNCR */ - unsigned short WORD; /* Word Access */ - struct { /* Bit Access */ - unsigned short PN7MD:2; /* PN7MD */ - unsigned short PN6MD:2; /* PN6MD */ - unsigned short PN5MD:2; /* PN5MD */ - unsigned short PN4MD:2; /* PN4MD */ - unsigned short PN3MD:2; /* PN3MD */ - unsigned short PN2MD:2; /* PN2MD */ - unsigned short PN1MD:2; /* PN1MD */ - unsigned short PN0MD:2; /* PN0MD */ - } BIT; /* */ - } PNCR; /* */ - char wk1[327724]; /* */ - union { /* PECR2 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :1; /* */ - unsigned char PE6MD:1; /* PE6MD */ - unsigned char PE5MD:1; /* PE5MD */ - unsigned char PE4MD:1; /* PE4MD */ - } BIT; /* */ - } PECR2; /* */ - char wk2; /* */ - union { /* PFCR2 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :4; /* */ - unsigned char PF3MD:1; /* PF3MD */ - unsigned char PF2MD:1; /* PF2MD */ - unsigned char PF1MD:1; /* PF1MD */ - unsigned char PF0MD:1; /* PF0MD */ - } BIT; /* */ - } PFCR2; /* */ - char wk3[15]; /* */ - union { /* PNCR2 */ - unsigned char BYTE; /* Byte Access */ - struct { /* Bit Access */ - unsigned char :1; /* */ - unsigned char PN6MD:1; /* PN6MD */ - unsigned char PN5MD:1; /* PN5MD */ - unsigned char PN4MD:1; /* PN4MD */ - unsigned char PN3MD:1; /* PN3MD */ - unsigned char PN2MD:1; /* PN2MD */ - unsigned char PN1MD:1; /* PN1MD */ - unsigned char PN0MD:1; /* PN0MD */ - } BIT; /* */ - } PNCR2; /* */ -}; /* */ - -struct st_mmu { - union { - unsigned int LONG; - struct { - unsigned int :23; - unsigned char SV :1; - unsigned char :2; - unsigned char RC :2; - unsigned char :1; - unsigned char TF :1; - unsigned char IX :1; - unsigned char AT :1; - } BIT; - } MMUCR; - - char wk1[12]; - union { - unsigned int LONG; - struct { - unsigned int VPN :22; - unsigned char :2; - unsigned char ASID :8; - } BIT; - } PTEH; - - union { - unsigned int LONG; - struct { - unsigned char :3; - unsigned int :19; - unsigned char :1; - unsigned char V :1; - unsigned char :1; - unsigned char PR :2; - unsigned char SZ :1; - unsigned char C :1; - unsigned char D :1; - unsigned char SH :1; - unsigned char :1; - } BIT; - } PTEL; - - unsigned int TTB; -}; - - -struct st_ubc { - unsigned int BDRB; - - unsigned int BDMRB; - - union { - unsigned int LONG; - struct { - unsigned short :10; - unsigned int BASMA :1; - unsigned int BASMB :1; - unsigned int :4; - unsigned int SCMFCA :1; - unsigned int SCMFCB :1; - unsigned int SCMFDA :1; - unsigned int SCMFDB :1; - unsigned int PCTE :1; - unsigned int PCBA :1; - unsigned int :2; - unsigned int DBEB :1; - unsigned int PCBB :1; - unsigned int :2; - unsigned int SEQ :1; - unsigned int :2; - unsigned int ETBE :1; - } BIT; - } BRCR; - - unsigned short BETR; - - char wk1[2]; - - unsigned int BARB; - - unsigned int BAMRB; - - union { - unsigned short WORD; - struct { - unsigned short :8; - unsigned short CDB :2; - unsigned short IDB :2; - unsigned short RWB :2; - unsigned short SZB :2; - } BIT; - } BBRB; - - char wk2[2]; - - union { - unsigned int LONG; - struct { - unsigned int SVF :1; - unsigned char :3; - unsigned int BSA :28; - } BIT; - } BRSR; - - unsigned int BARA; - - unsigned int BAMRA; - - union { - unsigned short WORD; - struct { - unsigned short :8; - unsigned short CDA :2; - unsigned short IDA :2; - unsigned short RWA :2; - unsigned short SZA :2; - } BIT; - } BBRA; - - char wk3[2]; - - union { - unsigned int LONG; - struct { - unsigned int DVF :1; - unsigned char :3; - unsigned int BDA :28; - } BIT; - } BRDR; - - char wk4[36]; - - unsigned char BASRA; - - char wk5[3]; - - unsigned char BASRB; -}; - - -union un_stbcr2 { - unsigned char BYTE; - struct { - unsigned char _UDI :1; - unsigned char _UBC :1; - unsigned char _DMAC :1; - unsigned char :1; - unsigned char _TLB :1; - unsigned char _CACHE :1; - unsigned char :2; - } BIT; -}; - - - -#define CPG (*(volatile struct st_cpg *)0xffffff80) -#define WDT (*(volatile struct st_wdt *)0xffffff84) -#define INTC (*(volatile struct st_intc *)0xfffffee0) -#define INTX (*(volatile struct st_intx *)0xa4000000) -#define PA (*(volatile struct st_pa *)0xa4000120) -#define PB (*(volatile struct st_pb *)0xa4000122) -#define PC (*(volatile struct st_pc *)0xa4000124) -#define PD (*(volatile struct st_pd *)0xa4000126) -#define PE (*(volatile struct st_pe *)0xa4000128) -#define PF (*(volatile struct st_pf *)0xa400012a) -#define PG (*(volatile struct st_pg *)0xa400012c) -#define PH (*(volatile struct st_ph *)0xa400012e) -#define PJ (*(volatile struct st_pj *)0xa4000130) -#define PK (*(volatile struct st_pk *)0xa4000132) -#define PL (*(volatile struct st_pl *)0xa4000134) -#define SCP (*(volatile struct st_scp *)0xa4000136) -#define PM (*(volatile struct st_pm *)0xa4000138) -#define PN (*(volatile struct st_pn *)0xa400013a) -#define USB (*(volatile struct st_usb *)0xa40a0008) -#define SCIF0 (*(volatile struct st_scif *)0xa4400000) -#define SCIF2 (*(volatile struct st_scif *)0xa4410000) -#define STBCR3 (*(volatile union un_stbcr3 *)0xa40a0000) -#define PFC (*(volatile struct st_pfc *)0xa4000100) -#define TEA (*(volatile unsigned int *)0xfffffffc) -#define MMU (*(volatile struct st_mmu *)0xffffffe0) -#define UBC (*(volatile struct st_ubc *)0xffffff90) -#define STBCR2 (*(volatile union un_stbcr2 *)0xffffff88) - -#endif // _7705_H diff --git a/include/alloca.h b/include/alloca.h deleted file mode 100644 index 2011266..0000000 --- a/include/alloca.h +++ /dev/null @@ -1,25 +0,0 @@ -//--- -// -// standard library module: alloca -// -// Allows dynamic memory allocation on the stack. Memory is automatically -// freed when the calling function exits, but this function suffers from -// risks of stack overflow; make sure you don't inline functions that use -// alloca or allocate more than a few hundred bytes with it. -// -//--- - -#ifndef _ALLOCA_H -#define _ALLOCA_H - -#include - -/* - alloca() - Allocates a memory block on the stack. -*/ -void *alloca(size_t size); - -#define alloca(size) __builtin_alloca(size) - -#endif // _ALLOCA_H diff --git a/include/bfile.h b/include/bfile.h deleted file mode 100644 index 81c0599..0000000 --- a/include/bfile.h +++ /dev/null @@ -1,74 +0,0 @@ -//--- -// -// gint core module: BFile interface -// -// Syscall-based interface to the BFile driver (which I would never dare -// to re-write considering how much the storage memory filesystem is an -// awful mess). -// -//--- - -#ifndef _BFILE_H -#define _BFILE_H - -/* - BFile_Remove() - Remove a file from the filesystem. The path must be encoded as two-byte - fontcharacters! -*/ -int BFile_Remove(const uint16_t *file); - -/* - BFile_Create() - Creates an entry in the filesystem (two-byte fontcharacter path) of the - given type. The size pointer must point to the file size for files, and - may be NULL for folders. -*/ -enum BFile_EntryType -{ - BFile_File = 1, - BFile_Folder = 5, -}; -int BFile_Create(const uint16_t *file, enum BFile_EntryType type, int *size); - -/* - BFile_Open() - Opens an existing file (two-byte fontcharacter path) with the required - mode, and returns a handle (positive integer) on success, or an - negative integer on error. -*/ -enum BFile_OpenMode -{ - BFile_ReadOnly = 0x01, - BFile_WriteOnly = 0x02, - BFile_ReadWrite = BFile_ReadOnly | BFile_WriteOnly, -}; -int BFile_Open(const uint16_t *file, enum BFile_OpenMode mode); - -/* - BFile_Close() - Closes an open file. -*/ -int BFile_Close(int handle); - -/* - BFile_Write() - Writes data to a file. The data is taken from the second-argument - buffer. The size to write is given as third argument. - WARNING: Always write an even number of bytes or you're in for trouble! -*/ -int BFile_Write(int handle, const void *ram_buffer, int even_size); - -/* - BFile_Read() - Reads from an open file. The second and third arguments indicate where - to store data and how much to read. The location from where the data is - read depends on the value of `whence`: - - If `whence` >= 0, it is considered as the absolute location (in - bytes) of the requested data in the file; - - If `whence` == -1, BFile_Read() reads from the current virtual - position in the file. -*/ -int BFile_Read(int handle, void *ram_buffer, int size, int whence); - -#endif // _BFILE_H diff --git a/include/bopti.h b/include/bopti.h deleted file mode 100644 index 95465c7..0000000 --- a/include/bopti.h +++ /dev/null @@ -1,73 +0,0 @@ -//--- -// -// gint drawing module: bopti -// -// This module is a powerful bitmap renderer. It *heavily* relies on the -// line-based structure of the video RAM as well as the high density of -// information. A single CPU access (longword operation) can affect 32 -// pixels at once, which is crucial for performance. The same goes for all -// other drawing modules, but this one typically has 350 lines of code -// just to wrap these longword accesses -- and it's blazingly fast. -// -//--- - -#ifndef _BOPTI_H -#define _BOPTI_H - -/* - image_t - This structure holds meta-data of a bitmap encoded with fxconv. Data is - accessed using longword operations for performance considerations, - which requires that the all fields of the structure be properly aligned - and of a correct size. -*/ -typedef struct -{ - uint8_t magic; - uint8_t format; - - uint8_t width; - uint8_t height; - - const uint32_t data[]; - -} __attribute__((packed, aligned(4))) image_t; - -/* - dimage() - Displays a monochrome image in the vram. This function does a real lot - of optimization. -*/ -void dimage(int x, int y, image_t *image); - -/* - dimage_part() - Draws a portion of an image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void dimage_part( - int x, int y, - image_t *img, - int left, int top, int width, int height -); - -/* - gimage() - Displays a gray image in the dual-vram. -*/ -void gimage(int x, int y, image_t *image); - -/* - gimage_part() - Draws a portion of a gray image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void gimage_part( - int x, int y, - image_t *image, - int left, int top, int width, int height -); - -#endif // _BOPTI_H diff --git a/include/clock.h b/include/clock.h deleted file mode 100644 index 571b56c..0000000 --- a/include/clock.h +++ /dev/null @@ -1,121 +0,0 @@ -//--- -// -// gint core module: clock -// -// This module interfaces with the MPU clocks and is used to measure the -// clock frequencies at the beginning of execution. At this stage, it -// assumes that clock mode 3 is used on SH7305 (as does FTune), because -// there doesn't seem to be a way of getting this information. -// -// It also provides some sleep and time conversion functions, and access -// to how the clocks are configured. In the future, it would be the module -// that supports overclock. -// -//--- - -#ifndef _CLOCK_H -#define _CLOCK_H - -#include - -//--- -// Sleep functions. -//--- - -/* - sleep() - Puts the processor to sleep until an interrupt request is accepted. - This function should be called every time the program because idle - because it doesn't have anything to do -- between two game frames or - while waiting for a keyboard event. - This function is called by getkey_opt(), getkey(), waitevent(), this - module's sleep functions among others. -*/ -void sleep(void); - -/* - sleep_ms() - Sleeps for the given number of milliseconds using a virtual timer. -*/ -void sleep_ms(int ms_delay); - -/* - sleep_us() - Sleeps for the given number of microseconds using the hardware timer - timer_user. -*/ -void sleep_us(int us_delay); - - - -//--- -// Clock management. -//--- - -/* - clock_unit_t - Enumerated type used by the time conversion functions. It indicates the - type (delay or frequency) of a parameter. -*/ -typedef enum -{ - clock_us = 0, - clock_ms = 1, - clock_s = 2, - - clock_Hz = 10, - clock_kHz = 11, - clock_MHz = 12, - -} clock_unit_t; - -/* - clock_config_t - A copy of the Clock Pulse Generator (CPG) configuration. Be sure to - check which MPU the program is running on (using ) to access the - right fields. -*/ -typedef struct -{ - union - { - int PLL1; // SH7705 - int FLL; // SH7305 - }; - union - { - int PLL2; // SH7705 - int PLL; // SH7305 - }; - - int Bphi_div1; - int Iphi_div1; - int Pphi_div1; - - union - { - int CKIO_f; // SH7705 - int RTCCLK_f; // SH7305 - }; - - int Bphi_f; // Bus clock frequency - int Iphi_f; // Processor clock frequency - int Pphi_f; // Peripheral clock frequency - -} clock_config_t; - -/* - clock_setting() - Returns the P_phi / 4 timer setting that will last for the given time. - Several units can be used. Be aware that the result is approximate, and - very high frequencies or very short delays will yield important errors. -*/ -uint32_t clock_setting(int duration, clock_unit_t unit); - -/* - clock_config() - Returns a copy of what the library knows about the clocks. -*/ -clock_config_t clock_config(void); - -#endif // _CLOCK_H diff --git a/include/ctype.h b/include/ctype.h deleted file mode 100644 index 3037ccd..0000000 --- a/include/ctype.h +++ /dev/null @@ -1,35 +0,0 @@ -//--- -// -// standard library module: ctype -// -// Some character manipulation. -// -//--- - -#ifndef _CTYPE_H -#define _CTYPE_H - -#include - -extern uint8_t ctype_classes[0x80]; - -// Character classes. -#define isalnum(c) (ctype_classes[(int)(c)] & 0xf0) -#define isalpha(c) (ctype_classes[(int)(c)] & 0x30) -#define iscntrl(c) (ctype_classes[(int)(c)] & 0x01) -#define isdigit(c) (ctype_classes[(int)(c)] & 0x40) -#define isgraph(c) (ctype_classes[(int)(c)] & 0xf4) -#define islower(c) (ctype_classes[(int)(c)] & 0x10) -#define isprint(c) (ctype_classes[(int)(c)] & 0x08) -#define ispunct(c) (ctype_classes[(int)(c)] & 0x04) -#define isspace(c) (ctype_classes[(int)(c)] & 0x02) -#define isupper(c) (ctype_classes[(int)(c)] & 0x20) -#define isxdigit(c) (ctype_classes[(int)(c)] & 0x80) -#define isascii(c) ((unsigned)c <= 0x7f) -#define isblank(c) (c == '\t' || c == ' ') - -// Character manipulation. -#define tolower(c) ((c) | isupper(c)) -#define toupper(c) ((c) & ~(islower(c) << 1)) - -#endif // _CTYPE_H diff --git a/include/display.h b/include/display.h deleted file mode 100644 index 924c6dc..0000000 --- a/include/display.h +++ /dev/null @@ -1,156 +0,0 @@ -//--- -// -// gint drawing module: display -// -// This module does most of the monochrome drawing. It manages the video -// memory although image rendering and text rendering, as complex tasks, -// are left to other modules (bopti and tales, respectively). -// -//--- - -#ifndef _DISPLAY_H -#define _DISPLAY_H - -#include -#include - -//--- -// Drawing-related types and constants. -//--- - -#define DWIDTH 128 /* Width of the screen */ -#define DHEIGHT 64 /* Height of the screen */ - -/* - color_t - Defines all colors that the library knows about: - - white is exactly what you think it is; - - light is a light gray used by the gray module; - - dark is a dark gray, also used by the gray engine; - - black is nothing more than black; (sorry) - - none means transparent, but is shorter to write. - There are also some transformation-associated colors: - - invert reverses the intensity of the color (white -> black, dark -> - light, etc); - - lighten is some kind of partially-transparent white. It lightens the - color which it is drawn onto (black -> dark, light -> light); - - lighten2 is the same as lighten, except it lightens more (black -> - light, light -> white); - - darken is the exact opposite of lighten (light -> dark, black -> - black). - - darken2 is the same to darken as lighten2 to lighten (white -> dark, - dark -> black); - All transformations except invert only operate when the gray engine is - running. -*/ -typedef enum -{ - color_white = 0, - color_light = 1, - color_dark = 2, - color_black = 3, - color_none = 4, - - color_invert = 5, - color_lighten = 6, - color_lighten2 = 7, - color_darken = 8, - color_darken2 = 9, - -} color_t; - -// The bopti module provides bitmap rendering functions. -#include - -// The tales module provides text rendering functions but requires the color_t -// type definition. -#include - - - -//--- -// Video RAM management. -//--- - -/* - display_getLocalVRAM() - Returns gint's local video RAM address. Gint does not use the system's - buffer because it is misaligned. This function always returns the same - address. Both the display and the gray module heavily use this buffer; - make sure you don't interfere with them if you access it. - This function does not necessarily returns the video ram that is - currently in use; call display_getCurrentVRAM() for this. -*/ -uint32_t *display_getLocalVRAM(void); - -/* - display_getCurrentVRAM() - Returns the current monochrome video ram buffer. This function usually - returns the parameter of the last call to display_useVRAM(), or the - local vram address (which is default when the library starts). - The return value of this function is undefined if the gray engine is - running. -*/ -uint32_t *display_getCurrentVRAM(void); - -/* - display_useVRAM() - Changes the current monochrome video ram address. The argument must be - a 4-aligned 1024-byte buffer because the library's design requires it. - This function refuses misaligned buffers but trusts that enough space - is available; failing to provide enough memory may crash the program. - This function will most likely have no effect when running the gray - engine. -*/ -void display_useVRAM(uint32_t *vram); - - - -//--- -// Global drawing functions. -//--- - -/* - dupdate() - Pushes the video RAM to the physical screen. This function also works - when the gray engine is running, but that's probably not what you want. -*/ -void dupdate(void); - -/* - dclear() - Clears the whole video ram, making all pixels white. -*/ -void dclear(void); - -/* - drect() - Draws a rectangle on the screen. This function can use any color which - is not associated with the gray engine, including the reverse operator. - Both end points (x1, y1) and (x2, y2) are affected as well. -*/ -void drect(int x1, int y1, int x2, int y2, color_t operator); - - - -//--- -// Local drawing functions. -//--- - -/* - dpixel() - Changes a pixel's color in the video ram. The result may depend on the - current color of the pixel. -*/ -void dpixel(size_t x, size_t y, color_t operator); - -/* - dline() - Draws a line in the vram. Automatically optimizes horizontal and - vertical lines. - - Uses an algorithm written by PierrotLL for MonochromeLib. -*/ -void dline(int x1, int y1, int x2, int y2, color_t operator); - -#endif // _DISPLAY_H diff --git a/include/events.h b/include/events.h deleted file mode 100644 index b4bce55..0000000 --- a/include/events.h +++ /dev/null @@ -1,113 +0,0 @@ -//--- -// -// gint core module: events -// -// Finally some user-friendly API. This module is in charge of managing -// the event queue. The waitevent() function should be particularly useful -// in program main loops to record key presses and releases in real-time -// games. -// -// Other functions such as the getkey() of the keyboard module provide -// more advanced features such as SHIFT and ALPHA modifiers, backlight -// control for instance; these functions rely on this module and they -// ignore all events that they do not handle. If you want to catch several -// types of events (eg. keyboard and serial communication), then you need -// to use directly this module. -// -//--- - -#ifndef _EVENTS_H -#define _EVENTS_H - -#include - -/* - event_type_t - Something user programs will surely use most often. -*/ -typedef enum -{ - // Specific events. - event_none = 0, - event_user = 1, - - // Keyboard events. - event_key_press = 2, - event_key_repeat = 3, - event_key_release = 4, - - // Other events. - event_timer_underflow = 5, - -} event_type_t; - -/* - key_event_t - Keyboard events. "I think the user wants something." -*/ -typedef struct -{ - // This is the key code as defined in (a matrix code), and - // probably what you need. - uint16_t code; - // This is a "compact id" which can be used for array subscript. There - // are only a few holes in the "compact id" numbering. - uint16_t id; - // Character associated with the event key. - int character; - -} __attribute__((packed, aligned(4))) key_event_t; - -/* - event_t - Wake up, something's going on. The union member that holds information - about the event is specified by the type attribute. -*/ -typedef struct -{ - event_type_t type; - - union - { - // For event_user. - void *data; - // For event_key_press, event_key_repeat and event_key_release. - key_event_t key; - // For event_timer_underflow. - timer_t *timer; - }; - -} __attribute__((packed, aligned(4))) event_t; - - - -//--- -// Event management. -//--- - -/* - event_push() - Queues a user-defined event, allowing it to be retrieved by getevent() - or pollevent() later. Most often you will not need to use this, as - system events are automatically queued. Pushing ET_None events is not - allowed. - Returns non-zero on error. -*/ -int event_push(event_t event); - -/* - waitevent() - Returns the next event. If no one is available, waits for something to - happen. This function uses low-level sleep and should be preferred to - active waiting using loops. -*/ -event_t waitevent(void); - -/* - pollevent() - Returns the next event. If no one is available, returns an event whose - type is ET_None. This function always returns immediately. -*/ -event_t pollevent(void); - -#endif // _EVENTS_H diff --git a/include/extended/endian.h b/include/extended/endian.h deleted file mode 100644 index b6f5d6f..0000000 --- a/include/extended/endian.h +++ /dev/null @@ -1,47 +0,0 @@ -#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) - : "r0" - ); - 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/extended/inttypes.h b/include/extended/inttypes.h deleted file mode 100644 index 5864d31..0000000 --- a/include/extended/inttypes.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef _INTTYPES_H -#define _INTTYPES_H - -// Decimal notation. -#define PRId8 "d" -#define PRId16 "d" -#define PRId32 "d" -#define PRId64 "lld" - -#define PRIdLEAST8 "d" -#define PRIdLEAST16 "d" -#define PRIdLEAST32 "d" -#define PRIdLEAST64 "lld" - -#define PRIdFAST8 "d" -#define PRIdFAST16 "d" -#define PRIdFAST32 "d" -#define PRIdFAST64 "lld" - -// Decimal notation, again. -#define PRIi8 "i" -#define PRIi16 "i" -#define PRIi32 "i" -#define PRIi64 "lli" - -#define PRIiLEAST8 "i" -#define PRIiLEAST16 "i" -#define PRIiLEAST32 "i" -#define PRIiLEAST64 "lli" - -#define PRIiFAST8 "i" -#define PRIiFAST16 "i" -#define PRIiFAST32 "i" -#define PRIiFAST64 "lli" - -// Octal notation. -#define PRIo8 "o" -#define PRIo16 "o" -#define PRIo32 "o" -#define PRIo64 "llo" - -#define PRIoLEAST8 "o" -#define PRIoLEAST16 "o" -#define PRIoLEAST32 "o" -#define PRIoLEAST64 "llo" - -#define PRIoFAST8 "o" -#define PRIoFAST16 "o" -#define PRIoFAST32 "o" -#define PRIoFAST64 "llo" - -// Unsigned integers. -#define PRIu8 "u" -#define PRIu16 "u" -#define PRIu32 "u" -#define PRIu64 "llu" - -#define PRIuLEAST8 "u" -#define PRIuLEAST16 "u" -#define PRIuLEAST32 "u" -#define PRIuLEAST64 "llu" - -#define PRIuFAST8 "u" -#define PRIuFAST16 "u" -#define PRIuFAST32 "u" -#define PRIuFAST64 "llu" - -// Lowercase hexadecimal notation. -#define PRIx8 "x" -#define PRIx16 "x" -#define PRIx32 "x" -#define PRIx64 "llx" - -#define PRIxLEAST8 "x" -#define PRIxLEAST16 "x" -#define PRIxLEAST32 "x" -#define PRIxLEAST64 "llx" - -#define PRIxFAST8 "x" -#define PRIxFAST16 "x" -#define PRIxFAST32 "x" -#define PRIxFAST64 "llx" - -// Uppercase hexadecimal notation. -#define PRIX8 "X" -#define PRIX16 "X" -#define PRIX32 "X" -#define PRIX64 "llX" - -#define PRIXLEAST8 "X" -#define PRIXLEAST16 "X" -#define PRIXLEAST32 "X" -#define PRIXLEAST64 "llX" - -#define PRIXFAST8 "X" -#define PRIXFAST16 "X" -#define PRIXFAST32 "X" -#define PRIXFAST64 "llX" - -// Format specifiers of intmax_t and uintmax_t. -#define PRIdMAX "lld" -#define PRIiMAX "lli" -#define PRIoMAX "llo" -#define PRIuMAX "llu" -#define PRIxMAX "llx" -#define PRIXMAX "llX" - -// Format specifiers of intptr_t and uintptr_t. -#define PRIdPTR "d" -#define PRIiPTR "i" -#define PRIoPTR "o" -#define PRIuPTR "u" -#define PRIxPTR "x" -#define PRIXPTR "X" - -#endif // _INTTYPES_H diff --git a/include/gint.h b/include/gint.h deleted file mode 100644 index 3060a89..0000000 --- a/include/gint.h +++ /dev/null @@ -1,251 +0,0 @@ -//--- -// -// gint core module: interrupt handler -// -// This module is the core of the gint library. It controls the interrupt -// handler, allows the user to customize interrupt management, provides -// peripheral register access and some information about the runtime -// environment. -// -//--- - -#ifndef _GINT_H -#define _GINT_H - -#include -#include - -// The version symbol is defined through the linker and consists of four -// fields: -// - Version type, an ascii char ('a'lpha, 'b'eta, 'd'ev, 'r'elease), 8 bits -// - Major version number, 4 bits -// - Minor version number, 4 bits -// - Build number, 16 bits -// Please note that the version number is the *ADDRESS* of GINT_VERSION, which -// you definitely want to cast to uint32_t. Evaluating GINT_VERSION is illegal -// (dereferencing a pointer which is actually a four-field version number just -// cannot work) and will certainly crash your program. -extern uint32_t GINT_VERSION; - -//--- -// System info provided by the library -//--- - -typedef struct -{ - /* Returns the current VBR address. */ - uint32_t (*vbr)(void); - /* Gint's VBR address. */ - uint32_t gint_vbr; - /* The system's VBR address, saved when gint was initialized. */ - uint32_t system_vbr; -} gint_info_t; - -extern gint_info_t gint; - - - -//--- -// Exception and interrupt handlers -//--- - -typedef enum -{ - //--- - // Resets - // Obviously there will be no handler for these ones. - //--- - - /* Power-on reset: raised when power is supplied */ - exc_poweron_reset = 0, - /* Manual reset: probably when RESET button is pressed */ - exc_manual_reset = 0, - /* TLB multihit: more than one entry matches the requested address - (SH7305 only) */ - exc_tlb_multihit = 0, - - //--- - // General exceptions - //--- - - /* - Address error: an invalid address was accessed - - Location of instruction - - Address at fault - - Access type - 1: Instruction or data read access - 2: Data write access - */ - exc_address_error = 1, - - /* - TLB protection violation: address access is prevented by TLB - - Location of instruction - - Address at fault - - Access type - 1: Instruction or data read access - 2: Data write access - */ - exc_tlb_protection_violation = 2, - - /* - TLB invalid: entry was found but valid bit is cleared (SH7705 only) - - Location of instruction - - Address at fault - - Access type - 1: Instruction or data read access - 2: Data write access - */ - exc_tlb_invalid = 3, - - /* - Illegal instruction: current instruction is not a valid opcode - - Location of instruction - - Opcode at fault - */ - exc_illegal_instruction = 4, - - /* - Illegal slot: doing something illegal within a delayed slot - - Location of instruction - - Opcode at fault - */ - exc_illegal_slot = 5, - - /* User break: a user break request was fulfilled */ - exc_user_break = 6, - /* Initial page write: trying to write while dirty bit is reset */ - exc_initial_page_write = 7, - - /* - Unconditional trap: a 'trapa' instruction was executed - - Location of instruction - - Trap number - */ - exc_trap = 8, - - /* DMA address error: the DMAC violated word or long memory access - alignments (SH7705 only) */ - exc_dma_address = 9, - - //--- - // TLB misses - //--- - - /* - TLB miss: no match found in TLB for requested address - - Location of instruction - - Address at fault - - Access type - 1: Instruction or data read access - 2: Data write access - */ - exc_tlb_miss = 10, - - //--- - // Interrupt requests - //--- - - /* - Non-Maskable Interrupt: triggered by an external pin - */ - int_nmi = 11, - - /* - Timer underflow: a timer's counter reached zero - - Timer channel - 0: Channel 0 - 1: Channel 1 - 2: Channel 2 - */ - int_timer_underflow = 12, - - /* - Timer input capture: a capture of timer channel 2 has been requested - by the external clock (SH7705 only) - - Captured value - */ - int_timer_input_capture = 13, - - /* - Real-time clock alarm interrupt: configured alarm registers and - current time match - */ - int_rtc_alarm = 14, - - /* - Real-time clock periodic interrupt: various possible frequencies - - Current interrupt frequency - */ - int_rtc_periodic = 15, - - /* - Real-time clock carry interrupt: when a carry occurs while you're - reading time - */ - int_rtc_carry = 16, - - //--- - // Other flags - //--- - - // Maximum valid value for this type. - exc_type_max, - -} gint_interrupt_type_t; - -/* - gint_install() - Installs an exception or interrupt handler for one of gint's recognized - interrupts. The type signature of the handler function depends on the - particular signal it's answering. Please refer to the documentation to - know what parameters each handler function is provided with. -*/ -void gint_install(gint_interrupt_type_t signal, void *function); - -/* - gint_uninstall() - Uninstalls the exception or interrupt handler that was used for the - given interrupt, falling back to gint's default handler. -*/ -void gint_uninstall(gint_interrupt_type_t signal); - - - -//--- -// Register access -//--- - -typedef enum -{ - register_expevt = 0, - register_intevt = 1, - register_mmucr = 2, - register_tea = 3, - register_tra = 4, - -} gint_register_t; - -/* - gint_register() - Returns the address of a platform-shared register. All these registers - exist on both platforms but they may hold different values for the same - kind of information (f.i the periodic RTC interrupt will change the - value of INTEVT to 0x4a0 on SH7705, and 0xaa0 on SH7305). Higher-level - interfaces may provide platform-independent information in such cases. -*/ -volatile void *gint_reg(gint_register_t reg); - - - -//--- -// Other functions -//--- - -/* - gint_switch() - Temporarily returns to the system's main menu. -*/ -void gint_switch(void); - -#endif // _GINT_H diff --git a/include/gray.h b/include/gray.h deleted file mode 100644 index 86fa721..0000000 --- a/include/gray.h +++ /dev/null @@ -1,139 +0,0 @@ -//--- -// -// gint core/drawing module: gray -// -// Runs the gray engine and handles drawing for the dual-buffer system. -// -//--- - -#ifndef _GRAY_H -#define _GRAY_H - -#include -#include - -// This module provides bitmap rendering. -#include - -//--- -// Engine control. -//--- - -/* - gray_runs() - Returns 1 if the gray engine is running, 0 otherwise. -*/ -int gray_runs(void); - -/* - gray_start() - Starts the gray engine. The control of the screen is transferred to the - gray engine. -*/ -void gray_start(void); - -/* - gray_stop() - Stops the gray engine. The monochrome display system takes control of - the video ram. -*/ -void gray_stop(void); - -/* - gray_lightVRAM() - Returns the module's light gray vram address. -*/ -uint32_t *gray_lightVRAM(void); - -/* - gray_darkVRAM() - Returns the module's dark gray vram address. -*/ -uint32_t *gray_darkVRAM(void); - -/* - gray_currentVRAM() - Returns the currently displayed video ram (if the engine runs). Used - internally, but has no interest for the user. You don't want to draw to - this vram. -*/ -uint32_t *gray_currentVRAM(void); - -/* - gray_getDelays() - Returns the gray engine delays. Pointers are not set if NULL. -*/ -void gray_getDelays(int *light, int *dark); - -/* - gray_setDelays() - Changes the gray engine delays. Usually you don't need to call this, - because the engine has its default values. - Finding values that give proper grays is quite the hard part of the - gray engine. Usual values are about 1000, with light being between 75 - and 90% of dark. - - Typical values: - - values stability stripes colors - --------------------------------------------------------- - 860, 1298 excellent worst static good - 912, 1343 bad none very good (default) - 993, 1609 medium light fast good - 1325, 1607 bad light fast excellent - --------------------------------------------------------- -*/ -void gray_setDelays(int light, int dark); - - - -//--- -// Global drawing functions. -//--- - -/* - gupdate() - Swaps the vram buffer sets. You need to call this function each time - you finish drawing something in the video ram. Unlike the monochrome - function dupdate(), gupdate() only does a quick operation indicating - that drawing and exposed buffers have been swapped, but nothing on the - screen will change until the gray timer fires. -*/ -void gupdate(void); - -/* - gclear() - Clears the gray video ram, making all pixels white. -*/ -void gclear(void); - -/* - grect() - Draws a rectangle in the gray video ram; this function accepts all - values of the color_t type, including gray operators. -*/ -void grect(int x1, int y1, int x2, int y2, color_t operator); - - - -//--- -// Local drawing functions. -//--- - -/* - gpixel() - Puts a pixel in the vram. This function accepts all values of the - color_t type, including gray operators. -*/ -void gpixel(size_t x, size_t y, color_t operator); - -/* - gline() - Draws a line in the vram while automatically optimizing special cases. - This function supports all plain colors from the color_t type, but not - the gray operators. If you need them for horizontal or vertical lines, - you may want to use grect() as a replacement. -*/ -void gline(int x1, int y1, int x2, int y2, color_t operator); - -#endif // _GRAY_H diff --git a/include/internals/bopti.h b/include/internals/bopti.h deleted file mode 100644 index e1b11dd..0000000 --- a/include/internals/bopti.h +++ /dev/null @@ -1,162 +0,0 @@ -#ifndef _INTERNALS_BOPTI_H -#define _INTERNALS_BOPTI_H - -#include -#include - -/* - channel_t - Indicates what operation a layer is made for. Each operation affects - the video ram differently (setting or clearing pixels, transparency, - etc). An image is made of several layers. -*/ -typedef enum -{ - channel_full_alpha = 0x01, - channel_light_alpha = 0x02, - channel_dark_alpha = 0x04, - - channel_mono = 0x08, - channel_light = 0x10, - channel_dark = 0x20, - -} channel_t; - -/* - format_t - Describes the various combination of layer channels that are allowed by - bopti. Technically one could try other formats but they're not of much - use (transparent gray is even totally useless). -*/ -typedef enum -{ - format_mono = channel_mono, - format_mono_alpha = format_mono | channel_full_alpha, - format_gray = channel_light | channel_dark, - format_gray_alpha = format_gray | channel_full_alpha, - format_greater_alpha = format_mono | channel_light_alpha | - channel_dark_alpha -} format_t; - -/* - structure_t - Basically an image's dimensions, data pointer, and a few other useful - information such as the pitch in bytes. -*/ -typedef struct -{ - int width, height; - int layer_size; - - const uint8_t *data; - int columns; - int end_size, end_bytes; - -} structure_t; - -/* - command_t - The parameters of a drawing operation. A pointer to such a structure is - created by the public functions and passed down to the module's - sub-functions during rendering. -*/ -typedef struct command_t -{ - // Channel being drawn. - channel_t channel; - // Operation used (whether bopti_op_mono() or bopti_op_gray()). - void (*op)(int offset, uint32_t operator, struct command_t *command); - // Portion of the bitmap which is drawn. 'top' and 'bottom' refer to - // lines where 'left' and 'right' refer to column ids. - int left, right, top, bottom; - // Position of the bitmap on the screen. - int x, y; - // Rectangle masks that define the drawing area. - uint32_t masks[4]; - // Video rams being used. - union { - // "Different names, same fate." (Kingdom Hearts II) - uint32_t *vram; - uint32_t *v1; - }; - uint32_t *v2; - -} command_t; - -// The video ram addresses are set by the public functions and used internally -// by the module. -// Monochrome video ram, light and dark buffers (in this order). -extern uint32_t *bopti_vram, *bopti_v1, *bopti_v2; - - - -//--- -// Internal bopti routines. -//--- - -/* - bopti_op() - Operates on a vram long. The operator will often not contain 32 bits of - image information. Since neutral bits are not the same for all - operations, a mask is used to indicate which bits should be used for - the operation. This mask is taken for the image's rectangle masks (see - the 'display' module internal header for more information on rectangle - masks). Which operation is performed is determined by the channel - setting of the command argument. -*/ -void bopti_op_mono(int offset, uint32_t operator, command_t *c); -void bopti_op_gray(int offset, uint32_t operator, command_t *c); - -/* - bopti_grid() -- general form - bopti_grid_a32() -- when x is a multiple of 32 - - Draws the grid at the beginning of a layer's data. The length of this - grid is always a multiple of 32. - The need for bopti_grid_a32() is not only linked to optimization, - because bopti_grid() will perform a 32-bit shift when x is a multiple - of 32, which is undefined behavior. - bopti_grid() automatically calls bopti_grid_a32() when required. -*/ -void bopti_grid_a32(const uint32_t *layer, int columns, int height, - command_t *c); -void bopti_grid(const uint32_t *layer, int columns, int height, command_t *c); -/* - bopti_end_get() - Returns an operator for the end of a line, whose width is lower than 32 - (by design: otherwise, it would have been a column). The given pointer - is read and updated so that it points to the next line at the end of - the operation. -*/ -uint32_t bopti_end_get1(const unsigned char **data); -uint32_t bopti_end_get2(const unsigned char **data); - -/* - bopti_rest() -- general form - bopti_rest_nover() -- when the end does not overlap two vram longs - - Draws the end of a layer, which can be considered as a whole layer - whose with is lower than 32. (Actually is it lower or equal to 16; - otherwise it would have been a column and the end would be empty). The - 'size' arguments is in bytes, thus 1 or 2. - Unlike bopti_grid_a32(), bopti_end_nover() is not called automatically - by bopti_end(). -*/ -void bopti_end_nover(const unsigned char *end, int size, command_t *c); -void bopti_end(const unsigned char *end, int size, command_t *c); - -/* - bopti() - Draws a layer's data in the video ram areas specified in the command - argument. -*/ -void bopti(const unsigned char *layer, structure_t *s, command_t *c); - -/* - getStructure() - Determines the image size (large images have a somehow different - structure), the data pointer and a few dimensions inside the image. -*/ -void getStructure(image_t *img, structure_t *structure); - -#endif // _INTERNALS_BOPTI_H diff --git a/include/internals/clock.h b/include/internals/clock.h deleted file mode 100644 index 6de2c43..0000000 --- a/include/internals/clock.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _INTERNALS_CLOCK_H -#define _INTERNALS_CLOCK_H - -/* - clock_measure() - Begins the frequency measurements. The measurements will end - automatically. While doing measurements, do not use the RTC interrupt - or the user timer. - Call clock_measure_end() to wait until the measurements are finished. - It is possible to execute code during the measurements, so that less - time is spent. -*/ -void clock_measure(void); - -/* - clock_measure_end() - Waits until the measurements are finished. This may be immediate. -*/ -void clock_measure_end(void); - -#endif // _INTERNALS_CLOCK_H diff --git a/include/internals/display.h b/include/internals/display.h deleted file mode 100644 index 8157b5b..0000000 --- a/include/internals/display.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _INTERNALS_DISPLAY_H -#define _INTERNALS_DISPLAY_H - -#include -#include - -extern uint32_t *vram; - -//--- -// Rectangle masks. -// -// The concept of 'rectangle masks' is used several times in this module. -// It relies on the fact that operations affecting a rectangle act the -// same for all lines, and line operation is very optimized. A rectangle -// mask is a set of integers, where each bit indicate whether a specific -// pixel is affected (1) by the operation, or not (0). -// -// For example to clear a rectangle such as (14, 16, 112, 48), the masks -// will need to hold 0003ffff ffffffff ffffffff ffff0000. Bitwise- -// combining them with video ram long entries yields very good performance -// as compared to operation on single pixels. Each bitwise operation will -// produce different results, which is very flexible. -// -// This technique can also be used in subtle cases with patterns more -// complicated than rectangles, but within this module this is unlikely to -// happen. -//--- - -/* - adjustRectangle() - Adjusts the given rectangle coordinates to ensure that : - - the rectangle is entirely contained in the screen; - - x1 < x2; - - y1 < y2, - which is needed when working with screen rectangles. Returns non-zero - if the rectangle is outside the screen, which usually means there is - nothing to do. -*/ -int adjustRectangle(int *x1, int *y1, int *x2, int *y2); - -/* - getMasks() - Computes the rectangle masks needed to affect pixels located between x1 - and x2 (both included). The four masks are stored in the third argument - (seen as an array). -*/ -void getMasks(size_t x1, size_t x2, uint32_t *masks); - -#endif // _INTERNALS_DISPLAY_H diff --git a/include/internals/events.h b/include/internals/events.h deleted file mode 100644 index ac8b7ee..0000000 --- a/include/internals/events.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _INTERNALS_EVENTS_H -#define _INTERNALS_EVENTS_H - -#include - -#ifndef EVENTS_QUEUE_SIZE -#define EVENTS_QUEUE_SIZE 64 -#endif - -/* - This module is just a circular-array queue that pushes and pops events - like any other queue. Trying to add an event when the queue is full - fails, and the operation is ignored. -*/ -extern volatile event_t event_queue[]; -extern volatile int queue_start; -extern volatile int queue_size; - -#endif // _INTERNALS_EVENT_H diff --git a/include/internals/exceptions.h b/include/internals/exceptions.h deleted file mode 100644 index 80b4b38..0000000 --- a/include/internals/exceptions.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef _INTERNALS_EXCEPTIONS_H -#define _INTERNALS_EXCEPTIONS_H - -#include - -/* - exch_address_error() - CPU address error, e.g. alignment issues. -*/ -void exch_address_error(uint32_t pc, uint32_t tea, uint32_t access); - -/* - exch_tlb_protection_violation() - You don't have the right to access this address. -*/ -void exch_tlb_protection_violation(uint32_t pc, uint32_t tea, uint32_t access); - -/* - exch_tlb_invalid() - The translation info for this address is marked as invalid. -*/ -void exch_tlb_invalid(uint32_t pc, uint32_t tea, uint32_t access); - -/* - exch_illegal_instruction() - What's this opcode anyway? -*/ -void exch_illegal_instruction(uint32_t pc, uint32_t opcode); - -/* - exch_illegal_slot() - You can't execute this in a delay slot. -*/ -void exch_illegal_slot(uint32_t pc, uint32_t opcode); - -/* - exch_user_break() - One of the user break conditions you requested was fulfilled. -*/ -void exch_user_break(void); - -/* - exch_initial_page_write() - You can't write to this memory page, it's too early. -*/ -void exch_initial_page_write(void); - -/* - exch_trap() - You asked for it. -*/ -void exch_trap(uint32_t pc, uint32_t trap); - -/* - exch_dma_address() - The DMAC is accessing badly-aligned addresses. -*/ -void exch_dma_address(void); - -/* - exch_tlb_miss() - This virtual address points nowhere. -*/ -void exch_tlb_miss(uint32_t pc, uint32_t tea, uint32_t access); - -#endif // _INTERNALS_EXCEPTIONS_H diff --git a/include/internals/gint.h b/include/internals/gint.h deleted file mode 100644 index 6e7ed83..0000000 --- a/include/internals/gint.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef _INTERNALS_GINT_H -#define _INTERNALS_GINT_H - -#include -#include -#include - -//--- -// Interrupt handlers. -//--- - -// General exception handler. -void gint_exc(void); -// TLB miss handler. -void gint_tlb(void); -// Interrupt handler. -void gint_int(void); - - - -//--- -// Assembler-level VBR management. -//--- - -/* - gint_getvbr() - Retrieves the current VBR address. -*/ -uint32_t gint_getvbr(void); - -/* - gint_setvbr() - Sets the VBR address and calls the configuration function while - interrupts are disabled. -*/ -void gint_setvbr(uint32_t vbr, void (*setup)(void)); - - - -//--- -// Initialization and termination routines, environment saves. -//--- - -/* - gint_init() - Initializes gint. Loads the interrupt handler into the memory and sets - the new vbr address. -*/ -void gint_init(mpu_t mpu); - -/* - gint_quit() - Stops gint. Restores the system's configuration and vbr address. -*/ -void gint_quit(void); - -#include -#include - -/* - environment_t - Structure where all registers used by gint are saved by default to - ensure that the operating system is not disturbed. -*/ -typedef struct -{ - // Interrupt controller. - uint16_t IPR[8]; - - // Real-Time Clock. - uint8_t RCR1, RCR2; - - // Timer Unit. - mod_tmu_timer_t TMU0, TMU1, TMU2; - uint8_t TSTR; - - // I/O ports for the keyboard driver. - uint16_t PACR, PBCR, PMCR; - uint8_t PADR, PBDR, PMDR; - -} environment_7705_t; - -typedef struct -{ - // Interrupt controller. - uint16_t IPR[12]; - - // Real-Time Clock. - uint8_t RCR1, RCR2; - - // Timer Unit. - mod_tmu_timer_t TMU0, TMU1, TMU2; - uint8_t TSTR; - - // I/O ports for the keyboard driver. - uint16_t PMCR, PNCR, PZCR; - uint8_t PMDR, PNDR, PZDR; - uint8_t key; - -} environment_7305_t; - -typedef union -{ - environment_7705_t env_7705; - environment_7305_t env_7305; - -} environment_t; - -/* - gint_save() - Saves many registers into an internal environment buffer. -*/ -void gint_save_7705(environment_7705_t *env); -void gint_save_7305(environment_7305_t *env); - -/* - gint_lock_and_setup() - Locks all interrupts (ie. disables them by default) and sets initial - values to all registers, allows specific interrupts, etc. -*/ -void gint_lock_and_setup_7705(void); -void gint_lock_and_setup_7305(void); - -/* - gint_restore_and_unlock() - Restores the parameters saved in the environment buffer to give back - the interrupt control to the system. -*/ -void gint_restore_and_unlock_7705(environment_7705_t *env); -void gint_restore_and_unlock_7305(environment_7305_t *env); - -/* - gint_reg() - Returns the address of a platform-shared register. -*/ -volatile void *gint_reg_7705(gint_register_t reg); -volatile void *gint_reg_7305(gint_register_t reg); - -#endif // _INTERNALS_GINT_H diff --git a/include/internals/init.h b/include/internals/init.h deleted file mode 100644 index f6b7f47..0000000 --- a/include/internals/init.h +++ /dev/null @@ -1,29 +0,0 @@ -//--- -// gint core module: init -// Program initialization and display manipulation for the startup logs. -//--- - -#ifndef _INTERNALS_INIT_H -#define _INTERNALS_INIT_H - -#include - -/* init_version() -- get a version string */ -const char *init_version(void); - -/* init_stage() -- change the current init stage */ -void init_stage(const char *name); - -/* init_halt() -- halt the program */ -void init_halt(void); - -/* print() -- print text on a 21*8 grid */ -#define print(x, y, str) dtext((x) * 6 - 5, (y) * 8 - 8, (str)) - -/* print_dec() -- print a number in base 10 */ -void print_dec(int x, int y, int n, int digits); - -/* print_hex() -- print a number in base 16 */ -void print_hex(int x, int y, uint32_t n, int digits); - -#endif // _INTERNALS_INIT_H diff --git a/include/internals/interrupt_maps.h b/include/internals/interrupt_maps.h deleted file mode 100644 index a1d716b..0000000 --- a/include/internals/interrupt_maps.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef _INTERRUPT_MAPS -#define _INTERRUPT_MAPS - -#include -#include - -//--- -// Interrupt handlers. -//--- - -/* - gint_interrupt_arg_t - 8-bit integer representing an argument to pass as uint32_t to an - exception or interrupt handler. -*/ -typedef enum -{ - // Empty argument (always at end of list). - arg_none = 0x00, - // Signal subtype (f.i address error subtypes: code, read, write). - arg_subtype = 0x01, - // Value of register SPC is passed. - arg_pc = 0x02, - // Instruction pointed at by SPC is passed (re-execution types). - arg_opcode = 0x03, - // Address indicated in register TEA is passed. - arg_tea = 0x04, - // Trap number is passed. - arg_trap = 0x05, - // Timer channel 2 captured input (SH7705 only). - arg_timer_capt = 0x06, - -} gint_interrupt_arg_t; - -/* - gint_interrupt_handler_t - Contains both static and dynamic information of interrupt handlers: - the current handler is stored there, as well as the type signature - information through an array of parameters. -*/ -typedef struct -{ - // Current handler function. The type signature may vary, hence the - // void * pointer. - void *function; - // Default handler and fallback if the current handler is un-installed. - void *default_function; - // Default interrupt priorities (interrupts only). - uint8_t priority; - // Arguments passed to the handler (these are values of type arg_t). - // This array is the only thing that defines the type signature of the - // two functions above. - uint8_t args[3]; - -} gint_interrupt_handler_t; - -// Handler array. -extern gint_interrupt_handler_t gint_handlers[]; - - - -//--- -// Interrupt maps. -//--- - -/* - gint_interrupt_map_t - Maps an event code to an interrupt type and subtype. The subtypes allow - group-handling similar interrupts (for instance TLB misses for code and - data accesses, or timer underflows from various channels). -*/ -typedef struct -{ - uint8_t type; - uint8_t subtype; - -} __attribute__((packed)) gint_interrupt_map_t; - -/* - gint_map_7705() - TLB misses and TLB invalid have the same event code though they are - handled by different functions in the VBR space. The offset argument - expects values 0x100, 0x400 or 0x600 to distinguish between them. -*/ -gint_interrupt_map_t gint_map_7705(uint32_t event_code, uint32_t offset); - -/* - gint_map_7305() - Maps an event code to an interrupt type. The SH7305 does not have TLB - invalid exceptions so no event codes overlap. -*/ -gint_interrupt_map_t gint_map_7305(uint32_t event_code); - -/* - gint_invoke() - Invokes an interrupt or exception handler, given its type and subtype. -*/ -void gint_invoke(uint8_t type, uint8_t subtype); - -#endif // _INTERRUPT_MAPS diff --git a/include/internals/interrupts.h b/include/internals/interrupts.h deleted file mode 100644 index 0957d11..0000000 --- a/include/internals/interrupts.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _INTERNALS_INTERRUPTS_H -#define _INTERNALS_INTERRUPTS_H - -#include - -/* - inth_timer_underflow() - Wake up, your timer has expired! -*/ -void inth_timer_underflow(uint32_t channel); - -/* - inth_rtc_periodic() - Don't you forget to execute the periodic tasks. -*/ -void inth_rtc_periodic(void); - -#endif // _INTERNALS_INTERRUPTS_H diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h deleted file mode 100644 index 01d24eb..0000000 --- a/include/internals/keyboard.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _INTERNALS_KEYBOARD_H -#define _INTERNALS_KEYBOARD_H - -#include -#include -#include - -// Current keyboard state and keyboard interrupt flag. -extern volatile uint8_t keyboard_state[10]; -extern volatile int interrupt_flag; - -// Delays (milliseconds) before repetitions, last key pressed, how many times -// it has been repeated already, time elapsed since last repetition (ms). -extern int repeat_first, repeat_next; -extern int last_key, last_repeats, last_time; - -// Virtual timer object. -extern timer_t *vtimer; - -/* - getPressedKey() - Finds a pressed key in the keyboard state and returns it. -*/ -int getPressedKey(volatile uint8_t *keyboard_state); - -/* - getPressedKeys() - Find 'count' pressed keys in the keyboard state and fills the 'keys' - array. Returns the number of keys found. - WARNING: keyboard artifacts make this function read as pressed keys - that aren't (typically, LEFT + DOWN + SHIFT => ALPHA). -*/ -int getPressedKeys(volatile uint8_t *keyboard_state, int *keys, int count); - -/* - keyboard_updateState() - Updates the keyboard state. -*/ -void keyboard_updateState_7705(volatile uint8_t *state); -void keyboard_updateState_7305(volatile uint8_t *state); - -/* - keyboard_interrupt() - Answers an interrupt event by updating the keyboard state and - generating the associated keyboard events. -*/ -void keyboard_interrupt(void); - -#endif // _INTERNALS_KEYBOARD_H diff --git a/include/internals/mmu.h b/include/internals/mmu.h deleted file mode 100644 index 1fcf6ac..0000000 --- a/include/internals/mmu.h +++ /dev/null @@ -1,21 +0,0 @@ -//--- -// -// gint core module: mmu -// -// A wise application should avoid tampering with the system's -// configuration of the MMU and the TLB. This module implicitly forces the -// system to load the required pages but does not interact with the TLB. -// -//--- - -#ifndef _INTERNALS_MMU_H -#define _INTERNALS_MMU_H - -/* - mmu_pseudoTLBInit() - Tries to have the system load enough data into TLB to allow add-in to - execute. -*/ -void mmu_pseudoTLBInit(void); - -#endif // _MMU_H diff --git a/include/internals/modules.h b/include/internals/modules.h deleted file mode 100644 index c96e770..0000000 --- a/include/internals/modules.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _INTERNALS_MODULES_H -#define _INTERNALS_MODULES_H - -/* - mod_init() - Initializes the module data to make register access cross-platform. The - MPU needs to have been detected or this function will yield wrong - results. -*/ -void mod_init(void); - -#endif // _INTERNALS_MODULES_H diff --git a/include/internals/rtc.h b/include/internals/rtc.h deleted file mode 100644 index 4b9f9e0..0000000 --- a/include/internals/rtc.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _INTERNALS_RTC_H -#define _INTERNALS_RTC_H - -#include -#include - -#ifndef RTC_CB_ARRAY_SIZE -#define RTC_CB_ARRAY_SIZE 5 -#endif - -/* - rtc_callback_t - An RTC callback with a unique id. -*/ -typedef struct -{ - rtc_frequency_t freq; - int id; - - void (*callback)(void); - int repeats; - -} rtc_callback_t; - -// The callback array. -rtc_callback_t cb_array[RTC_CB_ARRAY_SIZE]; - -/* - rtc_perodic_interrupt() - Handles periodic interrupts and calls the callbacks. -*/ -void rtc_periodic_interrupt(void); - -/* - rtc_cb_interrupt() - Calls the RTC callbacks if necessary, and updates the repeat counts. - Should only be called when RTC periodic interrupts occur. -*/ -void rtc_cb_interrupt(void); - -#endif // _INTERNALS_RTC_H diff --git a/include/internals/stdio.h b/include/internals/stdio.h deleted file mode 100644 index 7a67647..0000000 --- a/include/internals/stdio.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef _INTERNALS_STDIO_H -#define _INTERNALS_STDIO_H - -#include -#include - -//--- -// Formatted printing. -//--- - -#ifndef __stdio_buffer_size -#define __stdio_buffer_size 256 -#endif - -extern char __stdio_buffer[]; - -/* - __printf() - Formatted printing to the stdio buffer. -*/ -int __printf(size_t size, const char *format, va_list args); - -#endif // _INTERNALS_STDIO_H diff --git a/include/internals/syscalls.h b/include/internals/syscalls.h deleted file mode 100644 index 594c50b..0000000 --- a/include/internals/syscalls.h +++ /dev/null @@ -1,29 +0,0 @@ -//--- -// -// gint core module: syscalls -// -// Some of the functionality still requires interacting with the system. -// -//--- - -#ifndef _INTERNALS_SYSCALLS_H -#define _INTERNALS_SYSCALLS_H - -#include - -/* malloc() -- allocate data in heap */ -void *__malloc(size_t size); - -/* free() -- free data allocated by malloc(), calloc() or realloc() */ -void __free(void *ptr); - -/* realloc() -- reallocate a chunk of memory */ -void *__realloc(void *chunk, size_t new_size); - -/* get_os_version() -- write the OS version in format MM.mm.pppp to a string */ -void __get_os_version(char *str); - -/* system_menu() -- go back to menu, assuming the system has the control */ -void __system_menu(const void *vram); - -#endif // _INTERNALS_SYSCALLS_H diff --git a/include/internals/tales.h b/include/internals/tales.h deleted file mode 100644 index 455246d..0000000 --- a/include/internals/tales.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _INTERNALS_TALES_H -#define _INTERNALS_TALES_H - -#include -#include -#include - -#define OPERATE_ARGS uint32_t *operators, int height, int x, int y - -extern font_t *font; -extern color_t operator; - -/* - getCharacterIndex() - Returns the index of a character in a font data area depending on the - font format and the size of the characters. Returns the index in the - data area, as long array, or -1 when the character does not belong to - the font format set. -*/ -int getCharacterIndex(int c); - -/* - operate() - Operates on the vram using the given operators. The x-coordinate should - be a multiple of 32. There should be `height` operators. -*/ -void operate_mono(OPERATE_ARGS); -void operate_gray(OPERATE_ARGS); - -/* - update() - Updates the operators using the given glyph. The operation will not be - complete if there are not enough bits available in the operator data. - In this case the offset will become negative, which means that the - calling procedure has to call operate() and re-call update(). - `available` represents the number of free bits in the operators (lower - bits). - Returns the number of bits available after the operation. If it's - negative, call operate() and update() again. -*/ -int update(uint32_t *operators, int height, int available, uint32_t *glyph); - -/* - render() - Renders text without any formatting analysis, using the given operation - function. -*/ -void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS)); - -#endif // _INTERNALS_TALES_H diff --git a/include/internals/time.h b/include/internals/time.h deleted file mode 100644 index aef910b..0000000 --- a/include/internals/time.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _INTERNALS_TIME_H -#define _INTERNALS_TIME_H - -/* - isLeap() - Determines whether the given year is a leap year. -*/ -int isLeap(int year); - -/* - daysInMonth() - Returns number of days for the given month (between 0 and 11) and year. -*/ -int daysInMonth(int month, int year); - -#endif // _INTERNALS_TIME_H diff --git a/include/internals/timer.h b/include/internals/timer.h deleted file mode 100644 index 8e4feec..0000000 --- a/include/internals/timer.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef _INTERNALS_TIMER_H -#define _INTERNALS_TIMER_H - -#include -#include - -/* - timer_t - A virtual or hardware timer. We need to declare the struct timer_t name - so that we can forward-reference it. -*/ -typedef struct timer_t -{ - // Current delay, how much time elapsed since last interrupt occurred. - uint32_t ms_delay; - uint32_t ms_elapsed; - - // Is the virtual slot free? Is the virtual timer active? - uint8_t used :1; - uint8_t active :1; - // Is this a virtual timer? Is this the virtual timer support? - uint8_t virtual :1; - uint8_t vsupport :1; - // How many events do I have received but not executed? - uint8_t events :4; - // How many repeats are left. - uint32_t repeats_left :24; - - // Callback function (NULL for event-firing timers) and its argument. - void *callback; - void *argument; - -} __attribute__((packed, aligned(4))) timer_t; - -// Hardware timers. -extern timer_t htimers[3]; -// Virtual timers. -extern timer_t vtimers[TIMER_SLOTS]; - -/* - timer_interrupt() - Handles the interrupt for the given timer channel. -*/ -void timer_interrupt(int channel); - -/* - timer_callback_event() - Executes the callback of a timer, or pushes a new timer event depending - on the timer configuration. Also reduces the amount of repeats left and - clears the active flag (or stops the hardware timer) if this number - falls from one to zero. -*/ -void timer_callback_event(timer_t *timer); - -/* - vtimer_interrupt() - Interrupt handling subsystem for the virtual timers. -*/ -void vtimer_interrupt(void); - -/* - vtimer_updateOne() - Update the virtual timer hardware support timer, knowing that a virtual - timer with the given delay has been started. -*/ -void vtimer_updateOne(int additional_delay_ms); - -/* - vtimer_updateAll() - Updates the virtual timer hardware support after computing the GCD of - all virtual timers delays. This is rather long so avoid calling this - when possible. -*/ -void vtimer_updateAll(void); - -#endif // _INTERNALS_TIMER_H diff --git a/include/keyboard.h b/include/keyboard.h deleted file mode 100644 index 4acb419..0000000 --- a/include/keyboard.h +++ /dev/null @@ -1,329 +0,0 @@ -//--- -// -// gint core module: keyboard analyzer -// -// Probably the most difficult hardware interaction. There is very few -// documentation on how the system actually analyzes the keyboard. While -// disassembling syscalls reveals the following procedure (which was -// already documented by SimonLothar), there is nothing about the -// detection problems of the multi-getkey system. -// -//--- - -#ifndef _KEYBOARD_H -#define _KEYBOARD_H - -#include -#include - -//--- -// Keycodes and related. -//--- - -/* - key_t - The following codes are gint matrix codes. They are not compatible with - the system's. Some keycodes are special event codes; all others are - made of a key identifier and possibly one or more modifiers. - Binary-and a keycode with MOD_CLEAR to remove the modifiers; this will - not work with special event codes. -*/ -typedef enum -{ - // Special events codes. - KEY_NONE = 0x00, - KEY_NOEVENT = 0xff, - - // Key modifiers. - MOD_SHIFT = 0x80, - MOD_ALPHA = 0x100, - MOD_CLEAR = ~(MOD_SHIFT | MOD_ALPHA), - - // Key identifiers. - - KEY_F1 = 0x69, - KEY_F2 = 0x59, - KEY_F3 = 0x49, - KEY_F4 = 0x39, - KEY_F5 = 0x29, - KEY_F6 = 0x19, - - KEY_SHIFT = 0x68, - KEY_OPTN = 0x58, - KEY_VARS = 0x48, - KEY_MENU = 0x38, - KEY_LEFT = 0x28, - KEY_UP = 0x18, - - KEY_ALPHA = 0x67, - KEY_SQUARE = 0x57, - KEY_POWER = 0x47, - KEY_EXIT = 0x37, - KEY_DOWN = 0x27, - KEY_RIGHT = 0x17, - - KEY_XOT = 0x66, - KEY_LOG = 0x56, - KEY_LN = 0x46, - KEY_SIN = 0x36, - KEY_COS = 0x26, - KEY_TAN = 0x16, - - KEY_FRAC = 0x65, - KEY_FD = 0x55, - KEY_LEFTP = 0x45, - KEY_RIGHTP = 0x35, - KEY_COMMA = 0x25, - KEY_ARROW = 0x15, - - KEY_7 = 0x64, - KEY_8 = 0x54, - KEY_9 = 0x44, - KEY_DEL = 0x34, - KEY_AC_ON = 0x24, - - KEY_4 = 0x63, - KEY_5 = 0x53, - KEY_6 = 0x43, - KEY_MUL = 0x33, - KEY_DIV = 0x23, - - KEY_1 = 0x62, - KEY_2 = 0x52, - KEY_3 = 0x42, - KEY_PLUS = 0x32, - KEY_MINUS = 0x22, - - KEY_0 = 0x61, - KEY_DOT = 0x51, - KEY_EXP = 0x41, - KEY_NEG = 0x31, - KEY_EXE = 0x21, - -} key_t; - - - -//--- -// Keyboard configuration. -//--- - -/* - keyboard_setAnalysisDelay() - Sets the keyboard analysis delay, that is, the delay (in ms) between - two keyboard analyzes. If a key is pressed then released in the lapse - between two analyzes, the program won't notice anything. On the other - hand, if the program spends too much time reading the keyboard, it will - lose a bit of execution power. - The default frequency is about 40 Hz; very few programs will need to - change this setting. Please note that the repeat delays should be - multiples of the analysis delay for better accuracy. -*/ -void keyboard_setAnalysisDelay(int analysis_delay_ms); - -/* - keyboard_setRepeatRate() - Sets the default repeat rate for key events. The delay before the first - repeat may have a different value (usually longer). The unit for the - argument is ms, but the repeat events themselves may only be fired when - a keyboard analysis is performed; which means that for better accuracy, - these delays should be a multiple of the keyboard period. The keyboard - period may be changed by calling keyboard_setAnalysisDelay(). - For instance, delays of (625 ms, 125 ms) will imitate the system's - default setting. - You can disable repetitions by passing 0 as arguments: - - if first = 0, no repetition will ever occur; - - if first != 0 and next = 0, only one repetition will occur. -*/ -void keyboard_setRepeatRate(int first, int next); - - - -//--- -// Keyboard access. -//--- - -/* - getkey_opt_t - Options available to customize the behavior of the getkey_opt() - function. -*/ -typedef enum -{ - getkey_none = 0x00, - - // Consider [SHIFT] and [ALPHA] as modifiers. Returns key identifiers - // with MOD_SHIFT and MOD_ALPHA flags instead of returning KEY_SHIFT - // and KEY_ALPHA. - getkey_shift_modifier = 0x01, - getkey_alpha_modifier = 0x02, - - // Allow changing the backlight status on [SHIFT] + [OPTN] on - // compatible models. - getkey_manage_backlight = 0x04, - - // Allow returning to menu using the [MENU] key. (This operation is not - // absolutely safe.) - getkey_task_switch = 0x08, - - // Allow key repetition. This option does not control the generation of - // repeat events (use keyboard_setRepeatRate() for this) but filters - // them. Please note that modifiers will never be repeated, even when - // pressed continuously. - getkey_repeat_arrow_keys = 0x10, - getkey_repeat_char_keys = 0x20, - getkey_repeat_ctrl_keys = 0x40, - getkey_repeat_func_keys = 0x80, - // Shorthand for the four previous properties. - getkey_repeat_all_keys = 0xf0, - - // Default combination of getkey(). - getkey_default = 0x1f, - -} getkey_option_t; - -/* - getkey() - Blocking function with auto-repeat that heeds for the SHIFT and ALPHA - modifiers. In short, this function reproduces the behavior of the - system's GetKey() function. It returns a matrix code, possibly with - modifier bits. - This function does not return until a key is pressed. -*/ -int getkey(void); - -/* - getkey_opt() - Enhances getkey() with more general functionalities. An OR-combination - of options of type getkey_option_t may be given as first argument. - If delay is non-zero and positive, getkey_opt() will return KEY_NOEVENT - if no event occurs during the given delay. Please note that this - function can only ever return after a keyboard analysis is performed; - the actual delay may exceed the requested time if it's not a multiple - of the keyboard period (which can be changed by calling - keyboard_setAnalysisDelay()). - Like getkey(), returns the pressed key matrix code, possibly with - modifiers depending on the options. -*/ -int getkey_opt(getkey_option_t options, int delay_ms); - -/* - multigetkey() - - Listens the keyboard for simultaneous key hits. This functions fills - the 'keys' array with 'count' keycodes, padding with KEY_NONE values at - the end if less that 'count' keys are detected. - If 'delay_ms' is positive and nothing happens during this delay, this - function returns an array of KEY_NONE. Please note that the delay - detection suffers the same limitation as getkey_opt(). - - This function suffers from severe limitations and may not be very - convenient to use. For more accuracy, consider using the event system. - - WARNING: - Because of hardware limitations, this function generally yields poor - results. Rectangle and column effects make it read unpressed keys as - pressed (see documentation for more information). The more pressed - keys, the more errors. - - The results are guaranteed to be exact if two keys or less are pressed. - With three keys or more, column effects (on SH4) and rectangle effects - (on both platforms) mess up the results by making this function think - that some keys, which are actually released, are pressed. - - This function is designed to make combinations of one or two arrow keys - with another key as viable as possible. On SH4, this works pretty well - even if combinations like Left + Down + SHIFT trigger ALPHA sometimes. - On SH3, rectangle effects are *always* present, making it impossible to - use Left + Down or Up + Right with any other key in their rows without - having this function return junk. - - Any other combination of keys may quite randomly result in variably - incorrect results. Please do not expect multigetkey() to work as an - ideal multi-key analyzer. -*/ -void multigetkey(int *keys, int count, int delay_ms); - -/* - keyboard_stateBuffer() - - Returns the address of the keyboard state array. The keyboard state - consists in 10 bytes, in which every key is represented as a bit. - The returned address is the original buffer address. You should avoid - editing the array. It wouldn't influence the behavior of the keyboard - functions, but the buffer data is very volatile and any data written to - it could be replaced anytime without prior notice. - - If the user wishes to do really advanced keyboard management that they - can't achieve it using the library, they can access this buffer. - Updates of this buffer's contents can be detected by watching the - 'interrupt_flag' variable defined in internals/keyboard.h. However, the - library will continue firing events so the user needs to catch them and - ignore them. -*/ -volatile uint8_t *keyboard_stateBuffer(void); - - - -//--- -// Key analysis. -//--- - -/* - keyid() - Transforms a key identifier and returns a key code that is more - convenient for array subscript that the original matrix codes. The new - codes are laid out the following way: - - +0 +1 +2 +3 +4 +5 - ------------------------------------ - +0 | F1 F2 F3 F4 F5 F6 - +6 | SHIFT OPTN VARS MENU Left Top - +12 | ALPHA x^2 ^ EXIT Down Right - +18 | X,O,T log ln sin cos tan - +24 | Frac F<>D ( ) , -> - +30 | 7 8 9 DEL AC/ON - +36 | 4 5 6 * / - +42 | 1 2 3 + - - +48 | 0 . x10^ (-) EXE - - The returned key code is the sum of the line and column headings. For - instance key_id(KEY_SIN) would be 18 + 3 = 21. Please note that there - are a few holes in the numbering. - This function ignores modifiers and returns -1 on error. -*/ -int key_id(int matrix_key); - -/* - key_char() - Returns the ASCII character associated with a character key, and 0 for - other keys. This function expects a matrix code and not a key_id() - code, and heeds for the ALPHA modifier. -*/ -int key_char(int matrix_key); - -/* - key_type_t - Categorizes the keyboard's keys into several types: - - Arrow keys only include the REPLAY pad; - - Function keys only include the F1 .. F6 keys; - - Character keys are those which input characters; - - Control characters are all others. -*/ -typedef enum -{ - key_type_arrow = 1, - key_type_character = 2, - key_type_control = 4, - key_type_function = 8, - -} key_type_t; - -/* - key_type() - Returns a key's type. This functions ignores modifiers and expects - matrix codes as argument, not key_id() codes. -*/ -key_type_t key_type(int matrix_key); - -#endif // _KEYBOARD_H diff --git a/include/math.h b/include/math.h deleted file mode 100644 index 1bcbbcb..0000000 --- a/include/math.h +++ /dev/null @@ -1,41 +0,0 @@ -//--- -// -// gint libc module: math -// -// Provides mathematical functions as well as a few useful extensions. -// -//--- - -#ifndef _MATH_H -#define _MATH_H - -#include - -//--- -// Function extensions -//--- - -/* - qdiv() - Quickly divides by predefined integers using a 64-bit multiplication - technique. These functions should be ~10 times faster than dividing - using opeator "/". -*/ - -typedef struct qdiv_t -{ - uint32_t q; /* Quotient */ - uint32_t r; /* Remainer */ - -} __attribute__((packed, aligned(4))) qdiv_t; - -qdiv_t qdiv(uint32_t n, uint32_t divider, uint32_t reciprocal); - -/* Predefined magic numbers */ -#define qdiv_3(n) qdiv(n, 3, 0x55555556) -#define qdiv_5(n) qdiv(n, 5, 0x33333334) -#define qdiv_10(n) qdiv(n, 10, 0x1999999a) -#define qdiv_100(n) qdiv(n, 100, 0x028f5c29) -#define qdiv_1000(n) qdiv(n, 1000, 0x00418938) - -#endif // _MATH_H diff --git a/include/modules/interrupts.h b/include/modules/interrupts.h deleted file mode 100644 index 3ffd9d1..0000000 --- a/include/modules/interrupts.h +++ /dev/null @@ -1,688 +0,0 @@ -#ifndef _MODULES_INTERRUPTS_H -#define _MODULES_INTERRUPTS_H - -#include -#include - -//--- -// Interrupt controller. -// This part is difficult to handle, because the interrupt controllers of -// SH7705 and SH7305 MPUs have virtually nothing in common. I eventually -// decided to completely split it up into two kinds of structures and -// types. -// Use the gint API, not this module, for platform-independent interrupt -// management. -//--- - -//--- -// SH7705-related definitions. -//--- - -/* - mod_intc_ipc_7705_t - Interrupt priority controller. Just a bunch of 16-bit-registers that - handle the interrupt priority of all interrupt sources. - - Please note that because the interrupt priority controller of the - SH7705 MPU has registers scattered everywhere in the memory, its - structure below has a different pointer for each register. On the - opposite, the SH7305 registers are all in a contiguous area thus there - is only one pointer for the whole group. -*/ -typedef struct -{ - volatile word_union(*IPRA, - uint TMU0 :4; /* Timer 0 */ - uint TMU1 :4; /* Timer 1 */ - uint TMU2 :4; /* Timer 2 */ - uint RTC :4; /* Real-Time Clock */ - ); - - volatile word_union(*IPRB, - uint WDT :4; /* Watchdog Timer */ - uint REF :4; /* BSC Refresh Request, SDRAM (?) */ - uint :4; - uint :4; - ); - - volatile word_union(*IPRC, - uint IRQ3 :4; /* Interrupt request 3 */ - uint IRQ2 :4; /* Interrupt request 2 */ - uint IRQ1 :4; /* Interrupt request 1 */ - uint IRQ0 :4; /* Interrupt request 0 */ - ); - - volatile word_union(*IPRD, - uint PINT0_7 :4; /* External interrupt pins 0 to 7 */ - uint PINT8_15 :4; /* External interrupt pins 8 to 15 */ - uint IRQ5 :4; /* Interrupt request 5 */ - uint IRQ4 :4; /* Interrupt request 4 */ - ); - - volatile word_union(*IPRE, - uint DMAC :4; /* Direct Memory Access Controller */ - uint SCIF0 :4; /* Serial Communication Interface 0 */ - uint SCIF2 :4; /* Serial Communication Interface 2 */ - uint ADC :4; /* Analog/Decimal Converter */ - ); - - volatile word_union(*IPRF, - uint :4; - uint :4; - uint USB :4; /* USB Controller */ - uint :4; - ); - - volatile word_union(*IPRG, - uint TPU0 :4; /* Timer Pulse Unit 0 */ - uint TPU1 :4; /* Timer Pulse Unit 1 */ - uint :4; - uint :4; - ); - - volatile word_union(*IPRH, - uint TPU2 :4; /* Timer Pulse Unit 2 */ - uint TPU3 :4; /* Timer Pulse Unit 3 */ - uint :4; - uint :4; - ); - -} __attribute__((packed)) mod_intc_ipc_7705_t; - -/* - mod_intc_icr0_7705_t - Interrupt control register 0: configuration of the NMI interrupt. -*/ -typedef struct -{ - word_union(, - uint const NMIL :1; /* NMI Input Level */ - uint :6; - uint NMIE :1; /* NMI Edge Select */ - uint :8; - ); - -} __attribute__((packed, aligned(2))) mod_intc_icr0_7705_t; - -/* - mod_intc_icr1_7705_t - Interrupt control register 1: general interrupt configuration. -*/ -typedef struct -{ - word_union(, - uint MAI :1; /* Mask All Interrupts */ - uint IRQLVL :1; /* Interrupt Request Level Detect */ - uint BLMSK :1; /* Enable NMI when BL is set */ - uint :1; - uint IRQ5E :2; /* IRQ 5 Edge Detection */ - uint IRQ4E :2; - uint IRQ3E :2; - uint IRQ2E :2; - uint IRQ1E :2; - uint IRQ0E :2; - ); - -} __attribute__((packed, aligned(2))) mod_intc_icr1_7705_t; - -/* - mod_intc_icr2_7705_t - Interrupt control register 2: individual PINT interrupt management. -*/ -typedef struct -{ - word_union(, - uint PINT15 :1; /* PINT15 interrupt detection level */ - uint PINT14 :1; - uint PINT13 :1; - uint PINT12 :1; - uint PINT11 :1; - uint PINT10 :1; - uint PINT9 :1; - uint PINT8 :1; - uint PINT7 :1; - uint PINT6 :1; - uint PINT5 :1; - uint PINT4 :1; - uint PINT3 :1; - uint PINT2 :1; - uint PINT1 :1; - uint PINT0 :1; - ); - -} __attribute__((packed, aligned(2))) mod_intc_icr2_7705_t; - -/* - mod_intc_pinter_7705_t - PINTER register: individual masks for all PINT interrupts. -*/ -typedef struct -{ - word_union(, - uint PINT15 :1; /* PINT15 interrupt enable */ - uint PINT14 :1; - uint PINT13 :1; - uint PINT12 :1; - uint PINT11 :1; - uint PINT10 :1; - uint PINT9 :1; - uint PINT8 :1; - uint PINT7 :1; - uint PINT6 :1; - uint PINT5 :1; - uint PINT4 :1; - uint PINT3 :1; - uint PINT2 :1; - uint PINT1 :1; - uint PINT0 :1; - ); - -} __attribute__((packed, aligned(2))) mod_intc_pinter_7705_t; - -/* - mod_intc_irr0_7705_t - Interrupt Request Register 0: Indicates whether interrupt requests are - being input to the various interrupt lines. Also allows to clear the - IRQ request bits in edge-detection mode. -*/ -typedef struct -{ - byte_union(, - uint const PINT0_7R :1; /* PINT0-7 state */ - uint const PINT8_15R :1; /* PINT8-15 state */ - uint IRQ5 :1; /* IRQ5 request pin state */ - uint IRQ4 :1; - uint IRQ3 :1; - uint IRQ2 :1; - uint IRQ1 :1; - uint IRQ0 :1; - ); - -} __attribute__((packed)) mod_intc_irr0_7705_t; - -/* - mod_intc_irr1_7705_t - Interrupt Request Register 1: State of SCIF0 and DMAC requests. -*/ -typedef struct -{ - const byte_union(, - uint TXIOR :1; /* SCIF0 TXI interrupt */ - uint :1; - uint RXI0R :1; /* SCIF0 RXI interrupt */ - uint ERI0R :1; /* SCIF0 ERI interrupt */ - uint DEI3R :1; /* DMAC DEI3 interrupt */ - uint DEI2R :1; /* DMAC DEI2 interrupt */ - uint DEI1R :1; /* DMAC DEI1 interrupt */ - uint DEI0R :1; /* DMAC DEI0 interrupt */ - ); - -} __attribute__((packed)) mod_intc_irr1_7705_t; - -/* - mod_intc_irr2_7705_t - Interrupt Request Register 2: State of SCIF2 and ADC requests. -*/ -typedef struct -{ - const byte_union(, - uint :3; - uint ADIR :1; /* AD/C ADI interrupt */ - uint TXI2R :1; /* SCIF2 TXI interrupt */ - uint :1; - uint RXI2R :1; /* SCIF2 RXI interrupt */ - uint ERI2R :1; /* SCIF2 ERI interrupt */ - ); - -} __attribute__((packed, aligned(2))) mod_intc_irr2_7705_t; - -/* - mod_intc_7705_t - Finally the SH7705 interrupt controller. -*/ -typedef struct -{ - /* All interrupt priority registers */ - mod_intc_ipc_7705_t iprs; - - /* Control registers */ - volatile mod_intc_icr0_7705_t *ICR0; - volatile mod_intc_icr1_7705_t *ICR1; - volatile mod_intc_icr2_7705_t *ICR2; - - /* PINTER register */ - volatile mod_intc_pinter_7705_t *PINTER; - - /* Interrupt request registers */ - volatile mod_intc_irr0_7705_t *IRR0; - volatile mod_intc_irr1_7705_t *IRR1; - volatile mod_intc_irr2_7705_t *IRR2; - -} __attribute__((packed)) mod_intc_7705_t; - - - -//--- -// SH7305-related definitions. -//--- - -/* - mod_intc_ipc_7305_t - Interrupt priority controller, same idea as the previous one. - - Some of the interrupt fields of the SH7305 registers, the contents of - which have been directly taken from the SH7724 documentation, have been - left unnamed because the related peripheral modules are *very* unlikely - to even exist in the SH7305, let alone be of any use to us. -*/ -typedef struct -{ - word_union(IPRA, - uint TMU0_0 :4; /* TMU0 Channel 0 */ - uint TMU0_1 :4; /* TMU0 Channel 1 */ - uint TMU0_2 :4; /* TMU0 Channel 2 */ - uint IrDA :4; /* Infrared Communication */ - ); - pad(2); - - word_union(IPRB, - uint :4; /* JPEG Processing Unit */ - uint LCDC :4; /* LCD Controller */ - uint DMAC1A :4; /* Direct Memory Access Controller 1 */ - uint :4; /* Blending Engine Unit */ - ); - pad(2); - - word_union(IPRC, - uint TMU1_0 :4; /* TMU1 Channel 0 */ - uint TMU1_1 :4; /* TMU1 Channel 1 */ - uint TMU1_2 :4; /* TMU1 Channel 2 */ - uint :4; /* Sound Processing Unit */ - ); - pad(2); - - word_union(IPRD, - uint :4; - uint MMCIF :4; /* MultiMedia Card Interface */ - uint :4; - uint :4; /* ATAPI Interface */ - ); - pad(2); - - word_union(IPRE, - uint DMAC0A :4; /* Direct Memory Access Controller 0 */ - uint :4; /* Various graphical engines */ - uint SCIFA3 :4; /* SCIFA channel 3 interrupt */ - uint :4; /* Video Processing Unit */ - ); - pad(2); - - word_union(IPRF, - uint KEYSC :4; /* Key Scan Interface */ - uint DMACOB :4; /* DMAC0 transfer/error info */ - uint USB0_1 :4; /* USB controller */ - uint CMT :4; /* Compare Match Timer */ - ); - pad(2); - - word_union(IPRG, - uint SCIF0 :4; /* SCIF0 transfer/error info */ - uint SCIF1 :4; /* SCIF1 transfer/error info */ - uint SCIF2 :4; /* SCIF2 transfer/error info */ - uint :4; /* Video Engine Unit */ - ); - pad(2); - - word_union(IPRH, - uint MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */ - uint MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */ - uint :4; /* I2C Interface channel 0 */ - uint :4; /* I2C Interface channel 1 */ - ); - pad(2); - - word_union(IPRI, - uint SCIFA4 :4; /* SCIFA channel 4 interrupt */ - uint :4; /* MediaRAM InterConnected Buffers */ - uint :4; /* Transport Stream Interface */ - uint :4; /* 2D Graphics Accelerator & ICB */ - ); - pad(2); - - word_union(IPRJ, - uint :4; /* Capture Engine Unit */ - uint :4; /* Ethernet Memory Access Controller */ - uint FSI :4; /* FIFO-Buffered Serial Interface */ - uint SDHI1 :4; /* SD Card Host Interface channel 1 */ - ); - pad(2); - - word_union(IPRK, - uint RTC :4; /* Real-Time Clock */ - uint DMAC1B :4; /* DMAC1 transfer/error info */ - uint :4; /* MediaRAM InterConnected Buffers */ - uint SDHI0 :4; /* SD Card Host Interface channel 0 */ - ); - pad(2); - - word_union(IPRL, - uint SCIFA5 :4; /* SCIFA channel 5 interrupt */ - uint :4; - uint TPU :4; /* Timer-Pulse Unit */ - uint :4; /* Image Extraction DMAC */ - ); - pad(2); - -} __attribute((packed, aligned(4))) mod_intc_ipc_7305_t; - -/* - mod_intc_icr0_7305_t - Interrupt Control Register 0: Detection mode of external pins. -*/ -typedef struct -{ - word_union(, - uint const NMIL :1; /* NMI Input Level */ - uint MAI :1; /* Mask All Interrupts */ - uint :4; - uint NMIB :1; /* Enable NMI when BL is set */ - uint NMIE :1; /* NMI Edge Selection */ - uint :2; - uint LVLMODE :1; /* Level-Sensed IRQ Retention Mode */ - uint :5; - ); - -} __attribute__((packed, aligned(2))) mod_intc_icr0_7305_t; - -/* - mod_intc_icr1_7305_t - Interrupt Control Register 1: Manages detection of IRQ interrupts -*/ -typedef struct -{ - word_union(, - uint IRQ0 :2; /* IRQ0 Sense (Edge) Select */ - uint IRQ1 :2; - uint IRQ2 :2; - uint IRQ3 :2; - uint IRQ4 :2; - uint IRQ5 :2; - uint IRQ6 :2; - uint IRQ7 :2; - ); - -} __attribute__((packed, aligned(2))) mod_intc_icr1_7305_t; - -/* - mod_intc_intpri00_7305_t - Interrupt Priority 00: Priority settings for IRQ interrupts. -*/ -typedef struct -{ - lword_union(, - uint IRQ0 :4; /* IRQ0 Interrupt Priority */ - uint IRQ1 :4; - uint IRQ2 :4; - uint IRQ3 :4; - uint IRQ4 :4; - uint IRQ5 :4; - uint IRQ6 :4; - uint IRQ7 :4; - ); - -} __attribute((packed, aligned(4))) mod_intc_intpri00_7305_t; - -/* - mod_intc_intreq00_7305_t - Interrupt Request 00: Request information of IRQ interrupts. Each of - these bits indicates whether an interrupt is being input. - - mod_intc_intmsk00_7305_t - Interrupt Mask 00: Set interrupt mask for IRQ interrupts. Writing 0 to - these bits is ignored, writing 1 masks the interrupt. - - mod_intc_intmskclr00_7305_t - Interrupt Mask Clear 00: Clear interrupt mask for IRQ interrupts. - Writing 0 to these bits is ignored, writing 1 clears the mask. -*/ -typedef struct -{ - byte_union(, - uint IRQ0 :1; - uint IRQ1 :1; - uint IRQ2 :1; - uint IRQ3 :1; - uint IRQ4 :1; - uint IRQ5 :1; - uint IRQ6 :1; - uint IRQ7 :1; - ); - -} __attribute__((packed)) mod_intc_irq_7305_t; - -typedef mod_intc_irq_7305_t mod_intc_intreq00_7305_t; -typedef mod_intc_irq_7305_t mod_intc_intmsk00_7305_t; -typedef mod_intc_irq_7305_t mod_intc_intmskclr00_7305_t; - -/* - mod_intc_masks_7305_t - A set of bits to mask individual interrupts. - - Masking interrupts is realized by writing 1 to IMRs ; - - Clearing masks is realized by writing 1 to IMCRs ; - Using the wrong register set, such as writing 0 to IMRs to clear a - mask, will be ignored and have no effect. Reading from IMCRs yields an - undefined value. -*/ -typedef struct -{ - byte_union(IMR0, - uint :1; - uint TUNI1_2 :1; /* TMU1 overflow interrupts */ - uint TUNI1_1 :1; - uint TUNI1_0 :1; - uint SDHII3 :1; /* SD Card Host 1 interrupts */ - uint SDHII2 :1; - uint SDHII1 :1; - uint SDHII0 :1; - ); - pad(3); - - byte_union(IMR1, - uint :4; - uint DEI3 :1; /* DMAC0A interrupts */ - uint DEI2 :1; - uint DEI1 :1; - uint DEI0 :1; - ); - pad(3); - - byte_union(IMR2, - uint :7; - uint SCIFA0 :1; /* Asynchronous Serial interrupts */ - ); - pad(3); - - byte_union(IMR3, - uint DEI3 :1; /* DMAC1A interrupts */ - uint DEI2 :1; - uint DEI1 :1; - uint DEI0 :1; - uint :4; - ); - pad(3); - - byte_union(IMR4, - uint :1; - uint TUNI0_2 :1; /* TMU0 overflow interrupts */ - uint TUNI0_1 :1; - uint TUNI0_0 :1; - uint :3; - uint LCDCI :1; /* LCD Controller Interrupt */ - ); - pad(3); - - byte_union(IMR5, - uint KEYI :1; /* Key Interface */ - uint DADERR :1; /* DMAC0B interrupts */ - uint DEI5 :1; - uint DEI4 :1; - uint :1; - uint SCIF2 :1; /* Serial Communication Interface */ - uint SCIF1 :1; - uint SCIF0 :1; - ); - pad(3); - - byte_union(IMR6, - uint :2; - uint :1; - uint SCIFA4 :1; /* SCIFA4 interrupt */ - uint :1; - uint :1; - uint MSIOFI0 :1; /* Clock-synchronized SCIF channel 0 */ - uint MSIOFI1 :1; /* Clock-synchronized SCIF channel 1 */ - ); - pad(3); - - uint8_t IMR7; - pad(3); - - byte_union(IMR8, - uint SDHII3 :1; /* SD Card Host 0 interrupts */ - uint SDHII2 :1; - uint SDHII1 :1; - uint SDHII0 :1; - uint :2; - uint SCFIA5 :1; /* SCIFA5 interrupt */ - uint FSI :1; /* FIFO-Buffered Serial Interface */ - ); - pad(3); - - byte_union(IMR9, - uint :3; - uint CMTI :1; /* Compare Match Timer Interrupt */ - uint :1; - uint USI1 :1; /* USB1 */ - uint USI0 :1; /* USB0 */ - uint :1; - ); - pad(3); - - byte_union(IMR10, - uint :1; - uint DADERR :1; /* DMAC1B interrupts */ - uint DEI5 :1; - uint DEI4 :1; - uint :1; - uint ATI :1; /* RTC Alarm interrupt */ - uint PRI :1; /* RTC Periodic interrupt */ - uint CUI :1; /* RTC Carry interrupt */ - ); - pad(3); - - byte_union(IMR11, - uint :5; - uint TPUI :1; /* Timer-Pulse Unit */ - uint :2; - ); - pad(3); - - uint8_t IMR12; - char gap2[15]; - -} __attribute__((packed, aligned(4))) mod_intc_masks_7305_t; - -/* - mod_intc_userimask_7305_t - User Interrupt Mask: Specifies the minimum required level for - interrupts to be accepted. - - WARNING: Writing to this register requires the eight upper bits of the - operand (ie. the new value of USERIMASK) to be 0xa5; otherwise, the - write is ignored. To modify the value of this register, do not access - the bit field directly, backup the variable and modify it: - - void set_user_imask(int new_level) - { - mod_intc_userimask_7305_t mask = *(INTC._7305.USERIMASK); - mask._0xa5 = 0xa5; - mask.UIMASK = new_level & 0x0f; - *(INTC._7305.USERIMASK) = mask; - } -*/ -typedef struct -{ - lword_union(, - uint _0xa5 :8; /* Always set to 0xa5 before writing */ - uint :16; - uint UIMASK :4; /* User Interrupt Mask Level */ - uint :4; - ); - -} __attribute__((packed, aligned(4))) mod_intc_userimask_7305_t; - -/* - mod_intc_nmifcr_7305_t - NMI Flag Control Register: Indicates the state of the NMI pin and the - NMI interrupt request. -*/ -typedef struct -{ - word_union(, - uint const NMIL :1; /* NMI Interupt Level */ - uint :14; - uint NMIFL :1; /* NMI Interrupt Request Flag */ - ); - -} __attribute__((packed, aligned(2))) mod_intc_nmifcr_7305_t; - -/* - mod_intc_7305_t - Finally the whole interrupt controller. -*/ -typedef struct -{ - /* Control registers */ - volatile mod_intc_icr0_7305_t *ICR0; - volatile mod_intc_icr1_7305_t *ICR1; - - /* Interrupt priority registers */ - volatile mod_intc_intpri00_7305_t *INTPRI00; - volatile mod_intc_ipc_7305_t *iprs; - - /* Interrupt mask & mask clear registers */ - volatile mod_intc_intmsk00_7305_t *INTMSK00; - volatile mod_intc_masks_7305_t *masks; - volatile mod_intc_intmskclr00_7305_t *INTMSKCLR00; - volatile mod_intc_masks_7305_t *masks_clear; - - /* Other registers */ - volatile mod_intc_intreq00_7305_t *INTREQ00; - volatile mod_intc_userimask_7305_t *USERIMASK; - volatile mod_intc_nmifcr_7305_t *NMIFCR; - -} mod_intc_7305_t; - - - -//--- -// Platform-independent structures. -// Unfortunately there is nothing here. Users willing to manage interrupts -// using the INTC register will have to handle explicitely both platforms. -//--- - -/* - mod_intc_t - Interrupt Controller. -*/ -typedef union -{ - mod_intc_7705_t _7705; - mod_intc_7305_t _7305; - -} __attribute__((packed)) mod_intc_t; - -// Here's what you'll need to use. -extern mod_intc_t INTC; - -#endif // _MODULE_INTERRUPTS_H diff --git a/include/modules/macros.h b/include/modules/macros.h deleted file mode 100644 index 327dde7..0000000 --- a/include/modules/macros.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef _MODULES_MACROS_H -#define _MODULES_MACROS_H - -#include - -// Padding is just empty space, we don't want to give it a type. There's also -// some subtle preprocessor trick to automatically add a (supposedly) unique -// name to each padding member. For instance the substitution may operate as: -// name(__LINE__) -> namesub(78) -> _##78 -> _78 -#define namesub(x) _##x -#define name(x) namesub(x) -#define pad(bytes) \ - uint8_t name(__LINE__)[bytes] - -// Fixed-width types for bit field are totally meaningless. -typedef unsigned uint; - -/* - byte_union() - Union between an uint8_t 'byte' attribute and a provided bit-field - structure that describe the contents of the byte. -*/ -#define byte_union(name, fields) \ - union \ - { \ - uint8_t byte; \ - struct { fields } \ - __attribute__((packed)); \ - } __attribute__((packed)) name - -/* - word_union() - Union between an uint16_t 'word' attribute and a provided bit-field - structure that describe the contents of the word. -*/ -#define word_union(name, fields) \ - union \ - { \ - uint16_t word; \ - struct { fields } \ - __attribute__((packed)); \ - } __attribute__((packed, aligned(2))) name - -/* - lword_union() - Union between an uint32_t 'lword' attribute and a provided bit-field - structure that describe the contents of the longword. -*/ -#define lword_union(name, fields) \ - union \ - { \ - uint32_t lword; \ - struct { fields } \ - __attribute__((packed)); \ - } __attribute__((packed, aligned(4))) name - -#endif // _MODULES_MACROS_H diff --git a/include/modules/rtc.h b/include/modules/rtc.h deleted file mode 100644 index 7eb7ef0..0000000 --- a/include/modules/rtc.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef _MODULES_RTC_H -#define _MODULES_RTC_H - -#include -#include - -/* - mod_rtc_rcr1_t - First RTC configuration register. -*/ -typedef struct -{ - byte_union(, - uint CF :1; /* Carry flag */ - uint :2; - uint CIE :1; /* Carry interrupt enable */ - uint AIE :1; /* Alarm interrupt enable */ - uint :2; - uint AF :1; /* Alarm flag */ - ); - -} __attribute__((packed)) mod_rtc_rcr1_t; - -/* - mod_rtc_rcr2_t - Second RTC configuration register. -*/ -typedef struct -{ - byte_union(, - uint PEF :1; /* Periodic interrupt flag */ - uint PES :3; /* Periodic interrupt interval */ - uint :1; - uint ADJ :1; /* 30-second adjustment */ - uint RESET :1; /* Reset */ - uint START :1; /* Start bit */ - ); - -} __attribute__((packed)) mod_rtc_rcr2_t; - -/* - mod_rtc_rcr3_t - Third RTC configuration register. - -typedef struct -{ - byte_union(, - uint YAEN :1; - uint :7; - ); - -} __attribute__((packed)) mod_rtc_rcr3_t; -*/ - -/* - mod_rtc_time_t - A set of registers describing the current time in BCD format. -*/ -typedef struct -{ - const uint8_t R64CNT; /* More or less a 64-Hz counter */ - pad(1); - - byte_union(RSECCNT, /* Second count */ - uint :1; - uint TENS :3; - uint ONES :4; - ); - pad(1); - - byte_union(RMINCNT, /* Minute count */ - uint :1; - uint TENS :3; - uint ONES :4; - ); - pad(1); - - byte_union(RHRCNT, /* Hour count */ - uint :2; - uint TENS :2; - uint ONES :4; - ); - pad(1); - - /* 0 = Sunday .. 6 = Saturday, other settings are prohibited. */ - uint8_t RWKCNT; /* Day of week */ - pad(1); - - byte_union(RDAYCNT, /* Day count */ - uint :2; - uint TENS :2; - uint ONES :4; - ); - pad(1); - - byte_union(RMONCNT, /* Month count */ - uint :3; - uint TENS :1; - uint ONES :4; - ); - pad(1); - - word_union(RYRCNT, /* Year count */ - uint THOUSANDS :4; - uint HUNDREDS :4; - uint TENS :4; - uint ONES :4; - ); - -} __attribute__((packed, aligned(2))) mod_rtc_time_t; - -/* - mod_rtc_t (resides into gint memory) - Three control registers and timer information. -*/ -typedef struct -{ - /* RCR1, RCR2, and RCR3 are the same for both platforms. */ - volatile mod_rtc_rcr1_t *RCR1; - volatile mod_rtc_rcr2_t *RCR2; -// volatile mod_rtc_rcr3_t *RCR3; - /* Current time in register memory */ - volatile mod_rtc_time_t *time; - -} mod_rtc_t; - -// All you need to use is this structure, initialized by gint, which contains -// address that work with your execution platform. -extern mod_rtc_t RTC; - -#endif // _INTERNALS_RTC_H diff --git a/include/modules/timer.h b/include/modules/timer.h deleted file mode 100644 index 46657ae..0000000 --- a/include/modules/timer.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef _MODULES_TIMER_H -#define _MODULES_TIMER_H - -#include -#include - -/* - mod_tmu_timer_t - Registers of a single timer. -*/ -typedef struct -{ - uint32_t TCOR; /* Timer constant register */ - uint32_t TCNT; /* Timer counter */ - - word_union(TCR, - uint :7; - uint UNF :1; /* Underflow flag */ - uint :2; - uint UNIE :1; /* Underflow interrupt enable */ - uint CKEG :2; /* Clock edge (SH7705) */ - uint TPSC :3; /* Timer prescaler */ - ); - - uint16_t :16; - -} __attribute__((packed, aligned(4))) mod_tmu_timer_t; - -/* - mod_tmu_tstr_t - The timer start register. -*/ -typedef struct -{ - byte_union(, - uint :5; - uint STR2 :1; /* Counter start 2 */ - uint STR1 :1; /* Counter start 1 */ - uint STR0 :1; /* Counter start 0 */ - ); - -} __attribute__((packed)) mod_tmu_tstr_t; - -/* - mod_tmu_t (resides into gint memory) - Basically three timer units and an additional register to control who's - running. -*/ -typedef struct -{ - /* Timer configurations */ - volatile mod_tmu_timer_t *timers[3]; - /* Timer start register */ - volatile mod_tmu_tstr_t *TSTR; - /* Timer input capture 2 (SH7705) */ - volatile const uint32_t *TCPR2; - -} mod_tmu_t; - -// The actual thing is there. It's initialized by gint at startup and contains -// addresses and information suitable for the current platform. -extern mod_tmu_t TMU; - -#endif // _MODULES_TIMER diff --git a/include/mpu.h b/include/mpu.h deleted file mode 100644 index 8b7219d..0000000 --- a/include/mpu.h +++ /dev/null @@ -1,71 +0,0 @@ -//--- -// -// gint core module: mpu -// -// Determines which kind of MPU is running the program. This module -// provides macro tests isSH3(), isSH4(), and the identifier of the MPU, -// which is stored in a global variable MPU_CURRENT and determined at -// startup. -// -// If you need to do MPU-dependent jobs, use isSH3() or (possibly) isSH4() -// instead of testing the value of MPU_CURRENT because these macros take -// care of assuming unknown MPUs are SH4, which is the more reasonable -// option. -// -// It is advised to always use the following alternative (which gint -// does): -// -// if(isSH3()) -// { -// ... -// } -// else -// { -// ... -// } -// -//--- - -#ifndef _MPU_H -#define _MPU_H - -/* - mpu_t - This type holds information about the calculator's MPU. -*/ -typedef enum -{ - mpu_unknown = 0, - // fx-9860G SH3. - mpu_sh7337 = 1, - // fx-9860G II SH3. - mpu_sh7355 = 2, - // fx-9860G II SH4. - mpu_sh7305 = 3, - // Just for reference (no calculator uses it). - mpu_sh7724 = 4, - -} mpu_t; - -// Global MPU variable, accessible for direct tests. Initialized at the -// beginning of execution. -extern const mpu_t MPU_CURRENT; - -// Quick SH3 test. It is safer to assume that an unknown model is SH4 because -// SH3-based models are not produced anymore. -#define isSH3() (MPU_CURRENT == mpu_sh7337 || MPU_CURRENT == mpu_sh7355) -#define isSH4() (!isSH3()) - - - -//--- -// Public API. -//--- - -/* - getMPU() - Determines the MPU type and returns it. MPU_CURRENT is not updated. -*/ -mpu_t getMPU(void); - -#endif // _MPU_H diff --git a/include/rtc.h b/include/rtc.h deleted file mode 100644 index 2c65876..0000000 --- a/include/rtc.h +++ /dev/null @@ -1,105 +0,0 @@ -//--- -// -// gint core module: rtc -// -// Manages the Real-Time Clock (RTC). This module provides access to the -// current time as well as regular callbacks at predefined frequencies, -// more or less like a timer. -// The standard time module is built upon this one. -// -//--- - -#ifndef _RTC_H -#define _RTC_H - -#include - -//--- -// Time access. -//--- - -/* - rtc_time_t - Defines a point in time. -*/ -typedef struct -{ - uint8_t seconds; // Seconds in range 0-59 - uint8_t minutes; // Minutes in range 0-59 - uint8_t hours; // Hours in range 0-23 - uint8_t month_day; // Day of month in range 1-31 - uint8_t month; // Month in range 0-11 - uint8_t week_day; // Day of week in range 0(Sunday)-6(Saturday). - uint16_t year; // Years (around 2000) - -} rtc_time_t; - -/* - rtc_getTime() - Reads the current time from the RTC. There is no guarantee that the - week day is correct (use the time API for that). -*/ -void rtc_getTime(rtc_time_t *time); - -/* - rtc_setTime() - Sets the time in the RTC registers. The week day is set to 0 if greater - than 6. Other fields are not checked. -*/ -void rtc_setTime(const rtc_time_t *time); - - - -//--- -// Callback API. -//--- - -/* - rtc_frequency_t - Describes the possible frequencies available for the real-time clock - interrupt. -*/ -typedef enum -{ - rtc_freq_500mHz = 7, - rtc_freq_1Hz = 6, - rtc_freq_2Hz = 5, - rtc_freq_4Hz = 4, - rtc_freq_16Hz = 3, - rtc_freq_64Hz = 2, - rtc_freq_256Hz = 1, - rtc_freq_none = 0, - -} rtc_frequency_t; - -/* - rtc_cb_add() - Registers a new callback for the RTC. Returns the callback id on - success (positive integer), or one of the following error codes: - -1 Array is full - -2 Invalid parameter - The number of repeats may be set to 0, in which case the callback is - called indefinitely unless the user calls rtc_cb_end(). -*/ -int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats); - -/* - rtc_cb_end() - Removes the callback with the given id (as returned by rtc_cb_add()) - from the callback array. -*/ -void rtc_cb_end(int id); - -/* - rtc_cb_edit() - Changes information related to a callback. This function returns 0 on - success, or one of the following error codes: - -1 Callback does not exist - -2 Invalid parameters - This function never removes a callback. Call rtc_cb_end() for this. One - can set the function to NULL or the frequency to rtc_freq_none to - temporarily disable the callback. -*/ -int rtc_cb_edit(int id, rtc_frequency_t new_freq, void (*new_function)(void)); - -#endif // _RTC_H diff --git a/include/screen.h b/include/screen.h deleted file mode 100644 index ec459a5..0000000 --- a/include/screen.h +++ /dev/null @@ -1,35 +0,0 @@ -//--- -// -// gint display module: screen -// -// Interacts with the physical screen. See other display modules for video -// ram management and drawing. -// -// The screen basically has two input values, which are a register -// selector and the selected register's value. What this module does is -// essentially selecting registers by setting *selector and assigning them -// values by setting *data. -//--- - -#ifndef _SCREEN_H -#define _SCREEN_H - -/* - screen_display() - Displays contents on the full screen. Expects a 1024-byte buffer. -*/ -void screen_display(const void *vram); - -/* - screen_setBacklight() - On compatible models, turns on or turns off the backlight. -*/ -void screen_setBacklight(int on); - -/* - screen_toggleBacklight() - Changes the backlight state, regardless of its current state. -*/ -void screen_toggleBacklight(void); - -#endif diff --git a/include/setjmp.h b/include/setjmp.h deleted file mode 100644 index b3aa872..0000000 --- a/include/setjmp.h +++ /dev/null @@ -1,37 +0,0 @@ -//--- -// -// gint standard module: setjmp -// -// Long jumps. The register contents are saved in a buffer when setjmp() -// is called and restored at any time when longjmp() performs the jump. -// -//--- - -#ifndef _SETJMP_H -#define _SETJMP_H - -#include - -// There are 16 CPU registers that *must* be saved to ensure a basically -// safe jump. -typedef uint32_t jmp_buf[16]; - - - -//--- -// Long jump functions. -//--- - -/* - setjmp() O(1) - Configures a jump by saving data to the given jump buffer. -*/ -int setjmp(jmp_buf env); - -/* - longjmp() O(1) - Performs a long jump. -*/ -void longjmp(jmp_buf env, int value); - -#endif // _SETJMP_H diff --git a/include/stdio.h b/include/stdio.h deleted file mode 100644 index ba27de6..0000000 --- a/include/stdio.h +++ /dev/null @@ -1,44 +0,0 @@ -//--- -// -// standard library module: stdio -// -// Handles most input/output for the program. This module does not -// interact with the file system directly. -// -//--- - -#ifndef _STDIO_H -#define _STDIO_H - -#include -#include - -//--- -// Formatted printing. -//--- - -/* - sprintf() - Prints to a string. -*/ -int sprintf(char *str, const char *format, ...); - -/* - snprintf() - Prints to a string with a size limit. -*/ -int snprintf(char *str, size_t size, const char *format, ...); - -/* - vsprintf() - Prints to a string from an argument list. -*/ -int vsprintf(char *str, const char *format, va_list args); - -/* - vsnprintf() - The most generic formatted printing function around there. -*/ -int vsnprintf(char *str, size_t size, const char *format, va_list args); - -#endif // _STDIO_H diff --git a/include/stdlib.h b/include/stdlib.h deleted file mode 100644 index 59b6272..0000000 --- a/include/stdlib.h +++ /dev/null @@ -1,149 +0,0 @@ -//--- -// -// standard library module: stdlib -// -// Provides standard functionalities such as dynamic allocation, -// string/numeric conversion, and abort calls. -// -//--- - -#ifndef _STDLIB_H -#define _STDLIB_H - -//--- -// Common definitions. -//--- - -#include -#include - -// Common exit codes. -#define EXIT_SUCCESS 1 -#define EXIT_FAILURE 0 - -// Number of atexit() registrations guaranteed. -#ifndef ATEXIT_MAX -#define ATEXIT_MAX 16 -#endif - -// Maximum value returned by rand(). -#define RAND_MAX INT_MAX - -// Integer division result. -typedef struct -{ - int quot, rem; -} div_t; -typedef struct -{ - long quot, rem; -} ldiv_t; - - - -//--- -// Program exit functions. -//--- - -/* - abort() - Aborts the program execution without calling the exit handlers. -*/ -void abort(void); - -/* - exit() - Stops the program execution with the given status code, after calling - the exit handlers. -*/ -void exit(int status); - -/* - atexit() - Registers a function to be called at normal program termination. -*/ -int atexit(void (*function)(void)); - - - -//--- -// Dynamic storage allocation. -//--- - -/* - malloc() - Allocates 'size' bytes and returns a pointer to a free memory area. - Returns NULL on error. -*/ -void *malloc(size_t size); - -/* - calloc() - Allocates 'n' elements of size 'size' and wipes the memory area. - Returns NULL on error. -*/ -void *calloc(size_t n, size_t size); - -/* - realloc() - Reallocates a memory block and moves its data. -*/ -void *realloc(void *ptr, size_t size); - -/* - free() - Frees a memory block allocated by malloc(), calloc() or realloc(). -*/ -void free(void *ptr); - - - -//--- -// Random number generation. -//--- - -/* - rand() - Returns a pseudo-random number. -*/ -int rand(void); - -/* - srand() - Changes the seed used by rand(). -*/ -void srand(unsigned int seed); - -//--- -// Integer arithmetic. -//--- - -/* - abs() - Returns the absolute value of an integer. -*/ -int abs(int x); -// Use a macro instead, when possible. -#define abs(x) ((x) < 0 ? -(x) : (x)) - -/* - labs() - Returns the absolute value of a long integer. -*/ -long labs(long x); -// Use a macro instead. -#define labs(x) ((x) < 0 ? -(x) : (x)) - -/* - div() - Computes the integer division of numerator by denominator. -*/ -div_t div(int numerator, int denominator); - -/* - ldiv() - Computes the integer division of two long integers. -*/ -ldiv_t ldiv(long numerator, long denominator); - -#endif // _STDLIB_H diff --git a/include/string.h b/include/string.h deleted file mode 100644 index 70ac65a..0000000 --- a/include/string.h +++ /dev/null @@ -1,94 +0,0 @@ -//--- -// -// standard library module: string -// -// String manipulation using NUL-terminated byte arrays, without extended -// characters. -// -//--- - -#ifndef _STRING_H -#define _STRING_H - -#include - -//--- -// Memory manipulation. -//--- - -/* - 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_count); - -/* - 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_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); - - - -//--- -// String manipulation. -//--- - -/* - strlen() O(len(str)) - Returns the length of a string. -*/ -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. -*/ -const char *strchr(const char *str, int value); - -/* - 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); - -#endif // _STRING_H diff --git a/include/tales.h b/include/tales.h deleted file mode 100644 index 2f189b7..0000000 --- a/include/tales.h +++ /dev/null @@ -1,128 +0,0 @@ -//--- -// -// gint drawing module: tales -// -// Text displaying. Does some pretty good optimization, though requires -// dynamic allocation. The stack is used. -// -//--- - -#ifndef _TALES_H -#define _TALES_H - -#include -#include -#include - -//--- -// Types and constants. -//--- - -/* - font_format_t - This type holds information about the characters in the font. Each bit - represents various characters, and the type itself is a combination of - several of those bits. - Bits represent the following characters (lsb right): - -- -- -- non-print | special capitals lower numbers -*/ -typedef enum -{ - font_format_unknown = 0x00, - font_format_numeric = 0x01, - font_format_lower = 0x02, - font_format_upper = 0x04, - font_format_letters = 0x06, - font_format_common = 0x07, - font_format_print = 0x0f, - font_format_ascii = 0x1f, - -} font_format_t; - -/* - font_glyph_t - Holds a glyph's data. The width is used for spacing, and the raw data - is encoded line after line, from to to bottom, by appending bits - without consideration of the byte boundaries. - This structure is actually never used, because data is read directly - as a longword array (hence the 4-byte alignment). -*/ -typedef struct -{ - uint8_t width; - const uint8_t data[]; - -} __attribute__((packed, aligned(4))) font_glyph_t; - -/* - font_t - Holds a font's data. Data is accessed using longword operations, hence - the 4-alignment attributes. The line height is the one given in the - font image header line, which may be used by applications that write - strings on several lines. The data height is the height of the biggest - glyph. Every glyph is encoded on 'data_height' lines, for optimization - considerations. - The index field is used to reduce character access time. - The name field may not be NUL-terminated when the name contains 28 - characters. When the name is shorter, the field is padded with zeros. -*/ -typedef struct -{ - uint8_t magic; - uint8_t format; - uint8_t line_height; - uint8_t data_height; - - // Warning : this field may not be NUL-terminated. - char name[28]; - - uint16_t index[16]; - - __attribute__((aligned(4))) const uint32_t glyphs[]; - -} __attribute__((packed, aligned(4))) font_t; - - - -//--- -// Generic functions. -//--- - -/* - text_configure() - Sets the font and color to use for subsequent text operations. Pass - font = NULL to use the default font. -*/ -void text_configure(font_t *font, color_t operator); - -/* - text_length() - Computes the length of a string using the currently configured font. -*/ -size_t text_length(const char *str); - -/* - dtext() - Prints the given string, without any analysis. -*/ -void dtext(int x, int y, const char *str); - -/* - gtext() - Prints the given raw string. -*/ -void gtext(int x, int y, const char *str); - -/* - dprint() - Prints a formatted string. Works the same as printf(). -*/ -void dprint(int x, int y, const char *format, ...); - -/* - gprint() - Prints a formatted string. Works the same as printf(). -*/ -void gprint(int x, int y, const char *format, ...); - -#endif // _TALES_H diff --git a/include/time.h b/include/time.h deleted file mode 100644 index 9ec8298..0000000 --- a/include/time.h +++ /dev/null @@ -1,126 +0,0 @@ -//--- -// -// standard library module: time -// -// Provides time manipulation and representation functions. -// -//--- - -#ifndef _TIME_H -#define _TIME_H - -#include - -//--- -// Some related types. -//--- - -/* - struct tm - Represents a point in time and gives some date information. -*/ -struct tm -{ - int tm_sec; // Seconds in range 0-59 - int tm_min; // Minutes in range 0-59 - int tm_hour; // Hours in range 0-23 - int tm_mday; // Day of month in range 1-31 - int tm_mon; // Month in range 0-11 - int tm_year; // Number of years since 1900 - int tm_wday; // Day of week in range 0(Sunday)-6(Saturday). - int tm_yday; // Day of the year in range 0-365. - int tm_isdst; // This will always be 0. -}; - -/* - clock_t - Only used by clock(). -*/ -typedef signed int clock_t; - -/* - time_t - Number of seconds elapsed since 1970-01-01 00:00:00. -*/ -typedef signed int time_t; - - - -//--- -// Time access. -//--- - -/* - clock() - Should return elapsed CPU time since beginning of program execution. - This is currently not implemented and returns -1. -*/ -clock_t clock(void); - -/* - time() - Returns the current time as calendar time. If you need a broken-down - time, either use the RTC API or gmtime(). However, this function is - already based on mktime() (for hardware reasons) so it would be much - faster to use the RTC API if possible. - If timeptr is not NULL, it is set to the current time, that is, the - value that is returned. -*/ -time_t time(time_t *timeptr); - -/* - difftime() - Returns the number of seconds between the given points. -*/ -double difftime(time_t end, time_t beginning); -// But this macro should do. -#define difftime(end, beginning) ((double)((end) - (beginning))) - - - -//--- -// Time representation. -//--- - -/* - asctime() - Converts broken-down time to string representation on the form - "Wed Jun 30 21:49:08 1993\n". The returned string is statically - allocated and may be overwritten by any subsequent call to a time - function. -*/ -char *asctime(const struct tm *time); - -/* - ctime() - Converts calendar time to string representation on the form - "Wed Jun 30 21:49:08 1993\n". The returned string is statically - allocated and may be overwritten by any subsequent call to a time - function. -*/ -char *ctime(const time_t *timer); - - - -//--- -// Time conversion. -//--- - -/* - mktime() - Converts broken-down time to calendar time. Computes structure fields - tm_wday and tm_yday using the other fields. Member structures outside - their range are normalized (e.g. 40 October becomes 9 November) and - tm_isdst is set. -*/ -time_t mktime(struct tm *time); - -/* - gmtime() - Converts calendar time to broken-down time. The returned pointer is - statically allocated and may be overwritten by any subsequent call to - a time function. -*/ -struct tm *gmtime(const time_t *t); - -#endif // _TIME_H diff --git a/include/timer.h b/include/timer.h deleted file mode 100644 index f6cad24..0000000 --- a/include/timer.h +++ /dev/null @@ -1,177 +0,0 @@ -//--- -// -// gint core module: timer -// -// Basic timer unit manipulation. Starts and stops timers with variable -// number of repeats, and allows timer reloads without pause. -// -//--- - -#ifndef _TIMER_H -#define _TIMER_H - -#include -#include -#include - -// The timer object is manipulated by the module; the user needs not access it -// directly. Its contents are defined in . -struct timer_t; -typedef struct timer_t timer_t; - -//--- -// Virtual timer API -// Gint allows users to create virtual timers with 1-ms precision to -// handle an important amount of timed events. The amount of virtual -// timers available is gapped by the TIMER_SLOTS parameter, but this -// parameter can be customized when building the library; thus, the -// amount of usable virtual timers is not limited. -//--- - -#ifndef TIMER_SLOTS -#define TIMER_SLOTS 16 -#endif - -/* - timer_create() - Basic timer configuration. This function creates a virtual timer and - configures its delay and repetition count. It returns a virtual timer - object that will be used by other functions. At most TIMER_SLOTS - virtual timers can be used at the same time: this function returns NULL - if there is no new slot available. - By default a virtual timer configured by timer_configure() will fire - ET_Timer events, which the user will need to catch. The user should - then execute some action. - There is another option: the user may call timer_attach() to attach a - callback to a virtual timer. A virtual timer which has a callback - attached will not fire any ET_Timer event and will call the callback - automatically instead. -*/ -timer_t *timer_create(int delay_ms, int repeats); - -/* - timer_reload() - Changes a virtual timer's delay. The timer is not stopped nor started: - it keeps running or waiting. Events that were waiting to be handled are - dropped and the number of repeats left is not changed. The timer - restarts counting from 0 regardless of how much time had elapsed since - it last fired. -*/ -void timer_reload(timer_t *timer, int new_ms_delay); - -/* - timer_destroy() - Destroys a virtual timer. This virtual timer pointer becomes invalid - and should not be used anymore. -*/ -void timer_destroy(timer_t *virtual_timer); - - - -//--- -// Hardware timer API -// Gint provides access to hardware timer. These timer offer more or less -// microsecond-level control. However, the user should ensure, when using -// hardware timers, that they are not overriding the configuration of -// timers that are already running -- gint won't check. -//--- - -/* - timer_hard_t - Available hardware timers. The user can use timer_user freely, but - timer_gray and timer_virtual should not be used as long as the gray - engine or virtual timers are running (respectively). -*/ -typedef enum -{ - timer_tmu0 = 0, - timer_virtual = timer_tmu0, - - timer_tmu1 = 1, - timer_gray = timer_tmu1, - - timer_tmu2 = 2, - timer_user = timer_tmu2, - -} timer_hard_t; - -/* - timer_input_t - Available input clocks for the hardware timer: - - Po/4 Peripheral clock (frequency divided by 4) - - Po/16 Peripheral clock (frequency divided by 16) - - Po/64 Peripheral clock (frequency divided by 64) - - Po/256 Peripheral clock (frequency divided by 256) - - TCLK External clock - I'm not totally sure there is any signal on the external clock, so - don't use it unless you know what you are doing. -*/ -typedef enum -{ - timer_Po_4 = 0, - timer_Po_16 = 1, - timer_Po_64 = 2, - timer_Po_256 = 3, - timer_tclk = 5, - -} timer_input_t; - -/* - htimer_setup() - Configures a hardware timer. By default hardware timers generates - ET_Timer events but catching them may take some time, especially if - there are other events waiting in the queue. For increased timing, and - for fast timers, the user should consider using callbacks instead. - Returns a hardware timer object. - Returns the correct timer structure if the requested timer is free and - NULL otherwise. -*/ -timer_t *htimer_setup(timer_hard_t id, uint32_t constant, timer_input_t input, - int repeats); - -/* - htimer_reload() - Reloads a hardware timer without starting or stopping it. -*/ -void htimer_reload(timer_hard_t id, uint32_t new_constant); - - - -//--- -// Common API -// The following functions work with both virtual and hardware timers. -//--- - -/* - timer_attach() - This function attaches a callback to a virtual or hardware timer. A - timer with a callback attached will stop firing ET_Timer events and - will call the callback function directly instead. - The type signature of the function should be as follows: - - void callback(void) if argument == NULL - - void callback(void *argument) if argument is non-NULL - In the latter case, the argument pointer will be passed to the callback - function. -*/ -void timer_attach(timer_t *timer, void *callback, void *argument); - -/* - timer_start() - Starts a virtual or hardware timer. If the timer has a callback - attached, then the callback function will start being called regularly; - otherwise, the timer will start pushing ET_Timer events to the event - queue. It is advised, not to change a timer's configuration while it's - running. -*/ -void timer_start(timer_t *timer); - -/* - timer_stop() - Stops a timer. If the argument is virtual timer, it is paused and can - be started again later. - If it's a hardware timer, it is freed and made accessible to other - uses. It should be set up again before being started. -*/ -void timer_stop(timer_t *timer); - -#endif // _TIMER_H diff --git a/src/bopti/bopti_internals.c b/src/bopti/bopti_internals.c deleted file mode 100644 index cfe5013..0000000 --- a/src/bopti/bopti_internals.c +++ /dev/null @@ -1,331 +0,0 @@ -#include - -// Monochrome video ram, light and dark buffers (in this order). -uint32_t *bopti_vram, *bopti_v1, *bopti_v2; - -/* - bopti_op() - Operates on a vram long. The operator will often not contain 32 bits of - image information. Since neutral bits are not the same for all - operations, a mask is used to indicate which bits should be used for - the operation. This mask is taken for the image's rectangle masks (see - module display for more information on rectangle masks). - Which operation is performed is determined by the channel setting. -*/ -void bopti_op_mono(int offset, uint32_t operator, command_t *c) -{ - operator &= c->masks[offset & 3]; - - switch(c->channel) - { - case channel_full_alpha: - bopti_vram[offset] &= ~operator; - break; - - case channel_mono: - bopti_vram[offset] |= operator; - break; - - default: - break; - } -} -void bopti_op_gray(int offset, uint32_t operator, command_t *c) -{ - operator &= c->masks[offset & 3]; - - switch(c->channel) - { - case channel_full_alpha: - bopti_v1[offset] &= ~operator; - bopti_v2[offset] &= ~operator; - break; - - case channel_light_alpha: - case channel_dark_alpha: - break; - - case channel_mono: - bopti_v1[offset] |= operator; - bopti_v2[offset] |= operator; - break; - - case channel_light: - bopti_v1[offset] |= operator; - break; - - case channel_dark: - bopti_v2[offset] |= operator; - break; - } -} - -/* - bopti_grid() -- general form - bopti_grid_a32() -- when x is a multiple of 32 - - Draws the grid at the beginning of a layer's data. The length of this - grid is always a multiple of 32. - The need for bopti_grid_a32() is not only linked to optimization, - because bopti_grid() will perform a 32-bit shift when x is a multiple - of 32, which is undefined behavior. -*/ -void bopti_grid_a32(const uint32_t *layer, int column_count, int height, - command_t *c) -{ - int vram_column_offset = (c->y << 2) + (c->x >> 5); - int vram_offset = vram_column_offset; - int column, row; - - for(column = 0; column < column_count; column++) - { - for(row = c->top; row < c->bottom; row++) - { - (c->op)(vram_offset, layer[row], c); - vram_offset += 4; - } - - vram_column_offset++; - vram_offset = vram_column_offset; - layer += height; - } -} -void bopti_grid(const uint32_t *layer, int column_count, int height, - command_t *c) -{ - if(!column_count) return; - if(!(c->x & 31)) - { - bopti_grid_a32(layer, column_count, height, c); - return; - } - - const uint32_t *p1, *p2; - uint32_t l1, l2, operator; - int right_column, line; - int actual_column_count; - - int vram_column_offset = (c->y << 2) + (c->x >> 5) + (c->x < 0); - int vram_offset = vram_column_offset; - - int shift2 = (c->x & 31); - int shift1 = 32 - shift2; - - // Initializing two pointers. They will read two adjacent columns at - // the same time (p2 is column ahead of p1). Since the columns are - // written one after another, incrementing them will suffice when - // reaching the end of two columns, to move them to the next ones. - p1 = layer - height; - p2 = layer; - - // We don't want to write the first vram column when x is negative - // because it's outside the screen. - if(c->x < 0) p1 += height, p2 += height; - right_column = (c->x < 0); - // For the same reason, we don't to draw the additional rightmost - // column when it begins after 96. - if(c->x + (column_count << 5) > 128) - actual_column_count = column_count - 1; - else - actual_column_count = column_count; - - // Drawing vram longwords, using pairs of columns. - while(right_column <= actual_column_count) - { - for(line = c->top; line < c->bottom; line++) - { - l1 = (right_column > 0) ? p1[line] : (0); - l2 = (right_column < column_count) ? p2[line] : (0); - - operator = (l1 << shift1) | (l2 >> shift2); - (c->op)(vram_offset, operator, c); - - vram_offset += 4; - } - - p1 += height; - p2 += height; - vram_column_offset++; - vram_offset = vram_column_offset; - right_column++; - } -} - -/* - bopti_end_get() - Returns an operator for the end of a line, whose width is lower than 32 - (by design: otherwise, it would have been a column). The given pointer - is read and updated so that it points to the next line at the end of - the operation. -*/ -uint32_t bopti_end_get1(const unsigned char **data) -{ - uint32_t operator = **data; - *data += 1; - return operator; -} -uint32_t bopti_end_get2(const unsigned char **data) -{ - uint32_t operator = *((uint16_t *)*data); - *data += 2; - return operator; -} - -/* - bopti_rest() -- general form - bopti_rest_nover() -- when the end does not overlap two vram longs - - Draws the end of a layer, which can be considered as a whole layer - whose with is lower than 32. (Actually is it lower or equal to 16; - otherwise it would have been a column and the end would be empty.) -*/ -void bopti_end_nover(const unsigned char *end, int size, command_t *c) -{ - uint32_t (*get)(const unsigned char **data) = - (size == 2) ? bopti_end_get2 : bopti_end_get1; - - // We *have* shift >= 0 because of this function's 'no overlap' - // requirement. - int shift = (32 - (size << 3)) - (c->x & 31); - int vram_offset = (c->y << 2) + (c->x >> 5); - uint32_t operator; - int row; - - // Skipping c->top lines (because get() function only allows sequential - // access). - end += c->top * size; - - for(row = c->top; row < c->bottom; row++) - { - operator = (*get)(&end); - operator <<= shift; - (c->op)(vram_offset, operator, c); - vram_offset += 4; - } -} -void bopti_end(const unsigned char *end, int size, command_t *c) -{ - uint32_t (*get)(const unsigned char **data) = - (size == 2) ? (bopti_end_get2) : (bopti_end_get1); - - int vram_offset = (c->y << 2) + (c->x >> 5); - uint32_t row_data; - int row; - - int shift_base = (32 - (size << 3)); - int shift1 = (c->x & 31) - shift_base; - int shift2 = shift_base + 32 - (c-> x & 31); - - // Skipping c->top lines (because get() function only allows sequential - // access). - end += c->top * size; - - for(row = c->top; row < c->bottom; row++) - { - row_data = (*get)(&end); - (c->op)(vram_offset, row_data >> shift1, c); - (c->op)(vram_offset + 1, row_data << shift2, c); - vram_offset += 4; - } -} - - - -//--- -// Wrappers and various functions. -//--- - -/* - bopti() - Draws a layer in the video ram. -*/ -void bopti(const unsigned char *layer, structure_t *s, command_t *c) -{ - const unsigned char *grid, *end; - int grid_columns, has_end; - - // Skipping columns at the beginning. - grid = layer + ((c->left * s->height) << 2); - - // Updating the command arguments to eliminate some information about - // parts that are not being drawn. - c->x += (c->left << 5); - c->y += c->top; - - // Columns are identified by ids 0 to s->columns - 1, and the end has - // id s->columns. So the end is drawn if this last column is included. - has_end = (c->right == s->columns); - // Computing number of grid columns to draw. - grid_columns = c->right - c->left + 1 - has_end; - - bopti_grid((const uint32_t *)grid, grid_columns, s->height, c); - - if(has_end) - { - end = layer + ((s->columns * s->height) << 2); - c->x += (grid_columns << 5); - - if((c->x & 31) + s->end_size <= 32) - bopti_end_nover(end, s->end_bytes, c); - else - bopti_end(end, s->end_bytes, c); - } -} - -/* - getStructure() - Determines the image size (large images have a somehow different - structure), the data pointer and a few dimensions inside the image. -*/ -void getStructure(image_t *img, structure_t *s) -{ - int column_count, end, end_bytes, layer; - - // Large images. - if(!img->width && !img->height) - { - s->width = img->data[0] >> 16; - s->height = img->data[0] & 0xffff; - s->data = (uint8_t *)img->data + 4; - - column_count = (s->width + 31) >> 5; - end = 0; - end_bytes = 0; - } - else - { - s->width = img->width; - s->height = img->height; - s->data = (uint8_t *)img->data; - - /* TODO This is the same as - column_count = (img->width + 15) >> 5; - end = (img->width & 0x0f); - end_bytes = (end + 7) >> 3; - */ - - column_count = img->width >> 5; - end = img->width & 31; - end_bytes = - !end ? 0 : - end <= 8 ? 1 : - end <= 16 ? 2 : - 4; - - if(end_bytes == 4) - { - column_count++; - end = 0; - end_bytes = 0; - } - } - - // The layer size must be rounded to a multiple of 4. - layer = s->height * ((column_count << 2) + end_bytes); - layer = (layer + 3) & ~3; - - s->columns = column_count; - s->end_bytes = end_bytes; - s->end_size = end; - s->layer_size = layer; -} diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c deleted file mode 100644 index d32c09f..0000000 --- a/src/bopti/dimage.c +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include - -/* - dimage_part() - Draws a portion of an image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void dimage_part(int x, int y, image_t *img, int left, int top, int width, - int height) -{ - if(!img || img->magic != 0x01) return; - - structure_t s; - command_t command; - int actual_width, actual_height; - int format = img->format; - - if(format != format_mono && format != format_mono_alpha) return; - getStructure(img, &s); - if(width < 0) width = s.width; - if(height < 0) height = s.height; - - //--- - // Adjusting the bounding rectangle. - //--- - - // This is what happens when the bounding rectangle overflows from the - // image... - if(left < 0) left = 0; - if(top < 0) top = 0; - if(left + width > s.width) width = s.width - left; - if(top + height > s.height) height = s.height - top; - - if(x + width <= 0 || x > 127 || y + height <= 0 || y > 63 - || width <= 0 || height <= 0) - return; - - command.top = (y < 0) ? (top - y) : top; - actual_height = (y + height > 64) ? (64 - y) : height; - command.bottom = top + actual_height; - - command.left = ((x < 0) ? (left - x) : left) >> 5; - actual_width = (x + width > 128) ? (128 - x) : width; - command.right = (left + actual_width - 1) >> 5; - - command.op = bopti_op_mono; - - if(x >= 0) getMasks(x, x + actual_width - 1, command.masks); - else getMasks(0, actual_width + x - 1, command.masks); - - bopti_vram = display_getCurrentVRAM(); - - for(int i = 0; format; format >>= 1, i++) if(format & 1) - { - command.x = x - left; - command.y = y - top; - command.channel = (1 << i); - - bopti(s.data, &s, &command); - s.data += s.layer_size; - } -} - -/* - dimage() - Displays a monochrome image in the video ram. -*/ -void dimage(int x, int y, image_t *img) -{ - dimage_part(x, y, img, 0, 0, -1, -1); -} diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c deleted file mode 100644 index 84dc241..0000000 --- a/src/bopti/gimage.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include - -/* - gimage_part() - Draws a portion of a gray image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void gimage_part(int x, int y, image_t *img, int left, int top, int width, - int height) -{ - if(!img || img->magic != 0x01) return; - - structure_t s; - command_t command; - int actual_width, actual_height; - int format = img->format; - - getStructure(img, &s); - if(width < 0) width = s.width; - if(height < 0) height = s.height; - - //--- - // Adjusting the bounding rectangle. - //--- - - // This is what happens when the bounding rectangle overflows from the - // image... - if(left < 0) left = 0; - if(top < 0) top = 0; - if(left + width > s.width) width = s.width - left; - if(top + height > s.height) height = s.height - top; - - if(x + width <= 0 || x > 127 || y + height <= 0 || y > 63 - || width <= 0 || height <= 0) - return; - - // command.bottom is excluded... - command.top = (y < 0) ? (top - y) : top; - actual_height = (y + height > 64) ? (64 - y) : height; - command.bottom = top + actual_height; - - // ... but command.right is included. Great. - command.left = ((x < 0) ? (left - x) : left) >> 5; - actual_width = (x + width > 128) ? (128 - x) : width; - command.right = (left + actual_width - 1) >> 5; - - command.op = bopti_op_gray; - - if(x >= 0) getMasks(x, x + actual_width - 1, command.masks); - else getMasks(0, actual_width + x - 1, command.masks); - - bopti_v1 = gray_lightVRAM(); - bopti_v2 = gray_darkVRAM(); - - for(int i = 0; format; format >>= 1, i++) if(format & 1) - { - command.x = x - left; - command.y = y - top; - command.channel = (1 << i); - - bopti(s.data, &s, &command); - s.data += s.layer_size; - } -} - -/* - gimage() - Displays a gray image in the video ram. -*/ -void gimage(int x, int y, image_t *img) -{ - gimage_part(x, y, img, 0, 0, -1, -1); -} diff --git a/src/clock/clock.c b/src/clock/clock.c deleted file mode 100644 index 8084c4d..0000000 --- a/src/clock/clock.c +++ /dev/null @@ -1,272 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -static clock_config_t conf = { - .FLL = -1, .PLL = -1, - .Bphi_div1 = -1, .Iphi_div1 = -1, .Pphi_div1 = -1, - .CKIO_f = -1, - .Bphi_f = -1, .Iphi_f = -1, .Pphi_f = -1 -}; - -/* - clock_setting() - Returns the P_phi / 4 timer setting that will last for the given time. - Several units can be used. Be aware that the result is approximate, and - very high frequencies or very short delays will yield important errors. -*/ -uint32_t clock_setting(int duration, clock_unit_t unit) -{ - if(conf.Pphi_f <= 0) return 0xffffffff; - uint64_t f = conf.Pphi_f >> 2; - uint64_t result; - - switch(unit) - { - case clock_us: - result = (duration * f) / 1000000; - break; - case clock_ms: - result = (duration * f) / 1000; - break; - case clock_s: - result = (duration * f); - break; - - case clock_Hz: - result = f / duration; - break; - case clock_kHz: - result = f / (duration * 1000); - break; - case clock_MHz: - result = f / (duration * 1000000); - break; - - default: - return -1; - } - - return (result > 0xffffffff) ? (0xffffffff) : (result); -} - -/* - clock_config() - Returns a copy of the clock configuration. -*/ -clock_config_t clock_config(void) -{ - return conf; -} - -/* - sleep() - Sleeps until an interrupt is accepted. -*/ -void sleep(void) -{ - __asm__( - "sleep" - ); -} - -static void sleep_callback(void *arg) -{ - int *flag = arg; - *flag = 1; -} - -/* - sleep_ms() - Sleeps for the given number of milliseconds using a virtual timer. -*/ -void sleep_ms(int ms_delay) -{ - volatile int sleep_done = 0; - - timer_t *timer = timer_create(ms_delay, 1); - timer_attach(timer, sleep_callback, (void *)&sleep_done); - timer_start(timer); - - while(!sleep_done) sleep(); -} - -/* - sleep_us() - Sleeps for the given number of microseconds using the hardware timer - timer_user. -*/ -void sleep_us(int us_delay) -{ - volatile int sleep_done = 0; - const uint32_t constant = clock_setting(us_delay, clock_us); - - timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1); - timer_attach(timer, sleep_callback, (void *)&sleep_done); - timer_start(timer); - - while(!sleep_done) sleep(); -} - - - -//--- -// Clock frequency measurements -- Public API. -//--- - -// Indicates whether the measurements are finished. -static volatile int clock_measure_done = 0; -// Once again SH7705 and SH7305 need different methods... -static timer_t *htimer_7705 = NULL; -static int cb_id_7705 = -1; -static void clock_measure_7705(void); -static void clock_compute_7305(void); - -/* - clock_measure() - Begins the frequency measurements. The measurements will end - automatically. While doing measurements, do not use the RTC interrupt - or the user timer. - Call clock_measure_end() to wait until the measurements are finished. - It is possible to execute code during the measurements, so that less - time is spent. -*/ -void clock_measure(void) -{ - // On SH7705 we cannot have the value of CKIO simply, so we measure - // P_phi using a timer/RTC combination, and we deduce CKIO. - if(isSH3()) - { - htimer_7705 = htimer_setup(timer_user, 0xffffffff, timer_Po_4, - 1); - cb_id_7705 = rtc_cb_add(rtc_freq_256Hz, clock_measure_7705, 0); - } - - // On SH7305, assuming clock mode 3, we can compute the clock - // frequencies because we know that RTC_CLK oscillates at 32768 Hz. - else - { - clock_compute_7305(); - clock_measure_done = 1; - } -} - -/* - clock_measure_end() - Waits until the measurements are finished. This may be immediate. -*/ -void clock_measure_end(void) -{ - while(!clock_measure_done) sleep(); -} - -//--- -// Clock frequency measurements -- SH7305. -//--- - -/* - clock_compute_7305() - Computes the clock frequencies according to the CPG parameters. -*/ -static void clock_compute_7305(void) -{ - volatile unsigned int *FRQCRA = (void *)0xa4150000; - volatile unsigned int *PLLCR = (void *)0xa4150024; - volatile unsigned int *FLLFRQ = (void *)0xa4150050; - - // Surely the documentation of SH7724 does not meet the specification - // of SH7305 for the PLL setting, because the register accepts other - // values than the ones specified for SH7724. The relation given by - // Sentaro21 (thanks again!) yields good results. - int pll = (*FRQCRA >> 24) & 0x3f; // Raw setting - pll = pll + 1; // Resulting multiplier - conf.PLL = pll; - - // This one is simpler. The FLL ratio is actually the setting value. - int fll = *FLLFRQ & 0x7ff; // Raw setting = multiplier - if(*FLLFRQ & (1 << 14)) fll >>= 1; // Halve-output flag - conf.FLL = fll; - - // The divider1 ratios are NOT those of SH7724. The relation between - // the values below and the divider ratios is given by Sentaro21 - // (thanks to him!) and satisfies ratio = 1 / (2 ** (setting + 1)). - int div1_bphi = (*FRQCRA >> 8) & 0xf; - int div1_iphi = (*FRQCRA >> 20) & 0xf; - int div1_pphi = (*FRQCRA ) & 0xf; - - conf.Bphi_div1 = 1 << (div1_bphi + 1); - conf.Iphi_div1 = 1 << (div1_iphi + 1); - conf.Pphi_div1 = 1 << (div1_pphi + 1); - - // Computing the frequency of the signal, which is input to divider 1. - int base = 32768; - if(*PLLCR & (1 << 12)) base *= fll; - if(*PLLCR & (1 << 14)) base *= pll; - - conf.RTCCLK_f = 32768; - conf.Bphi_f = base >> (div1_bphi + 1); - conf.Iphi_f = base >> (div1_iphi + 1); - conf.Pphi_f = base >> (div1_pphi + 1); -} - -//--- -// Clock frequency measurements -- SH7705. -//--- - -/* - clock_measure_7705_finalize() - Given the number of P_phi / 4 timer ticks elapsed between two RTC - 256 Hz interrupts, determines the clock configuration. -*/ -static void clock_measure_7705_finalize(uint32_t elapsed) -{ - volatile unsigned int *FRQCR = (void *)0xffffff80; - - conf.Pphi_f = elapsed * 4 * 256; - if(conf.Pphi_f <= 0) return; - - conf.PLL1 = ((*FRQCR >> 8) & 0x03) + 1; - conf.PLL2 = -1; - - conf.Bphi_div1 = 0; - conf.Iphi_div1 = ((*FRQCR >> 4) & 0x03) + 1; - conf.Pphi_div1 = ((*FRQCR ) & 0x03) + 1; - - conf.CKIO_f = (conf.Pphi_f * conf.Pphi_div1) / conf.PLL1; - conf.Bphi_f = conf.CKIO_f; - conf.Iphi_f = (conf.CKIO_f * conf.PLL1) / conf.Iphi_div1; -} - -/* - clock_measure_7705_callback() - Starts measurements. Measurements will end automatically. Do not use - RTC interrupt or the user timer will doing measurements. - Call clock_measure_end() when you need to use those, to ensure - measurements are finished. -*/ -static void clock_measure_7705_callback(void) -{ - timer_stop(htimer_7705); - rtc_cb_end(cb_id_7705); - - uint32_t elapsed = 0xffffffff - TMU.timers[timer_user]->TCNT; - clock_measure_7705_finalize(elapsed); - clock_measure_done = 1; -} - -/* - clock_measure_7705() - Programs the clock measurements. We need to have the user timer and the - RTC synchronized for this operation, so we wait for an RTC interrupt - and we prepare the timer beforehand to avoid losing processor time in - configuring the registers. -*/ -static void clock_measure_7705(void) -{ - timer_start(htimer_7705); - rtc_cb_edit(cb_id_7705, rtc_freq_256Hz, clock_measure_7705_callback); -} diff --git a/src/core/exceptions.c b/src/core/exceptions.c deleted file mode 100644 index ad2ac4a..0000000 --- a/src/core/exceptions.c +++ /dev/null @@ -1,172 +0,0 @@ -#include -#include -#include - -//--- -// On-screen error messages -//--- - -#define print(x, y, str) dtext(6 * (x) - 5, 8 * (y) - 7, str) - -static void print_hex(int x, int y, uint32_t n, int digits) -{ - #define hexdigit(n) ((n) + '0' + 39 * ((n) > 9)) - - char str[9]; - if(digits >= 8) digits = 8; - str[digits] = 0; - - while(digits) - { - str[digits - 1] = hexdigit(n & 0xf); - n >>= 4; - digits--; - } - - print(x, y, str); - - #undef hexdigit -} - -static void show_error(const char *name, uint32_t *access_mode, uint32_t *tea, - uint32_t *opcode) -{ - uint32_t *vram = display_getCurrentVRAM(); - uint32_t spc, ssr; - __asm__("stc spc, %0" : "=r"(spc)); - __asm__("stc ssr, %0" : "=r"(ssr)); - - dclear(); - text_configure(NULL, color_black); - - print(3, 1, "EXCEPTION RAISED!"); - for(int i = 0; i < 36; i++) vram[i] = ~vram[i]; - print(1 + ((21 - strlen(name)) >> 1), 2, name); - - print(4, 4, "PC"); - print_hex(7, 4, spc, 8); - print(4, 5, "SR"); - print_hex(7, 5, ssr, 8); - - int y = 6; - if(access_mode) - { - print(2, y, "Type"); - print(7, y++, *access_mode == 1 ? "Code/Data read" : - "Data write"); - } - if(tea) - { - print(3, y, "TEA"); - print_hex(7, y++, *tea, 8); - } - if(opcode) - { - print(2, y, "Code"); - print_hex(7, y++, *opcode, 4); - } - - dupdate(); - while(1); -} - -#undef print - - - -//--- -// Exception handlers -//--- - -#pragma GCC diagnostic ignored "-Wunused-parameter" - -/* - exch_address_error() - CPU address error, e.g. alignment issues. -*/ -void exch_address_error(uint32_t pc, uint32_t tea, uint32_t access) -{ - show_error("Address error", &access, &tea, NULL); -} - -/* - exch_tlb_protection_violation() - You don't have the right to access this address. -*/ -void exch_tlb_protection_violation(uint32_t pc, uint32_t tea, uint32_t access) -{ - show_error("TLB protection", &access, &tea, NULL); -} - -/* - exch_tlb_invalid() - The translation info for this address is marked as invalid. -*/ -void exch_tlb_invalid(uint32_t pc, uint32_t tea, uint32_t access) -{ - show_error("TLB invalid", &access, &tea, NULL); -} - -/* - exch_illegal_instruction() - What's this opcode anyway? -*/ -void exch_illegal_instruction(uint32_t pc, uint32_t opcode) -{ - show_error("Illegal instruction", NULL, NULL, &opcode); -} - -/* - exch_illegal_slot() - You can't execute this in a delay slot. -*/ -void exch_illegal_slot(uint32_t pc, uint32_t opcode) -{ - show_error("Illegal slot", NULL, NULL, &opcode); -} - -/* - exch_user_break() - One of the user break conditions you requested was fulfilled. -*/ -void exch_user_break(void) -{ - show_error("User break", NULL, NULL, NULL); -} - -/* - exch_initial_page_write() - You can't write to this memory page, it's too early. -*/ -void exch_initial_page_write(void) -{ - show_error("Initial page write", NULL, NULL, NULL); -} - -/* - exch_trap() - You asked for it. -*/ -void exch_trap(uint32_t pc, uint32_t trap) -{ -} - -/* - exch_dma_address() - The DMAC is accessing badly-aligned addresses. -*/ -void exch_dma_address(void) -{ - show_error("DMA address error", NULL, NULL, NULL); -} - -/* - exch_tlb_miss() - This virtual address points nowhere. -*/ -void exch_tlb_miss(uint32_t pc, uint32_t tea, uint32_t access) -{ - show_error("TLB miss", &access, &tea, NULL); -} - -#pragma GCC diagnostic pop diff --git a/src/core/gint.c b/src/core/gint.c deleted file mode 100644 index f213dcf..0000000 --- a/src/core/gint.c +++ /dev/null @@ -1,180 +0,0 @@ -#include -#include -#include -#include -#include -#include - -// Referencing the interrupt handlers to force ld to link them in (there should -// be a better way of achieving this, but I can't seem to find it). -__attribute__((used)) static void (*_exc)(void) = &gint_exc; -__attribute__((used)) static void (*_tlb)(void) = &gint_tlb; -__attribute__((used)) static void (*_int)(void) = &gint_int; - - - -//--- -// Exception and interrupt handlers -//--- - -gint_interrupt_handler_t gint_handlers[] = { - - //--- - // Resets and non-events - //--- - - { NULL, NULL, 0, { 0x00, 0x00, 0x00 } }, - - //--- - // General exceptions - //--- - - // Address error. - { NULL, exch_address_error, 0, - { arg_pc, arg_tea, arg_subtype } }, - // TLB protection violation. - { NULL, exch_tlb_protection_violation, 0, - { arg_pc, arg_tea, arg_subtype } }, - // TLB Invalid (SH7705 only). - { NULL, exch_tlb_invalid, 0, - { arg_pc, arg_tea, arg_subtype } }, - // Illegal instruction. - { NULL, exch_illegal_instruction, 0, - { arg_pc, arg_opcode, 0x00 } }, - // Illegal opcode. - { NULL, exch_illegal_slot, 0, - { arg_pc, arg_opcode, 0x00 } }, - // User break. - { NULL, exch_user_break, 0, - { 0x00, 0x00, 0x00 } }, - // Initial page write. - { NULL, exch_initial_page_write, 0, - { 0x00, 0x00, 0x00 } }, - // Unconditional trap. - { NULL, exch_trap, 0, - { arg_pc, arg_trap, 0x00 } }, - // DMA address error. - { NULL, exch_dma_address, 0, - { 0x00, 0x00, 0x00 } }, - - //--- - // TLB misses - //--- - - { NULL, exch_tlb_miss, - 0, { arg_pc, arg_tea, arg_subtype } }, - - //--- - // Interrupt requests - //--- - - // Non-Maskable Interrupt. - { NULL, NULL, 0, - { 0x00, 0x00, 0x00 } }, - // Timer underflow. - { NULL, inth_timer_underflow, 12, - { arg_subtype, 0x00, 0x00 } }, - // Timer channel 2 input capture (SH7705 only) - { NULL, NULL, 0, - { arg_timer_capt, 0x00, 0x00 } }, - // Real-time clock alarm interrupt. - { NULL, NULL, 0, - { 0x00, 0x00, 0x00 } }, - // Real-time clock periodic interrupt. - { NULL, inth_rtc_periodic, 10, - { 0x00, 0x00, 0x00 } }, - // Real-time clock carry interrupt. - { NULL, NULL, 0, - { 0x00, 0x00, 0x00 } }, -}; - -/* - gint_invoke() - Invokes an interrupt or exception handler, given its type and subtype. -*/ -void gint_invoke(uint8_t type, uint8_t subtype) -{ - if(type >= exc_type_max || !gint_handlers[type].function) return; - - volatile uint32_t *tea, *tra, *tcpr_2; - uint32_t args[3]; - uint16_t *pc; - - // Getting some initial information to work with. - __asm__("stc spc, %0" : "=r"(pc)); - tea = gint_reg(register_tea); - tra = gint_reg(register_tra); - tcpr_2 = (void *)0xfffffeb8; /* SH7705 only */ - - // Building up the argument list. - for(int i = 0; i < 3; i++) switch(gint_handlers[type].args[i]) - { - case arg_none: args[i] = 0; break; - case arg_subtype: args[i] = subtype; break; - case arg_pc: args[i] = (uint32_t)pc; break; - case arg_opcode: args[i] = *pc; break; - case arg_tea: args[i] = *tea; break; - case arg_trap: args[i] = *tra >> 2; break; - case arg_timer_capt: args[i] = *tcpr_2; break; - } - - // Calling the function with the required amount of arguments. - if(gint_handlers[type].args[2]) - { - void (*handler)(uint32_t, uint32_t, uint32_t); - handler = gint_handlers[type].function; - (*handler)(args[0], args[1], args[2]); - } - else if(gint_handlers[type].args[1]) - { - void (*handler)(uint32_t, uint32_t); - handler = gint_handlers[type].function; - (*handler)(args[0], args[1]); - } - else if(gint_handlers[type].args[0]) - { - void (*handler)(uint32_t); - handler = gint_handlers[type].function; - (*handler)(args[0]); - } - else (*(void (*)(void))gint_handlers[type].function)(); -} - -/* - gint_install() - Installs an exception or interrupt handler for one of gint's recognized - interrupts. -*/ -void gint_install(gint_interrupt_type_t type, void *function) -{ - if((unsigned)type >= exc_type_max) return; - gint_handlers[type].function = function; -} - -/* - gint_uninstall() - Un-installs the exception or interrupt handler that was used for the - given interrupt, falling back to gint's default handler. -*/ -void gint_uninstall(gint_interrupt_type_t type) -{ - if((unsigned)type >= exc_type_max) return; - gint_handlers[type].function = gint_handlers[type].default_function; -} - - - -//--- -// Register access -//--- - -/* - gint_reg() - Returns the address of a common register. All common registers exist - on both platforms but they may hold different values for the same - information. -*/ -volatile void *gint_reg(gint_register_t reg) -{ - return isSH3() ? gint_reg_7705(reg) : gint_reg_7305(reg); -} diff --git a/src/core/gint_sh7305.c b/src/core/gint_sh7305.c deleted file mode 100644 index 9c54118..0000000 --- a/src/core/gint_sh7305.c +++ /dev/null @@ -1,136 +0,0 @@ -#include -#include -#include -#include -#include -#include <7305.h> -#include - -/* - gint_reg() - Returns the address of a common register. All common registers exist - on both platforms but they may hold different values for the same - information (f.i. EXPEVT may not return the same value for a given - exception on both 7705 and 7305). -*/ -volatile void *gint_reg_7305(gint_register_t reg) -{ - switch(reg) - { - case register_tea: return (void *)0xff00000c; - case register_mmucr: return (void *)0xff000010; - case register_tra: return (void *)0xff000020; - case register_expevt: return (void *)0xff000024; - case register_intevt: return (void *)0xff000028; - default: return NULL; - } -} - - - -//--- -// Register saves, setup, interrupt locks, register restoration. -//--- - -void gint_save_7305(environment_7305_t *e) -{ - // Saving interrupt priorities. - e->IPR[0] = INTX.IPRA.WORD; - e->IPR[1] = INTX.IPRB.WORD; - e->IPR[2] = INTX.IPRC.WORD; - e->IPR[3] = INTX.IPRD.WORD; - e->IPR[4] = INTX.IPRE.WORD; - e->IPR[5] = INTX.IPRF.WORD; - e->IPR[6] = INTX.IPRG.WORD; - e->IPR[7] = INTX.IPRH.WORD; - e->IPR[8] = INTX.IPRI.WORD; - e->IPR[9] = INTX.IPRJ.WORD; - e->IPR[10] = INTX.IPRK.WORD; - e->IPR[11] = INTX.IPRL.WORD; - - // Saving RTC registers. - e->RCR1 = RTC.RCR1->byte; - e->RCR2 = RTC.RCR2->byte; - - // Saving TMU registers. - e->TMU0 = *(TMU.timers[0]); - e->TMU1 = *(TMU.timers[1]); - e->TMU2 = *(TMU.timers[2]); - e->TSTR = TMU.TSTR->byte; - - // Saving port data used to access the keyboard. - e->PMCR = *((volatile uint16_t *)0xa4050116); - e->PMDR = *((volatile uint8_t *)0xa4050136); - e->PNCR = *((volatile uint16_t *)0xa4050118); - e->PNDR = *((volatile uint8_t *)0xa4050138); - e->PZCR = *((volatile uint16_t *)0xa405014c); - e->PZDR = *((volatile uint8_t *)0xa405016c); - e->key = *((volatile uint8_t *)0xa40501c6); -} - -void gint_lock_and_setup_7305(void) -{ - // Disabling everything by default to avoid freezing on non-handled - // interrupts. - INTX.IPRA.WORD = 0x0000; - INTX.IPRB.WORD = 0x0000; - INTX.IPRC.WORD = 0x0000; - INTX.IPRD.WORD = 0x0000; - INTX.IPRE.WORD = 0x0000; - INTX.IPRF.WORD = 0x0000; - INTX.IPRG.WORD = 0x0000; - INTX.IPRH.WORD = 0x0000; - INTX.IPRI.WORD = 0x0000; - INTX.IPRJ.WORD = 0x0000; - INTX.IPRK.WORD = 0x0000; - INTX.IPRL.WORD = 0x0000; - - // Allowing RTC and timer to schedule automatic tasks such as keyboard - // analysis. - INTX.IPRK._RTC = 10; - INTX.IPRA.TMU0_0 = 12; - INTX.IPRA.TMU0_1 = 12; - INTX.IPRA.TMU0_2 = 12; - - // Don't enable the RTC interrupt by default. - RTC.RCR2->byte = 0x09; -} - -void gint_restore_and_unlock_7305(environment_7305_t *e) -{ - // Restoring the interrupt priorities. - INTX.IPRA.WORD = e->IPR[0]; - INTX.IPRB.WORD = e->IPR[1]; - INTX.IPRC.WORD = e->IPR[2]; - INTX.IPRD.WORD = e->IPR[3]; - INTX.IPRE.WORD = e->IPR[4]; - INTX.IPRF.WORD = e->IPR[5]; - INTX.IPRG.WORD = e->IPR[6]; - INTX.IPRH.WORD = e->IPR[7]; - INTX.IPRI.WORD = e->IPR[8]; - INTX.IPRJ.WORD = e->IPR[9]; - INTX.IPRK.WORD = e->IPR[10]; - INTX.IPRL.WORD = e->IPR[11]; - - // Restoring RTC registers. - RTC.RCR1->byte = e->RCR1; - RTC.RCR2->byte = e->RCR2; - - // Restoring TMU registers. - *(TMU.timers[0]) = e->TMU0; - *(TMU.timers[1]) = e->TMU1; - *(TMU.timers[2]) = e->TMU2; - TMU.TSTR->byte = e->TSTR; - - // Restoring keyboard-related I/O port registers. However the backlight - // pin is in PNDR and we would like the backlight to persist when we - // leave the application, so we just keep this bit. - *((volatile uint16_t *)0xa4050116) = e->PMCR; - *((volatile uint8_t *)0xa4050136) = e->PMDR; - *((volatile uint16_t *)0xa4050118) = e->PNCR; - *((volatile uint8_t *)0xa4050138) &= 0x10; - *((volatile uint8_t *)0xa4050138) |= (e->PNDR & ~0x10); - *((volatile uint16_t *)0xa405014c) = e->PZCR; - *((volatile uint8_t *)0xa405016c) = e->PZDR; - *((volatile uint8_t *)0xa40501c6) = e->key; -} diff --git a/src/core/gint_sh7705.c b/src/core/gint_sh7705.c deleted file mode 100644 index 80df397..0000000 --- a/src/core/gint_sh7705.c +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include -#include -#include -#include -#include - -/* - gint_reg() - Returns the address of a common register. All common registers exist - on both platforms but they may hold different values for the same - information (f.i. EXPEVT may not return the same value for a given - exception on both 7705 and 7305). -*/ -volatile void *gint_reg_7705(gint_register_t reg) -{ - switch(reg) - { - case register_intevt: return (void *)0xa4000000; - case register_tra: return (void *)0xffffffd0; - case register_expevt: return (void *)0xffffffd4; - case register_mmucr: return (void *)0xfffffff4; - case register_tea: return (void *)0xfffffffc; - default: return NULL; - } -} - - - -//--- -// Register saves, setup, interrupt locks, register restoration. -//--- - -void gint_save_7705(environment_7705_t *e) -{ - mod_intc_7705_t *intc = &INTC._7705; - mod_intc_ipc_7705_t *ipc = &intc->iprs; - - // Saving the interrupt masks from registers IPRA to IPRH. - e->IPR[0] = ipc->IPRA->word; - e->IPR[1] = ipc->IPRB->word; - e->IPR[2] = ipc->IPRC->word; - e->IPR[3] = ipc->IPRD->word; - e->IPR[4] = ipc->IPRE->word; - e->IPR[5] = ipc->IPRF->word; - e->IPR[6] = ipc->IPRG->word; - e->IPR[7] = ipc->IPRH->word; - - // Saving RTC registers. - e->RCR1 = RTC.RCR1->byte; - e->RCR2 = RTC.RCR2->byte; - - // Saving TMU registers. - e->TMU0 = *(TMU.timers[0]); - e->TMU1 = *(TMU.timers[1]); - e->TMU2 = *(TMU.timers[2]); - e->TSTR = TMU.TSTR->byte; - -/* // Saving port data used to access the keyboard. - e->PACR = PFC.PACR.WORD; - e->PADR = PA.DR.BYTE; - e->PBCR = PFC.PBCR.WORD; - e->PBDR = PB.DR.BYTE; - e->PMCR = PFC.PMCR.WORD; - e->PMDR = PM.DR.BYTE; -*/ -} - -void gint_lock_and_setup_7705(void) -{ - mod_intc_7705_t *intc = &INTC._7705; - mod_intc_ipc_7705_t *ipc = &intc->iprs; - - // Disabling everything by default to avoid receiving an interrupt that - // the handler doesn't handle, which would cause the user program to - // freeze. - ipc->IPRA->word = 0x0000; - ipc->IPRB->word = 0x0000; - ipc->IPRC->word = 0x0000; - ipc->IPRD->word = 0x0000; - ipc->IPRE->word = 0x0000; - ipc->IPRF->word = 0x0000; - ipc->IPRG->word = 0x0000; - ipc->IPRH->word = 0x0000; - - // Allowing RTC and timer (which handles keyboard and a whole bunch of - // other things). - ipc->IPRA->RTC = 10; - ipc->IPRA->TMU0 = 12; - ipc->IPRA->TMU1 = 12; - ipc->IPRA->TMU2 = 12; - - // Don't enable RTC periodic signals by default. - RTC.RCR2->byte = 0x09; -} - -void gint_restore_and_unlock_7705(environment_7705_t *e) -{ - mod_intc_7705_t *intc = &INTC._7705; - mod_intc_ipc_7705_t *ipc = &intc->iprs; - - // Restoring the saved states. - ipc->IPRA->word = e->IPR[0]; - ipc->IPRB->word = e->IPR[1]; - ipc->IPRC->word = e->IPR[2]; - ipc->IPRD->word = e->IPR[3]; - ipc->IPRE->word = e->IPR[4]; - ipc->IPRF->word = e->IPR[5]; - ipc->IPRG->word = e->IPR[6]; - ipc->IPRH->word = e->IPR[7]; - - // Restoring RTC registers. - RTC.RCR1->byte = e->RCR1; - RTC.RCR2->byte = e->RCR2; - - // Restoring TMU registers. - *(TMU.timers[0]) = e->TMU0; - *(TMU.timers[1]) = e->TMU1; - *(TMU.timers[2]) = e->TMU2; - TMU.TSTR->byte = e->TSTR; - -/* // Restoring keyboard-related I/O port registers. - PFC.PACR.WORD = e->PACR; - PA.DR.BYTE = e->PADR; - PFC.PBCR.WORD = e->PBCR; - PB.DR.BYTE = e->PBDR; - PFC.PMCR.WORD = e->PMCR; - PM.DR.BYTE = e->PMDR; -*/ -} diff --git a/src/core/gint_vbr.s b/src/core/gint_vbr.s deleted file mode 100644 index 067aadb..0000000 --- a/src/core/gint_vbr.s +++ /dev/null @@ -1,66 +0,0 @@ -/* - gint_vbr - - Some of the work, especially related to setting and un-setting the vbr - address needs to be done in assembler. -*/ - - .global _gint_getvbr - .global _gint_setvbr - - - -/* - gint_getvbr() - Returns the current vbr address. -*/ -_gint_getvbr: - rts - stc vbr, r0 - - - -/* - gint_setvbr() - - This is quite the hard part when modifying the vbr. We need to set - immediately the interrupt priorities of our own handler, or restore - the ones used by the system; otherwise we may receive interrupts - requests that the new handler doesn't handle, which will cause the - whole program to freeze. - - Therefore, we must set vbr *and* change interrupt priorities while - having disabled all the interrupts in the status register. That's why - this function takes as parameter the priority management function. -*/ -_gint_setvbr: - sts.l pr, @-r15 - - /* Blocking all interrupts. */ - mov.l sr_block, r0 - stc sr, r3 - or r0, r3 - ldc r3, sr - - /* Setting the vbr address. */ - ldc r4, vbr - - /* Calling the priority manager. */ - jsr @r5 - nop - - /* Enabling interrupts again. */ - mov.l sr_block, r0 - not r0, r0 - stc sr, r3 - and r0, r3 - ldc r3, sr - - lds.l @r15+, pr - rts - nop - - .align 4 - -sr_block: - .long (1 << 28) diff --git a/src/core/init_quit.c b/src/core/init_quit.c deleted file mode 100644 index 4d92d5c..0000000 --- a/src/core/init_quit.c +++ /dev/null @@ -1,130 +0,0 @@ -#include -#include -#include -#include -#include - -gint_info_t gint; -static environment_t env; - -//--- -// Initialization routines -//--- - -/* - gint_init() - Initializes gint. Loads the interrupt handler into the memory and sets - the new vbr address. -*/ -static void setup(void) -{ - isSH3() ? gint_lock_and_setup_7705() - : gint_lock_and_setup_7305(); -} -void gint_init(mpu_t mpu) -{ - // Setting the MPU type. I don't like const-casting but this is still - // better than allowing the user to change the variable by mistake. - *((mpu_t *)&MPU_CURRENT) = mpu; - // Loading the register addresses of the current platform. - mod_init(); - - // Linker script symbols -- gint. - extern uint32_t - gint_vbr, - gint_data, - bgint, egint; - - uint32_t *ptr = &bgint; - uint32_t *src = &gint_data; - - // Loading the interrupt handler into the memory. - while(ptr < &egint) *ptr++ = *src++; - - isSH3() ? gint_save_7705(&env.env_7705) - : gint_save_7305(&env.env_7305); - - // Installing gint's default exception/interrupt handlers. - for(int i = 0; i < exc_type_max; i++) - { - gint_handlers[i].function = gint_handlers[i].default_function; - } - - // Filling the information structure for the user. - gint.vbr = gint_getvbr; - gint.system_vbr = gint_getvbr(); - gint.gint_vbr = (uint32_t)&gint_vbr; - - // Setting the VBR! - gint_setvbr(gint.gint_vbr, setup); -} - - - -//--- -// Context restoration routines -//--- - -/* - gint_quit() - Stops gint. Restores the system's configuration and vbr address. -*/ -static void stop(void) -{ - isSH3() ? gint_restore_and_unlock_7705(&env.env_7705) - : gint_restore_and_unlock_7305(&env.env_7305); -} -void gint_quit(void) -{ - // Restoring the system's VBR. - gint_setvbr(gint.system_vbr, stop); -} - - - -//--- -// System-switch technique -//--- - -#include -#include - -/* - __system_menu() - Updates the system's vram and triggers the calculator's main menu. -*/ -void __system_menu(void *vram); - -// Stores gint's configuration while the user visits the main menu. -static environment_t switch_env; - -/* - gint_switch() - Temporarily returns to the system's main menu. -*/ -static void restore(void) -{ - isSH3() ? gint_restore_and_unlock_7705(&switch_env.env_7705) - : gint_restore_and_unlock_7305(&switch_env.env_7305); -} -void gint_switch(void) -{ - isSH3() ? gint_save_7705(&switch_env.env_7705) - : gint_save_7305(&switch_env.env_7305); - - gint_setvbr(gint.system_vbr, stop); - - // When returning to the add-in from the menu, the system displays the - // add-in's video ram again, but it's often blank. We thus need to copy - // the contents of the gint video ram to the system's. - void *vram = gray_runs() - ? gray_currentVRAM() - : display_getCurrentVRAM(); - - // Use system calls to put KEY_MENU in the key buffer and invoke - // GetKeyWait(), triggering the main menu. - __system_menu(vram); - - // If the user came back, restore the gint working environment. - gint_setvbr(gint.gint_vbr, restore); -} diff --git a/src/core/interrupt_maps_7305.c b/src/core/interrupt_maps_7305.c deleted file mode 100644 index bf76fa9..0000000 --- a/src/core/interrupt_maps_7305.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include - -//--- -// Mapping hardware interrupts info to gint interrupts -- 7305 interrupts -//--- - -#define NO_EVENT (gint_interrupt_map_t){ .type = 0, .subtype = 0 } - -static gint_interrupt_map_t map0[16] = { - { exc_poweron_reset, 0 }, { exc_manual_reset, 0 }, - { exc_tlb_miss, 1 }, { exc_tlb_miss, 2 }, - { exc_initial_page_write, 0 }, { exc_tlb_protection_violation, 1 }, - { exc_tlb_protection_violation, 2 }, { exc_address_error, 1 }, -}; - -static gint_interrupt_map_t map1[16] = { - { exc_address_error, 2 }, NO_EVENT, /* FPU exception */ - { exc_tlb_multihit, 0 }, { exc_trap, 0 }, - { exc_illegal_instruction, 0 }, { exc_illegal_slot, 0 }, - { int_nmi, 0 }, { exc_user_break, 0 }, -}; - -static gint_interrupt_map_t map4[16] = { - { int_timer_underflow, 0 }, { int_timer_underflow, 1 }, - { int_timer_underflow, 2 }, NO_EVENT, - NO_EVENT, NO_EVENT, - NO_EVENT, NO_EVENT, /* SDHI1 [SDHII0] */ -}; - -static gint_interrupt_map_t mapa[16] = { - NO_EVENT, NO_EVENT, /* USB0 [USI0] */ - NO_EVENT, /* USB0 [USI1] */ NO_EVENT, - { int_rtc_alarm, 0 }, { int_rtc_periodic, 0 }, - { int_rtc_carry, 0 }, NO_EVENT, -}; - -static gint_interrupt_map_t *map[16] = { - map0, map1, NULL, NULL, map4, NULL, NULL, NULL, - NULL, NULL, mapa, NULL, NULL, NULL, NULL, NULL, -}; - -/* - gint_map() - Maps an event code to an exception/interrupt type and subtype. -*/ -gint_interrupt_map_t gint_map_7305(uint32_t event_code) -{ - int ctgy = event_code >> 8; - int event = (event_code & 0xff) >> 5; - - return map[ctgy] ? map[ctgy][event] : NO_EVENT; -} diff --git a/src/core/interrupt_maps_7705.c b/src/core/interrupt_maps_7705.c deleted file mode 100644 index d471d0a..0000000 --- a/src/core/interrupt_maps_7705.c +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -//--- -// Mapping hardware interrupt info to gint interrupts -- 7705 interrupts -//--- - -#define NO_EVENT (gint_interrupt_map_t){ .type = 0, .subtype = 0 } - -static gint_interrupt_map_t map0[16] = { - NO_EVENT, { exc_manual_reset, 0 }, - { exc_tlb_invalid, 1 }, { exc_tlb_invalid, 2 }, - { exc_initial_page_write, 0 }, { exc_tlb_protection_violation, 1 }, - { exc_tlb_protection_violation, 2 }, { exc_address_error, 1 }, -}; - -static gint_interrupt_map_t map1[16] = { - { exc_address_error, 2 }, NO_EVENT, - NO_EVENT, { exc_trap, 0 }, - { exc_illegal_instruction, 0 }, { exc_illegal_slot, 0 }, - { int_nmi, 0 }, { exc_user_break, 0 }, -}; - -static gint_interrupt_map_t map4[16] = { - { int_timer_underflow, 0 }, { int_timer_underflow, 1 }, - { int_timer_underflow, 2 }, { int_timer_input_capture, 0 }, - { int_rtc_alarm, 0 }, { int_rtc_periodic, 0 }, - { int_rtc_carry, 0 }, NO_EVENT, -}; - -static gint_interrupt_map_t *map[16] = { - map0, map1, NULL, NULL, map4, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, -}; - -/* - gint_map() - Maps an event code and a VBR offset to an exception/interrupt type and - subtype. -*/ -gint_interrupt_map_t gint_map_7705(uint32_t event_code, uint32_t offset) -{ - // Handling TLB misses (only two events). - if(offset == 0x400) switch(event_code) - { - case 0x040: - return (gint_interrupt_map_t) { - .type = exc_tlb_miss, - .subtype = 1, - }; - case 0x060: - return (gint_interrupt_map_t) { - .type = exc_tlb_miss, - .subtype = 2, - }; - default: - return NO_EVENT; - } - - // Handling general exceptions and interrupts. - int ctgy = event_code >> 8; - int event = (event_code & 0xff) >> 5; - - return map[ctgy] ? map[ctgy][event] : NO_EVENT; -} diff --git a/src/core/interrupts.c b/src/core/interrupts.c deleted file mode 100644 index eff3d94..0000000 --- a/src/core/interrupts.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include - -/* - inth_timer_underflow() - Wake up, your timer has expired! -*/ -void inth_timer_underflow(uint32_t channel) -{ - timer_interrupt(channel); -} - -/* - inth_rtc_periodic() - Don't you forget to execute the periodic tasks. -*/ -void inth_rtc_periodic(void) -{ - rtc_periodic_interrupt(); -} diff --git a/src/core/modules.c b/src/core/modules.c deleted file mode 100644 index 9e5c429..0000000 --- a/src/core/modules.c +++ /dev/null @@ -1,91 +0,0 @@ -#include -#include -#include - -#include -#include - -//--- -// Structure information -// Here resides most of the platform-dependent register configuration. -// Module structures are arranged to mask as much as possible hardware -// differences to the user. When it becomes impossible to do so at -// compile-time, gint provides functions to ensure that the user does not -// confront to the hardware directly. -//--- - -mod_tmu_t TMU; -mod_rtc_t RTC; -mod_intc_t INTC; - - - -//--- -// Initializer -//--- - -static void mod_init_7705(void) -{ - INTC._7705.iprs.IPRA = (void *)0xfffffee2; - INTC._7705.iprs.IPRB = (void *)0xfffffee4; - INTC._7705.iprs.IPRC = (void *)0xa4000016; - INTC._7705.iprs.IPRD = (void *)0xa4000018; - INTC._7705.iprs.IPRE = (void *)0xa400001a; - INTC._7705.iprs.IPRF = (void *)0xa4080000; - INTC._7705.iprs.IPRG = (void *)0xa4080002; - INTC._7705.iprs.IPRH = (void *)0xa4080004; - - INTC._7705.ICR0 = (void *)0xfffffee0; - INTC._7705.ICR1 = (void *)0xa4000010; - INTC._7705.ICR2 = (void *)0xa4000012; - INTC._7705.PINTER = (void *)0xa4000014; - INTC._7705.IRR0 = (void *)0xa4000004; - INTC._7705.IRR1 = (void *)0xa4000006; - INTC._7705.IRR2 = (void *)0xa4000008; - - TMU.timers[0] = (void *)0xfffffe94; - TMU.timers[1] = (void *)0xfffffea0; - TMU.timers[2] = (void *)0xfffffeac; - TMU.TSTR = (void *)0xfffffe92; - TMU.TCPR2 = (void *)0xfffffeb8; - - RTC.RCR1 = (void *)0xfffffedc; - RTC.RCR2 = (void *)0xfffffede; - RTC.time = (void *)0xfffffec0; -} - -static void mod_init_7305(void) -{ - INTC._7305.ICR0 = (void *)0xa4140000; - INTC._7305.ICR1 = (void *)0xa414001c; - INTC._7305.INTPRI00 = (void *)0xa4140010; - INTC._7305.iprs = (void *)0xa4080000; - INTC._7305.INTMSK00 = (void *)0xa4140044; - INTC._7305.masks = (void *)0xa4080080; - INTC._7305.INTMSKCLR00 = (void *)0xa4140064; - INTC._7305.masks_clear = (void *)0xa40800c0; - INTC._7305.INTREQ00 = (void *)0xa4140024; - INTC._7305.USERIMASK = (void *)0xa4700000; - INTC._7305.NMIFCR = (void *)0xa41400c0; - - TMU.timers[0] = (void *)0xa4490008; - TMU.timers[1] = (void *)0xa4490014; - TMU.timers[2] = (void *)0xa4490020; - TMU.TSTR = (void *)0xa4490004; - TMU.TCPR2 = NULL; - - RTC.RCR1 = (void *)0xa413fedc; - RTC.RCR2 = (void *)0xa413fede; - RTC.time = (void *)0xa413fec0; -} - -/* - mod_init() - Initializes the module data to make register access cross-platform. The - MPU needs to have been detected or this function will yield wrong - results. -*/ -void mod_init(void) -{ - isSH3() ? mod_init_7705() : mod_init_7305(); -} diff --git a/src/core/mpu.c b/src/core/mpu.c deleted file mode 100644 index 73d0a46..0000000 --- a/src/core/mpu.c +++ /dev/null @@ -1,70 +0,0 @@ -//--- -// -// gint core module: mpu -// -// Determines which kind of MPU is running the program. -// -//--- - -#include -#include - -/* Located in gint's uninitialized bss section */ -__attribute__((section(".gint.bss"))) const mpu_t MPU_CURRENT; - -/* - getMPU() - - Returns the MPU identifier of the calculator. - Thanks to SimonLothar for this function and related information. - - Processor version register (PVR) and product control register (PRR) - hold information about the MPU version, they but are only accessible on - SH-4-based MPUs. - To detect SH-3-based MPUs, this function uses port L control register - (PLCR), whose bits 8 to 15 cannot be set with SH7337 where bits 8 to 11 - can be set with SH7355. - - Additionally, the CPU core ID register (CPIDR) at 0xff000048 returns 1 - on SH7305. -*/ -mpu_t getMPU(void) -{ - // Processor version register. - volatile uint32_t *pvr = (void *)0xff000030; - // Product version register. - volatile uint32_t *prr = (void *)0xff000044; - // Port L control register. - volatile uint16_t *plcr = (void *)0xa4000114; - // Saved value for PLCR. - uint16_t saved_plcr; - uint16_t tested_plcr; - - // Looking for SH-3-based MPUs by testing PLCR writing access. - saved_plcr = *plcr; - *plcr = 0xffff; - tested_plcr = *plcr; - *plcr = saved_plcr; - - // Checking whether we are working with an SH7337 or an SH7355. - if(tested_plcr == 0x00ff) return mpu_sh7337; - if(tested_plcr == 0x0fff) return mpu_sh7355; - - // Looking for SH-4-based MPUs by testing the version registers. This - // needs the three upper bytes of the processor version register to - // match 0x10300b : - if((*pvr & 0xffffff00) != 0x10300b00) return mpu_unknown; - - // Now that we have an SH-4-based MPU, checking whether it is SH7305 or - // SH7724, just for reference. - switch(*prr & 0xfffffff0) - { - case 0x00002c00: - return mpu_sh7305; - case 0x00002200: - return mpu_sh7724; - } - - // By default, the MPU is unknown. - return mpu_unknown; -} diff --git a/src/core/syscalls.s b/src/core/syscalls.s deleted file mode 100644 index 0ebf0d2..0000000 --- a/src/core/syscalls.s +++ /dev/null @@ -1,198 +0,0 @@ -/* - gint core module: syscalls - - System calls (and the like) used by the library. The library should - rely the least possible on the system, but sometimes using the syscalls - is nothing of a nuisance. - - For instance, using the malloc()-family syscalls is a bit annoying - because it "locks" many functionalities. On the other hand, getting the - SaveDisp() buffer addresses to store data here is not a problem since - such data can very easily be relocated to static RAM. -*/ - - /* Dynamic allocation */ - .global ___malloc - .global ___free - .global ___realloc - - /* OS version */ - .global ___get_os_version - - /* Return to menu */ - .global ___system_menu - - /* Storage memory filesystem */ - .global _BFile_Remove - .global _BFile_Create - .global _BFile_Open - .global _BFile_Close - .global _BFile_Write - .global _BFile_Read - - - -/* Dynamic memory allocation */ - -___malloc: - mov.l syscall_table, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0xacd - -___free: - mov.l syscall_table, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0xacc - -___realloc: - mov.l syscall_table, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0xe6d - -/* OS version access */ - -___get_os_version: - mov.l syscall_table, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0x02ee - -/* - __system_menu() - Brings one back to the system menu by putting KEY_MENU in the system's - key buffer and calling GetKeyWait(). Of course this needs to be - executed while under system control. -*/ -___system_menu: - sts.l pr, @-r15 - add #-4, r15 - - /* Copying gint's VRAM into the system's. */ - mov.l syscall_table, r1 - mov.l .syscall_vram, r0 - jsr @r1 - mov.l r4, @r15 /* gint video ram */ - mov r0, r4 - mov.l @r15, r5 - mov.w .vram_size, r6 - mov.l .memcpy, r1 - jsr @r1 - nop - - /* Putting the matrix code in the key buffer. */ - - mov r15, r4 - mov.w .matrix_menu, r2 - mov.w r2, @r4 - mov.l syscall_table, r1 - mov.l .syscall_putcode, r0 - jsr @r1 - nop - - /* Calling GetKeyWait() to display menu. */ - - mov r15, r4 /* column pointer */ - add #-4, r15 - mov r15, r5 /* row pointer */ - add #-4, r15 - mov r15, r1 /* keycode pointer */ - - mov #2, r6 /* type of waiting */ - mov #0, r7 /* timeout period */ - mov.l r1, @-r15 - mov #0, r2 /* allow return to menu */ - mov.l r2, @-r15 - - mov.l syscall_table, r1 - mov.l .syscall_getkeywait, r0 - jsr @r1 - nop - - /* If the program counter reaches this place, it means that the user - has come back to the program. Restore stack and leave. */ - - add #20, r15 - lds.l @r15+, pr - rts - nop - - .align 4 - -.syscall_getkeywait: - .long 0x0247 -.syscall_putcode: - .long 0x024f -.syscall_vram: - .long 0x0135 -.matrix_menu: - .word 0x0308 -.vram_size: - .word 1024 -.memcpy: - .long _memcpy - -/* BFile driver */ - - -# int BFile_Remove(const uint16_t *file) -_BFile_Remove: - mov.l 1f, r0 - mov.l syscall_table, r1 - jmp @r1 - mov #0, r5 -1: .long 0x0439 - -# int BFile_Create(const uint16_t *file, enum { file = 1, folder = 5 }, -# int *size) -_BFile_Create: - mov.l 1f, r0 - mov.l syscall_table, r1 - jmp @r1 - nop -1: .long 0x0434 - -# int BFile_Open(const uint16_t *file, int mode) -_BFile_Open: - mov.l 1f, r0 - mov.l syscall_table, r1 - jmp @r1 - mov #0, r6 -1: .long 0x042c - -# int BFile_Close(int handle) -_BFile_Close: - mov.l 1f, r0 - mov.l syscall_table, r1 - jmp @r1 - nop -1: .long 0x042d - -# int BFile_Write(int handle, const void *ram_buffer, int even_size) -_BFile_Write: - mov.l 1f, r0 - mov.l syscall_table, r1 - jmp @r1 - nop -1: .long 0x0435 - -# int BFile_Read(int handle, void *ram_buffer, int size, int whence) -_BFile_Read: - mov.l 1f, r0 - mov.l syscall_table, r1 - jmp @r1 - nop -1: .long 0x0432 - - - - .align 4 - -syscall_table: - .long 0x80010070 diff --git a/src/core/vbr_space.c b/src/core/vbr_space.c deleted file mode 100644 index 2344854..0000000 --- a/src/core/vbr_space.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include -#include - -// Compiler optimization of the interrupt handlers seems to cause crashes at -// some point. Some research showed that illegal slot exceptions were raised on -// rte; lds.l @r15+, mach, even though it's a legal slot. For now I just turn -// off optimization until I figure out where the true problem is. -#pragma GCC push_options -#pragma GCC optimize("O0") - -//--- -// VBR space -//--- - -#ifdef GINT_DIAGNOSTICS -static void register_interrupt(int offset) -{ - volatile gint_diagnostics_t *dg = gint_diagnostics(); - volatile uint32_t *expevt, *intevt, *tea; - uint32_t event_code, spc, ssr; - - // Getting the addresses of some registers. - expevt = gint_reg(register_expevt); - intevt = gint_reg(register_intevt); - tea = gint_reg(register_tea); - - // Adding an entry in the event history. - event_code = (offset == 0x600) ? (*intevt) : (*expevt); - size_t len = sizeof dg->except_vect; - dg->except_vect[dg->excepts++] = event_code >> 4; - if(dg->excepts >= len) dg->excepts -= len; - - // Updating some fields in the diagnostic record. - __asm__("stc spc, %0" : "=r"(spc)); - __asm__("stc ssr, %0" : "=r"(ssr)); - dg->spc = spc; - dg->ssr = ssr; - dg->expevt = event_code; - dg->tea = *tea; -} -#endif - -/* - gint_exc() - Handles exceptions. -*/ -__attribute__((section(".gint.exc"), interrupt_handler)) -void gint_exc(void) -{ - #ifdef GINT_DIAGNOSTICS - register_interrupt(0x100); - #endif - - uint32_t event = *(uint32_t *)gint_reg(register_expevt); - gint_interrupt_map_t map; - map = isSH3() ? gint_map_7705(event, 0x100) : gint_map_7305(event); - - gint_invoke(map.type, map.subtype); -} - -/* - gint_tlb() - Handles TLB misses. -*/ -__attribute__((section(".gint.tlb"), interrupt_handler)) -void gint_tlb(void) -{ - #ifdef GINT_DIAGNOSTICS - register_interrupt(0x400); - #endif - - uint32_t event = *(uint32_t *)gint_reg(register_expevt); - gint_interrupt_map_t map; - map = isSH3() ? gint_map_7705(event, 0x400) : gint_map_7305(event); - - gint_invoke(map.type, map.subtype); -} - -/* - gint_int() - Handles interrupts. -*/ -__attribute__((section(".gint.int"), interrupt_handler)) -void gint_int(void) -{ - #ifdef GINT_DIAGNOSTICS - register_interrupt(0x600); - #endif - - uint32_t event = *(uint32_t *)gint_reg(register_intevt); - gint_interrupt_map_t map; - map = isSH3() ? gint_map_7705(event, 0x600) : gint_map_7305(event); - - gint_invoke(map.type, map.subtype); -} - -#pragma GCC pop_options diff --git a/src/ctype/ctype_classes.c b/src/ctype/ctype_classes.c deleted file mode 100644 index 96ddd24..0000000 --- a/src/ctype/ctype_classes.c +++ /dev/null @@ -1,43 +0,0 @@ -#include - -// Let's save up some space and readability (that's Cake's idea, it's a bit of -// a preprocessor trick - but a rather nice trick). -#define r4(x) (x), (x), (x), (x) -#define r5(x) r4(x), (x) -#define r6(x) r5(x), (x) -#define r7(x) r6(x), (x) -#define r9(x) r7(x), (x), (x) -#define r10(x) r6(x), r4(x) -#define r15(x) r10(x), r4(x), (x) -#define r18(x) r9(x), r9(x) -#define r20(x) r10(x), r10(x) - -enum { - cntrl = 0x01, - space = 0x02, - punct = 0x04, - print = 0x08, - upper = 0x20, - lower = 0x10, - digit = 0x40, - xdigt = 0x80, -}; - -uint8_t ctype_classes[0x80] = { - // Control characters. - r9(cntrl), r5(cntrl | space), r18(cntrl), - // Space and some punctuation. - space | print, r15(punct | print), - // Decimal digits. - r10(digit | xdigt | print), - // Some punctuation. - r7(punct | print), - // Uppercase alphabet. - r6(upper | xdigt | print), r20(upper | print), - // Other punctuation symbols. - r6(punct | print), - // Lowercase alphabet. - r6(lower | xdigt | print), r20(lower | print), - // Last punctuation characters and DEL. - r4(punct | print), cntrl, -}; diff --git a/src/ctype/ctype_functions.c b/src/ctype/ctype_functions.c deleted file mode 100644 index ad1500b..0000000 --- a/src/ctype/ctype_functions.c +++ /dev/null @@ -1,73 +0,0 @@ -//--- -// Character type functions. -// Normally this functions need not be linked because there are macros to -// optimize performance, but we still need them to get some pointers. -//--- - -// We don't want to include because it defines all the macros... -#include -extern uint8_t ctype_classes[0x80]; - -#define _inline __attribute__((always_inline)) inline - -_inline int isalnum(int c) { - return ctype_classes[c] & 0xf0; -} - -_inline int isalpha(int c) { - return ctype_classes[c] & 0x30; -} - -_inline int iscntrl(int c) { - return ctype_classes[c] & 0x01; -} - -_inline int isdigit(int c) { - return ctype_classes[c] & 0x40; -} - -_inline int isgraph(int c) { - return ctype_classes[c] & 0xf4; -} - -_inline int islower(int c) { - return ctype_classes[c] & 0x10; -} - -_inline int isprint(int c) { - return ctype_classes[c] & 0x08; -} - -_inline int ispunct(int c) { - return ctype_classes[c] & 0x04; -} - -_inline int isspace(int c) { - return ctype_classes[c] & 0x02; -} - -_inline int isupper(int c) { - return ctype_classes[c] & 0x20; -} - -_inline int isxdigit(int c) { - return ctype_classes[c] & 0x80; -} - -_inline int isascii(int c) { - return ((unsigned)c <= 0x7f); -} - -_inline int isblank(int c) { - return (c == '\t' || c == ' '); -} - -_inline int tolower(int c) { - return c | isupper(c); -} - -_inline int toupper(int c) { - return c & ~(islower(c) << 1); -} - -#undef _inline diff --git a/src/display/adjustRectangle.c b/src/display/adjustRectangle.c deleted file mode 100644 index 5d2f482..0000000 --- a/src/display/adjustRectangle.c +++ /dev/null @@ -1,29 +0,0 @@ -#include - -/* - adjustRectangle() - Adjusts the given rectangle coordinates to ensure that : - - the rectangle is entirely contained in the screen - - x1 < x2 - - y1 < y2 - which is needed when working with screen rectangles. - Returns non-zero if the rectangle is entirely outside the screen. -*/ -int adjustRectangle(int *x1, int *y1, int *x2, int *y2) -{ - #define swap(a, b) tmp = a, a = b, b = tmp - int tmp; - - if(*x2 < *x1) swap(*x1, *x2); - if(*y2 < *y1) swap(*y1, *y2); - - if(*x1 > 127 || *y1 > 63 || *x2 < 0 || *y2 < 0) return 1; - - if(*x1 < 0) *x1 = 0; - if(*y1 < 0) *y1 = 0; - if(*x2 > 127) *x2 = 127; - if(*y2 > 63) *y2 = 63; - - return 0; - #undef swap -} diff --git a/src/display/dclear.c b/src/display/dclear.c deleted file mode 100644 index 6fadb5a..0000000 --- a/src/display/dclear.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include - -/* - dclear() - Clears the whole vram, making all pixels white. -*/ -void dclear(void) -{ - // I tend to use pre-decrement more than post-increment. - uint32_t *index = vram + 256; - - while(index > vram) - { - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - *--index = 0; - } -} diff --git a/src/display/display_vram.c b/src/display/display_vram.c deleted file mode 100644 index c0c48ed..0000000 --- a/src/display/display_vram.c +++ /dev/null @@ -1,47 +0,0 @@ -#include - -/* Add-in monochrome vram in gint's uninitialized bss section */ -__attribute__((section(".gint.bss"))) static uint32_t vram_local[256]; -__attribute__((section(".gint.bss"))) uint32_t *vram; - -/* - display_getLocalVRAM() - Returns gint's local video RAM address. Gint does not use the system's - buffer because it is misaligned. This function always returns the same - address. Both the display and the gray module heavily use this buffer; - make sure you don't interfere with them if you access it. - This function does not necessarily returns the video ram that is - currently in use; call display_getCurrentVRAM() for this. -*/ -inline uint32_t *display_getLocalVRAM(void) -{ - return vram_local; -} - -/* - display_getCurrentVRAM() - Returns the current monochrome video ram buffer. This function usually - returns the parameter of the last call to display_useVRAM(), or the - local vram address (which is default when the library starts). - The return value of this function is undefined if the gray engine is - running. -*/ -inline uint32_t *display_getCurrentVRAM(void) -{ - return vram; -} - -/* - display_useVRAM() - Changes the current monochrome video ram address. The argument must be - a 4-aligned 1024-byte buffer because the library's design requires it. - This function refuses misaligned buffers but trusts that enough space - is available; failing to provide enough memory may crash the program. - This function will most likely have no effect when running the gray - engine. -*/ -inline void display_useVRAM(uint32_t *ptr) -{ - if((uintptr_t)ptr & 3) return; - vram = ptr; -} diff --git a/src/display/dline.c b/src/display/dline.c deleted file mode 100644 index 95d57b6..0000000 --- a/src/display/dline.c +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include - -/* - dhline() - Optimized procedure for drawing an horizontal line. Uses a rectangle - mask. -*/ -static void dhline(size_t x1, size_t x2, int y, color_t operator) -{ - uint32_t masks[4]; - uint32_t *video = vram + (y << 2) + 4; - - // Swapping x1 and x2 if needed. - if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2; - getMasks(x1, x2, masks); - - switch(operator) - { - case color_white: - *--video &= ~masks[3]; - *--video &= ~masks[2]; - *--video &= ~masks[1]; - *--video &= ~masks[0]; - break; - - case color_black: - *--video |= masks[3]; - *--video |= masks[2]; - *--video |= masks[1]; - *--video |= masks[0]; - break; - - case color_invert: - *--video ^= masks[3]; - *--video ^= masks[2]; - *--video ^= masks[1]; - *--video ^= masks[0]; - break; - - default: - break; - } -} - -/* - dvline() - Optimized procedure for drawing a vertical line. This one is far less - powerful than dhline() because the video ram is essentially line- - oriented. It also uses a mask. -*/ -static void dvline(int y1, int y2, int x, color_t operator) -{ - uint32_t *base = vram + (y1 << 2) + (x >> 5); - uint32_t *video = vram + (y2 << 2) + (x >> 5) + 4; - uint32_t mask = 0x80000000 >> (x & 31); - - switch(operator) - { - case color_white: - while(video > base) video -= 4, *video &= ~mask; - break; - - case color_black: - while(video > base) video -= 4, *video |= mask; - break; - - case color_invert: - while(video > base) video -= 4, *video ^= mask; - break; - - default: - break; - } -} - -#define sgn(x) ((x) < 0 ? -1 : 1) -#define abs(x) ((x) < 0 ? -(x) : (x)) - -/* - dline() - Line drawing algorithm more or less directly taken for MonochromeLib. - Thanks PierrotLL for this. Relies on dhline() and dvline() for specific - cases. -*/ -void dline(int x1, int y1, int x2, int y2, color_t operator) -{ - if(adjustRectangle(&x1, &y1, &x2, &y2)) return; - - // Possible optimizations. - if(y1 == y2) - { - dhline(x1, x2, y1, operator); - return; - } - if(x1 == x2) - { - dvline(y1, y2, x1, operator); - return; - } - - int i, x = x1, y = y1, cumul; - int dx = x2 - x1, dy = y2 - y1; - int sx = sgn(dx), sy = sgn(dy); - - dx = abs(dx), dy = abs(dy); - - dpixel(x1, y1, operator); - - if(dx >= dy) - { - cumul = dx >> 1; - for(i = 1; i < dx; i++) - { - x += sx; - cumul += dy; - if(cumul > dx) cumul -= dx, y += sy; - dpixel(x, y, operator); - } - } - else - { - cumul = dy >> 1; - for(i = 1; i < dy; i++) - { - y += sy; - cumul += dx; - if(cumul > dy) cumul -= dy, x += sx; - dpixel(x, y, operator); - } - } - - dpixel(x2, y2, operator); -} diff --git a/src/display/dpixel.c b/src/display/dpixel.c deleted file mode 100644 index 8423bff..0000000 --- a/src/display/dpixel.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -/* - dpixel() - Changes a pixel's color in the video ram. The result may depend on the - current color of the pixel. -*/ -void dpixel(size_t x, size_t y, color_t operator) -{ - // Let's be honest, all this module's code *heavily* relies on the - // screen dimension in the end, so it's not that big a deal. - if(x > 127 || y > 63) return; - - uint32_t *video = vram + (y << 2) + (x >> 5); - uint32_t mask = 0x80000000 >> (x & 31); - - switch(operator) - { - case color_white: *video &= ~mask; break; - case color_black: *video |= mask; break; - case color_invert: *video ^= mask; break; - default: return; - } -} diff --git a/src/display/drect.c b/src/display/drect.c deleted file mode 100644 index 5e4d02b..0000000 --- a/src/display/drect.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include - -/* - drect() - Draws a rectangle on the screen. This function can use any color which - is not associated with the gray engine, including the reverse operator. -*/ -void drect(int x1, int y1, int x2, int y2, color_t operator) -{ - // Avoid wasting time if the requested operation is invalid here. - if(operator != color_white && operator != color_black - && operator != color_invert) return; - // Make sure the coordinates are in the right order, and that the - // requested rectangle crosses the screen. - if(adjustRectangle(&x1, &y1, &x2, &y2)) return; - - uint32_t masks[4]; - getMasks(x1, x2, masks); - - uint32_t *base = vram + (y1 << 2); - uint32_t *video = vram + (y2 << 2) + 4; - - switch(operator) - { - case color_white: - while(video > base) - { - *--video &= ~masks[3]; - *--video &= ~masks[2]; - *--video &= ~masks[1]; - *--video &= ~masks[0]; - } - break; - - case color_black: - while(video > base) - { - *--video |= masks[3]; - *--video |= masks[2]; - *--video |= masks[1]; - *--video |= masks[0]; - } - break; - - case color_invert: - while(video > base) - { - *--video ^= masks[3]; - *--video ^= masks[2]; - *--video ^= masks[1]; - *--video ^= masks[0]; - } - break; - - // Avoid some warnings. - default: return; - } -} diff --git a/src/display/dupdate.c b/src/display/dupdate.c deleted file mode 100644 index a6c92c9..0000000 --- a/src/display/dupdate.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include - -/* - dupdate() - Displays the vram on the physical screen. -*/ -void dupdate(void) -{ - screen_display((const void *)vram); -} diff --git a/src/display/font_system.bmp b/src/display/font_system.bmp deleted file mode 100644 index cee82be5f2e0b35e5c6ce8f0ab74fa0799a77ab5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32762 zcmeH~v2tul5kwbUOdP&v9V{Fo_zOHd9QX=876(#F%~6uoedo^1dyft6?FhB9PGx2H zbnNV|e*V+H|MAoN`n&eO>gS*O`HOyj^V1)+t5*N_zw^&K(Dw5m|NOU`KKuK(W&F>z zt{1po;Cg}U1+EvkUf_CxU)c-je~0oDSD7tq42bjyvXV=iCxl28&(gE(ZZSfL4RG6l zw$c69`!bZY8E65-hLi9V9AS~I11)oQtIU=)2E_D~Qzho50OH({;#qQy2`8C1JQ(Lz zc$OkhNxd`!%c+uuM*=8ej!0WNlTULJG{QMfFwC9g^e3Erl01UhhhR?C;PSZgyDVpx zXhfbO2%n~w5;id3eZci}WZGG8!PUPC;~IyYO>=}dWOArfZDQs>mt@H+HWf51VHUUhk;^RSM~ z!PI4|^1QPKA1Z&$DGyyqX5DIDY)K}ZXT4>`nud#9FL1p;y@0+kN&Ke=JFWO)eg5|s zh)v=c=5f>MbHy!)ozBX!Q&Tvl3udOBCPW!JLE7W)<}S|2)E@WjnLVR+{`8;`d57m< zotq)MR(ewBWjsq4k-Xq;+&0XJ^e>^$I@ojS*e0i!ZPNJB2wD;e(Gt0Ke)H)RVI*}O zhi7tUronodW=IQ>u}e7fi{w@P8P{a${4ZQsZO2C&e%}b{fayBy=w{Sw5CvkUjCpf$ks9j39B%F6@q)yo-r_ED7CC^&^sYPIl^Uge_ z?vR0{%ULC6!jo9%$WF_dttzu+y(4f$McplB@XCZ6`}{q-lYF{gP!+3^MXJcu*s_aF zqWD2c3z6f7x%-q!l3VYHc%GH!UF*PK;}4Y;n@m;GCv+W`B^sf8G-3zqGU?Be)agN6E4D-#1k2K4T4B^mG# z$Pu*OU7y>rbB>EJO!uzfbUR(=_LUWZ&MGnWtpB2V{7wg@R39z+=!l))Z^ zorXCDNl4C4!9xW%Vn#NFiObc8k7CB~Xp zQnN->0%xnb<(z&WZqB}g7|5~nyuT8TOU0(aW$tv#dCb#{MlfpumtgHY@TC(df%Bwz zVnoSvYLbHsl$lZj?eQuzvszVh$>h>3qD0VBWOu2T(5Gk1$s$Ww=i0_TyG00J@(Jq> zS(eUHIa#LOT|l0+rOsRJc*Uj-jhNTV7Eu${pq;zD#gUUd(|N ztL`Ko5ziJSkRvSWblLJsF4M^=76-ehNH})NcE))S;+8`0dLA-3Fk_`zM2T>L)IgjE zg2D6@beTKma^B?_Bs5#&Ri^nspPxG8lqXl7EADFpO(^M3XL`_ceAklG3xkDV5tU2l zv@OXpaOnh|A35o?t;{ODIM+h(E-3@$oZYoP&W^Jn77R{t-sxHSXUG9L??2;Q-qwS= z@#H&adiX3$I3FQ1Yotn&Ulb3;o;OdYN%0*N_u`3EqIZI)zzmW29x(Ht*2{}6(_V3U zwi1?bzLjit)~%L&2|ZXLRaW>_UN3OHz%S|r<`>pqcJEyE7-Eya`2^HoK#bR4Rp!9+ z2W8+KcaEMIfcDBiDQ04BR7oCcA^2{kRohEa9l8QQ0 z&X7r*Gx;Q(I9ofi<3vf=^NKUmE`7bg^#Z?;7pO0_%3Qvr{%py00KdnF%KPXVK4J+E zPM80n`+scJ#j;Gzr_QTRgSrzYNxd{Lp{C9Z`flYs2z*cdOs2GO+Aeb$yMq~3T4yE6 zGPRRW64|^+R_O%DQrza=ey72FlCoh)?pby`4~Y!);4*O9?w(R3S(a9rM9C%v&F8sg zW7gu>KRuP^@m(h76lp#f_ekzCXK8IH&2r}5UHF&;V7yvYI{ACR zgFDx8DsvqS{>YZE^!ka)sgNR7mia2L7r0*FdVw$Y0{)jCd;U96+NH(RJcV0XCP7P} zWk$c_kCq`VGQX;rpLddctJJ*G6ib`maGD`ZCGpf(-StVmc8^nBrTq08$0WN;E$BtK z$~xk!mj0x<`;*oSWQj)9yPe^Tq_Ea_CJ27*l^^!~qksIfhqkaSwV!1YaYGCC;^|49 Ne(7snFYujS;J=W - -/* - getMasks() - Computes the rectangle masks needed to affect pixels located between x1 - and x2 (both included). The four masks are stored in the third argument - (seen as an array). -*/ -void getMasks(size_t x1, size_t x2, uint32_t *masks) -{ - // Indexes of the first and last longs that are not empty. - size_t l1 = x1 >> 5; - size_t l2 = x2 >> 5; - size_t i = 0; - - // Setting the base masks. Those are the final values, except for the - // longs between indexes l1 and l2, that still need to be adjusted. - while(i < l1) masks[i++] = 0x00000000; - while(i <= l2) masks[i++] = 0xffffffff; - while(i < 4) masks[i++] = 0x00000000; - - // Removing the long number information in x1 and x2 (that is, the - // multiples of 32) to keep only the interesting information -- the - // number of null bits to add in l1 and l2. - x1 &= 31; - // Inverting x2 is here the same as computing 32 - x, since 32 is a - // power of 2 (positive bits at the left are removed by the mask). - x2 = ~x2 & 31; - - // Setting the first and last masks. - masks[l1] &= (0xffffffff >> x1); - masks[l2] &= (0xffffffff << x2); -} diff --git a/src/events/event_get.c b/src/events/event_get.c deleted file mode 100644 index 62b4c7f..0000000 --- a/src/events/event_get.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include - -/* - pollevent() - Returns the next event. If no one is available, returns an event whose - type is ET_None. This function always returns immediately. -*/ -event_t pollevent(void) -{ - event_t event = { - .type = event_none, - }; - if(queue_size <= 0) return event; - - event = event_queue[queue_start]; - - queue_size--; - if(queue_start == EVENTS_QUEUE_SIZE - 1) queue_start = 0; - else queue_start++; - - return event; -} - -/* - waitevent() - Returns the next event. If no one is available, waits for something to - happen. This function uses low-level sleep and should be preferred to - active waiting using loops. -*/ -event_t waitevent(void) -{ - event_t event; - - while((event = pollevent()).type == event_none) sleep(); - return event; -} diff --git a/src/events/event_push.c b/src/events/event_push.c deleted file mode 100644 index fc01a21..0000000 --- a/src/events/event_push.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include - -volatile event_t event_queue[EVENTS_QUEUE_SIZE]; -volatile int queue_start = 0; -volatile int queue_size = 0; - -/* - event_push() - Queues a user-defined event, allowing it to be retrieved by getevent() - or pollevent() later. Pushing ET_None events is not allowed. - Returns non-zero on error. -*/ -int event_push(event_t event) -{ - if(queue_size >= EVENTS_QUEUE_SIZE) return 1; - if(event.type == event_none) return 2; - - int index = queue_start + queue_size; - if(index >= EVENTS_QUEUE_SIZE) index -= EVENTS_QUEUE_SIZE; - - event_queue[index] = event; - queue_size++; - - return 0; -} diff --git a/src/gray/gclear.c b/src/gray/gclear.c deleted file mode 100644 index 60571ec..0000000 --- a/src/gray/gclear.c +++ /dev/null @@ -1,33 +0,0 @@ -#include - -/* - gclear() - Clears the video ram. -*/ -void gclear(void) -{ - uint32_t *lbase = gray_lightVRAM(); - uint32_t *v1 = lbase + 256; - uint32_t *v2 = gray_darkVRAM() + 256; - - while(v1 > lbase) - { - *--v1 = 0; - *--v1 = 0; - *--v1 = 0; - *--v1 = 0; - *--v1 = 0; - *--v1 = 0; - *--v1 = 0; - *--v1 = 0; - - *--v2 = 0; - *--v2 = 0; - *--v2 = 0; - *--v2 = 0; - *--v2 = 0; - *--v2 = 0; - *--v2 = 0; - *--v2 = 0; - } -} diff --git a/src/gray/gline.c b/src/gray/gline.c deleted file mode 100644 index 1599f58..0000000 --- a/src/gray/gline.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -/* - gline() - Draws a line in the vram. Automatically optimizes special cases. This - function does not support the light operators. -*/ -void gline(int x1, int y1, int x2, int y2, color_t operator) -{ - color_t op1, op2; - - if(operator == color_invert) op1 = op2 = color_invert; - else if(operator >= color_none) return; - else - { - op1 = 3 * (operator & 1); - op2 = 3 * (operator >> 1); - } - - display_useVRAM(gray_lightVRAM()); - dline(x1, y1, x2, y2, op1); - display_useVRAM(gray_darkVRAM()); - dline(x1, y1, x2, y2, op2); -} diff --git a/src/gray/gpixel.c b/src/gray/gpixel.c deleted file mode 100644 index eae2460..0000000 --- a/src/gray/gpixel.c +++ /dev/null @@ -1,59 +0,0 @@ -#include - -/* - gpixel() - Puts a pixel in the vram. -*/ -void gpixel(size_t x, size_t y, color_t operator) -{ - if(x > 127 || y > 63) return; - - uint32_t *light = gray_lightVRAM() + (y << 2) + (x >> 5); - uint32_t *dark = gray_darkVRAM() + (y << 2) + (x >> 5); - uint32_t mask = 0x80000000 >> (x & 31); - - switch(operator) - { - case color_white: - *light &= ~mask; - *dark &= ~mask; - break; - case color_light: - *light |= mask; - *dark &= ~mask; - break; - case color_dark: - *light &= ~mask; - *dark |= mask; - break; - case color_black: - *light |= mask; - *dark |= mask; - break; - case color_none: - return; - - case color_invert: - *light ^= mask; - *dark ^= mask; - break; - case color_lighten:; - uint32_t old_light_1 = *light; - *light &= *dark | ~mask; - *dark = (old_light_1 | ~mask) & (mask ^ *dark); - break; - case color_lighten2: - *dark &= *light | ~mask; - *light &= ~mask; - break; - case color_darken:; - uint32_t old_light_2 = *light; - *light |= *dark & mask; - *dark = (old_light_2 & mask) | (mask ^ *dark); - break; - case color_darken2: - *dark |= *light | mask; - *light |= mask; - break; - } -} diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c deleted file mode 100644 index 666b725..0000000 --- a/src/gray/gray_engine.c +++ /dev/null @@ -1,197 +0,0 @@ -//--- -// -// gint core/drawing module: gray -// -// Runs the gray engine and handles drawing for the dual-buffer system. -// -//--- - -#include -#include -#include -#include -#include - -// Additional video rams used by the gray engine. -#ifdef GINT_STATIC_GRAY -static uint32_t internals_vrams[3][256]; -#endif -static uint32_t *vrams[4]; - -// Current vram set (0 or 1), delays of the light and dark frames respectively. -static int current = 0; -static int delays[2]; - -// Is the engine currently running? -static int runs = 0; - -// Hardware timer used to run the engine. -static timer_t *gray_timer = NULL; - - - -//--- -// Interrupt control and initialization. -//--- - -/* gray_interrupt() -- switch buffers and update the screen */ -void gray_interrupt(void) -{ - htimer_reload(timer_gray, delays[(~current) & 1]); - - screen_display(vrams[current]); - current ^= 1; -} - -/* gray_init() -- setup the video ram buffers and timer delays */ -__attribute__((constructor)) static void gray_init(void) -{ - vrams[0] = display_getLocalVRAM(); -#ifdef GINT_STATIC_GRAY - vrams[1] = internal_vrams[0]; - vrams[2] = internal_vrams[1]; - vrams[3] = internal_vrams[2]; -#else - vrams[1] = NULL; - vrams[2] = NULL; - vrams[3] = NULL; -#endif - delays[0] = 912; - delays[1] = 1343; -} - -/* gray_quit() -- Free the gray engine's heap-allocated video rams */ -__attribute__((destructor)) static void gray_quit(void) -{ -#ifndef GINT_STATIC_GRAY - free(vrams[1]); - free(vrams[2]); - free(vrams[3]); -#endif -} - - - -//--- -// Engine control. -//--- - -/* - gray_start() - Starts the gray engine. The control of the screen is transferred to the - gray engine. -*/ -void gray_start(void) -{ -#ifndef GINT_STATIC_GRAY - for(int i = 0; i < 4; i++) - { - if(!vrams[i]) vrams[i] = malloc(1024); - /* Don't continue if any of the buffer is missing */ - if(!vrams[i]) return; - } -#endif - if(runs) return; - - gray_timer = htimer_setup(timer_gray, delays[0], timer_Po_64, 0); - timer_attach(gray_timer, gray_interrupt, NULL); - timer_start(gray_timer); - - current &= 1; - runs = 1; -} - -/* - gray_stop() - Stops the gray engine. The monochrome display system takes control of - the video ram. -*/ -void gray_stop(void) -{ - timer_stop(gray_timer); - runs = 0; - - /* TODO This may not be very wise considering the fact that the user - may have specified another monochrome vram address. This raises again - the idea of a parameter stack. */ - display_useVRAM(display_getLocalVRAM()); -} - -/* - gray_setDelays() - Changes the gray engine delays. -*/ -void gray_setDelays(int light, int dark) -{ - delays[0] = light; - delays[1] = dark; -} - - - -//--- -// Engine information. -//--- - -/* - gray_runs() - Returns 1 if the gray engine is running, 0 otherwise. -*/ -inline int gray_runs(void) -{ - return runs; -} - -/* - gray_lightVRAM() - Returns the module's gray vram address. -*/ -uint32_t *gray_lightVRAM(void) -{ - return vrams[~current & 2]; -} - -/* - gray_lightVRAM() - Returns the module's dark vram address. -*/ -uint32_t *gray_darkVRAM(void) -{ - return vrams[(~current & 2) | 1]; -} - -/* - gray_currentVRAM() - Returns the currently displayed video ram (if the engine runs). Used - internally, but has no interest for the user. You don't want to draw to - this vram. -*/ -uint32_t *gray_currentVRAM(void) -{ - return vrams[current ^ 1]; -} - -/* - gray_getDelays() - Returns the gray engine delays. Pointers are not set if NULL. -*/ -void gray_getDelays(int *light, int *dark) -{ - if(light) *light = delays[0]; - if(dark) *dark = delays[1]; -} - - - -//--- -// Drawing. -//--- - -/* - gupdate() - Swaps the vram buffer sets. -*/ -inline void gupdate(void) -{ - current ^= 2; -} diff --git a/src/gray/grect.c b/src/gray/grect.c deleted file mode 100644 index a123d94..0000000 --- a/src/gray/grect.c +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include - -/* - grect() - Draws a rectangle on the screen. This function can use all colors. -*/ -void grect(int x1, int y1, int x2, int y2, color_t operator) -{ - if(operator == color_none) return; - if(adjustRectangle(&x1, &y1, &x2, &y2)) return; - - uint32_t masks[4]; - getMasks(x1, x2, masks); - - uint32_t *lbase = gray_lightVRAM() + (y1 << 2); - uint32_t *lvideo = gray_lightVRAM() + (y2 << 2) + 4; - uint32_t *dvideo = gray_darkVRAM() + (y2 << 2) + 4; - - // Doing things in this order will be slower, but man, I can't stand - // writing that many lines of code for such a simple task. It will be - // terribly heavy in the binary file... - while(lvideo > lbase) for(int i = 3; i >= 0; i--) switch(operator) - { - case color_white: - *--lvideo &= ~masks[i]; - *--dvideo &= ~masks[i]; - break; - case color_light: - *--lvideo |= masks[i]; - *--dvideo &= ~masks[i]; - break; - case color_dark: - *--lvideo &= ~masks[i]; - *--dvideo |= masks[i]; - break; - case color_black: - *--lvideo |= masks[i]; - *--dvideo |= masks[i]; - break; - case color_none: return; - - case color_invert: - *--lvideo ^= masks[i]; - *--dvideo ^= masks[i]; - break; - case color_lighten:; - uint32_t light_1 = *lvideo; - dvideo--; - *--lvideo &= *dvideo | ~masks[i]; - *dvideo = (light_1 | ~masks[i]) & (masks[i] ^ *dvideo); - break; - case color_lighten2: - lvideo--; - *--dvideo &= *lvideo | ~masks[i]; - *lvideo &= ~masks[i]; - break; - case color_darken:; - uint32_t light_2 = *lvideo; - dvideo--; - *--lvideo |= *dvideo & masks[i]; - *dvideo = (light_2 & masks[i]) | (masks[i] ^ *dvideo); - break; - case color_darken2: - lvideo--; - *--dvideo |= *lvideo | masks[i]; - *lvideo |= masks[i]; - break; - } -} diff --git a/src/init/crt0.c b/src/init/crt0.c deleted file mode 100644 index 712fd3c..0000000 --- a/src/init/crt0.c +++ /dev/null @@ -1,306 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -/* We need some more functionality to generate these */ -#ifdef GINT_STARTUP_LOG - #include - #include - #include - #include - - #ifndef GINT_NO_SYSCALLS - #include - #endif -#endif - -int main(void); - -static void init(void); -static void fini(void); - -// Symbols provided by the linker script. -extern uint32_t - bdata, sdata, // Location of .data section - bbss, sbss, // Location of .bss section - bgint, egint, // Location of interrupt handler and gint data - bgbss, egbss, // Location of interrupt handler and gint data - gint_vbr, // VBR address - romdata, // ROM address of .data section contents - gint_data; // ROM address of .gint section contents -extern void - (*bctors)(void), (*ectors)(void), // Constructors - (*bdtors)(void), (*edtors)(void); // Destructors - -// This variable should be overwritten before being returned, so the default -// value doesn't matter much. -static int exit_code = EXIT_SUCCESS; -static jmp_buf env; - -// Exit handlers. -static void (*atexit_handlers[ATEXIT_MAX])(void); -static int atexit_index = 0; - -#include - -/* - start() - Program entry point. Loads the data section into the memory and invokes - main(). Also prepares the execution environment by initializing all the - modules. -*/ -__attribute__((section(".pretext.entry"))) int start(void) -{ - /* Configure the display output or we won't get anywhere - this is also - needed for the add-in even if the startup logs are disabled */ - display_useVRAM(display_getLocalVRAM()); - text_configure(NULL, color_black); - -#ifdef GINT_STARTUP_LOG - /* Start outputting information on the screen so that we can quickly - debug anything that would fail */ - uint32_t rom_size = ((uint32_t)&romdata - 0x00300000) >> 10; - uint32_t ram_size = ((uint32_t)&gint_data - (uint32_t)&romdata) - + ((uint32_t)&sbss); - uint32_t rram_size = ((uint32_t)&egbss - (uint32_t)&gint_vbr); - - dclear(); - init_stage("Startup"); - dupdate(); - - print(1, 1, init_version()); - print(1, 2, "ROM RAM RRAM"); - print(4, 3, "k c d"); - print_dec(1, 3, rom_size, 3); - print_dec(6, 3, ram_size, 4); - print_dec(11, 3, rram_size, 4); - print_dec(17, 3, &ectors - &bctors, 2); - print_dec(20, 3, &edtors - &bdtors, 2); - dupdate(); -#endif - - /* Determining the processor type */ - mpu_t mpu = getMPU(); - -#ifdef GINT_STARTUP_LOG - const char *mpu_names[] = { "SH7337", "SH7355", "SH7305", "SH7724" }; - if(mpu >= 1 && mpu <= 4) print(16, 2, mpu_names[mpu - 1]); - else print_dec(16, 2, mpu, 6); - dupdate(); -#endif - - /* Make sure the MPU is of a valid type */ - if(!mpu || mpu > 4) init_halt(); - -#ifdef GINT_STARTUP_LOG - init_stage(" MMU"); - dupdate(); -#endif - - /* Try to get the TLB filled by the system by accessing all the pages - while the system still answers TLB misses */ - mmu_pseudoTLBInit(); - -#ifdef GINT_STARTUP_LOG - /* Read the TLB and count how much memory has been mapped */ - /* TODO: Debug TLB at startup */ - print(1, 4, "MMU ROM:???k RAM:???k"); - - init_stage("Section"); - dupdate(); -#endif - - /* Copying the data section, then clearing the bss section. This - doesn't affect the previously used variables, which are stored in - another section for this purpose */ - volatile uint32_t *data = &bdata; - volatile uint32_t *data_end = (void *)&bdata + (int)&sdata; - volatile uint32_t *src = &romdata; - while(data < data_end) *data++ = *src++; - volatile uint32_t *bss = &bbss; - volatile uint32_t *bss_end = (void *)&bbss + (int)&sbss; - while(bss < bss_end) *bss++ = 0; - -#ifdef GINT_STARTUP_LOG - init_stage("Handler"); - dupdate(); -#endif - - /* Initialize gint and debug all the interrupt priorities */ - gint_init(mpu); - -#ifdef GINT_STARTUP_LOG - if(isSH3()) - { - print(1, 5, "ABCD"); - print_hex( 6, 5, INTC._7705.iprs.IPRA->word, 4); - print_hex(10, 5, INTC._7705.iprs.IPRB->word, 4); - print_hex(14, 5, INTC._7705.iprs.IPRC->word, 4); - print_hex(18, 5, INTC._7705.iprs.IPRD->word, 4); - print(1, 6, "EFGH"); - print_hex( 6, 6, INTC._7705.iprs.IPRE->word, 4); - print_hex(10, 6, INTC._7705.iprs.IPRF->word, 4); - print_hex(14, 6, INTC._7705.iprs.IPRG->word, 4); - print_hex(18, 6, INTC._7705.iprs.IPRH->word, 4); - } - else - { - print(1, 5, "ACFG"); - print_hex( 6, 5, INTC._7305.iprs->IPRA.word, 4); - print_hex(10, 5, INTC._7305.iprs->IPRC.word, 4); - print_hex(14, 5, INTC._7305.iprs->IPRF.word, 4); - print_hex(18, 5, INTC._7305.iprs->IPRG.word, 4); - print(1, 6, "HJKL"); - print_hex( 6, 6, INTC._7305.iprs->IPRH.word, 4); - print_hex(10, 6, INTC._7305.iprs->IPRJ.word, 4); - print_hex(14, 6, INTC._7305.iprs->IPRK.word, 4); - print_hex(18, 6, INTC._7305.iprs->IPRL.word, 4); - } -#endif - - /* Checking that at least the required interrupts are enabled */ - if(isSH3() - ? (!INTC._7705.iprs.IPRA->word) - : (!INTC._7305.iprs->IPRA.word || !INTC._7305.iprs->IPRK.word) - ) init_halt(); - -#ifdef GINT_STARTUP_LOG - init_stage(" Clocks"); - dupdate(); -#endif - - /* Measuring the clock speed */ - clock_measure(); - clock_measure_end(); - -#ifdef GINT_STARTUP_LOG - clock_config_t clock = clock_config(); - print(1, 7, "Clock I B P"); - print_dec( 8, 7, clock.Iphi_f / 1000000, 2); - print_dec(12, 7, clock.Bphi_f / 1000000, 2); - print_dec(16, 7, clock.Pphi_f / 1000000, 2); - - init_stage("Boot OK"); - dupdate(); - - /* Displaying some interrupt controller config */ - if(isSH3()) - { - print(1, 8, "INTC:0"); - print_hex(7, 8, INTC._7705.ICR1->word, 4); - } - else - { - print(1, 8, "INTC:"); - print_hex(6, 8, INTC._7305.ICR0->word, 4); - print_hex(10, 8, (INTC._7305.USERIMASK->lword >> 4) & 0xf, 1); - } - - #ifndef GINT_NO_SYSCALLS - /* Print the OS version */ - char version[11]; - __get_os_version(version); - version[2] = version[3]; - version[3] = version[4]; - version[4] = version[6]; - version[5] = version[7]; - version[6] = version[8]; - version[7] = version[9]; - version[8] = 0; - print(12, 8, "OS"); - print(14, 8, version); - #endif - - dupdate(); -#endif - - /* Call the global constructors */ - init(); - -#ifdef GINT_STARTUP_LOG - /* Keep this visible if a key is kept pressed */ - keyboard_interrupt(); - if(pollevent().type != event_none) getkey(); -#endif - - /* Otherwise, call main() and get the show on the road! */ - int x = setjmp(env); - if(!x) exit_code = main(); - - while(atexit_index > 0) (*atexit_handlers[--atexit_index])(); - fini(); - - gint_quit(); - - return exit_code; -} - -/* init() -- call the constructors */ -static void init(void) -{ - extern void - (*bctors)(void), - (*ectors)(void); - void (**func)(void) = &bctors; - - while(func < &ectors) (*(*func++))(); -} - -/* fini() -- call the destructors */ -static void fini(void) -{ - extern void - (*bdtors)(void), - (*edtors)(void); - void (**func)(void) = &bdtors; - - while(func < &edtors) (*(*func++))(); -} - - - -/* - abort() - Immediately ends the program without invoking the exit handlers. -*/ -void abort(void) -{ - exit_code = EXIT_FAILURE; - // Avoiding any exit handler call. - atexit_index = 0; - longjmp(env, 1); -} - -/* - exit() - Ends the program and returns the given exit code status. Calls exit - handlers before returning. - Usually exit() would ask the operating system to stop the process but - the fx-9860G executes only one program at a time and calls it as a - function. Reaching the program end point thus efficiently exits. -*/ -void exit(int status) -{ - exit_code = status; - longjmp(env, 1); -} - -/* - atexit() - Registers a function to be called at normal program termination. -*/ -int atexit(void (*function)(void)) -{ - if(atexit_index >= ATEXIT_MAX) return 1; - - atexit_handlers[atexit_index++] = function; - return 0; -} diff --git a/src/init/util.c b/src/init/util.c deleted file mode 100644 index bb2ad40..0000000 --- a/src/init/util.c +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include -#include -#include -#include - -/* init_version() -- get a version string */ -const char *init_version(void) -{ - static char data[14]; - uint32_t s = (uint32_t)&GINT_VERSION; - - /* Force the string constant to reside in ROM because we haven't - initialized the data section yet */ - memcpy(data, "gint #0.0-000", 14); - - /* Quickly get the three digits of the build number */ - qdiv_t x = qdiv_10(s & 0xffff); - qdiv_t y = qdiv_10(x.q); - - data[5] = (s & 0xff000000) >> 24; - data[6] += ((s & 0x00f00000) >> 20); - data[8] += ((s & 0x000f0000) >> 16); - data[10] += y.q; - data[11] += y.r; - data[12] += x.r; - - return data; -} - -/* init_stage() -- change the current init stage */ -void init_stage(const char *name) -{ - drect(85, 0, 127, 7, color_white); - print(15, 1, name); -} - -/* init_halt() -- halt the program */ -void init_halt(void) -{ - while(1) sleep(); -} - -/* print_dec() -- print a number in base 10 */ -void print_dec(int x, int y, int n, int digits) -{ - char str[20]; - str[digits] = 0; - - while(--digits >= 0) - { - qdiv_t d = qdiv_10(n); - str[digits] = '0' + d.r; - n = d.q; - } - - print(x, y, str); -} - -/* print_hex() -- print a number in base 16 */ -void print_hex(int x, int y, uint32_t n, int digits) -{ - char str[20]; - str[digits] = 0; - - while(--digits >= 0) - { - str[digits] = (n & 0xf) + '0' + 39 * ((n & 0xf) > 9); - n >>= 4; - } - - print(x, y, str); -} diff --git a/src/keyboard/getPressedKeys.c b/src/keyboard/getPressedKeys.c deleted file mode 100644 index c3b21f1..0000000 --- a/src/keyboard/getPressedKeys.c +++ /dev/null @@ -1,53 +0,0 @@ -#include - -/* - getPressedKeys() - Find 'count' pressed keys in the keyboard state and fills the 'keys' - array. Returns the number of actually-pressed keys found. -*/ -int getPressedKeys(volatile uint8_t *keyboard_state, int *keys, int count) -{ - int row = 1, column; - int found = 0, actually_pressed; - int state; - if(count <= 0) return 0; - - if(keyboard_state[0] & 1) - { - keys[found++] = KEY_AC_ON; - count--; - } - - while(count && row <= 9) - { - while(row <= 9 && !keyboard_state[row]) row++; - if(row > 9) break; - - state = keyboard_state[row]; - column = 0; - - while(count && column < 8) - { - if(state & 1) - { - keys[found++] = (column << 4) | row; - count--; - } - - state >>= 1; - column++; - } - - row++; - } - - actually_pressed = found; - - while(count) - { - keys[found++] = KEY_NONE; - count--; - } - - return actually_pressed; -} diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c deleted file mode 100644 index 9b1f38b..0000000 --- a/src/keyboard/getkey.c +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include -#include -#include -#include - -/* - getkey() - Blocking function with auto-repeat and SHIFT modifying functionalities. - Roughly reproduces the behavior of the system's GetKey(). -*/ -int getkey(void) -{ - return getkey_opt(getkey_default, 0); -} - -/* - getkey_opt() - Enhances getkey() with more general functionalities. - If delay_ms is non-zero and positive, getkey_opt() will return - KEY_NOEVENT if no event occurs before delay_ms. -*/ -static void getkey_opt_wait(int *delay_ms) -{ - while(!interrupt_flag) sleep(); - interrupt_flag = 0; - - if(*delay_ms > 0) - { - (*delay_ms) -= vtimer->ms_delay; - if(*delay_ms < 0) *delay_ms = 0; - } -} -int getkey_opt(getkey_option_t options, int delay_ms) -{ - event_t event; - int modifier = 0, key; - key_type_t type; - - if(delay_ms <= 0) delay_ms = -1; - - while(delay_ms != 0) switch((event = pollevent()).type) - { - case event_none: - getkey_opt_wait(&delay_ms); - break; - - case event_key_press: - key = event.key.code; - - if(options & getkey_manage_backlight && key == KEY_OPTN - && (modifier & MOD_SHIFT)) - { - screen_toggleBacklight(); - modifier &= ~MOD_SHIFT; - continue; - } - if((options & getkey_task_switch) && key == KEY_MENU - && !modifier) - { - continue; - } - if(options & getkey_shift_modifier && key == KEY_SHIFT) - { - modifier ^= MOD_SHIFT; - continue; - } - if(options & getkey_alpha_modifier && key == KEY_ALPHA) - { - modifier ^= MOD_ALPHA; - continue; - } - - last_key = key; - last_repeats = 0; - last_time = 0; - return key | modifier; - - case event_key_repeat: - key = event.key.code; - if(key != last_key) continue; - - // Checking that this type of repetition is allowed. - type = key_type(key); - if(!(options & (type << 4))) break; - - last_time += vtimer->ms_delay; - int cmp = last_repeats ? repeat_next : repeat_first; - - if(last_time >= cmp) - { - last_repeats++; - last_time -= cmp; - return last_key; - } - break; - - case event_key_release: -#ifndef GINT_NO_SYSCALLS - if((options & getkey_task_switch) && event.key.code == KEY_MENU - && !modifier) - { - gint_switch(); - continue; - } -#endif - - if((int)event.key.code != last_key) break; - last_key = KEY_NONE; - last_repeats = 0; - last_time = 0; - break; - - default: - break; - } - - last_key = KEY_NONE; - last_repeats = 0; - last_time = 0; - return KEY_NONE; -} diff --git a/src/keyboard/key_char.c b/src/keyboard/key_char.c deleted file mode 100644 index 1e1ccd1..0000000 --- a/src/keyboard/key_char.c +++ /dev/null @@ -1,36 +0,0 @@ -#include - -/* - key_char() - Returns the ASCII character associated with a character key, and 0 for - other keys. This function expects a matrix code and not a key_id() - code, and heeds for the ALPHA modifier. -*/ -int key_char(int matrix_key) -{ - char flat[] = { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, '2', '^', 0x0, 0x0, 0x0, - 'X', 'L', 'l', 's', 'c', 't', - 0x0, 0x0, '(', ')', ',', '>', - '7', '8', '9', 0x0, 0x0, 0x0, - '4', '5', '6', '*', '/', 0x0, - '1', '2', '3', '+', '-', 0x0, - '0', '.', 'e', '_', 0x0, 0x0 - }; - char alpha[] = { - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 'r', 'o', 0x0, 0x0, 0x0, - 'A', 'B', 'C', 'D', 'E', 'F', - 'G', 'H', 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 0x0, 0x0, 0x0, - 'P', 'Q', 'R', 'S', 'T', 0x0, - 'U', 'V', 'W', 'X', 'Y', 0x0, - 'Z', ' ', '"', 0x0, 0x0, 0x0 - }; - - int id = key_id(matrix_key); - return (matrix_key & MOD_ALPHA) ? alpha[id] : flat[id]; -} diff --git a/src/keyboard/key_id.c b/src/keyboard/key_id.c deleted file mode 100644 index 152208c..0000000 --- a/src/keyboard/key_id.c +++ /dev/null @@ -1,19 +0,0 @@ -#include - -/* - keyid() - Transforms a key identifier and returns a key code that is more - convenient for array subscript that the original matrix codes. Please - note that there are a few holes in the numbering. - This function ignores modifiers and returns -1 on error. -*/ -int key_id(int matrix_key) -{ - if(matrix_key < 0) return -1; - matrix_key &= MOD_CLEAR; - - int row = 9 - (matrix_key & 0x0f); - int column = 6 - ((matrix_key & 0xf0) >> 4); - - return 6 * row + column; -} diff --git a/src/keyboard/key_type.c b/src/keyboard/key_type.c deleted file mode 100644 index b29e617..0000000 --- a/src/keyboard/key_type.c +++ /dev/null @@ -1,24 +0,0 @@ -#include - -/* - key_type() - Returns a key's type. This functions ignores modifiers and expects - matrix codes as argument, not key_id() codes. -*/ -key_type_t key_type(int matrix_key) -{ - matrix_key &= MOD_CLEAR; - - // Arrow keys. - if(matrix_key == KEY_UP || matrix_key == KEY_RIGHT - || matrix_key == KEY_DOWN || matrix_key == KEY_LEFT) - { - return key_type_arrow; - } - - // Function keys (F1 .. F6) are ton only keys of the ninth row. - if((matrix_key & 0x0f) == 0x09) return key_type_function; - - // Then character keys are those that have an associated character. =p - return key_char(matrix_key) ? key_type_character : key_type_control; -} diff --git a/src/keyboard/keyboard_core.c b/src/keyboard/keyboard_core.c deleted file mode 100644 index 530b5fc..0000000 --- a/src/keyboard/keyboard_core.c +++ /dev/null @@ -1,178 +0,0 @@ -#include -#include -#include -#include -#include -#include - -//--- -// Keyboard variables. -//--- - -// Current keyboard state: each element represents a row, and each bit a key, -// but not all bits are used. -volatile uint8_t keyboard_state[10] = { 0 }; -// Interrupt flag: set when an interrupt occurs, and cleared by functions such -// as getkey() that watch it (because such functions will wake up every time an -// interrupt occurs, and they need to know whether it was the keyboard). -volatile int interrupt_flag = 0; - -// Delays, in milliseconds, before repeating keys (the first repetition may -// have a different delay). -int repeat_first = 625, repeat_next = 125; - -// Which key was pressed last, how many times it has been repeated, and how -// much time (in milliseconds) has elapsed since it was last repeated. -int last_key = KEY_NONE, last_repeats = 0, last_time = 0; - -// Virtual timer object. -timer_t *vtimer = NULL; - - - -//--- -// Interrupt management. -//--- - -/* - keyboard_interrupt() - Callback function for keyboard update; called by the timer manager when - the keyboard's virtual timer fires. Ideally this function should be - interrupt-driven but the PINT interrupts seem to fire continuously, - which is annoying. -*/ -void keyboard_interrupt(void) -{ - // This procedure is critical for speed. If there's anything that could - // be optimized, please tell me. - - // New keyboard state. - uint8_t state[10] = { 0 }; - isSH3() ? keyboard_updateState_7705(state) - : keyboard_updateState_7305(state); - - // Event types associated with each old/new state pair (see later). - uint8_t events[4] = { - event_none, - event_key_release, - event_key_press, - event_key_repeat, - }; - - // AC/ON has not a matrix code that corresponds to its location in the - // buffer, so we need to check it independently. - if(keyboard_state[0] | state[0]) - { - int kind = (state[0] << 1) | keyboard_state[0]; - - if(kind) - { - event_t event = { - .type = events[kind], - .key.code = KEY_AC_ON, - .key.id = key_id(KEY_AC_ON), - .key.character = key_char(KEY_AC_ON) - }; - event_push(event); - } - } - keyboard_state[0] = state[0]; - - for(int row = 1; row <= 9; row++) - { - // Shifting the new state will allow us to make up a 2-bit - // value for each key more easily, improving efficiency. - uint16_t old = keyboard_state[row]; - uint16_t new = state[row] << 1; - - if(!new && !old) continue; - keyboard_state[row] = state[row]; - - for(uint8_t code = row; code < (row | 0x80); code += 0x10) - { - int kind = (new & 2) | (old & 1); - old >>= 1; - new >>= 1; - - if(!kind) continue; - - event_t event = { - .type = events[kind], - .key.code = code, - .key.id = key_id(code), - .key.character = key_char(code) - }; - event_push(event); - } - } - - // Signal the interrupt to the higher-level functions. - interrupt_flag = 1; -} - - - -//--- -// Keyboard configuration. -//--- - -/* - keyboard_init() - Starts the keyboard timer. -*/ -__attribute__((constructor)) void keyboard_init(void) -{ - keyboard_setRepeatRate(625, 125); - - vtimer = timer_create(25, 0); - timer_attach(vtimer, keyboard_interrupt, NULL); - timer_start(vtimer); -} - -/* - keyboard_setAnalysisDelay() - Sets the keyboard analysis delay, that is, the delay (in ms) between - two keyboard analyzes. Please note that the repeat delays should be - multiples of the analysis delay for better accuracy. -*/ -void keyboard_setAnalysisDelay(int analysis_delay_ms) -{ - if(analysis_delay_ms <= 0) return; - timer_reload(vtimer, analysis_delay_ms); -} - -/* - keyboard_setRepeatRate() - Sets the default repeat rate for key events. The delay before the first - repeat may have a different value (usually longer). The unit for the - argument is ms, but the repeat events themselves may only be fired when - a keyboard analysis is performed; which means that for better accuracy, - these delays should be a multiple of the keyboard period. - For instance, delays of (625 ms, 125 ms) will imitate the system's - default setting. -*/ -void keyboard_setRepeatRate(int first, int next) -{ - repeat_first = (first > 0) ? first : 0; - repeat_next = (next > 0) ? next : 0; -} - -/* - keyboard_quit() - Stops the keyboard timer. -*/ -__attribute__((destructor)) void keyboard_quit(void) -{ - timer_destroy(vtimer); - vtimer = NULL; -} - -/* - keyboard_stateBuffer() - Returns the address of the keyboard state array. The returned address - is the handler's buffer, therefore it contains volatile data. -*/ -volatile uint8_t *keyboard_stateBuffer(void) -{ - return keyboard_state; -} diff --git a/src/keyboard/keyboard_sh7305.c b/src/keyboard/keyboard_sh7305.c deleted file mode 100644 index f1647d4..0000000 --- a/src/keyboard/keyboard_sh7305.c +++ /dev/null @@ -1,146 +0,0 @@ -//--- -// -// gint core module: keyboard analyzer -// -// Probably the most difficult hardware interaction. There is very few -// documentation on how the system actually analyzes the keyboard. While -// disassembling syscalls reveals the following procedure (which was -// already documented by SimonLothar), there is nothing about the -// detection problems of the multi-getkey system. -// -//--- - -#include -#include -#include <7305.h> - -//--- -// Keyboard management. -//--- - -/* - kdelay() - Should sleep during a few milliseconds. Well... - This delay has a great influence on key detection. Here are a few - observations of what happens with common numbers of "nop" (without - overclock). Take care of the effect of overclock! - - Column effects - May have something to do with register HIZCRB not being used here. When - many keys on the same column are pressed, other keys of the same column - may be triggered. - - (The following values do not apply to the latest tests, but the trend - remains the same.) - - Less Bad key detection. - - 8 Very few column effects. Most often, three keys may be pressed - simultaneously. However, [UP] has latencies and is globally not - detected. - - 12 Seems best. Every individual key is detected well. No column - effect observed before four keys. - - 16 Every single key is detected correctly. Pressing two keys on a - same column does not usually create a column effect. Three keys - almost always. - - More Does not improve single key detection, and increase column - effects. At 256 every single key press create a whole column - effect. -*/ -static void kdelay(void) -{ - #define r4(str) str str str str - - __asm__ - ( - r4(r4("nop\n\t")) - r4(r4("nop\n\t")) - ); - - #undef r4 -} - -/* - krow() - Reads a keyboard row. Works like krow() for SH7705; see source file - keyboard_7705.c for more details. -*/ -static uint8_t krow(int row) -{ - volatile uint16_t *injector1 = (void *)0xa4050116; - volatile uint8_t *data1 = (void *)0xa4050136; - - volatile uint16_t *injector2 = (void *)0xa4050118; - volatile uint8_t *data2 = (void *)0xa4050138; - - volatile uint16_t *detector = (void *)0xa405014c; - volatile uint8_t *keys = (void *)0xa405016c; - - volatile uint8_t *key_register = (void *)0xa40501c6; -// volatile uint16_t *hizcrb = (void *)0xa405015a; - - uint16_t smask; - uint8_t cmask; - uint8_t result = 0; - - if(row < 0 || row > 9) return 0x00; - - // Additional configuration for SH7305. - *detector = 0xaaaa; - *key_register = 0xff; - *injector1 = (*injector1 & 0xf000) | 0x0555; - *injector2 = (*injector2 & 0xff00) | 0x0055; - *data1 |= 0x3f; - *data2 |= 0x0f; - kdelay(); - - if(row < 6) - { - smask = 0x0003 << (row * 2); - cmask = ~(1 << row); - - *injector1 = ((*injector1 & 0xf000) | 0x0aaa) ^ smask; - *injector2 = (*injector2 & 0xff00) | 0x00aa; - kdelay(); - - *data1 = (*data1 & 0xc0) | (cmask & 0x3f); - *data2 |= 0x0f; - kdelay(); - } - else - { - smask = 0x0003 << ((row - 6) * 2); - cmask = ~(1 << (row - 6)); - - *injector1 = (*injector1 & 0xf000) | 0x0aaa; - *injector2 = ((*injector2 & 0xff00) | 0x00aa) ^ smask; - kdelay(); - - *data1 |= 0x3f; - *data2 = (*data2 & 0xf0) | (cmask & 0x0f); - kdelay(); - } - - // Reading the keyboard row. - result = ~*keys; - kdelay(); - - // Re-initializing the port configuration and data. - *injector1 = (*injector1 & 0xf000) | 0x0aaa; - *injector2 = (*injector2 & 0xff00) | 0x00aa; - kdelay(); - *injector1 = (*injector1 & 0xf000) | 0x0555; - *injector2 = (*injector2 & 0xff00) | 0x0055; - kdelay(); - *data1 &= 0xc0; - *data2 &= 0xf0; - - return result; -} - -/* - keyboard_updateState() - Updates the keyboard state. -*/ -void keyboard_updateState_7305(volatile uint8_t *keyboard_state) -{ - for(int i = 0; i < 10; i++) keyboard_state[i] = krow(i); -} diff --git a/src/keyboard/keyboard_sh7705.c b/src/keyboard/keyboard_sh7705.c deleted file mode 100644 index a7fb0a3..0000000 --- a/src/keyboard/keyboard_sh7705.c +++ /dev/null @@ -1,135 +0,0 @@ -//--- -// -// gint core module: keyboard analyzer -// -// Probably the most difficult hardware interaction. There is very few -// documentation on how the system actually analyzes the keyboard. While -// disassembling syscalls reveals the following procedure (which was -// already documented by SimonLothar), there is nothing about the -// detection problems of the multi-getkey system. -// -//--- - -#include -#include <7705.h> - -//--- -// Keyboard management. -//--- - -/* - kdelay() - Used to be a low-level sleep using the watchdog, as in the system. This - way seems OK at least, and it doesn't create column effects as for - SH7305. -*/ -static void kdelay(void) -{ - #define r4(str) str str str str - - __asm__ - ( - r4("nop\n\t") - ); - - #undef r4 - - /* Watchdog delay version. - const int delay = 0xf4; - - // Disabling the watchdog timer interrupt and resetting the - // configuration. Setting the delay. - INTC.IPRB.BIT._WDT = 0; - WDT.WTCSR.WRITE = 0xa500; - WDT.WTCNT.WRITE = 0x5a00 | (delay & 0xff); - - // Counting on Po/256. - WDT.WTCSR.WRITE = 0xa505; - // Starting the timer (sets again to Po/256). - WDT.WTCSR.WRITE = 0xa585; - - // Waiting until it overflows (delaying), then clearing the overflow - // flag. - while((WDT.WTCSR.READ.BYTE & 0x08) == 0); - WDT.WTCSR.WRITE = 0xa500 | (WDT.WTCSR.READ.BYTE & 0xf7); - - // Resetting the configuration and the counter. - WDT.WTCSR.WRITE = 0xa500; - WDT.WTCSR.WRITE = 0x5a00; - - // Enabling back the watchdog timer interrupt. - INTC.IPRB.BIT._WDT = GINT_INTP_WDT; - */ -} - -/* - krow() - Reads a keyboard row. -*/ -static uint8_t krow(int row) -{ - // '11' on the active row, '00' everywhere else. - uint16_t smask = 0x0003 << ((row % 8) * 2); - // '0' on the active row, '1' everywhere else. - uint8_t cmask = ~(1 << (row % 8)); - // Line results. - uint8_t result = 0; - - if(row < 0 || row > 9) return 0x00; - - // Initial configuration. - PFC.PBCR.WORD = 0xaaaa; - PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x0055; - kdelay(); - - if(row < 8) - { - // Configuring port B/M as input except for the row to check, - // which has to be an output. This sets '01' (output) on the - // active row, '10' (input) everywhere else. - PFC.PBCR.WORD = 0xaaaa ^ smask; - PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x00aa; - kdelay(); - - // Every bit set to 1 except the active row bit. - PB.DR.BYTE = cmask; - PM.DR.BYTE = (PM.DR.BYTE & 0xf0) | 0x0f; - kdelay(); - } - else - { - // The same, but deals with port M. - PFC.PBCR.WORD = 0xaaaa; - PFC.PMCR.WORD = ((PFC.PMCR.WORD & 0xff00) | 0x00aa) ^ smask; - kdelay(); - - PB.DR.BYTE = 0xff; - PM.DR.BYTE = (PM.DR.BYTE & 0xf0) | cmask; - kdelay(); - } - - // Reading the keyboard row. - result = ~PA.DR.BYTE; - kdelay(); - - // Re-initializing the port configuration and data. - PFC.PBCR.WORD = 0xaaaa; - PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x00aa; - kdelay(); - PFC.PBCR.WORD = 0x5555; - PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x0055; - kdelay(); - PB.DR.BYTE = 0x00; - PM.DR.BYTE &= 0xf0; - - return result; -} - -/* - keyboard_updateState() - Updates the keyboard state. -*/ -void keyboard_updateState_7705(volatile uint8_t *keyboard_state) -{ - for(int i = 0; i < 10; i++) keyboard_state[i] = krow(i); -} diff --git a/src/math/math_qdiv.c b/src/math/math_qdiv.c deleted file mode 100644 index be7b59c..0000000 --- a/src/math/math_qdiv.c +++ /dev/null @@ -1,23 +0,0 @@ -#include - -/* - Quickly divides by predefined integers using a 64-bit multiplication - technique. These functions should be ~10 times faster than dividing - using opeator "/". -*/ -__attribute__((always_inline)) -inline qdiv_t qdiv(uint32_t n, uint32_t divider, uint32_t magic) -{ - qdiv_t result; - - __asm__( - "dmuls.l %1, %2 \n\t" - "sts mach, %0 " - : "=r"(result.q) - : "r"(n), "r"(magic) - : "macl", "mach" - ); - - result.r = n - divider * result.q; - return result; -} diff --git a/src/mmu/pseudoTLBInit.c b/src/mmu/pseudoTLBInit.c deleted file mode 100644 index c947651..0000000 --- a/src/mmu/pseudoTLBInit.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include - -/* - mmu_pseudoTLBInit() - We need the system to fill the TLB for us, so that it knows what - happens to its contents. We don't want to edit the TLB ourselves, so - we'll just read random data from every 4k-page in the rom area to have - the system virtualize it entirely. - The index system for TLB entries ensures this process will work for - 128-k or less add-ins. On SH7705 there is a limit, which is 384-k (it - does probably not exist on SH7305) but there is no guarantee that the - system will not go wild after 128-k. -*/ -void mmu_pseudoTLBInit(void) -{ - extern uint32_t romdata; - uint32_t limit = (uint32_t)&romdata; - - uint32_t address = 0x00300000; - - // Access every page to make the system load everything in the TLB (and - // just hope it works and the add-in fits in). - while(address <= limit) - { - *((volatile uint32_t *)address); - address += 0x1000; - } -} diff --git a/src/rtc/rtc_callback.c b/src/rtc/rtc_callback.c deleted file mode 100644 index 775910d..0000000 --- a/src/rtc/rtc_callback.c +++ /dev/null @@ -1,152 +0,0 @@ -#include -#include -#include -#include - -// Array holding callback informations. -rtc_callback_t cb_array[RTC_CB_ARRAY_SIZE] = { 0 }; -// Callback identifier (unique). -static int unique_id = 1; -// Current RTC interrupt frequency. -static rtc_frequency_t rtc_freq = rtc_freq_none; -// 256-Hz tick count. This counter is stopped when no callback is registered. -static unsigned elapsed256 = 0; - -/* - rtc_cb_update() - After successful registration or deletion of a callback, updates the - RTC interrupt frequency stored in register RCR2. After update, the - interrupt frequency is high enough to handle all callbacks, but nothing - more (so that no time is wasted handling interrupts that occur too - often). -*/ -static void rtc_cb_update(void) -{ - rtc_frequency_t max = rtc_freq_none; - int n; - - for(n = 0; n < RTC_CB_ARRAY_SIZE; n++) if(cb_array[n].id) - { - if(!max || (cb_array[n].freq && cb_array[n].freq < max)) - max = cb_array[n].freq; - } - - if(rtc_freq == max) return; - rtc_freq = max; - - RTC.RCR2->byte = (rtc_freq << 4) | 0x09; -} - -/* - rtc_cb_add() - Registers a new callback for the RTC. Returns the callback id on - success (positive integer), or one of the following error codes: - -1 Array is full - -2 Invalid parameter - The number of repeats may be set to 0, in which case the callback is - called indefinitely until the user calls rtc_cb_end(). -*/ -int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats) -{ - int n = 0; - if(freq == rtc_freq_none || !function || repeats < 0) return -2; - - while(n < RTC_CB_ARRAY_SIZE && cb_array[n].id) n++; - if(n >= RTC_CB_ARRAY_SIZE) return -1; - - cb_array[n].freq = freq; - cb_array[n].callback = function; - cb_array[n].repeats = repeats; - cb_array[n].id = unique_id++; - - rtc_cb_update(); - - return cb_array[n].id; -} - -/* - rtc_cb_end() - Removes the callback with the given id (as returned by rtc_cb_add()) - from the callback array. -*/ -void rtc_cb_end(int id) -{ - int n = 0; - - while(n < RTC_CB_ARRAY_SIZE && cb_array[n].id != id) n++; - if(n >= RTC_CB_ARRAY_SIZE) return; - - cb_array[n].id = 0; - cb_array[n].freq = rtc_freq_none; - cb_array[n].callback = NULL; - cb_array[n].repeats = 0; - - rtc_cb_update(); -} - -/* - rtc_cb_edit() - Changes information related to a callback. This function returns 0 on - success, or one of the following error codes: - -1 Callback does not exist - -2 Invalid parameters - This function never removes a callback. Call rtc_cb_end() for this. -*/ -int rtc_cb_edit(int id, rtc_frequency_t new_freq, - void (*new_function)(void)) -{ - if(new_freq < 0 || new_freq > 7) return -2; - int n = 0; - - while(n < RTC_CB_ARRAY_SIZE && cb_array[n].id != id) n++; - if(n >= RTC_CB_ARRAY_SIZE) return -1; - - cb_array[n].freq = new_freq; - cb_array[n].callback = new_function; - rtc_cb_update(); - - return 0; -} - -/* - rtc_cb_interrupt() - Handles an RTC interrupt. Calls the RTC callbacks if necessary, and - updates the repeat counts. -*/ -void rtc_cb_interrupt(void) -{ - int n; - - int scales[] = { - 1, // 256 Hz - 4, // 64 Hz - 16, // 16 Hz - 64, // 4 Hz - 128, // 2 Hz - 256, // 1 Hz - 512, // 0.5 Hz - }; - // Adding to elapsed256 the number of 256-Hz ticks that correspond to - // the current interrupt frequency, and rounding the result to a - // multiple of this tick number. - elapsed256 += scales[rtc_freq - 1]; - elapsed256 &= ~(scales[rtc_freq - 1] - 1); - - for(n = 0; n < RTC_CB_ARRAY_SIZE; n++) - { - rtc_callback_t *cb = &cb_array[n]; - if(!cb->id || !cb->freq) continue; - - // Only execute callback when the number of elapsed 256-Hz - // ticks reach a multiple that correspond to the callback - // frequency. - if(elapsed256 & (scales[cb->freq - 1] - 1)) continue; - - if(cb->callback) (*cb->callback)(); - if(cb->repeats) - { - if(cb->repeats == 1) rtc_cb_end(cb->id); - else cb->repeats--; - } - } -} diff --git a/src/rtc/rtc_getTime.c b/src/rtc/rtc_getTime.c deleted file mode 100644 index dab7074..0000000 --- a/src/rtc/rtc_getTime.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include -#include - -/* - integer8(), integer16() [static] - Converts a BCD value to an integer. -*/ -static int integer8(uint8_t bcd) -{ - return (bcd & 0x0f) + 10 * (bcd >> 4); -} -static int integer16(uint16_t bcd) -{ - return (bcd & 0xf) + 10 * ((bcd >> 4) & 0xf) + 100 * ((bcd >> 8) & 0xf) - + 1000 * (bcd >> 12); -} - -/* - rtc_getTime() - Reads the current time from the RTC. There is no guarantee that the - week day will contain a correct value (use the time API for that). -*/ -void rtc_getTime(rtc_time_t *time) -{ - if(!time) return; - - do - { - RTC.RCR1->CF = 0; - - time->seconds = integer8(RTC.time->RSECCNT.byte); - time->minutes = integer8(RTC.time->RMINCNT.byte); - time->hours = integer8(RTC.time->RHRCNT .byte); - time->month_day = integer8(RTC.time->RDAYCNT.byte); - time->month = integer8(RTC.time->RMONCNT.byte); - time->year = integer16(RTC.time->RYRCNT.word); - time->week_day = RTC.time->RWKCNT; - } - while(RTC.RCR1->CF != 0); -} diff --git a/src/rtc/rtc_interrupt.c b/src/rtc/rtc_interrupt.c deleted file mode 100644 index c3e0df6..0000000 --- a/src/rtc/rtc_interrupt.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include -#include - -/* - rtc_periodic_interrupt() - Handles an RTC interrupt by calling the callback. -*/ -void rtc_periodic_interrupt(void) -{ - rtc_cb_interrupt(); - - RTC.RCR2->PEF = 0; -} diff --git a/src/rtc/rtc_setTime.c b/src/rtc/rtc_setTime.c deleted file mode 100644 index 27c8a3a..0000000 --- a/src/rtc/rtc_setTime.c +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include -#include - -/* - bcd8(), bcd16() [static] - Converts an integer to a BCD value. -*/ -static uint8_t bcd8(int integer) -{ - integer %= 100; - return ((integer / 10) << 4) | (integer % 10); -} -static uint16_t bcd16(int integer) -{ - integer %= 10000; - return (bcd8(integer / 100) << 8) | bcd8(integer % 100); -} - -/* - rtc_setTime() - Sets the time in the RTC registers. The week day is set to 0 if greater - than 6. Other fields are not checked. -*/ -void rtc_setTime(const rtc_time_t *time) -{ - if(!time) return; - int wday = (time->week_day < 7) ? (time->week_day) : (0); - - do - { - RTC.RCR1->CF = 0; - - RTC.time->RSECCNT.byte = bcd8(time->seconds); - RTC.time->RMINCNT.byte = bcd8(time->minutes); - RTC.time->RHRCNT .byte = bcd8(time->hours); - RTC.time->RDAYCNT.byte = bcd8(time->month_day); - RTC.time->RMONCNT.byte = bcd8(time->month); - RTC.time->RYRCNT .word = bcd16(time->year); - RTC.time->RWKCNT = wday; - } - while(RTC.RCR1->CF != 0); -} diff --git a/src/screen/screen_backlight.c b/src/screen/screen_backlight.c deleted file mode 100644 index e344212..0000000 --- a/src/screen/screen_backlight.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include - -/* - screen_setBacklight() - On compatible models, turns on or turns off the backlight. -*/ -void screen_setBacklight(int on) -{ - if(isSH3()) - { - volatile uint8_t *PGDR = (void *)0xa400012c; - if(on) *PGDR |= 0x80; - else *PGDR &= ~0x80; - } - else - { - volatile uint8_t *PNDR = (void *)0xa4050138; - if(on) *PNDR |= 0x10; - else *PNDR &= ~0x10; - } -} - -/* - screen_toggleBacklight() - Changes the backlight state, regardless of its current state. -*/ -void screen_toggleBacklight(void) -{ - if(isSH3()) - { - volatile uint8_t *PGDR = (void *)0xa400012c; - *PGDR ^= 0x80; - } - else - { - volatile uint8_t *PNDR = (void *)0xa4050138; - *PNDR ^= 0x10; - } -} diff --git a/src/screen/screen_display.c b/src/screen/screen_display.c deleted file mode 100644 index 0d76d0e..0000000 --- a/src/screen/screen_display.c +++ /dev/null @@ -1,55 +0,0 @@ -#include -#include - -/* - screen_display() - - Displays the given vram on the screen. Only bytes can be transferred - through the screen registers, which is unfortunate because most of the - vram-related operations use longword-base operations. -*/ -void screen_display(const void *ptr) -{ - volatile uint8_t *selector = (void *)0xb4000000; - volatile uint8_t *data = (void *)0xb4010000; - const uint8_t *vram = ptr; - - for(int line = 0; line < 64; line++) - { - // Setting the x-address register. - *selector = 4; - *data = line + 0xc0; - - // Setting Y-Up mode. - *selector = 1; - *data = 1; - - // Setting y-address. - *selector = 4; - *data = 0; - - // Selecting data write register 7 and sending a line's bytes. - // We could loop but I suspect it will be faster to iterate. - *selector = 7; - - /* TODO Manually load the video-ram line into the cache? */ - - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - *data = *vram++; - } -} diff --git a/src/setjmp/setjmp.s b/src/setjmp/setjmp.s deleted file mode 100644 index 463673e..0000000 --- a/src/setjmp/setjmp.s +++ /dev/null @@ -1,76 +0,0 @@ - - .global _setjmp - .global _longjmp - -/* - setjmp() - - This function is implemented using a trick that changes the value of - pr (so that it points just after the call to setjmp()) within the - longjmp() function, which lets the rts function perform the actual - jump. This value is obtained from the save buffer. - - setjmp() returns 0 when called to set up the jump point and a non-zero - value when invoked through a long jump. If 0 is provided as argument to - longjmp(), 1 is returned instead. -*/ -_setjmp: - /* Getting some free space. */ - add #64, r4 - - /* Saving general-purpose registers. */ - mov.l r15, @-r4 - mov.l r14, @-r4 - mov.l r13, @-r4 - mov.l r12, @-r4 - mov.l r11, @-r4 - mov.l r10, @-r4 - mov.l r9, @-r4 - mov.l r8, @-r4 - - /* Saving control and system registers. */ - stc.l sr, @-r4 - stc.l ssr, @-r4 - stc.l spc, @-r4 - stc.l gbr, @-r4 - stc.l vbr, @-r4 - sts.l mach, @-r4 - sts.l macl, @-r4 - sts.l pr, @-r4 - - /* This function always return 0. The cases where setjmp() seems to - return non-zero values, when a long jump has just been performed, are - those when the longjmp() function returns. */ - rts - mov #0, r0 - - - -_longjmp: - /* Restoring the system and control registers. Restoring pr is actually - what performs the jump -- and makes the user program think that - setjmp() has just returned. */ - lds.l @r4+, pr - lds.l @r4+, macl - lds.l @r4+, mach - ldc.l @r4+, vbr - ldc.l @r4+, gbr - ldc.l @r4+, spc - ldc.l @r4+, ssr - ldc.l @r4+, sr - - /* Restoring the general-purpose registers. */ - mov.l @r4+, r8 - mov.l @r4+, r9 - mov.l @r4+, r10 - mov.l @r4+, r11 - mov.l @r4+, r12 - mov.l @r4+, r13 - mov.l @r4+, r14 - mov.l @r4+, r15 - - /* Preventing return value from being 0 (must be at least 1). */ - tst r5, r5 - movt r0 - rts - add r5, r0 diff --git a/src/stdio/snprintf.c b/src/stdio/snprintf.c deleted file mode 100644 index 20c9c9b..0000000 --- a/src/stdio/snprintf.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#include - -/* - snprintf() - Prints to a string with a size limit. -*/ -int snprintf(char *str, size_t size, const char *format, ...) -{ - va_list args; - va_start(args, format); - - int x = __printf(size, format, args); - memcpy(str, __stdio_buffer, x + 1); - - va_end(args); - return x; -} diff --git a/src/stdio/sprintf.c b/src/stdio/sprintf.c deleted file mode 100644 index f0a018e..0000000 --- a/src/stdio/sprintf.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include -#include - -/* - sprintf() - Prints to a string. -*/ -int sprintf(char *str, const char *format, ...) -{ - va_list args; - va_start(args, format); - - int x = __printf(0, format, args); - memcpy(str, __stdio_buffer, x + 1); - - va_end(args); - return x; -} diff --git a/src/stdio/stdio_format.c b/src/stdio/stdio_format.c deleted file mode 100644 index 5a8ecfb..0000000 --- a/src/stdio/stdio_format.c +++ /dev/null @@ -1,895 +0,0 @@ -#include -#include - -#include -#include -#include - -/* - Internal buffer. - - Using a buffer *really* simplifies everything. But it also has - disadvantages, such as some memory loss and limited output size. - - So, in case we find a possibility to get rid of this buffer, we will - just have to change function character(), which is for now the only - function that directly accesses the buffer. -*/ -char __stdio_buffer[__stdio_buffer_size]; - - - -/* - Composed types for format definition. - - The Format structure handles everything in a format: data type, value, - alternative forms, alignment and character number, precision... there - are mainly a data type (altered by a size option), a value to print - and a number of characters. - - Other options are handled by The FormatFlags enumeration. See the - description of functions __printf() for further description on option - precedence and influence. -*/ - -enum FormatFlags -{ - // Alternatives forms add '0' and '0x' prefixes in octal and - // hexadecimal bases. (#) - Alternative = 1, - // Under specific conditions, zero-padding may be used instead of - // whitespace-padding. (0) - ZeroPadded = 2, - // Left alignment specifies that additional spaces should be added - // after the value instead of before. (-) - LeftAlign = 4, - // In numeric display, this forces a blank sign to be written before - // positive values. ( ) - BlankSign = 8, - // In numeric display, this forces an explicit sign in all cases. This - // option overrides BlankSign. (+) - ForceSign = 16 -}; - -struct Format -{ - // Format type, one of 'diouxXcs' ('eEfFgGaApnm' still to add). - char type; - // Format size, one of 'l', 'h', 'i' ('i' means 'hh'). - char size; - - // Number of characters printed. - int characters; - // Number of digits after the dot. - int precision; - - // Various flags. - enum FormatFlags flags; - - // Value to output. - union - { - // Signed int with formats %c, %d and %i. - signed int _int; - // Unsigned int with formats %o, %u, %x and %X. - unsigned int _unsigned; - // Double with formats %f, %F, %e, %E, %g and %G. -// double _double; - // String pointer with format %s. - const char *_pointer; - }; -}; - - - -//--- -// Static declarations. -//--- - -// Outputs a character in the buffer. Updates counters. -static void character(int c); -// Outputs n timers the given character. -static void character_n(int c, int n); -// Reads a format from the given string pointer address (must begin with '%'). -static struct Format get_format(const char **pointer); -// Computes the number of spaces and zeros to add to the bare output. -static void get_spacing(struct Format format, int *begin_spaces, int *sign, - int *zeros, int length, int *end_spaces); - -static void format_di (struct Format format); -static void format_u (struct Format format); -static void format_oxX (struct Format format); -// static void format_e (struct Format format); -static void format_c (struct Format format); -static void format_s (struct Format format); -static void format_p (struct Format format); - -#define abs(x) ((x) < 0 ? -(x) : (x)) - -// Number of characters currently written. -static size_t written = 0; -// Total number of function calls (characters theoretically written). -static size_t total = 0; -// Maximum number of characters to output. -static size_t max = 0; - - - -/* - character() - Outputs a character to the buffer. This function centralizes all the - buffer interface, so that if we come to remove it for property reasons, - we would just have to edit this function. - - Static variables written and total are both needed, because the - terminating NUL character may be written after the maximum has been - reached. - In other words, when the function ends, we need to have a variable - counting the current position in the buffer (written), and one other - containing the total number of theoretic characters (total) because - these two values may be different. - - Of course the three variables need to be initialized before using this - function. -*/ -static void character(int c) -{ - // If there is space left in the buffer. - if(written < max - 1) __stdio_buffer[written++] = c; - total++; -} - -/* - character_n() - Outputs n times the same character. Thought to be used to output spaces - or zeros without heavy loops. -*/ -static void character_n(int c, int n) -{ - int i = 0; - while(i++ < n) character(c); -} - -/* - get_format() - Reads the format from the given string pointer and returns a - corresponding Format structure. The string pointer points to is also - modified, so that is points to the first character after the format. - This function expects **pointer == '%'. -*/ -static struct Format get_format(const char **pointer) -{ - const char *convspec = "diouxXeEfFgGaAcspnm"; - struct Format format; - - const char *string = *pointer, *ptr; - int c, i; - - // Moving the string pointer after the '%' character. - string++; - - // Initializing structure. - format.type = 0; - format.size = 0; - format.flags = 0; - // Initializing digit counts. - format.characters = -1; - format.precision = -1; - - // Parsing the format string. Testing each character until a - // conversion specifier is found. - while((c = *string)) - { - // Looking for a conversion specifier. - ptr = strchr(convspec, c); - if(ptr) - { - format.type = *ptr; - break; - } - - // Looking for a left precision string (number of digits before - // the dot), introduced by a non-null digit. - if(c >= '1' && c <= '9') - { - format.characters = 0; - for(i = 0; i < 9 && isdigit(*string); string++) - { - format.characters *= 10; - format.characters += *string - '0'; - } - - // As pointer is now pointing to the next character, - // we want to try tests again from the beginning. - continue; - } - - // Looking for a right precision string (number of digits after - // the dot), introduced by a point. - if(c == '.') - { - string++; - if(!isdigit(*string)) continue; - - format.precision = 0; - for(i = 0; i < 9 && isdigit(*string); string++) - { - format.precision *= 10; - format.precision += *string - '0'; - } - - // As pointer is now pointing on the next character, - // we want to try tests again from the beginning. - continue; - } - - // Handling predefined characters. - switch(*string) - { - // Length modifiers. - case 'h': - format.size = 'h' + (format.size == 'h'); - break; - case 'l': - case 'L': - case 'z': - case 't': - format.size = *string; - break; - - // Flags. - case '#': - format.flags |= Alternative; - break; - case '0': - format.flags |= ZeroPadded; - break; - case '-': - format.flags |= LeftAlign; - break; - case ' ': - format.flags |= BlankSign; - break; - case '+': - format.flags |= ForceSign; - break; - } - - string++; - } - - // If the format hasn't ended, the type attribute is left to zero and - // the main loop will handle failure and break. Nothing has to be done - // here. - - *pointer = string + 1; - return format; -} - -/* - get_spacing() - Computes the arrangement of beginning spaces, sign, zeros, pure value - and ending spaces in formats. - This formatting follows a recurrent model which is centralized in this - function. Note that you can't have `begin_spaces` and `end_spaces` - both non-zero: at least one is null. -*/ -static void get_spacing(struct Format format, int *begin_spaces, int *sign, - int *zeros, int length, int *end_spaces) -{ - // Using a list of types involving a sign. - const char *signed_types = "dieEfFgGaA"; - int spaces; - // Digits represents pure output + zeros (don't mix up with the *real* - // displayed digits). - int digits; - int left = format.flags & LeftAlign; - - // Getting the total number of digits. - switch(format.type) - { - // In integer display, the number of digits output is specified in the - // precision. - case 'd': - case 'i': - case 'u': - digits = format.precision; - if(digits < length) digits = length; - break; - - // Binary display has prefixes such as '0' and '0x'. - case 'o': - case 'x': - case 'X': - digits = format.precision; - if(digits == -1) digits = length; - - if(format.flags & Alternative) - { - int hexa = (format.type == 'x' || format.type == 'X'); - digits += 1 + hexa; - length += 1 + hexa; - } - if(digits < length) digits = length; - break; - - // Other formats do not have additional zeros. - default: - digits = length; - break; - } - - if(sign) - { - if(strchr(signed_types, format.type)) - { - if(format.flags & BlankSign) *sign = ' '; - // Option '+' overrides option ' '. - if(format.flags & ForceSign) *sign = '+'; - // And of course negative sign overrides everything! - if(format.type == 'd' || format.type == 'i') - { - if(format._int < 0) *sign = '-'; - } -// else if(format._double < 0) *sign = '-'; - - } - else *sign = 0; - } - - // Computing the number of spaces. - spaces = format.characters - digits; - // Computing the number of zeros. - *zeros = digits - length; - - // Removing a space when a sign appears. - if(sign && *sign) spaces--; - - // Option '0' translates spaces to zeros, but only if no precision is - // specified; additionally, left alignment overrides zero-padding. - if(!left && format.precision == -1 && format.flags & ZeroPadded) - { - *zeros += spaces; - spaces = 0; - } - - // Setting the correct spaces number to the computed value, depending - // on the left alignment parameter. - *begin_spaces = left ? 0 : spaces; - *end_spaces = left ? spaces : 0; -} - -/* - __printf() - - Basic buffered formatted printing function. Fully-featured, so that - any call to a printf()-family function can be performed by __printf(). - - It always returns the number of characters of the theoretic formatted - output. The real output may be limited in size by the given size - argument when working with nprintf()-family functions, or the internal - buffer itself. - - The Flags structure isn't necessary, but it simplifies a lot format - argument handling (because flag effects depend on format type, which - is unknown when the flags are read). Also, length modifiers 'hh' is - stored as 'i' to simplify structure handling. 'll' is not supported. - Support for format '%lf' (C99) is planned. - - Generic information on options precedence and influence. - - Number of characters and precision represent different lengths - depending on the data type. - - '#' is independent. - - '+' overrides ' '. - - In integer display, '0' replaces spaces to zeros, only if no - precision (decimal digit number) is specified. - - '-' also overrides it, forcing whitespaces to be written at the end. - - Limits of function. - - Internal buffer size (there should be a loop to allow larger data). - - Precision values (format %a.b) are limited to 127. - - Unsupported features. - - ''' (single quote) (thousands grouping) - - 'I' (outputs locale's digits (glibc 2.2)) - - Length modifiers 'll' and 'q' (libc 5 and 4.4 BSD) - - This is not really a feature but incorrect characters in formats are - ignored and don't invalidate the format. -*/ -int __printf(size_t size, const char *string, va_list args) -{ - struct Format format; - - // Avoiding overflow by adjusting the size argument. - if(!size || size > __stdio_buffer_size) size = __stdio_buffer_size; - - // Initializing character() variables. - written = 0; - total = 0; - max = size; - - // Parsing the format string. At each iteration, a literal character, a - // '%%' identifier or a format is parsed. - while(*string) - { - // Literal text. - if(*string != '%') - { - character(*string++); - continue; - } - - // Literal '%'. - if(string[1] == '%') - { - string += 2; - character('%'); - continue; - } - - // Getting the format. - format = get_format(&string); - if(!format.type) break; - - /* Some debugging... - printf( - "Format found :%s%c%c, options %d, and %d.%d " - "digits\n", - format.size ? " " : "", - format.size ? format.size : ' ', - format.type, - format.flags, - format.digits, - format.mantissa - ); */ - - switch(format.type) - { - // Signed integers. - case 'd': - case 'i': - format._int = va_arg(args, signed int); - - // Reducing value depending on format size. - if(format.size == 'h') format._int &= 0x0000ffff; - if(format.size == 'i') format._int &= 0x000000ff; - - format_di(format); - break; - - // Unsigned integers. - case 'u': - format._unsigned = va_arg(args, unsigned int); - format_u(format); - break; - case 'o': - case 'x': - case 'X': - format._unsigned = va_arg(args, unsigned int); - format_oxX(format); - break; - -/* // Exponent notation. - case 'e': - case 'E': - format._double = va_arg(args, double); - format_e(format); - break; -*/ - // Characters. - case 'c': - format._int = va_arg(args, signed int) & 0xff; - format_c(format); - break; - - // Strings. - case 's': - format._pointer = va_arg(args, const char *); - format_s(format); - break; - - // Pointers. - case 'p': - format._unsigned = va_arg(args, unsigned int); - format_p(format); - break; - - // Character counter. - case 'n': - *va_arg(args, int *) = written; - break; - } - } - - // Adding a terminating NUL character. Function character() should have - // left an empty byte for that. - __stdio_buffer[written] = 0; - return total; -} - -/* - format_di() - - Subroutine itoa(). Writes the given signed integer to the internal - buffer, trough function character(). - It is used by conversion specifiers 'd' and 'i'. - Options '#' and '0' have no effect. -*/ -static void format_di(struct Format format) -{ - // In integer display, character number includes pure digits and - // additional zeros and spacing. - // The precision represents the number of digits (pure digits and - // zeros) to print. - // For example: ' 0004', pure digits: 1, digits: 4, characters: 5. - - int sign = 0; - signed int x = format._int; - // Using integers to store the number pure digits and additional spaces - // and zeros. - int bspaces, zeros, digits = 0, espaces; - // Using a multiplier to output digit in the correct order. - int multiplier = 1; - - // Returning if the argument is null with an explicit precision of - // zero, but only if there are no spaces. - if(!x && format.characters == -1 && !format.precision) return; - - - - //--- - // Computations. - //--- - - // Computing the number of digits and the multiplier. - x = abs(format._int); - if(!x) digits = 1; - else while(x) - { - digits++; - x /= 10; - if(x) multiplier *= 10; - } - - // Getting the corresponding spacing. - get_spacing(format, &bspaces, &sign, &zeros, digits, &espaces); - - - - //--- - // Output. - //--- - - character_n(' ', bspaces); - if(sign) character(sign); - character_n('0', zeros); - - x = abs(format._int); - // Writing the pure digits, except if the value is null with an - // explicit precision of zero. - if(x || format.precision) while(multiplier) - { - character((x / multiplier) % 10 + '0'); - multiplier /= 10; - } - - character_n(' ', espaces); -} - -/* - format_u() - Unsigned integers in base 10. Options ' ', '+' and '#' have no effect. -*/ -static void format_u(struct Format format) -{ - int bspaces, zeros, digits = 0, espaces; - int x = format._unsigned; - int multiplier = 1; - - // Computing number of digits. - if(!x) digits = 1; - else while(x) - { - digits++; - x /= 10; - if(x) multiplier *= 10; - } - - get_spacing(format, &bspaces, NULL, &zeros, digits, &espaces); - - //--- - // Output. - //--- - - character_n(' ', bspaces); - character_n('0', zeros); - - x = format._unsigned; - while(multiplier) - { - character('0' + (x / multiplier) % 10); - multiplier /= 10; - } - - character_n(' ', espaces); -} - -/* - format_oxX() - Unsigned integers in base 8 or 16. - Since the argument is unsigned, options ' ' and '+' have no effect. - Option '#' adds prefix '0' in octal or '0x' in hexadecimal. -*/ -static void format_oxX(struct Format format) -{ - // In unsigned display, the digit number specifies the minimal number - // of characters that should be output. If the prefix (alternative - // form) is present, it is part of this count. - // Integer part and decimal part digit numbers behave the same way as - // in signed integer display. - - // Using integers to store the number of digits, zeros and spaces. - int bspaces, zeros, digits = 0, espaces; - unsigned int x = format._unsigned; - int multiplier = 0, shift, mask; - int c, disp; - - - - //--- - // Computations. - //--- - - shift = (format.type == 'o') ? (3) : (4); - mask = (1 << shift) - 1; - disp = (format.type == 'x') ? (39) : (7); - - // Computing number of digits. - if(!x) digits = 1; - else while(x) - { - digits++; - x >>= shift; - if(x) multiplier += shift; - } - - // Getting the spacing distribution. - get_spacing(format, &bspaces, NULL, &zeros, digits, &espaces); - - - - //--- - // Output. - //--- - - character_n(' ', bspaces); - x = format._unsigned; - - // Writing the alternative form prefix. - if(format.flags & Alternative && x) - { - character('0'); - if(format.type != 'o') character(format.type); - } - - character_n('0', zeros); - - // Extracting the digits. - while(multiplier >= 0) - { - c = (x >> multiplier) & mask; - c += '0' + (c > 9) * disp; - - character(c); - multiplier -= shift; - } - - character_n(' ', espaces); -} - -/* - format_e() - Exponent notation. Option '#' has no effect. - -static void format_e(struct Format format) -{ - // In exponent display, the precision is the number of digits after the - // dot. - - // Using an integer to store the number exponent. - int exponent = 0; - // Using a double value for temporary computations, and another to - // store the format parameter. - double tmp = 1, x = format._double; - // Using spacing variables. Default length is for '0.e+00'; - int bspaces, zeros, sign, length = 6, espaces; - // Using an iterator and a multiplier. - int i, mul; - - - - //--- - // Computations. - //--- - - // Computing the exponent. For positive exponents, increasing until - // the temporary value gets greater than x. - if(x > 1) - { - // Looping until we haven't reached a greater exponent. - while(tmp < x) - { - // Incrementing the exponent. - exponent++; - // Multiplying the test value. - tmp *= 10; - } - // Removing an additional incrementation. - exponent--; - } - // For negative exponents, decreasing until it's lower. - else while(tmp > x) - { - // Decrementing the exponent. - exponent--; - // Dividing the test value. - tmp *= 0.1; - } - - // Adding a character if the exponent is greater that 100. - if(exponent >= 100) length++; - // Adding another one if it's greater than 1000. - if(exponent >= 1000) length++; - - // Adjusting the format precision, defaulting to 6. - if(format.precision == -1) format.precision = 6; - // Adding the decimal digits. - length += format.precision; - - // Getting the space repartition. - get_spacing(format, &bspaces, &sign, &zeros, length, &espaces); - - - - //--- - // Output. - //--- - - // Writing the beginning whitespaces. - character_n(' ', bspaces); - // Writing the sign if existing. - if(sign) character(sign); - // Writing the zeros. - character_n('0', zeros); - - // Initializing x. - x = abs(format._double) / tmp; - // Writing the first digit. - character(x + '0'); - character('.'); - - // Writing the decimal digits. - for(i = 0; i < format.precision; i++) - { - // Multiplying x by 10 and getting rid of the previous digit. - x = (x - (int)x) * 10; - // Writing the current digit. - character(x + '0'); - } - - // Writing the exponent letter and its sign. - character(format.type); - character(exponent < 0 ? '-' : '+'); - - // Getting a positive exponent. - exponent = abs(exponent); - - // Using a multiplier for the exponent. - if(exponent >= 1000) mul = 1000; - else if(exponent >= 100) mul = 100; - else mul = 10; - - // Writing the exponent characters. - while(mul) - { - // Writing the next character. - character((exponent / mul) % 10 + '0'); - // Dividing the multiplier. - mul *= 0.1; - } - - // Writing the ending whitespaces if left-aligned. - character_n(' ', espaces); -} -*/ - -/* - format_c() - Character output. Only handles left alignment and spacing. - Options '#', '0', ' ' and '+', as well as mantissa digit number, have - no effect. -*/ -static void format_c(struct Format format) -{ - // In character display, the digit number represents the number of - // characters written, including the argument and additional - // whitespaces. - - int spaces = format.characters - 1; - int left = format.flags & LeftAlign; - - if(!left) character_n(' ', spaces); - character(format._int & 0xff); - if(left) character_n(' ', spaces); -} - -/* - format_s() - String output. Spaces if needed. -*/ -void format_s(struct Format format) -{ - // In string display, the character number specify the minimum size of - // output (padded with whitespaces if needed) and the precision - // specify the maximum number of string characters output. - - int string = format.precision; - int spaces; - - const char *str = format._pointer; - int length, i; - int left = format.flags & LeftAlign; - - // Computing length of string and number of whitespaces. - length = strlen(str); - if(string > length || string == -1) string = length; - spaces = format.characters - string; - - if(!left) character_n(' ', spaces); - for(i = 0; i < string; i++) character(str[i]); - if(left) character_n(' ', spaces); -} - -/* - format_p() - Pointer output. Simple hexadecimal dump. Prints "(nil)" if pointer is - NULL. -*/ -void format_p(struct Format format) -{ - // Pointer display falls back to %#08x in the pointer is non-null, - // "(nil)" otherwise. - - unsigned int x = format._unsigned; - int bspaces, zeros, digits = 0, espaces; - int c, i; - - digits = x ? 10 : 5; - get_spacing(format, &bspaces, NULL, &zeros, digits, &espaces); - - character_n(' ', bspaces); - character_n('0', zeros); - - if(x) - { - character('0'); - character('x'); - for(i = 0; i < 8; i++) - { - c = x >> 28; - c += '0' + 39 * (c > 9); - character(c); - x <<= 4; - } - } - else - { - character('('); - character('n'); - character('i'); - character('l'); - character(')'); - } - - character_n(' ', espaces); -} diff --git a/src/stdio/vsnprintf.c b/src/stdio/vsnprintf.c deleted file mode 100644 index 0bfafd4..0000000 --- a/src/stdio/vsnprintf.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include - -/* - vsnprintf() - The most generic formatted printing function around there. -*/ -int vsnprintf(char *str, size_t size, const char *format, va_list args) -{ - int x = __printf(size, format, args); - memcpy(str, __stdio_buffer, x + 1); - - return x; -} diff --git a/src/stdio/vsprintf.c b/src/stdio/vsprintf.c deleted file mode 100644 index 0571fc0..0000000 --- a/src/stdio/vsprintf.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include - -/* - vsprintf() - Prints to a string from an argument list. -*/ -int vsprintf(char *str, const char *format, va_list args) -{ - int x = __printf(0, format, args); - memcpy(str, __stdio_buffer, x + 1); - - return x; -} diff --git a/src/stdlib/calloc.c b/src/stdlib/calloc.c deleted file mode 100644 index e1b9ac9..0000000 --- a/src/stdlib/calloc.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -/* - calloc() - Allocates 'n' elements of size 'size' and wipes the memory area. - Returns NULL on error. -*/ -void *calloc(size_t n, size_t size) -{ - void *ptr = malloc(n * size); - return ptr ? memset(ptr, 0, n * size) : NULL; -} diff --git a/src/stdlib/free.c b/src/stdlib/free.c deleted file mode 100644 index b967fda..0000000 --- a/src/stdlib/free.c +++ /dev/null @@ -1,23 +0,0 @@ -#include - -#pragma GCC diagnostic ignored "-Wunused-parameter" - -#ifndef GINT_NO_SYSCALLS -#include -#endif - -/* - free() - Frees a memory block allocated by malloc(), calloc() or realloc(). -*/ -void free(void *ptr) -{ - // Just to be sure. - if(!ptr) return; - - #ifndef GINT_NO_SYSCALLS - __free(ptr); - #endif -} - -#pragma GCC diagnostic pop diff --git a/src/stdlib/malloc.c b/src/stdlib/malloc.c deleted file mode 100644 index 3b7724a..0000000 --- a/src/stdlib/malloc.c +++ /dev/null @@ -1,27 +0,0 @@ -#include - -/* - malloc() - Allocates 'size' bytes and returns a pointer to a free memory area. - Returns NULL on error. -*/ - -#ifndef GINT_NO_SYSCALLS -#include - -void *malloc(size_t size) -{ - return __malloc(size); -} - -#else - -#pragma GCC diagnostic ignored "-Wunused-parameter" -void *malloc(size_t size) -{ - return NULL; -} -#pragma GCC diagnostic pop - -#endif - diff --git a/src/stdlib/realloc.c b/src/stdlib/realloc.c deleted file mode 100644 index 7c30537..0000000 --- a/src/stdlib/realloc.c +++ /dev/null @@ -1,25 +0,0 @@ -#include - -/* - realloc() - Reallocates a memory block and moves its data. -*/ - -#ifndef GINT_NO_SYSCALLS -#include - -void *realloc(void *ptr, size_t size) -{ - return __realloc(ptr, size); -} - -#else - -#pragma GCC diagnostic ignored "-Wunused-parameter" -void *realloc(void *ptr, size_t size) -{ - return NULL; -} -#pragma GCC diagnostic pop - -#endif diff --git a/src/stdlib/stdlib_abs.c b/src/stdlib/stdlib_abs.c deleted file mode 100644 index 3e4c4f8..0000000 --- a/src/stdlib/stdlib_abs.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#undef abs -#undef labs - -int abs(int x) -{ - return (x < 0) ? (-x) : x; -} - -long labs(long x) -{ - return (x < 0) ? (-x) : x; -} diff --git a/src/stdlib/stdlib_div.c b/src/stdlib/stdlib_div.c deleted file mode 100644 index 77f22a1..0000000 --- a/src/stdlib/stdlib_div.c +++ /dev/null @@ -1,29 +0,0 @@ -#include - -/* - div() - Computes the integer division of numerator by denominator. -*/ -div_t div(int numerator, int denominator) -{ - div_t result; - - result.quot = numerator / denominator; - result.rem = numerator - result.quot * denominator; - - return result; -} - -/* - ldiv() - Computes the integer division of two long integers. -*/ -ldiv_t ldiv(long numerator, long denominator) -{ - ldiv_t result; - - result.quot = numerator / denominator; - result.rem = numerator - result.quot * denominator; - - return result; -} diff --git a/src/stdlib/stdlib_rand.c b/src/stdlib/stdlib_rand.c deleted file mode 100644 index 6afcc10..0000000 --- a/src/stdlib/stdlib_rand.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -static uint32_t seed = 1; - -void srand(unsigned int new_seed) -{ - seed = new_seed; -} - -int rand(void) -{ - seed = 16807 * seed; - uint32_t top = ((uint32_t)1 << 31) - 1; - while(seed >= top) seed -= top; - /* TODO Or maybe seed = (16807 * seed) % ((1 << 31) - 1); */ -// seed = seed * 1103515245 + 12345; - return seed & 0x7fffffff; -} diff --git a/src/string/memchr.c b/src/string/memchr.c deleted file mode 100644 index e7ad19a..0000000 --- a/src/string/memchr.c +++ /dev/null @@ -1,20 +0,0 @@ -#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 deleted file mode 100644 index a862d3b..0000000 --- a/src/string/memcmp.c +++ /dev/null @@ -1,84 +0,0 @@ -#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 deleted file mode 100644 index 28a5da1..0000000 --- a/src/string/memcpy.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include - -/* - memcpy() O(n) - Copies a memory area. A smart copy is performed if possible. -*/ -void *memcpy(void *d, const void *s, size_t n) -{ - uint8_t *dest = (uint8_t *)d; - const uint8_t *src = (const uint8_t *)s; - - // A long-based copy needs the source and destination to be 4-aligned - // at the same time. - if(((intptr_t)dest & 3) == ((intptr_t)src & 3)) - { - // Getting to a long offset. - while((intptr_t)dest & 3) - { - *dest++ = *src++; - n--; - } - - // Copying groups of four bytes. - while(n >= 4) - { - *((uint32_t *)dest) = *((const uint32_t *)src); - - dest += 4, src += 4; - n -= 4; - } - - // Ending the copy. - while(n) - { - *dest++ = *src++; - n--; - } - } - - // Or we could try a word-based copy. - else if(((intptr_t)dest & 1) == ((intptr_t)src & 1)) - { - // Getting to a word offset. - if((intptr_t)dest & 1) - { - *dest++ = *src++; - n--; - } - - // Copying groups of two bytes. - while(n >= 2) - { - *((uint16_t *)dest) = *((const uint16_t *)src); - - dest += 2, src += 2; - n -= 2; - } - - // Ending the copy. - while(n) - { - *dest++ = *src++; - n--; - } - } - - // In some cases we can just perform a raw copy. - else while(n) - { - *dest++ = *src++; - n--; - } - - return d; -} diff --git a/src/string/memset.c b/src/string/memset.c deleted file mode 100644 index 4843e3a..0000000 --- a/src/string/memset.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include - -/* - 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_count) -{ - uint8_t *dest = (uint8_t *)d; - unsigned short word = (byte << 8) | byte; - unsigned int longword = (word << 16) | word; - - // When the area is small, simply copying using byte operations. The - // minimum length used for long operations must be at least 3. - if(byte_count < 8) - { - while(byte_count) - { - *dest++ = byte; - byte_count--; - } - - return d; - } - - // Reaching a long offset. - while((intptr_t)dest & 3) - { - *dest++ = byte; - byte_count--; - } - // Copying using long operations. - while(byte_count >= 4) - { - *((uint32_t *)dest) = longword; - dest += 4; - byte_count -= 4; - } - // Ending the copy. - while(byte_count) - { - *dest++ = byte; - byte_count--; - } - - return d; -} diff --git a/src/string/strchr.c b/src/string/strchr.c deleted file mode 100644 index 7954fcf..0000000 --- a/src/string/strchr.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -/* - strchr - Searches a character in a string. -*/ -const char *strchr(const char *str, int value) -{ - while(*str && *str != value) str++; - return *str ? str : NULL; -} diff --git a/src/string/strcmp.c b/src/string/strcmp.c deleted file mode 100644 index 36971a4..0000000 --- a/src/string/strcmp.c +++ /dev/null @@ -1,18 +0,0 @@ -#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/strcpy.c b/src/string/strcpy.c deleted file mode 100644 index 7659120..0000000 --- a/src/string/strcpy.c +++ /dev/null @@ -1,11 +0,0 @@ -#include - -/* - strcpy() - Copies a string to another. -*/ -char *strcpy(char *destination, const char *source) -{ - size_t length = strlen(source); - return memcpy(destination, source, length); -} diff --git a/src/string/strlen.c b/src/string/strlen.c deleted file mode 100644 index 7b5a9a1..0000000 --- a/src/string/strlen.c +++ /dev/null @@ -1,12 +0,0 @@ -#include - -/* - strlen() - Returns the length of a string. -*/ -size_t strlen(const char *str) -{ - int len = 0; - while(str[len]) len++; - return len; -} diff --git a/src/string/strncpy.c b/src/string/strncpy.c deleted file mode 100644 index 98422fa..0000000 --- a/src/string/strncpy.c +++ /dev/null @@ -1,20 +0,0 @@ -#include - -/* - strncpy() - Copies part of a string to another. -*/ -char *strncpy(char *destination, const char *source, size_t size) -{ - size_t length = strlen(source); - - if(length >= size) - { - return memcpy(destination, source, size); - } - else - { - memset(destination + length, 0, size - length); - return memcpy(destination, source, length); - } -} diff --git a/src/string/strnlen.c b/src/string/strnlen.c deleted file mode 100644 index 57d1344..0000000 --- a/src/string/strnlen.c +++ /dev/null @@ -1,13 +0,0 @@ -#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; -} diff --git a/src/tales/dprint.c b/src/tales/dprint.c deleted file mode 100644 index cf3c4e8..0000000 --- a/src/tales/dprint.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include - -/* - dprint() - Prints a formatted string. Works the same as printf(). -*/ -void dprint(int x, int y, const char *format, ...) -{ - va_list args; - - va_start(args, format); - __printf(0, format, args); - va_end(args); - - dtext(x, y, __stdio_buffer); -} diff --git a/src/tales/dtext.c b/src/tales/dtext.c deleted file mode 100644 index 22e7b59..0000000 --- a/src/tales/dtext.c +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include - -/* - dtext() - Prints the given string, without any analysis. -*/ -void dtext(int x, int y, const char *str) -{ - render(x, y, str, operate_mono); -} diff --git a/src/tales/gprint.c b/src/tales/gprint.c deleted file mode 100644 index 8f58f29..0000000 --- a/src/tales/gprint.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include - -/* - gprint() - Prints a formatted string. Works the same as printf(). -*/ -void gprint(int x, int y, const char *format, ...) -{ - va_list args; - - va_start(args, format); - __printf(0, format, args); - va_end(args); - - gtext(x, y, __stdio_buffer); -} diff --git a/src/tales/gtext.c b/src/tales/gtext.c deleted file mode 100644 index 118aafd..0000000 --- a/src/tales/gtext.c +++ /dev/null @@ -1,11 +0,0 @@ -#include -#include - -/* - gtext() - Prints the given raw string. -*/ -void gtext(int x, int y, const char *str) -{ - render(x, y, str, operate_gray); -} diff --git a/src/tales/tales_configuration.c b/src/tales/tales_configuration.c deleted file mode 100644 index 6f099cc..0000000 --- a/src/tales/tales_configuration.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -/* - text_configure() - Sets the font and mode to use for the following print operations. -*/ -void text_configure(font_t *next_font, color_t next_operator) -{ - extern font_t gint_font_system; - if(next_font) font = next_font; - else font = &gint_font_system; - - operator = next_operator; -} diff --git a/src/tales/tales_gray.c b/src/tales/tales_gray.c deleted file mode 100644 index 10589b1..0000000 --- a/src/tales/tales_gray.c +++ /dev/null @@ -1,67 +0,0 @@ -#include -#include -#include - -void operate_gray(OPERATE_ARGS) -{ - if(x < 0) return; - - int start = (y < 0) ? (-y) : (0); - size_t vram_offset = (x >> 5) + ((y + start) << 2); - uint32_t *light = (uint32_t *)gray_lightVRAM() + vram_offset; - uint32_t *dark = (uint32_t *)gray_darkVRAM() + vram_offset; - uint32_t op, old_light; - - for(int i = start; i < height; i++) - { - op = operators[i]; - - switch(operator) - { - case color_white: - *light &= ~op; - *dark &= ~op; - break; - case color_light: - *light |= op; - *dark &= ~op; - break; - case color_dark: - *light &= ~op; - *dark |= op; - break; - case color_black: - *light |= op; - *dark |= op; - break; - case color_none: - return; - - case color_invert: - *light ^= op; - *dark ^= op; - break; - case color_lighten: - old_light = *light; - *light &= *dark | ~op; - *dark = (old_light | ~op) & (op ^ *dark); - break; - case color_lighten2: - *dark &= *light | ~op; - *light &= ~op; - break; - case color_darken: - old_light = *light; - *light |= *dark & op; - *dark = (old_light & op) | (op ^ *dark); - break; - case color_darken2: - *dark |= *light | op; - *light |= op; - break; - } - - light += 4; - dark += 4; - } -} diff --git a/src/tales/tales_internals.c b/src/tales/tales_internals.c deleted file mode 100644 index f1d6a99..0000000 --- a/src/tales/tales_internals.c +++ /dev/null @@ -1,283 +0,0 @@ -#include -#include -#include -#include -#include - -/* Put these in gint's uninitialized bss section so that text rendering can be - used before the library is fully initialized */ -__attribute__((section(".gint.bss"))) font_t *font = NULL; -__attribute__((section(".gint.bss"))) color_t operator; - -/* - getCharacterIndex() - Returns the index of a character in a font data area depending on the - font format and the size of the characters. Returns the index in the - data area, as long array, or -1 when the character does not belong to - the font format set. -*/ -int getCharacterIndex(int c) -{ - const char *data = (const char *)&font->glyphs; - int index, current; - int width, bits; - size_t offset; - - c &= 0x7f; - - - - // Getting the character index in the glyph array. - - if(font->format == font_format_ascii) index = c; - else if(font->format == font_format_print) index = c - 32; - - else switch(font->format) - { - case font_format_numeric: - if(!isdigit(c)) return -1; - index = c - '0'; - break; - case font_format_lower: - if(!islower(c)) return -1; - index = c - 'a'; - break; - case font_format_upper: - if(!isupper(c)) return -1; - index = c - 'A'; - break; - case font_format_letters: - if(!isalpha(c)) return -1; - index = c - 'A' - ('a' - 'Z') * (c >= 'a'); - break; - case font_format_common: - if(!isalnum(c)) return -1; - index = c - '0' - ('A' - '9') * (c >= 'A') - - ('a' - 'Z') * (c >= 'a'); - break; - case font_format_unknown: - default: - return -1; - } - - - - // Reaching the character offset. - - current = index & ~7; - offset = font->index[current >> 3]; - - while(current < index) - { - width = data[offset << 2]; - bits = font->data_height * width + 8; - - offset += (bits + 31) >> 5; - current++; - } - - return offset; -} - -/* - operate() - Operates on the vram using the given operators. The x-coordinate should - be a multiple of 32. There should be `height` operators. -*/ -void operate_mono(OPERATE_ARGS) -{ - if(x < 0) return; - - uint32_t *vram = display_getCurrentVRAM(); - int start = (y < 0) ? (-y) : (0); - uint32_t *video = vram + (x >> 5) + ((y + start) << 2); - - switch(operator) - { - case color_white: - for(int i = start; i < height; i++) - { - *video &= ~operators[i]; - video += 4; - } - break; - - case color_black: - for(int i = start; i < height; i++) - { - *video |= operators[i]; - video += 4; - } - break; - - case color_invert: - for(int i = start; i < height; i++) - { - *video ^= operators[i]; - video += 4; - } - break; - - default: return; - } -} - -/* - update() - Updates the operators using the given glyph. The operation will not be - complete if there are not enough bits available in the operator data. - In this case the offset will become negative, which means that the - calling procedure has to call operate() and re-call update(). - `available` represents the number of free bits in the operators (lower - bits). - Returns the number of bits available after the operation. If it's - negative, call operate() and update() again. -*/ -int update(uint32_t *operators, int height, int available, uint32_t *glyph) -{ - // Glyph width. - int width = glyph[0] >> 24; - int i; - - // The glyph mask extracts 'width' bits at the left. The partial mask - // is used when there are less than 'width' bits available in the - // current data longword. - uint32_t glyph_mask = 0xffffffff << (32 - width); - uint32_t partial_mask; - - int shift; - uint32_t line; - - // Current data longword, next data array index, and number of bits - // still available in 'data'. - uint32_t data = glyph[0] << 8; - int data_index = 1; - int bits_available = 24; - - for(i = 0; i < height; i++) - { - shift = 32 - available; - - // Getting the next 'width' bits. In some cases these bits will - // intersect two different longs... - line = data & glyph_mask; - line = (shift >= 0) ? (line >> shift) : (line << -shift); - operators[i] |= line; - - data <<= width; - bits_available -= width; - - // ... continue looping until they do. - if(bits_available >= 0) continue; - - // Computing a special mask that extracts just the number of - // bits missing, and loading a new data longword. - partial_mask = 0xffffffff << (32 + bits_available); - data = glyph[data_index++]; - shift += width + bits_available; - - // In case this condition is not verified, the program invokes - // undefined behavior because of the bit shift. Anyway, it - // means that the end of the end of the operators was reached, - // in which case the function should not continue writing. - if(shift <= 31) - { - line = data & partial_mask; - line = (shift >= 0) ? (line >> shift) : - (line << -shift); - operators[i] |= line; - } - - data <<= -bits_available; - bits_available += 32; - } - - return available - width; -} - -/* - render() - Renders text without any formatting analysis, using the given operation - function. -*/ -void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS)) -{ - if(!font || font->magic != 0x01) return; - - // Operator data, and number of available bits in the operators (which - // is the same for all operators, since they are treated equally). - uint32_t *operators; - int available; - - // Raw glyph data, each glyph being represented as one or several - // longwords, and an index in this array. - uint32_t *data = (uint32_t *)font->glyphs; - int index; - - // Storage height of each glyph. This is a constant for all glyphs - // because the format required it. It makes this routine consequently - // faster. - int height = font->data_height; - - - - // Allocating data. There will be one operator for each line. - if(x > 127 || y > 63 || y <= -height) return; - if(y + height > 64) height = 64 - y; - - operators = alloca(height * sizeof(uint32_t)); - if(!operators) return; - for(int i = 0; i < height; i++) operators[i] = 0; - - // Computing the initial operator offset to have 32-aligned operators. - // This allows to write directly video ram longs instead of having to - // shift operators, and do all the vram operation twice. - // I double-checked that this operation is still valid when x is - // negative. - available = 32 - (x & 31); - x &= ~31; - - // Displaying character after another. - while(*str) - { - index = getCharacterIndex(*str++); - if(index < 0) continue; - - // Updating the operators. - available = update(operators, height, available, data + index); - - // Continue until operators are full (this includes an - // additional bit to add a space between each character). - if(available > 1) - { - available--; - continue; - } - - // When operators are full, updating the video ram and - // preparing the operators for another row. - - if(x >= 0) (*op)(operators, height, x, y); - x += 32; - if(x > 96) break; - - for(int i = 0; i < height; i++) operators[i] = 0; - if(available >= 0) - { - available += 31; - continue; - } - - // Finishing update, in cases where it has been only partially - // done because there was not enough bits available to fit all - // the information. Also adding a space, assuming that - // characters aren't more than 30 bits wide. (=p) - available += 32 + (data[index] >> 24); - available = update(operators, height, available, data + index); - available--; - } - - // Final operation. This condition allows a single bit of the operators - // to be used - that's because the loop will add a final spacing pixel. - if(x <= 96 && available < 31) (*op)(operators, height, x, y); -} diff --git a/src/tales/text_length.c b/src/tales/text_length.c deleted file mode 100644 index 2add43e..0000000 --- a/src/tales/text_length.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -/* - text_length() - Computes the length of a string using the currently configured font. -*/ -size_t text_length(const char *str) -{ - if(!str) return 0; - size_t len = 0; - - while(*str) - { - int index = getCharacterIndex(*str); - len += font->glyphs[index] >> 24; - if(*++str) len++; - } - - return len; -} diff --git a/src/time/asctime.c b/src/time/asctime.c deleted file mode 100644 index ed3d0a3..0000000 --- a/src/time/asctime.c +++ /dev/null @@ -1,57 +0,0 @@ -#include - -static char str[26]; - -/* - asctime() - Converts broken-down time to string representation on the form - "Wed Jun 30 21:49:08 1993\n". The returned string is statically - allocated and may be overwritten by any subsequent call to a time - function. -*/ -char *asctime(const struct tm *time) -{ - const char *days[] = { - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" - }, *months[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", - "Sep", "Oct", "Nov", "Dec" - }; - int year = time->tm_year + 1900; - - str[0] = days[time->tm_wday][0]; - str[1] = days[time->tm_wday][1]; - str[2] = days[time->tm_wday][2]; - str[3] = ' '; - - str[4] = months[time->tm_mon][0]; - str[5] = months[time->tm_mon][1]; - str[6] = months[time->tm_mon][2]; - str[7] = ' '; - - str[8] = '0' + (time->tm_mday / 10); - str[9] = '0' + (time->tm_mday % 10); - str[10] = ' '; - - str[11] = '0' + (time->tm_hour / 10); - str[12] = '0' + (time->tm_hour % 10); - str[13] = ':'; - str[14] = '0' + (time->tm_min / 10); - str[15] = '0' + (time->tm_min % 10); - str[16] = ':'; - str[17] = '0' + (time->tm_sec / 10); - str[18] = '0' + (time->tm_sec % 10); - str[19] = ' '; - - str[20] = '0' + (year / 1000); - year %= 1000; - str[21] = '0' + (year / 100); - year %= 100; - str[22] = '0' + (year / 10); - str[23] = '0' + (year % 10); - - str[24] = '\n'; - str[25] = 0; - - return str; -} diff --git a/src/time/ctime.c b/src/time/ctime.c deleted file mode 100644 index f3768e0..0000000 --- a/src/time/ctime.c +++ /dev/null @@ -1,13 +0,0 @@ -#include - -/* - ctime() - Converts calendar time to string representation on the form - "Wed Jun 30 21:49:08 1993\n". The returned string is statically - allocated and may be overwritten by any subsequent call to a time - function. -*/ -char *ctime(const time_t *timer) -{ - return asctime(gmtime(timer)); -} diff --git a/src/time/gmtime.c b/src/time/gmtime.c deleted file mode 100644 index 4d84587..0000000 --- a/src/time/gmtime.c +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include -#include - -static struct tm tm; - -/* - gmtime() - Converts calendar time to broken-down time. The returned pointer is - statically allocated and may be overwritten by any subsequent call to - a time function. -*/ -struct tm *gmtime(const time_t *timeptr) -{ - time_t t = *timeptr; - div_t d; - int sec; - - tm.tm_year = 1970; - tm.tm_mon = 0; - - sec = daysInMonth(tm.tm_mon, tm.tm_year) * 24 * 3600; - while(t >= sec) - { - t -= sec; - if(++tm.tm_mon == 12) - { - tm.tm_year++; - tm.tm_mon = 0; - } - sec = daysInMonth(tm.tm_mon, tm.tm_year) * 24 * 3600; - } - tm.tm_year -= 1900; - - d = div(sec, 24 * 3600); - tm.tm_mday = d.quot; - d = div(d.rem, 3600); - tm.tm_hour = d.quot; - d = div(d.rem, 60); - tm.tm_min = d.quot; - tm.tm_sec = d.rem; - - mktime(&tm); - return &tm; -} diff --git a/src/time/mktime.c b/src/time/mktime.c deleted file mode 100644 index 0ea26a9..0000000 --- a/src/time/mktime.c +++ /dev/null @@ -1,68 +0,0 @@ -#include -#include - -/* - mktime() - Converts broken-down time to calendar time. Computes structure fields - tm_wday and tm_yday using the other fields. Member structures outside - their range are normalized (e.g. 40 October becomes 9 November) and - tm_isdst is set. -*/ -time_t mktime(struct tm *time) -{ - int leaps, yr; - int days, i; - - // Normalizing time. - time->tm_min += (time->tm_sec / 60); - time->tm_hour += (time->tm_min / 60); - time->tm_mday += (time->tm_hour / 24); - time->tm_sec %= 60; - time->tm_min %= 60; - time->tm_hour %= 24; - - // Normalizing date. - days = daysInMonth(time->tm_mon, time->tm_year + 1900); - while(time->tm_mday >= days) - { - time->tm_mday -= days; - if(++time->tm_mon == 12) - { - time->tm_mon = 0; - time->tm_year++; - } - days = daysInMonth(time->tm_mon, time->tm_year + 1900); - } - - // Setting the year day. - days = 0; - for(i = 0; i < time->tm_mon; i++) - days += daysInMonth(i, time->tm_year + 1900); - time->tm_yday = days + time->tm_mday; - - // Setting the week day. The calendar is periodic over 400 years and - // 1601-01-01 was a Monday. - - // Years completely elapsed since last 400n + 1 year (1601-2001-etc). - yr = (time->tm_year + 1900 - 1) % 400; - // Leap years in these yr years. - leaps = (yr / 4) - (yr >= 100) - (yr >= 200) - (yr >= 300); - // Days completely elapsed since last 400n + 1 year, 01-01. - days = 365 * yr + leaps + time->tm_yday; - // Current day of week (1 means Monday 1601-01-01). - time->tm_wday = (1 + days) % 7; - - // This RTC does not seem to have any DST feature. - time->tm_isdst = 0; - - if(time->tm_year + 1900 < 1970) return (time_t)-1; - - // 134774 is the number of days between 1601-01-01 and 1970-01-01. Thus - // days become the number of days elapsed since 1970-01-01. - days -= 134774; - // days may become negative, so add the calendar period. - if(days < 0) days += 146097; - - return (time_t)((24 * 3600) * days + 3600 * time->tm_hour + - 60 * time->tm_min + time->tm_sec); -} diff --git a/src/time/time.c b/src/time/time.c deleted file mode 100644 index e3edd16..0000000 --- a/src/time/time.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include - -/* - time() - Returns the current time as calendar time. If you need a broken-down - time, either use the RTC API or gmtime(). However, this function is - already based on mktime() (for hardware reasons) so it would be much - faster to use the RTC API if possible. - If timeptr is not NULL, it is set to the current time, that is, the - value that is returned. -*/ -time_t time(time_t *timeptr) -{ - rtc_time_t rtc; - struct tm tm; - time_t calendar; - - rtc_getTime(&rtc); - tm.tm_sec = rtc.seconds; - tm.tm_min = rtc.minutes; - tm.tm_hour = rtc.hours; - tm.tm_mday = rtc.month_day; - tm.tm_mon = rtc.month; - tm.tm_year = rtc.year - 1900; - - calendar = mktime(&tm); - if(timeptr) *timeptr = calendar; - return calendar; -} diff --git a/src/time/time_misc.c b/src/time/time_misc.c deleted file mode 100644 index 88e01fc..0000000 --- a/src/time/time_misc.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#undef difftime - -/* - clock() - Should return elapsed CPU time since beginning of program execution. - This is currently not implemented and returns -1. -*/ -clock_t clock(void) -{ - return (clock_t)-1; -} - -/* - difftime() - Returns the number of seconds between the given points. -*/ -double difftime(time_t end, time_t beginning) -{ - return (double)(end - beginning); -} diff --git a/src/time/time_util.c b/src/time/time_util.c deleted file mode 100644 index 11c7048..0000000 --- a/src/time/time_util.c +++ /dev/null @@ -1,28 +0,0 @@ -#include - -/* - isLeap() - Determines whether the given year is a leap year. -*/ -int isLeap(int year) -{ - int leap = !(year & 3); // Take multiples of 4 - if(!(year % 100)) leap = 0; // Remove multiples of 100 - if(!(year % 400)) leap = 1; // Take multiples of 400 - - return leap; -} - -/* - daysInMonth() - Returns number of days for the given month (between 0 and 11) and year. -*/ -int daysInMonth(int month, int year) -{ - int days[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - - if(month != 1) return days[month]; - return days[month] + isLeap(year); -} diff --git a/src/timer/common_api.c b/src/timer/common_api.c deleted file mode 100644 index d403e57..0000000 --- a/src/timer/common_api.c +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include -#include - -/* - timer_attach() - Attaches a callback to a virtual or hardware timer. -*/ -void timer_attach(timer_t *timer, void *callback, void *argument) -{ - if(!timer) return; - - timer->callback = callback; - timer->argument = argument; -} - -/* - timer_start() - Starts a virtual or hardware timer. If the timer has a callback - attached, then the callback function will start being called regularly; - otherwise, the timer will start pushing ET_Timer events to the event - queue. It is advised, not to change a timer's configuration while it's - running. -*/ -void timer_start(timer_t *timer) -{ - if(!timer) return; - timer->active = 1; - - if(timer->virtual) vtimer_updateOne(timer->ms_delay); - else TMU.TSTR->byte |= (1 << (timer - htimers)); -} - -/* - timer_stop() - Pauses a virtual or hardware timer. The timer stops counting and can be - started again later. -*/ -void timer_stop(timer_t *timer) -{ - if(!timer) return; - timer->active = 0; - - if(timer->virtual) vtimer_updateAll(); - else - { - TMU.TSTR->byte &= ~(1 << (timer - htimers)); - timer->used = 0; - } -} - -/* - timer_interrupt() - Handles the interrupt for the given timer channel. -*/ -void timer_interrupt(int channel) -{ - // Is this the virtual timer support? - if(htimers[channel].vsupport) vtimer_interrupt(); - else timer_callback_event(&htimers[channel]); - - // Clearing the interrupt flag. - TMU.timers[channel]->TCR.UNF = 0; -} - -/* - timer_callback_event() - Executes the callback of a timer, or pushes a new timer event depending - on the timer configuration. Also reduces the amount of repeats left and - clears the active flag (or stops the hardware timer) if this number - falls from one to zero. -*/ -void timer_callback_event(timer_t *timer) -{ - if(!timer) return; - - // Callback-type timers. - if(timer->callback) - { - if(!timer->argument) - { - void (*fun)(void) = timer->callback; - fun(); - } - else - { - void (*fun)(void *arg) = timer->callback; - fun(timer->argument); - } - } - - // Event-type timers. - else - { - event_t event = { - .type = event_timer_underflow, - .timer = timer - }; - event_push(event); - } - - // Reducing the number of repeats and stopping the timer if required. - if(timer->repeats_left > 0) - { - if(!--timer->repeats_left) - { - if(timer->virtual) timer->active = 0; - else timer_stop(timer); - } - } -} diff --git a/src/timer/hardware_timers.c b/src/timer/hardware_timers.c deleted file mode 100644 index 57e247f..0000000 --- a/src/timer/hardware_timers.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include - -//--- -// Public API -//--- - -timer_t htimers[3] = { 0 }; - -/* - htimer_setup() - Configures a hardware timer. -*/ -timer_t *htimer_setup(timer_hard_t id, uint32_t constant, timer_input_t input, - int repeats) -{ - if(id < 0 || id >= 3 || htimers[id].used) return NULL; - timer_t *timer = &htimers[id]; - - // We don't care about this. - timer->ms_delay = 0; - timer->ms_elapsed = 0; - - timer->repeats_left = repeats; - - timer->used = 1; - timer->active = 0; - timer->virtual = 0; - timer->vsupport = 0; - timer->events = 0; - - timer->callback = NULL; - timer->argument = NULL; - - // Time to set up the real thing. - volatile mod_tmu_timer_t *tmu = TMU.timers[id]; - - tmu->TCOR = constant; - tmu->TCNT = constant; - tmu->TCR.TPSC = input; - - tmu->TCR.UNF = 0; // Clear the interrupt flag. - tmu->TCR.UNIE = 1; // Enable underflow interrupt. - tmu->TCR.CKEG = 0; // Count on rising edge (SH7705). - - return timer; -} - -/* - htimer_reload() - Reloads a hardware timer without starting or stopping it. -*/ -void htimer_reload(timer_hard_t id, uint32_t new_constant) -{ - volatile mod_tmu_timer_t *tmu = TMU.timers[id]; - - // This is not much but we want to make the transition as swift as - // possible and I can prove that at least one cycle will be lost in - // processor operation. Thus... - tmu->TCNT = new_constant - 1; - tmu->TCOR = new_constant; -} diff --git a/src/timer/virtual_timers.c b/src/timer/virtual_timers.c deleted file mode 100644 index b250065..0000000 --- a/src/timer/virtual_timers.c +++ /dev/null @@ -1,241 +0,0 @@ -#include -#include -#include -#include - -//--- -// Public API -//--- - -timer_t vtimers[TIMER_SLOTS] = { 0 }; - -/* - timer_create() - Creates a virtual timer and configures its delay and repetition count. -*/ -timer_t *timer_create(int ms_delay, int repeats) -{ - if(ms_delay <= 0) return NULL; - timer_t *timer = vtimers; - - // Finding an available virtual slot. - while(timer < vtimers + TIMER_SLOTS && timer->used) timer++; - if(timer >= vtimers + TIMER_SLOTS) return NULL; - - timer->ms_delay = ms_delay; - timer->ms_elapsed = 0; - timer->repeats_left = repeats; - - timer->used = 1; - timer->active = 0; - timer->virtual = 1; - timer->vsupport = 0; - timer->events = 0; - - timer->callback = NULL; - timer->argument = NULL; - - return timer; -} - -/* - timer_reload() - Changes a virtual timer's delay. The timer is not stopped nor started: - it keeps running or waiting. Events that were waiting to be handled are - dropped and the number of repeats left is not changed. The timer - restarts counting from 0 regardless of how much time had elapsed since - it last fired. -*/ -void timer_reload(timer_t *timer, int new_ms_delay) -{ - if(!timer->virtual) return; - - timer->ms_delay = new_ms_delay; - timer->ms_elapsed = 0; - - vtimer_updateAll(); -} - -/* - timer_destroy() - Destroys a virtual timer. This virtual timer pointer becomes invalid - and should not be used anymore. -*/ -void timer_destroy(timer_t *timer) -{ - timer->events = 0; - timer->active = 0; - timer->used = 0; - - vtimer_updateAll(); -} - - - -//--- -// Virtual timers management -//--- - -static int current_delay = 0; -static uint32_t current_constant = 0; - -/* - vtimer_interrupt() - Interrupt handling subsystem for the virtual timers. -*/ -void vtimer_interrupt(void) -{ - // Do we need to recompute the hardware support frequency? - int recalc = 0; - - // No timer is running. - if(!current_delay) return; - - // Update them all and call the required callbacks. Stop the ones that - // have been running for long enough. - for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++) - if(timer->used) - { - timer->ms_elapsed += current_delay; - - // This should happen only once but in case there is a problem, - // this loop will ensure that at least the correct amount of - // callbacks is executed. - // We could divide but it would be slower. - while(timer->ms_elapsed >= timer->ms_delay) - { - // We don't call the callbacks now because we may need - // to update the timer frequency later and we want to - // get there asap. - timer->events++; - timer->ms_elapsed -= timer->ms_delay; - } - - // We would need to stop one virtual timer. - if(timer->repeats_left > 0 && timer->repeats_left <= - timer->events) recalc = 1; - } - - if(recalc) vtimer_updateAll(); - - for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++) - if(timer->used) - { - while(timer->active && timer->events) - { - timer_callback_event(timer); - timer->events--; - } - timer->used = timer->active; - } -} - -/* - vtimer_update() - Swiftly updates the hardware support counter and constant to keep up - with new kinds of timers being added while disturbing the counting flow - as little as possible. -*/ -static void vtimer_update(int new_delay) -{ - volatile mod_tmu_timer_t *tmu = TMU.timers[timer_virtual]; - timer_t *timer = &htimers[timer_virtual]; - - if(new_delay == current_delay) return; - - if(!new_delay) - { - current_delay = 0; - current_constant = 0; - - // At this point the virtual support timer was running and has - // been stopped, thus the hardware timer structure is filled - // properly so we can use this function. - timer_stop(timer); - return; - } - uint32_t new_constant = clock_setting(new_delay, clock_ms); - - // The transition needs to be as smooth as possible. We have probably - // spent a lot of time calculating this new GCD delay so we want to - // take into consideration the current value of the timer counter. - // The -1 is here to take into account the time between reading and - // writing the new constant (~10 I_phi cycles thus usually 5 P_phi - // cycles; given this timer runs on P_phi / 4 this makes 1 unit). - // Here we suppose that the new TCNT is positive, i.e that the update - // happened in less than 1 ms. This is reasonable as long as this - // happens *before* calling callbacks. - tmu->TCNT = new_constant - (current_constant - tmu->TCNT - 1); - tmu->TCOR = new_constant; - - if(!current_delay) - { - // Ups, we've been using a random counter; the timer's not - // running. - tmu->TCNT = tmu->TCOR; - // Set it up then. - tmu->TCR.TPSC = timer_Po_4; - tmu->TCR.UNF = 0; - tmu->TCR.UNIE = 1; - tmu->TCR.CKEG = 0; - // Tell them that the virtual timer support is running there. - timer->used = 1; - timer->active = 0; - timer->virtual = 0; - timer->vsupport = 1; - timer->events = 0; - // And let's roll! - timer_start(timer); - } - - current_delay = new_delay; - current_constant = new_constant; -} - -/* - gcd() - Well, the Euclidean algorithm. That's O(ln(max(a, b))) FWIW. -*/ -static int gcd(int a, int b) -{ - while(b) - { - int r = a % b; - a = b; - b = r; - } - - return a; -} - -/* - vtimer_updateOne() - Update the virtual timer hardware support timer, knowing that a virtual - timer with the given delay has been started. -*/ -void vtimer_updateOne(int additional_delay) -{ - vtimer_update(gcd(current_delay, additional_delay)); -} - -/* - vtimer_updateAll() - Updates the virtual timer hardware support after computing the GCD of - all virtual timers delays. This computation is rather long (especially - this modulo can afaik only be achieved using division on SuperH and - it's a 70-cycle operation) so it should be avoided when possible. -*/ - -void vtimer_updateAll(void) -{ - int gcd_delay = 0; - - for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++) - if(timer->used && timer->active && (timer->repeats_left <= 0 || - timer->repeats_left > timer->events)) - { - gcd_delay = gcd(gcd_delay, timer->ms_delay); - } - - vtimer_update(gcd_delay); -} diff --git a/version b/version deleted file mode 100644 index 3bada75..0000000 --- a/version +++ /dev/null @@ -1 +0,0 @@ -beta-0.9-585 From 5c20997c9a36bc2de05e31a2f8aebdb1298ccd4e Mon Sep 17 00:00:00 2001 From: lephe Date: Thu, 19 Apr 2018 13:24:26 +0200 Subject: [PATCH 22/24] Hybrid build system and runtime (no interrupts). t6k11 driver. Basic r61524 driver. --- Makefile | 72 +++++-- configure | 68 +++---- fx9860g.ld | 186 ++++++++++++++++++ fxcg50.ld | 173 +++++++++++++++++ include/core/bootlog.h | 24 +++ include/core/intc.h | 390 ++++++++++++++++++++++++++++++++++++++ include/core/mmu.h | 63 ++++++ include/core/mpu.h | 52 +++++ include/core/setup.h | 23 +++ include/defs/attributes.h | 35 ++++ include/defs/types.h | 78 ++++++++ include/drivers/t6k11.h | 53 ++++++ include/gint/display-fx.h | 8 + include/gint/display.h | 32 ++++ include/gint/drivers.h | 47 +++++ include/gint/gint.h | 59 ++++++ src/core/bootlog.c | 117 ++++++++++++ src/core/inth.S | 110 +++++++++++ src/core/memory.c | 107 +++++++++++ src/core/mmu.c | 42 ++++ src/core/mpu.c | 60 ++++++ src/core/setup.c | 135 +++++++++++++ src/core/start.c | 211 +++++++++++++++++++++ src/core/vbr.s | 50 +++++ src/r61524/r61524.c | 341 +++++++++++++++++++++++++++++++++ src/t6k11/t6k11.c | 209 ++++++++++++++++++++ 26 files changed, 2697 insertions(+), 48 deletions(-) create mode 100644 fx9860g.ld create mode 100644 fxcg50.ld create mode 100644 include/core/bootlog.h create mode 100644 include/core/intc.h create mode 100644 include/core/mmu.h create mode 100644 include/core/mpu.h create mode 100644 include/core/setup.h create mode 100644 include/defs/attributes.h create mode 100644 include/defs/types.h create mode 100644 include/drivers/t6k11.h create mode 100644 include/gint/display-fx.h create mode 100644 include/gint/display.h create mode 100644 include/gint/drivers.h create mode 100644 include/gint/gint.h create mode 100644 src/core/bootlog.c create mode 100644 src/core/inth.S create mode 100644 src/core/memory.c create mode 100644 src/core/mmu.c create mode 100644 src/core/mpu.c create mode 100644 src/core/setup.c create mode 100644 src/core/start.c create mode 100644 src/core/vbr.s create mode 100644 src/r61524/r61524.c create mode 100644 src/t6k11/t6k11.c diff --git a/Makefile b/Makefile index f577bc5..ff5264b 100755 --- a/Makefile +++ b/Makefile @@ -15,9 +15,17 @@ cfg := config/Makefile.cfg # Those that do may use this special dependency cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg)) +cf-fx := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \ + -I include $(cfg_macros) +cf-cg := -m4 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \ + -I include $(cfg_macros) + +# On fx9860g, use the sh3eb-elf toolchain; on fxcg50, prefer sh4eb-elf +toolchain := $(if $(filter $(cfg_target),fx),sh3eb-elf,sh4eb-elf) + # Compiler flags, assembler flags, dependency generation, archiving -cflags := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -O2 \ - -I include $(cfg_defs) +cflags := $(cf-$(cfg_target)) +sflags := $(cfg_macros) dflags = -MMD -MT $@ -MF $(@:.o=.d) -MP arflags := @@ -25,47 +33,73 @@ arflags := # File listings # -# Target file -target := bin/libgint.a +# Target file (cfg_target is either "fx" or "cg") +target := bin/libgint-$(cfg_target).a # Automatic names for object and dependency files src2obj = build/$(1:src/%=%).o src2dep = build/$(1:src/%=%).d # Source files -src := $(shell find src -name '*.[cs]') +src := $(shell find src -name '*.[csS]') src_obj := $(foreach s,$(src),$(call src2obj,$s)) # Files with special handling -spe := src/display/font.bmp -spe_obj := $(foreach s,$(spe),$(call src2obj,$s)) +# spe := build/version.o src/display/font.bmp +spe := +spe_obj := build/version.o $(foreach s,$(spe),$(call src2obj,$s)) # All object files obj := $(src_obj) $(spe_obj) +# Version file +version := config/version + # # Toolchain # -gcc = sh3eb-elf-gcc -as = sh3eb-elf-as -ld = sh3eb-elf-ld -ar = sh3eb-elf-ar +gcc = $(toolchain)-gcc +as = $(toolchain)-as +ld = $(toolchain)-ld +ar = $(toolchain)-ar +objcopy = $(toolchain)-objcopy conv = fxconv + +# +# Version management +# + +# Retrieve version information +v_file = $(shell cat $(version) | sed 's/[-.]/ /g') +v_type = $(word 1,$(v_file)) +v_major = $(word 2,$(v_file)) +v_minor = $(word 3,$(v_file)) +v_build = $(word 4,$(v_file)) + +# Create the current version symbol and the new version integer +v_newbuild = $(shell echo $$(($(v_build) + 1))) +v_letter = $(shell echo -n $(v_type) | sed -r 's/^(.).*/\1/') +v_symbol = $(shell printf '0x%02x%01x%01x%04x' "'$(v_letter)'" \ + $(v_major) $(v_minor) $(v_build)) + # # Build rules # all: $(target) -$(target): $(obj) | $(dir $(target)) +$(target): $(obj) $(version) | $(dir $(target)) $(call cmd_l,ar,$@) $(ar) -crs $(arflags) $@ $(obj) # Assembler sources build/%.s.o: src/%.s build/%.s.d @ mkdir -p $(dir $@) - $(call cmd_b,as,$<) $(as) -c $< -o $@ $(sflags) + $(call cmd_b,as,$<) $(gcc) -c $< -o $@ $(sflags) +build/%.S.o: src/%.S build/%.S.d + @ mkdir -p $(dir $@) + $(call cmd_b,as,$<) $(gcc) -c $< -o $@ $(sflags) # C sources build/%.c.o: src/%.c build/%.c.d $(cfgdep) @@ -77,6 +111,13 @@ $(call src2obj,src/display/font.bmp): src/display/font.bmp @ mkdir -p $(dir $@) $(call cmd_m,fxconv,$<) $(conv) -font $< -n gint_font -o $@ +# Version symbol. ld generates a .stack section for unknown reasons; I fall +# back to removing it afterwards. +build/version.o: + @ mkdir -p $(dir $@) + @ echo "_GINT_VERSION = $(v_symbol);" > $@.txt + $(call cmd_b,ld,$@) $(ld) -r -R $@.txt -o $@ + # # Cleaning # @@ -85,12 +126,15 @@ clean: @ rm -rf build/ distclean: clean @ rm -rf bin/ - @ rm -f $(cfg) + @ rm -f $(config) # # Utilities # +$(version): $(src) $(wildcard include/**/*) + @ echo '$(v_type)-$(v_major).$(v_minor)-$(v_newbuild)' > $@ + # Evaluated when a rule requires the configuration file but it doesn't exist CFGDEP: @ echo "Configuration file $(cfg) is missing. Have you configured?" diff --git a/configure b/configure index e99fe35..b1be97f 100755 --- a/configure +++ b/configure @@ -40,31 +40,33 @@ help() Configuration script for the gint library. Usage: $0 [options...] -Required settings: - $Cr--target$C0=${Cg}fx9860g$C0,${Cg}fxcg50$C0 - Select the target platform, either ${Cg}fx9860g$C0 for monochrome - calculators, or ${Cg}fxcg50$C0 for Prizm calculators. +Platform settings (specify exactly one): + ${Cg}-fx9860g$C0 + Target platform is fx-9860G II: all monochrome models that support add-ins + or can be flashed to support them. + ${Cg}-fxcg50$C0 + Target platform is fx-CG50. Options that affect the behavior of the library: - $Cr--boot-log $C_[default:$Cp not specified$C_]$C0 + $Cr-boot-log $C_[default:$Cp not specified$C_]$C0 Enable an on-screen log at startup if a key is kept pressed while launching the add-in, allowing easy debug and crash diagnoses. - $Cr--no-syscalls $C_[default:$Cp not specified$C_]$C0 + $Cr-no-syscalls $C_[default:$Cp not specified$C_]$C0 Never use syscalls. Expect trouble with malloc() and the gray engine. Do not trigger this switch unless you know what you are doing. - $Cr--extended-libc $C_[default:$Cp not specified$C_]$C0 + $Cr-extended-libc $C_[default:$Cp not specified$C_]$C0 Enable specific C99 headers/features that are normally not required by calculator programs. This may allow porting programs from other platforms. - $Cr--static-gray-engine $C_[default:$Cp not specified$C_]$C0 + $Cr-static-gray-engine $C_[default:$Cp not specified$C_]$C0 Place the gray engine vram in static ram instead of using the heap. Always - use this option when using both the gray engine and --no-syscalls. + use this option when using both the gray engine and -no-syscalls. Options that customize size limits: - $Cr--atexit-max$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0 + $Cr-atexit-max$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0 Number of exit handlers that can be registered by atexit(). - $Cr--timer-slots$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0 + $Cr-timer-slots$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0 Number of virtual timers that may be registered at the same time. - $Cr--events-queue-size$C0=${Cg}integer$C_ [default:$Cp 64$C_]$C0 + $Cr-events-queue-size$C0=${Cg}integer$C_ [default:$Cp 64$C_]$C0 Number of events simultaneously stored in the event queue. EOF @@ -79,43 +81,37 @@ fail=false for arg; do case "$arg" in -h | -? | --help) help;; - --target=*) - conf_target=${arg#*=} - if [[ $conf_target = "fx9860g" ]]; then - conf_target="GINT_FX9860G" - else if [[ $conf_target = "fxcg50" ]]; then - conf_target="GINT_FXCG50" - else - echo -e "$error Invalid target. See $0 --help." - fail=true; - fi; fi;; + -fx9860g) + conf_target="FX9860G";; + -fxcg50) + conf_target="FXCG50";; - --boot-log) conf[GINT_BOOT_LOG]=true;; - --no-syscalls) conf[GINT_NO_SYSCALLS]=true;; - --extended-libc) conf[GINT_EXTENDED_LIBC]=true;; - --static-gray-engine) conf[GINT_STATIC_GRAY]=true;; + -boot-log) conf[GINT_BOOT_LOG]=true;; + -no-syscalls) conf[GINT_NO_SYSCALLS]=true;; + -extended-libc) conf[GINT_EXTENDED_LIBC]=true;; + -static-gray-engine) conf[GINT_STATIC_GRAY]=true;; - --atexit-max=*) + -atexit-max=*) size=${arg#*=} if [[ $size == +([0-9]) ]]; then conf[ATEXIT_MAX]=$size - else echo -e "$error --atexit-max expects an integer value" + else echo -e "$error -atexit-max expects an integer value" fail=true; fi;; - --timer-slots=*) + -timer-slots=*) size=${arg#*=} if [[ $size == +([0-9]) ]]; then conf[TIMER_SLOTS]=$size - else echo -e "$error --timer-slots expects an integer value" + else echo -e "$error -timer-slots expects an integer value" fail=true; fi;; - --events-queue-size=*) + -events-queue-size=*) size=${arg#*=} if [[ $size == +([0-9]) ]]; then conf[EVENTS_QUEUE_SIZE]=$size - else echo -e "$error --events-queue-size expects an integer"\ + else echo -e "$error -events-queue-size expects an integer"\ "value" fail=true; fi;; - --atexit-max | --timer-slots | --events-queue-size) + -atexit-max | -timer-slots | -events-queue-size) echo -e "$error syntax for $arg is $arg=";; *) @@ -137,7 +133,11 @@ fi output_config() { - echo -n "cfg_defs =" + [ ${conf_target} == "FX9860G" ] \ + && echo "cfg_target = fx" \ + || echo "cfg_target = cg" + + echo -n "cfg_macros =" echo -n " -D$conf_target" [ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG" diff --git a/fx9860g.ld b/fx9860g.ld new file mode 100644 index 0000000..dce9c53 --- /dev/null +++ b/fx9860g.ld @@ -0,0 +1,186 @@ +/* + Linker script for the fx9860g platform. Most of the symbols defined + here are used in the initialization routine in core/start.c; others are + used in core/setup.c. +*/ + +/* fx9860g may mean SH3 or SH4 and we want full compatibility */ +OUTPUT_ARCH(sh3) +/* ELF offers a lot of symbol/section/relocation insights */ +OUTPUT_FORMAT(elf32-sh) +/* Located in core/start.c */ +ENTRY(_start) + +MEMORY +{ + /* Userspace mapping of the add-in (0x200 B are for the G1A header). + 220k is the maximum amount of simultaneously-mappable code */ + rom (rx): o = 0x00300200, l = 220k + /* This is mapped to RAM; 8k on SH3, apparently 32k on SH4 */ + ram (rw): o = 0x08100000, l = 8k + /* gint's VBR space, mentioned here for completeness */ + vbr (rwx): o = 0x8800e000, l = 4k + /* Some RAM region from P1 area; gint's data will reside here */ + rram (rwx): o = 0x8800f000, l = 4k +} + +SECTIONS +{ + /* + ** ROM sections + */ + + /* First address to be mapped to ROM (including G1A header) */ + _brom = 0x00300000; + /* Size of ROM mappings */ + _srom = 0x200 + + SIZEOF(.text) + SIZEOF(.rodata) + + SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks); + + /* Machine code going to ROM: + - Initialization sections (.pretext.entry and .pretext) + - Compiler-provided constructors (.ctors) and destructors (.dtors) + - All text from .text and .text.* (including user code) + - Code sections from fxlib, named "C" and "P" */ + + .pretext : { + *(.pretext.entry) + *(.pretext) + + _btors = . ; + *(.ctors .ctors.*) + _mtors = . ; + *(.dtors .dtors.*) + _etors = . ; + } > rom + + .text : { + *(.text .text.*) + *(C P) + } > rom + + /* Interrupt handlers going to ROM: + - gint's interrupt handler blocks (.gint.blocks) + + Although gint's blocks end up in VBR space, they are selected and + installed on-the-fly by the library and the drivers, so we can't + just put them in the vbr region and wait for the copy */ + .gint.blocks : { + KEEP(*(.gint.blocks)); + } > rom + + /* Driver data going to ROM: + - Exposed driver interfaces (.gint.drivers) + + The driver information is required to start and configure the + driver, even if the symbols are not referenced */ + .gint.drivers : { + _bdrv = . ; + KEEP(*(.gint.drivers)); + _edrv = . ; + } > rom + + /* Read-only data going to ROM: + - Resources or assets from fxconv or similar converters + - Data marked read-only by the compiler (.rodata and .rodata.*) */ + .rodata : SUBALIGN(4) { + *(.rodata .rodata.*) + } > rom + + + + /* + ** RAM sections + */ + + . = ORIGIN(ram); + + /* BSS stuff going to RAM: + - Data marked BSS by the compiler + - BSS sections from fxlib, namely "B" and "R" + The BSS section is to be stripped from the ELF file later, and wiped + at startup. */ + .bss (NOLOAD) : { + _rbss = . ; + + *(.bss COMMON) + *(B R) + + . = ALIGN(16); + } > ram :NONE + + _sbss = SIZEOF(.bss); + + /* Read-write data going to RAM: + - Data sections generated by the compiler (.data and .data.*) + - Data sections from fxlib, "D" */ + .data ALIGN(4) : ALIGN(4) { + _ldata = LOADADDR(.data); + _rdata = . ; + + *(.data .data.*) + *(D) + + . = ALIGN(16); + } > ram AT> rom + + _sdata = SIZEOF(.data); + + + + /* + ** RRAM sections + ** 8800e000:4k VBR space + ** 8800f000:4k .gint.data and .gint.bss + */ + + /* VBR address: let's just start at the beginning of the RRAM area. + There's an unused 0x100-byte gap at the start of the VBR space. + The VBR space is already a large block (> 2 kiB), so I'm cutting off + the gap to spare some memory */ + _gint_vbr = 0x8800df00; + + . = ORIGIN(rram); + + /* gint's data section, going to Real RAM. This section contains many + small objects from the library (static/global variables, etc) */ + .gint.data ALIGN(4) : ALIGN(4) { + _lgdata = LOADADDR(.gint.data); + _rgdata = . ; + + *(.gint.data .gint.data.*) + + . = ALIGN(16); + } > rram AT> rom + + _sgdata = SIZEOF(.gint.data); + + /* gint's uninitialized BSS section, going to Real RAM. All the large + data arrays will be located here */ + .gint.bss (NOLOAD) : { + /* Since it's uninitialized, the location doesn't matter */ + *(.gint.bss .gint.bss.*) + + . = ALIGN(16); + } > rram :NONE + + _sgbss = SIZEOF(.gint.bss); + + + + /* + ** Other sections + */ + + /* Unwanted sections going to meet Dave Null: + - Java classes registration (why are there even here?) + - Asynchronous unwind tables: no C++ exception handling for now ^^ + - Comments or anything the compiler might put in its assembler + - A stack section sometimes generated for build/version.o */ + /DISCARD/ : { + *(.jcr) + *(.eh_frame_hdr) + *(.eh_frame) + *(.comment) + } +} diff --git a/fxcg50.ld b/fxcg50.ld new file mode 100644 index 0000000..9b13ad8 --- /dev/null +++ b/fxcg50.ld @@ -0,0 +1,173 @@ +/* + Linker script for fxcg50 add-ins. Most symbols are used in the startup + routine in core/start.c; some others in core/setup.c. +*/ + +/* All fxcg50 have SH4 processors (finally rid of compatibility issues) */ +OUTPUT_ARCH(sh4) +/* ELF offers a lot of symbol/section/relocation insights */ +OUTPUT_FORMAT(elf32-sh) +/* Located in core/start.c */ +ENTRY(_start) + +MEMORY +{ + /* Userspace mapping of the add-in (without G3A header) */ + rom (rx): o = 0x00300000, l = 220k + /* Static RAM; stack grows down from the end of this region. + The first 0x2000 bytes are reserved by gint, see below */ + ram (rw): o = 0x08102000, l = 512k + /* gint's VBR space, mentioned here for completeness */ + vbr (rwx): o = 0x8c160000, l = 4k + /* Some RAM region from P1 area; gint's data will reside here */ + rram (rwx): o = 0x8c161000, l = 4k +} + +SECTIONS +{ + /* + ** ROM sections + */ + + /* First address to be mapped to ROM */ + _brom = 0x00300000; + /* Size of ROM mappings */ + _srom = SIZEOF(.text) + SIZEOF(.rodata) + + SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks); + + /* Machine code going to ROM: + - Initialization sections (.pretext.entry and .pretext) + - Compiler-provided constructors (.ctors) and destructors (.dtors) + - All text from .text and .text.* (including user code) */ + .text : { + *(.pretext.entry) + *(.pretext) + + _btors = . ; + *(.ctors .ctors.*) + _mtors = . ; + *(.dtors .dtors.*) + _etors = . ; + + *(.text .text.*) + } > rom + + /* Interrupt handlers going to ROM: + - gint's interrupt handler blocks (.gint.blocks) + + Although gint's blocks end up in VBR space, they are installed at + startup by the library/drivers, so we store them here for now */ + .gint.blocks : { + KEEP(*(.gint.blocks)); + } > rom + + /* Driver data going to ROM: + - Exposed driver interfaces (.gint.drivers) + + The driver information is required to start and configure the + driver, even if the symbols are not referenced */ + .gint.drivers : { + _bdrv = . ; + KEEP(*(.gint.drivers)); + _edrv = . ; + } > rom + + /* Read-only data going to ROM: + - Resources or assets from fxconv or similar converters + - Data marked read-only by the compiler (.rodata and .rodata.*) */ + .rodata : SUBALIGN(4) { + /* Put these first, they need to be 4-aligned */ + *(.rodata.assets) + + *(.rodata .rodata.*) + } > rom + + + + /* + ** RAM sections + */ + + . = ORIGIN(ram); + + /* BSS data going to RAM. The BSS section is to be stripped from the + ELF file later, and wiped at startup */ + .bss (NOLOAD) : { + _rbss = . ; + + *(.bss COMMON) + + . = ALIGN(16); + } > ram :NONE + + _sbss = SIZEOF(.bss); + + /* Read-write data sextions going to RAM (.data and .data.*) */ + .data ALIGN(4) : ALIGN(4) { + _ldata = LOADADDR(.data); + _rdata = . ; + + *(.data .data.*) + + . = ALIGN(16); + } > ram AT> rom + + _sdata = SIZEOF(.data); + + + + /* + ** gint-related sections + ** 88160000:4k VBR space + ** 88161000:4k .gint.data and .gint.bss + */ + + /* VBR address: let's just start at the beginning of the RAM area. + There's an unused 0x100-byte gap at the start of the VBR space. + The VBR space is already a large block (> 2 kiB), so I'm cutting off + the gap to spare some memory */ + _gint_vbr = 0x8815ff00; + + . = ORIGIN(rram); + + /* gint's data section, going to static RAM. This section contains many + small objects from the library (static/global variables, etc) */ + .gint.data ALIGN(4) : ALIGN(4) { + _lgdata = LOADADDR(.gint.data); + _rgdata = . ; + + *(.gint.data .gint.data.*) + + . = ALIGN(16); + } > rram AT> rom + + _sgdata = SIZEOF(.gint.data); + + /* gint's uninitialized BSS section, going to static RAM. All the large + data arrays will be located here */ + .gint.bss (NOLOAD) : { + /* Since it's uninitialized, the location doesn't matter */ + *(.gint.bss .gint.bss.*) + + . = ALIGN(16); + } > rram :NONE + + _sgbss = SIZEOF(.gint.bss); + + + + /* + ** Other sections + */ + + /* Unwanted sections going to meet Dave Null: + - Java classes registration (why are there even here?) + - Asynchronous unwind tables: no C++ exception handling for now ^^ + - Comments or anything the compiler might put in its assembler */ + /DISCARD/ : { + *(.jcr) + *(.eh_frame_hdr) + *(.eh_frame) + *(.comment) + } +} diff --git a/include/core/bootlog.h b/include/core/bootlog.h new file mode 100644 index 0000000..152bdea --- /dev/null +++ b/include/core/bootlog.h @@ -0,0 +1,24 @@ +//--- +// gint:core:bootlog - Boot-time on-screen log for extreme debugging +//--- + +#ifndef GINT_CORE_BOOTLOG +#define GINT_CORE_BOOTLOG + +/* bootlog_loaded() - Section loading stage + Called when RAM sections have been wiped and copied */ +void bootlog_loaded(void); + +/* bootlog_mapped() - ROM mapping stage + Called after all ROM pages have been traversed. All of them may not have + been mapped. + @rom Amount of mapped ROM, in bytes + @ram Amount of mapped RAM, in bytes */ +void bootlog_mapped(uint32_t rom, uint32_t ram); + +/* bootlog_kernel() - Gint loading stage + Called when gint has been installed, the VBR switched and the interrupt + handlers set up. */ +void bootlog_kernel(void); + +#endif /* GINT_CORE_BOOTLOG */ diff --git a/include/core/intc.h b/include/core/intc.h new file mode 100644 index 0000000..f660619 --- /dev/null +++ b/include/core/intc.h @@ -0,0 +1,390 @@ +//--- +// gint:core:intc - Interrupt Controller +// +// The interrupt controller is unwieldy because SH7705 and SH7305 have a +// completely different interface. Everything here is split up and you'll +// have to explicitly handle both to be able to use the device. +// +// gint's API provides higher-level and platform-agnostic interrupt +// management. This is probably what you are looking for. +//--- + +#ifndef GINT_CORE_INTC +#define GINT_CORE_INTC + +#include + +//--- +// SH7705 Interrupt Controller. Refer to: +// "Renesas SH7705 Group Hardware Manual" +// Section 6: "Interrupt Controller (INTC)" +//--- + +/* sh7705_intc_ipc_t - Interrupt Priority Controller + A set of 16-bit register that control the interrupt priorities. The SH7705's + IPC has its registers scattered everywhere in the memory, so there is a + pointer for each register. The SH7305 needs only one pointer for the whole + IPC because the registers are in a contiguous area. */ +typedef struct +{ + volatile word_union(*IPRA, + uint TMU0 :4; /* Timer 0 */ + uint TMU1 :4; /* Timer 1 */ + uint TMU2 :4; /* Timer 2 */ + uint RTC :4; /* Real-Time Clock */ + ); + + volatile word_union(*IPRB, + uint WDT :4; /* Watchdog Timer */ + uint REF :4; /* BSC Refresh Request, SDRAM (?) */ + uint :4; + uint :4; + ); + + volatile word_union(*IPRC, + uint IRQ3 :4; /* Interrupt request 3 */ + uint IRQ2 :4; /* Interrupt request 2 */ + uint IRQ1 :4; /* Interrupt request 1 */ + uint IRQ0 :4; /* Interrupt request 0 */ + ); + + volatile word_union(*IPRD, + uint PINT0_7 :4; /* External interrupt pins 0 to 7 */ + uint PINT8_15 :4; /* External interrupt pins 8 to 15 */ + uint IRQ5 :4; /* Interrupt request 5 */ + uint IRQ4 :4; /* Interrupt request 4 */ + ); + + volatile word_union(*IPRE, + uint DMAC :4; /* Direct Memory Access Controller */ + uint SCIF0 :4; /* Serial Communication Interface 0 */ + uint SCIF2 :4; /* Serial Communication Interface 2 */ + uint ADC :4; /* Analog/Decimal Converter */ + ); + + volatile word_union(*IPRF, + uint :4; + uint :4; + uint USB :4; /* USB Controller */ + uint :4; + ); + + volatile word_union(*IPRG, + uint TPU0 :4; /* Timer Pulse Unit 0 */ + uint TPU1 :4; /* Timer Pulse Unit 1 */ + uint :4; + uint :4; + ); + + volatile word_union(*IPRH, + uint TPU2 :4; /* Timer Pulse Unit 2 */ + uint TPU3 :4; /* Timer Pulse Unit 3 */ + uint :4; + uint :4; + ); + +} PACKED(4) sh7705_intc_ipc_t; + +/* sh7705_intc_icr1_t - Interrupt Control Register 1 (general) */ +typedef word_union(sh7705_intc_icr1_t, + uint MAI :1; /* Mask All Interrupts */ + uint IRQLVL :1; /* Interrupt Request Level Detect */ + uint BLMSK :1; /* Enable NMI when BL is set */ + uint :1; + uint IRQ5E :2; /* IRQ 5 Edge Detection */ + uint IRQ4E :2; /* etc. */ + uint IRQ3E :2; + uint IRQ2E :2; + uint IRQ1E :2; + uint IRQ0E :2; +); + +/* sh7705_intc_t - the SH7705 interrupt controller */ +typedef struct +{ + /* All interrupt priority registers */ + union { + sh7705_intc_ipc_t _; + volatile uint16_t *IPRS[8]; + } PACKED(4); + + /* Control registers */ + volatile sh7705_intc_icr1_t *ICR1; + +} PACKED(4) sh7705_intc_t; + + + +//--- +// SH7305 Interrupt Controller. Refer to: +// "Renesas SH7724 User's Manual: Hardware" +// Section 13: "Interrupt Controller (INTC)" +//--- + +/* sh7305_intc_ipc_t - Interrupt Priority Controller + Some of the fields have been left unnamed because they correspond to SH7724 + peripheral modules that are *very* unlikely to even exist in the SH7305, let + alone by of any use to us */ +typedef struct +{ + word_union(IPRA, + uint TMU0_0 :4; /* TMU0 Channel 0 */ + uint TMU0_1 :4; /* TMU0 Channel 1 */ + uint TMU0_2 :4; /* TMU0 Channel 2 */ + uint IrDA :4; /* Infrared Communication */ + ); + pad(2); + + word_union(IPRB, + uint :4; /* JPEG Processing Unit */ + uint LCDC :4; /* LCD Controller */ + uint DMAC1A :4; /* Direct Memory Access Controller 1 */ + uint :4; /* Blending Engine Unit */ + ); + pad(2); + + word_union(IPRC, + uint TMU1_0 :4; /* TMU1 Channel 0 */ + uint TMU1_1 :4; /* TMU1 Channel 1 */ + uint TMU1_2 :4; /* TMU1 Channel 2 */ + uint :4; /* Sound Processing Unit */ + ); + pad(2); + + word_union(IPRD, + uint :4; + uint MMCIF :4; /* MultiMedia Card Interface */ + uint :4; + uint :4; /* ATAPI Interface */ + ); + pad(2); + + word_union(IPRE, + uint DMAC0A :4; /* Direct Memory Access Controller 0 */ + uint :4; /* Various graphical engines */ + uint SCIFA3 :4; /* SCIFA channel 3 interrupt */ + uint :4; /* Video Processing Unit */ + ); + pad(2); + + word_union(IPRF, + uint KEYSC :4; /* Key Scan Interface */ + uint DMACOB :4; /* DMAC0 transfer/error info */ + uint USB0_1 :4; /* USB controller */ + uint CMT :4; /* Compare Match Timer */ + ); + pad(2); + + word_union(IPRG, + uint SCIF0 :4; /* SCIF0 transfer/error info */ + uint SCIF1 :4; /* SCIF1 transfer/error info */ + uint SCIF2 :4; /* SCIF2 transfer/error info */ + uint :4; /* Video Engine Unit */ + ); + pad(2); + + word_union(IPRH, + uint MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */ + uint MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */ + uint :4; /* I2C Interface channel 0 */ + uint :4; /* I2C Interface channel 1 */ + ); + pad(2); + + word_union(IPRI, + uint SCIFA4 :4; /* SCIFA channel 4 interrupt */ + uint :4; /* MediaRAM InterConnected Buffers */ + uint :4; /* Transport Stream Interface */ + uint :4; /* 2D Graphics Accelerator & ICB */ + ); + pad(2); + + word_union(IPRJ, + uint :4; /* Capture Engine Unit */ + uint :4; /* Ethernet Memory Access Controller */ + uint FSI :4; /* FIFO-Buffered Serial Interface */ + uint SDHI1 :4; /* SD Card Host Interface channel 1 */ + ); + pad(2); + + word_union(IPRK, + uint RTC :4; /* Real-Time Clock */ + uint DMAC1B :4; /* DMAC1 transfer/error info */ + uint :4; /* MediaRAM InterConnected Buffers */ + uint SDHI0 :4; /* SD Card Host Interface channel 0 */ + ); + pad(2); + + word_union(IPRL, + uint SCIFA5 :4; /* SCIFA channel 5 interrupt */ + uint :4; + uint TPU :4; /* Timer-Pulse Unit */ + uint :4; /* Image Extraction DMAC */ + ); + pad(2); + +} PACKED(4) sh7305_intc_ipc_t; + +/* sh7305_intc_masks_t - Interrupt mask management + Writing 1 to IMR masks interrupts; writing 1 to IMCRs clears the masks. + Writing 0 is ignored; reading from IMCRs yields undefined values */ +typedef struct +{ + byte_union(IMR0, + uint :1; + uint TUNI1_2 :1; /* TMU1 overflow interrupts */ + uint TUNI1_1 :1; + uint TUNI1_0 :1; + uint SDHII3 :1; /* SD Card Host 1 interrupts */ + uint SDHII2 :1; + uint SDHII1 :1; + uint SDHII0 :1; + ); + pad(3); + + byte_union(IMR1, + uint :4; + uint DEI3 :1; /* DMAC0A interrupts */ + uint DEI2 :1; + uint DEI1 :1; + uint DEI0 :1; + ); + pad(3); + + byte_union(IMR2, + uint :7; + uint SCIFA0 :1; /* Asynchronous Serial interrupts */ + ); + pad(3); + + byte_union(IMR3, + uint DEI3 :1; /* DMAC1A interrupts */ + uint DEI2 :1; + uint DEI1 :1; + uint DEI0 :1; + uint :4; + ); + pad(3); + + byte_union(IMR4, + uint :1; + uint TUNI0_2 :1; /* TMU0 overflow interrupts */ + uint TUNI0_1 :1; + uint TUNI0_0 :1; + uint :3; + uint LCDCI :1; /* LCD Controller Interrupt */ + ); + pad(3); + + byte_union(IMR5, + uint KEYI :1; /* Key Interface */ + uint DADERR :1; /* DMAC0B interrupts */ + uint DEI5 :1; + uint DEI4 :1; + uint :1; + uint SCIF2 :1; /* Serial Communication Interface */ + uint SCIF1 :1; + uint SCIF0 :1; + ); + pad(3); + + byte_union(IMR6, + uint :2; + uint :1; + uint SCIFA4 :1; /* SCIFA4 interrupt */ + uint :1; + uint :1; + uint MSIOFI0 :1; /* Clock-synchronized SCIF channel 0 */ + uint MSIOFI1 :1; /* Clock-synchronized SCIF channel 1 */ + ); + pad(3); + + uint8_t IMR7; + pad(3); + + byte_union(IMR8, + uint SDHII3 :1; /* SD Card Host 0 interrupts */ + uint SDHII2 :1; + uint SDHII1 :1; + uint SDHII0 :1; + uint :2; + uint SCFIA5 :1; /* SCIFA5 interrupt */ + uint FSI :1; /* FIFO-Buffered Serial Interface */ + ); + pad(3); + + byte_union(IMR9, + uint :3; + uint CMTI :1; /* Compare Match Timer Interrupt */ + uint :1; + uint USI1 :1; /* USB1 */ + uint USI0 :1; /* USB0 */ + uint :1; + ); + pad(3); + + byte_union(IMR10, + uint :1; + uint DADERR :1; /* DMAC1B interrupts */ + uint DEI5 :1; + uint DEI4 :1; + uint :1; + uint ATI :1; /* RTC Alarm interrupt */ + uint PRI :1; /* RTC Periodic interrupt */ + uint CUI :1; /* RTC Carry interrupt */ + ); + pad(3); + + byte_union(IMR11, + uint :5; + uint TPUI :1; /* Timer-Pulse Unit */ + uint :2; + ); + pad(3); + + uint8_t IMR12; + +} PACKED(4) sh7305_intc_masks_t; + +/* sh7305_intc_userimask_t - User Interrupt Mask + Sets the minimum required level for interrupts to be accepted. + + WARNING: Writing to this register is only allowed when the upper bits of the + operand (ie. the new value of USERIMASK) are 0xa5; otherwise, the write is + ignored. To modify the value of this register, do not access the bit field + directly, backup the variable and modify it: + + void set_user_imask(int new_level) + { + sh7305_intc_userimask_t mask = *(INTC._7305.USERIMASK); + mask._0xa5 = 0xa5; + mask.UIMASK = new_level & 0x0f; + *(INTC._7305.USERIMASK) = mask; + } +*/ +typedef lword_union(sh7305_intc_userimask_t, + uint _0xa5 :8; /* Always set to 0xa5 before writing */ + uint :16; + uint UIMASK :4; /* User Interrupt Mask Level */ + uint :4; +); + +/* sh7305_intc_t - the SH7305 interrupt controller */ +typedef struct +{ + /* Interrupt priority registers */ + union { + volatile sh7305_intc_ipc_t *_; + volatile uint16_t *IPRS; + }; + + /* Interrupt mask & mask clear registers */ + volatile sh7305_intc_masks_t *MSK; + volatile sh7305_intc_masks_t *MSKCLR; + + /* Other registers */ + volatile sh7305_intc_userimask_t *USERIMASK; + +} PACKED(4) sh7305_intc_t; + +#endif /* GINT_CORE_INTC */ diff --git a/include/core/mmu.h b/include/core/mmu.h new file mode 100644 index 0000000..140ebfa --- /dev/null +++ b/include/core/mmu.h @@ -0,0 +1,63 @@ +//--- +// gint:core:mmu - MMU-related definitions +// +// gint does not touch the MMU because the risk of interfering with the +// system is deemed too high. However, to ensure that the add-in runs +// properly, checks using read-only access to the MMU are performed. +//--- + +#ifndef GINT_CORE_MMU +#define GINT_CORE_MMU + +#include +#include + +/* utlb_addr_t - address part of a UTLB entry */ +typedef struct +{ + uint VPN :22; + uint D :1; + uint V :1; + uint ASID :8; + +} PACKED(4) utlb_addr_t; + +/* utlb_addr() - get the P4 address of a UTLB address entry + @E Entry number (should be in range 0..63) + Returns a pointer to the entry. */ +const utlb_addr_t *utlb_addr(uint E); + +/* utlb_data_t - data part of a UTLB entry */ +typedef struct +{ + uint :3; + uint PPN :19; + uint :1; + uint V :1; + uint SZ1 :1; + uint PR :2; + uint SZ2 :1; + uint C :1; + uint D :1; + uint SH :1; + uint WT :1; + +} PACKED(4) utlb_data_t; + +/* utlb_data() - get the P4 address of a UTLB data entry + @E Entry number (should be in range 0..63) + Returns a pointer to the entry. */ +const utlb_data_t *utlb_data(uint E); + +/* utlb_mapped_memory() - count amount of mapped memory + This function returns the amount of mapped text and data segment memory, in + bytes. The ranges are defined as follows: + ROM 00300000:512k + RAM 08100000:512k + Other mappings are ignored. Both pointers may be NULL. + + @rom Pointer to amount of mapped ROM + @ram Pointer to amount of mapped RAM */ +void utlb_mapped_memory(uint32_t *rom, uint32_t *ram); + +#endif /* GINT_CORE_MMU */ diff --git a/include/core/mpu.h b/include/core/mpu.h new file mode 100644 index 0000000..91164d9 --- /dev/null +++ b/include/core/mpu.h @@ -0,0 +1,52 @@ +//--- +// gint:core:mpu - Runtime MPU detection +// +// This component detects the architecture and MPU type of the underlying +// hardware by relying on version registers and/or side-information. It +// provides macros isSH3() and isSH4() to perform MPU-dependent jobs: +// +// if(isSH3()) { ... } else { ... } +//--- + +#ifndef GINT_CORE_MPU +#define GINT_CORE_MPU + +#include + +/* mpu_t - Supported MPUs */ +typedef enum +{ + mpu_unknown = 0, + mpu_sh7337 = 1, /* fx9860g, SH-3 based */ + mpu_sh7305 = 2, /* fx9860g II, fxcg50, SH-4A based */ + mpu_sh7355 = 3, /* fx9860g II, SH-3 based */ + mpu_sh7724 = 4, /* For reference */ +} mpu_t; + +#ifdef FX9860G + + /* mpu_id() - get the name of the underlying MPU */ + HDRFUNC mpu_t mpu_id(void) + { + extern const mpu_t mpu; + return mpu; + } + + /* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */ + #define isSH3() (mpu_id() & 1) + #define isSH4() (!isSH3()) + + /* mpu_init() - probe the MPU type + This function must be executed before mpu_id() can be used. */ + void mpu_init(void); + +#else /* FXCG50 */ + + /* All fxcg50 machines have an SH7305, which makes things simpler. */ + #define mpu_id() mpu_sh7305 + #define isSH3() 0 + #define isSH4() 1 + +#endif /* FX9860G */ + +#endif /* GINT_CORE_MPU */ diff --git a/include/core/setup.h b/include/core/setup.h new file mode 100644 index 0000000..72c46eb --- /dev/null +++ b/include/core/setup.h @@ -0,0 +1,23 @@ +//--- +// gint:core:setup - Installing and unloading the library +//--- + +#ifndef GINT_CORE_SETUP +#define GINT_CORE_SETUP + +#include + +/* Prototypes for the library management functions are in */ + +/* gint_setvbr() + Changes the VBR address and the calls the configuration function while + interrupts are disabled. The configuration function must change either the + priority registers or the interrupt masks, and make sure that all the + interrupts that it leaves enabled are handled by the new VBR handlers. + + @vbr New VBR address + @configure Configuration function + Returns the previous VBR address. */ +uint32_t gint_setvbr(uint32_t vbr, void (*configure)(void)); + +#endif /* GINT_CORE_SETUP */ diff --git a/include/defs/attributes.h b/include/defs/attributes.h new file mode 100644 index 0000000..64a38a7 --- /dev/null +++ b/include/defs/attributes.h @@ -0,0 +1,35 @@ +//--- +// gint:defs:attributes - Macros for compiler-specific attributes +//--- + +#ifndef GINT_DEFS_ATTRIBUTES +#define GINT_DEFS_ATTRIBUTES + +/* Generic attribute element */ +#define ATTR(...) __attribute__((__VA_ARGS__)) + +/* Objects from specific sections */ +#define SECTION(x) __attribute__((section(x))) +/* Objects from the .gint.data and .gint.bss sections */ +#define GDATA __attribute__((section(".gint.data"))) +#define GBSS __attribute__((section(".gint.bss"))) +/* Initialization functions */ +#define PRETEXT __attribute__((section(".pretext"))) + +/* Unused parameters or variables */ +#define UNUSED __attribute__((unused)) +/* Functions that *must* be inlined */ +#define INLINE __attribute__((always_inline)) inline +/* Short static functions defined in header files */ +#define HDRFUNC __attribute__((always_inline)) static inline + +/* Constructors and destructors */ +#define CTOR(x) __attribute__((constructor ((x) + 101))) PRETEXT +#define DTOR(x) __attribute__((destructor ((x) + 101))) + +/* Packed structures. I require explicit alignment because if it's unspecified, + GCC cannot optimize access size, and reads to memory-mapped I/O with invalid + access sizes silently fail - honestly you don't want this to happen */ +#define PACKED(x) __attribute__((packed, aligned(x))) + +#endif /* GINT_DEFS_ATTRIBUTES */ diff --git a/include/defs/types.h b/include/defs/types.h new file mode 100644 index 0000000..e7d487f --- /dev/null +++ b/include/defs/types.h @@ -0,0 +1,78 @@ +//--- +// gint:defs:types - Type-related macros +//--- + +#ifndef GINT_DEFS_TYPES +#define GINT_DEFS_TYPES + +#include +#include +#include + +//--- +// Const casts +//--- + +/* const_cast() - perform const casts + This is intended for initialization purposes only, like "final" in Java. + This macro is the most generic form without any type inference; using + const_cint() or const_cptr() may be more convenient for simple types */ +#define const_cast(x, T) (*((T *)(&(x)))) + +/* const_cptr() - perform const casts on pointers */ +#define const_cptr(x) (*((void **)(&(x)))) + +/* const_cint() - perform const casts on integers + This macro saves you from specifying the integral type that you're + manipulating, which may avoid errors if the type changes. It will only work + with the primitive types that are either mentioned in the following list or + aliased as one of the listed types. + This is to prevent unforeseen effects of tricks such as typeof(x + 0) that + promotes various small integers to ints */ +#define const_cint(x) (*_Generic((x), \ + char: (char *) (&(x)), \ + unsigned char: (unsigned char *) (&(x)), \ + short: (short *) (&(x)), \ + unsigned short: (unsigned short *) (&(x)), \ + int: (int *) (&(x)), \ + unsigned int: (unsigned int *) (&(x)), \ + long: (long *) (&(x)), \ + unsigned long: (unsigned long *) (&(x)), \ + long long: (long long *) (&(x)), \ + unsigned long long: (unsigned long long *) (&(x)) \ +)) + +//--- +// Structure elements +//---- + +/* Fixed-width types for bit fields are entirely meaningless */ +typedef unsigned int uint; + +/* Giving a type to padding bytes is misguiding */ +#define pad_nam2(c) _ ## c +#define pad_name(c) pad_nam2(c) +#define pad(bytes) uint8_t pad_name(__COUNTER__)[bytes] + +/* byte_union() - union between an uint8_t 'byte' element and a bit field */ +#define byte_union(name, fields) \ + union { \ + uint8_t byte; \ + struct { fields } PACKED(1); \ + } PACKED(1) name + +/* word_union() - union between an uint16_t 'word' element and a bit field */ +#define word_union(name, fields) \ + union { \ + uint16_t word; \ + struct { fields } PACKED(2); \ + } PACKED(2) name + +/* lword_union() - union between an uint32_t 'lword' element and a bit field */ +#define lword_union(name, fields) \ + union { \ + uint32_t lword; \ + struct { fields } PACKED(4); \ + } PACKED(4) name + +#endif /* GINT_DEFS_TYPES */ diff --git a/include/drivers/t6k11.h b/include/drivers/t6k11.h new file mode 100644 index 0000000..5b89f40 --- /dev/null +++ b/include/drivers/t6k11.h @@ -0,0 +1,53 @@ +//--- +// gint:drivers:t6k11 - Toshiba T6K11 driver +// +// This is the screen driver used by fx9860g (monochrome) models. +//--- + +#ifndef GINT_DRIVERS_T6K11 +#define GINT_DRIVERS_T6K11 + +#include + +/* t6k11_display() - send vram data to the LCD device + + A typical 128*64 VRAM area would use y1 = 0, y2 = 64, stride = 16. It is + possible to send only a section of the video RAM by specifying y1 > 0 or + y2 < 64 and moving the vram pointer accordingly. + + @vram Video RAM address + @y1 First row to send + @y2 Last row to send + 1 + @stride Number of bytes between each row */ +void t6k11_display(const void *vram, int y1, int y2, size_t stride); + +/* t6k11_contrast() - change the contrast setting + + Adjusts the screen contrast. The parameter takes value in range 0 .. 32 and + is adjusted when not in range. + + 0 (bright) <-------- 14 (OS default) --------> 32 (dark) + + It is not possible to read the contrast value from the display driver, but + the system stores its contrast setting in RAM. The location is OS-dependent. + It would be possible to restore contrast, or update the system value on + change, if the address was known for all OS versions. + + OS 02.05.2201 8800b93c + + @contrast Requested contrast value */ +void t6k11_contrast(int contrast); + +/* t6k11_backlight() - manage the screen backlight + + Changes the backlight setting depending on the value of "setting": + - If setting = 0, turns the backlight off. + - If setting > 0, turns the backlight on. + - If setting < 0, toggles backlight. + + This function has no effect on models that do not support the backlight. + + @setting Requested backlight setting */ +void t6k11_backlight(int setting); + +#endif /* GINT_DRIVERS_T6K11 */ diff --git a/include/gint/display-fx.h b/include/gint/display-fx.h new file mode 100644 index 0000000..fb54785 --- /dev/null +++ b/include/gint/display-fx.h @@ -0,0 +1,8 @@ +//--- +// gint:display-fx - fx9860g drawing functions +//--- + +#ifndef GINT_DISPLAY_FX +#define GINT_DISPLAY_FX + +#endif /* GINT_DISPLAY_FX */ diff --git a/include/gint/display.h b/include/gint/display.h new file mode 100644 index 0000000..f3f0465 --- /dev/null +++ b/include/gint/display.h @@ -0,0 +1,32 @@ +//--- +// gint:display - Drawing functions +//--- + +#ifndef GINT_DISPLAY +#define GINT_DISPLAY + +/* Expose the VRAM variable if GINT_NEED_VRAM is defined. This address is used + as the VRAM basis by all of gint's drawing functions, and must point to a + suitable buffer: + [fx9860g] A 4-aligned buffer of size 1024. + [fxcg50] A 4-aligned buffer of size 177408. + + This variable is primarily meant to be exposed to gint functions, but + add-ins may use it or change it freely: + - To use another video ram area (triple buffering or more, gray engine) ; + - To implement additional drawing functions ; + - When not drawing, as additional RAM (especially on fxcg50). */ +#ifdef GINT_NEED_VRAM +extern uint32_t *vram; +#endif + +/* As you would expect, display on fx9860g and display on fxcg50 are completely + different worlds. As a consequence, so are the headers ^^ */ + +#ifdef FX9860G +#include +#else +#include +#endif + +#endif /* GINT_DISPLAY */ diff --git a/include/gint/drivers.h b/include/gint/drivers.h new file mode 100644 index 0000000..b1ebf95 --- /dev/null +++ b/include/gint/drivers.h @@ -0,0 +1,47 @@ +//--- +// gint:drivers - General tools for drivers +//--- + +#ifndef GINT_DRIVERS +#define GINT_DRIVERS + +#include +#include + +/* gint_driver_t - driver meta-information used by gint */ +typedef struct +{ + /* Driver name */ + const char *name; + + /* Size of a context object for the driver */ + uint ctx_size; + + /* System context. The driver has to allocate a buffer of size at least + ctx_size, where gint stores the system's configuration. It is + advised to place this buffer in the .gint_bss section using the GBSS + macro of if it doesn't need to be initialized */ + void *sys_ctx; + + /* ctx_save() - save the driver's hardware support + This function is provided by the driver to save the state of its + hardware support (memory-mapped MPU registers, port state, etc). + @ctx A buffer of size ctx_size */ + void (*ctx_save)(void *ctx); + + /* ctx_restore() - restore a saved context + This function is provided by the driver to restore the state saved + by ctx_save(). It can alter the contents of the buffer freely. + @ctx A context buffer filled by ctx_save() */ + void (*ctx_restore)(void *ctx); + +} PACKED(4) gint_driver_t; + +/* GINT_DECLARE_DRIVER() - make a driver visible to gint + Use this macro to expose a driver by passing it the name of a gint_driver_t + structure. This macro moves the structure to the .gint.drivers section, + which is automatically traversed at startup */ +#define GINT_DECLARE_DRIVER(name) \ + SECTION(".gint.drivers") extern gint_driver_t name; + +#endif /* GINT_DRIVERS */ diff --git a/include/gint/gint.h b/include/gint/gint.h new file mode 100644 index 0000000..f081a5d --- /dev/null +++ b/include/gint/gint.h @@ -0,0 +1,59 @@ +//--- +// gint - An alternative runtime environment for fx9860g and fxcg50 +//--- + +#ifndef GINT_GINT +#define GINT_GINT + +#include +#include + +/* gint_version() - get the library version number + Returns gint's running version number, which is made of four fields: + + 31 24 23 20 19 16 15 0 + +---------------+---------------+---------------+---------------+ + | channel | major | minor | build | + +---------------+---------------+---------------+---------------+ + + The first field is a letter indicating the type of version ('a'lpha, 'b'eta, + 'r'elease, 'd'ev, etc). The second and third field are the version number on + the form "major.minor". The last field is the build number for this version. + The build number uniquely identifies a binary version of the library. + + For instance, 0x72100053 translates as "release 1.0 build 83". */ +HDRFUNC uint32_t gint_version(void) +{ + extern char GINT_VERSION; + return (uint32_t)&GINT_VERSION; +} + +//--- +// Library management +//--- + +/* gint_install() - install and start gint + This function installs event handlers, masks interrupts and switches VBR. + Unless you are doing experimental runtime switching and you know how this + function is implemented, you should not call it. */ +void gint_install(void); + +/* gint_unload() - unload gint and give back control to the system + This function restores the runtime environment saved by gint_install(). It + is only called when the add-in terminates. To temporarily leave gint during + execution, use gint_pause(). When possible, use syscalls without leaving + gint for better performance. */ +void gint_unload(void); + +/* gint_pause() - return to main menu, with possibility of coming back + + This function safely invokes the calculator's main menu by unloading gint. + If the user selects the gint application again in the menu, this function + reloads gint and returns. Otherwise, the add-in is fully unloaded by the + system and the application terminates. + + This function is typically called when the [MENU] key is pressed during a + getkey() call. */ +void gint_pause(void); + +#endif /* GINT_GINT */ diff --git a/src/core/bootlog.c b/src/core/bootlog.c new file mode 100644 index 0000000..ed73906 --- /dev/null +++ b/src/core/bootlog.c @@ -0,0 +1,117 @@ +//--- +// gint:core:bootlog - Boot-time on-screen log for extreme debugging +//--- + +#include +#include +#include +#include +#include + +/* Linker script symbols - see core/start.c for details */ +extern char + brom, srom, + sgdata, sgbss, sdata, sbss, + btors, mtors, etors; + +/* bootlog_loaded() - Section loading stage */ +void bootlog_loaded(void) +{ + /* Version string - the string constant resides in ROM */ + uint32_t v = gint_version(); + const char *model = "gint #0.0-000"; + char str[14]; + + for(int i = 0; i < 14; i++) str[i] = model[i]; + + /* Quickly get the three digits of the build number */ + int x_q = (v & 0xffff) / 10; + int x_r = (v & 0xffff) - 10 * x_q; + int y_q = x_q / 10; + int y_r = x_q - 10 * y_q; + + str[5] = (v & 0xff000000) >> 24; + str[6] += (v & 0x00f00000) >> 20; + str[8] += (v & 0x000f0000) >> 16; + str[10] += y_q; + str[11] += y_r; + str[12] += x_r; + + /* Size of memory sections */ + uint32_t rom_size = (uint32_t)&srom; + uint32_t ram_size = (uint32_t)&sdata + (uint32_t)&sbss; + uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss; + + /* MPU type */ + mpu_t mpu = mpu_id(); + const char *names = "SH7337\0 SH7305\0 SH7355\0 SH7724"; + + /* TODO: Use a solid API for boot-time printing */ + Bdisp_AllClr_VRAM(); + print(1, 1, str); + print(15, 1, " Loaded"); + + if((uint)mpu < 4) print(16, 2, names + 8 * (mpu - 1)); + else print_dec(16, 2, mpu, 6); + + print(1, 2, "ROM RAM GINT"); + print(4, 3, "k c d"); + print_dec(1, 3, (rom_size + 0x3ff) >> 10, 3); + print_dec(6, 3, ram_size, 4); + print_dec(11, 3, gint_size, 4); + print_dec(17, 3, &mtors - &btors, 2); + print_dec(20, 3, &etors - &mtors, 2); + Bdisp_PutDisp_DD(); +} + +/* bootlog_mapped() - ROM mapping stage */ +void bootlog_mapped(int rom, int ram) +{ + rom = (rom + 0x3ff) >> 10; + ram = (ram + 0x3ff) >> 10; + + print(15, 1, " Mapped"); + print(1, 4, "MMU ROM: k RAM: k"); + (rom < 0) ? print(9, 4, "???") : print_dec(9, 4, rom, 3); + (ram < 0) ? print(18, 4, "???") : print_dec(18, 4, ram, 3); + Bdisp_PutDisp_DD(); +} + +/* bootlog_kernel() - Gint loading stage */ +void bootlog_kernel(void) +{ + if(isSH3()) + { + #ifdef FX9860G + extern sh7705_intc_t INTC3; + + print(1, 5, "ABCD"); + print_hex( 6, 5, INTC3._.IPRA->word, 4); + print_hex(10, 5, INTC3._.IPRB->word, 4); + print_hex(14, 5, INTC3._.IPRC->word, 4); + print_hex(18, 5, INTC3._.IPRD->word, 4); + print(1, 6, "EFGH"); + print_hex( 6, 6, INTC3._.IPRE->word, 4); + print_hex(10, 6, INTC3._.IPRF->word, 4); + print_hex(14, 6, INTC3._.IPRG->word, 4); + print_hex(18, 6, INTC3._.IPRH->word, 4); + #endif + } + else + { + extern sh7305_intc_t INTC4; + + print(1, 5, "ACFG"); + print_hex( 6, 5, INTC4._->IPRA.word, 4); + print_hex(10, 5, INTC4._->IPRC.word, 4); + print_hex(14, 5, INTC4._->IPRF.word, 4); + print_hex(18, 5, INTC4._->IPRG.word, 4); + print(1, 6, "HJKL"); + print_hex( 6, 6, INTC4._->IPRH.word, 4); + print_hex(10, 6, INTC4._->IPRJ.word, 4); + print_hex(14, 6, INTC4._->IPRK.word, 4); + print_hex(18, 6, INTC4._->IPRL.word, 4); + } + + Bdisp_PutDisp_DD(); +} diff --git a/src/core/inth.S b/src/core/inth.S new file mode 100644 index 0000000..ef79caf --- /dev/null +++ b/src/core/inth.S @@ -0,0 +1,110 @@ +/* +** gint:core:inth - Interrupt handlers +** This file only contains the entry points because the gates are managed +** by device drivers. Each driver will supply its own interrupt handler +** blocks depending on its configuration. +*/ + + + .global _inth_entry_7305 + +#ifdef FX9860G + .global _inth_entry_7705 +#endif + + .section .gint.blocks, "ax" + .align 4 + +/* Interrupt handlers + + The .gint.blocks section consists of blocks of 32 bytes intended for mapping + into the VBR space (exception, TLB miss, and interrupt handlers). Each event + gate is linearly associated with a block number: + + block_id = (event_code - 0x380) / 0x20 + + This file provides entry points; drivers may provide their own interrupt + handlers, and store them in the .gint.blocks section for consistency. They + should be aware of the consequences of reordering the blocks into the VBR + space: + + - It is possible to map MPU-specific blocks at runtime, to avoid checking + the MPU each time an interrupt is handled; + - However, the blocks cannot rely on relative displacements or cross- + references unless their relative order is fully known. This happens with + the timer driver, and it works swimmingly; just be careful. */ + +/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */ + +_inth_entry_7305: + /* Get the event code from the INTEVT register */ + mov.l 1f, r0 + /* TODO: mov.l @r0, r0 */ + mov.l @r0, r4 + + /* Interrupt codes start at 0x400 */ + mov #4, r1 + shll8 r1 + sub r1, r0 + + /* Jump to a C routine (TODO: Remove this) */ + sts.l pr, @-r15 + mov.l 2f, r0 + jsr @r0 + nop + lds.l @r15+, pr + rte + nop + + /* Add the distance between nop and the first entry, and jump + add #16, r0 + braf r0 + nop */ + +2: .long _debug +1: .long 0xff000028 + +#ifdef FX9860G + +/* SH7705-TYPE INTERRUT HANDLER ENTRY - 32 BYTES */ + +_inth_entry_7705: + /* Get the event code from the INTEVT2 register */ + mov.l 1f, r0 + mov.l @r0, r0 /* r0 = old_code */ + + /* Translate the event code to SH4 format */ + mov.l 2f, r2 + mov #-5, r3 + shld r3, r0 /* r0 = old_code >> 5 */ + add #-32, r0 /* r0 = (old_code - 0x400) >> 5 */ + mov.b @(r0, r2), r0 /* r0 = (new_code - 0x400) >> 5 */ + mov #5, r3 + shld r3, r0 /* r0 = new_code - 0x400 */ + + /* Add the distance between nop and the first entry, and jump */ + add #8, r0 + braf r0 + nop + +1: .long 0xa4000000 /* INTEVT2 register */ +2: .long _inth_remap + + + .section .gint.data + .align 4 + +/* EVENT CODE TRANSLATION TABLE - 72 BYTES */ + +_inth_remap: + .byte 0x00, 0x01, 0x02, 0xfe, 0x34, 0x35, 0x36, 0xff + .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f + .byte 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0xff, 0xff + .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + .byte 0x20, 0x21, 0x22, 0x23, 0x28, 0x28, 0xff, 0x28 + .byte 0x48, 0x48, 0xff, 0x48, 0xff, 0xff, 0xff, 0xff + .byte 0xff, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff + .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + .byte 0x2d, 0x2d, 0xff, 0xff, 0x2d, 0x2d, 0xff, 0xff + +#endif diff --git a/src/core/memory.c b/src/core/memory.c new file mode 100644 index 0000000..029dd68 --- /dev/null +++ b/src/core/memory.c @@ -0,0 +1,107 @@ +#include +#include +#include + +static void memcpy4(uint32_t * restrict d, const void * restrict src, size_t n) +{ + int modulo = (uintptr_t)src & 3; + + /* Best case: perform 32-bit accesses only */ + if(!modulo) + { + const uint32_t *s = src; + while(n--) *d++ = *s++; + } + +#if 0 + /* Here's where SH-3 and SH-4A start working differently. SH-4A has a + 2-cycle 'movua' instruction to perform unaligned reads */ + else if(isSH4()) + { + uint32_t longword; + const uint32_t *s = src; + + while(n--) + { + __asm__( + "movua.l %1, %0" + : "=z"(longword) + : "m>"(*s) + ); + s++; + *d++ = longword; + } + } +#endif + + /* On SH-3, we can only hope that there is 2-alignment */ + else if(!(modulo & 1)) + { + const uint16_t *s = src; + uint16_t * restrict dst = (void *)d; + + while(n--) + { + *dst++ = *s++; + *dst++ = *s++; + } + } + + /* Or just perform the raw copy */ + else + { + const uint8_t *s = src; + uint8_t * restrict dst = (void *)d; + + while(n--) *dst++ = *s++; + } +} + +void *memcpy(void * restrict dst, const void * restrict src, size_t n) +{ + uint8_t *d = dst; + const uint8_t *s = src; + + /* Small areas: don't bother with complex methods */ + if(n < 32) + { + while(n--) *d++ = *s++; + return dst; + } + + /* Find a longword offset to perform word or longword operations */ + while((uintptr_t)d & 3) *d++ = *s++, n--; + + /* Perform the big, efficient copy */ + memcpy4((void *)d, s, (n >> 2)); + + size_t m = n & 3; + d += (n - m); + s += (n - m); + n = m; + + /* Copy around the last bytes */ + while(n--) *d++ = *s++; + return dst; +} + +void *_memmove(void *dst, const void *src, size_t n) +{ + // (same as memcpy, but heed for direction if areas overlap) + + // more complicated + // allocate a buffer aligned with destination (source would be ok too) + // read unaligned from source to buffer + // copy aligned from buffer to destination + return dst; +} + +int _memcmp(const void *s1, const void *s2, size_t n) +{ + return 0; +} + +void *_memset(void *s, int byte, size_t n) +{ + return s; +} diff --git a/src/core/mmu.c b/src/core/mmu.c new file mode 100644 index 0000000..291b831 --- /dev/null +++ b/src/core/mmu.c @@ -0,0 +1,42 @@ +//--- +// gint:core:mmu - MMU-related definitions +//--- + +#include + +/* utlb_addr() - get the P4 address of a UTLB address entry */ +INLINE const utlb_addr_t *utlb_addr(uint E) +{ + uint32_t addr = 0xf6000000 | ((E & 0x3f) << 8); + return (void *)addr; +} + +/* utlb_data() - get the P4 address of a UTLB data entry */ +INLINE const utlb_data_t *utlb_data(uint E) +{ + uint32_t addr = 0xf7000000 | ((E & 0x3f) << 8); + return (void *)addr; +} + +/* utlb_mapped_memory() - count amount of mapped memory */ +void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram) +{ + uint32_t rom = 0, ram = 0; + + for(int E = 0; E < 64; E++) + { + const utlb_addr_t *addr = utlb_addr(E); + const utlb_data_t *data = utlb_data(E); + if(!addr->V || !data->V) continue; + + int sz = ((data->SZ1 << 1) | data->SZ2) << 3; + int size = 1 << ((0x14100c0a >> sz) & 0xff); + + uint32_t src = addr->VPN << 10; + if(src >= 0x00300000 && src < 0x00380000) rom += size; + if(src >= 0x08100000 && src < 0x08180000) ram += size; + } + + if(p_rom) *p_rom = rom; + if(p_ram) *p_ram = ram; +} diff --git a/src/core/mpu.c b/src/core/mpu.c new file mode 100644 index 0000000..f571818 --- /dev/null +++ b/src/core/mpu.c @@ -0,0 +1,60 @@ +//--- +// gint:core:mpu - Runtime MPU detection +//--- + +#include +#include +#include + +/* This file is only useful on fx9860g machines because all fxcg50 are SH4 */ +#ifdef FX9860G + +/* Holds the name of the current MPU; initialized at startup by mpu_init() */ +GBSS const mpu_t mpu; + +/* mpu_detect() - detect the underlying MPU + Many thanks to Simon Lothar for relevant documentation. + + Processor Version Register (PVR) and Product Version Register (PRR) provide + info for SH-4-based MPUS; SH-3 based boards are detected and distinguished + by testing writable bits in the Port L Control Register (PLCR). + + Returns the detected MPU type, falling back on mpu_unknown */ +static mpu_t mpu_detect(void) +{ + /* Processor Version Register */ + volatile uint32_t *pvr = (void *)0xff000030; + /* Product Version Register */ + volatile uint32_t *prr = (void *)0xff000044; + /* Port L Control Register */ + volatile uint16_t *plcr = (void *)0xa4000114; + + /* Detecting SH-3-based MPUs by testing writable bits in PLCR */ + + uint16_t old = *plcr; + *plcr = 0xffff; + uint16_t tested = *plcr; + *plcr = old; + + if(tested == 0x00ff) return mpu_sh7337; + if(tested == 0x0fff) return mpu_sh7355; + + /* Check that we're dealing with an SH-4-based MPU */ + if((*pvr & 0xffffff00) != 0x10300b00) return mpu_unknown; + + /* Tell SH-4 MPUs by testing the product version register */ + + uint32_t ver = *prr & 0xfffffff0; + if(ver == 0x00002c00) return mpu_sh7305; + if(ver == 0x00002200) return mpu_sh7724; + + return mpu_unknown; +} + +/* mpu_init() - detect and save information about the underlying MPU */ +void mpu_init(void) +{ + const_cast(mpu, mpu_t) = mpu_detect(); +} + +#endif diff --git a/src/core/setup.c b/src/core/setup.c new file mode 100644 index 0000000..1335a63 --- /dev/null +++ b/src/core/setup.c @@ -0,0 +1,135 @@ +//--- +// gint:core:setup - Installing and unloading the library +//--- + +#include +#include +#include +#include + +/* Interrupt controllers */ + +#ifdef FX9860G +GDATA sh7705_intc_t INTC3 = { + .IPRS = { + (void *)0xfffffee2, (void *)0xfffffee4, + (void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a, + (void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004, + }, + .ICR1 = (void *)0xa4000010, +}; +#endif + +GDATA sh7305_intc_t INTC4 = { + .IPRS = (void *)0xa4080000, + .MSK = (void *)0xa4080080, + .MSKCLR = (void *)0xa40800c0, + .USERIMASK = (void *)0xa4700000, +}; + +/* VBR address, from the linker script */ +extern char gint_vbr; +/* System's VBR address */ +GBSS static uint32_t system_vbr; + +//--- +// Context system for gint's core +//--- + +typedef struct +{ + uint16_t iprs[12]; + +} PACKED(2) gint_core_ctx; + +/* gint_ctx_save() - save interrupt controller configuration + @arg ctx gint core context object */ +static void gint_ctx_save(gint_core_ctx *ctx) +{ + if(isSH3()) + { + #ifdef FX9860G + for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]); + #endif + } + else + { + for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i]; + } +} + +/* gint_ctx_restore() - restore interrupt controller configuration + @arg ctx gint core context object */ +static void gint_ctx_restore(gint_core_ctx *ctx) +{ + if(isSH3()) + { + #ifdef FX9860G + for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i]; + #endif + } + else + { + for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i]; + } +} + +//--- +// Initialization and unloading +//--- + +/* System context */ +GBSS static gint_core_ctx sys_ctx; + +/* lock() - lock interrupts to avoid receiving unsupported signals */ +static void lock(void) +{ + /* Just disable everything, drivers will enable what they support */ + if(isSH3()) + { + #ifdef FX9860G + for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000; + #endif + } + else + { + for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000; + } +} + +/* gint_install() - install and start gint */ +void gint_install(void) +{ + /* VBR address, provided by the linker script */ + uint32_t vbr = (uint32_t)&gint_vbr; + + /* Event handler entry points */ + extern void inth_entry_7705(void); + extern void inth_entry_7305(void); + + /* First save the hardware configuration. This step is crucial because + we don't want the system to find out about us directly manipulating + the peripheral modules */ + gint_ctx_save(&sys_ctx); + + /* Load the event handler entry points into memory */ + /* TODO: Load an exception handler and a TLB miss handler */ + + void *inth_entry = isSH3() ? inth_entry_7705 : inth_entry_7305; + memcpy((void *)(vbr + 0x600), inth_entry, 32); + + /* Time to switch VBR and roll! */ + system_vbr = gint_setvbr(vbr, lock); +} + +/* unlock() - unlock interrupts, restoring system settings */ +static void unlock(void) +{ + gint_ctx_restore(&sys_ctx); +} + +/* gint_unload() - unload gint and give back control to the system */ +void gint_unload(void) +{ + gint_setvbr(system_vbr, unlock); +} diff --git a/src/core/start.c b/src/core/start.c new file mode 100644 index 0000000..48c0445 --- /dev/null +++ b/src/core/start.c @@ -0,0 +1,211 @@ +//--- +// gint:core:start - Kernel initialization and C runtime +//-- + +#include +#include +#include +#include +#include +#include +#include + +/* Symbols provided by the linker script. For sections: + - l* represents the load address (source address in ROM) + - s* represents the size of the section + - r* represents the relocation address (destination address in RAM) + gint's BSS section is not mentioned here because it's never initialized */ +extern uint32_t + brom, srom, /* Limits of ROM mappings */ + lgdata, sgdata, rgdata, /* gint's data section */ + ldata, sdata, rdata, /* User's data section */ + sbss, rbss, /* User's BSS section */ + btors, mtors, etors; /* Constructor/destructor arrays */ +extern gint_driver_t + bdrv, edrv; /* Driver table */ + +/* User-provided main() function */ +int main(int isappli, int optnum); + +/* regcpy() - copy a memory region using symbol information + @l Source pointer (load address) + @s Size of area (should be a multiple of 16) + @r Destination pointer (relocation address) */ +SECTION(".pretext") +static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r) +{ + while(s > 0) + { + *r++ = *l++; + *r++ = *l++; + *r++ = *l++; + *r++ = *l++; + s -= 16; + } +} +#define regcpy(l, s, r) regcpy(&l, (int32_t)&s, &r) + +/* regclr() - clear a memory region using symbol information + @r Source pointer (base address) + @s Size of area (should be a multiple of 16) */ +SECTION(".pretext") +static void regclr(uint32_t *r, int32_t s) +{ + while(s > 0) + { + *r++ = 0; + *r++ = 0; + *r++ = 0; + *r++ = 0; + s -= 16; + } +} +#define regclr(r, s) regclr(&r, (int32_t)&s) + +/* explore() - read data from a memory region to get it mapped by the system + This function accesses all the 1k-blocks between b and b + s. This + corresponds to region [b; b+s) when b and s are 1k-aligned. + @b Base pointer: first address checked + @s Size of region */ +SECTION(".pretext") +static void explore(volatile void *b, int32_t s) +{ + ATTR(unused) uint8_t x; + + while(s > 0) + { + x = *(volatile uint8_t *)b; + b += 1024; + s -= 1024; + } +} +#define explore(b, s) explore(&b, (int32_t)&s) + +/* acall() - call an array of functions (constructors or destructors) + @f First element of array + @l First element outside of the array */ +SECTION(".pretext") +static void acall(void (**f)(void), void (**l)(void)) +{ + while(f < l) (*(*f++))(); +} +#define acall(f, l) acall((void (**)(void))&f, (void (**)(void))&l) + + +/* start() - this is where it all starts + Returns a status code. Invoking main menu is better than returning! */ +SECTION(".pretext.entry") +int start(int isappli, int optnum) +{ + /* We are currently in a dynamic userspace mapping of an add-in run + from the storage memory. We are running in privileged mode with one + golden rule: + + Do not disturb the operating system. + + gint will silently run in an isolated part of the memory (fx9860g) + or at the start of the RAM (fxcg50). It acts as a kernel, + redirecting interrupts and reimplementing drivers, so we can't rely + too much on the system. Ladies and gentlemen, let's have fun! ;D */ + + /* For now, we rely on the system to map the ROM pages. RAM is always + fully mapped, but we need to initialize it. We also need to do some + hardware detection because old fx9860g models have an older + processor with some incompatible features */ + + /* Detect architecture - this will tell SH3 from SH4 on fx9860g */ + #ifdef FX9860G + mpu_init(); + #endif + + + /* Load data sections and wipe the bss section. This has to be done + first for static and global variables to be initialized */ + regcpy(lgdata, sgdata, rgdata); + regcpy(ldata, sdata, rdata); + regclr(rbss, sbss); + + #ifdef GINT_BOOT_LOG + bootlog_loaded(); + #endif + + /* TODO: Do the TLB investigation on SH3 (UTLB things are SH4 only) */ + + /* Traverse all ROM pages */ + explore(brom, srom); + /* Count how much memory got mapped from this process */ + uint32_t rom, ram; + utlb_mapped_memory(&rom, &ram); + + //--- + + #ifdef GINT_BOOT_LOG + isSH3() ? bootlog_mapped(-1, -1) + : bootlog_mapped(rom, ram); + #endif + + /* Cancel add-in execution if not all pages are mapped + TODO: Resort to better graphical display */ + if(rom < (uint32_t)&srom) + { + Bdisp_AllClr_VRAM(); + print(1, 1, "Missing memory!"); + print_hex(1, 2, rom, 8); + print(1, 3, "<"); + print_hex(1, 4, (uint32_t)&srom, 8); + Bdisp_PutDisp_DD(); + delay(20); + return 1; + } + + //--- + + /* Install gint and switch VBR */ + gint_install(); + + #ifdef GINT_BOOT_LOG + bootlog_kernel(); + #endif + + /* We are now running on our own in kernel mode. To avoid breaking the + system, we want to limit our use of syscalls, so we'll have to do + device driving ourselves. */ + + gint_driver_t *drv; + + /* Initialize all drivers by saving the system settings */ + for(drv = &bdrv; drv < &edrv; drv++) + { + drv->ctx_save(drv->sys_ctx); + } + + /* With gint fully initialized, we are ready to start the hosted user + application. We have already loaded the RAM sections earlier; all + that's left is calling the constructors and rolling main(). */ + + /* Call the constructors */ + acall(btors, mtors); + + /* Execute the user application */ + int ret = main(isappli, optnum); + + /* Call the destructors*/ + acall(mtors, etors); + + /* Before leaving the application, we need to clean up our mess. We + have changed many OS settings while accessing the peripheral + modules. The OS is bound to be confused (and crash) if we don't + resoture them. */ + + /* Restore all driver settings */ + for(drv = &drv; drv < &edrv; drv++) + { + drv->ctx_restore(drv->sys_ctx); + } + + /* Finally, unload gint and give back control to the system */ + gint_unload(); + + /* TODO: Invoke main menu instead of returning? */ + return ret; +} diff --git a/src/core/vbr.s b/src/core/vbr.s new file mode 100644 index 0000000..463db39 --- /dev/null +++ b/src/core/vbr.s @@ -0,0 +1,50 @@ +/* +** gint:core:vbr - Assembler-level VBR management +*/ + + .global _gint_setvbr + .text + .align 2 + +/* gint_setvbr() + Changes the VBR address and the calls the configuration function while + interrupts are disabled. The configuration function must change either the + priority registers or the interrupt masks, and make sure that all the + interrupts that it leaves enabled are handled by the new VBR handlers. + + @arg vbr New VBR address (uint32_t) + @arg configure Configuration function (void -> void) + Returns the previous VBR address. */ +_gint_setvbr: + mov.l r9, @-r15 + mov.l r8, @-r15 + sts.l pr, @-r15 + + mov #1, r8 + mov #28, r0 + shld r0, r8 + + /* Block all interrupts */ + stc sr, r0 + or r8, r0 + ldc r0, sr + + /* Set the new VBR address */ + stc vbr, r9 + ldc r4, vbr + + /* Call the configuration function */ + jsr @r5 + nop + + /* Enable interrupts again */ + stc sr, r0 + not r8, r8 + and r8, r0 + ldc r0, sr + + lds.l @r15+, pr + mov.l @r15+, r8 + mov r9, r0 + rts + mov.l @r15+, r9 diff --git a/src/r61524/r61524.c b/src/r61524/r61524.c new file mode 100644 index 0000000..461e66e --- /dev/null +++ b/src/r61524/r61524.c @@ -0,0 +1,341 @@ +//--- +// gint:r61524 - Renesas R61524 driver +//--- + +#include +#include + +#ifdef FXCG50 + +//--- +// Device specification sheet +//--- + +/* Registers and operations */ +enum { + device_code_read = 0x000, + driver_output_control = 0x001, + entry_mode = 0x003, + display_control_2 = 0x008, + low_power_control = 0x00b, + + ram_address_horizontal = 0x200, + ram_address_vertical = 0x201, + write_data = 0x202, + + horizontal_ram_start = 0x210, + horizontal_ram_end = 0x211, + vertical_ram_start = 0x212, + vertical_ram_end = 0x213, +}; + +typedef word_union(entry_mode_t, + uint TRI :1; + uint DFM :1; + uint :1; + uint BGR :1; + uint :2; + uint HWM :1; + uint :1; + uint ORG :1; + uint :1; + uint ID :2; + uint AM :1; + uint :1; + uint EPF :2; +); + +//--- +// Device communication primitives +//--- + +#define synco() __asm__ volatile ("synco":::"memory") + +/* Interface with the controller */ +GDATA static volatile uint16_t *intf = (void *)0xb4000000; +/* Bit 4 of Port R controls the RS bit of the display driver */ +GDATA static volatile uint8_t *PRDR = (void *)0xa405013c; + +INLINE static void select(uint16_t reg) +{ + /* Clear RS and write the register number */ + *PRDR &= ~0x10; + synco(); + *intf = reg; + synco(); + + /* Set RS back. We don't do this in read()/write() because the display + driver is optimized for consecutive GRAM access. LCD-transfers will + be faster when executing select() followed by many write(). + (Most applications should use DMA though.) */ + *PRDR |= 0x10; + synco(); +} + +INLINE static uint16_t read(void) +{ + return *intf; +} + +INLINE static void write(uint16_t data) +{ + *intf = data; +} + +//--- +// Driver functions +//--- + +void pixel(int ha, int va, int color) +{ + select(ram_address_horizontal); + write(ha); + + select(ram_address_vertical); + write(va); + + select(write_data); + write(color); +} + +void r61524_test(void) +{ + uint16_t device_name; + uint16_t doc; + int SM, SS; + entry_mode_t em; + uint16_t dc2; + int FP, BP; + uint16_t lpc; + int VEM, COL; + + uint32_t AD; + uint16_t HSA, HEA; + uint16_t VSA, VEA; + + //--- + + select(device_code_read); + device_name = read(); + + Bdisp_AllClr_VRAM(); + print(1, 1, "Name=????"); + print_hex(6, 1, device_name, 4); + + if(device_name != 0x1524) + { + print(1, 2, "Aborting."); + Bdisp_PutDisp_DD(); + delay(20); + return; + } + + //--- + + select(driver_output_control); + doc = read(); + SM = (doc >> 10) & 1; + SS = (doc >> 8) & 1; + + select(entry_mode); + em.word = read(); + + select(display_control_2); + dc2 = read(); + FP = (dc2 >> 8) & 0xf; + BP = dc2 & 0xf; + + select(low_power_control); + lpc = read(); + VEM = (lpc >> 4) & 1; + COL = lpc & 1; + + /* Shift by 9, not 8, because of horizontal/vertical range inversion */ + select(ram_address_horizontal); + AD = read(); + select(ram_address_vertical); + AD |= read() << 9; + + select(horizontal_ram_start); + HSA = read(); + select(horizontal_ram_end); + HEA = read(); + + select(vertical_ram_start); + VSA = read(); + select(vertical_ram_end); + VEA = read(); + + //--- + + print(15, 4, " SM=?"); + print_hex(19, 4, SM, 1); + print(15, 5, " SS=?"); + print_hex(19, 5, SS, 1); + + print(1, 2, "TRI=? DFM=? BGR=?"); + print_hex(5, 2, em.TRI, 1); + print_hex(12, 2, em.DFM, 1); + print_hex(19, 2, em.BGR, 1); + print(1, 3, "HWM=? ORG=? ID=?"); + print_hex(5, 3, em.HWM, 1); + print_hex(12, 3, em.DFM, 1); + print_hex(19, 3, em.ID, 1); + print(1, 4, " AM=? EPF=?"); + print_hex(5, 4, em.AM, 1); + print_hex(12, 4, em.EPF, 1); + + print(1, 5, " FP=? BP=?"); + print_hex(5, 5, FP, 1); + print_hex(12, 5, BP, 1); + + print(1, 6, "VEM=? COL=?"); + print_hex(5, 6, VEM, 1); + print_hex(12, 6, COL, 1); + + Bdisp_PutDisp_DD(); + delay(10); + + //--- + + Bdisp_AllClr_VRAM(); + + print(1, 1, "Address=?????"); + print_hex(9, 1, AD, 5); + + print(1, 2, "HSA=??? HEA=???"); + print_hex(5, 2, HSA, 3); + print_hex(14, 2, HEA, 3); + print(1, 3, "VSA=??? VEA=???"); + print_hex(5, 3, VSA, 3); + print_hex(14, 3, VEA, 3); + + Bdisp_PutDisp_DD(); + delay(20); + + //--- + + select(horizontal_ram_start); + write(0); + select(horizontal_ram_end); + write(395); + + select(vertical_ram_start); + write(0); + select(vertical_ram_end); + write(223); + + //--- + +// Bdisp_AllClr_VRAM(); +// Bdisp_PutDisp_DD(); + + select(ram_address_horizontal); + write(HEA); + select(ram_address_vertical); + write(VSA); + select(write_data); + + uint16_t color = 0xf3e4; + for(int v = 0; v < 396; v++) + for(int h = 0; h < 224; h++) + { + color = (v << 7) ^ h ^ 0x3754; + write(color); + } + + delay(60); + + //--- + + /* Shift by 9, not 8, because of horizontal/vertical range inversion */ + select(ram_address_horizontal); + AD = read(); + select(ram_address_vertical); + AD |= read() << 9; + + select(horizontal_ram_start); + HSA = read(); + select(horizontal_ram_end); + HEA = read(); + + select(vertical_ram_start); + VSA = read(); + select(vertical_ram_end); + VEA = read(); + + Bdisp_AllClr_VRAM(); + + print(1, 1, "Address=?????"); + print_hex(9, 1, AD, 5); + + print(1, 2, "HSA=??? HEA=???"); + print_hex(5, 2, HSA, 3); + print_hex(14, 2, HEA, 3); + print(1, 3, "VSA=??? VEA=???"); + print_hex(5, 3, VSA, 3); + print_hex(14, 3, VEA, 3); + + Bdisp_PutDisp_DD(); + delay(10); +} + +//--- +// Context system for this driver +//--- + +typedef struct +{ + /* Graphics RAM range */ + uint16_t HSA, HEA, VSA, VEA; + +} PACKED(2) ctx_t; + +/* Allocate one buffer in gint's storage section */ +GBSS static ctx_t sys_ctx; + +static void ctx_save(void *buf) +{ + ctx_t *ctx = buf; + + select(horizontal_ram_start); + ctx->HSA = read(); + select(horizontal_ram_end); + ctx->HEA = read(); + + select(vertical_ram_start); + ctx->VSA = read(); + select(vertical_ram_end); + ctx->VEA = read(); +} + +static void ctx_restore(void *buf) +{ + ctx_t *ctx = buf; + + select(horizontal_ram_start); + write(ctx->HSA); + select(horizontal_ram_end); + write(ctx->HEA); + + select(vertical_ram_start); + write(ctx->VSA); + select(vertical_ram_end); + write(ctx->VEA); +} + +//--- +// Driver structure definition +//--- + +gint_driver_t drv_r61524 = { + .name = "Renesas R61524", + .ctx_size = sizeof(ctx_t), + .sys_ctx = &sys_ctx, + .ctx_save = ctx_save, + .ctx_restore = ctx_restore, +}; + +GINT_DECLARE_DRIVER(drv_r61524); + +#endif /* FXCG50 */ diff --git a/src/t6k11/t6k11.c b/src/t6k11/t6k11.c new file mode 100644 index 0000000..97207bc --- /dev/null +++ b/src/t6k11/t6k11.c @@ -0,0 +1,209 @@ +//--- +// gint:t6k11 - Toshiba T6K11 driver +//--- + +#include +#include + +#include +#include +#include + +#ifdef FX9860G + +//--- +// Device specification sheet +//--- + +/* Screen registers. Registers 8..11 and 13..15 must not be used! */ +enum { + reg_display = 0, + reg_counter = 1, + reg_analog = 2, + reg_alternate = 3, + + reg_yaddr = 4, /* These two use the same register */ + reg_xaddr = 4, /* (interpretation depends on count mode) */ + + reg_zaddr = 5, + reg_contrast = 6, + reg_data = 7, + reg_daconv = 12, +}; + +/* Automatic counter increment during read/write */ +enum { + cnt_ydown = 0, + cnt_yup = 1, + cnt_xdown = 2, + cnt_xup = 3, +}; + +//--- +// Device communication primitives +//--- + +/* I/O may be performed either with RS = 0 or RS = 1. The memory-mapping of the + device I/O maps bit 16 of the address to pin RS. There may be other mapped + pins in the address. (?) */ + +/* RS = 0: Register selection */ +GDATA static volatile uint8_t *sel = (void *)0xb4000000; +/* RS = 1: Command data or vram data */ +GDATA static volatile uint8_t *cmd = (void *)0xb4010000; + +/* command() - send a command to set the value of a register + @reg Register number + @data Value to set in reg */ +INLINE static void command(uint8_t reg, uint8_t data) +{ + *sel = reg; + *cmd = data; +} + +/* status() - read the status byte from the display driver */ +INLINE static uint8_t status(void) +{ + return *sel; +} + +/* write_row() - send 16 bytes to the display driver + @buf Buffer to take data from */ +INLINE static void write_row(const uint8_t *buf) +{ + *sel = reg_data; + + /* Unroll the loop for more speed */ + *cmd = *buf++; + *cmd = *buf++; + *cmd = *buf++; + *cmd = *buf++; + + *cmd = *buf++; + *cmd = *buf++; + *cmd = *buf++; + *cmd = *buf++; + + *cmd = *buf++; + *cmd = *buf++; + *cmd = *buf++; + *cmd = *buf++; + + *cmd = *buf++; + *cmd = *buf++; + *cmd = *buf++; + *cmd = *buf++; +} + +//--- +// Driver functions +//--- + +/* t6k11_display() - send vram data to the LCD device */ +void t6k11_display(const void *vram, int y1, int y2, size_t stride) +{ + for(int y = y1; y < y2; y++) + { + /* Set the X-address register for this row */ + command(reg_xaddr, y | 0xc0); + + /* Use Y-Up mode */ + command(reg_counter, cnt_yup); + + /* Start counting Y from 0 */ + command(reg_yaddr, 0); + + /* Send the row's data to the device */ + write_row(vram); + vram += stride; + } +} + +/* t6k11_contrast() - change the contrast setting */ +void t6k11_contrast(int contrast) +{ + if(contrast < 0) contrast = 0; + if(contrast > 32) contrast = 32; + + /* Reasonable values for contrast, taken from the diagnostic mode of an + fx9860G II with OS 02.05.2201, are in range 0x97 .. 0xb7. + This means VLC0 = 2 and CONTRAST in [23..55] */ + command(reg_contrast, 0x97 + contrast); +} + +/* t6k11_backlight() - manage the screen backlight */ +void t6k11_backlight(int setting) +{ + volatile uint8_t *port; + uint8_t mask; + + /* This setting is mapped to an I/O port: + - On SH3, bit 7 of port G + - On SH4, bit 4 of port N */ + if(isSH3()) + { + port = (void *)0xa400012c; + mask = 0x80; + } + else + { + port = (void *)0xa4050138; + mask = 0x10; + } + + if(!setting) *port &= ~mask; + if(setting > 0) *port |= mask; + if(setting < 0) *port ^= mask; +} + +//--- +// Context system for this driver +//--- + +typedef struct +{ + /* Some status bits, obtained by using the STRD command */ + uint8_t strd; + + /* There *are* other parameters that are affected by the driver, but + they cannot be read, so I can't determine the system's setting */ + +} PACKED(1) ctx_t; + +/* Pre-allocate a context in gint's uninitialized section */ +GBSS static ctx_t sys_ctx; + +static void ctx_save(void *buf) +{ + ctx_t *ctx = buf; + ctx->strd = status(); +} + +static void ctx_restore(void *buf) +{ + ctx_t *ctx = buf; + + /* Set an X-address of 0 with the original display mode */ + uint8_t nf = (ctx->strd & 0x04) >> 2; + command(reg_xaddr, 0x80 | (nf << 6)); + + /* Restore the counter mode */ + uint8_t cnt = (ctx->strd & 0x03); + command(reg_counter, cnt); +} + +//--- +// Driver structure definition +//--- + +gint_driver_t drv_t6k11 = { + .name = "Toshiba T6K11", + .ctx_size = sizeof(ctx_t), + .sys_ctx = &sys_ctx, + .ctx_save = ctx_save, + .ctx_restore = ctx_restore, +}; + +GINT_DECLARE_DRIVER(drv_t6k11); + +#endif /* FX9860G */ From 48b552b295c5c0ee7d9ba959f709998328f11ca9 Mon Sep 17 00:00:00 2001 From: lephe Date: Wed, 1 Aug 2018 20:41:36 +0200 Subject: [PATCH 23/24] More interrupt system, exceptions, timers. --- Makefile | 8 +- configure | 26 ++- fx9860g.ld | 3 + fxcg50.ld | 13 +- include/core/bootlog.h | 4 +- include/core/intc.h | 341 +++++++++++------------------- include/core/memory.h | 22 ++ include/core/mmu.h | 64 +++++- include/core/mpu.h | 14 +- include/defs/attributes.h | 3 + include/drivers/t6k11.h | 7 +- include/gint/clock.h | 8 + include/gint/display-fx.h | 105 ++++++++++ include/gint/drivers.h | 33 +++ include/gint/gint.h | 64 ++++++ include/gint/timer.h | 157 ++++++++++++++ src/clock/freq.c | 0 src/core/bootlog.c | 23 +- src/core/exch.S | 32 +++ src/core/gint.c | 64 ++++++ src/core/inth.S | 48 +++-- src/core/memory.c | 15 +- src/core/mmu.c | 55 ++++- src/core/setup.c | 98 ++++----- src/core/start.c | 34 ++- src/r61524/r61524.c | 65 ++---- src/t6k11/t6k11.c | 12 +- src/tmu/inth.s | 225 ++++++++++++++++++++ src/tmu/tmu.c | 429 ++++++++++++++++++++++++++++++++++++++ 29 files changed, 1568 insertions(+), 404 deletions(-) create mode 100644 include/core/memory.h create mode 100644 include/gint/clock.h create mode 100644 include/gint/timer.h create mode 100644 src/clock/freq.c create mode 100644 src/core/exch.S create mode 100644 src/core/gint.c create mode 100644 src/tmu/inth.s create mode 100644 src/tmu/tmu.c diff --git a/Makefile b/Makefile index ff5264b..ca1ac0b 100755 --- a/Makefile +++ b/Makefile @@ -16,12 +16,12 @@ cfg := config/Makefile.cfg cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg)) cf-fx := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \ - -I include $(cfg_macros) + -fstrict-volatile-bitfields -I include $(cfg_macros) cf-cg := -m4 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \ - -I include $(cfg_macros) + -fstrict-volatile-bitfields -I include $(cfg_macros) -# On fx9860g, use the sh3eb-elf toolchain; on fxcg50, prefer sh4eb-elf -toolchain := $(if $(filter $(cfg_target),fx),sh3eb-elf,sh4eb-elf) +# On fx9860g, use the sh3eb-elf toolchain; on fxcg50, prefer sh4eb-nofpu-elf +toolchain := $(if $(filter $(cfg_target),fx),sh3eb-elf,sh4eb-nofpu-elf) # Compiler flags, assembler flags, dependency generation, archiving cflags := $(cf-$(cfg_target)) diff --git a/configure b/configure index b1be97f..d97157a 100755 --- a/configure +++ b/configure @@ -12,6 +12,7 @@ conf_target= # Behavior conf[GINT_BOOT_LOG]= conf[GINT_NO_SYSCALLS]= +cong[GINT_LAX]= conf[GINT_EXTENDED_LIBC]= conf[GINT_STATIC_GRAY]= @@ -45,28 +46,31 @@ Platform settings (specify exactly one): Target platform is fx-9860G II: all monochrome models that support add-ins or can be flashed to support them. ${Cg}-fxcg50$C0 - Target platform is fx-CG50. + Target platform is fx-CG 50; there is some compatibility with fx-CG 10/20. -Options that affect the behavior of the library: - $Cr-boot-log $C_[default:$Cp not specified$C_]$C0 +Options that affect the behavior of the library $C_[${Cp}default$C_]$C0: + $Cr-boot-log $C_[${Cp}disabled$C_]$C0 Enable an on-screen log at startup if a key is kept pressed while launching the add-in, allowing easy debug and crash diagnoses. - $Cr-no-syscalls $C_[default:$Cp not specified$C_]$C0 + $Cr-no-syscalls $C_[${Cp}disabled$C_]$C0 Never use syscalls. Expect trouble with malloc() and the gray engine. Do not trigger this switch unless you know what you are doing. - $Cr-extended-libc $C_[default:$Cp not specified$C_]$C0 + $Cr-lax $C_[${Cp}disabled$C_]$C0 + Make more assumptions about functions parameters. This disables coordinate + checks in drawing functions. Be careful! + $Cr-extended-libc $C_[${Cp}disabled$C_]$C0 Enable specific C99 headers/features that are normally not required by calculator programs. This may allow porting programs from other platforms. - $Cr-static-gray-engine $C_[default:$Cp not specified$C_]$C0 + $Cr-static-gray-engine $C_[${Cp}disabled$C_]$C0 Place the gray engine vram in static ram instead of using the heap. Always use this option when using both the gray engine and -no-syscalls. -Options that customize size limits: - $Cr-atexit-max$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0 +Options that customize size limits $C_[${Cp}default$C_]$C0: + $Cr-atexit-max$C0=${Cg}integer$C_ [${Cp}16$C_]$C0 Number of exit handlers that can be registered by atexit(). - $Cr-timer-slots$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0 + $Cr-timer-slots$C0=${Cg}integer$C_ [${Cp}16$C_]$C0 Number of virtual timers that may be registered at the same time. - $Cr-events-queue-size$C0=${Cg}integer$C_ [default:$Cp 64$C_]$C0 + $Cr-events-queue-size$C0=${Cg}integer$C_ [${Cp}64$C_]$C0 Number of events simultaneously stored in the event queue. EOF @@ -88,6 +92,7 @@ for arg; do case "$arg" in -boot-log) conf[GINT_BOOT_LOG]=true;; -no-syscalls) conf[GINT_NO_SYSCALLS]=true;; + -lax) conf[GINT_LAX]=true;; -extended-libc) conf[GINT_EXTENDED_LIBC]=true;; -static-gray-engine) conf[GINT_STATIC_GRAY]=true;; @@ -142,6 +147,7 @@ output_config() [ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG" [ "${conf[GINT_NO_SYSCALLS]}" ] && echo -n " -DGINT_NO_SYSCALLS" + [ "${conf[GINT_LAX]}" ] && echo -n " -DGINT_LAX" [ "${conf[GINT_EXTENDED_LIBC]}" ] && echo -n " -DGINT_EXTENDED_LIBC" [ "${conf[GINT_STATIC_GRAY]}" ] && echo -n " -DGINT_STATIC_GRAY" diff --git a/fx9860g.ld b/fx9860g.ld index dce9c53..dba5111 100644 --- a/fx9860g.ld +++ b/fx9860g.ld @@ -173,11 +173,14 @@ SECTIONS */ /* Unwanted sections going to meet Dave Null: + - Debug sections, often come out of libgcc - Java classes registration (why are there even here?) - Asynchronous unwind tables: no C++ exception handling for now ^^ - Comments or anything the compiler might put in its assembler - A stack section sometimes generated for build/version.o */ /DISCARD/ : { + *(.debug_info .debug_abbrev .debug_loc .debug_aranges + .debug_ranges .debug_line .debug_str) *(.jcr) *(.eh_frame_hdr) *(.eh_frame) diff --git a/fxcg50.ld b/fxcg50.ld index 9b13ad8..c35132d 100644 --- a/fxcg50.ld +++ b/fxcg50.ld @@ -118,15 +118,15 @@ SECTIONS /* ** gint-related sections - ** 88160000:4k VBR space - ** 88161000:4k .gint.data and .gint.bss + ** 8c160000:4k VBR space + ** 8c161000:4k .gint.data and .gint.bss */ /* VBR address: let's just start at the beginning of the RAM area. There's an unused 0x100-byte gap at the start of the VBR space. The VBR space is already a large block (> 2 kiB), so I'm cutting off the gap to spare some memory */ - _gint_vbr = 0x8815ff00; + _gint_vbr = ORIGIN(vbr) - 0x100; . = ORIGIN(rram); @@ -161,10 +161,17 @@ SECTIONS */ /* Unwanted sections going to meet Dave Null: + - SH3-only data sections + - Debug sections, often come out of libgcc - Java classes registration (why are there even here?) - Asynchronous unwind tables: no C++ exception handling for now ^^ - Comments or anything the compiler might put in its assembler */ /DISCARD/ : { + *(.gint.bss.sh3) + *(.gint.data.sh3) + + *(.debug_info .debug_abbrev .debug_loc .debug_aranges + .debug_ranges .debug_line .debug_str) *(.jcr) *(.eh_frame_hdr) *(.eh_frame) diff --git a/include/core/bootlog.h b/include/core/bootlog.h index 152bdea..8b65247 100644 --- a/include/core/bootlog.h +++ b/include/core/bootlog.h @@ -5,7 +5,7 @@ #ifndef GINT_CORE_BOOTLOG #define GINT_CORE_BOOTLOG -/* bootlog_loaded() - Section loading stage +/* bootlog_loaded() - section loading stage Called when RAM sections have been wiped and copied */ void bootlog_loaded(void); @@ -16,7 +16,7 @@ void bootlog_loaded(void); @ram Amount of mapped RAM, in bytes */ void bootlog_mapped(uint32_t rom, uint32_t ram); -/* bootlog_kernel() - Gint loading stage +/* bootlog_kernel() - gint loading stage Called when gint has been installed, the VBR switched and the interrupt handlers set up. */ void bootlog_kernel(void); diff --git a/include/core/intc.h b/include/core/intc.h index f660619..255f7f8 100644 --- a/include/core/intc.h +++ b/include/core/intc.h @@ -28,75 +28,75 @@ typedef struct { volatile word_union(*IPRA, - uint TMU0 :4; /* Timer 0 */ - uint TMU1 :4; /* Timer 1 */ - uint TMU2 :4; /* Timer 2 */ - uint RTC :4; /* Real-Time Clock */ + uint16_t TMU0 :4; /* Timer 0 */ + uint16_t TMU1 :4; /* Timer 1 */ + uint16_t TMU2 :4; /* Timer 2 */ + uint16_t RTC :4; /* Real-Time Clock */ ); volatile word_union(*IPRB, - uint WDT :4; /* Watchdog Timer */ - uint REF :4; /* BSC Refresh Request, SDRAM (?) */ - uint :4; - uint :4; + uint16_t WDT :4; /* Watchdog Timer */ + uint16_t REF :4; /* BSC Refresh Request, SDRAM (?) */ + uint16_t :4; + uint16_t :4; ); volatile word_union(*IPRC, - uint IRQ3 :4; /* Interrupt request 3 */ - uint IRQ2 :4; /* Interrupt request 2 */ - uint IRQ1 :4; /* Interrupt request 1 */ - uint IRQ0 :4; /* Interrupt request 0 */ + uint16_t IRQ3 :4; /* Interrupt request 3 */ + uint16_t IRQ2 :4; /* Interrupt request 2 */ + uint16_t IRQ1 :4; /* Interrupt request 1 */ + uint16_t IRQ0 :4; /* Interrupt request 0 */ ); volatile word_union(*IPRD, - uint PINT0_7 :4; /* External interrupt pins 0 to 7 */ - uint PINT8_15 :4; /* External interrupt pins 8 to 15 */ - uint IRQ5 :4; /* Interrupt request 5 */ - uint IRQ4 :4; /* Interrupt request 4 */ + uint16_t PINT0_7 :4; /* External interrupt pins 0 to 7 */ + uint16_t PINT8_15 :4; /* External interrupt pins 8 to 15 */ + uint16_t IRQ5 :4; /* Interrupt request 5 */ + uint16_t IRQ4 :4; /* Interrupt request 4 */ ); volatile word_union(*IPRE, - uint DMAC :4; /* Direct Memory Access Controller */ - uint SCIF0 :4; /* Serial Communication Interface 0 */ - uint SCIF2 :4; /* Serial Communication Interface 2 */ - uint ADC :4; /* Analog/Decimal Converter */ + uint16_t DMAC :4; /* Direct Memory Access Controller */ + uint16_t SCIF0 :4; /* Serial Communication Interface 0 */ + uint16_t SCIF2 :4; /* Serial Communication Interface 2 */ + uint16_t ADC :4; /* Analog/Decimal Converter */ ); volatile word_union(*IPRF, - uint :4; - uint :4; - uint USB :4; /* USB Controller */ - uint :4; + uint16_t :4; + uint16_t :4; + uint16_t USB :4; /* USB Controller */ + uint16_t :4; ); volatile word_union(*IPRG, - uint TPU0 :4; /* Timer Pulse Unit 0 */ - uint TPU1 :4; /* Timer Pulse Unit 1 */ - uint :4; - uint :4; + uint16_t TPU0 :4; /* Timer Pulse Unit 0 */ + uint16_t TPU1 :4; /* Timer Pulse Unit 1 */ + uint16_t :4; + uint16_t :4; ); volatile word_union(*IPRH, - uint TPU2 :4; /* Timer Pulse Unit 2 */ - uint TPU3 :4; /* Timer Pulse Unit 3 */ - uint :4; - uint :4; + uint16_t TPU2 :4; /* Timer Pulse Unit 2 */ + uint16_t TPU3 :4; /* Timer Pulse Unit 3 */ + uint16_t :4; + uint16_t :4; ); } PACKED(4) sh7705_intc_ipc_t; /* sh7705_intc_icr1_t - Interrupt Control Register 1 (general) */ -typedef word_union(sh7705_intc_icr1_t, - uint MAI :1; /* Mask All Interrupts */ - uint IRQLVL :1; /* Interrupt Request Level Detect */ - uint BLMSK :1; /* Enable NMI when BL is set */ - uint :1; - uint IRQ5E :2; /* IRQ 5 Edge Detection */ - uint IRQ4E :2; /* etc. */ - uint IRQ3E :2; - uint IRQ2E :2; - uint IRQ1E :2; - uint IRQ0E :2; +typedef volatile word_union(sh7705_intc_icr1_t, + uint16_t MAI :1; /* Mask All Interrupts */ + uint16_t IRQLVL :1; /* Interrupt Request Level Detect */ + uint16_t BLMSK :1; /* Enable NMI when BL is set */ + uint16_t :1; + uint16_t IRQ5E :2; /* IRQ 5 Edge Detection */ + uint16_t IRQ4E :2; /* etc. */ + uint16_t IRQ3E :2; + uint16_t IRQ2E :2; + uint16_t IRQ1E :2; + uint16_t IRQ0E :2; ); /* sh7705_intc_t - the SH7705 interrupt controller */ @@ -109,7 +109,7 @@ typedef struct } PACKED(4); /* Control registers */ - volatile sh7705_intc_icr1_t *ICR1; + sh7705_intc_icr1_t *ICR1; } PACKED(4) sh7705_intc_t; @@ -125,101 +125,101 @@ typedef struct Some of the fields have been left unnamed because they correspond to SH7724 peripheral modules that are *very* unlikely to even exist in the SH7305, let alone by of any use to us */ -typedef struct +typedef volatile struct { word_union(IPRA, - uint TMU0_0 :4; /* TMU0 Channel 0 */ - uint TMU0_1 :4; /* TMU0 Channel 1 */ - uint TMU0_2 :4; /* TMU0 Channel 2 */ - uint IrDA :4; /* Infrared Communication */ + uint16_t TMU0_0 :4; /* TMU0 Channel 0 */ + uint16_t TMU0_1 :4; /* TMU0 Channel 1 */ + uint16_t TMU0_2 :4; /* TMU0 Channel 2 */ + uint16_t IrDA :4; /* Infrared Communication */ ); pad(2); word_union(IPRB, - uint :4; /* JPEG Processing Unit */ - uint LCDC :4; /* LCD Controller */ - uint DMAC1A :4; /* Direct Memory Access Controller 1 */ - uint :4; /* Blending Engine Unit */ + uint16_t :4; + uint16_t LCDC :4; /* LCD Controller */ + uint16_t DMAC1A :4; /* Direct Memory Access Controller 1 */ + uint16_t :4; ); pad(2); word_union(IPRC, - uint TMU1_0 :4; /* TMU1 Channel 0 */ - uint TMU1_1 :4; /* TMU1 Channel 1 */ - uint TMU1_2 :4; /* TMU1 Channel 2 */ - uint :4; /* Sound Processing Unit */ + uint16_t TMU1_0 :4; /* TMU1 Channel 0 */ + uint16_t TMU1_1 :4; /* TMU1 Channel 1 */ + uint16_t TMU1_2 :4; /* TMU1 Channel 2 */ + uint16_t :4; ); pad(2); word_union(IPRD, - uint :4; - uint MMCIF :4; /* MultiMedia Card Interface */ - uint :4; - uint :4; /* ATAPI Interface */ + uint16_t :4; + uint16_t MMCIF :4; /* MultiMedia Card Interface */ + uint16_t :4; + uint16_t :4; ); pad(2); word_union(IPRE, - uint DMAC0A :4; /* Direct Memory Access Controller 0 */ - uint :4; /* Various graphical engines */ - uint SCIFA3 :4; /* SCIFA channel 3 interrupt */ - uint :4; /* Video Processing Unit */ + uint16_t DMAC0A :4; /* Direct Memory Access Controller 0 */ + uint16_t :4; + uint16_t ETMU3 :4; /* Extra TMU 3 */ + uint16_t :4; ); pad(2); word_union(IPRF, - uint KEYSC :4; /* Key Scan Interface */ - uint DMACOB :4; /* DMAC0 transfer/error info */ - uint USB0_1 :4; /* USB controller */ - uint CMT :4; /* Compare Match Timer */ + uint16_t KEYSC :4; /* Key Scan Interface */ + uint16_t DMACOB :4; /* DMAC0 transfer/error info */ + uint16_t USB0_1 :4; /* USB controller */ + uint16_t CMT :4; /* Compare Match Timer */ ); pad(2); word_union(IPRG, - uint SCIF0 :4; /* SCIF0 transfer/error info */ - uint SCIF1 :4; /* SCIF1 transfer/error info */ - uint SCIF2 :4; /* SCIF2 transfer/error info */ - uint :4; /* Video Engine Unit */ + uint16_t SCIF0 :4; /* SCIF0 transfer/error info */ + uint16_t ETMU1 :4; /* Extra TMU 1 */ + uint16_t ETMU2 :4; /* Extra TMU 2 */ + uint16_t :4; ); pad(2); word_union(IPRH, - uint MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */ - uint MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */ - uint :4; /* I2C Interface channel 0 */ - uint :4; /* I2C Interface channel 1 */ + uint16_t MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */ + uint16_t MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */ + uint16_t :4; + uint16_t :4; ); pad(2); word_union(IPRI, - uint SCIFA4 :4; /* SCIFA channel 4 interrupt */ - uint :4; /* MediaRAM InterConnected Buffers */ - uint :4; /* Transport Stream Interface */ - uint :4; /* 2D Graphics Accelerator & ICB */ + uint16_t ETMU4 :4; /* Extra TMU 4 */ + uint16_t :4; + uint16_t :4; + uint16_t :4; ); pad(2); word_union(IPRJ, - uint :4; /* Capture Engine Unit */ - uint :4; /* Ethernet Memory Access Controller */ - uint FSI :4; /* FIFO-Buffered Serial Interface */ - uint SDHI1 :4; /* SD Card Host Interface channel 1 */ + uint16_t ETMU0 :4; /* Extra TMU 0 */ + uint16_t :4; + uint16_t FSI :4; /* FIFO-Buffered Serial Interface */ + uint16_t SDHI1 :4; /* SD Card Host Interface channel 1 */ ); pad(2); word_union(IPRK, - uint RTC :4; /* Real-Time Clock */ - uint DMAC1B :4; /* DMAC1 transfer/error info */ - uint :4; /* MediaRAM InterConnected Buffers */ - uint SDHI0 :4; /* SD Card Host Interface channel 0 */ + uint16_t RTC :4; /* Real-Time Clock */ + uint16_t DMAC1B :4; /* DMAC1 transfer/error info */ + uint16_t :4; + uint16_t SDHI0 :4; /* SD Card Host Interface channel 0 */ ); pad(2); word_union(IPRL, - uint SCIFA5 :4; /* SCIFA channel 5 interrupt */ - uint :4; - uint TPU :4; /* Timer-Pulse Unit */ - uint :4; /* Image Extraction DMAC */ + uint16_t ETMU5 :4; /* Extra TMU 5 */ + uint16_t :4; + uint16_t TPU :4; /* Timer-Pulse Unit */ + uint16_t :4; ); pad(2); @@ -228,124 +228,25 @@ typedef struct /* sh7305_intc_masks_t - Interrupt mask management Writing 1 to IMR masks interrupts; writing 1 to IMCRs clears the masks. Writing 0 is ignored; reading from IMCRs yields undefined values */ -typedef struct +typedef volatile struct { - byte_union(IMR0, - uint :1; - uint TUNI1_2 :1; /* TMU1 overflow interrupts */ - uint TUNI1_1 :1; - uint TUNI1_0 :1; - uint SDHII3 :1; /* SD Card Host 1 interrupts */ - uint SDHII2 :1; - uint SDHII1 :1; - uint SDHII0 :1; - ); - pad(3); - - byte_union(IMR1, - uint :4; - uint DEI3 :1; /* DMAC0A interrupts */ - uint DEI2 :1; - uint DEI1 :1; - uint DEI0 :1; - ); - pad(3); - - byte_union(IMR2, - uint :7; - uint SCIFA0 :1; /* Asynchronous Serial interrupts */ - ); - pad(3); - - byte_union(IMR3, - uint DEI3 :1; /* DMAC1A interrupts */ - uint DEI2 :1; - uint DEI1 :1; - uint DEI0 :1; - uint :4; - ); - pad(3); - - byte_union(IMR4, - uint :1; - uint TUNI0_2 :1; /* TMU0 overflow interrupts */ - uint TUNI0_1 :1; - uint TUNI0_0 :1; - uint :3; - uint LCDCI :1; /* LCD Controller Interrupt */ - ); - pad(3); - - byte_union(IMR5, - uint KEYI :1; /* Key Interface */ - uint DADERR :1; /* DMAC0B interrupts */ - uint DEI5 :1; - uint DEI4 :1; - uint :1; - uint SCIF2 :1; /* Serial Communication Interface */ - uint SCIF1 :1; - uint SCIF0 :1; - ); - pad(3); - - byte_union(IMR6, - uint :2; - uint :1; - uint SCIFA4 :1; /* SCIFA4 interrupt */ - uint :1; - uint :1; - uint MSIOFI0 :1; /* Clock-synchronized SCIF channel 0 */ - uint MSIOFI1 :1; /* Clock-synchronized SCIF channel 1 */ - ); - pad(3); - - uint8_t IMR7; - pad(3); - - byte_union(IMR8, - uint SDHII3 :1; /* SD Card Host 0 interrupts */ - uint SDHII2 :1; - uint SDHII1 :1; - uint SDHII0 :1; - uint :2; - uint SCFIA5 :1; /* SCIFA5 interrupt */ - uint FSI :1; /* FIFO-Buffered Serial Interface */ - ); - pad(3); - - byte_union(IMR9, - uint :3; - uint CMTI :1; /* Compare Match Timer Interrupt */ - uint :1; - uint USI1 :1; /* USB1 */ - uint USI0 :1; /* USB0 */ - uint :1; - ); - pad(3); - - byte_union(IMR10, - uint :1; - uint DADERR :1; /* DMAC1B interrupts */ - uint DEI5 :1; - uint DEI4 :1; - uint :1; - uint ATI :1; /* RTC Alarm interrupt */ - uint PRI :1; /* RTC Periodic interrupt */ - uint CUI :1; /* RTC Carry interrupt */ - ); - pad(3); - - byte_union(IMR11, - uint :5; - uint TPUI :1; /* Timer-Pulse Unit */ - uint :2; - ); - pad(3); - + uint8_t IMR0; pad(3); + uint8_t IMR1; pad(3); + uint8_t IMR2; pad(3); + uint8_t IMR3; pad(3); + uint8_t IMR4; pad(3); + uint8_t IMR5; pad(3); + uint8_t IMR6; pad(3); + uint8_t IMR7; pad(3); + uint8_t IMR8; pad(3); + uint8_t IMR9; pad(3); + uint8_t IMR10; pad(3); + uint8_t IMR11; pad(3); uint8_t IMR12; } PACKED(4) sh7305_intc_masks_t; + /* sh7305_intc_userimask_t - User Interrupt Mask Sets the minimum required level for interrupts to be accepted. @@ -362,11 +263,11 @@ typedef struct *(INTC._7305.USERIMASK) = mask; } */ -typedef lword_union(sh7305_intc_userimask_t, - uint _0xa5 :8; /* Always set to 0xa5 before writing */ - uint :16; - uint UIMASK :4; /* User Interrupt Mask Level */ - uint :4; +typedef volatile lword_union(sh7305_intc_userimask_t, + uint32_t _0xa5 :8; /* Always set to 0xa5 before writing */ + uint32_t :16; + uint32_t UIMASK :4; /* User Interrupt Mask Level */ + uint32_t :4; ); /* sh7305_intc_t - the SH7305 interrupt controller */ @@ -374,17 +275,25 @@ typedef struct { /* Interrupt priority registers */ union { - volatile sh7305_intc_ipc_t *_; - volatile uint16_t *IPRS; + sh7305_intc_ipc_t *_; + volatile uint16_t *IPRS; }; /* Interrupt mask & mask clear registers */ - volatile sh7305_intc_masks_t *MSK; - volatile sh7305_intc_masks_t *MSKCLR; + sh7305_intc_masks_t *MSK; + sh7305_intc_masks_t *MSKCLR; /* Other registers */ - volatile sh7305_intc_userimask_t *USERIMASK; + sh7305_intc_userimask_t *USERIMASK; } PACKED(4) sh7305_intc_t; +//--- +// Forward definitions +//--- + +/* Provided by core/gint.c */ +extern sh7705_intc_t INTC3; +extern sh7305_intc_t INTC4; + #endif /* GINT_CORE_INTC */ diff --git a/include/core/memory.h b/include/core/memory.h new file mode 100644 index 0000000..49c02aa --- /dev/null +++ b/include/core/memory.h @@ -0,0 +1,22 @@ +//--- +// gint:core:memory - Core memory functions +// +// These are the basic standard memory functions required by GCC. +//--- + +#ifndef GINT_CORE_MEMORY +#define GINT_CORE_MEMORY + +/* memcpy() - copy a chunk of memory to a non-overlapping destination */ +void *memcpy(void * restrict dest, const void * restrict src, size_t n); + +/* memmove() - copy a chunk of memory to any destination */ +void *memmove(void *dest, const void *src, size_t n); + +/* memcmp() - compare two chunks of memory of the same size */ +int memcmp(const void *src1, const void *src2, size_t n); + +/* memset() - fill a chunk of memory with a single byte */ +void *memset(void *dest, int byte, size_t n); + +#endif /* GINT_CORE_MEMORY */ diff --git a/include/core/mmu.h b/include/core/mmu.h index 140ebfa..d92d3ee 100644 --- a/include/core/mmu.h +++ b/include/core/mmu.h @@ -12,6 +12,68 @@ #include #include +//--- +// SH7705 TLB +//--- + +#ifdef FX9860G + +/* tlb_addr_t - address part of a TLB entry */ +typedef struct +{ + uint VPN :22; + uint :1; + uint V :1; + uint ASID :8; + +} PACKED(4) tlb_addr_t; + +/* tlb_data_t - data part of a TLB entry */ +typedef struct +{ + uint :3; + uint PPN :19; + uint :1; + uint V :1; + uint :1; + uint PR :2; + uint SZ :1; + uint C :1; + uint D :1; + uint SH :1; + uint :1; + +} PACKED(4) tlb_data_t; + +/* tlb_addr() - get the P4 address of a TLB address entry + @way TLB way (0..3) + @E Entry number (0..31) + Returns a pointer to the entry. */ +const tlb_addr_t *tlb_addr(uint way, uint E); + +/* tlb_data() - get the P4 address of a TLB data entry + @way TLB way (0..3) + @E Entry number (0..31) + Returns a pointer to the entry. */ +const tlb_data_t *tlb_data(uint way, uint E); + +/* tlb_mapped_memort() - count amount of mapped memory + This function returns the amount of mapped text and data segment memory, in + bytes. The ranges are defined as follows: + ROM 00300000:512k + RAM 08100000:512k + Other mappings are ignored. Both pointers may be NULL. + + @rom Pointer to amount of mapped ROM + @ram Pointer to amount of mapped RAM */ +void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram); + +#endif + +//--- +// SH7305 Unified TLB +//--- + /* utlb_addr_t - address part of a UTLB entry */ typedef struct { @@ -52,7 +114,7 @@ const utlb_data_t *utlb_data(uint E); /* utlb_mapped_memory() - count amount of mapped memory This function returns the amount of mapped text and data segment memory, in bytes. The ranges are defined as follows: - ROM 00300000:512k + ROM 00300000:4M RAM 08100000:512k Other mappings are ignored. Both pointers may be NULL. diff --git a/include/core/mpu.h b/include/core/mpu.h index 91164d9..95486e3 100644 --- a/include/core/mpu.h +++ b/include/core/mpu.h @@ -3,9 +3,13 @@ // // This component detects the architecture and MPU type of the underlying // hardware by relying on version registers and/or side-information. It -// provides macros isSH3() and isSH4() to perform MPU-dependent jobs: +// provides macros isSH3() and isSH4(), but the best way of performing +// MPU-dependent jobs is to use mpuSwitch(): // -// if(isSH3()) { ... } else { ... } +// mpuSwitch( +// print("SH3 code"), +// print("SH4 code") +// ); //--- #ifndef GINT_CORE_MPU @@ -13,7 +17,7 @@ #include -/* mpu_t - Supported MPUs */ +/* mpu_t - supported MPUs */ typedef enum { mpu_unknown = 0, @@ -36,6 +40,8 @@ typedef enum #define isSH3() (mpu_id() & 1) #define isSH4() (!isSH3()) + #define mpuSwitch(a, b) do { if(isSH3()) { a; } else { b; } } while(0) + /* mpu_init() - probe the MPU type This function must be executed before mpu_id() can be used. */ void mpu_init(void); @@ -47,6 +53,8 @@ typedef enum #define isSH3() 0 #define isSH4() 1 + #define mpuSwitch(a, b) do { b; } while(0) + #endif /* FX9860G */ #endif /* GINT_CORE_MPU */ diff --git a/include/defs/attributes.h b/include/defs/attributes.h index 64a38a7..67d45a2 100644 --- a/include/defs/attributes.h +++ b/include/defs/attributes.h @@ -13,6 +13,9 @@ /* Objects from the .gint.data and .gint.bss sections */ #define GDATA __attribute__((section(".gint.data"))) #define GBSS __attribute__((section(".gint.bss"))) +/* Additional sections that are only needed on SH3 */ +#define GDATA3 __attribute__((section(".gint.data.sh3"))) +#define GBSS3 __attribute__((section(".gint.bss.sh3"))) /* Initialization functions */ #define PRETEXT __attribute__((section(".pretext"))) diff --git a/include/drivers/t6k11.h b/include/drivers/t6k11.h index 5b89f40..a4bc7e1 100644 --- a/include/drivers/t6k11.h +++ b/include/drivers/t6k11.h @@ -40,12 +40,13 @@ void t6k11_contrast(int contrast); /* t6k11_backlight() - manage the screen backlight - Changes the backlight setting depending on the value of "setting": - - If setting = 0, turns the backlight off. + Changes the backlight status depending on the value of the argument: - If setting > 0, turns the backlight on. + - If setting = 0, turns the backlight off. - If setting < 0, toggles backlight. - This function has no effect on models that do not support the backlight. + This function has no effect on models that do not support the backlight, + although gint does not provide any facility for detecting them. @setting Requested backlight setting */ void t6k11_backlight(int setting); diff --git a/include/gint/clock.h b/include/gint/clock.h new file mode 100644 index 0000000..959c76a --- /dev/null +++ b/include/gint/clock.h @@ -0,0 +1,8 @@ +//--- +// gint:clock - Clock signals +//--- + +#ifndef GINT_CLOCK +#define GINT_CLOCK + +#endif /* GINT_CLOCK */ diff --git a/include/gint/display-fx.h b/include/gint/display-fx.h index fb54785..c7448c1 100644 --- a/include/gint/display-fx.h +++ b/include/gint/display-fx.h @@ -1,8 +1,113 @@ //--- // gint:display-fx - fx9860g drawing functions +// +// This module is in charge of all monochrome rendering. The gray engine +// has its own functions, but often relies on this module (because the +// gray effect is created through two monochrome buffers). //--- #ifndef GINT_DISPLAY_FX #define GINT_DISPLAY_FX +#ifdef FX9860G + +/* Screen dimensions on fx9860g */ +#define DWIDTH 128 +#define DHEIGHT 64 + +/* color_t - colors available for drawing + The following colors are defined by the library: + + OPAQUE COLORS (override existing pixels) + white, black - the usual thing + light, dark - additional colors used by the gray engine + + OPERATORS (combine with existing pixels) + none - leaves unchanged + reverse - inverts white <-> black, light <-> dark + lighten - shifts black -> dark -> light -> white -> white + darken - shifts white -> light -> dark -> black -> black + + Not all colors can be used with all functions. To avoid ambiguities, all + functions explicitly indicate compatible colors. */ +typedef enum +{ + /* Opaque colors */ + color_white = 0, + color_light = 1, + color_dark = 2, + color_black = 3, + + /* Monochrome operators */ + color_none = 4, + color_reverse = 5, + + /* Gray operators */ + color_lighten = 6, + color_darken = 7, + +} color_t; + +//--- +// Area drawing functions +//--- + +/* dupdate() - pushes the video RAM to the display driver + This function makes the contents of the VRAM visible on the screen. It is + the direct equivalent of Bdisp_PutDisp_DD() */ +void dupdate(void); + +/* dclear() - fill the screen with a single color + This function clears the screen by replacing all the pixels with a single + color. This function is optimized for opaque drawing. If you wish to apply + operators, use drect(). + + @color Allowed colors: white, black */ +void dclear(color_t color); + +/* drect() - fill a rectangle of the screen + This functions applies a color or an operator to a rectangle defined by two + points (x1 y1) and (x2 y2). Both are included in the rectangle. + + If GINT_LAX is defined, this function makes the following assumptions: + 0 <= x1 < x2 <= 127 + 0 <= y1 < y2 <= 63 + + @x1 @y1 @x2 @y2 Bounding rectangle (drawn area). + @color Allowed colors: white, black, none, reverse */ +void drect(int x1, int y1, int x2, int y2, color_t color); + +//--- +// Point drawing functions +//--- + +/* dpixel() - change a pixel's color + If the requested color is an operator, the result will depend on the current + color of the pixel. + + If GINT_LAX is defined, this function makes the following assumptions: + 0 <= x <= 127 + 0 <= y <= 63 + + @x @y Coordinates of the pixel to repaint + @color Allowed colors: white, black, none, reverse */ +void dpixel(int x, int y, color_t color); + +/* dline() - render a straight line + This function draws a line using a Bresenham-style algorithm. Please note + that the affected pixels may not be exactly the same when using dline() and + Bdisp_DrawLineVRAM(). + + dline() has optimization facilities for horizontal and vertical lines. + + If GINT_LAX is defined, this function makes the following assumptions: + 0 <= x1 <= x2 <= 127 + 0 <= y1, y2 <= 63 + + @x1 @y1 @x2 @y2 End points of theline (both included). + @color Allowed colors: white, black, none, reverse */ +void dline(int x1, int y1, int x2, int y2, color_t color); + +#endif /* FX9860G */ + #endif /* GINT_DISPLAY_FX */ diff --git a/include/gint/drivers.h b/include/gint/drivers.h index b1ebf95..d52e088 100644 --- a/include/gint/drivers.h +++ b/include/gint/drivers.h @@ -8,12 +8,37 @@ #include #include +//--- +// Driver initialization +// +// Drivers are initialized in linking order. For each driver, the +// following functions are called: +// - driver_sh3() [if running on an SH3-based machine] +// - ctx_save() +// - init() +// +// When the driver is unloaded, the following functions are called: +// - ctx_restore() +//--- + /* gint_driver_t - driver meta-information used by gint */ typedef struct { /* Driver name */ const char *name; + /* driver_sh3() - rectify driver initialization on SH3 platforms + This function is called during driver initialization on SH3. It may + be NULL. */ + void (*driver_sh3)(void); + + /* init() - initialize the driver + This function is called after ctx_save() and needs not save the + system setting. It is typically used to initialize registers to + suitable values on startup. If there is no init function, this field + may be set to NULL */ + void (*init)(void); + /* Size of a context object for the driver */ uint ctx_size; @@ -44,4 +69,12 @@ typedef struct #define GINT_DECLARE_DRIVER(name) \ SECTION(".gint.drivers") extern gint_driver_t name; +/* GINT_DRIVER_SH3() - declare a function for SH3-rectification + This macros allows the argument function to not exist on fxcg50. */ +#ifdef FX9860G + #define GINT_DRIVER_SH3(name) name +#else + #define GINT_DRIVER_SH3(name) NULL +#endif + #endif /* GINT_DRIVERS */ diff --git a/include/gint/gint.h b/include/gint/gint.h index f081a5d..a9906f3 100644 --- a/include/gint/gint.h +++ b/include/gint/gint.h @@ -56,4 +56,68 @@ void gint_unload(void); getkey() call. */ void gint_pause(void); +//--- +// Public functions +//--- + +/* gint_intlevel() - configure the level of interrupts + + This function changes the interrupt level of the requested interrupt. Make + sure you are aware of interrupt assignments to avoid breaking other code. + This function is mainly used by drivers to enable the interrupts that they + support. + + The first parameter 'intid' identifies an interrupt by its position in the + sequence: + + IPRA & 0xf000 ; IPRA & 0x0f00 .. IPRA & 0x000f ; IPRB & 0xf000 .. + + For instance ID 7 refers to the low nibble of IPRB. These IDs and the range + for which there are valid is heavily platform-dependent and any call to this + function should be wrapped inside an MPU type check. This function will + crash if the provided interrupt ID is invalid. + + The interrupt level should be in the range 0 (disabled) .. 15 (highest + priority). + + @intid Interrupt ID of the targeted interrupt + @level Requested interrupt level + Returns the interrupt level that was assigned before the call. */ +int gint_intlevel(int intid, int level); + +/* gint_inthandler() - configure interrupt handlers + + This function installs (copies) interrupt handlers in the VBR space of the + application. Each handler is a 32-byte block aligned on a 32-byte boundary. + When an interrupt request is accepted, the hardware jumps to a specific + interrupt handler at an address that depends on the interrupt source. + + Each interrupt handler should only refer to data within its own block + because the relative displacement between blocks is MPU-dependent. There are + a few exceptions to this, such as timer handlers, which are contiguous on + all currently-used platforms. Be careful. + + This function allows anyone to replace any interrupt handler so make sure + you're not interfering with usual interrupt assignments. + + The first parameter 'event_code' represents the event_code associated with + the interrupt. These codes are normally platform-dependent, but gint always + uses the SH7305 codes: SH3 platforms have a translation table. See the + documentation for a list of event codes and their associated interrupts. + + The handler function must be an interrupt handler: it should not raise + exceptions, must end with 'rte', uses the kernel register bank... and it + must fit within 32 bytes. If it's not written in assembler, then you're + likely doing something wrong. + + It is common for interrupt handlers to have a few bytes of data, such as the + address of a callback function. gint often stores this data in the last + bytes of the block. This function returns the VBR address of the block to + allow the caller to edit the parameters. + + @event_code Identifier of the interrupt block + @handler Address of handler function + Returns the VBR address where the handlers was installed. */ +void *gint_inthandler(int event_code, const void *handler); + #endif /* GINT_GINT */ diff --git a/include/gint/timer.h b/include/gint/timer.h new file mode 100644 index 0000000..82d9e47 --- /dev/null +++ b/include/gint/timer.h @@ -0,0 +1,157 @@ +//--- +// gint:timer - Timer operation +//--- + +#ifndef GINT_TIMER +#define GINT_TIMER + +#include +#include +#include + +/* Timer identifiers + + Hardware timers are numbered with integers starting from 0. You can freely + access all the available timers by using their number once you have + configured them with timer_setup(). The number of timers depends on the MPU: + + SH3-based: 4 timers, ids 0..3 [SH7355, SH7337] + SH4-based: 9 timers, ids 0..8 [SH7305] + + You should be aware that some of these timers are used by default by gint: + - Timer 1 is used by the gray engine on fx9860g. + - Timer 3 is used by the keyboard, unless GINT_RTC_KEYBOARD is defined. This + macro is controlled by the -rtc-keyboard switch when gint is compiled. + + timer_setup() will fail if you try to use a timer that's already running. + Always check the return value of timer_setup()! Using a timer id that has + not been validated by timer_setup() will work, but do *something else* than + what you intended. */ + +/* timer_count() - tells how many timers are available on the platform */ +HDRFUNC int timer_count(void) +{ + mpuSwitch( + return 4, /* SH3-based */ + return 9 /* SH4-based */ + ); +} + +/* Clock input + + Timers count down when their input clock ticks, and fire when their counter + reach 0. The choice of the input clock influences the resolution of the + timer, but if the clock is too fast, the 32-bit counter might not be able to + represent long delays. + + Several input clocks are available. The peripheral clock (Po) can be divided + by 4, 16, 64 or 256; as an alternative the external clock TCLK can be used + for counting. I suspect TCLK runs at a fixed frequency of 32768 Hz, but this + has yet to be verified. + + You don't really need to choose an input clock unless you are doing + something very specific. In most practical cases you can use timer_default + which is 0. See the timer_delay() function for more information. */ +typedef enum +{ + timer_Po_4 = 0, + timer_Po_16 = 1, + timer_Po_64 = 2, + timer_Po_256 = 3, + timer_TCLK = 5, + + timer_default = timer_Po_4, + +} timer_input_t; + +//--- +// Timer functions +//--- + +/* timer_setup() - set up a timer + + This function configures the requested timer without starting it. On + success, it returns the first argument "timer", which is used as a timer + identifier in all other timer functions. If the requested timer is already + in use, this function fails and returns a negative number. + + This function sets the timer delay, the clock source, and registers a + callback function to be called when the timer fires. An argument can be + supplied to the callback function in the form of a pointer. + + When the timer fires, the callback function is called with the provided + argument pointer. The callback decides whether the timer should continue + running (by returning 0) or stop (by returning nonzero). In the latter case, + events accumulated while the callback was running are dropped. + + It is sometimes difficult to choose a timer constant and a clock source + given a wished delay in seconds, especially when overclock is used. The + timer_delay() function is provided for this purpose. + + @timer Requested timer id + @delay Delay between each event (the unit depends on the clock source) + @clock Clock source used by the timer for counting down + @callback Callback function (called when the timer fires) + @arg Passed as argument to the callback function */ +int timer_setup(int timer, uint32_t delay, timer_input_t clock, + int (*callback)(void *arg), void *arg); + +/* timer_delay() - compute a delay constant from a duration in seconds + + This function can used as a facility to calculate the [delay] argument to + the timer_setup() function. It takes a microsecond delay as an argument and + returns the corresponding timer constant. A typical use to start a timer + with a 25 ms interval would be: + + timer_setup(0, timer_delay(0, 25 * 1000), 0, callback, arg); + + WARNING: Only timers 0 to 2 can count microseconds! Other timers have a + resolution of around 30 us. Counting in ms is safe for all timers, though. + + For standard timers (0 to 2) it uses Po / 4 as clock input, which is very + precise and can represent up to 3 minutes' time; for extra timers (3 and + above) the clock is fixed to 32768 Hz. + + @timer The timer you are planning to use + @delay_us Requested delay in microseconds */ +uint32_t timer_delay(int timer, int delay_us); + +/* timer_start() - start a configured timer + The specified timer will start counting down and fire callbacks at regular + intervals. + + @timer Timer id, as returned by timer_setup() */ +void timer_start(int timer); + +/* timer_reload() - change a timer's delay and source + + Changes the delay and source of the given timer. This function does not + start the timer if it stopped. The timer will start counting down from the + new delay when it is started (or immediately if it was running), no matter + how much time has elapsed since it last fired. Accumulated events are not + dropped. + + In cases where choosing a delay and input clock is difficult (such as when + using overclocking), use the timer_delay() function. + + @timer Timer id, as returned by timer_setup() + @delay New delay (unit depends on the clock source) + @clock New clock source to be used by the timer */ +void timer_reload(int timer, uint32_t delay, timer_input_t clock); + +/* timer_pause() - stop a running timer + The specified timer will be paused; its counter will not be reset. A stopped + timer can be resumed anytime by calling timer_start(). If you want to also + reset the counter, use timer_reload(). + + @timer Timer id, as returned by timer_setup() */ +void timer_pause(int timer); + +/* timer_free() - free a timer + Stops and destroys a timer, making its id free for re-use. The id must not + be used anymore until it is returned by a further call to timer_setup(). + + @timer Timer id, as returned by timer_setup() */ +void timer_free(int timer); + +#endif /* GINT_TIMER */ diff --git a/src/clock/freq.c b/src/clock/freq.c new file mode 100644 index 0000000..e69de29 diff --git a/src/core/bootlog.c b/src/core/bootlog.c index ed73906..635bf56 100644 --- a/src/core/bootlog.c +++ b/src/core/bootlog.c @@ -14,7 +14,7 @@ extern char sgdata, sgbss, sdata, sbss, btors, mtors, etors; -/* bootlog_loaded() - Section loading stage */ +/* bootlog_loaded() - section loading stage */ void bootlog_loaded(void) { /* Version string - the string constant resides in ROM */ @@ -61,6 +61,7 @@ void bootlog_loaded(void) print_dec(11, 3, gint_size, 4); print_dec(17, 3, &mtors - &btors, 2); print_dec(20, 3, &etors - &mtors, 2); + Bdisp_PutDisp_DD(); } @@ -74,17 +75,17 @@ void bootlog_mapped(int rom, int ram) print(1, 4, "MMU ROM: k RAM: k"); (rom < 0) ? print(9, 4, "???") : print_dec(9, 4, rom, 3); (ram < 0) ? print(18, 4, "???") : print_dec(18, 4, ram, 3); + Bdisp_PutDisp_DD(); } -/* bootlog_kernel() - Gint loading stage */ +/* bootlog_kernel() - gint loading stage */ void bootlog_kernel(void) { - if(isSH3()) - { - #ifdef FX9860G - extern sh7705_intc_t INTC3; + print(15, 1, " Kernel"); + mpuSwitch({ + /* SH3-based */ print(1, 5, "ABCD"); print_hex( 6, 5, INTC3._.IPRA->word, 4); print_hex(10, 5, INTC3._.IPRB->word, 4); @@ -95,12 +96,8 @@ void bootlog_kernel(void) print_hex(10, 6, INTC3._.IPRF->word, 4); print_hex(14, 6, INTC3._.IPRG->word, 4); print_hex(18, 6, INTC3._.IPRH->word, 4); - #endif - } - else - { - extern sh7305_intc_t INTC4; - + },{ + /* SH4-based */ print(1, 5, "ACFG"); print_hex( 6, 5, INTC4._->IPRA.word, 4); print_hex(10, 5, INTC4._->IPRC.word, 4); @@ -111,7 +108,7 @@ void bootlog_kernel(void) print_hex(10, 6, INTC4._->IPRJ.word, 4); print_hex(14, 6, INTC4._->IPRK.word, 4); print_hex(18, 6, INTC4._->IPRL.word, 4); - } + }); Bdisp_PutDisp_DD(); } diff --git a/src/core/exch.S b/src/core/exch.S new file mode 100644 index 0000000..a7840cb --- /dev/null +++ b/src/core/exch.S @@ -0,0 +1,32 @@ +/* +** gint:core:exch - Exception handlers +*/ + + .global _exch_entry_7305 + +#ifdef FX9860G + .global _exch_entry_7705 +#endif + + .section .gint.blocks, "ax" + .align 4 + +/* SH7305-TYPE DEBUG EXCEPTION HANDLER - 26 BYTES */ + +_exch_entry_7705: +_exch_entry_7305: + mov.l 1f, r0 + mov.l @r0, r4 + + sts.l pr, @-r15 + mov.l 2f, r0 + jsr @r0 + nop + lds.l @r15+, pr + rte + nop + + .zero 6 + +2: .long _debug_exc +1: .long 0xff000024 diff --git a/src/core/gint.c b/src/core/gint.c new file mode 100644 index 0000000..278557d --- /dev/null +++ b/src/core/gint.c @@ -0,0 +1,64 @@ +//--- +// gint:core:gint - Library functions +//--- + +#include +#include +#include +#include + +/* Interrupt controllers */ + +GDATA3 sh7705_intc_t INTC3 = { + .IPRS = { + (void *)0xfffffee2, (void *)0xfffffee4, + (void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a, + (void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004, + }, + .ICR1 = (void *)0xa4000010, +}; + +GDATA sh7305_intc_t INTC4 = { + .IPRS = (void *)0xa4080000, + .MSK = (void *)0xa4080080, + .MSKCLR = (void *)0xa40800c0, + .USERIMASK = (void *)0xa4700000, +}; + +//--- +// Library functions +//--- + +/* gint_intlevel() - configure the level of interrupts */ +int gint_intlevel(int intid, int level) +{ + int shift = (~intid & 0x3) << 2; + volatile uint16_t *ipr; + level &= 0xf; + + mpuSwitch( + ipr = INTC3.IPRS[intid >> 2], /* SH3-based */ + ipr = &INTC4.IPRS[2 * (intid >> 2)] /* SH4-based */ + ); + + int oldlevel = (*ipr >> shift) & 0xf; + *ipr = (*ipr & ~(0xf << shift)) | (level << shift); + + return oldlevel; +} + +/* gint_inthandler() - configure interrupt handlers */ +void *gint_inthandler(int event_code, const void *handler) +{ + extern char gint_vbr; + + /* Normalize the event code */ + event_code -= 0x400; + event_code &= ~0x1f; + + /* Prevent overriding the entry gate */ + if(event_code < 0) return NULL; + + void *dest = (void *)&gint_vbr + event_code + 0x620; + return memcpy(dest, handler, 32); +} diff --git a/src/core/inth.S b/src/core/inth.S index ef79caf..102ae52 100644 --- a/src/core/inth.S +++ b/src/core/inth.S @@ -5,11 +5,10 @@ ** blocks depending on its configuration. */ - - .global _inth_entry_7305 + .global _inth_entry_7305 #ifdef FX9860G - .global _inth_entry_7705 + .global _inth_entry_7705 #endif .section .gint.blocks, "ax" @@ -34,20 +33,13 @@ references unless their relative order is fully known. This happens with the timer driver, and it works swimmingly; just be careful. */ -/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */ +/* SH7305-TYPE DEBUG INTERRUPT HANDLER - 26 BYTES */ +#if 0 _inth_entry_7305: - /* Get the event code from the INTEVT register */ mov.l 1f, r0 - /* TODO: mov.l @r0, r0 */ mov.l @r0, r4 - /* Interrupt codes start at 0x400 */ - mov #4, r1 - shll8 r1 - sub r1, r0 - - /* Jump to a C routine (TODO: Remove this) */ sts.l pr, @-r15 mov.l 2f, r0 jsr @r0 @@ -56,13 +48,31 @@ _inth_entry_7305: rte nop - /* Add the distance between nop and the first entry, and jump - add #16, r0 - braf r0 - nop */ + .zero 6 2: .long _debug 1: .long 0xff000028 +#endif + +/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */ + +_inth_entry_7305: + /* Get the event code from the INTEVT register */ + mov.l 1f, r0 + mov.l @r0, r0 + + /* Interrupt codes start at 0x400 */ + mov #4, r1 + shll8 r1 + sub r1, r0 + + /* Add the distance to the first entry and jump */ + add #16, r0 + braf r0 + nop + + .zero 12 +1: .long 0xff000028 #ifdef FX9860G @@ -90,11 +100,10 @@ _inth_entry_7705: 1: .long 0xa4000000 /* INTEVT2 register */ 2: .long _inth_remap - .section .gint.data .align 4 -/* EVENT CODE TRANSLATION TABLE - 72 BYTES */ +/* EVENT CODE TRANSLATION TABLE - 96 BYTES */ _inth_remap: .byte 0x00, 0x01, 0x02, 0xfe, 0x34, 0x35, 0x36, 0xff @@ -106,5 +115,8 @@ _inth_remap: .byte 0xff, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff .byte 0x2d, 0x2d, 0xff, 0xff, 0x2d, 0x2d, 0xff, 0xff + .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + .byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + .byte 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff #endif diff --git a/src/core/memory.c b/src/core/memory.c index 029dd68..3322120 100644 --- a/src/core/memory.c +++ b/src/core/memory.c @@ -85,23 +85,24 @@ void *memcpy(void * restrict dst, const void * restrict src, size_t n) return dst; } -void *_memmove(void *dst, const void *src, size_t n) +void *_memmove(UNUSED void *dst, UNUSED const void *src, UNUSED size_t n) { // (same as memcpy, but heed for direction if areas overlap) - // more complicated - // allocate a buffer aligned with destination (source would be ok too) - // read unaligned from source to buffer - // copy aligned from buffer to destination + // copy by increasing addresses if dst < src + // copy by decreasing addresses if dst > src return dst; } -int _memcmp(const void *s1, const void *s2, size_t n) +int _memcmp(UNUSED const void *s1, UNUSED const void *s2, UNUSED size_t n) { return 0; } -void *_memset(void *s, int byte, size_t n) +void *memset(void *s, int byte, size_t n) { + /* TODO: Do it efficiently */ + char *dst = s; + while(n--) *dst++ = byte; return s; } diff --git a/src/core/mmu.c b/src/core/mmu.c index 291b831..c942c6e 100644 --- a/src/core/mmu.c +++ b/src/core/mmu.c @@ -4,6 +4,58 @@ #include +//--- +// SH7705 TLB +//--- + +#ifdef FX9860G + +/* tlb_addr() - get the P4 address of a TLB address entry */ +INLINE const tlb_addr_t *tlb_addr(uint way, uint E) +{ + uint32_t addr = 0xf2000000 | (E << 12) | (way << 8); + return (void *)addr; +} + +/* tlb_data() - get the P4 address of a TLB data entry */ +INLINE const tlb_data_t *tlb_data(uint way, uint E) +{ + uint32_t addr = 0xf3000000 | (E << 12) | (way << 8); + return (void *)addr; +} + +/* tlb_mapped_memort() - count amount of mapped memory */ +void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram) +{ + uint32_t rom = 0, ram = 0; + + for(int way = 0; way < 4; way++) + for(int E = 0; E < 32; E++) + { + const tlb_addr_t *addr = tlb_addr(way, E); + const tlb_data_t *data = tlb_data(way, E); + if(!addr->V || !data->V) continue; + + int size = data->SZ ? 4096 : 1024; + uint32_t src; + + if(data->SZ) src = (((addr->VPN >> 2) | E) << 12); + else src = (addr->VPN | (E << 2)) << 10; + + if(src >= 0x00300000 && src < 0x00380000) rom += size; + if(src >= 0x08100000 && src < 0x08180000) ram += size; + } + + if(p_rom) *p_rom = rom; + if(p_ram) *p_ram = ram; +} + +#endif + +//--- +// SH7305 Unified TLB +//--- + /* utlb_addr() - get the P4 address of a UTLB address entry */ INLINE const utlb_addr_t *utlb_addr(uint E) { @@ -29,11 +81,12 @@ void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram) const utlb_data_t *data = utlb_data(E); if(!addr->V || !data->V) continue; + /* Magic formula to get the size without using an array */ int sz = ((data->SZ1 << 1) | data->SZ2) << 3; int size = 1 << ((0x14100c0a >> sz) & 0xff); uint32_t src = addr->VPN << 10; - if(src >= 0x00300000 && src < 0x00380000) rom += size; + if(src >= 0x00300000 && src < 0x00700000) rom += size; if(src >= 0x08100000 && src < 0x08180000) ram += size; } diff --git a/src/core/setup.c b/src/core/setup.c index 1335a63..c307ae3 100644 --- a/src/core/setup.c +++ b/src/core/setup.c @@ -3,34 +3,18 @@ //--- #include +#include +#include #include #include #include -/* Interrupt controllers */ - -#ifdef FX9860G -GDATA sh7705_intc_t INTC3 = { - .IPRS = { - (void *)0xfffffee2, (void *)0xfffffee4, - (void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a, - (void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004, - }, - .ICR1 = (void *)0xa4000010, -}; -#endif - -GDATA sh7305_intc_t INTC4 = { - .IPRS = (void *)0xa4080000, - .MSK = (void *)0xa4080080, - .MSKCLR = (void *)0xa40800c0, - .USERIMASK = (void *)0xa4700000, -}; - /* VBR address, from the linker script */ extern char gint_vbr; /* System's VBR address */ GBSS static uint32_t system_vbr; +/* Driver table */ +extern gint_driver_t bdrv, edrv; //--- // Context system for gint's core @@ -46,32 +30,24 @@ typedef struct @arg ctx gint core context object */ static void gint_ctx_save(gint_core_ctx *ctx) { - if(isSH3()) - { - #ifdef FX9860G - for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]); - #endif - } - else - { - for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i]; - } + mpuSwitch( + /* SH3-based */ + for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]), + /* SH4-based */ + for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i] + ); } /* gint_ctx_restore() - restore interrupt controller configuration @arg ctx gint core context object */ static void gint_ctx_restore(gint_core_ctx *ctx) { - if(isSH3()) - { - #ifdef FX9860G - for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i]; - #endif - } - else - { - for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i]; - } + mpuSwitch( + /* SH3-based */ + for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i], + /* SH4-based */ + for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i] + ); } //--- @@ -85,16 +61,12 @@ GBSS static gint_core_ctx sys_ctx; static void lock(void) { /* Just disable everything, drivers will enable what they support */ - if(isSH3()) - { - #ifdef FX9860G - for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000; - #endif - } - else - { - for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000; - } + mpuSwitch( + /* SH3-based */ + for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000, + /* SH4-based */ + for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000 + ); } /* gint_install() - install and start gint */ @@ -104,8 +76,8 @@ void gint_install(void) uint32_t vbr = (uint32_t)&gint_vbr; /* Event handler entry points */ - extern void inth_entry_7705(void); - extern void inth_entry_7305(void); + void *exch_entry; + void *inth_entry; /* First save the hardware configuration. This step is crucial because we don't want the system to find out about us directly manipulating @@ -115,7 +87,21 @@ void gint_install(void) /* Load the event handler entry points into memory */ /* TODO: Load an exception handler and a TLB miss handler */ - void *inth_entry = isSH3() ? inth_entry_7705 : inth_entry_7305; + mpuSwitch({ + /* SH3-based */ + extern void exch_entry_7705(void); + extern void inth_entry_7705(void); + exch_entry = exch_entry_7705; + inth_entry = inth_entry_7705; + },{ + /* SH4-based */ + extern void exch_entry_7305(void); + extern void inth_entry_7305(void); + exch_entry = exch_entry_7305; + inth_entry = inth_entry_7305; + }); + + memcpy((void *)(vbr + 0x100), exch_entry, 32); memcpy((void *)(vbr + 0x600), inth_entry, 32); /* Time to switch VBR and roll! */ @@ -126,6 +112,12 @@ void gint_install(void) static void unlock(void) { gint_ctx_restore(&sys_ctx); + + /* Restore all driver settings */ + for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++) + { + drv->ctx_restore(drv->sys_ctx); + } } /* gint_unload() - unload gint and give back control to the system */ diff --git a/src/core/start.c b/src/core/start.c index 48c0445..7b8bf3d 100644 --- a/src/core/start.c +++ b/src/core/start.c @@ -118,7 +118,6 @@ int start(int isappli, int optnum) mpu_init(); #endif - /* Load data sections and wipe the bss section. This has to be done first for static and global variables to be initialized */ regcpy(lgdata, sgdata, rgdata); @@ -129,23 +128,25 @@ int start(int isappli, int optnum) bootlog_loaded(); #endif - /* TODO: Do the TLB investigation on SH3 (UTLB things are SH4 only) */ - /* Traverse all ROM pages */ explore(brom, srom); + /* Count how much memory got mapped from this process */ uint32_t rom, ram; - utlb_mapped_memory(&rom, &ram); + mpuSwitch( + tlb_mapped_memory(&rom, &ram), /* SH3-based */ + utlb_mapped_memory(&rom, &ram) /* SH4-based */ + ); + + #ifdef GINT_BOOT_LOG + bootlog_mapped(rom, ram); + #endif //--- - #ifdef GINT_BOOT_LOG - isSH3() ? bootlog_mapped(-1, -1) - : bootlog_mapped(rom, ram); - #endif - /* Cancel add-in execution if not all pages are mapped - TODO: Resort to better graphical display */ + TODO: Resort to better graphical display, but still fxlib since + add-in is not mapped yet */ if(rom < (uint32_t)&srom) { Bdisp_AllClr_VRAM(); @@ -176,7 +177,9 @@ int start(int isappli, int optnum) /* Initialize all drivers by saving the system settings */ for(drv = &bdrv; drv < &edrv; drv++) { + if(isSH3() && drv->driver_sh3) drv->driver_sh3(); drv->ctx_save(drv->sys_ctx); + if(drv->init) drv->init(); } /* With gint fully initialized, we are ready to start the hosted user @@ -195,15 +198,10 @@ int start(int isappli, int optnum) /* Before leaving the application, we need to clean up our mess. We have changed many OS settings while accessing the peripheral modules. The OS is bound to be confused (and crash) if we don't - resoture them. */ + restore them */ - /* Restore all driver settings */ - for(drv = &drv; drv < &edrv; drv++) - { - drv->ctx_restore(drv->sys_ctx); - } - - /* Finally, unload gint and give back control to the system */ + /* Unload gint and give back control to the system. Driver settings + will be restored while interrupts are disabled */ gint_unload(); /* TODO: Invoke main menu instead of returning? */ diff --git a/src/r61524/r61524.c b/src/r61524/r61524.c index 461e66e..eb2c6d5 100644 --- a/src/r61524/r61524.c +++ b/src/r61524/r61524.c @@ -66,8 +66,8 @@ INLINE static void select(uint16_t reg) /* Set RS back. We don't do this in read()/write() because the display driver is optimized for consecutive GRAM access. LCD-transfers will - be faster when executing select() followed by many write(). - (Most applications should use DMA though.) */ + be faster when executing select() followed by several calls to + write(). (Although most applications should use the DMA instead.) */ *PRDR |= 0x10; synco(); } @@ -126,7 +126,7 @@ void r61524_test(void) { print(1, 2, "Aborting."); Bdisp_PutDisp_DD(); - delay(20); + delay(40); return; } @@ -194,7 +194,7 @@ void r61524_test(void) print_hex(12, 6, COL, 1); Bdisp_PutDisp_DD(); - delay(10); + delay(50); //--- @@ -211,7 +211,12 @@ void r61524_test(void) print_hex(14, 3, VEA, 3); Bdisp_PutDisp_DD(); - delay(20); + delay(50); + + // + + Bdisp_AllClr_VRAM(); + Bdisp_PutDisp_DD(); //--- @@ -227,57 +232,24 @@ void r61524_test(void) //--- -// Bdisp_AllClr_VRAM(); -// Bdisp_PutDisp_DD(); - select(ram_address_horizontal); write(HEA); select(ram_address_vertical); write(VSA); select(write_data); - uint16_t color = 0xf3e4; - for(int v = 0; v < 396; v++) - for(int h = 0; h < 224; h++) + uint16_t color; + for(int v = 0; v < 224; v++) + for(int h = 0; h < 396; h++) { - color = (v << 7) ^ h ^ 0x3754; +// int offset = 396 * v + h; +// uint8_t *src = gimp_image.pixel_data + 2 * offset; + color = 0x3eb7; // (src[1] << 8) | src[0]; + write(color); } - delay(60); - - //--- - - /* Shift by 9, not 8, because of horizontal/vertical range inversion */ - select(ram_address_horizontal); - AD = read(); - select(ram_address_vertical); - AD |= read() << 9; - - select(horizontal_ram_start); - HSA = read(); - select(horizontal_ram_end); - HEA = read(); - - select(vertical_ram_start); - VSA = read(); - select(vertical_ram_end); - VEA = read(); - - Bdisp_AllClr_VRAM(); - - print(1, 1, "Address=?????"); - print_hex(9, 1, AD, 5); - - print(1, 2, "HSA=??? HEA=???"); - print_hex(5, 2, HSA, 3); - print_hex(14, 2, HEA, 3); - print(1, 3, "VSA=??? VEA=???"); - print_hex(5, 3, VSA, 3); - print_hex(14, 3, VEA, 3); - - Bdisp_PutDisp_DD(); - delay(10); + delay(200); } //--- @@ -330,6 +302,7 @@ static void ctx_restore(void *buf) gint_driver_t drv_r61524 = { .name = "Renesas R61524", + .init = NULL, .ctx_size = sizeof(ctx_t), .sys_ctx = &sys_ctx, .ctx_save = ctx_save, diff --git a/src/t6k11/t6k11.c b/src/t6k11/t6k11.c index 97207bc..e58a026 100644 --- a/src/t6k11/t6k11.c +++ b/src/t6k11/t6k11.c @@ -140,16 +140,15 @@ void t6k11_backlight(int setting) /* This setting is mapped to an I/O port: - On SH3, bit 7 of port G - On SH4, bit 4 of port N */ - if(isSH3()) - { + mpuSwitch({ + /* SH3-based */ port = (void *)0xa400012c; mask = 0x80; - } - else - { + },{ + /* SH4-based */ port = (void *)0xa4050138; mask = 0x10; - } + }); if(!setting) *port &= ~mask; if(setting > 0) *port |= mask; @@ -198,6 +197,7 @@ static void ctx_restore(void *buf) gint_driver_t drv_t6k11 = { .name = "Toshiba T6K11", + .init = NULL, .ctx_size = sizeof(ctx_t), .sys_ctx = &sys_ctx, .ctx_save = ctx_save, diff --git a/src/tmu/inth.s b/src/tmu/inth.s new file mode 100644 index 0000000..1bc7a40 --- /dev/null +++ b/src/tmu/inth.s @@ -0,0 +1,225 @@ +/* +** gint:tmu:inth - Interrupt handlers for the timer units +** Perhaps the most technical of my interrupt handlers. They implement a +** simple kind of interrupt handler communication by letting the control flow +** from each interrupt handler to the next. +*/ + + /* Gates for the standard Timer Unit (TMU) */ + .global _inth_tmu_0 + .global _inth_tmu_1 + .global _inth_tmu_2 + .global _inth_tmu_storage + + /* Gates for the extra timers (informally called ETMU) */ + .global _inth_tmu_extra2 + .global _inth_tmu_extra_help + .global _inth_tmu_extra_others + + .section .gint.blocks, "ax" + .align 4 + +/* TMU INTERRUPT HANDLERS - 128 BYTES + Unfortunately I did not manage to write a handler that cleared the interrupt + flag and invoked a callback in less than 34 bytes data included. So I + decided to make several gates operate as a whole and add a bit more features + in them. Basically, these handlers: + - Clear the interrupt flag + - Invoke a callback function and pass it a user-provided argument + - Stop the timer if the callback returns non-zero + - Host their own callback and arguments + + It is important to notice that the code of the following gates looks like + they are contiguous in memory. The assembler will make that assumption, and + turn any address reference between two gates into a *relative displacement*. + If the gates don't have the same relative location at runtime, the code will + crash because we will have broken the references. This is why we can only do + it with handlers that are mapped to consecutive event codes. */ + +/* FIRST GATE - TMU0 entry, clear underflow flag and call back */ +_inth_tmu_0: + mova .storage0, r0 + mov #-2, r1 + + /*** This is the first shared section ***/ + +.clearflag: + mov.l r1, @-r15 + + /* Load the TCR address and clear the interrupt flag */ + mov.l .mask, r3 + mov.l @(8, r0), r1 + mov.w @r1, r2 + and r3, r2 + mov.w r2, @r1 + + /* Invoke the callback function and pass the argument */ + sts.l pr, @-r15 + mov.l @r0, r1 + jsr @r1 + mov.l @(4, r0), r4 + lds.l @r15+, pr + + /* Prepare stopping the timer */ + mov.l .tstr, r5 + bra .stoptimer + mov.l @r15+, r1 + +/* SECOND GATE - TMU1 entry and stop timer */ +_inth_tmu_1: + mova .storage1, r0 + bra .clearflag + mov #-3, r1 + + /*** This is the second shared section ***/ + +.stoptimer: + /* Stop the timer if the return value is not zero */ + tst r0, r0 + bt .end + mov.b @r5, r2 + and r1, r2 + mov.b r2, @r5 + +.end: + rte + nop + + .zero 12 + +/* THIRD GATE - TMU2 entry and storage for TMU0 */ +_inth_tmu_2: + mova .storage2, r0 + bra .clearflag + mov #-5, r1 + + .zero 14 + +.storage0: + .long 0 /* Callback: Configured dynamically */ + .long 0 /* Argument: Configured dynamically */ + .long 0xa4490010 /* TCR0: Overridden at startup on SH3 */ + +/* FOURTH GATE - Storage for TMU1, TMU2 and other values */ +_inth_tmu_storage: + +.mask: .long 0x0000feff +.tstr: .long 0xa4490004 /* TSTR: Overridden at startup on SH3 */ + +.storage1: + .long 0 /* Callback: Configured dynamically */ + .long 0 /* Argument: Configured dynamically */ + .long 0xa449001c /* TCR1: Overridden at startup on SH3 */ + +.storage2: + .long 0 /* Callback: Configured dynamically */ + .long 0 /* Argument: Configured dynamically */ + .long 0xa4490028 /* TCR2: Overridden at startup on SH3 */ + + + +/* EXTRA TMU INTERRUPT HANDLERS - 96 BYTES + To implement the same functionalities as the standard timers, several blocks + are once again needed. But the handlers for the extra timers are not located + in adjacent gates, except for ETMU1 and ETMU2 which have event codes 0xc20 + and 0xc40. Since handler 0xc60 is free on both SH3 and SH4, I'm using it to + build a three-handler block and achieve the same result as above. + + On SH4 this means that an extra gate has to be installed, but no interrupt + pointed here. On SH3 this means that four gates are used for the only extra + timer, but the incurred cost is minimal (96 bytes on the binary file) + because the size of the VBR area can hardly be shrunk anyway. + + It *is* possible to do generalized communication between interrupt handlers + that do not reside in consecutive gates. The general way of performing a + jump or data access between two interrupt handlers would be to store at + runtime the address of the target resource in a reserved longword in the + source handler. But longwords are costly in 32-byte areas. Even if the event + codes of the interrupt handlers are known at development time, the best I + can think of is hardcoding the relative displacements, and one would need to + use the unnatural and unmaintainable @(disp, pc) addressing modes. */ + +/* FIRST GATE - ETMU2 entry, clear flag and prepare callback */ +_inth_tmu_extra2: + /* Warning: the size of this section (2 bytes) is hardcoded in another + interrupt handler, _inth_tmu_extra_others */ + mova .storage_extra_1, r0 + +.extra_callback: + mov.l r8, @-r15 + mov r0, r8 + stc.l gbr, @-r15 + + /* Invoke the callback function */ + sts.l pr, @-r15 + mov.l @r8, r1 + jsr @r1 + mov.l @(4, r8), r4 + + bra .extra_clearflag + lds.l @r15+, pr + +.storage_extra_1: + .long 0 /* Callback: Configured dynamically */ + .long 0 /* Argument: Configured dynamically */ + .long 0 /* Structure address: Edited at startup */ + +/* SECOND GATE - Helper entry, invoke callback and stop timer if requested */ +_inth_tmu_extra_help: + +.extra_clearflag: + mov r0, r5 + + /* Load struture address */ + mov.l @(8, r8), r0 + ldc r0, gbr + mov #12, r0 + +.extra_loopclear: + /* Aggressively clear the interrupt flag. The loop is required because + clearing takes "some time". The system does it. Not doing it will + cause the interrupt to be triggered again until the flag is cleared, + which can be ~20 interrupts if the handler is fast! */ + and.b #0xfd, @(r0, gbr) + tst.b #0x02, @(r0, gbr) + bf .extra_loopclear + + /* Check whether to stop the timer */ + tst r5, r5 + bt .extra_end + +.extra_stoptimer: + mov.b @(0, gbr), r0 + and #0xfe, r0 + mov.b r0, @(0, gbr) + +.extra_end: + ldc.l @r15+, gbr + mov.l @r15+, r8 + rte + nop + +/* FOURTH GATE - All other ETMU entries, deferred to the previous ones */ +_inth_tmu_extra_others: + /* Dynamically compute the target of the jump */ + stc vbr, r1 + mov.l 1f, r2 + add r2, r1 + + mova .storage_extra_others, r0 + jmp @r1 + nop + + /* Offset from VBR where extra timer 2 is located: + - 0x600 to reach the interrupt handlers + - 0x020 to jump over the entry gate + - 0x840 to reach the handler of extra timer 2 + - 0x002 to jump over the first part of its code */ +1: .long 0xe62 + + .zero 4 + +.storage_extra_others: + .long 0 /* Callback: Configured dynamically */ + .long 0 /* Argument: Configured dynamically */ + .long 0 /* Structure address: Edited at startup */ diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c new file mode 100644 index 0000000..dd7a95d --- /dev/null +++ b/src/tmu/tmu.c @@ -0,0 +1,429 @@ +//--- +// gint:tmu - Timer operation +//--- + +#include +#include +#include + +#include + +#include +#include + +//--- +// Timer structures +//--- + +/* tmu_t - a single timer from a standard timer unit */ +typedef volatile struct +{ + uint32_t TCOR; /* Constant register */ + uint32_t TCNT; /* Counter register, counts down */ + + word_union(TCR, + uint16_t :7; + uint16_t UNF :1; /* Underflow flag */ + uint16_t :2; + uint16_t UNIE :1; /* Underflow interrupt enable */ + uint16_t CKEG :2; /* Input clock edge */ + uint16_t TPSC :3; /* Timer prescaler (input clock) */ + ); + +} PACKED(4) tmu_t; + +/* tmu_extra_t - extra timers on sh7337, sh7355 and sh7305 */ +typedef volatile struct +{ + uint8_t TSTR; /* Only bit 0 is used */ + pad(3); + + uint32_t TCOR; /* Constant register */ + uint32_t TCNT; /* Counter register */ + + byte_union(TCR, + uint8_t :6; + uint8_t UNF :1; /* Underflow flag */ + uint8_t UNIE :1; /* Underflow interrupt enable */ + ); + +} PACKED(4) tmu_extra_t; + +/* inth_data_t - data storage inside interrupt handlers */ +typedef struct +{ + int (*callback)(void *arg); /* User-provided callback function */ + void *arg; /* Argument for [callback] */ + volatile void *structure; /* Either TCR or timer address */ + +} PACKED(4) inth_data_t; + +/* timer_t - all data required to run a single timer */ +typedef struct +{ + void *tmu; /* Address of timer structure */ + inth_data_t *data; /* Interrupt handler data */ + uint16_t event; /* Interrupt event code */ +} timer_t; + + +//--- +// Driver storage +//--- + +/* This is the description of the structure on SH4. SH3-based fx9860g models, + which are already very rare, will adapt the values in init functions */ +GDATA static timer_t timers[9] = { + { .tmu = (void *)0xa4490008, .event = 0x400 }, + { .tmu = (void *)0xa4490014, .event = 0x420 }, + { .tmu = (void *)0xa4490020, .event = 0x440 }, + { .tmu = (void *)0xa44d0030, .event = 0x9e0 }, + { .tmu = (void *)0xa44d0050, .event = 0xc20 }, + { .tmu = (void *)0xa44d0070, .event = 0xc40 }, + { .tmu = (void *)0xa44d0090, .event = 0x900 }, + { .tmu = (void *)0xa44d00b0, .event = 0xd00 }, + { .tmu = (void *)0xa44d00d0, .event = 0xfa0 }, +}; + +/* TSTR register for standard timers */ +GDATA static volatile uint8_t *TSTR = (void *)0xa4490004; + +//--- +// Timer API +//--- + +/* timer_setup() - set up a timer */ +int timer_setup(int tid, uint32_t delay, timer_input_t clock, + int (*callback)(void *arg), void *arg) +{ + /* We need to distinguish normal and extra timers */ + if(tid < 3) + { + /* Refuse to setup timers that are already in use */ + tmu_t *t = timers[tid].tmu; + if(t->TCR.UNIE) return -1; + + /* Configure the registers of the target timer */ + t->TCOR = delay; + t->TCNT = delay; + t->TCR.TPSC = clock; + + /* Clear the interrupt flag */ + do t->TCR.UNF = 0; + while(t->TCR.UNF); + + t->TCR.UNIE = 1; /* Enable interrupt on underflow */ + t->TCR.CKEG = 0; /* Count on rising edge (SH7705) */ + } + + /* Extra timers have a simpler structure */ + else + { + tmu_extra_t *t = timers[tid].tmu; + if(t->TCR.UNIE) return -1; + + /* There is no clock input and no clock edge settings */ + t->TCOR = delay; + t->TCNT = delay; + + /* Clear the interrupt flag */ + do t->TCR.UNF = 0; + while(t->TCR.UNF); + + t->TCR.UNIE = 1; + } + + /* Register the callback and its argument */ + timers[tid].data->callback = callback; + timers[tid].data->arg = arg; + + /* Return the timer id, since configuration was successful */ + return tid; +} + +/* timer_delay() - compute a delay constant from a duration in seconds */ +uint32_t timer_delay(int tid, int delay_us) +{ + /* TODO: Proper timer_delay() */ +#ifdef FX9860G + uint64_t freq = 14750000 >> 2; /* 14.75 MHz / 4 */ +#else + uint64_t freq = 29020000 >> 2; /* 29.49 MHz / 4 */ +#endif + if(tid >= 3) freq = 32768; /* 32768 Hz */ + + uint64_t product = freq * (uint64_t)delay_us; + return product / 1000000; +} + +/* timer_control() - start or stop a timer + @timer Timer ID to configure + @state 0 to start the timer, 1 to stop it (nothing else!) */ +static void timer_control(int tid, int state) +{ + if(tid < 3) + { + /* For standard timers, use the MPU's TSTR register */ + *TSTR = (*TSTR | (1 << tid)) ^ (state << tid); + } + else + { + /* Extra timers all have their own TSTR register */ + tmu_extra_t *t = timers[tid].tmu; + t->TSTR = (t->TSTR | 1) ^ state; + } +} + +/* timer_start() - start a configured timer */ +void timer_start(int tid) +{ + timer_control(tid, 0); +} + +/* timer_reload() - change a timer's constant register for next interrupts */ +void timer_reload(int tid, uint32_t delay, timer_input_t clock) +{ + if(tid < 3) ((tmu_t *)timers[tid].tmu)->TCOR = delay; + else ((tmu_extra_t *)timers[tid].tmu)->TCOR = delay; +} + +/* timer_pause() - stop a running timer */ +void timer_pause(int tid) +{ + timer_control(tid, 1); +} + +/* timer_free() - free a timer */ +void timer_free(int tid) +{ + /* Stop the timer and disable UNIE to indicate that it's free */ + timer_pause(tid); + + if(tid < 3) + { + tmu_t *t = timers[tid].tmu; + t->TCR.UNIE = 0; + } + else + { + tmu_extra_t *t = timers[tid].tmu; + t->TCR.UNIE = 0; + + /* Also clear TCOR and TCNT to avoid spurious interrupts */ + t->TCOR = 0xffffffff; + t->TCNT = 0xffffffff; + + do t->TCR.UNF = 0; + while(t->TCR.UNF); + } +} + +//--- +// Driver initialization +//--- + +/* Interrupt handlers provided by tmu/inth.s for standard timers */ +extern void inth_tmu_0(void); +extern void inth_tmu_1(void); +extern void inth_tmu_2(void); +extern void inth_tmu_storage(void); + +/* Interrupt handlers provided by tmu/inth.s for extra timers */ +extern void inth_tmu_extra1(void); +extern void inth_tmu_extra2(void); +extern void inth_tmu_extra_help(void); +extern void inth_tmu_extra_others(void); + +static void init(void) +{ + /* Install the standard's TMU interrupt handlers. By chance TMU gates + use the same event codes on SH7705 and SH7305 */ + UNUSED void *h0, *h1, *h2, *hs; + h0 = gint_inthandler(0x400, inth_tmu_0); + h1 = gint_inthandler(0x420, inth_tmu_1); + h2 = gint_inthandler(0x440, inth_tmu_2); + hs = gint_inthandler(0x460, inth_tmu_storage); + + /* User information in interrupt handlers */ + timers[0].data = h2 + 20; + timers[1].data = hs + 8; + timers[2].data = hs + 20; + + /* SH3: Override the address of TSTR in the interrupt handler helper */ + if(isSH3()) *(volatile uint8_t **)(hs + 4) = TSTR; + + /* Stop all timers */ + *TSTR = 0; + + /* This driver uses the UNIE (UNderflow Interrupt Enable) bit of the + TCR register to indicate which timers are being used; reset them */ + for(int i = 0; i < 3; i++) + { + tmu_t *t = timers[i].tmu; + t->TCR.UNIE = 0; + + do t->TCR.UNF = 0; + while(t->TCR.UNF); + + /* Standard timers: TCR is provided to the interrupt handler */ + timers[i].data->structure = &t->TCR; + } + + /* Clear the extra timers */ + for(int i = 3; i < timer_count(); i++) + { + tmu_extra_t *t = timers[i].tmu; + + /* This is *extremely important*: extra timers will generate + interrupts as long as TCNT = 0 *regardless of TSTR*! */ + t->TCOR = 0xffffffff; + t->TCNT = 0xffffffff; + + t->TCR.UNIE = 0; + t->TSTR = 0; + + do t->TCR.UNF = 0; + while(t->TCR.UNF); + } + + /* Install the extra timers. We need three extra timers for the + interrupt handlers to work, so install 3 on SH3, even if only one + actually exists */ + int limit = isSH3() ? 6 : 9; + + for(int i = 3; i < limit; i++) + { + void *handler = (i == 5) + ? inth_tmu_extra2 + : inth_tmu_extra_others; + void *h = gint_inthandler(timers[i].event, handler); + + timers[i].data = h + 20; + timers[i].data->structure = timers[i].tmu; + } + + /* Also install the helper handler */ + gint_inthandler(0xc60, inth_tmu_extra_help); + + /* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */ + gint_intlevel(0, 13); + gint_intlevel(1, 11); + gint_intlevel(2, 9); + + /* Enable the extra TMUs at level 7 */ + mpuSwitch({ + /* SH3-based */ + gint_intlevel(23, 7); + },{ + /* SH4-based */ + gint_intlevel(36, 7); + gint_intlevel(25, 7); + gint_intlevel(26, 7); + gint_intlevel(18, 7); + gint_intlevel(32, 7); + gint_intlevel(44, 7); + + /* Unmask the interrupts */ + INTC4.MSKCLR->IMR2 = 0x01; + INTC4.MSKCLR->IMR5 = 0x06; + INTC4.MSKCLR->IMR6 = 0x18; + INTC4.MSKCLR->IMR8 = 0x02; + }); +} + +//--- +// Context system for this driver +//--- + +typedef struct +{ + tmu_t std[3]; + tmu_extra_t extra[6]; + uint8_t TSTR; + +} PACKED(4) ctx_t; + +/* Allocate a system buffer in gint's BSS area */ +GBSS static ctx_t sys_ctx; + +#ifdef FX9860G +static void driver_sh3(void) +{ + timers[0].tmu = (void *)0xfffffe94; + timers[1].tmu = (void *)0xfffffea0; + timers[2].tmu = (void *)0xfffffeac; + /* We don't need to change the event code of ETMU0 since it's + translated to the SH4 code by the interrupt handler */ + timers[3].tmu = (void *)0xa44c0030; + + TSTR = (void *)0xfffffe92; +} +#endif + +static void ctx_save(void *buf) +{ + ctx_t *ctx = buf; + + for(int i = 0; i < 3; i++) + { + tmu_t *t = timers[i].tmu; + ctx->std[i].TCOR = t->TCOR; + ctx->std[i].TCNT = t->TCNT; + ctx->std[i].TCR.word = t->TCR.word; + } + + ctx->TSTR = *TSTR; + + for(int i = 0; i < timer_count() - 3; i++) + { + tmu_extra_t *t = timers[i + 3].tmu; + ctx->extra[i].TCOR = t->TCOR; + ctx->extra[i].TCNT = t->TCNT; + ctx->extra[i].TCR.byte = t->TCR.byte; + ctx->extra[i].TSTR = t->TSTR; + } +} + +static void ctx_restore(void *buf) +{ + ctx_t *ctx = buf; + + for(int i = 0; i < 3; i++) + { + tmu_t *t = timers[i].tmu; + t->TCNT = ctx->std[i].TCNT; + t->TCOR = ctx->std[i].TCOR; + t->TCR.word = ctx->std[i].TCR.word; + } + + *TSTR = ctx->TSTR; + + for(int i = 0; i < timer_count() - 3; i++) + { + tmu_extra_t *t = timers[i + 3].tmu; + /* Avoid some interrupts that occur when TCNT = 0 */ + t->TSTR = 0; + t->TCNT = 0xffffffff; + + t->TCNT = ctx->extra[i].TCNT; + t->TCOR = ctx->extra[i].TCOR; + t->TCR.byte = ctx->extra[i].TCR.byte; + t->TSTR = ctx->extra[i].TSTR; + } +} + +//--- +// Driver structure definition +//--- + +gint_driver_t drv_tmu = { + .name = "Timer Unit", + .init = init, + .ctx_size = sizeof(ctx_t), + .sys_ctx = &sys_ctx, + .driver_sh3 = GINT_DRIVER_SH3(driver_sh3), + .ctx_save = ctx_save, + .ctx_restore = ctx_restore, +}; + +GINT_DECLARE_DRIVER(drv_tmu); From b20fcc4c7c29b7ad50bd3109334989d67f31bc72 Mon Sep 17 00:00:00 2001 From: lephe Date: Sun, 19 Aug 2018 17:11:37 +0200 Subject: [PATCH 24/24] More timers, RTC, basic overclock-resistant keyboard, CPG, PFC, driver levels. --- fx9860g.ld | 12 ++- fxcg50.ld | 12 ++- include/core/cpg.h | 80 ++++++++++++++++ include/core/mpu.h | 13 +-- include/core/pfc.h | 77 +++++++++++++++ include/defs/attributes.h | 3 + include/gint/clock.h | 76 ++++++++++++++- include/gint/drivers.h | 19 +++- include/gint/gint.h | 27 +++--- include/gint/keyboard.h | 67 +++++++++++++ include/gint/rtc.h | 82 ++++++++++++++++ include/gint/timer.h | 30 ++---- include/mod/rtc.h | 76 +++++++++++++++ src/clock/freq.c | 136 +++++++++++++++++++++++++++ src/clock/sleep.c | 0 src/core/bootlog.c | 11 ++- src/core/gint.c | 11 +-- src/core/setup.c | 61 ++++++------ src/core/start.c | 15 ++- src/keysc/iokbd.c | 114 +++++++++++++++++++++++ src/keysc/keysc.c | 188 +++++++++++++++++++++++++++++++++++++ src/r61524/r61524.c | 2 +- src/rtc/inth.s | 60 ++++++++++++ src/rtc/rtc.c | 191 ++++++++++++++++++++++++++++++++++++++ src/t6k11/t6k11.c | 13 +-- src/tmu/tmu.c | 99 +++++++++++--------- 26 files changed, 1326 insertions(+), 149 deletions(-) create mode 100644 include/core/cpg.h create mode 100644 include/core/pfc.h create mode 100644 include/gint/keyboard.h create mode 100644 include/gint/rtc.h create mode 100644 include/mod/rtc.h create mode 100644 src/clock/sleep.c create mode 100644 src/keysc/iokbd.c create mode 100644 src/keysc/keysc.c create mode 100644 src/rtc/inth.s create mode 100644 src/rtc/rtc.c diff --git a/fx9860g.ld b/fx9860g.ld index dba5111..3a45257 100644 --- a/fx9860g.ld +++ b/fx9860g.ld @@ -19,9 +19,9 @@ MEMORY /* This is mapped to RAM; 8k on SH3, apparently 32k on SH4 */ ram (rw): o = 0x08100000, l = 8k /* gint's VBR space, mentioned here for completeness */ - vbr (rwx): o = 0x8800e000, l = 4k + vbr (rwx): o = 0x8800e000, l = 5k /* Some RAM region from P1 area; gint's data will reside here */ - rram (rwx): o = 0x8800f000, l = 4k + rram (rwx): o = 0x8800f400, l = 3k } SECTIONS @@ -76,7 +76,13 @@ SECTIONS driver, even if the symbols are not referenced */ .gint.drivers : { _bdrv = . ; - KEEP(*(.gint.drivers)); + KEEP(*(.gint.drivers.0)); + KEEP(*(.gint.drivers.1)); + KEEP(*(.gint.drivers.2)); + KEEP(*(.gint.drivers.3)); + KEEP(*(.gint.drivers.4)); + KEEP(*(.gint.drivers.5)); + KEEP(*(.gint.drivers.6)); _edrv = . ; } > rom diff --git a/fxcg50.ld b/fxcg50.ld index c35132d..251cff0 100644 --- a/fxcg50.ld +++ b/fxcg50.ld @@ -18,9 +18,9 @@ MEMORY The first 0x2000 bytes are reserved by gint, see below */ ram (rw): o = 0x08102000, l = 512k /* gint's VBR space, mentioned here for completeness */ - vbr (rwx): o = 0x8c160000, l = 4k + vbr (rwx): o = 0x8c160000, l = 5k /* Some RAM region from P1 area; gint's data will reside here */ - rram (rwx): o = 0x8c161000, l = 4k + rram (rwx): o = 0x8c161400, l = 3k } SECTIONS @@ -68,7 +68,13 @@ SECTIONS driver, even if the symbols are not referenced */ .gint.drivers : { _bdrv = . ; - KEEP(*(.gint.drivers)); + KEEP(*(.gint.drivers.0)); + KEEP(*(.gint.drivers.1)); + KEEP(*(.gint.drivers.2)); + KEEP(*(.gint.drivers.3)); + KEEP(*(.gint.drivers.4)); + KEEP(*(.gint.drivers.5)); + KEEP(*(.gint.drivers.6)); _edrv = . ; } > rom diff --git a/include/core/cpg.h b/include/core/cpg.h new file mode 100644 index 0000000..07d5244 --- /dev/null +++ b/include/core/cpg.h @@ -0,0 +1,80 @@ +//--- +// gint:core:cpg - Clock Pulse Generator +//--- + +#ifndef GINT_CORE_CPG +#define GINT_CORE_CPG + +#include + +//--- +// SH7705 Clock Pulse Generator. Refer to: +// "Renesas SH7705 Group Hardware Manual" +// Section 9: "Interrupt Controller (INTC)" +//--- + +/* sh7705_cpg_t - Clock Pulse Generator registers */ +typedef volatile struct +{ + word_union(FRQCR, + uint16_t :3; + uint16_t CKOEN :1; /* Clock Output Enable */ + uint16_t :2; + uint16_t STC :2; /* PLL multiplication ratio */ + uint16_t :2; + uint16_t IFC :2; /* Internal clock divider */ + uint16_t :2; + uint16_t PFC :2; /* Peripheral clock divider */ + ); + +} PACKED(4) sh7705_cpg_t; + +#define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80)) + +//--- +// SH7305 Clock Pulse Generator. Refer to: +// "Renesas SH7724 User's Manual: Hardware" +// Section 17: "Clock Pulse Generator (CPG)" +//--- + +/* sh7305_cpg_t - Clock Pulse Generator registers + Fields marked with [*] don't have the meaning described in the SH7724 + documentation. */ +typedef volatile struct +{ + lword_union(FRQCRA, + uint32_t KICK :1; /* Flush FRQCRA modifications */ + uint32_t :1; + uint32_t STC :6; /* PLL multiplication [*] */ + uint32_t IFC :4; /* Iphi divider 1 [*] */ + uint32_t :4; + uint32_t SFC :4; /* Sphi divider 1 [*] */ + uint32_t BFC :4; /* Bphi divider 1 [*] */ + uint32_t :4; + uint32_t P1FC :4; /* Pphi divider 1 [*] */ + ); + pad(0x20); + + lword_union(PLLCR, + uint32_t :17; + uint32_t PLLE :1; /* PLL Enable */ + uint32_t :1; + uint32_t FLLE :1; /* FLL Enable */ + uint32_t :10; + uint32_t CKOFF :1; /* CKO Output Stop */ + uint32_t :1; + ); + pad(0x28); + + lword_union(FLLFRQ, + uint32_t :16; + uint32_t SELXM :2; /* FLL output division */ + uint32_t :3; + uint32_t FLF :11; /* FLL Multiplication Ratio */ + ); + +} PACKED(4) sh7305_cpg_t; + +#define SH7305_CPG (*((sh7305_cpg_t *)0xa4150000)) + +#endif /* GINT_CORE_CPG */ diff --git a/include/core/mpu.h b/include/core/mpu.h index 95486e3..c963eb6 100644 --- a/include/core/mpu.h +++ b/include/core/mpu.h @@ -3,13 +3,10 @@ // // This component detects the architecture and MPU type of the underlying // hardware by relying on version registers and/or side-information. It -// provides macros isSH3() and isSH4(), but the best way of performing -// MPU-dependent jobs is to use mpuSwitch(): +// provides macros isSH3() and isSH4() for MPU-dependent jobs // -// mpuSwitch( -// print("SH3 code"), -// print("SH4 code") -// ); +// if(isSH3()) print("SH3 code"); +// else print("SH4 code"); //--- #ifndef GINT_CORE_MPU @@ -40,8 +37,6 @@ typedef enum #define isSH3() (mpu_id() & 1) #define isSH4() (!isSH3()) - #define mpuSwitch(a, b) do { if(isSH3()) { a; } else { b; } } while(0) - /* mpu_init() - probe the MPU type This function must be executed before mpu_id() can be used. */ void mpu_init(void); @@ -53,8 +48,6 @@ typedef enum #define isSH3() 0 #define isSH4() 1 - #define mpuSwitch(a, b) do { b; } while(0) - #endif /* FX9860G */ #endif /* GINT_CORE_MPU */ diff --git a/include/core/pfc.h b/include/core/pfc.h new file mode 100644 index 0000000..bcee78b --- /dev/null +++ b/include/core/pfc.h @@ -0,0 +1,77 @@ +//--- +// gint:core:pfc - Pin Function Controller +// +// The Pin Function Controller has a simple register interface, the main +// difficulty is still understanding the role of its pins. +//--- + +#ifndef GINT_CORE_PFC +#define GINT_CORE_PFC + +#include + +//--- +// SH7705 Pin Function Controller. Refer to: +// "Renesas SH7705 Group Hardware Manual" +// Section 19: "Pin Function Controller" +//--- + +typedef volatile struct +{ + /* Control registers */ + uint16_t PACR; + uint16_t PBCR; + uint16_t PCCR; + uint16_t PDCR; + uint16_t PECR; + uint16_t PFCR; + uint16_t PGCR; + uint16_t PHCR; + uint16_t PJCR; + uint16_t PKCR; + uint16_t PLCR; + uint16_t SCPCR; /* Port SC control register */ + uint16_t PMCR; + uint16_t PNCR; + + pad(4); + + /* Data registers */ + uint8_t PADR; + pad(1); + uint8_t PBDR; + pad(1); + uint8_t PCDR; + pad(1); + uint8_t PDDR; + pad(1); + uint8_t PEDR; + pad(1); + uint8_t PFDR; + pad(1); + uint8_t PGDR; + pad(1); + uint8_t PHDR; + pad(1); + uint8_t PJDR; + pad(1); + uint8_t PKDR; + pad(1); + uint8_t PLDR; + pad(1); + uint8_t SCPDR; /* Port SC data register */ + pad(1); + uint8_t PMDR; + pad(1); + uint8_t PNDR; + pad(1); + +} PACKED(4) sh7705_pfc_t; + +#define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100)) + +//--- +// TODO: Document the SH7305 Pin Function Controller +//--- + +#endif /* GINT_CORE_PFC */ diff --git a/include/defs/attributes.h b/include/defs/attributes.h index 67d45a2..9986936 100644 --- a/include/defs/attributes.h +++ b/include/defs/attributes.h @@ -30,6 +30,9 @@ #define CTOR(x) __attribute__((constructor ((x) + 101))) PRETEXT #define DTOR(x) __attribute__((destructor ((x) + 101))) +/* Aligned variables */ +#define ALIGNED(x) __attribute__((aligned(x))) + /* Packed structures. I require explicit alignment because if it's unspecified, GCC cannot optimize access size, and reads to memory-mapped I/O with invalid access sizes silently fail - honestly you don't want this to happen */ diff --git a/include/gint/clock.h b/include/gint/clock.h index 959c76a..eeb3f00 100644 --- a/include/gint/clock.h +++ b/include/gint/clock.h @@ -1,8 +1,82 @@ //--- -// gint:clock - Clock signals +// gint:clock - Clock signals, overclock, and standby modes //--- #ifndef GINT_CLOCK #define GINT_CLOCK +#include + +//--- +// Clock signals +//--- + +/* clock_frequency_t + A dump of the Clock Pulse Generator's (CPG) configuration. Use the MPU + detection functions from to use the correct fields. */ +typedef struct +{ + union { + int PLL1; + int FLL; + }; + union { + int PLL2; + int PLL; + }; + + int Bphi_div; + int Iphi_div; + int Pphi_div; + + union { + int CKIO_f; + int RTCCLK_f; + }; + + int Bphi_f; + int Iphi_f; + int Pphi_f; + +} clock_frequency_t; + +/* clock_freq() - get the frequency of the main clocks + This function returns the address of a static object which is used by the + module; this address never changes. */ +const clock_frequency_t *clock_freq(void); + +//--- +// Overclock +//--- + +/* TODO: All overclock */ + +//--- +// Sleep functions +//--- + +/* sleep() - halt the processor until an event occurs + The function stops the processor until an interrupt is accepted; the + duration is not known in advance. This function should be used when the + add-in is idle, for instance while waiting for keyboard input. */ +void sleep(void); + +/* sleep_us() - sleep for a definite duration in microseconds + + Stops the processor until [delay_us] microseconds have elapsed. Interrupts + may occur during that time (especially timers firing), in which case the + events will be treated as usual. The processor will resume sleeping after + handling them. + + The user may choose the timer used to time out the sleep. Remember that only + timers 0 to 2 have microsecond-level resolution; other timers count in units + of about 30 us. + + @timer Which timer to use to time out the sleep + @us_delay How long to sleep (in microseconds) */ +void sleep_us(int timer, int us_delay); + +/* sleep_ms() - sleep for a definite duration in milliseconds */ +#define sleep_ms(timer, ms_delay) sleep_us(timer, (ms_delay) * 1000) + #endif /* GINT_CLOCK */ diff --git a/include/gint/drivers.h b/include/gint/drivers.h index d52e088..475378e 100644 --- a/include/gint/drivers.h +++ b/include/gint/drivers.h @@ -39,6 +39,11 @@ typedef struct may be set to NULL */ void (*init)(void); + /* unload() - unitialize the driver + This function is called before ctx_restore() when gint is unloaded. + If there is no unload function, the field may be set to NULL */ + void (*unload)(void); + /* Size of a context object for the driver */ uint ctx_size; @@ -63,11 +68,17 @@ typedef struct } PACKED(4) gint_driver_t; /* GINT_DECLARE_DRIVER() - make a driver visible to gint + Use this macro to expose a driver by passing it the name of a gint_driver_t - structure. This macro moves the structure to the .gint.drivers section, - which is automatically traversed at startup */ -#define GINT_DECLARE_DRIVER(name) \ - SECTION(".gint.drivers") extern gint_driver_t name; + structure. This macro moves the structure to the .gint.drivers.* sections, + which are automatically traversed at startup. + + The @level argument represents the priority level: lower numbers mean that + drivers will be loaded sooner. This numbering allows a primitive form of + dependency for drivers. You need to specifiy a level which is strictly + higher than the level of all the drivers you depend on. */ +#define GINT_DECLARE_DRIVER(level, name) \ + SECTION(".gint.drivers." #level) extern gint_driver_t name; /* GINT_DRIVER_SH3() - declare a function for SH3-rectification This macros allows the argument function to not exist on fxcg50. */ diff --git a/include/gint/gint.h b/include/gint/gint.h index a9906f3..14f11e9 100644 --- a/include/gint/gint.h +++ b/include/gint/gint.h @@ -92,23 +92,27 @@ int gint_intlevel(int intid, int level); When an interrupt request is accepted, the hardware jumps to a specific interrupt handler at an address that depends on the interrupt source. - Each interrupt handler should only refer to data within its own block - because the relative displacement between blocks is MPU-dependent. There are - a few exceptions to this, such as timer handlers, which are contiguous on - all currently-used platforms. Be careful. + For safety, interrupt handlers should avoir referring to data from other + blocks because the arrangement of blocks at runtime depends on event codes. + The assembler program will assume that consecutive blocks in the source code + will be consecutive in memory, which is not always true. Avoiding cross- + references is a practical rule to avoid problems. (gint breaks this rule + very often but does it carefully... I guess?) This function allows anyone to replace any interrupt handler so make sure you're not interfering with usual interrupt assignments. The first parameter 'event_code' represents the event_code associated with - the interrupt. These codes are normally platform-dependent, but gint always - uses the SH7305 codes: SH3 platforms have a translation table. See the + the interrupt. The codes are normally platform-dependent, but gint always + uses SH7305 codes: SH3 platforms have a translation table. See the documentation for a list of event codes and their associated interrupts. - The handler function must be an interrupt handler: it should not raise - exceptions, must end with 'rte', uses the kernel register bank... and it - must fit within 32 bytes. If it's not written in assembler, then you're - likely doing something wrong. + The handler function must be an interrupt handler: it must not raise + exceptions, must end with 'rte', and it will use the kernel register bank. + For convenience I allow any block size to be loaded as an interrupt handler, + but it should really be a multiple of 32 bytes and not override other + handlers. If it's not written in assembler, then you're likely doing + something wrong. It is common for interrupt handlers to have a few bytes of data, such as the address of a callback function. gint often stores this data in the last @@ -117,7 +121,8 @@ int gint_intlevel(int intid, int level); @event_code Identifier of the interrupt block @handler Address of handler function + @size How many bytes to copy Returns the VBR address where the handlers was installed. */ -void *gint_inthandler(int event_code, const void *handler); +void *gint_inthandler(int event_code, const void *handler, size_t size); #endif /* GINT_GINT */ diff --git a/include/gint/keyboard.h b/include/gint/keyboard.h new file mode 100644 index 0000000..12696dd --- /dev/null +++ b/include/gint/keyboard.h @@ -0,0 +1,67 @@ +//--- +// gint:keyboard - Keyboard input +//--- + +#ifndef GINT_KEYBOARD +#define GINT_KEYBOARD + +#include + +/* key_event_t - any keyboard event + This structure represents an event that occurs on the keyboard. This is a + low-level structure that is produced by the keyboard scanner. It reports key + presses, key releases, and key repetitions. + + These events are detected and reported each time the keyboard is scanned, + which is 16 Hz by default, so you'll get 16 repeat events by second if a key + is kept pressed. We can filter the events to emit one only every second, for + example, but it's difficult to do it for all keys at the same time. Thus the + control of repetition delays is restricted to getkey(). + + When mod = 1, shift and alpha indicate whether the key has been modified. + This is only possible for key press events returned by getkey(). Note that + you can't have key = shift and mod = 1 at the same time. + + The time attribute indicates when the event occurred. This value increases + at each keyboard scan and *it wraps around every 4 minutes* (at 16 Hz). + I expect this attribute to be useful to analyze combo sequences in games. + Make sure you are aware of the two nitpicks: + - Don't keep the time values for too long because the wrap-around effect. + - 0xfff is just before 0x000, not long after. */ +typedef struct +{ + uint time :12; /* Time of event, unique over short periods */ + + uint :7; /* Reserved for future use */ + + uint mod :1; /* Whether modifiers are used */ + uint shift :1; /* If mod=1, whether SHIFT was pressed */ + uint alpha :1; /* If mod=1, whether ALPHA was pressed */ + + uint type :2; /* Type of key event */ + uint key :8; /* Hit key */ + +} PACKED(4) key_event_t; + +/* Keyboard event types, as in the type field of key_event_t */ +enum +{ + KEYEV_NONE = 0, /* No event available (poll() only) */ + KEYEV_DOWN = 1, /* Key was pressed */ + KEYEV_UP = 2, /* Key was released */ + KEYEV_HOLD = 3, /* A key that was pressed has been held down */ +}; + +/* Size of the buffer event queue, can be customized using gint's configure + script before compiling the library. Better be a power of 2. */ +#ifndef KEYBOARD_QUEUE_SIZE +#define KEYBOARD_QUEUE_SIZE 64 +#endif + +//--- +// Keyboard functions +//--- + +key_event_t key_poll(void); + +#endif /* GINT_KEYBOARD */ diff --git a/include/gint/rtc.h b/include/gint/rtc.h new file mode 100644 index 0000000..fff6c5c --- /dev/null +++ b/include/gint/rtc.h @@ -0,0 +1,82 @@ +//--- +// gint:rtc - Real-Time Clock +//--- + +#ifndef GINT_RTC +#define GINT_RTC + +#include + +//--- +// Time management +//--- + +/* rtc_time_t - a point in time, representable in the RTC registers */ +typedef struct +{ + uint16_t year; /* Years (exact value, e.g. 2018) */ + uint8_t week_day; /* Day of week, (0=Sunday, 6=Saturday) */ + uint8_t month; /* Month (0..11) */ + uint8_t month_day; /* Day of month (1..31) */ + uint8_t hours; /* Hour (0..23) */ + uint8_t minutes; /* Minute (0..59) */ + uint8_t seconds; // Second (0..59) */ + +} rtc_time_t; + +/* rtc_get_time() - read the current time from the RTC + @time Pointer to rtc_time_t structure (needs not be initialized) */ +void rtc_get_time(rtc_time_t *time); + +/* rtc_set_time() - write a new current time to the RTC + If [time->week_day] is not in the valid range, it is set to 0. Other fields + are not checked. + @time Pointer to new time */ +void rtc_set_time(const rtc_time_t *time); + +//--- +// RTC timer +// The real-time clock produces a regular interrupt which may be used as a +// timer with a maximum frequency of 256 Hz. It is also useful to check +// that the clock settings (see ) are properly detected, by +// comparing the detected frequencies with the RTC. +//--- + +/* rtc_frequency_t - possible frequency settings for the RTC's interrupt */ +typedef enum +{ + rtc_500mHz = 7, + rtc_1Hz = 6, + rtc_2Hz = 5, + rtc_4Hz = 4, + rtc_16Hz = 3, + rtc_64Hz = 2, + rtc_256Hz = 1, + rtc_none = 0, + +} rtc_frequency_t; + +/* rtc_start_timer() - configure the RTC timer + + This function sets up the RTC timer to invoke the provided callback function + with its argument regularly. This works like normal timers (); + [callback] is passed [arg] as argument and the RTC timer is stopped if it + returns non-zero. + + This function will replace any existing callback! + + Note that, as opposed to timers, it is not possible to know how much time + will elapse before the callback will first be called. For instance, setting + up a 1 Hz-callback when the current time ends in 950 ms will result in the + callback being called after 50 ms, then every second. This is not a problem + for most uses. */ +void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void *arg), + void *arg); + +/* rtc_stop_timer() - stop the RTC timer + This function stops the RTC timer that was set up with rtc_start_timer(). If + the decision of stopping the timer comes from the callback, it is preferable + to return non-zero. */ +void rtc_stop_timer(void); + +#endif /* GINT_RTC */ diff --git a/include/gint/timer.h b/include/gint/timer.h index 82d9e47..7f325e7 100644 --- a/include/gint/timer.h +++ b/include/gint/timer.h @@ -29,13 +29,7 @@ what you intended. */ /* timer_count() - tells how many timers are available on the platform */ -HDRFUNC int timer_count(void) -{ - mpuSwitch( - return 4, /* SH3-based */ - return 9 /* SH4-based */ - ); -} +#define timer_count() (isSH3() ? 4 : 9) /* Clock input @@ -123,21 +117,15 @@ uint32_t timer_delay(int timer, int delay_us); @timer Timer id, as returned by timer_setup() */ void timer_start(int timer); -/* timer_reload() - change a timer's delay and source +/* timer_reload() - change a timer's delay constant for next interrupts - Changes the delay and source of the given timer. This function does not - start the timer if it stopped. The timer will start counting down from the - new delay when it is started (or immediately if it was running), no matter - how much time has elapsed since it last fired. Accumulated events are not - dropped. - - In cases where choosing a delay and input clock is difficult (such as when - using overclocking), use the timer_delay() function. + Changes the delay constant of the given timer. Nothing will happen until the + next callback; then the timer will update its delay to reflect the new + constant. The new delay can be calculated by the timer_delay() function. @timer Timer id, as returned by timer_setup() - @delay New delay (unit depends on the clock source) - @clock New clock source to be used by the timer */ -void timer_reload(int timer, uint32_t delay, timer_input_t clock); + @delay New delay (unit depends on the clock source) */ +void timer_reload(int timer, uint32_t delay); /* timer_pause() - stop a running timer The specified timer will be paused; its counter will not be reset. A stopped @@ -147,11 +135,11 @@ void timer_reload(int timer, uint32_t delay, timer_input_t clock); @timer Timer id, as returned by timer_setup() */ void timer_pause(int timer); -/* timer_free() - free a timer +/* timer_stop() - stop and free a timer Stops and destroys a timer, making its id free for re-use. The id must not be used anymore until it is returned by a further call to timer_setup(). @timer Timer id, as returned by timer_setup() */ -void timer_free(int timer); +void timer_stop(int timer); #endif /* GINT_TIMER */ diff --git a/include/mod/rtc.h b/include/mod/rtc.h new file mode 100644 index 0000000..b44215b --- /dev/null +++ b/include/mod/rtc.h @@ -0,0 +1,76 @@ +//--- +// gint:mod:rtc - Real-Time Clock +//--- + +#ifndef GINT_CORE_RTC +#define GINT_CORE_RTC + +//--- +// Hybrid SH7705-SH7305 Real-Time Clock. Refer to: +// "Renesas SH7705 Group Hardware Manual" +// Section 15: "Real-Time Clock (RTC)" +// "Renesas SH7724 User's Manual: Hardware" +// Section 28: "Real-Time Clock (RTC)" +//--- + +/* rtc_BCD2_t - a 2-digit BCD counter with a 1-byte gap */ +typedef struct +{ + byte_union(, + uint8_t TENS :4; + uint8_t ONES :4; + ); + pad(1); + +} PACKED(2) rtc_BCD2_t; + +/* sh7705_rtc_t, sh7305_rtc_t - Date and time access, RTC control */ +typedef volatile struct +{ + uint8_t const R64CNT; /* A 64-Hz counter */ + pad(1); + + rtc_BCD2_t RSECCNT; /* Second count */ + rtc_BCD2_t RMINCNT; /* Minute count */ + rtc_BCD2_t RHRCNT; /* Hour count */ + + uint8_t RWKCNT; /* Day of week, must be in [0..6] */ + pad(1); + + rtc_BCD2_t RDAYCNT; /* Day count */ + rtc_BCD2_t RMONCNT; /* Month count */ + + word_union(RYRCNT, /* Year count */ + uint THOUSANDS :4; + uint HUNDREDS :4; + uint TENS :4; + uint ONES :4; + ); + pad(12); /* Alarm registers... */ + + byte_union(RCR1, + uint8_t CF :1; /* Carry flag */ + uint8_t :2; + uint8_t CIE :1; /* Carry interrupt enable */ + uint8_t AIE :1; /* Alarm interrupt enable */ + uint8_t :2; + uint8_t AF :1; /* Alarm flag */ + ); + pad(1); + + byte_union(RCR2, + uint8_t PEF :1; /* Periodic interrupt flag */ + uint8_t PES :3; /* Periodic interrupt interval */ + uint8_t :1; + uint8_t ADJ :1; /* 30-second adjustment */ + uint8_t RESET :1; /* Reset trigger */ + uint8_t START :1; /* Start bit */ + ); + pad(1); + +} PACKED(4) rtc_t; + +#define SH7705_RTC (*((rtc_t *)0xfffffec0)) +#define SH7305_RTC (*((rtc_t *)0xa413fec0)) + +#endif /* GINT_CORE_RTC */ diff --git a/src/clock/freq.c b/src/clock/freq.c index e69de29..4ff270f 100644 --- a/src/clock/freq.c +++ b/src/clock/freq.c @@ -0,0 +1,136 @@ +//--- +// gint:core:freq - Clock frequency management +//--- + +#include +#include + +#include +#include +#include + +//--- +// Driver storage +//--- + +/* Local copy of the CPG settings */ +GBSS static clock_frequency_t freq; + +/* clock_freq() - get the frequency of the main clocks */ +const clock_frequency_t *clock_freq(void) +{ + return &freq; +} + +//--- +// SH7705 Clock signals +//--- + +#ifdef FX9860G +#define CPG SH7705_CPG + +static void sh7705_probe(void) +{ + /* According to Sentaro21 in the sources of Ftune 1.0.1, the clock mode + is thought to be 5, which means that: + - CPG input is XTAL (14.745'600 MHz) + - PLL2 is active and *2 (29.491'200 MHz) + - CKIO is output from PLL2 (29.491'200 MHz) */ + int xtal = 14745600; + int pll2 = 2; + int ckio = xtal * pll2; + + /* This signal is multiplied by the PLL1 circuit */ + int pll1 = CPG.FRQCR.STC + 1; + + /* Iphi and Pphi have dividers (Bphi is always equal to CKIO) */ + int idiv = CPG.FRQCR.IFC; + int pdiv = CPG.FRQCR.PFC; + + /* Fill in the setting structure */ + freq.PLL1 = pll1; + freq.PLL2 = pll2; + freq.Bphi_div = 1; + freq.Iphi_div = idiv + 1; + freq.Pphi_div = pdiv + 1; + + /* Deduce the frequency of the main clocks. The following piece of code + hardcodes ckio / 3 and avoids using the division operator */ + int ckio_3 = 9830400; + + /* Exchange the setting values 2 and 3 */ + idiv = idiv ^ (idiv >> 1); + pdiv = pdiv ^ (pdiv >> 1); + + freq.CKIO_f = ckio; + freq.Bphi_f = ckio; + freq.Iphi_f = (idiv == 3) ? ckio_3 : ckio >> idiv; + freq.Pphi_f = (pdiv == 3) ? ckio_3 : ckio >> pdiv; +} + +#undef CPG +#endif /* FX9860G */ + +//--- +// SH7305 clock signals +//--- + +#define CPG SH7305_CPG + +static void sh7305_probe(void) +{ + /* The meaning of the PLL setting on SH7305 differs from the + documentation of SH7224; the setting must not be doubled. */ + int pll = CPG.FRQCRA.STC + 1; + freq.PLL = pll; + + /* The FLL ratio is the value of the setting, possibly halved */ + int fll = CPG.FLLFRQ.FLF; + if(CPG.FLLFRQ.SELXM == 1) fll >>= 1; + freq.FLL = fll; + + /* On SH7724, the divider ratio is given by 1 / (setting + 1), but here + it's actually 1 / (2^setting + 1). */ + + int divb = CPG.FRQCRA.BFC; + int divi = CPG.FRQCRA.IFC; + int divp = CPG.FRQCRA.P1FC; + + freq.Bphi_div = 1 << (divb + 1); + freq.Iphi_div = 1 << (divi + 1); + freq.Pphi_div = 1 << (divp + 1); + + /* Deduce the input frequency of divider 1 */ + int base = 32768; + if(CPG.PLLCR.FLLE) base *= fll; + if(CPG.PLLCR.PLLE) base *= pll; + + /* And the frequency of all other input clocks */ + freq.RTCCLK_f = 32768; + freq.Bphi_f = base >> (divb + 1); + freq.Iphi_f = base >> (divi + 1); + freq.Pphi_f = base >> (divp + 1); +} + +#undef CPG + +//--- +// Other driver stuff +//--- + +static void init(void) +{ + isSH3() ? sh7705_probe() + : sh7305_probe(); +} + +gint_driver_t drv_cpg = { + .name = "Clock Pulse Generator", + .init = init, + .ctx_size = 0, + .sys_ctx = NULL, + .ctx_save = NULL, + .ctx_restore = NULL, +}; + +GINT_DECLARE_DRIVER(1, drv_cpg); diff --git a/src/clock/sleep.c b/src/clock/sleep.c new file mode 100644 index 0000000..e69de29 diff --git a/src/core/bootlog.c b/src/core/bootlog.c index 635bf56..d0877aa 100644 --- a/src/core/bootlog.c +++ b/src/core/bootlog.c @@ -84,8 +84,8 @@ void bootlog_kernel(void) { print(15, 1, " Kernel"); - mpuSwitch({ - /* SH3-based */ + if(isSH3()) + { print(1, 5, "ABCD"); print_hex( 6, 5, INTC3._.IPRA->word, 4); print_hex(10, 5, INTC3._.IPRB->word, 4); @@ -96,8 +96,9 @@ void bootlog_kernel(void) print_hex(10, 6, INTC3._.IPRF->word, 4); print_hex(14, 6, INTC3._.IPRG->word, 4); print_hex(18, 6, INTC3._.IPRH->word, 4); - },{ - /* SH4-based */ + } + else + { print(1, 5, "ACFG"); print_hex( 6, 5, INTC4._->IPRA.word, 4); print_hex(10, 5, INTC4._->IPRC.word, 4); @@ -108,7 +109,7 @@ void bootlog_kernel(void) print_hex(10, 6, INTC4._->IPRJ.word, 4); print_hex(14, 6, INTC4._->IPRK.word, 4); print_hex(18, 6, INTC4._->IPRL.word, 4); - }); + } Bdisp_PutDisp_DD(); } diff --git a/src/core/gint.c b/src/core/gint.c index 278557d..8be9115 100644 --- a/src/core/gint.c +++ b/src/core/gint.c @@ -36,10 +36,9 @@ int gint_intlevel(int intid, int level) volatile uint16_t *ipr; level &= 0xf; - mpuSwitch( - ipr = INTC3.IPRS[intid >> 2], /* SH3-based */ - ipr = &INTC4.IPRS[2 * (intid >> 2)] /* SH4-based */ - ); + ipr = isSH3() + ? INTC3.IPRS[intid >> 2] /* SH3-based */ + : &INTC4.IPRS[2 * (intid >> 2)]; /* SH4-based */ int oldlevel = (*ipr >> shift) & 0xf; *ipr = (*ipr & ~(0xf << shift)) | (level << shift); @@ -48,7 +47,7 @@ int gint_intlevel(int intid, int level) } /* gint_inthandler() - configure interrupt handlers */ -void *gint_inthandler(int event_code, const void *handler) +void *gint_inthandler(int event_code, const void *handler, size_t size) { extern char gint_vbr; @@ -60,5 +59,5 @@ void *gint_inthandler(int event_code, const void *handler) if(event_code < 0) return NULL; void *dest = (void *)&gint_vbr + event_code + 0x620; - return memcpy(dest, handler, 32); + return memcpy(dest, handler, size); } diff --git a/src/core/setup.c b/src/core/setup.c index c307ae3..b830239 100644 --- a/src/core/setup.c +++ b/src/core/setup.c @@ -30,24 +30,28 @@ typedef struct @arg ctx gint core context object */ static void gint_ctx_save(gint_core_ctx *ctx) { - mpuSwitch( - /* SH3-based */ - for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]), - /* SH4-based */ - for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i] - ); + if(isSH3()) + { + for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]); + } + else + { + for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i]; + } } /* gint_ctx_restore() - restore interrupt controller configuration @arg ctx gint core context object */ static void gint_ctx_restore(gint_core_ctx *ctx) { - mpuSwitch( - /* SH3-based */ - for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i], - /* SH4-based */ - for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i] - ); + if(isSH3()) + { + for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i]; + } + else + { + for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i]; + } } //--- @@ -61,12 +65,14 @@ GBSS static gint_core_ctx sys_ctx; static void lock(void) { /* Just disable everything, drivers will enable what they support */ - mpuSwitch( - /* SH3-based */ - for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000, - /* SH4-based */ - for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000 - ); + if(isSH3()) + { + for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000; + } + else + { + for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000; + } } /* gint_install() - install and start gint */ @@ -87,19 +93,20 @@ void gint_install(void) /* Load the event handler entry points into memory */ /* TODO: Load an exception handler and a TLB miss handler */ - mpuSwitch({ - /* SH3-based */ + if(isSH3()) + { extern void exch_entry_7705(void); extern void inth_entry_7705(void); exch_entry = exch_entry_7705; inth_entry = inth_entry_7705; - },{ - /* SH4-based */ + } + else + { extern void exch_entry_7305(void); extern void inth_entry_7305(void); exch_entry = exch_entry_7305; inth_entry = inth_entry_7305; - }); + } memcpy((void *)(vbr + 0x100), exch_entry, 32); memcpy((void *)(vbr + 0x600), inth_entry, 32); @@ -113,10 +120,12 @@ static void unlock(void) { gint_ctx_restore(&sys_ctx); - /* Restore all driver settings */ - for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++) + /* Restore all driver settings, but do it in reverse order of loading + to honor the dependency system */ + for(gint_driver_t *drv = &edrv; (--drv) >= &edrv;) { - drv->ctx_restore(drv->sys_ctx); + if(drv->unload) drv->unload(); + if(drv->ctx_restore) drv->ctx_restore(drv->sys_ctx); } } diff --git a/src/core/start.c b/src/core/start.c index 7b8bf3d..13925b5 100644 --- a/src/core/start.c +++ b/src/core/start.c @@ -133,10 +133,8 @@ int start(int isappli, int optnum) /* Count how much memory got mapped from this process */ uint32_t rom, ram; - mpuSwitch( - tlb_mapped_memory(&rom, &ram), /* SH3-based */ - utlb_mapped_memory(&rom, &ram) /* SH4-based */ - ); + isSH3() ? tlb_mapped_memory(&rom, &ram) + : utlb_mapped_memory(&rom, &ram); #ifdef GINT_BOOT_LOG bootlog_mapped(rom, ram); @@ -155,7 +153,8 @@ int start(int isappli, int optnum) print(1, 3, "<"); print_hex(1, 4, (uint32_t)&srom, 8); Bdisp_PutDisp_DD(); - delay(20); + while(1); +// delay(20); return 1; } @@ -178,7 +177,7 @@ int start(int isappli, int optnum) for(drv = &bdrv; drv < &edrv; drv++) { if(isSH3() && drv->driver_sh3) drv->driver_sh3(); - drv->ctx_save(drv->sys_ctx); + if(drv->ctx_save) drv->ctx_save(drv->sys_ctx); if(drv->init) drv->init(); } @@ -197,8 +196,8 @@ int start(int isappli, int optnum) /* Before leaving the application, we need to clean up our mess. We have changed many OS settings while accessing the peripheral - modules. The OS is bound to be confused (and crash) if we don't - restore them */ + modules. The OS is bound to be confused (and hang, or crash, or any + other kind of giving up) if we don't restore them */ /* Unload gint and give back control to the system. Driver settings will be restored while interrupts are disabled */ diff --git a/src/keysc/iokbd.c b/src/keysc/iokbd.c new file mode 100644 index 0000000..dd0f8fb --- /dev/null +++ b/src/keysc/iokbd.c @@ -0,0 +1,114 @@ +//--- +// gint:keysc:iokbd - I/O-based keyboard input +//--- + +#include +#include + +/* This file is SH7705-only. */ +#ifdef FX9860G +#define PFC SH7705_PFC + +/* iokbd_delay() - wait a bit so that I/O can keep up + May use the watchdog timer, but the keyboard driver will need to save it. */ +static void iokbd_delay(void) +{ + __asm__( + "nop\n\t" + "nop\n\t" + "nop\n\t" + "nop\n\t" + ); + +#if 0 + /* Watchdog delay version */ + const int delay = 0xf4; + + /* Disable the watchdog timer interrupt and reset configuration */ + INTC.IPRB.BIT._WDT = 0; + WDT.WTCSR.WRITE = 0xa500; + + /* Set the delay, input on Pphi / 256 and start counting */ + WDT.WTCNT.WRITE = 0x5a00 | (delay & 0xff); + WDT.WTCSR.WRITE = 0xa505; + WDT.WTCSR.WRITE = 0xa585; + + /* Actively wait for overflow, then clear the interrupt flag */ + while((WDT.WTCSR.READ.BYTE & 0x08) == 0); + WDT.WTCSR.WRITE = 0xa500 | (WDT.WTCSR.READ.BYTE & 0xf7); + + /* Reset configuration, counter, and re-enabled interrupt */ + WDT.WTCSR.WRITE = 0xa500; + WDT.WTCSR.WRITE = 0x5a00; + INTC.IPRB.BIT._WDT = GINT_INTP_WDT; +#endif +} + +/* iokbd_row() - acquire hit status of a single row from the I/O ports + @row Requested row number, 0..9 + Returns 8 bits of key state. */ +uint8_t iokbd_row(int row) +{ + if((unsigned)row > 9) return 0x00; + row ^= 1; + + /* This will enable output (01) on @row, input (10) everywhere else */ + uint16_t ctrl_mask = 0x0003 << ((row & 7) * 2); + /* Enable output (0) on @row, input (1) everywhere else */ + uint8_t data_mask = ~(1 << (row & 7)); + + /* When row < 8, the associated bits are in port B */ + if(row < 8) + { + /* Set @row as output in port B; port M is unused */ + PFC.PBCR = 0xaaaa ^ ctrl_mask; + PFC.PMCR = (PFC.PMCR & 0xff00) | 0x00aa; + iokbd_delay(); + + /* Set @row to 0, everything else to 1 */ + PFC.PBDR = data_mask; + PFC.PMDR = (PFC.PMDR & 0xf0) | 0x0f; + iokbd_delay(); + } + /* When row >= 8, the associated bits are in port M */ + else + { + /* Set @row as output in port M; port B is unused */ + PFC.PBCR = 0xaaaa; + PFC.PMCR = (PFC.PMCR & 0xff00) | (0x00aa ^ ctrl_mask); + iokbd_delay(); + + /* Set @row to 0, everything else to 1 */ + PFC.PBDR = 0xff; + PFC.PMDR = PFC.PMDR & data_mask; + } + + /* Now read the input data from the keyboard! */ + uint8_t input = ~PFC.PADR; + iokbd_delay(); + + /* Reset the port configuration. I don't know why the intermediate step + is necessary; refer to SimLo's documentation. */ + PFC.PBCR = 0xaaaa; + PFC.PMCR = (PFC.PMCR & 0xff00) | 0x00aa; + iokbd_delay(); + PFC.PBCR = 0x5555; + PFC.PMCR = (PFC.PMCR & 0xff00) | 0x0055; + iokbd_delay(); + + /* Now also reset the data registers. This was forgotten from SimLo's + CheckKeyRow() and blows up everything. */ + PFC.PBDR = 0x00; + PFC.PMDR = PFC.PMDR & 0xf0; + + return input; +} + +void iokbd_scan(uint8_t *scan) +{ + /* Scan each row independently; the gain from scanning them altogether + is probably not worth it */ + for(int i = 0; i < 12; i++) scan[i] = iokbd_row(i); +} + +#endif /* FX9860G */ diff --git a/src/keysc/keysc.c b/src/keysc/keysc.c new file mode 100644 index 0000000..f4e8d76 --- /dev/null +++ b/src/keysc/keysc.c @@ -0,0 +1,188 @@ +//--- +// gint:keysc - The SH7305 and I/O Key Scan Interfaces +//--- + +#include +#include +#include +#include + +#include +#include + +//--- +// Keyboard buffer +//--- + +/* The driver's internal state. At each step of time, this file compares the + internal state with the hardware state and generates events accordingly. + Events can be seen as a delta-encoding of the state of the keyboard over + time, and this buffer must be the sum of all events. This means that if an + event cannot be generated, f.i. because the buffer is full, this state must + *not* be updated. */ +GDATA volatile uint8_t state[12] = { 0 }; + +/* A driver event, which is a change in a full row, not a key. */ +typedef struct +{ + uint time :12; /* Locally unique time identifier */ + uint row :4; /* Row number */ + uint old :8; /* Key status for the old row */ + uint new :8; /* Key status for the new row */ + +} driver_event_t; + +/* The keyboard event buffer. This is a circular list defined by [buffer_start] + and [buffer_end]. To avoid an ambiguity when start = end, the buffer is not + allowed to be full (at least one free cell must be remaining). */ +GBSS static driver_event_t buffer[KEYBOARD_QUEUE_SIZE]; +/* Buffer bounds */ +GDATA static int buffer_start = 0; +GDATA static int buffer_end = 0; + +/* Current time, in keyboard-scanning ticks */ +GDATA int time = 0; +GDATA int full_release = 2; + +/* buffer_push() - add an event in the keyboard buffer + Returns non-zero if the event cannot be pushed. */ +static int buffer_push(driver_event_t ev) +{ + int next = (buffer_end + 1) % KEYBOARD_QUEUE_SIZE; + if(next == buffer_start) return 1; + + buffer[buffer_end] = ev; + buffer_end = next; + return 0; +} + +/* buffer_poll() - generate key events from the buffer + Sets [ev] and returns zero on success, otherwise non-zero. */ +static int buffer_poll(driver_event_t *ev) +{ + if(buffer_start == buffer_end) return 1; + + *ev = buffer[buffer_start]; + buffer_start = (buffer_start + 1) % KEYBOARD_QUEUE_SIZE; + return 0; +} + + +/* keysc_frame() - generate a round of interrupts for the current frame */ +void keysc_frame(void) +{ + ALIGNED(2) uint8_t scan[12] = { 0 }; + time++; + + /* First scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */ + if(isSH3()) iokbd_scan(&scan); + else + { + volatile uint16_t *KEYSC = (void *)0xa44b0000; + uint16_t *array = (void *)&scan; + + for(int i = 0; i < 6; i++) array[i] = KEYSC[i]; + } + + for(int row = 0; row < 12; row++) + { + /* Compare new data with the internal state. */ + int old = state[row]; + int new = scan[row]; + if(old == new && !new) continue; + + driver_event_t ev = { + .time = time, + .row = row, + .old = old, + .new = new, + }; + + /* Update internal status if the event could be pushed */ + if(!buffer_push(ev)) state[row] = new; + } +} + +/* key_poll() - poll the next keyboard event from the buffer */ +key_event_t key_poll(void) +{ + /* Every time a driver event is unqueued, its key events are generated + and put in this small buffer */ + static key_event_t events[8]; + /* Number of pending events in the previous buffer */ + static int events_pending = 0; + + /* If there are pending events, use them */ + if(events_pending > 0) return events[--events_pending]; + + /* Unqueue something from the driver buffer, if possible */ + driver_event_t ev; + if(buffer_poll(&ev)) return (key_event_t){ .type = KEYEV_NONE }; + + /* Generate new key events */ + int old = ev.old << 1; + int new = ev.new; + + for(int code = ((ev.row ^ 1) << 4) | 0x7; code & 0x7; code--) + { + int kind = (old & 2) | (new & 1); + old >>= 1; + new >>= 1; + + if(!kind) continue; + + key_event_t keyev = { + .time = ev.time, + .type = kind, + .key = code, + }; + events[events_pending++] = keyev; + } + + /* Return the first of these generated events */ + return events[--events_pending]; +} + +//--- +// Driver initialization +//--- + +static int callback(UNUSED void *arg) +{ + keysc_frame(); + return 0; +} + +/* init() - setup the support timer */ +static void init(void) +{ + int tid = isSH3() ? 4 : 7; + + /* 32768 Hz divided by 16 is 2048 */ + /* TODO: Use I/O port scanning on SH3 */ + timer_setup(tid, 2048, 0, callback, NULL); + timer_start(tid); +} + +/* unload() - stop the support timer */ +static void unload(void) +{ + int tid = isSH3() ? 4 : 7; + timer_stop(tid); +} + +//--- +// Driver structure definition +//--- + +gint_driver_t drv_keysc = { + .name = "KEYSC", + .init = init, + .unload = unload, + .ctx_size = 0, + .sys_ctx = NULL, + .ctx_save = NULL, + .ctx_restore = NULL, +}; + +GINT_DECLARE_DRIVER(4, drv_keysc); diff --git a/src/r61524/r61524.c b/src/r61524/r61524.c index eb2c6d5..cf5960d 100644 --- a/src/r61524/r61524.c +++ b/src/r61524/r61524.c @@ -309,6 +309,6 @@ gint_driver_t drv_r61524 = { .ctx_restore = ctx_restore, }; -GINT_DECLARE_DRIVER(drv_r61524); +GINT_DECLARE_DRIVER(5, drv_r61524); #endif /* FXCG50 */ diff --git a/src/rtc/inth.s b/src/rtc/inth.s new file mode 100644 index 0000000..1cbc37a --- /dev/null +++ b/src/rtc/inth.s @@ -0,0 +1,60 @@ +/* +** gint:rtc:inth - Interrupt handler for the Real-Time Clock +** This one is fairly simple, just a flag to clear and potentially a timer to +** stop if the callback returns non-zero. +*/ + + .global _inth_rtc_pri + .global _inth_rtc_pri_helper + .section .gint.blocks, "ax" + .align 4 + +/* RTC PERIODIC INTERRUPT HANDLER - 56 BYTES */ + +_inth_rtc_pri: + /* Invoke the callback function with its argument */ + sts.l pr, @-r15 + mov.l 1f, r0 + mov.l 2f, r4 + jsr @r0 + nop + + /* Save the return value */ + mov r0, r3 + + /* Prepare to clear the interrupt flag */ + mov.l 3f, r1 + + /* Jump to another gate to finish the work: + - 0xc is the size of storage below + - 0x20 is the size of the next gate (alarm interrupt) */ + mov #0x2c, r2 + braf r2 + nop + +1: .long 0 /* Callback function: edited dynamically */ +2: .long 0 /* Argument to callback function */ +3: .long 0xa413fede /* RCR2 address, edited at startup on SH3 */ + +_inth_rtc_pri_helper: + +.clear: + /* Clear the interrupt flag */ + mov.b @r1, r0 + tst #0x80, r0 + and #0x7f, r0 + bf.s .clear + mov.b r0, @r1 + + /* Stop the timer if the return value of the callback was non-zero */ + tst r3, r3 + bt .end + and #0x8f, r0 + mov.b r0, @r1 + +.end: + lds.l @r15+, pr + rte + nop + + .zero 8 diff --git a/src/rtc/rtc.c b/src/rtc/rtc.c new file mode 100644 index 0000000..7b08671 --- /dev/null +++ b/src/rtc/rtc.c @@ -0,0 +1,191 @@ +//--- +// gint:rtc - Real-Time Clock +//--- + +#include +#include +#include +#include + +#include +#include + +//--- +// Real-Time Clock peripheral registers +//--- + +/* RTC address on SH7305, adjusted at startup on SH7337 and SH7355 */ +GDATA static rtc_t *RTC = &SH7305_RTC; +/* Address of interrupt handler parameters */ +GBSS struct { + int (*callback)(void *arg); + void *arg; + volatile uint8_t *RCR2; +} PACKED(4) *params; + +//--- +// Time management +//--- + +/* int8(), int16() + Converts BCD values to integers */ +static int int8(uint8_t bcd) +{ + return (bcd & 0x0f) + 10 * (bcd >> 4); +} +static int int16(uint16_t bcd) +{ + return (bcd & 0xf) + 10 * ((bcd >> 4) & 0xf) + 100 * ((bcd >> 8) & 0xf) + + 1000 * (bcd >> 12); +} + +/* bcd8(), bcd16() + Converts integer values to BCD + TODO: Use some kind of qdiv() for bcd8() and bcd16()? */ +static uint8_t bcd8(int integer) +{ + integer %= 100; + return ((integer / 10) << 4) | (integer % 10); +} +static uint16_t bcd16(int integer) +{ + integer %= 10000; + return (bcd8(integer / 100) << 8) | bcd8(integer % 100); +} + +/* rtc_get_time() - read the current time from the RTC */ +void rtc_get_time(rtc_time_t *time) +{ + if(!time) return; + + do { + RTC->RCR1.CF = 0; + + time->seconds = int8(RTC->RSECCNT.byte); + time->minutes = int8(RTC->RMINCNT.byte); + time->hours = int8(RTC->RHRCNT.byte); + time->month_day = int8(RTC->RDAYCNT.byte); + time->month = int8(RTC->RMONCNT.byte); + time->year = int16(RTC->RYRCNT.word); + time->week_day = RTC->RWKCNT; + + } while(RTC->RCR1.CF != 0); +} + +/* rtc_set_time() - write a new current time to the RTC */ +void rtc_set_time(const rtc_time_t *time) +{ + if(!time) return; + int wday = (time->week_day < 7) ? (time->week_day) : (0); + + do { + RTC->RCR1.CF = 0; + + RTC->RSECCNT.byte = bcd8(time->seconds); + RTC->RMINCNT.byte = bcd8(time->minutes); + RTC->RHRCNT.byte = bcd8(time->hours); + RTC->RDAYCNT.byte = bcd8(time->month_day); + RTC->RMONCNT.byte = bcd8(time->month); + RTC->RYRCNT.word = bcd16(time->year); + RTC->RWKCNT = wday; + + } while(RTC->RCR1.CF != 0); +} + +//--- +// RTC timer +//--- + +/* rtc_start_timer() - configure the RTC timer */ +void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void*arg), void*arg) +{ + /* Set up the callback */ + params->callback = callback; + params->arg = arg; + + /* Clear the interrupt flag */ + do RTC->RCR2.PEF = 0; + while(RTC->RCR2.PEF); + + /* Enable the interrupt */ + RTC->RCR2.PES = freq; +} + +/* rtc_stop_timer() - stop the RTC timer */ +void rtc_stop_timer(void) +{ + RTC->RCR2.PES = rtc_none; +} + +//--- +// Driver initialization +//--- + +#ifdef FX9860G +static void driver_sh3(void) +{ + /* Adjust the address of the RTC */ + RTC = &SH7705_RTC; +} +#endif + +static void init(void) +{ + /* Interrupt handlers provided by rtc/inth.s */ + extern void inth_rtc_pri(void); + extern void inth_rtc_pri_helper(void); + + /* Install the RTC interrupt handler */ + UNUSED void *h0, *h1; + h0 = gint_inthandler(0xaa0, inth_rtc_pri, 32); + h1 = gint_inthandler(0xae0, inth_rtc_pri_helper, 32); + + params = h0 + 20; + params->RCR2 = &RTC->RCR2.byte; + + /* Disable the periodic interrupt for now, but give it priority 5 */ + RTC->RCR2.PES = rtc_none; + gint_intlevel(isSH3() ? 3 : 40, 5); +} + +//--- +// Context system for this driver +//--- + +typedef struct +{ + uint8_t RCR1; + uint8_t RCR2; +} ctx_t; + +GBSS static ctx_t sys_ctx; + +static void ctx_save(void *buf) +{ + ctx_t *ctx = buf; + ctx->RCR1 = RTC->RCR1.byte; + ctx->RCR2 = RTC->RCR2.byte; +} + +static void ctx_restore(void *buf) +{ + ctx_t *ctx = buf; + ctx->RCR1 = RTC->RCR1.byte; + ctx->RCR2 = RTC->RCR2.byte; +} + +//--- +// Driver structure definition +//--- + +gint_driver_t drv_rtc = { + .name = "Real-Time Clock", + .driver_sh3 = GINT_DRIVER_SH3(driver_sh3), + .init = init, + .ctx_size = sizeof(ctx_t), + .sys_ctx = &sys_ctx, + .ctx_save = ctx_save, + .ctx_restore = ctx_restore, +}; + +GINT_DECLARE_DRIVER(2, drv_rtc); diff --git a/src/t6k11/t6k11.c b/src/t6k11/t6k11.c index e58a026..f5d0eca 100644 --- a/src/t6k11/t6k11.c +++ b/src/t6k11/t6k11.c @@ -140,15 +140,16 @@ void t6k11_backlight(int setting) /* This setting is mapped to an I/O port: - On SH3, bit 7 of port G - On SH4, bit 4 of port N */ - mpuSwitch({ - /* SH3-based */ + if(isSH3()) + { port = (void *)0xa400012c; mask = 0x80; - },{ - /* SH4-based */ + } + else + { port = (void *)0xa4050138; mask = 0x10; - }); + } if(!setting) *port &= ~mask; if(setting > 0) *port |= mask; @@ -204,6 +205,6 @@ gint_driver_t drv_t6k11 = { .ctx_restore = ctx_restore, }; -GINT_DECLARE_DRIVER(drv_t6k11); +GINT_DECLARE_DRIVER(5, drv_t6k11); #endif /* FX9860G */ diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c index dd7a95d..faa8d4d 100644 --- a/src/tmu/tmu.c +++ b/src/tmu/tmu.c @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -66,7 +67,6 @@ typedef struct uint16_t event; /* Interrupt event code */ } timer_t; - //--- // Driver storage //--- @@ -133,9 +133,12 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock, t->TCR.UNIE = 1; } - /* Register the callback and its argument */ - timers[tid].data->callback = callback; - timers[tid].data->arg = arg; + /* Register the callback and its argument (TMU-owned timers only) */ + if(timers[tid].data) + { + timers[tid].data->callback = callback; + timers[tid].data->arg = arg; + } /* Return the timer id, since configuration was successful */ return tid; @@ -145,11 +148,14 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock, uint32_t timer_delay(int tid, int delay_us) { /* TODO: Proper timer_delay() */ -#ifdef FX9860G - uint64_t freq = 14750000 >> 2; /* 14.75 MHz / 4 */ -#else - uint64_t freq = 29020000 >> 2; /* 29.49 MHz / 4 */ -#endif + const clock_frequency_t *clock = clock_freq(); + uint64_t freq; + + freq = isSH3() ? 14750000 >> 2 : clock->Pphi_f >> 2; + + /* fxcg50: Calculated = 29491200 but it's too low */ + // uint64_t freq = 29020000 >> 2; + if(tid >= 3) freq = 32768; /* 32768 Hz */ uint64_t product = freq * (uint64_t)delay_us; @@ -180,8 +186,8 @@ void timer_start(int tid) timer_control(tid, 0); } -/* timer_reload() - change a timer's constant register for next interrupts */ -void timer_reload(int tid, uint32_t delay, timer_input_t clock) +/* timer_reload() - change a timer's delay constant for next interrupts */ +void timer_reload(int tid, uint32_t delay) { if(tid < 3) ((tmu_t *)timers[tid].tmu)->TCOR = delay; else ((tmu_extra_t *)timers[tid].tmu)->TCOR = delay; @@ -193,9 +199,11 @@ void timer_pause(int tid) timer_control(tid, 1); } -/* timer_free() - free a timer */ -void timer_free(int tid) +/* timer_stp() - stop and free a timer */ +void timer_stop(int tid) { + return; + /* Stop the timer and disable UNIE to indicate that it's free */ timer_pause(tid); @@ -234,15 +242,29 @@ extern void inth_tmu_extra2(void); extern void inth_tmu_extra_help(void); extern void inth_tmu_extra_others(void); +#ifdef FX9860G +static void driver_sh3(void) +{ + timers[0].tmu = (void *)0xfffffe94; + timers[1].tmu = (void *)0xfffffea0; + timers[2].tmu = (void *)0xfffffeac; + /* We don't need to change the event code of ETMU0 since it's + translated to the SH4 code by the interrupt handler */ + timers[3].tmu = (void *)0xa44c0030; + + TSTR = (void *)0xfffffe92; +} +#endif + static void init(void) { /* Install the standard's TMU interrupt handlers. By chance TMU gates use the same event codes on SH7705 and SH7305 */ UNUSED void *h0, *h1, *h2, *hs; - h0 = gint_inthandler(0x400, inth_tmu_0); - h1 = gint_inthandler(0x420, inth_tmu_1); - h2 = gint_inthandler(0x440, inth_tmu_2); - hs = gint_inthandler(0x460, inth_tmu_storage); + h0 = gint_inthandler(0x400, inth_tmu_0, 32); + h1 = gint_inthandler(0x420, inth_tmu_1, 32); + h2 = gint_inthandler(0x440, inth_tmu_2, 32); + hs = gint_inthandler(0x460, inth_tmu_storage, 32); /* User information in interrupt handlers */ timers[0].data = h2 + 20; @@ -296,14 +318,14 @@ static void init(void) void *handler = (i == 5) ? inth_tmu_extra2 : inth_tmu_extra_others; - void *h = gint_inthandler(timers[i].event, handler); + void *h = gint_inthandler(timers[i].event, handler, 32); timers[i].data = h + 20; timers[i].data->structure = timers[i].tmu; } /* Also install the helper handler */ - gint_inthandler(0xc60, inth_tmu_extra_help); + gint_inthandler(0xc60, inth_tmu_extra_help, 32); /* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */ gint_intlevel(0, 13); @@ -311,11 +333,12 @@ static void init(void) gint_intlevel(2, 9); /* Enable the extra TMUs at level 7 */ - mpuSwitch({ - /* SH3-based */ + if(isSH3()) + { gint_intlevel(23, 7); - },{ - /* SH4-based */ + } + else + { gint_intlevel(36, 7); gint_intlevel(25, 7); gint_intlevel(26, 7); @@ -323,12 +346,12 @@ static void init(void) gint_intlevel(32, 7); gint_intlevel(44, 7); - /* Unmask the interrupts */ + /* Unmask the extra timers' interrupts */ INTC4.MSKCLR->IMR2 = 0x01; INTC4.MSKCLR->IMR5 = 0x06; INTC4.MSKCLR->IMR6 = 0x18; INTC4.MSKCLR->IMR8 = 0x02; - }); + } } //--- @@ -346,20 +369,6 @@ typedef struct /* Allocate a system buffer in gint's BSS area */ GBSS static ctx_t sys_ctx; -#ifdef FX9860G -static void driver_sh3(void) -{ - timers[0].tmu = (void *)0xfffffe94; - timers[1].tmu = (void *)0xfffffea0; - timers[2].tmu = (void *)0xfffffeac; - /* We don't need to change the event code of ETMU0 since it's - translated to the SH4 code by the interrupt handler */ - timers[3].tmu = (void *)0xa44c0030; - - TSTR = (void *)0xfffffe92; -} -#endif - static void ctx_save(void *buf) { ctx_t *ctx = buf; @@ -401,10 +410,12 @@ static void ctx_restore(void *buf) for(int i = 0; i < timer_count() - 3; i++) { tmu_extra_t *t = timers[i + 3].tmu; - /* Avoid some interrupts that occur when TCNT = 0 */ - t->TSTR = 0; - t->TCNT = 0xffffffff; + /* This thing is being unloaded while interrupts are disabled, + so we don't have to heed for ctx->extra[i].TCNT = 0, which + can generate interrupts. I tried to do t->TCNT = 0xffffffff + then restore ctx->extra[i], but it causes hangs on SH3 when + the overclock level is too high. */ t->TCNT = ctx->extra[i].TCNT; t->TCOR = ctx->extra[i].TCOR; t->TCR.byte = ctx->extra[i].TCR.byte; @@ -418,12 +429,12 @@ static void ctx_restore(void *buf) gint_driver_t drv_tmu = { .name = "Timer Unit", + .driver_sh3 = GINT_DRIVER_SH3(driver_sh3), .init = init, .ctx_size = sizeof(ctx_t), .sys_ctx = &sys_ctx, - .driver_sh3 = GINT_DRIVER_SH3(driver_sh3), .ctx_save = ctx_save, .ctx_restore = ctx_restore, }; -GINT_DECLARE_DRIVER(drv_tmu); +GINT_DECLARE_DRIVER(2, drv_tmu);

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 s0r9OE?c!NrzMIs{8f91wQ* +#include + +void vsprintf_int(char **buffer_ptr, int n) +{ + char *buffer = *buffer_ptr; + if(!n) + { + *buffer++ = '0'; + *buffer_ptr = buffer; + return; + } + if(n < 0) + { + *buffer++ = '-'; + n = -n; + } + + int digits = 0, x = n, copy; + while(x) digits++, x /= 10; + copy = digits; + + while(digits) + { + buffer[--digits] = n % 10 + '0'; + n /= 10; + } + + *buffer_ptr = buffer + copy; +} + +void vsprintf_hexa(char **buffer_ptr, uint32_t val, int digits, int zero) +{ + char *buffer = *buffer_ptr; + if(!val) + { + while(digits-- > 1) *buffer++ = (zero) ? '0' : ' '; + *buffer++ = '0'; + *buffer_ptr = buffer; + return; + } + + if(digits <= 0) + { + uint32_t x = val; + while(x) digits++, x >>= 4; + } + int copy = digits; + + while(val && digits) + { + buffer[--digits] = (val & 15) + '0' + 39 * ((val & 15) > 9); + val >>= 4; + } + while(digits) + { + buffer[--digits] = (zero) ? '0': ' '; + } + + *buffer_ptr = buffer + copy; +} + +void vsprintf_ptr(char **buffer_ptr, void *ptr) +{ + vsprintf_hexa(buffer_ptr, (uint32_t)ptr, 8, 1); +} + +void vsprintf_char(char **buffer_ptr, int c) +{ + char *buffer = *buffer_ptr; + *buffer++ = c; + *buffer_ptr = buffer; +} + +void vsprintf_str(char **buffer_ptr, const char *str) +{ + char *buffer = *buffer_ptr; + while(*str) *buffer++ = *str++; + *buffer_ptr = buffer; +} + +int vsprintf(char *buffer, const char *format, va_list args) +{ + char *save = buffer; + int zero, count; + + while(*format) + { + if(*format != '%') + { + *buffer++ = *format++; + continue; + } + if(!*++format) break; + + zero = 0; + count = 0; + + if(*format == '0') zero = 1, format++; + while(*format >= '0' && *format <= '9') + { + count *= 10; + count += (*format++ - '0'); + } + if(!*format) break; + + switch(*format) + { + case 'd': + vsprintf_int(&buffer, va_arg(args, int)); + break; + case 'x': + vsprintf_hexa(&buffer, va_arg(args, uint32_t), count, + zero); + break; + case 'p': + vsprintf_ptr(&buffer, va_arg(args, void *)); + break; + case 'c': + vsprintf_char(&buffer, va_arg(args, int)); + break; + case 's': + vsprintf_str(&buffer, va_arg(args, const char *)); + break; + default: + *buffer++ = *format; + break; + } + + format++; + } + + *buffer = 0; + return buffer - save; +} + +int sprintf(char *buffer, const char *format, ...) +{ + va_list args; + va_start(args, format); + int x = vsprintf(buffer, format, args); + va_end(args); + + return x; +} diff --git a/demo/gintdemo.c b/demo/gintdemo.c index de90854..b9b92f6 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -341,7 +341,6 @@ void main_menu(int *category, int *app) //--- dclear(); - dupdate(); switch(tab) { @@ -390,6 +389,7 @@ void main_menu(int *category, int *app) dreverse_area(0, 8 * (index - scroll) + 8, 127, 8 * (index - scroll) + 15); } + dupdate(); //--- @@ -402,6 +402,9 @@ void main_menu(int *category, int *app) switch(getkey()) { +// case KEY_7: +// crt0_system_menu(); +// break; case KEY_F1: if(!tab) break; tab = 0; @@ -499,37 +502,30 @@ int main(void) switch((category << 8) | app) { - case 0x0101: - test_keyboard(); - break; - case 0x0102: - test_gray(); - break; - case 0x0103: - test_bopti(); - break; - case 0x0104: - test_tales(); - break; - case 0x0105: - test_rtc(); - break; - case 0x0106: - test_timer(); - break; + case 0x0101: test_keyboard(); break; + case 0x0102: test_gray(); break; + case 0x0103: test_bopti(); break; + case 0x0104: test_tales(); break; + case 0x0105: test_rtc(); break; + case 0x0106: test_timer(); break; - case 0x0201: -// perf_bopti(); - break; - case 0x0202: -// perf_tales(); - break; + case 0x0201: /* perf_bopti(); */ break; + case 0x0202: /* perf_tales(); */ break; - case 0x0301: -// if(isSH3()) debug_tlb(); - break; + case 0x0301: /* if(isSH3()) debug_tlb(); */ break; } } return 0; } + +/* +void crash(void) +{ + __asm__( + "mov #0, r0 \n\t" + "ldc r0, vbr \n\t" + "trapa #1 " + ); +} +*/ diff --git a/demo/gintdemo.ld b/demo/gintdemo.ld index 1947163..9111fcc 100644 --- a/demo/gintdemo.ld +++ b/demo/gintdemo.ld @@ -26,6 +26,8 @@ SECTIONS */ .text : { + _btext = . ; + /* Initialization code. */ *(.pretext.entry) *(.pretext) @@ -39,6 +41,8 @@ SECTIONS *(.text) *(.text.*) + + _etext = . ; } > rom .rodata : { @@ -77,6 +81,7 @@ SECTIONS *(.eh_frame) *(.jcr) + . = ALIGN(4); _gint_data = _romdata + SIZEOF(.data) + SIZEOF(.cc) ; } > ram @@ -94,17 +99,14 @@ SECTIONS /* Exception handler. */ . = _gint_vbr + 0x100 ; - *(.gint.exc.entry) *(.gint.exc) /* TLB miss handler. */ . = _gint_vbr + 0x400 ; - *(.gint.tlb.entry) *(.gint.tlb) /* Interrupt handler. */ . = _gint_vbr + 0x600 ; - *(.gint.int.entry) *(.gint.int) _egint = . ; diff --git a/demo/test_bopti.c b/demo/test_bopti.c index f769365..818bdb6 100644 --- a/demo/test_bopti.c +++ b/demo/test_bopti.c @@ -95,10 +95,14 @@ static Image *select(Image *current) 8 * i, 7, 7); if(i == row) { + print(2, 2 + i + 1, "%08x", (unsigned int) + images[i].img); +/* int width, height; getwh(images[i].img, &width, &height); print(2, 2 + i + 1, "%d\x04%d", width, height); locate(10, 2 + i + 1, images[i].info); +*/ } } diff --git a/demo/test_tales.c b/demo/test_tales.c index 1a64352..644fc23 100644 --- a/demo/test_tales.c +++ b/demo/test_tales.c @@ -113,7 +113,7 @@ void test_tales(void) for(int j = 0; j < 16; j++) str[j] = 32 + (i << 4) + j; str[16] = 0; - gtext(-10, 2 + i * height, str); + gtext(2, 2 + i * height, str); } gimage(0, 56, &res_opt_tales); diff --git a/demo/test_timer.c b/demo/test_timer.c index 2f634d3..d8158b5 100644 --- a/demo/test_timer.c +++ b/demo/test_timer.c @@ -12,7 +12,7 @@ #include static void draw(int new_tab); -static struct ClockConfig conf; +static clock_config_t conf; //--- // Timer-RTC comparison. diff --git a/include/clock.h b/include/clock.h index 58e5c30..65ed96b 100644 --- a/include/clock.h +++ b/include/clock.h @@ -11,17 +11,29 @@ #define _CLOCK_H //--- -// Some type declarations. +// Sleep functions. //--- -enum Clock -{ - Clock_CKIO = 0, // SH7705 - Clock_RTCCLK = 1, // SH7305 - Clock_Bphi = 2, - Clock_Iphi = 3, - Clock_Pphi = 4, -}; +/* + sleep() + Puts the processor to sleep until an interrupt request is issued. +*/ +void sleep(void); + +/* + sleep_ms(), sleep_us() + Sleeps for the given number of milliseconds / microseconds using the + TIMER_USER timer. The actual sleep time will always be slightly less + than requested. +*/ +void sleep_ms(int ms_delay); +void sleep_us(int us_delay); + + + +//--- +// Clock management. +//--- enum ClockUnit { @@ -34,7 +46,7 @@ enum ClockUnit Clock_MHz = 12, }; -struct ClockConfig +typedef struct { union { @@ -60,69 +72,23 @@ struct ClockConfig int Bphi_f; int Iphi_f; int Pphi_f; -}; -//--- -// Public API. -//--- - -/* - clock_frequency() - Returns the approximate frequency, in Hz, of the given clock. The - measurements need to have been done. Returns a negative number on - error. -*/ -int clock_frequency(enum Clock clock); +} clock_config_t; /* clock_setting() Returns the P_phi / 4 timer setting that will last for the given time. Several units can be used. Be aware that the result is approximate, and very high frequencies or very short delays will yield important errors. + Normally you need not use this function when setting up timers because + timer_start() handles this conversion for you. */ int clock_setting(int duration, enum ClockUnit unit); /* clock_config() - Returns a copy of the clock configuration. + Returns a copy of what the library knows about the clocks. */ -struct ClockConfig clock_config(void); - -/* - sleep() - Sleeps until an interrupt is accepted. -*/ -void sleep(void); - -/* - sleep_us() - Sleeps for the given number of us using the user timer. The result will - always be slightly less than required. -*/ -void sleep_us(int us_delay); - - - -//--- -// Internal API. -// Referenced for documentation purposes only. Do not use. -//--- - -/* - clock_measure() - Begins the frequency measurements. The measurements will end - automatically. While doing measurements, do not use the RTC interrupt - or the user timer. - Call clock_measure_end() to wait until the measurements are finished. - It is possible to execute code during the measurements, so that less - time is spent. -*/ -void clock_measure(void); - -/* - clock_measure_end() - Waits until the measurements are finished. This may be immediate. -*/ -void clock_measure_end(void); +clock_config_t clock_config(void); #endif // _CLOCK_H diff --git a/include/ctype.h b/include/ctype.h index a325f23..23ceac4 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -62,7 +62,7 @@ __attribute__((always_inline)) static inline int isxdigit(int c) { } __attribute__((always_inline)) static inline int isascii(int c) { - return (c >= 0 && c <= 0x7f); + return ((unsigned)c <= 0x7f); } __attribute__((always_inline)) static inline int isblank(int c) { diff --git a/include/display.h b/include/display.h index 5112f86..c5dba6e 100644 --- a/include/display.h +++ b/include/display.h @@ -10,6 +10,7 @@ #define _DISPLAY_H 1 #include +#include //--- diff --git a/include/gint.h b/include/gint.h index c49cb5f..85fc84f 100644 --- a/include/gint.h +++ b/include/gint.h @@ -8,185 +8,221 @@ //--- #ifndef _GINT_H -#define _GINT_H 1 +#define _GINT_H -#define GINT_VERSION 0x01000000 -#define GINT_VERSION_STR "01.00" +#include +#include + +#define GINT_VERSION 0x000100a3 +#define GINT_VERSION_STR "00.01" //--- -// Interrupt handler control. +// System info provided by the library //--- -/* - gint_getVBR() - Returns the current vbr address. -*/ -unsigned int gint_getVBR(void); - -/* - gint_systemVBR() - Returns the vbr address used by the system (saved when execution - starts). -*/ -unsigned int gint_systemVBR(void); - -/* - gint_setDefaultHandler() - In case gint receives an interrupt it doesn't recognize, it can fall - back to a user-provided interrupt handler. Set it to NULL to disable - this feature. - Be aware that the event code passed to the default handler will either - be INTEVT2 (SH7705) or INTEVT (SH7305), but its value for each - interrupt source is completely platform-dependent. Remember to handle - both platforms for increased portability, if possible. -*/ -void gint_setDefaultHandler(void (*default_handler)(int event_code)); - - - -//--- -// Register access. -//--- - -/* - enum Register - Represents common registers. Used as identifiers to retrieve their - values using gint_register(). -*/ -enum Register +typedef struct { - Register_EXPEVT, - Register_MMUCR, - Register_TEA, -}; + /* Returns the current VBR address. */ + uint32_t (*vbr)(void); + /* Gint's VBR address. */ + uint32_t gint_vbr; + /* The system's VBR address, saved when gint was initialized. */ + uint32_t system_vbr; +} gint_info_t; + +extern gint_info_t gint; + + + +//--- +// Exception and interrupt handlers +//--- + +typedef enum +{ + //--- + // Resets + // Obviously there will be no handler for these ones. + //--- + + /* Power-on reset: raised when power is supplied */ + exc_poweron_reset = 0, + /* Manual reset: probably when RESET button is pressed */ + exc_manual_reset = 0, + /* TLB multihit: more than one entry matches the requested address + (SH7305 only) */ + exc_tlb_multihit = 0, + + //--- + // General exceptions + //--- + + /* + Address error: an invalid address was accessed + - Location of instruction + - Address at fault + - Access type + 1: Instruction or data read access + 2: Data write access + */ + exc_address_error = 1, + + /* + TLB protection violation: address access is prevented by TLB + - Location of instruction + - Address at fault + - Access type + 1: Instruction or data read access + 2: Data write access + */ + exc_tlb_protection_violation = 2, + + /* + TLB invalid: entry was found but valid bit is cleared (SH7705 only) + - Location of instruction + - Address at fault + - Access type + 1: Instruction or data read access + 2: Data write access + */ + exc_tlb_invalid = 3, + + /* + Illegal instruction: current instruction is not a valid opcode + - Location of instruction + - Opcode at fault + */ + exc_illegal_instruction = 4, + + /* + Illegal slot: doing something illegal within a delayed slot + - Location of instruction + - Opcode at fault + */ + exc_illegal_slot = 5, + + /* User break: a user break request was fulfilled */ + exc_user_break = 6, + /* Initial page write: trying to write while dirty bit is reset */ + exc_initial_page_write = 7, + + /* + Unconditional trap: a 'trapa' instruction was executed + - Location of instruction + - Trap number + */ + exc_trap = 8, + + /* DMA address error: the DMAC violated word or long memory access + alignments (SH7705 only) */ + exc_dma_address = 9, + + //--- + // TLB misses + //--- + + /* + TLB miss: no match found in TLB for requested address + - Location of instruction + - Address at fault + - Access type + 1: Instruction or data read access + 2: Data write access + */ + exc_tlb_miss = 10, + + //--- + // Interrupt requests + //--- + + /* + Non-Maskable Interrupt: triggered by an external pin + */ + int_nmi = 11, + + /* + Timer underflow: a timer's counter reached zero + - Timer channel + 0: Channel 0 + 1: Channel 1 + 2: Channel 2 + */ + int_timer_underflow = 12, + + /* + Timer input capture: a capture of timer channel 2 has been requested + by the external clock (SH7705 only) + - Captured value + */ + int_timer_input_capture = 13, + + /* + Real-time clock alarm interrupt: configured alarm registers and + current time match + */ + int_rtc_alarm = 14, + + /* + Real-time clock periodic interrupt: various possible frequencies + - Current interrupt frequency + */ + int_rtc_periodic = 15, + + /* + Real-time clock carry interrupt: when a carry occurs while you're + reading time + */ + int_rtc_carry = 16, + + //--- + // Other flags + //--- + + // Maximum valid value for this type. + exc_type_max, + +} gint_interrupt_type_t; + +/* + gint_install() + Installs an exception or interrupt handler for one of gint's recognized + interrupts. The type signature of the handler function depends on the + particular signal it's answering. Please refer to the documentation to + know what parameters each handler function is provided with. +*/ +void gint_install(gint_interrupt_type_t signal, void *function); + +/* + gint_uninstall() + Uninstalls the exception or interrupt handler that was used for the + given interrupt, falling back to gint's default handler. +*/ +void gint_uninstall(gint_interrupt_type_t signal); + + + +//--- +// Register access +//--- + +typedef enum +{ + register_expevt = 0, + register_intevt = 1, + register_mmucr = 2, + register_tea = 3, + register_tra = 4, + +} gint_register_t; /* gint_register() - Returns the address of a common register. All common registers exist - on both platforms but they may hold different values for the same - information (f.i. EXPEVT may not return the same value for a given - exception on both 7705 and 7305). + Returns the address of a platform-shared register. All these registers + exist on both platforms but they may hold different values for the same + kind of information (f.i the periodic RTC interrupt will change the + value of INTEVT to 0x4a0 on SH7705, and 0xaa0 on SH7305). Higher-level + interfaces may provide platform-independent information in such cases. */ -volatile void *gint_reg(enum Register reg); - -/* - gint_strerror() - Returns a string that describe the error set in EXPEVT in case of - general exception of TLB miss exception. This string is platform- - independent. - Some exception codes represent different errors when invoked inside the - general exception handler and the TLB error handler. Parameter 'is_tlb' - should be set to zero for general exception meanings, and anything non- - zero for TLB error meanings. -*/ -const char *gint_strerror(int is_tlb); - - - -//--- -// Internal API. -// Referenced here for documentation purposes only. -// Do NOT call these functions, you'll most probably screw up the whole -// interrupt handling system. -//--- - -/* - gint_setVBR() - Sets the vbr address and calls the configuration function while - interrupts are disabled. -*/ -void gint_setVBR(unsigned int new_vbr_address, void (*setup)(void)); - -/* - gint_callDefaultHandler() - Calls the user-provided default interrupt handler. -*/ -void gint_callDefaultHandler(int event_code); - -/* - gint_init() - Initializes gint. Loads the interrupt handler into the memory and sets - the new vbr address. -*/ -void gint_init(void); - -/* - gint_quit() - Stops gint. Restores the system's configuration and vbr address. -*/ -void gint_quit(void); - -/* - gint_setup() - Configures interrupt priorities and some parameters to allow gint to - take control of the interrupt flow. -*/ -void gint_setup_7705(void); -void gint_setup_7305(void); - -/* - gint_stop() - Un-configures the interrupt flow to give back the interrupt control to - the system. -*/ -void gint_stop_7705(void); -void gint_stop_7305(void); - -/* - gint_reg() - gint_strerror() - See "Register access" section. -*/ -volatile void *gint_reg_7705(enum Register reg); -volatile void *gint_reg_7305(enum Register reg); -const char *gint_strerror_7705(int is_tlb); -const char *gint_strerror_7305(void); - - -//--- -// Exception handling. -//--- - -/* - gint_exc() - Handles exceptions. -*/ -void gint_exc(void) __attribute__((section(".gint.exc.entry"), - interrupt_handler)); -void gint_exc_7705(void) __attribute__((section(".gint.exc"))); -void gint_exc_7305(void) __attribute__((section(".gint.exc"))); - -/* - gint_tlb() - Handles TLB misses. -*/ -void gint_tlb(void) __attribute__((section(".gint.tlb.entry"), - interrupt_handler)); -void gint_tlb_7705(void) __attribute__((section(".gint.tlb"))); -void gint_tlb_7305(void) __attribute__((section(".gint.tlb"))); - -/* - gint_int() - Handles interrupts. -*/ -void gint_int(void) __attribute__((section(".gint.int.entry"), - interrupt_handler)); -void gint_int_7705(void) __attribute__((section(".gint.int"))); -void gint_int_7305(void) __attribute__((section(".gint.int"))); - - - -//--- -// Internal platform-independent definitions. -//--- - -#define GINT_INTP_WDT 4 -#define GINT_INTP_RTC 12 - -#define GINT_INTP_GRAY 15 -#define GINT_INTP_KEY 8 -#define GINT_INTP_TIMER 10 - - +volatile void *gint_reg(gint_register_t reg); #endif // _GINT_H diff --git a/include/gray.h b/include/gray.h index e18a20a..bbae4f0 100644 --- a/include/gray.h +++ b/include/gray.h @@ -138,23 +138,4 @@ void gimage(int x, int y, struct Image *image); void gimage_part(int x, int y, struct Image *image, int left, int top, int width, int height); - - -//--- -// Internal API. -// Referenced here for documentation purposes only. Do not call. -//-- - -/* - gray_interrupt() - Answers a timer interrupt. Swaps the two buffers. -*/ -void gray_interrupt(void) __attribute__((section(".gint.int"))); - -/* - gray_init() - Initializes the gray engine. -*/ -void gray_init(void) __attribute__((constructor)); - #endif // _GRAY_H diff --git a/include/internals/clock.h b/include/internals/clock.h new file mode 100644 index 0000000..6de2c43 --- /dev/null +++ b/include/internals/clock.h @@ -0,0 +1,21 @@ +#ifndef _INTERNALS_CLOCK_H +#define _INTERNALS_CLOCK_H + +/* + clock_measure() + Begins the frequency measurements. The measurements will end + automatically. While doing measurements, do not use the RTC interrupt + or the user timer. + Call clock_measure_end() to wait until the measurements are finished. + It is possible to execute code during the measurements, so that less + time is spent. +*/ +void clock_measure(void); + +/* + clock_measure_end() + Waits until the measurements are finished. This may be immediate. +*/ +void clock_measure_end(void); + +#endif // _INTERNALS_CLOCK_H diff --git a/include/internals/exceptions.h b/include/internals/exceptions.h new file mode 100644 index 0000000..80b4b38 --- /dev/null +++ b/include/internals/exceptions.h @@ -0,0 +1,66 @@ +#ifndef _INTERNALS_EXCEPTIONS_H +#define _INTERNALS_EXCEPTIONS_H + +#include + +/* + exch_address_error() + CPU address error, e.g. alignment issues. +*/ +void exch_address_error(uint32_t pc, uint32_t tea, uint32_t access); + +/* + exch_tlb_protection_violation() + You don't have the right to access this address. +*/ +void exch_tlb_protection_violation(uint32_t pc, uint32_t tea, uint32_t access); + +/* + exch_tlb_invalid() + The translation info for this address is marked as invalid. +*/ +void exch_tlb_invalid(uint32_t pc, uint32_t tea, uint32_t access); + +/* + exch_illegal_instruction() + What's this opcode anyway? +*/ +void exch_illegal_instruction(uint32_t pc, uint32_t opcode); + +/* + exch_illegal_slot() + You can't execute this in a delay slot. +*/ +void exch_illegal_slot(uint32_t pc, uint32_t opcode); + +/* + exch_user_break() + One of the user break conditions you requested was fulfilled. +*/ +void exch_user_break(void); + +/* + exch_initial_page_write() + You can't write to this memory page, it's too early. +*/ +void exch_initial_page_write(void); + +/* + exch_trap() + You asked for it. +*/ +void exch_trap(uint32_t pc, uint32_t trap); + +/* + exch_dma_address() + The DMAC is accessing badly-aligned addresses. +*/ +void exch_dma_address(void); + +/* + exch_tlb_miss() + This virtual address points nowhere. +*/ +void exch_tlb_miss(uint32_t pc, uint32_t tea, uint32_t access); + +#endif // _INTERNALS_EXCEPTIONS_H diff --git a/include/internals/gint.h b/include/internals/gint.h index 4f038d2..91a07a7 100644 --- a/include/internals/gint.h +++ b/include/internals/gint.h @@ -1,30 +1,184 @@ #ifndef _INTERNALS_GINT_H #define _INTERNALS_GINT_H 1 +#include +#include + //--- -// Exception code strings. +// Interrupt handlers //--- -extern const char *gint_str[]; +// General exception handler. +void gint_exc(void); +// TLB miss handler. +void gint_tlb(void); +// Interrupt handler. +void gint_int(void); //--- -// Register access. +// Assembler-level VBR management //--- /* - gint_spc() - Returns the saved program counter, which is the last state of execution - saved by interrupt processing. + gint_getvbr() + Retrieves the current VBR address. */ -unsigned int gint_spc(void); +uint32_t gint_getvbr(void); /* - gint_ssr() - Returns the saved status register, which is the last configuration - saved by interrupt processing. + gint_setvbr() + Sets the VBR address and calls the configuration function while + interrupts are disabled. */ -unsigned int gint_ssr(void); +void gint_setvbr(uint32_t vbr, void (*setup)(void)); + + + +//--- +// Initialization and termination routines +//--- + +/* + gint_init() + Initializes gint. Loads the interrupt handler into the memory and sets + the new vbr address. +*/ +void gint_init(void); + +/* + gint_quit() + Stops gint. Restores the system's configuration and vbr address. +*/ +void gint_quit(void); + +/* + gint_save() + Saves many registers into a buffer to ensure that the system is not + upset by gint's configuration when the application ends. +*/ +//void gint_save_7705(gint_save_buffer_t *buffer); +//void gint_save_7305(gint_save_buffer_t *buffer); + +/* + gint_setup() + Configures interrupt priorities and some parameters to allow gint to + take control of the interrupt flow. +*/ +void gint_setup_7705(void); +void gint_setup_7305(void); + +/* + gint_restore() + Restores the parameters saved in a save buffer to give back the + interrupt control to the system. +*/ +//void gint_restore_7705(gint_save_buffer_t *buffer); +//void gint_restore_7305(gint_save_buffer_t *buffer); + +/* + gint_reg() + Returns the address of a platform-shared register. +*/ +volatile void *gint_reg_7705(gint_register_t reg); +volatile void *gint_reg_7305(gint_register_t reg); + + + +//--- +// Diagnostics +// When diagnostics are enabled, gint saves runtime information to a +// buffer in RAM, which by chance is held if the application crashes (I +// don't really know why). This allows deeper debugging. +//--- + +#ifdef GINT_DIAGNOSTICS + +#include + +// Determining whether the SaveDisp() buffer actually contains gint diagnostics +// is performed by checking a magic number (1/256 chance of failure) and the +// validity of all fields in the diagnostic information. Formally, a picture +// taken by SaveDisp() could fool the checks but this is *very* unlikely and +// the diagnostics should only be read just after gint crashes or stops anyway. +#define GINT_DIAGNOSTICS_MAGIC 0xb7 + +typedef enum +{ + stage_startup = 0, + stage_sections = 1, + stage_mmu = 2, + stage_gint = 3, + stage_clock = 4, + stage_ctors = 5, + stage_running = 6, + stage_leaving = 7, + stage_dtors = 8, + stage_terminated = 9, + +} gint_stage_t; + +typedef struct +{ + uint32_t address; + uint32_t length; + +} gint_memsection_t; + +typedef struct +{ + // Magic number to check whether there is a diagnostic. + uint8_t magic :8; + // Unique counter that is incremented at each execution, allowing to + // distinguish diagnostics output at different times if the application + // crashes repeatedly. + uint8_t counter :8; + // How many work of initialization had been successfully done before + // the application crashed. + gint_stage_t stage :8; + // What kind of MPU the library detected (undefined if the + // initialization stage does not reach stage_gint). + mpu_t mpu :8; + + // Frequency of the main clocks, in MHz. + uint8_t Bphi_f :8; + uint8_t Iphi_f :8; + uint8_t Pphi_f :8; + + // What kind of exceptions occurred last. + uint8_t excepts :8; + uint8_t except_vect[12]; + // Last values held by registers SPC, SSR, EXPEVT / INTEVT2 / INTEVT, + // and TEA. + uint32_t spc; + uint32_t ssr; + uint32_t expevt; + uint32_t tea; + + // Gint version number, on the form 0xMMmmbbbb, where MM is the major + // version, mm the minor version and bbbb the build number. + uint32_t version; + // Location of the VBR at the time of execution. + uint32_t vbr_address; + + // Memory map. + uint32_t romdata; + gint_memsection_t section_text; + gint_memsection_t section_data; + gint_memsection_t section_bss; + gint_memsection_t section_gint; + +} gint_diagnostics_t; + +// This is somewhere inside the buffers of SaveDisp(), 3 bytes inside the first +// buffer to be exact (that's to be 4-aligned). +// This buffer is 1024-byte long so the logs must fit in 1021 bytes to be safe. +// It looks like that this RAM area is generally not cleared when the +// calculator reboots after a crash, even though it does not seem to work with +// manual resets. Maybe it can provide useful information in some cases. +#define gint_diagnostics() ((volatile gint_diagnostics_t *)0x88004d90) + +#endif // GINT_DIAGNOSTICS #endif // _INTERNALS_GINT_H diff --git a/include/internals/interrupt_maps.h b/include/internals/interrupt_maps.h new file mode 100644 index 0000000..ac66baf --- /dev/null +++ b/include/internals/interrupt_maps.h @@ -0,0 +1,100 @@ +#ifndef _INTERRUPT_MAPS +#define _INTERRUPT_MAPS + +#include +#include + +//--- +// Interrupt handlers +//--- + +/* + gint_interrupt_arg_t + 8-bit integer representing an argument to pass as uint32_t to an + exception or interrupt handler. +*/ +typedef enum +{ + // Empty argument (always at end of list). + arg_none = 0x00, + // Signal subtype (f.i address error subtypes: code, read, write). + arg_subtype = 0x01, + // Value of register SPC is passed. + arg_pc = 0x02, + // Instruction pointed at by SPC is passed (re-execution types). + arg_opcode = 0x03, + // Address indicated in register TEA is passed. + arg_tea = 0x04, + // Trap number is passed. + arg_trap = 0x05, + // Timer channel 2 captured input (SH7705 only). + arg_timer_capt = 0x06, + +} gint_interrupt_arg_t; + +/* + gint_interrupt_handler_t + Contains both static and dynamic information of interrupt handlers: + the current handler is stored there, as well as the type signature + information through an array of parameters. +*/ +typedef struct +{ + // Current handler function. The type signature may vary, hence the + // void * pointer. + void *function; + // Default handler and fallback if the current handler is un-installed. + void *default_function; + // Default interrupt priorities (interrupts only). + uint8_t priority; + // Arguments passed to the handler (these are values of type arg_t). + // This array is the only thing that defines the type signature of the + // two functions above. + uint8_t args[3]; + +} gint_interrupt_handler_t; + +// Handler array. +extern gint_interrupt_handler_t gint_handlers[]; + + + +//--- +// Interrupt maps +//--- + +/* + gint_interrupt_map_t + Maps an event code to an interrupt type and subtype. The subtypes allow + group-handling similar interrupts (for instance TLB misses for code and + data accesses, or timer underflows from various channels). +*/ +typedef struct +{ + uint8_t type; + uint8_t subtype; + +} __attribute__((packed)) gint_interrupt_map_t; + +/* + gint_map_7705() + TLB misses and TLB invalid have the same event code though they are + handled by different functions in the VBR space. The offset argument + expects values 0x100, 0x400 or 0x600 to distinguish between them. +*/ +gint_interrupt_map_t gint_map_7705(uint32_t event_code, uint32_t offset); + +/* + gint_map_7305() + Maps an event code to an interrupt type. The SH7305 does not have TLB + invalid exceptions so no event codes overlap. +*/ +gint_interrupt_map_t gint_map_7305(uint32_t event_code); + +/* + gint_invoke() + Invokes an interrupt or exception handler, given its type and subtype. +*/ +void gint_invoke(uint8_t type, uint8_t subtype); + +#endif // _INTERRUPT_MAPS diff --git a/include/internals/interrupts.h b/include/internals/interrupts.h new file mode 100644 index 0000000..0957d11 --- /dev/null +++ b/include/internals/interrupts.h @@ -0,0 +1,18 @@ +#ifndef _INTERNALS_INTERRUPTS_H +#define _INTERNALS_INTERRUPTS_H + +#include + +/* + inth_timer_underflow() + Wake up, your timer has expired! +*/ +void inth_timer_underflow(uint32_t channel); + +/* + inth_rtc_periodic() + Don't you forget to execute the periodic tasks. +*/ +void inth_rtc_periodic(void); + +#endif // _INTERNALS_INTERRUPTS_H diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h index f982fcf..2824893 100644 --- a/include/internals/keyboard.h +++ b/include/internals/keyboard.h @@ -2,6 +2,7 @@ #define _INTERNALS_KEYBOARD_H #include +#include // Keyboard variables. extern volatile unsigned char keyboard_state[10]; @@ -14,12 +15,6 @@ extern int last_key, last_repeats, last_events; // RTC callback id. extern unsigned cb_id; -/* - sleep() - Puts the CPU into sleep until an interrupt request is accepted. -*/ -void sleep(void); - /* getPressedKey() Finds a pressed key in the keyboard state and returns it. diff --git a/include/keyboard.h b/include/keyboard.h index a1b101b..4bb2483 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -280,16 +280,14 @@ enum KeyType keytype(int key); Notifies the keyboard module that an interrupt request has been issued, and updates the keyboard state. */ -void keyboard_interrupt(void) __attribute__((section(".gint.int"))); +void keyboard_interrupt(void); /* keyboard_updateState() Updates the keyboard state. */ -void keyboard_updateState_7705(volatile unsigned char *state) - __attribute__((section(".gint.int"))); -void keyboard_updateState_7305(volatile unsigned char *state) - __attribute__((section(".gint.int"))); +void keyboard_updateState_7705(volatile unsigned char *state); +void keyboard_updateState_7305(volatile unsigned char *state); /* keyboard_init() diff --git a/include/mpu.h b/include/mpu.h index fd4a498..e789cdd 100644 --- a/include/mpu.h +++ b/include/mpu.h @@ -4,15 +4,22 @@ // // Determines which kind of MPU is running the program. This module // provides macro tests isSH3(), isSH4(), and the identifier of the MPU, -// which is stored in a global variable MPU_CURRENT. +// which is stored in a global variable MPU_CURRENT and determined at +// startup. // -// If you need to do MPU-dependant jobs, prefer the following alternative: +// If you need to do MPU-dependant jobs, use isSH3() or (possibly) isSH4() +// instead of testing the value of MPU_CURRENT because these macros take +// care of assuming unknown MPUs are SH4, which is the more reasonable +// option. +// +// In a general way, it is advised to always use the following +// alternative (which gint does): // // if(isSH3()) // { // ... // } -// else +// else // { // ... // } @@ -23,29 +30,30 @@ #define _MPU_H 1 /* - enum MPU + mpu_t This type holds information about the calculator's MPU. */ -enum MPU +typedef enum { - MPU_Unknown = 0, + mpu_unknown = 0, // fx-9860G SH3. - MPU_SH7337 = 1, + mpu_sh7337 = 1, // fx-9860G II SH3. - MPU_SH7355 = 2, + mpu_sh7355 = 2, // fx-9860G II SH4. - MPU_SH7305 = 3, - // Just for reference. - MPU_SH7724 = 4 -}; + mpu_sh7305 = 3, + // Just for reference (no calculator uses it). + mpu_sh7724 = 4, + +} mpu_t; // Global MPU variable, accessible for direct tests. Initialized at the // beginning of execution. -extern enum MPU MPU_CURRENT; +extern mpu_t MPU_CURRENT; // Quick SH3 test. It is safer to assume that an unknown model is SH4 because // SH3-based models are not produced anymore. -#define isSH3() (MPU_CURRENT == MPU_SH7337 || MPU_CURRENT == MPU_SH7355) +#define isSH3() (MPU_CURRENT == mpu_sh7337 || MPU_CURRENT == mpu_sh7355) #define isSH4() !isSH3() @@ -58,19 +66,6 @@ extern enum MPU MPU_CURRENT; getMPU() Determines the MPU type and returns it. MPU_CURRENT is not updated. */ -enum MPU getMPU(void); - - - -//--- -// Internal API. -// Referenced here for documentation purposes only. Do not call. -//--- - -/* - mpu_init() - Determines the MPU type and stores the result into MPU_CURRENT. -*/ -void mpu_init(void) __attribute__((constructor)); +mpu_t getMPU(void); #endif // _MPU_H diff --git a/include/rtc.h b/include/rtc.h index 805c91d..48f72e9 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -107,9 +107,9 @@ int rtc_cb_edit(int id, enum RTCFrequency new_freq, rtc_interrupt() Handles an RTC interrupt by calling the callback. */ -void rtc_interrupt(void) __attribute__((section(".gint.int"))); -void rtc_interrupt_7705(void) __attribute__((section(".gint.int"))); -void rtc_interrupt_7305(void) __attribute__((section(".gint.int"))); +void rtc_interrupt(void); +void rtc_interrupt_7705(void); +void rtc_interrupt_7305(void); /* rtc_cb_interrupt() diff --git a/include/tales.h b/include/tales.h index e1304c3..501e263 100644 --- a/include/tales.h +++ b/include/tales.h @@ -12,6 +12,7 @@ #include #include +#include //--- // Types and constants. diff --git a/include/timer.h b/include/timer.h index 34fe987..1b5cb7d 100644 --- a/include/timer.h +++ b/include/timer.h @@ -106,6 +106,6 @@ void timer_reload2(int timer, int new_delay); timer_interrupt() Handles the interrupt for the given timer. */ -void timer_interrupt(int timer) __attribute__((section(".gint.int"))); +void timer_interrupt(int timer); #endif // _TIMER_H diff --git a/src/bopti/bopti_internals.c b/src/bopti/bopti_internals.c index acdf435..4b6503f 100644 --- a/src/bopti/bopti_internals.c +++ b/src/bopti/bopti_internals.c @@ -81,7 +81,7 @@ void bopti_grid_a32(const uint32_t *layer, int column_count, int height, { for(row = c->top; row < c->bottom; row++) { - (*c->op)(vram_offset, layer[row], c); + (c->op)(vram_offset, layer[row], c); vram_offset += 4; } @@ -108,8 +108,8 @@ void bopti_grid(const uint32_t *layer, int column_count, int height, int vram_column_offset = (c->y << 2) + (c->x >> 5) + (c->x < 0); int vram_offset = vram_column_offset; - int shift1 = 32 - (c->x & 31); int shift2 = (c->x & 31); + int shift1 = 32 - shift2; // Initializing two pointers. They will read two adjacent columns at // the same time (p2 is column ahead of p1). Since the columns are @@ -138,7 +138,8 @@ void bopti_grid(const uint32_t *layer, int column_count, int height, l2 = (right_column < column_count) ? p2[line] : (0); operator = (l1 << shift1) | (l2 >> shift2); - (*c->op)(vram_offset, operator, c); + (c->op)(vram_offset, operator, c); + vram_offset += 4; } @@ -199,7 +200,8 @@ void bopti_end_nover(const unsigned char *end, int size, struct Command *c) operator = (*get)(&end); operator <<= shift; - (*c->op)(vram_offset, operator, c); + (c->op)(vram_offset, operator, c); + vram_offset += 4; } } @@ -225,10 +227,12 @@ void bopti_end(const unsigned char *end, int size, struct Command *c) row_data = (*get)(&end); operator = row_data >> shift1; - (*c->op)(vram_offset, operator, c); + (c->op)(vram_offset, operator, c); + operator = row_data << shift2; - (*c->op)(vram_offset + 1, operator, c); + (c->op)(vram_offset + 1, operator, c); + vram_offset += 4; } @@ -288,8 +292,8 @@ void getStructure(struct Image *img, struct Structure *s) // Large images. if(!img->width && !img->height) { - s->width = (img->data[0] << 8) | img->data[1]; - s->height = (img->data[2] << 8) | img->data[3]; + s->width = img->data[0] >> 16; + s->height = img->data[0] & 0xffff; s->data = (uint8_t *)img->data + 4; column_count = (s->width + 31) >> 5; diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c index 4033398..104aee8 100644 --- a/src/bopti/dimage.c +++ b/src/bopti/dimage.c @@ -2,10 +2,13 @@ #include /* - dimage() - Displays a monochrome image in the video ram. + dimage_part() + Draws a portion of an image, defined by its bounding rectangle. + Point (left, top) is included, but (left + width, top + height) is + excluded. */ -void dimage(int x, int y, struct Image *img) +void dimage_part(int x, int y, struct Image *img, int left, int top, + int width, int height) { if(!img || img->magic != 0x01) return; @@ -16,18 +19,28 @@ void dimage(int x, int y, struct Image *img) if(format != Format_Mono && format != Format_MonoAlpha) return; getStructure(img, &s); + if(width < 0) width = s.width; + if(height < 0) height = s.height; //--- - // Adjusting image parameters. + // Adjusting the bounding rectangle. //--- - if(x + s.width < 0 || x > 127 || y + s.height < 0 || y > 63) return; + // This is what happens when the bounding rectangle overflows from the + // image... + if(left < 0) left = 0; + if(top < 0) top = 0; + if(left + width > s.width) width = s.width - left; + if(top + height > s.height) height = s.height - top; - command.top = (y < 0) ? (-y) : (0); - command.bottom = (y + s.height > 64) ? (64 - y) : (s.height); - command.left = ((x < 0) ? (-x) : (0)) >> 5; - actual_width = (x + s.width > 128) ? (128 - x) : (s.width); - command.right = ((actual_width + 31) >> 5) - 1; + if(x + left + width <= 0 || x > 127 || y + top + height <= 0 || y > 63) + return; + + command.top = (y < 0) ? (top - y) : top; + command.bottom = top + ((y + height > 64) ? (64 - y) : height); + command.left = ((x < 0) ? (left - x) : left) >> 5; + actual_width = (x + width > 128) ? (128 - x) : width; + command.right = ((left + actual_width + 31) >> 5) - 1; command.op = bopti_op_mono; @@ -38,12 +51,10 @@ void dimage(int x, int y, struct Image *img) while(format) { - // Drawing every layer, in order of formats. if(format & 1) { - // These members are modified by bopti()! - command.x = x; - command.y = y; + command.x = x - left; + command.y = y - top; command.channel = (1 << i); bopti(s.data, &s, &command); @@ -53,5 +64,13 @@ void dimage(int x, int y, struct Image *img) format >>= 1; i++; } - +} + +/* + dimage() + Displays a monochrome image in the video ram. +*/ +void dimage(int x, int y, struct Image *img) +{ + dimage_part(x, y, img, 0, 0, -1, -1); } diff --git a/src/bopti/dimage_part.c b/src/bopti/dimage_part.c deleted file mode 100644 index 4af879f..0000000 --- a/src/bopti/dimage_part.c +++ /dev/null @@ -1,65 +0,0 @@ -#include -#include - -/* - dimage_part() - Draws a portion of an image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void dimage_part(int x, int y, struct Image *img, int left, int top, - int width, int height) -{ - if(!img || img->magic != 0x01) return; - - struct Structure s; - struct Command command; - int actual_width; - int format = img->format, i = 0; - - if(format != Format_Mono && format != Format_MonoAlpha) return; - getStructure(img, &s); - - //--- - // Adjusting the bounding rectangle. - //--- - - // This is what happens when the bounding rectangle overflows from the - // image... - if(left < 0) left = 0; - if(top < 0) top = 0; - if(left + width > s.width) width = s.width - left; - if(top + height > s.height) height = s.height - top; - - if(x + left + width <= 0 || x > 127 || y + top + height <= 0 || y > 63) - return; - - command.top = (y < 0) ? (top - y) : top; - command.bottom = top + ((y + height > 64) ? (64 - y) : height); - command.left = ((x < 0) ? (left - x) : left) >> 5; - actual_width = (x + width > 128) ? (128 - x) : width; - command.right = ((left + actual_width + 31) >> 5) - 1; - - command.op = bopti_op_mono; - - if(x >= 0) getMasks(x, x + actual_width - 1, command.masks); - else getMasks(0, actual_width + x - 1, command.masks); - - bopti_vram = display_getCurrentVRAM(); - - while(format) - { - if(format & 1) - { - command.x = x - left; - command.y = y - top; - command.channel = (1 << i); - - bopti(s.data, &s, &command); - s.data += s.layer_size; - } - - format >>= 1; - i++; - } -} diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c index e010f20..1bd4b5c 100644 --- a/src/bopti/gimage.c +++ b/src/bopti/gimage.c @@ -3,10 +3,13 @@ #include /* - gimage() - Displays a gray image in the video ram. + gimage_part() + Draws a portion of a gray image, defined by its bounding rectangle. + Point (left, top) is included, but (left + width, top + height) is + excluded. */ -void gimage(int x, int y, struct Image *img) +void gimage_part(int x, int y, struct Image *img, int left, int top, + int width, int height) { if(!img || img->magic != 0x01) return; @@ -16,18 +19,28 @@ void gimage(int x, int y, struct Image *img) int format = img->format, i = 0; getStructure(img, &s); + if(width < 0) width = s.width; + if(height < 0) height = s.height; //--- - // Adjusting image parameters. + // Adjusting the bounding rectangle. //--- - if(x + s.width <= 0 || x > 127 || y + s.height <= 0 || y > 63) return; + // This is what happens when the bounding rectangle overflows from the + // image... + if(left < 0) left = 0; + if(top < 0) top = 0; + if(left + width > s.width) width = s.width - left; + if(top + height > s.height) height = s.height - top; - command.top = (y < 0) ? (-y) : (0); - command.bottom = (y + s.height > 64) ? (64 - y) : (s.height); - command.left = ((x < 0) ? (-x) : (0)) >> 5; - actual_width = (x + s.width > 128) ? (128 - x) : (s.width); - command.right = ((actual_width + 31) >> 5) - 1; + if(x + left + width <= 0 || x > 127 || y + top + height <= 0 || y > 63) + return; + + command.top = (y < 0) ? (top - y) : top; + command.bottom = top + ((y + height > 64) ? (64 - y) : height); + command.left = ((x < 0) ? (left - x) : left) >> 5; + actual_width = (x + width > 128) ? (128 - x) : width; + command.right = ((left + actual_width + 31) >> 5) - 1; command.op = bopti_op_gray; @@ -39,12 +52,10 @@ void gimage(int x, int y, struct Image *img) while(format) { - // Drawing every layer, in order of formats. if(format & 1) { - // These members are modified by bopti()! - command.x = x; - command.y = y; + command.x = x - left; + command.y = y - top; command.channel = (1 << i); bopti(s.data, &s, &command); @@ -55,3 +66,12 @@ void gimage(int x, int y, struct Image *img) i++; } } + +/* + gimage() + Displays a gray image in the video ram. +*/ +void gimage(int x, int y, struct Image *img) +{ + gimage_part(x, y, img, 0, 0, -1, -1); +} diff --git a/src/bopti/gimage_part.c b/src/bopti/gimage_part.c deleted file mode 100644 index 7c3314f..0000000 --- a/src/bopti/gimage_part.c +++ /dev/null @@ -1,85 +0,0 @@ -#include -#include -#include - -/* -#define min(a, b) ((a) < (b) ? (a) : (b)) -#define max(a, b) ((a) > (b) ? (a) : (b)) - -struct Rect -{ - int top, bottom; - int left, right; -}; -struct Rect intersect(struct Rect r1, struct Rect r2) -{ - struct Rect result; - result.top = max(r1.top, r2.top); - result.bottom = min(r1.bottom, r2.bottom); - result.left = max(r1.left, r2.left); - result.right = min(r1.right, r2.right); -} -*/ - -/* - gimage_part() - Draws a portion of a gray image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void gimage_part(int x, int y, struct Image *img, int left, int top, - int width, int height) -{ - if(!img || img->magic != 0x01) return; - - struct Structure s; - struct Command command; - int actual_width; - int format = img->format, i = 0; - - getStructure(img, &s); - - //--- - // Adjusting the bounding rectangle. - //--- - - // This is what happens when the bounding rectangle overflows from the - // image... - if(left < 0) left = 0; - if(top < 0) top = 0; - if(left + width > s.width) width = s.width - left; - if(top + height > s.height) height = s.height - top; - - if(x + left + width <= 0 || x > 127 || y + top + height <= 0 || y > 63) - return; - - command.top = (y < 0) ? (top - y) : top; - command.bottom = top + ((y + height > 64) ? (64 - y) : height); - command.left = ((x < 0) ? (left - x) : left) >> 5; - actual_width = (x + width > 128) ? (128 - x) : width; - command.right = ((left + actual_width + 31) >> 5) - 1; - - command.op = bopti_op_gray; - - if(x >= 0) getMasks(x, x + actual_width - 1, command.masks); - else getMasks(0, actual_width + x - 1, command.masks); - - bopti_v1 = gray_lightVRAM(); - bopti_v2 = gray_darkVRAM(); - - while(format) - { - if(format & 1) - { - command.x = x - left; - command.y = y - top; - command.channel = (1 << i); - - bopti(s.data, &s, &command); - s.data += s.layer_size; - } - - format >>= 1; - i++; - } -} diff --git a/src/clock/clock.c b/src/clock/clock.c index 28230be..a88a216 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -5,39 +5,13 @@ #include #include -static struct ClockConfig conf = { +static clock_config_t conf = { .FLL = -1, .PLL = -1, .Bphi_div1 = -1, .Iphi_div1 = -1, .Pphi_div1 = -1, .CKIO_f = -1, .Bphi_f = -1, .Iphi_f = -1, .Pphi_f = -1 }; -/* - clock_frequency() - Returns the approximate frequency, in Hz, of the given clock. The - measurements need to have been done. Returns a negative number on - error. -*/ -int clock_frequency(enum Clock clock) -{ - switch(clock) - { - case Clock_CKIO: - return conf.CKIO_f; - case Clock_RTCCLK: - return conf.RTCCLK_f; - case Clock_Bphi: - return conf.Bphi_f; - case Clock_Iphi: - return conf.Iphi_f; - case Clock_Pphi: - return conf.Pphi_f; - - default: - return -2; - } -} - /* clock_setting() Returns the P_phi / 4 timer setting that will last for the given time. @@ -74,7 +48,7 @@ int clock_setting(int duration, enum ClockUnit unit) clock_config() Returns a copy of the clock configuration. */ -struct ClockConfig clock_config(void) +clock_config_t clock_config(void) { return conf; } @@ -91,24 +65,28 @@ void sleep(void) } /* - sleep_us() - Sleeps for the given number of us using the user timer. The result will - always be slightly less than required. + sleep_ms(), sleep_us() + Sleeps for the given number of milliseconds / microseconds using the + TIMER_USER timer. The actual sleep time will always be slightly less + than requested. */ -static volatile int sleep_us_done = 0; - -static void sleep_us_callback(void) +static volatile int sleep_done = 0; +static void sleep_callback(void) { - sleep_us_done = 1; + sleep_done = 1; } +void sleep_ms(int ms_delay) +{ + sleep_us(1000 * ms_delay); +} void sleep_us(int us_delay) { - sleep_us_done = 0; - timer_start(TIMER_USER, us_delay, Clock_us, sleep_us_callback, 1); + sleep_done = 0; + timer_start(TIMER_USER, us_delay, Clock_us, sleep_callback, 1); do sleep(); - while(!sleep_us_done); + while(!sleep_done); } @@ -126,7 +104,12 @@ static void clock_compute_7305(void); /* clock_measure() - Measures or computes the clock frequencies. + Begins the frequency measurements. The measurements will end + automatically. While doing measurements, do not use the RTC interrupt + or the user timer. + Call clock_measure_end() to wait until the measurements are finished. + It is possible to execute code during the measurements, so that less + time is spent. */ void clock_measure(void) { diff --git a/src/core/crt0.c b/src/core/crt0.c index d22de28..19cf44d 100644 --- a/src/core/crt0.c +++ b/src/core/crt0.c @@ -1,21 +1,27 @@ #include #include #include +#include -#include -#include +#include #include +#include +#include +#include int main(void); static void init(void); static void fini(void); -// Symbols imported from the linker script. -extern unsigned int - romdata, - bbss, ebss, - bdata, edata; +// Symbols provided by the linker script. +extern uint32_t + etext, btext, // Location of .text section + bdata, edata, // Location of .data section + bbss, ebss, // Location of .bss section + bgint, egint, // Location of interrut handler and gint data + gint_vbr, // VBR address + romdata; // ROM address of .data section contents // This variable should be overwritten before being returned, so the default // value doesn't matter much. @@ -34,55 +40,133 @@ int atexit_index = 0; main(). Also prepares the execution environment by initializing all the modules. */ - -int start(void) - __attribute__(( - section(".pretext.entry") - )); - -int start(void) +__attribute__((section(".pretext.entry"))) int start(void) { - // Linker symbols. - unsigned int *bss = &bbss; - unsigned int *data = &bdata, *src = &romdata; - int x; + #ifdef GINT_DIAGNOSTICS + + // Ok, so fill as much information as possible so that in case anything + // goes wrong we can try to analyze what's been going on. + volatile gint_diagnostics_t *dg = gint_diagnostics(); + + // All that follows is "safe" information that can be saved immediately + // because it will never change anyway. + // Basic environment description. + dg->magic = GINT_DIAGNOSTICS_MAGIC; + dg->counter = dg->counter + 1; + dg->mpu = mpu_unknown; + dg->version = GINT_VERSION; + dg->stage = stage_startup; + + // Exception records: what kind of exceptions / TLB faults / interrupts + // occured last to get some trace in case of crash. + dg->excepts = 0; + for(size_t i = 0; i < sizeof dg->except_vect; i++) + dg->except_vect[i] = 0; + dg->spc = 0x00000000; + dg->ssr = 0x00000000; + dg->expevt = 0x00000000; + dg->tea = 0x00000000; + + // Memory map: provides information about alignement and the relative + // size and position of each section, as well as read-only / read-write + // types of addresses. + dg->section_text.address = (uint32_t)&btext; + dg->section_text.length = (uint32_t)&etext - (uint32_t)&btext; + dg->section_data.address = (uint32_t)&bdata; + dg->section_data.length = (uint32_t)&edata - (uint32_t)&bdata; + dg->section_bss.address = (uint32_t)&bbss; + dg->section_bss.length = (uint32_t)&ebss - (uint32_t)&bbss; + dg->romdata = (uint32_t)&romdata; + + // Basic information about the running library. + dg->vbr_address = (uint32_t)&gint_vbr; + dg->section_gint.address = (uint32_t)&bgint; + dg->section_gint.length = (uint32_t)&egint - (uint32_t)&bgint; + #endif // Clearing the .bss section. + uint32_t *bss = &bbss; while(bss < &ebss) *bss++ = 0; + // Copying the .data section. + uint32_t *data = &bdata, *src = &romdata; while(data < &edata) *data++ = *src++; + #ifdef GINT_DIAGNOSTICS + dg->stage = stage_sections; + #endif + + // Trying to get the system to fill the TLB without editing it + // directly, so that it does not go on rampage when finding out. mmu_pseudoTLBInit(); + #ifdef GINT_DIAGNOSTICS + // At this point it would be wise to include a copy of the TLB, but + // it's already a huge array. Maybe later... + /* TODO Debug TLB? */ + dg->stage = stage_mmu; + #endif + // Initializing gint. gint_init(); - // Measure clock frequencies. + #ifdef GINT_DIAGNOSTICS + dg->mpu = MPU_CURRENT; + dg->stage = stage_gint; + #endif + + // Measuring clock frequencies. clock_measure(); clock_measure_end(); + #ifdef GINT_DIAGNOSTICS + clock_config_t clock = clock_config(); + dg->Bphi_f = clock.Bphi_f / 1000000; + dg->Iphi_f = clock.Iphi_f / 1000000; + dg->Pphi_f = clock.Pphi_f / 1000000; + dg->stage = stage_clock; + #endif + // Calling global constructors. init(); + #ifdef GINT_DIAGNOSTICS + dg->stage = stage_ctors; + dg->stage = stage_running; + #endif + // Saving the execution state there. - x = setjmp(env); + int x = setjmp(env); // If the program has just started, executing main(). Otherwise, the // exit code has already been set by abort() or similar. if(!x) exit_code = main(); + #ifdef GINT_DIAGNOSTICS + dg->stage = stage_leaving; + #endif - // Remember to flush and close opened streams. + /* TODO Flush and close opened streams. */ // Calling exit handlers. while(atexit_index > 0) (*atexit_handlers[--atexit_index])(); - // Un-initializing everything. + // Calling the destructors. fini(); + + #ifdef GINT_DIAGNOSTICS + dg->stage = stage_dtors; + #endif + + // Un-initializing gint. gint_quit(); + #ifdef GINT_DIAGNOSTICS + dg->stage = stage_terminated; + #endif + return exit_code; } @@ -166,3 +250,12 @@ int atexit(void (*function)(void)) return 0; } +void crt0_system_menu(void) +{ + fini(); + gint_quit(); + __system_menu(); + mmu_pseudoTLBInit(); + gint_init(); + init(); +} diff --git a/src/core/exceptions.c b/src/core/exceptions.c new file mode 100644 index 0000000..ca57cc0 --- /dev/null +++ b/src/core/exceptions.c @@ -0,0 +1,172 @@ +#include +#include +#include + +//--- +// On-screen error messages +//--- + +#define print(x, y, str) dtext(6 * (x) - 5, 8 * (y) - 7, str) + +static void print_hex(int x, int y, uint32_t n, int digits) +{ + #define hexdigit(n) ((n) + '0' + 39 * ((n) > 9)) + + char str[9]; + if(digits >= 8) digits = 8; + str[digits] = 0; + + while(digits) + { + str[digits - 1] = hexdigit(n & 0xf); + n >>= 4; + digits--; + } + + print(x, y, str); + + #undef hexdigit +} + +static void show_error(const char *name, uint32_t *access_mode, uint32_t *tea, + uint32_t *opcode) +{ + uint32_t *vram = display_getCurrentVRAM(); + uint32_t spc, ssr; + __asm__("stc spc, %0" : "=rm"(spc)); + __asm__("stc ssr, %0" : "=rm"(ssr)); + + dclear(); + text_configure(NULL, Color_Black); + + print(3, 1, "EXCEPTION RAISED!"); + for(int i = 0; i < 36; i++) vram[i] = ~vram[i]; + print(1 + ((21 - strlen(name)) >> 1), 2, name); + + print(4, 4, "PC"); + print_hex(7, 4, spc, 8); + print(4, 5, "SR"); + print_hex(7, 5, ssr, 8); + + int y = 6; + if(access_mode) + { + print(2, y, "Type"); + print(7, y++, *access_mode == 1 ? "Code/Data read" : + "Data write"); + } + if(tea) + { + print(3, y, "TEA"); + print_hex(7, y++, *tea, 8); + } + if(opcode) + { + print(2, y, "Code"); + print_hex(7, y++, *opcode, 4); + } + + dupdate(); + while(1); +} + +#undef print + + + +//--- +// Exception handlers +//--- + +#pragma GCC diagnostic ignored "-Wunused-parameter" + +/* + exch_address_error() + CPU address error, e.g. alignment issues. +*/ +void exch_address_error(uint32_t pc, uint32_t tea, uint32_t access) +{ + show_error("Address error", &access, &tea, NULL); +} + +/* + exch_tlb_protection_violation() + You don't have the right to access this address. +*/ +void exch_tlb_protection_violation(uint32_t pc, uint32_t tea, uint32_t access) +{ + show_error("TLB protection", &access, &tea, NULL); +} + +/* + exch_tlb_invalid() + The translation info for this address is marked as invalid. +*/ +void exch_tlb_invalid(uint32_t pc, uint32_t tea, uint32_t access) +{ + show_error("TLB invalid", &access, &tea, NULL); +} + +/* + exch_illegal_instruction() + What's this opcode anyway? +*/ +void exch_illegal_instruction(uint32_t pc, uint32_t opcode) +{ + show_error("Illegal instruction", NULL, NULL, &opcode); +} + +/* + exch_illegal_slot() + You can't execute this in a delay slot. +*/ +void exch_illegal_slot(uint32_t pc, uint32_t opcode) +{ + show_error("Illegal slot", NULL, NULL, &opcode); +} + +/* + exch_user_break() + One of the user break conditions you requested was fulfilled. +*/ +void exch_user_break(void) +{ + show_error("User break", NULL, NULL, NULL); +} + +/* + exch_initial_page_write() + You can't write to this memory page, it's too early. +*/ +void exch_initial_page_write(void) +{ + show_error("Initial page write", NULL, NULL, NULL); +} + +/* + exch_trap() + You asked for it. +*/ +void exch_trap(uint32_t pc, uint32_t trap) +{ +} + +/* + exch_dma_address() + The DMAC is accessing badly-aligned addresses. +*/ +void exch_dma_address(void) +{ + show_error("DMA address error", NULL, NULL, NULL); +} + +/* + exch_tlb_miss() + This virtual address points nowhere. +*/ +void exch_tlb_miss(uint32_t pc, uint32_t tea, uint32_t access) +{ + show_error("TLB miss", &access, &tea, NULL); +} + +#pragma GCC diagnostic pop diff --git a/src/core/gint.c b/src/core/gint.c index 28de9f2..608bce5 100644 --- a/src/core/gint.c +++ b/src/core/gint.c @@ -8,278 +8,182 @@ //--- #include +#include +#include +#include #include #include -#include -static unsigned int - new_vbr, - sys_vbr; - -static void (*default_handler)(int event_code) = NULL; +// Referencing the interrupt handlers to force ld to link them in (there should +// be a better way of achieving this, but I can't seem to find it). +__attribute__((used)) static void (*_exc)(void) = &gint_exc; +__attribute__((used)) static void (*_tlb)(void) = &gint_tlb; +__attribute__((used)) static void (*_int)(void) = &gint_int; //--- -// Local functions. +// Exception and interrupt handlers //--- -/* - gint_setup() - Configures interrupt priorities and some parameters to allow gint to - take control of the interrupt flow. -*/ -static void gint_setup(void) -{ - if(isSH3()) - gint_setup_7705(); - else - gint_setup_7305(); -} +gint_interrupt_handler_t gint_handlers[] = { + + //--- + // Resets and non-events + //--- + + { NULL, NULL, 0, { 0x00, 0x00, 0x00 } }, + + //--- + // General exceptions + //--- + + // Address error. + { NULL, exch_address_error, 0, + { arg_pc, arg_tea, arg_subtype } }, + // TLB protection violation. + { NULL, exch_tlb_protection_violation, 0, + { arg_pc, arg_tea, arg_subtype } }, + // TLB Invalid (SH7705 only). + { NULL, exch_tlb_invalid, 0, + { arg_pc, arg_tea, arg_subtype } }, + // Illegal instruction. + { NULL, exch_illegal_instruction, 0, + { arg_pc, arg_opcode, 0x00 } }, + // Illegal opcode. + { NULL, exch_illegal_slot, 0, + { arg_pc, arg_opcode, 0x00 } }, + // User break. + { NULL, exch_user_break, 0, + { 0x00, 0x00, 0x00 } }, + // Initial page write. + { NULL, exch_initial_page_write, 0, + { 0x00, 0x00, 0x00 } }, + // Unconditional trap. + { NULL, exch_trap, 0, + { arg_pc, arg_trap, 0x00 } }, + // DMA address error. + { NULL, exch_dma_address, 0, + { 0x00, 0x00, 0x00 } }, + + //--- + // TLB misses + //--- + + { NULL, exch_tlb_miss, + 0, { arg_pc, arg_tea, arg_subtype } }, + + //--- + // Interrupt requests + //--- + + // Non-Maskable Interrupt. + { NULL, NULL, 0, + { 0x00, 0x00, 0x00 } }, + // Timer underflow. + { NULL, inth_timer_underflow, 12, + { arg_subtype, 0x00, 0x00 } }, + // Timer channel 2 input capture (SH7705 only) + { NULL, NULL, 0, + { arg_timer_capt, 0x00, 0x00 } }, + // Real-time clock alarm interrupt. + { NULL, NULL, 0, + { 0x00, 0x00, 0x00 } }, + // Real-time clock periodic interrupt. + { NULL, inth_rtc_periodic, 10, + { 0x00, 0x00, 0x00 } }, + // Real-time clock carry interrupt. + { NULL, NULL, 0, + { 0x00, 0x00, 0x00 } }, +}; /* - gint_stop() - Un-configures the interrupt flow to give back the interrupt control to - the system. + gint_invoke() + Invokes an interrupt or exception handler, given its type and subtype. */ -static void gint_stop(void) +void gint_invoke(uint8_t type, uint8_t subtype) { - if(isSH3()) - gint_stop_7705(); - else - gint_stop_7305(); -} + if(type >= exc_type_max || !gint_handlers[type].function) return; + volatile uint32_t *tea, *tra, *tcpr_2; + uint32_t args[3]; + uint16_t *pc; + // Getting some initial information to work with. + __asm__("stc spc, %0" : "=rm"(pc)); + tea = gint_reg(register_tea); + tra = gint_reg(register_tra); + tcpr_2 = (void *)0xfffffeb8; -//--- -// Public API. -//--- - -/* - gint_systemVBR() - Returns the vbr address used by the system (saved when execution - starts). -*/ -inline unsigned int gint_systemVBR(void) -{ - return sys_vbr; -} - -/* - gint_setDefaultHandler() - In case gint receives an interrupt it doesn't recognize, it can fall - back to a user-provided interrupt handler. Set it to NULL to disable - this feature. - Be aware that the event code passed to the default handler will either - be INTEVT2 (SH7705) or INTEVT (SH7305), but its value for each - interrupt case is completely platform-dependent. Remember to handle - both platforms for increased portability, if possible. -*/ -void gint_setDefaultHandler(void (*new_handler)(int event_code)) -{ - default_handler = new_handler; -} - -/* - gint_init() - Initializes gint. Loads the interrupt handler into the memory and sets - the new vbr address. -*/ -void gint_init(void) -{ - // Linker script symbols -- gint. - extern unsigned int - gint_vbr, - gint_data, - bgint, egint; - - unsigned int *ptr = &bgint; - unsigned int *src = &gint_data; - - // This initialization routine is usually called before any - // constructor. We want to ensure that the MPU type is detected, but - // mpu_init() hasn't been called yet. - mpu_init(); - - // Loading the interrupt handler into the memory. - while(ptr < &egint) *ptr++ = *src++; - - sys_vbr = gint_getVBR(); - new_vbr = (unsigned int)&gint_vbr; - - gint_setVBR(new_vbr, gint_setup); -} - -/* - gint_quit() - Stops gint. Restores the system's configuration and vbr address. -*/ -void gint_quit(void) -{ - gint_setVBR(sys_vbr, gint_stop); -} - - - -//--- -// VBR space. -//--- - -#include -#define print(str, x, y) dtext(6 * (x) - 5, 8 * (y) - 7, str) -#define hexdigit(n) ((n) + '0' + 39 * ((n) > 9)) - -static void hex(unsigned int x, int digits, char *str) -{ - str[0] = '0'; - str[1] = 'x'; - str[digits + 2] = 0; - - while(digits) + // Building up the argument list. + for(int i = 0; i < 3; i++) switch(gint_handlers[type].args[i]) { - str[digits + 1] = hexdigit(x & 0xf); - x >>= 4; - digits--; + case arg_none: args[i] = 0; break; + case arg_subtype: args[i] = subtype; break; + case arg_pc: args[i] = (uint32_t)pc; break; + case arg_opcode: args[i] = *pc; break; + case arg_tea: args[i] = *tea; break; + case arg_trap: args[i] = *tra >> 2; break; + case arg_timer_capt: args[i] = *tcpr_2; break; } -} -static void reverse(void) -{ - int *vram = display_getCurrentVRAM(); - int i; - for(i = 0; i < 36; i++) vram[i] = ~vram[i]; + // Calling the function with the required amount of arguments. + if(gint_handlers[type].args[2]) + { + void (*handler)(uint32_t, uint32_t, uint32_t); + handler = gint_handlers[type].function; + (*handler)(args[0], args[1], args[2]); + } + else if(gint_handlers[type].args[1]) + { + void (*handler)(uint32_t, uint32_t); + handler = gint_handlers[type].function; + (*handler)(args[0], args[1]); + } + else if(gint_handlers[type].args[0]) + { + void (*handler)(uint32_t); + handler = gint_handlers[type].function; + (*handler)(args[0]); + } + else (*(void (*)(void))gint_handlers[type].function)(); } /* - gint_exc() - Handles exceptions. + gint_install() + Installs an exception or interrupt handler for one of gint's recognized + interrupts. */ -void gint_exc(void) +void gint_install(gint_interrupt_type_t type, void *function) { - volatile unsigned int *expevt = gint_reg(Register_EXPEVT); - volatile unsigned int *tea = gint_reg(Register_TEA); - unsigned int spc; - char str[11]; - - text_configure(NULL, Color_Black); - - __asm__("\tstc spc, %0" : "=r"(spc)); - - dclear(); - print("Exception raised!", 3, 1); - reverse(); - print(gint_strerror(0), 2, 3); - - print("expevt", 2, 4); - hex(*expevt, 3, str); - print(str, 16, 4); - - print("pc", 2, 5); - hex(spc, 8, str); - print(str, 11, 5); - - print("tea", 2, 6); - hex(*tea, 8, str); - print(str, 11, 6); - - print("Please reset.", 2, 7); - dupdate(); - while(1); + if((unsigned)type >= exc_type_max) return; + gint_handlers[type].function = function; } /* - gint_tlb() - Handles TLB misses. + gint_uninstall() + Uninstalls the exception or interrupt handler that was used for the + given interrupt, falling back to gint's default handler. */ -void gint_tlb(void) +void gint_uninstall(gint_interrupt_type_t type) { - volatile unsigned int *expevt = gint_reg(Register_EXPEVT); - volatile unsigned int *tea = gint_reg(Register_TEA); - unsigned int spc; - char str[11]; - - text_configure(NULL, Color_Black); - - __asm__("\tstc spc, %0" : "=r"(spc)); - - dclear(); - print("TLB error!", 6, 1); - reverse(); - print(gint_strerror(1), 2, 3); - - print("expevt", 2, 4); - hex(*expevt, 3, str); - print(str, 16, 4); - - print("pc", 2, 5); - hex(spc, 8, str); - print(str, 11, 5); - - print("tea", 2, 6); - hex(*tea, 8, str); - print(str, 11, 6); - - print("Please reset.", 2, 7); - dupdate(); - while(1); -} - -/* - gint_int() - Handles interrupts. -*/ -void gint_int(void) -{ - if(isSH3()) - gint_int_7705(); - else - gint_int_7305(); + if((unsigned)type >= exc_type_max) return; + gint_handlers[type].function = gint_handlers[type].default_function; } //--- -// Internal API. +// Register access //--- -/* - gint_callDefaultHandler() - Calls the user-provided default interrupt handler. -*/ -void gint_callDefaultHandler(int event_code) -{ - if(default_handler) default_handler(event_code); -} - /* gint_reg() Returns the address of a common register. All common registers exist on both platforms but they may hold different values for the same - information (f.i. EXPEVT may not return the same value for a given - exception on both 7705 and 7305). + information. */ -inline volatile void *gint_reg(enum Register reg) +volatile void *gint_reg(gint_register_t reg) { - if(isSH3()) - return gint_reg_7705(reg); - else - return gint_reg_7305(reg); -} - -/* - gint_strerror() - Returns a string that describe the error set in EXPEVT. This string is - not platform-dependent. - Some exception codes represent different errors when invoked inside the - general exception handler and the TLB error handler. Parameter 'is_tlb' - should be set to zero for general exception meanings, and anything non- - zero for TLB error meanings. -*/ -const char *gint_strerror(int is_tlb) -{ - if(isSH3()) - return gint_strerror_7705(is_tlb); - else - return gint_strerror_7305(); + return isSH3() ? gint_reg_7705(reg) : gint_reg_7305(reg); } diff --git a/src/mpu/gint_sh7305.c b/src/core/gint_sh7305.c similarity index 66% rename from src/mpu/gint_sh7305.c rename to src/core/gint_sh7305.c index 7327a38..7410084 100644 --- a/src/mpu/gint_sh7305.c +++ b/src/core/gint_sh7305.c @@ -15,52 +15,6 @@ #include <7305.h> #include -#include - -//--- -// Interrupt codes. -//--- - -#define IC_RTC_PRI 0xaa0 -#define IC_KEYSC 0xbe0 -#define IC_TMU0_TUNI0 0x400 -#define IC_TMU0_TUNI1 0x420 -#define IC_TMU0_TUNI2 0x440 - - - -//--- -// Exception handling. -//--- - -void gint_int_7305(void) -{ - volatile unsigned int *intevt = (unsigned int *)0xff000028; - unsigned int code = *intevt; - - switch(code) - { - case IC_RTC_PRI: - rtc_interrupt(); - break; - - case IC_TMU0_TUNI0: - timer_interrupt(TIMER_TMU0); - break; - - case IC_TMU0_TUNI1: - timer_interrupt(TIMER_TMU1); - break; - - case IC_TMU0_TUNI2: - timer_interrupt(TIMER_TMU2); - break; - - default: - gint_callDefaultHandler(code); - } -} - /* gint_reg() Returns the address of a common register. All common registers exist @@ -68,47 +22,19 @@ void gint_int_7305(void) information (f.i. EXPEVT may not return the same value for a given exception on both 7705 and 7305). */ -volatile void *gint_reg_7305(enum Register reg) +volatile void *gint_reg_7305(gint_register_t reg) { - volatile unsigned int *expevt = (unsigned int *)0xff000024; - volatile unsigned int *tea = (unsigned int *)0xff00000c; - volatile unsigned int *mmucr = (unsigned int *)0xff000010; - switch(reg) { - case Register_EXPEVT: return expevt; - case Register_TEA: return tea; - case Register_MMUCR: return mmucr; + case register_tea: return (void *)0xff00000c; + case register_mmucr: return (void *)0xff000010; + case register_tra: return (void *)0xff000020; + case register_expevt: return (void *)0xff000024; + case register_intevt: return (void *)0xff000028; default: return NULL; } } -/* - gint_strerror() - Returns a string that describe the error set in EXPEVT. This string is - not platform-dependent. -*/ -const char *gint_strerror_7305(void) -{ - volatile unsigned int *expevt = gint_reg_7305(Register_EXPEVT); - - switch(*expevt) - { - case 0x1e0: return gint_str[3]; - case 0x0e0: return gint_str[8]; - case 0x040: return gint_str[14]; - case 0x0a0: return gint_str[17]; - case 0x180: return gint_str[6]; - case 0x1a0: return gint_str[7]; - case 0x100: return gint_str[9]; - case 0x060: return gint_str[15]; - case 0x0c0: return gint_str[18]; - case 0x080: return gint_str[20]; - case 0x160: return gint_str[21]; - } - return gint_str[0]; -} - //--- @@ -164,10 +90,10 @@ static void gint_priority_lock_7305(void) // Allowing RTC. Keyboard analysis is done regularly using a RTC // because SH7305's special KEYSC interface does not allow us to clear // the keyboard interrupt flags. - INTX.IPRK._RTC = GINT_INTP_RTC; - INTX.IPRA.TMU0_0 = GINT_INTP_KEY; - INTX.IPRA.TMU0_1 = GINT_INTP_GRAY; - INTX.IPRA.TMU0_2 = GINT_INTP_TIMER; + INTX.IPRK._RTC = 10; + INTX.IPRA.TMU0_0 = 12; + INTX.IPRA.TMU0_1 = 12; + INTX.IPRA.TMU0_2 = 12; } static void gint_priority_unlock_7305(void) diff --git a/src/mpu/gint_sh7705.c b/src/core/gint_sh7705.c similarity index 51% rename from src/mpu/gint_sh7705.c rename to src/core/gint_sh7705.c index a1c19aa..9f5ac26 100644 --- a/src/mpu/gint_sh7705.c +++ b/src/core/gint_sh7705.c @@ -15,52 +15,6 @@ #include <7705.h> #include -#include - -//--- -// Interrupt codes. -//--- - -#define IC_RTC_PRI 0x4a0 -#define IC_PINT07 0x700 -#define IC_TMU0_TUNI0 0x400 -#define IC_TMU1_TUNI1 0x420 -#define IC_TMU2_TUNI2 0x440 - - - -//--- -// Exception handling. -//--- - -void gint_int_7705(void) -{ - volatile unsigned int *intevt2 = (unsigned int *)0xa4000000; - unsigned int code = *intevt2; - - switch(code) - { - case IC_RTC_PRI: - rtc_interrupt(); - break; - - case IC_TMU0_TUNI0: - timer_interrupt(TIMER_TMU0); - break; - - case IC_TMU1_TUNI1: - timer_interrupt(TIMER_TMU1); - break; - - case IC_TMU2_TUNI2: - timer_interrupt(TIMER_TMU2); - break; - - default: - gint_callDefaultHandler(code); - } -} - /* gint_reg() Returns the address of a common register. All common registers exist @@ -68,52 +22,19 @@ void gint_int_7705(void) information (f.i. EXPEVT may not return the same value for a given exception on both 7705 and 7305). */ -volatile void *gint_reg_7705(enum Register reg) +volatile void *gint_reg_7705(gint_register_t reg) { - volatile unsigned int *expevt = (unsigned int *)0xffffffd4; - volatile unsigned int *mmucr = (unsigned int *)0xfffffff4; - volatile unsigned int *tea = (unsigned int *)0xfffffffc; - switch(reg) { - case Register_EXPEVT: return expevt; - case Register_MMUCR: return mmucr; - case Register_TEA: return tea; + case register_intevt: return (void *)0xa4000000; + case register_tra: return (void *)0xffffffd0; + case register_expevt: return (void *)0xffffffd4; + case register_mmucr: return (void *)0xfffffff4; + case register_tea: return (void *)0xfffffffc; default: return NULL; } } -/* - gint_strerror() - Returns a string that describe the error set in EXPEVT. This string is - not platform-dependent. - Some exception codes represent different errors when invoked inside the - general exception handler and the TLB error handler. Parameter 'is_tlb' - should be set to zero for general exception meanings, and anything non- - zero for TLB error meanings. -*/ -const char *gint_strerror_7705(int is_tlb) -{ - volatile unsigned int *expevt = gint_reg_7705(Register_EXPEVT); - - switch(*expevt) - { - case 0x1e0: return gint_str[3]; - case 0x0e0: return gint_str[4]; - case 0x040: return gint_str[is_tlb ? 10 : 11]; - case 0x0a0: return gint_str[12]; - case 0x180: return gint_str[6]; - case 0x1a0: return gint_str[7]; - case 0x100: return gint_str[5]; - case 0x060: return gint_str[is_tlb ? 13 : 19]; - case 0x0c0: return gint_str[16]; - case 0x080: return gint_str[20]; - case 0x160: return gint_str[21]; - case 0x5c0: return gint_str[22]; - } - return gint_str[0]; -} - //--- @@ -148,10 +69,10 @@ static void gint_priority_lock_7705(void) INTX.IPRH.WORD = 0x0000; // Allowing RTC, which handles keyboard. - INTC.IPRA.BIT._RTC = GINT_INTP_RTC; - INTC.IPRA.BIT._TMU0 = GINT_INTP_KEY; - INTC.IPRA.BIT._TMU1 = GINT_INTP_GRAY; - INTC.IPRA.BIT._TMU2 = GINT_INTP_TIMER; + INTC.IPRA.BIT._RTC = 10; + INTC.IPRA.BIT._TMU0 = 12; + INTC.IPRA.BIT._TMU1 = 12; + INTC.IPRA.BIT._TMU2 = 12; } static void gint_priority_unlock_7705(void) diff --git a/src/core/gint_str.c b/src/core/gint_str.c deleted file mode 100644 index c7e9957..0000000 --- a/src/core/gint_str.c +++ /dev/null @@ -1,37 +0,0 @@ -#include - -const char *gint_str[] = { - "Unknown", - - // User breaks. - "User break (before)", - "User break (after)", - "User breakpoint", - - // General. - "Inst. address error", - "Data access error", - "Illegal instruction", - "Illegal slot", - "Data address (r)", - "Data address (w)", - - // Instruction TLB. - "Inst. TLB miss", - "Inst. TLB invalid", - "Inst. TLB protect.", - - // Data TLB. - "Data TLB miss", - "Data TLB miss (r)", - "Data TLB miss (w)", - "Data TLB protection", - "Data TLB prot. (r)", - "Data TLB prot. (w)", - "Data TLB invalid", - - // Others. - "Initial page write", - "Trap", - "DMA address error", -}; diff --git a/src/core/gint_vbr.s b/src/core/gint_vbr.s index 488be1b..7253a91 100644 --- a/src/core/gint_vbr.s +++ b/src/core/gint_vbr.s @@ -5,27 +5,27 @@ address needs to be done in assembler. */ - .global _gint_getVBR - .global _gint_setVBR + .global _gint_getvbr + .global _gint_setvbr /* - gint_getVBR() + gint_getvbr() Returns the current vbr address. */ -_gint_getVBR: +_gint_getvbr: rts stc vbr, r0 /* - gint_setVBR() + gint_setvbr() This is quite the hard part when modifying the vbr. We need to set immediately the interrupt priorities of our own handler, or restore - the ones used by the system ; otherwise we may receive interrupts + the ones used by the system; otherwise we may receive interrupts requests that the new handler doesn't handle, which will cause the whole program to freeze. @@ -33,7 +33,7 @@ _gint_getVBR: having disabled all the interrupts in the status register. That's why this function takes as parameter the priority management function. */ -_gint_setVBR: +_gint_setvbr: sts.l pr, @-r15 /* Blocking all interrupts. */ diff --git a/src/core/init_quit.c b/src/core/init_quit.c new file mode 100644 index 0000000..8ecea6b --- /dev/null +++ b/src/core/init_quit.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +gint_info_t gint; + +//--- +// Initialization routines +//--- + +/* + gint_init() + Initializes gint. Loads the interrupt handler into the memory and sets + the new vbr address. +*/ +static void setup(void) +{ + isSH3() ? gint_setup_7705() : gint_setup_7305(); +} +void gint_init(void) +{ + // Linker script symbols -- gint. + extern uint32_t + gint_vbr, + gint_data, + bgint, egint; + + uint32_t *ptr = &bgint; + uint32_t *src = &gint_data; + + // This initialization routine is usually called before any + // constructor. We want to ensure that the MPU type is detected, but + // mpu_init() hasn't been called yet. + mpu_init(); + + // Loading the interrupt handler into the memory. + while(ptr < &egint) *ptr++ = *src++; + + // Installing gint's default exception/interrupt handlers. + for(int i = 0; i < exc_type_max; i++) + { + gint_handlers[i].function = gint_handlers[i].default_function; + } + + // Filling the information structure for the user. + gint.vbr = gint_getvbr; + gint.system_vbr = gint_getvbr(); + gint.gint_vbr = (uint32_t)&gint_vbr; + + // Setting the VBR! + gint_setvbr(gint.gint_vbr, setup); +} + + + +//--- +// Context restoration routines +//--- + +/* + gint_quit() + Stops gint. Restores the system's configuration and vbr address. +*/ +static void stop(void) +{ + isSH3() ? gint_stop_7705() : gint_stop_7305(); +} +void gint_quit(void) +{ + // Restoring the system's VBR. + gint_setvbr(gint.system_vbr, stop); +} diff --git a/src/core/interrupt_maps_7305.c b/src/core/interrupt_maps_7305.c new file mode 100644 index 0000000..bf76fa9 --- /dev/null +++ b/src/core/interrupt_maps_7305.c @@ -0,0 +1,53 @@ +#include +#include + +//--- +// Mapping hardware interrupts info to gint interrupts -- 7305 interrupts +//--- + +#define NO_EVENT (gint_interrupt_map_t){ .type = 0, .subtype = 0 } + +static gint_interrupt_map_t map0[16] = { + { exc_poweron_reset, 0 }, { exc_manual_reset, 0 }, + { exc_tlb_miss, 1 }, { exc_tlb_miss, 2 }, + { exc_initial_page_write, 0 }, { exc_tlb_protection_violation, 1 }, + { exc_tlb_protection_violation, 2 }, { exc_address_error, 1 }, +}; + +static gint_interrupt_map_t map1[16] = { + { exc_address_error, 2 }, NO_EVENT, /* FPU exception */ + { exc_tlb_multihit, 0 }, { exc_trap, 0 }, + { exc_illegal_instruction, 0 }, { exc_illegal_slot, 0 }, + { int_nmi, 0 }, { exc_user_break, 0 }, +}; + +static gint_interrupt_map_t map4[16] = { + { int_timer_underflow, 0 }, { int_timer_underflow, 1 }, + { int_timer_underflow, 2 }, NO_EVENT, + NO_EVENT, NO_EVENT, + NO_EVENT, NO_EVENT, /* SDHI1 [SDHII0] */ +}; + +static gint_interrupt_map_t mapa[16] = { + NO_EVENT, NO_EVENT, /* USB0 [USI0] */ + NO_EVENT, /* USB0 [USI1] */ NO_EVENT, + { int_rtc_alarm, 0 }, { int_rtc_periodic, 0 }, + { int_rtc_carry, 0 }, NO_EVENT, +}; + +static gint_interrupt_map_t *map[16] = { + map0, map1, NULL, NULL, map4, NULL, NULL, NULL, + NULL, NULL, mapa, NULL, NULL, NULL, NULL, NULL, +}; + +/* + gint_map() + Maps an event code to an exception/interrupt type and subtype. +*/ +gint_interrupt_map_t gint_map_7305(uint32_t event_code) +{ + int ctgy = event_code >> 8; + int event = (event_code & 0xff) >> 5; + + return map[ctgy] ? map[ctgy][event] : NO_EVENT; +} diff --git a/src/core/interrupt_maps_7705.c b/src/core/interrupt_maps_7705.c new file mode 100644 index 0000000..d471d0a --- /dev/null +++ b/src/core/interrupt_maps_7705.c @@ -0,0 +1,65 @@ +#include +#include + +//--- +// Mapping hardware interrupt info to gint interrupts -- 7705 interrupts +//--- + +#define NO_EVENT (gint_interrupt_map_t){ .type = 0, .subtype = 0 } + +static gint_interrupt_map_t map0[16] = { + NO_EVENT, { exc_manual_reset, 0 }, + { exc_tlb_invalid, 1 }, { exc_tlb_invalid, 2 }, + { exc_initial_page_write, 0 }, { exc_tlb_protection_violation, 1 }, + { exc_tlb_protection_violation, 2 }, { exc_address_error, 1 }, +}; + +static gint_interrupt_map_t map1[16] = { + { exc_address_error, 2 }, NO_EVENT, + NO_EVENT, { exc_trap, 0 }, + { exc_illegal_instruction, 0 }, { exc_illegal_slot, 0 }, + { int_nmi, 0 }, { exc_user_break, 0 }, +}; + +static gint_interrupt_map_t map4[16] = { + { int_timer_underflow, 0 }, { int_timer_underflow, 1 }, + { int_timer_underflow, 2 }, { int_timer_input_capture, 0 }, + { int_rtc_alarm, 0 }, { int_rtc_periodic, 0 }, + { int_rtc_carry, 0 }, NO_EVENT, +}; + +static gint_interrupt_map_t *map[16] = { + map0, map1, NULL, NULL, map4, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* + gint_map() + Maps an event code and a VBR offset to an exception/interrupt type and + subtype. +*/ +gint_interrupt_map_t gint_map_7705(uint32_t event_code, uint32_t offset) +{ + // Handling TLB misses (only two events). + if(offset == 0x400) switch(event_code) + { + case 0x040: + return (gint_interrupt_map_t) { + .type = exc_tlb_miss, + .subtype = 1, + }; + case 0x060: + return (gint_interrupt_map_t) { + .type = exc_tlb_miss, + .subtype = 2, + }; + default: + return NO_EVENT; + } + + // Handling general exceptions and interrupts. + int ctgy = event_code >> 8; + int event = (event_code & 0xff) >> 5; + + return map[ctgy] ? map[ctgy][event] : NO_EVENT; +} diff --git a/src/core/interrupts.c b/src/core/interrupts.c new file mode 100644 index 0000000..d056774 --- /dev/null +++ b/src/core/interrupts.c @@ -0,0 +1,21 @@ +#include +#include +#include + +/* + inth_timer_underflow() + Wake up, your timer has expired! +*/ +void inth_timer_underflow(uint32_t channel) +{ + timer_interrupt(channel); +} + +/* + inth_rtc_periodic() + Don't you forget to execute the periodic tasks. +*/ +void inth_rtc_periodic(void) +{ + rtc_interrupt(); +} diff --git a/src/mpu/mpu.c b/src/core/mpu.c similarity index 85% rename from src/mpu/mpu.c rename to src/core/mpu.c index f4c7dc6..13f621c 100644 --- a/src/mpu/mpu.c +++ b/src/core/mpu.c @@ -8,7 +8,7 @@ #include -enum MPU MPU_CURRENT; +mpu_t MPU_CURRENT; /* getMPU() @@ -26,7 +26,7 @@ enum MPU MPU_CURRENT; Additionally, the CPU core ID register (CPIDR) at 0xff000048 returns 1 on SH7305. */ -enum MPU getMPU(void) +mpu_t getMPU(void) { // Processor version register. volatile unsigned int *pvr = (unsigned int *)0xff000030; @@ -46,26 +46,26 @@ enum MPU getMPU(void) *plcr = saved_plcr; // Checking whether we are working with an SH7337 or an SH7355. - if(tested_plcr == 0x00ff) return MPU_SH7337; - if(tested_plcr == 0x0fff) return MPU_SH7355; + if(tested_plcr == 0x00ff) return mpu_sh7337; + if(tested_plcr == 0x0fff) return mpu_sh7355; // Looking for SH-4-based MPUs by testing the version registers. This // needs to have the three upper bytes of the processor version // register match 0x10300b : - if((*pvr & 0xffffff00) != 0x10300b00) return MPU_Unknown; + if((*pvr & 0xffffff00) != 0x10300b00) return mpu_unknown; // Now that we have an SH-4-based MPU, checking whether it is SH7305 or // SH7724. switch(*prr & 0xfffffff0) { case 0x00002c00: - return MPU_SH7305; + return mpu_sh7305; case 0x00002200: - return MPU_SH7724; + return mpu_sh7724; } // By default, the MPU is unknown. - return MPU_Unknown; + return mpu_unknown; } @@ -74,7 +74,7 @@ enum MPU getMPU(void) mpu_init() Determines the MPU type and stores the result into MPU_CURRENT. */ -void mpu_init(void) +__attribute__((constructor)) void mpu_init(void) { MPU_CURRENT = getMPU(); } diff --git a/src/core/syscalls.s b/src/core/syscalls.s index e4dd817..0fdb38d 100644 --- a/src/core/syscalls.s +++ b/src/core/syscalls.s @@ -10,6 +10,7 @@ .global ___malloc .global ___free .global ___realloc + .global ___system_menu @@ -34,8 +35,65 @@ ___realloc: nop 1: .long 0xe6d +/* + __system_menu() + Brings one back to the system menu by putting KEY_MENU in the system's + key buffer and calling GetKeyWait(). Of course this needs to be + executed while under system control. +*/ +___system_menu: + sts.l pr, @-r15 + add #-4, r15 + + /* Putting the matrix code in the key buffer. */ + + mov r15, r4 + mov.w .matrix_menu, r2 + mov.w r2, @r4 + mov.l syscall_table, r1 + mov.l .syscall_putcode, r0 + jsr @r1 + nop + + /* Calling GetKeyWait() to display menu. */ + + mov r15, r4 /* column pointer */ + add #-4, r15 + mov r15, r5 /* row pointer */ + mov #-5, r15 + mov r15, r1 + + mov #2, r6 /* type of waiting */ + mov #0, r7 /* timeout period */ + mov.l r1, @-r15 /* keycode pointer */ + mov #0, r2 + mov.l r2, @-r15 /* allow return to menu */ + + mov.l syscall_table, r1 + mov.l .syscall_getkeywait, r0 + jsr @r1 + nop + + /* If the program counter reaches this place, it means that the user + has come back to the program. Restore stack and leave. */ + + add #20, r15 + lds.l @r15+, pr + rts + nop .align 4 +.syscall_getkeywait: + .long 0x0247 +.syscall_putcode: + .long 0x024f +.matrix_menu: + .word 0x0308 + + + + .align 4 + syscall_table: .long 0x80010070 diff --git a/src/core/vbr_space.c b/src/core/vbr_space.c new file mode 100644 index 0000000..2344854 --- /dev/null +++ b/src/core/vbr_space.c @@ -0,0 +1,98 @@ +#include +#include +#include + +// Compiler optimization of the interrupt handlers seems to cause crashes at +// some point. Some research showed that illegal slot exceptions were raised on +// rte; lds.l @r15+, mach, even though it's a legal slot. For now I just turn +// off optimization until I figure out where the true problem is. +#pragma GCC push_options +#pragma GCC optimize("O0") + +//--- +// VBR space +//--- + +#ifdef GINT_DIAGNOSTICS +static void register_interrupt(int offset) +{ + volatile gint_diagnostics_t *dg = gint_diagnostics(); + volatile uint32_t *expevt, *intevt, *tea; + uint32_t event_code, spc, ssr; + + // Getting the addresses of some registers. + expevt = gint_reg(register_expevt); + intevt = gint_reg(register_intevt); + tea = gint_reg(register_tea); + + // Adding an entry in the event history. + event_code = (offset == 0x600) ? (*intevt) : (*expevt); + size_t len = sizeof dg->except_vect; + dg->except_vect[dg->excepts++] = event_code >> 4; + if(dg->excepts >= len) dg->excepts -= len; + + // Updating some fields in the diagnostic record. + __asm__("stc spc, %0" : "=r"(spc)); + __asm__("stc ssr, %0" : "=r"(ssr)); + dg->spc = spc; + dg->ssr = ssr; + dg->expevt = event_code; + dg->tea = *tea; +} +#endif + +/* + gint_exc() + Handles exceptions. +*/ +__attribute__((section(".gint.exc"), interrupt_handler)) +void gint_exc(void) +{ + #ifdef GINT_DIAGNOSTICS + register_interrupt(0x100); + #endif + + uint32_t event = *(uint32_t *)gint_reg(register_expevt); + gint_interrupt_map_t map; + map = isSH3() ? gint_map_7705(event, 0x100) : gint_map_7305(event); + + gint_invoke(map.type, map.subtype); +} + +/* + gint_tlb() + Handles TLB misses. +*/ +__attribute__((section(".gint.tlb"), interrupt_handler)) +void gint_tlb(void) +{ + #ifdef GINT_DIAGNOSTICS + register_interrupt(0x400); + #endif + + uint32_t event = *(uint32_t *)gint_reg(register_expevt); + gint_interrupt_map_t map; + map = isSH3() ? gint_map_7705(event, 0x400) : gint_map_7305(event); + + gint_invoke(map.type, map.subtype); +} + +/* + gint_int() + Handles interrupts. +*/ +__attribute__((section(".gint.int"), interrupt_handler)) +void gint_int(void) +{ + #ifdef GINT_DIAGNOSTICS + register_interrupt(0x600); + #endif + + uint32_t event = *(uint32_t *)gint_reg(register_intevt); + gint_interrupt_map_t map; + map = isSH3() ? gint_map_7705(event, 0x600) : gint_map_7305(event); + + gint_invoke(map.type, map.subtype); +} + +#pragma GCC pop_options diff --git a/src/display/dupdate.c b/src/display/dupdate.c index 6ca42f8..a6c92c9 100644 --- a/src/display/dupdate.c +++ b/src/display/dupdate.c @@ -1,7 +1,6 @@ #include #include #include -#include /* dupdate() @@ -9,6 +8,5 @@ */ void dupdate(void) { - if(gray_runs()) return; screen_display((const void *)vram); } diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index 893897f..9c1071f 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -23,6 +23,38 @@ static int runs = 0; +//--- +// Interrupt control and initialization. +//--- + +/* + gray_interrupt() + Answers a timer interrupt. Swaps the buffers. +*/ +void gray_interrupt(void) +{ + timer_reload2(TIMER_GRAY, delays[(~current) & 1]); + screen_display(vrams[current]); + current ^= 1; +} + +/* + gray_init() + Initializes the gray engine. +*/ +__attribute__((constructor)) void gray_init(void) +{ + vrams[0] = (const void *)display_getLocalVRAM(); + vrams[1] = (const void *)internal_vrams[0]; + vrams[2] = (const void *)internal_vrams[1]; + vrams[3] = (const void *)internal_vrams[2]; + + delays[0] = 912; + delays[1] = 1343; +} + + + //--- // Engine control. //--- @@ -123,35 +155,3 @@ inline void gupdate(void) { current ^= 2; } - - - -//--- -// Interrupt control and initialization. -//--- - -/* - gray_interrupt() - Answers a timer interrupt. Swaps the buffers. -*/ -void gray_interrupt(void) -{ - timer_reload2(TIMER_GRAY, delays[(~current) & 1]); - screen_display(vrams[current]); - current ^= 1; -} - -/* - gray_init() - Initializes the gray engine. -*/ -void gray_init(void) -{ - vrams[0] = (const void *)display_getLocalVRAM(); - vrams[1] = (const void *)internal_vrams[0]; - vrams[2] = (const void *)internal_vrams[1]; - vrams[3] = (const void *)internal_vrams[2]; - - delays[0] = 912; - delays[1] = 1343; -} diff --git a/src/rtc/rtc_callback.c b/src/rtc/rtc_callback.c index 3a69a95..635eafc 100644 --- a/src/rtc/rtc_callback.c +++ b/src/rtc/rtc_callback.c @@ -129,7 +129,8 @@ void rtc_cb_interrupt(void) // Adding to elapsed256 the number of 256-Hz ticks that correspond to // the current interrupt frequency, and rounding the result to a // multiple of this tick number. - elapsed256 = (elapsed256 + scales[rtc_freq]) & ~(scales[rtc_freq] - 1); + elapsed256 += scales[rtc_freq - 1]; + elapsed256 &= ~(scales[rtc_freq - 1] - 1); for(n = 0; n < RTC_CB_ARRAY_SIZE; n++) { @@ -139,7 +140,7 @@ void rtc_cb_interrupt(void) // Only execute callback when the number of elapsed 256-Hz // ticks reach a multiple that correspond to the callback // frequency. - if(elapsed256 & (scales[cb->freq] - 1)) continue; + if(elapsed256 & (scales[cb->freq - 1] - 1)) continue; if(cb->callback) (*cb->callback)(); if(cb->repeats) diff --git a/src/tales/tales_print.c b/src/tales/dprint.c similarity index 56% rename from src/tales/tales_print.c rename to src/tales/dprint.c index be3d107..cf3c4e8 100644 --- a/src/tales/tales_print.c +++ b/src/tales/dprint.c @@ -16,18 +16,3 @@ void dprint(int x, int y, const char *format, ...) dtext(x, y, __stdio_buffer); } - -/* - gprint() - Prints a formatted string. Works the same as printf(). -*/ -void gprint(int x, int y, const char *format, ...) -{ - va_list args; - - va_start(args, format); - __printf(0, format, args); - va_end(args); - - gtext(x, y, __stdio_buffer); -} diff --git a/src/tales/tales_text.c b/src/tales/dtext.c similarity index 60% rename from src/tales/tales_text.c rename to src/tales/dtext.c index 3729f7d..22e7b59 100644 --- a/src/tales/tales_text.c +++ b/src/tales/dtext.c @@ -9,12 +9,3 @@ void dtext(int x, int y, const char *str) { render(x, y, str, operate_mono); } - -/* - gtext() - Prints the given raw string. -*/ -void gtext(int x, int y, const char *str) -{ - render(x, y, str, operate_gray); -} diff --git a/src/tales/gprint.c b/src/tales/gprint.c new file mode 100644 index 0000000..8f58f29 --- /dev/null +++ b/src/tales/gprint.c @@ -0,0 +1,18 @@ +#include +#include +#include + +/* + gprint() + Prints a formatted string. Works the same as printf(). +*/ +void gprint(int x, int y, const char *format, ...) +{ + va_list args; + + va_start(args, format); + __printf(0, format, args); + va_end(args); + + gtext(x, y, __stdio_buffer); +} diff --git a/src/tales/gtext.c b/src/tales/gtext.c new file mode 100644 index 0000000..118aafd --- /dev/null +++ b/src/tales/gtext.c @@ -0,0 +1,11 @@ +#include +#include + +/* + gtext() + Prints the given raw string. +*/ +void gtext(int x, int y, const char *str) +{ + render(x, y, str, operate_gray); +} diff --git a/src/tales/tales_internals.c b/src/tales/tales_internals.c index d0a2483..c12d2c5 100644 --- a/src/tales/tales_internals.c +++ b/src/tales/tales_internals.c @@ -132,8 +132,7 @@ void operate_mono(OPERATE_ARGS) Returns the number of bits available after the operation. If it's negative, call operate() and update() again. */ -int update(uint32_t *operators, int height, int available, - uint32_t *glyph) +int update(uint32_t *operators, int height, int available, uint32_t *glyph) { // Glyph width. int width = glyph[0] >> 24; From 4d0794f8992db1dbab3d13a40f74c3c0324b8d00 Mon Sep 17 00:00:00 2001 From: lephe Date: Sat, 25 Feb 2017 20:10:48 +0100 Subject: [PATCH 04/24] Erf. Large commits always fail. --- Makefile | 3 +-- README.md | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 3c2f7a8..907b636 100755 --- a/Makefile +++ b/Makefile @@ -56,8 +56,7 @@ debug-cflags = -m3 -mb -nostdlib -ffreestanding -I debug/include -I \ include -std=c11 -Os -Wall -Wextra # Specific objects -obj-lib-spec = build/display_font_system.bmp.o \ - build/display_font_terminal.bmp.o +obj-lib-spec = build/display_font_system.bmp.o obj-std-spec = # Configuration files diff --git a/README.md b/README.md index 79e9845..e6f748b 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ the storage folder of the fxSDK: When explicitly running target `all`, the following additional files will be generated in the working directory: * `gintdemo.g1a`, a test application +* `gintdbg.g1a`, a non-gint application used for debugging in case gint crashes The usual `clean`, `mrproper`, and `distclean` rules will clean the directory. There are configuration options, which can be obtained using From 0a694a1cd8f0e53cda2d8ce80fd230eff80db653 Mon Sep 17 00:00:00 2001 From: lephe Date: Sat, 25 Feb 2017 23:19:35 +0100 Subject: [PATCH 05/24] Key repeat events & Timer callbacks with arguments. Fixed sleep_us(). --- TODO | 19 +++-- demo/gintdemo.c | 6 +- demo/gintdemo.h | 6 +- demo/test_keyboard.c | 111 +++++++++++++++++++++++------- demo/test_timer.c | 2 +- include/events.h | 35 +++++----- include/internals/events.h | 2 +- include/internals/keyboard.h | 14 ++-- include/internals/timer.h | 3 +- include/keyboard.h | 36 +--------- include/timer.h | 23 ++++--- src/clock/clock.c | 38 ++++++---- src/events/event_get.c | 10 +-- src/events/event_push.c | 4 +- src/gray/gray_engine.c | 3 +- src/keyboard/getPressedKey.c | 2 +- src/keyboard/getPressedKeys.c | 3 +- src/keyboard/getkey.c | 20 +----- src/keyboard/keyboard_interrupt.c | 38 ++++++---- src/keyboard/keyboard_misc.c | 2 +- src/keyboard/keyboard_sh7305.c | 2 +- src/keyboard/keyboard_sh7705.c | 2 +- src/keyboard/multigetkey.c | 36 ++++++---- src/timer/timer_interrupt.c | 21 +++++- src/timer/timer_start.c | 15 ++-- 25 files changed, 264 insertions(+), 189 deletions(-) diff --git a/TODO b/TODO index 3f7b8de..5be8293 100644 --- a/TODO +++ b/TODO @@ -9,22 +9,21 @@ Simple improvements: - demo: Try 284x124 at (-60, -28) (all disadvantages) - display: Rectangle-based drawing functions - time: Compute CLOCKS_PER_SEC -- events: Introduce KeyRepeat events - string: Use cmp/str to implement memchr() (assembler examples) - string: Do some tests for memcmp() -- core: Register more interrupts (and understand their parameters) -- rtc: Take care of carry when reading time +- core: Register more interrupts (and understand their parameters) +- rtc: Take care of carry when reading time Larger improvements: -- errno: Introduce errno and use it more or less everywhere +- errno: Introduce errno and use it more or less everywhere - bopti: Monochrome bitmaps blending modes - bopti: Handle partial transparency - core: Implement all callbacks and a complete user API -* core: Better save registers -* core: Allow return to menu -- serial: Implement a driver -- usb: Implement a driver -- esper: Cleaner playback, synthetizing -- clock: Handle overclocking (relaunch clocks when overclocking) +* core: Better save registers +* core: Allow return to menu +- serial: Implement a driver +- usb: Implement a driver +- esper: Cleaner playback, synthetizing +- clock: Handle overclocking (relaunch clocks when overclocking) Things to investigate: - Packed bit fields alignment diff --git a/demo/gintdemo.c b/demo/gintdemo.c index b9b92f6..a65dad6 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -300,7 +300,7 @@ void main_menu(int *category, int *app) }; const char *list_tests[] = { - "Keyboard", + "Keyboard and events", "Gray engine", "Image rendering", "Text rendering", @@ -495,6 +495,8 @@ int main(void) { int category, app; + sleep_ms(2000); + while(1) { main_menu(&category, &app); @@ -502,7 +504,7 @@ int main(void) switch((category << 8) | app) { - case 0x0101: test_keyboard(); break; + case 0x0101: test_keyboard_events(); break; case 0x0102: test_gray(); break; case 0x0103: test_bopti(); break; case 0x0104: test_tales(); break; diff --git a/demo/gintdemo.h b/demo/gintdemo.h index 1d32bcd..2b4fc24 100644 --- a/demo/gintdemo.h +++ b/demo/gintdemo.h @@ -46,10 +46,10 @@ void print(int x, int y, const char *format, ...); //--- /* - test_keyboard() - Displays a real-time multigetkey() and the keyboard state. + test_keyboard_events() + Real-time keyboard management with events. */ -void test_keyboard(void); +void test_keyboard_events(void); /* test_gray() diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c index ab90268..0ab410d 100644 --- a/demo/test_keyboard.c +++ b/demo/test_keyboard.c @@ -2,13 +2,9 @@ #include #include #include +#include -/* - test_keyboard() - Displays a real-time multigetkey() and the keyboard state. -*/ - -static void draw(volatile unsigned char *state) +static void draw_keyboard(volatile uint8_t *state) { int i, j, k, l; int x, y; @@ -22,12 +18,16 @@ static void draw(volatile unsigned char *state) if(i == 4 && j == 5) continue; x = 5 * j + 1; - y = 61 - 5 * i; + y = 59 - 5 * i; + + // Space for the horizontal line. + y += 3 * (i < 7); + // Moving the [AC/ON] key. - if(!i) x = 5 * (5) + 1, y = 61 - 5 * (4); + if(!i) x = 5 * (5) + 1, y = 61 - 5 * (4) + 1; // Drawing a filled shape when the key is pressed. - if(state[i] & (128 >> j)) + if(state[i] & (0x80 >> j)) { for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++) if(abs(k) + abs(l) <= 2) @@ -40,9 +40,55 @@ static void draw(volatile unsigned char *state) if(k || l) dpixel(x + k, y + l, Color_Black); } } + + // Binding the arrow keys together for a more visual thing. + dpixel(28, 19, Color_Black); dpixel(29, 19, Color_Black); + dpixel(28, 24, Color_Black); dpixel(29, 24, Color_Black); + dpixel(26, 21, Color_Black); dpixel(26, 22, Color_Black); + dpixel(31, 21, Color_Black); dpixel(31, 22, Color_Black); + + // An horizontal line to separate parts of the keyboard. + dline(5, 28, 32, 28, Color_Black); } -void test_keyboard(void) +typedef struct { + event_type_t type; + int key; + int repeats; + +} enhanced_event_t; + +static void push_history(enhanced_event_t *history, int size, event_t event) +{ + #define event_eq(x, y) ((x).type == (y).type && (x).key == (y).key) + + // Determining where the history ends. + int length = 0; + while(length < size && history[length].type != ET_None) length++; + + // Checking if the previous event is being repeated. + if(length > 0 && event_eq(history[length - 1], event)) + { + history[length - 1].repeats++; + return; + } + + // Making up some space if required. + if(length == size) + { + for(int i = 0; i < size - 1; i++) history[i] = history[i + 1]; + length = size - 1; + } + + // Adding a new entry to the history. + history[length].type = event.type; + history[length].key = event.key; + history[length].repeats = 1; + + #undef event_eq +} + +static void draw_events(enhanced_event_t *history, int size) { const char *key_names[] = { "F1", "F2", "F3", "F4", "F5", "F6", @@ -55,28 +101,41 @@ void test_keyboard(void) "1", "2", "3", "+", "-", NULL, "0", ".", "\x08", "(-)", "EXE", NULL }; + const char *event_names[] = { + "None ", "User ", "Press", "Rept.", "Rel. " + }; - volatile unsigned char *state = keystate(); - int keys[4] = { 0 }; - int i; + for(int i = 0; i < size && history[i].type != ET_None; i++) + { + print(8, 3 + i, "%s %s", event_names[history[i].type], + key_names[keyid(history[i].key)]); + if(history[i].repeats > 1) + print(19, 3 + i, "%d", history[i].repeats); + } +} + +/* + test_keyboard_events() + Real-time keyboard management with events. +*/ +void test_keyboard_events(void) +{ + enhanced_event_t history[5]; + int history_size = 5; + event_t event; + + for(int i = 0; i < history_size; i++) history[i].type = ET_None; while(1) { dclear(); - locate(1, 1, "Keyboard driver"); - locate(8, 3, "Pressed keys:"); - draw(state); - - if(keys[0] == KEY_NONE) locate(9, 4, ":None"); - else for(i = 0; i < 4 && keys[i] != KEY_NONE; i++) - { - locate( 9, i + 4, ":"); - locate(10, i + 4, key_names[keyid(keys[i])]); - } - + locate(1, 1, "Keyboard and events"); + draw_keyboard(keystate()); + draw_events(history, history_size); dupdate(); - multigetkey(keys, 4, 1); - if(keys[0] == KEY_EXIT && keys[1] == KEY_NONE) break; + event = waitevent(); + if(event.type == ET_KeyPress && event.key == KEY_EXIT) break; + push_history(history, history_size, event); } } diff --git a/demo/test_timer.c b/demo/test_timer.c index d8158b5..53e8bd0 100644 --- a/demo/test_timer.c +++ b/demo/test_timer.c @@ -35,7 +35,7 @@ static void timing_timer(void) static void timing_start(void) { - timer_start(TIMER_USER, 64, Clock_Hz, timing_timer, 0); + timer_start(TIMER_USER, 64, Clock_Hz, timing_timer, NULL, 0); rtc_cb_edit(cb_id, RTCFreq_64Hz, timing_rtc); elapsed_timer = 0; diff --git a/include/events.h b/include/events.h index 01e5277..b1832a4 100644 --- a/include/events.h +++ b/include/events.h @@ -9,15 +9,11 @@ #ifndef _EVENTS_H #define _EVENTS_H -//--- -// Type definitions. -//--- - /* - enum EventType + event_type_t Something user programs will surely use most often. */ -enum EventType +typedef enum { EventType_None = 0, ET_None = EventType_None, @@ -28,27 +24,32 @@ enum EventType EventType_KeyPressed = 2, ET_KeyPress = EventType_KeyPressed, - EventType_KeyReleased = 3, + EventType_KeyRepeated = 3, + ET_KeyRepeat = EventType_KeyRepeated, + + EventType_KeyReleased = 4, ET_KeyRel = EventType_KeyReleased, -}; + +} event_type_t; /* - struct Event + event_t Wake up, something's going on. The union member that holds information about the event is implicitly defined by the type attribute. */ -struct Event +typedef struct { - enum EventType type; + event_type_t type; union { // For ET_User. void *data; - // For ET_KeyPress and ET_KeyRel. + // For ET_KeyPress, ET_KeyRepeat and ET_KeyRel. int key; }; -}; + +} event_t; @@ -64,21 +65,21 @@ struct Event allowed. Returns non-zero on error. */ -int event_push(struct Event event); +int event_push(event_t event); /* - getevent() + waitevent() Returns the next event. If no one is available, waits for something to happen. This function uses low-level sleep and should be preferred to active waiting using loops. */ -struct Event getevent(void); +event_t waitevent(void); /* pollevent() Returns the next event. If no one is available, returns an event whose type is ET_None. This function always returns immediately. */ -struct Event pollevent(void); +event_t pollevent(void); #endif // _EVENTS_H diff --git a/include/internals/events.h b/include/internals/events.h index 976e352..ac8b7ee 100644 --- a/include/internals/events.h +++ b/include/internals/events.h @@ -12,7 +12,7 @@ like any other queue. Trying to add an event when the queue is full fails, and the operation is ignored. */ -extern volatile struct Event event_queue[]; +extern volatile event_t event_queue[]; extern volatile int queue_start; extern volatile int queue_size; diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h index 2824893..10bd564 100644 --- a/include/internals/keyboard.h +++ b/include/internals/keyboard.h @@ -5,7 +5,7 @@ #include // Keyboard variables. -extern volatile unsigned char keyboard_state[10]; +extern volatile uint8_t keyboard_state[10]; extern volatile int interrupt_flag; // Key statistics. @@ -19,7 +19,7 @@ extern unsigned cb_id; getPressedKey() Finds a pressed key in the keyboard state and returns it. */ -int getPressedKey(volatile unsigned char *keyboard_state); +int getPressedKey(volatile uint8_t *keyboard_state); /* getPressedKeys() @@ -28,7 +28,13 @@ int getPressedKey(volatile unsigned char *keyboard_state); WARNING: keyboard artifacts make this function read as pressed keys that aren't (typically, LEFT + DOWN + SHIFT => ALPHA). */ -int getPressedKeys(volatile unsigned char *keyboard_state, int *keys, - int count); +int getPressedKeys(volatile uint8_t *keyboard_state, int *keys, int count); + +/* + keyboard_updateState() + Updates the keyboard state. +*/ +void keyboard_updateState_7705(volatile uint8_t *state); +void keyboard_updateState_7305(volatile uint8_t *state); #endif // _INTERNALS_KEYBOARD_H diff --git a/include/internals/timer.h b/include/internals/timer.h index f15abc1..19b4a4e 100644 --- a/include/internals/timer.h +++ b/include/internals/timer.h @@ -7,7 +7,8 @@ */ struct Timer { - void (*callback)(void); + void *callback; + void *data; int repeats; }; diff --git a/include/keyboard.h b/include/keyboard.h index 4bb2483..ea27483 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -13,6 +13,7 @@ #ifndef _KEYBOARD_H #define _KEYBOARD_H 1 +#include #include //--- @@ -232,7 +233,7 @@ int keylast(int *repeat_count); functions, but the buffer data is very volatile. Therefore, data written to the buffer could be replaced anytime. */ -volatile unsigned char *keystate(void); +volatile uint8_t *keystate(void); @@ -268,37 +269,4 @@ int keychar(int key); */ enum KeyType keytype(int key); - - -//--- -// Internal API. -// Reference here for documentation purposes only. Do not call. -//--- - -/* - keyboard_interrupt() - Notifies the keyboard module that an interrupt request has been issued, - and updates the keyboard state. -*/ -void keyboard_interrupt(void); - -/* - keyboard_updateState() - Updates the keyboard state. -*/ -void keyboard_updateState_7705(volatile unsigned char *state); -void keyboard_updateState_7305(volatile unsigned char *state); - -/* - keyboard_init() - Starts the keyboard timer. -*/ -void keyboard_init(void) __attribute__((constructor)); - -/* - keyboard_quit() - Stops the keyboard timer. -*/ -void keyboard_quit(void) __attribute__((destructor)); - #endif // _KEYBOARD_H diff --git a/include/timer.h b/include/timer.h index 1b5cb7d..e231cf8 100644 --- a/include/timer.h +++ b/include/timer.h @@ -48,17 +48,22 @@ not running the gray engine. Unit names are defined in the clock.h header and must be one of the following: - - Clock_us (microseconds) - - Clock_ms (milliseconds) - - Clock_s (seconds) - - Clock_Hz (hertz) - - Clock_kHz (kilohertz) - - Clock_MHz (megahertz) + - Clock_us (microseconds) + - Clock_ms (milliseconds) + - Clock_s (seconds) + - Clock_Hz (hertz) + - Clock_kHz (kilohertz) + - Clock_MHz (megahertz) The number of repeats may to set to 0. In this case, the timer will not stop until timer_stop() is explicitly called. + The callback is expected to be a function of the following type: + - void callback(void) if data == NULL + - void callback(void *data) if data is non-NULL + In the latter case, the data pointer will be passed as argument to the + callback function. */ void timer_start(int timer, int delay_or_frequency, enum ClockUnit unit, - void (*callback)(void), int repeats); + void *callback, void *data, int repeats); /* timer_start2() @@ -71,8 +76,8 @@ void timer_start(int timer, int delay_or_frequency, enum ClockUnit unit, - TIMER_Po_256 - TIMER_TCLK */ -void timer_start2(int timer, int delay, int prescaler, void (*callback)(void), - int repeats); +void timer_start2(int timer, int delay, int prescaler, void *callback, + void *data, int repeats); /* timer_stop() diff --git a/src/clock/clock.c b/src/clock/clock.c index a88a216..06426eb 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -4,6 +4,7 @@ #include #include #include +#include static clock_config_t conf = { .FLL = -1, .PLL = -1, @@ -21,27 +22,36 @@ static clock_config_t conf = { int clock_setting(int duration, enum ClockUnit unit) { if(conf.Pphi_f <= 0) return -1; - int f = conf.Pphi_f >> 2; + uint64_t f = conf.Pphi_f >> 2; + uint64_t result; switch(unit) { case Clock_us: - return (duration * f) / 1000000; + result = (duration * f) / 1000000; + break; case Clock_ms: - return (duration * f) / 1000; + result = (duration * f) / 1000; + break; case Clock_s: - return (duration * f); + result = (duration * f); + break; case Clock_Hz: - return f / duration; + result = f / duration; + break; case Clock_kHz: - return f / (duration * 1000); + result = f / (duration * 1000); + break; case Clock_MHz: - return f / (duration * 1000000); + result = f / (duration * 1000000); + break; default: return -1; } + + return (result > 0xffffffff) ? (0xffffffff) : (result); } /* @@ -60,7 +70,7 @@ clock_config_t clock_config(void) void sleep(void) { __asm__( - "sleep\n\t" + "sleep" ); } @@ -71,10 +81,9 @@ void sleep(void) than requested. */ -static volatile int sleep_done = 0; -static void sleep_callback(void) +static void sleep_callback(void *flag) { - sleep_done = 1; + *((int *)flag) = 1; } void sleep_ms(int ms_delay) @@ -83,8 +92,10 @@ void sleep_ms(int ms_delay) } void sleep_us(int us_delay) { - sleep_done = 0; - timer_start(TIMER_USER, us_delay, Clock_us, sleep_callback, 1); + volatile int sleep_done = 0; + timer_start(TIMER_USER, us_delay, Clock_us, sleep_callback, + (void *)&sleep_done, 1); + do sleep(); while(!sleep_done); } @@ -133,6 +144,7 @@ void clock_measure(void) tmu->TCR.CKEG = 0; timers[TIMER_USER].callback = NULL; + timers[TIMER_USER].data = NULL; timers[TIMER_USER].repeats = 0; cb_id_7705 = rtc_cb_add(RTCFreq_256Hz, clock_measure_7705, 0); diff --git a/src/events/event_get.c b/src/events/event_get.c index eceb1ff..5b2e707 100644 --- a/src/events/event_get.c +++ b/src/events/event_get.c @@ -7,9 +7,9 @@ Returns the next event. If no one is available, returns an event whose type is ET_None. This function always returns immediately. */ -struct Event pollevent(void) +event_t pollevent(void) { - struct Event event = { + event_t event = { .type = ET_None }; if(queue_size <= 0) return event; @@ -24,14 +24,14 @@ struct Event pollevent(void) } /* - getevent() + waitevent() Returns the next event. If no one is available, waits for something to happen. This function uses low-level sleep and should be preferred to active waiting using loops. */ -struct Event getevent(void) +event_t waitevent(void) { - struct Event event; + event_t event; while((event = pollevent()).type == ET_None) sleep(); return event; diff --git a/src/events/event_push.c b/src/events/event_push.c index 3c47d09..0e39b2c 100644 --- a/src/events/event_push.c +++ b/src/events/event_push.c @@ -1,7 +1,7 @@ #include #include -volatile struct Event event_queue[EVENTS_QUEUE_SIZE]; +volatile event_t event_queue[EVENTS_QUEUE_SIZE]; volatile int queue_start = 0; volatile int queue_size = 0; @@ -11,7 +11,7 @@ volatile int queue_size = 0; or pollevent() later. Pushing ET_None events is not allowed. Returns non-zero on error. */ -int event_push(struct Event event) +int event_push(event_t event) { if(queue_size >= EVENTS_QUEUE_SIZE) return 1; if(event.type == ET_None) return 2; diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index 9c1071f..fab319e 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -68,7 +68,8 @@ void gray_start(void) { if(runs) return; - timer_start2(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt, 0); + timer_start2(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt, + NULL, 0); current &= 1; runs = 1; } diff --git a/src/keyboard/getPressedKey.c b/src/keyboard/getPressedKey.c index ca8ba32..f7d7d06 100644 --- a/src/keyboard/getPressedKey.c +++ b/src/keyboard/getPressedKey.c @@ -4,7 +4,7 @@ getPressedKey() Finds a pressed key in the keyboard state and returns it. */ -int getPressedKey(volatile unsigned char *keyboard_state) +int getPressedKey(volatile uint8_t *keyboard_state) { int row = 1, column = 0; int state; diff --git a/src/keyboard/getPressedKeys.c b/src/keyboard/getPressedKeys.c index b6dd739..c3b21f1 100644 --- a/src/keyboard/getPressedKeys.c +++ b/src/keyboard/getPressedKeys.c @@ -5,8 +5,7 @@ Find 'count' pressed keys in the keyboard state and fills the 'keys' array. Returns the number of actually-pressed keys found. */ -int getPressedKeys(volatile unsigned char *keyboard_state, int *keys, - int count) +int getPressedKeys(volatile uint8_t *keyboard_state, int *keys, int count) { int row = 1, column; int found = 0, actually_pressed; diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c index 89e2bc5..1cbfcad 100644 --- a/src/keyboard/getkey.c +++ b/src/keyboard/getkey.c @@ -25,7 +25,7 @@ int getkey(void) If max_cycles is non-zero and positive, getkey_opt() will return KEY_NOEVENT if no event occurs during max_cycle analysis. */ -void getkey_opt_wait(int *cycles) +static void getkey_opt_wait(int *cycles) { while(!interrupt_flag) sleep(); interrupt_flag = 0; @@ -34,7 +34,7 @@ void getkey_opt_wait(int *cycles) } int getkey_opt(enum GetkeyOpt options, int cycles) { - struct Event event; + event_t event; int modifier = 0; static int event_ref = 0; @@ -120,19 +120,3 @@ int getkey_opt(enum GetkeyOpt options, int cycles) event_ref = 0; return KEY_NONE; } - -/* -int getkey_opt(enum GetkeyOpt options, int max_cycles) -{ - while(max_cycles != 0) - { - // Handling "new key" events. - - // Handling key repetitions. - - } - - // When no key was pressed during the given delay... - return KEY_NOEVENT; -} -*/ diff --git a/src/keyboard/keyboard_interrupt.c b/src/keyboard/keyboard_interrupt.c index e98dc51..9cf854a 100644 --- a/src/keyboard/keyboard_interrupt.c +++ b/src/keyboard/keyboard_interrupt.c @@ -10,7 +10,7 @@ //--- // These ones get modified by interrupts. -volatile unsigned char keyboard_state[10] = { 0 }; +volatile uint8_t keyboard_state[10] = { 0 }; volatile int interrupt_flag = 0; // Key statistics. @@ -28,16 +28,25 @@ unsigned cb_id; static void push_press(int keycode) { - struct Event event = { + event_t event = { .type = ET_KeyPress, .key = keycode, }; event_push(event); } +static void push_repeat(int keycode) +{ + event_t event = { + .type = ET_KeyRepeat, + .key = keycode, + }; + event_push(event); +} + static void push_release(int keycode) { - struct Event event = { + event_t event = { .type = ET_KeyRel, .key = keycode, }; @@ -51,7 +60,7 @@ static void push_release(int keycode) */ void keyboard_interrupt(void) { - unsigned char state[10] = { 0 }; + uint8_t state[10] = { 0 }; isSH3() ? keyboard_updateState_7705(state) : keyboard_updateState_7305(state) @@ -61,8 +70,8 @@ void keyboard_interrupt(void) // AC/ON. if(keyboard_state[0] ^ state[0]) { - unsigned char pressed = ~keyboard_state[0] & state[0]; - unsigned char released = keyboard_state[0] & ~state[0]; + uint8_t pressed = ~keyboard_state[0] & state[0]; + uint8_t released = keyboard_state[0] & ~state[0]; if(pressed & 1) push_press(KEY_AC_ON); if(released & 1) push_release(KEY_AC_ON); @@ -71,19 +80,22 @@ void keyboard_interrupt(void) for(int row = 1; row <= 9; row++) { - unsigned char pressed = ~keyboard_state[row] & state[row]; - unsigned char released = keyboard_state[row] & ~state[row]; + uint8_t pressed = ~keyboard_state[row] & state[row]; + uint8_t repeated = keyboard_state[row] & state[row]; + uint8_t released = keyboard_state[row] & ~state[row]; keyboard_state[row] = state[row]; // Fasten this a bit. - if(!pressed && !released) continue; + if(!(pressed | repeated | released)) continue; for(int column = 0; column < 8; column++) { - if(pressed & 1) push_press ((column << 4) | row); + if(pressed & 1) push_press ((column << 4) | row); + if(repeated & 1) push_repeat ((column << 4) | row); if(released & 1) push_release((column << 4) | row); - pressed >>= 1; + pressed >>= 1; + repeated >>= 1; released >>= 1; } } @@ -95,7 +107,7 @@ void keyboard_interrupt(void) keyboard_init() Starts the keyboard timer. */ -void keyboard_init(void) +__attribute__((constructor)) void keyboard_init(void) { cb_id = rtc_cb_add(RTCFreq_16Hz, keyboard_interrupt, 0); } @@ -114,7 +126,7 @@ void keyboard_setFrequency(enum KeyboardFrequency frequency) keyboard_quit() Stops the keyboard timer. */ -void keyboard_quit(void) +__attribute__((destructor)) void keyboard_quit(void) { rtc_cb_end(cb_id); } diff --git a/src/keyboard/keyboard_misc.c b/src/keyboard/keyboard_misc.c index abdf59c..6248a3d 100644 --- a/src/keyboard/keyboard_misc.c +++ b/src/keyboard/keyboard_misc.c @@ -19,7 +19,7 @@ int keylast(int *repeat_count) Returns the address of the keyboard state array. The returned address is the handler's buffer, therefore it contains volatile data. */ -volatile unsigned char *keystate(void) +volatile uint8_t *keystate(void) { return keyboard_state; } diff --git a/src/keyboard/keyboard_sh7305.c b/src/keyboard/keyboard_sh7305.c index ab515ed..8eb0bca 100644 --- a/src/keyboard/keyboard_sh7305.c +++ b/src/keyboard/keyboard_sh7305.c @@ -136,7 +136,7 @@ static int krow(int row) keyboard_updateState() Updates the keyboard state. */ -void keyboard_updateState_7305(volatile unsigned char *keyboard_state) +void keyboard_updateState_7305(volatile uint8_t *keyboard_state) { for(int i = 0; i < 10; i++) keyboard_state[i] = krow(i); } diff --git a/src/keyboard/keyboard_sh7705.c b/src/keyboard/keyboard_sh7705.c index 1ab1800..20cd32c 100644 --- a/src/keyboard/keyboard_sh7705.c +++ b/src/keyboard/keyboard_sh7705.c @@ -129,7 +129,7 @@ static int krow(int row) keyboard_updateState() Updates the keyboard state. */ -void keyboard_updateState_7705(volatile unsigned char *keyboard_state) +void keyboard_updateState_7705(volatile uint8_t *keyboard_state) { for(int i = 0; i < 10; i++) keyboard_state[i] = krow(i); } diff --git a/src/keyboard/multigetkey.c b/src/keyboard/multigetkey.c index dbe8a44..b1bc5e5 100644 --- a/src/keyboard/multigetkey.c +++ b/src/keyboard/multigetkey.c @@ -1,5 +1,6 @@ #include #include +#include /* multigetkey() @@ -12,23 +13,28 @@ Setting count = 3 is generally safe. The function returns after 'max_cycles' if no key is pressed. */ -void multigetkey(int *keys, int count, int max_cycles) +static void multigetkey_wait(int *cycles) { - int number; + while(!interrupt_flag) sleep(); + interrupt_flag = 0; - if(!max_cycles) max_cycles = -1; + if(*cycles > 0) (*cycles)--; +} +void multigetkey(int *keys, int count, int cycles) +{ + event_t event; + int number = 0; - while(max_cycles != 0) + if(count <= 0) return; + if(cycles <= 0) cycles = -1; + + while(cycles != 0) { - while(!interrupt_flag) sleep(); - interrupt_flag = 0; - if(max_cycles > 0) max_cycles--; - number = getPressedKeys(keyboard_state, keys, count); - // We need to update the last key data, in case multigetkey() - // returns a single key, and getkey() is called a short time - // after. Otherwise getkey() could re-send an event for this + // We want to update the last key data when multigetkey() + // returns a single key, because getkey() could be called a + // short time after we return, and send a new event for this // key. if(number == 1) { @@ -37,10 +43,12 @@ void multigetkey(int *keys, int count, int max_cycles) last_events = 0; } - if(number) return; + if(number) break; + multigetkey_wait(&cycles); } - // When no key was pressed during the given delay... (no need to fill - // the array, it has already been done by getPressedKeys()). + do event = pollevent(); + while(event.type != ET_None); + return; } diff --git a/src/timer/timer_interrupt.c b/src/timer/timer_interrupt.c index 85f4427..5b183f4 100644 --- a/src/timer/timer_interrupt.c +++ b/src/timer/timer_interrupt.c @@ -3,7 +3,11 @@ #include -struct Timer timers[3] = { { NULL, 0 }, { NULL, 0 }, { NULL, 0 } }; +struct Timer timers[3] = { + { .callback = NULL, .data = NULL, .repeats = 0 }, + { .callback = NULL, .data = NULL, .repeats = 0 }, + { .callback = NULL, .data = NULL, .repeats = 0 }, +}; /* timer_interrupt() @@ -15,7 +19,20 @@ void timer_interrupt(int timer) timer_get(timer, &tmu, NULL); tmu->TCR.UNF = 0; - if(timers[timer].callback) timers[timer].callback(); + + if(timers[timer].callback) + { + if(timers[timer].data) + { + void (*fun)(void *data) = timers[timer].callback; + fun(timers[timer].data); + } + else + { + void (*fun)(void) = timers[timer].callback; + fun(); + } + } // Reducing the number of repetitions left, if not infinite. if(!timers[timer].repeats) return; diff --git a/src/timer/timer_start.c b/src/timer/timer_start.c index 7e7c9d6..0792702 100644 --- a/src/timer/timer_start.c +++ b/src/timer/timer_start.c @@ -5,8 +5,8 @@ timer_start2() Configures and starts a time using a clock count and a prescaler. */ -void timer_start2(int timer, int delay, int prescaler, void (*callback)(void), - int repeats) +void timer_start2(int timer, int delay, int prescaler, void *callback, + void *data, int repeats) { volatile struct mod_tmu *tmu; volatile unsigned char *tstr; @@ -27,8 +27,9 @@ void timer_start2(int timer, int delay, int prescaler, void (*callback)(void), tmu->TCR.CKEG = 0; // Loading the structure information. - timers[timer].callback = callback; - timers[timer].repeats = repeats; + timers[timer].callback = callback; + timers[timer].data = data; + timers[timer].repeats = repeats; // Starting the timer. *tstr |= byte; @@ -39,9 +40,9 @@ void timer_start2(int timer, int delay, int prescaler, void (*callback)(void), Configures and starts a timer using a delay, or a frequency, and the associated unit. */ -void timer_start(int timer, int delay, enum ClockUnit unit, - void (*callback)(void), int repeats) +void timer_start(int timer, int delay, enum ClockUnit unit, void *callback, + void *data, int repeats) { timer_start2(timer, clock_setting(delay, unit), TIMER_Po_4, callback, - repeats); + data, repeats); } From 39fedcd271a62de4b2fcab9c88f4bda380888669 Mon Sep 17 00:00:00 2001 From: lephe Date: Wed, 1 Mar 2017 11:07:28 +0100 Subject: [PATCH 06/24] Redesigned timer module: got a possibly infinite amount of virtual timers. --- Makefile | 6 +- TODO | 4 +- configure | 16 +-- demo/gintdemo.c | 2 - demo/test_keyboard.c | 2 +- demo/test_rtc.c | 18 +-- demo/test_timer.c | 9 +- include/clock.h | 16 ++- include/events.h | 27 +++-- include/internals/modules.h | 12 ++ include/internals/rtc.h | 15 ++- include/internals/timer.h | 95 +++++++++------ include/modules/macros.h | 48 ++++++++ include/modules/timer.h | 64 +++++++++++ include/rtc.h | 61 ++++------ include/timer.h | 217 +++++++++++++++++++++-------------- src/clock/clock.c | 82 ++++++------- src/core/crt0.c | 10 +- src/core/init_quit.c | 5 - src/core/interrupts.c | 6 +- src/core/modules.c | 49 ++++++++ src/gray/gray_engine.c | 15 +-- src/rtc/rtc_callback.c | 8 +- src/rtc/rtc_getTime.c | 24 ++-- src/rtc/rtc_interrupt.c | 6 +- src/rtc/rtc_setTime.c | 23 ++-- src/time/time.c | 2 +- src/timer/common_api.c | 112 ++++++++++++++++++ src/timer/hardware_timers.c | 63 ++++++++++ src/timer/timer_get.c | 25 ---- src/timer/timer_interrupt.c | 41 ------- src/timer/timer_reload.c | 32 ------ src/timer/timer_start.c | 48 -------- src/timer/timer_stop.c | 18 --- src/timer/virtual_timers.c | 223 ++++++++++++++++++++++++++++++++++++ 35 files changed, 950 insertions(+), 454 deletions(-) create mode 100644 include/internals/modules.h create mode 100644 include/modules/macros.h create mode 100644 include/modules/timer.h create mode 100644 src/core/modules.c create mode 100644 src/timer/common_api.c create mode 100644 src/timer/hardware_timers.c delete mode 100644 src/timer/timer_get.c delete mode 100644 src/timer/timer_interrupt.c delete mode 100644 src/timer/timer_reload.c delete mode 100644 src/timer/timer_start.c delete mode 100644 src/timer/timer_stop.c create mode 100644 src/timer/virtual_timers.c diff --git a/Makefile b/Makefile index 907b636..9e84bf3 100755 --- a/Makefile +++ b/Makefile @@ -99,7 +99,7 @@ obj-std = $(foreach mod,$(modules-libc),$(mod-$(mod)-obj)) $(obj-std-spec) obj-lib = $(foreach mod,$(modules-gint),$(mod-$(mod)-obj)) $(obj-lib-spec) # Dependencies. -hdr-dep = $(wildcard include/*.h include/internals/*.h) +hdr-dep = $(wildcard include/*.h include/*/*.h) @@ -249,9 +249,9 @@ ifdef config_ext endif @ printf '\e[32;1mmsg \u00bb\e[0m All installed!\n' -install-demo: +install-demo: all p7 send -f $(target-g1a) -install-debug: +install-debug: all p7 send -f $(target-dbg) .PHONY: all-lib all help diff --git a/TODO b/TODO index 5be8293..45638bd 100644 --- a/TODO +++ b/TODO @@ -12,7 +12,8 @@ Simple improvements: - string: Use cmp/str to implement memchr() (assembler examples) - string: Do some tests for memcmp() - core: Register more interrupts (and understand their parameters) -- rtc: Take care of carry when reading time +- project: Clean headers that have some internal definitions +- project: Check size of all library structures Larger improvements: - errno: Introduce errno and use it more or less everywhere - bopti: Monochrome bitmaps blending modes @@ -24,6 +25,7 @@ Larger improvements: - usb: Implement a driver - esper: Cleaner playback, synthetizing - clock: Handle overclocking (relaunch clocks when overclocking) +- project: Unify this hellish mess of register access! Things to investigate: - Packed bit fields alignment diff --git a/configure b/configure index 5bd34db..5ff00f3 100755 --- a/configure +++ b/configure @@ -13,7 +13,7 @@ conf[GINT_EXTENDED_LIBC]= # Size limits conf[ATEXIT_MAX]=16 -conf[RTC_CB_ARRAY_SIZE]=5 +conf[TIMER_SLOTS]=16 conf[EVENTS_QUEUE_SIZE]=64 # Output files @@ -50,8 +50,8 @@ Options that affect the behavior of the library: Options that customize size limits: $Cr--atexit-max$C0=$Cy$Cg [default:$Cp 16$Cg]$C0 Number of exit handlers that can be registered by atexit(). - $Cr--rtc-callbacks$C0=$Cy$Cg [default:$Cp 5$Cg]$C0 - Number of RTC callbacks that can be registered. + $Cr--timer-slots$C0=$Cy$Cg [default:$Cp 16$Cg]$C0 + Number of virtual timers that may be registered at the same time. $Cr--events-queue-size$C0=$Cy$Cg [default:$Cp 64$Cg]$C0 Number of events simultaneously stored in the event queue. EOF @@ -76,11 +76,11 @@ for arg; do case "$arg" in conf[ATEXIT_MAX]=$size else echo -e "$error --atexit-max expects an integer value" fail=true; fi;; - --rtc-callbacks=*) + --timer-slots=*) size=${arg#*=} if [[ $size == +([0-9]) ]]; then - conf[RTC_CB_ARRAY_SIZE]=$size - else echo -e "$error --rtc-callbacks expects an integer value" + conf[TIMER_SLOTS]=$size + else echo -e "$error --timer-slots expects an integer value" fail=true; fi;; --events-queue-size=*) size=${arg#*=} @@ -90,7 +90,7 @@ for arg; do case "$arg" in "value" fail=true; fi;; - --atexit-max | --rtc-callbacks | --events-queue-size) + --atexit-max | --timer-slots | --events-queue-size) echo -e "$error syntax for $arg is $arg=";; *) @@ -108,7 +108,7 @@ output_config_gcc() [ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "-D GINT_EXTENDED_LIBC" echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}" - echo "-D RTC_CB_ARRAY_SIZE=${conf[RTC_CB_ARRAY_SIZE]}" + echo "-D TIMER_SLOTS=${conf[TIMER_SLOTS]}" echo "-D EVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}" } diff --git a/demo/gintdemo.c b/demo/gintdemo.c index a65dad6..bbd1785 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -521,7 +521,6 @@ int main(void) return 0; } -/* void crash(void) { __asm__( @@ -530,4 +529,3 @@ void crash(void) "trapa #1 " ); } -*/ diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c index 0ab410d..87e3ead 100644 --- a/demo/test_keyboard.c +++ b/demo/test_keyboard.c @@ -102,7 +102,7 @@ static void draw_events(enhanced_event_t *history, int size) "0", ".", "\x08", "(-)", "EXE", NULL }; const char *event_names[] = { - "None ", "User ", "Press", "Rept.", "Rel. " + "None ", "User ", "Press", "Rept.", "Rel. ", "Timer" }; for(int i = 0; i < size && history[i].type != ET_None; i++) diff --git a/demo/test_rtc.c b/demo/test_rtc.c index 2264f00..053e218 100644 --- a/demo/test_rtc.c +++ b/demo/test_rtc.c @@ -16,7 +16,7 @@ #include #include -static void draw(struct RTCTime time) +static void draw(rtc_time_t time) { extern Image res_rtc_segments; @@ -53,8 +53,7 @@ static void draw(struct RTCTime time) static void callback(void) { extern Image res_opt_rtc; - struct RTCTime time = rtc_getTime(); - time.year += 1900; + rtc_time_t time = rtc_getTime(); dclear(); draw(time); @@ -62,7 +61,7 @@ static void callback(void) dupdate(); } -static void set_region(struct RTCTime *time, int region, int value) +static void set_region(rtc_time_t *time, int region, int value) { switch(region) { @@ -129,12 +128,10 @@ static void set(void) { 72, 39, 7, 9 }, { 84, 39, 7, 9 }, { 90, 39, 7, 9 }, { 96, 39, 7, 9 }, { 102, 39, 7, 9 }, }; - struct RTCTime time = rtc_getTime(); + rtc_time_t time = rtc_getTime(); int region_count = 14; int n = 0, slide = 0, key, leave; - time.year += 1900; - while(1) { dclear(); @@ -157,11 +154,9 @@ static void set(void) else if(key == KEY_F1 || key == KEY_EXE) { - n++; slide = 0; - if(n == region_count) + if(++n == region_count) { - time.year -= 1900; rtc_setTime(time); return; } @@ -187,7 +182,7 @@ static void set(void) else if(n == 6 && (slide != 1 || k != 5)) { - int day = k + 4 * slide - 1; + int day = (k + 4 * slide - 1) % 7; set_region(&time, n, day); n++; slide = 0; @@ -221,7 +216,6 @@ static void set(void) n++; if(n == region_count) { - time.year -= 1900; rtc_setTime(time); return; } diff --git a/demo/test_timer.c b/demo/test_timer.c index 53e8bd0..a830677 100644 --- a/demo/test_timer.c +++ b/demo/test_timer.c @@ -22,6 +22,7 @@ static clock_config_t conf; static volatile int elapsed_timer = -1; static volatile int elapsed_rtc = -1; static int cb_id = -1; +static timer_t *htimer = NULL; static void timing_rtc(void) { @@ -35,7 +36,11 @@ static void timing_timer(void) static void timing_start(void) { - timer_start(TIMER_USER, 64, Clock_Hz, timing_timer, NULL, 0); + uint32_t delay = clock_setting(64, Clock_Hz); + htimer = htimer_setup(timer_user, delay, timer_Po_4, 0); + timer_attach(htimer, timing_timer, NULL); + timer_start(htimer); + rtc_cb_edit(cb_id, RTCFreq_64Hz, timing_rtc); elapsed_timer = 0; @@ -253,8 +258,8 @@ void test_timer(void) switch(getkey_opt(Getkey_NoOption, 1)) { case KEY_EXIT: - timer_stop(TIMER_USER); rtc_cb_end(cb_id); + timer_stop(htimer); return; case KEY_F1: diff --git a/include/clock.h b/include/clock.h index 65ed96b..00822e8 100644 --- a/include/clock.h +++ b/include/clock.h @@ -10,6 +10,8 @@ #ifndef _CLOCK_H #define _CLOCK_H +#include + //--- // Sleep functions. //--- @@ -21,12 +23,16 @@ void sleep(void); /* - sleep_ms(), sleep_us() - Sleeps for the given number of milliseconds / microseconds using the - TIMER_USER timer. The actual sleep time will always be slightly less - than requested. + sleep_ms() + Sleeps for the given number of milliseconds using a virtual timer. */ void sleep_ms(int ms_delay); + +/* + sleep_us() + Sleeps for the given number of microseconds using the hardware timer + timer_user. +*/ void sleep_us(int us_delay); @@ -83,7 +89,7 @@ typedef struct Normally you need not use this function when setting up timers because timer_start() handles this conversion for you. */ -int clock_setting(int duration, enum ClockUnit unit); +uint32_t clock_setting(int duration, enum ClockUnit unit); /* clock_config() diff --git a/include/events.h b/include/events.h index b1832a4..8c1f285 100644 --- a/include/events.h +++ b/include/events.h @@ -9,26 +9,31 @@ #ifndef _EVENTS_H #define _EVENTS_H +#include + /* event_type_t Something user programs will surely use most often. */ typedef enum { - EventType_None = 0, - ET_None = EventType_None, + EventType_None = 0, + ET_None = EventType_None, - EventType_User = 1, - ET_User = EventType_User, + EventType_User = 1, + ET_User = EventType_User, - EventType_KeyPressed = 2, - ET_KeyPress = EventType_KeyPressed, + EventType_KeyPressed = 2, + ET_KeyPress = EventType_KeyPressed, - EventType_KeyRepeated = 3, - ET_KeyRepeat = EventType_KeyRepeated, + EventType_KeyRepeated = 3, + ET_KeyRepeat = EventType_KeyRepeated, - EventType_KeyReleased = 4, - ET_KeyRel = EventType_KeyReleased, + EventType_KeyReleased = 4, + ET_KeyRel = EventType_KeyReleased, + + EventType_TimerUnderflow = 5, + ET_Timer = EventType_TimerUnderflow, } event_type_t; @@ -47,6 +52,8 @@ typedef struct void *data; // For ET_KeyPress, ET_KeyRepeat and ET_KeyRel. int key; + // For ET_Timer. + timer_t *timer; }; } event_t; diff --git a/include/internals/modules.h b/include/internals/modules.h new file mode 100644 index 0000000..c96e770 --- /dev/null +++ b/include/internals/modules.h @@ -0,0 +1,12 @@ +#ifndef _INTERNALS_MODULES_H +#define _INTERNALS_MODULES_H + +/* + mod_init() + Initializes the module data to make register access cross-platform. The + MPU needs to have been detected or this function will yield wrong + results. +*/ +void mod_init(void); + +#endif // _INTERNALS_MODULES_H diff --git a/include/internals/rtc.h b/include/internals/rtc.h index 228ce59..4f3e348 100644 --- a/include/internals/rtc.h +++ b/include/internals/rtc.h @@ -14,7 +14,7 @@ */ struct rtc_cb { - enum RTCFrequency freq; + rtc_frequency_t freq; int id; void (*callback)(void); @@ -24,6 +24,19 @@ struct rtc_cb // The callback array. struct rtc_cb cb_array[RTC_CB_ARRAY_SIZE]; +/* + rtc_perodic_interrupt() + Handles periodic interrupts and calls the callbacks. +*/ +void rtc_periodic_interrupt(void); + +/* + rtc_cb_interrupt() + Calls the RTC callbacks if necessary, and updates the repeat counts. + Should only be called when RTC periodic interrupts occur. +*/ +void rtc_cb_interrupt(void); + /* struct mod_rtc This structure describes the arrangement of RTC register in the memory. diff --git a/include/internals/timer.h b/include/internals/timer.h index 19b4a4e..c169a6e 100644 --- a/include/internals/timer.h +++ b/include/internals/timer.h @@ -1,49 +1,76 @@ #ifndef _INTERNALS_TIMER_H -#define _INTERNALS_TIMER_H 1 +#define _INTERNALS_TIMER_H + +#include +#include /* - struct Timer - This structure holds information for a running timer. + timer_t + A virtual or hardware timer. We need to declare the struct timer_t name + so that we can forward-reference it. */ -struct Timer +typedef struct timer_t { + // Current delay, how much time elapsed since last interrupt occurred, + // and how many repeats are left. + int ms_delay; + int ms_elapsed; + int repeats_left; + + // Is the virtual slot free? Is the virtual timer active? + uint8_t used :1; + uint8_t active :1; + // Is this a virtual timer? Is this the virtual timer support? + uint8_t virtual :1; + uint8_t vsupport :1; + // How many events do I have received but not executed? + uint8_t events :4; + + // Callback function (NULL for event-firing timers) and its argument. void *callback; - void *data; - int repeats; -}; + void *argument; -extern struct Timer timers[3]; +} timer_t; + +// Hardware timers. +extern timer_t htimers[3]; +// Virtual timers. +extern timer_t vtimers[TIMER_SLOTS]; /* - struct mod_tmu - This structure holds information about the timer unit (peripheral - module) registers. + timer_interrupt() + Handles the interrupt for the given timer channel. */ -struct mod_tmu -{ - unsigned int TCOR; // Timer constant register. - unsigned int TCNT; // Timer counter. - - union - { - unsigned short WORD; - struct - { - unsigned :7; - unsigned UNF :1; // Underflow flag. - unsigned :2; - unsigned UNIE :1; // Underflow interrupt enable. - unsigned CKEG :2; // Clock edge (SH7705 only). - unsigned TPSC :3; // Timer prescaler. - }; - } TCR; // Timer control register. -}; +void timer_interrupt(int channel); /* - timer_get() - Returns the timer and TSTR register addresses. + timer_callback_event() + Executes the callback of a timer, or pushes a new timer event depending + on the timer configuration. Also reduces the amount of repeats left and + clears the active flag (or stops the hardware timer) if this number + falls from one to zero. */ -void timer_get(int timer, volatile struct mod_tmu **tmu, - volatile unsigned char **tstr); +void timer_callback_event(timer_t *timer); + +/* + vtimer_interrupt() + Interrupt handling subsystem for the virtual timers. +*/ +void vtimer_interrupt(void); + +/* + vtimer_updateOne() + Update the virtual timer hardware support timer, knowing that a virtual + timer with the given delay has been started. +*/ +void vtimer_updateOne(int additional_delay_ms); + +/* + vtimer_updateAll() + Updates the virtual timer hardware support after computing the GCD of + all virtual timers delays. This is rather long so avoid calling this + when possible. +*/ +void vtimer_updateAll(void); #endif // _INTERNALS_TIMER_H diff --git a/include/modules/macros.h b/include/modules/macros.h new file mode 100644 index 0000000..5f4403b --- /dev/null +++ b/include/modules/macros.h @@ -0,0 +1,48 @@ +#ifndef _MODULES_MACROS_H +#define _MODULES_MACROS_H + +#include + +// Fixed-width types for bit field are totally meaningless. +typedef unsigned uint; + +/* + byte_union() + Union between an uint8_t 'byte' attribute and a provided bit-field + structure that describe the contents of the byte. +*/ +#define byte_union(name, fields) \ + union \ + { \ + uint8_t byte; \ + struct { fields } \ + __attribute__((packed)); \ + } __attribute__((packed)) name + +/* + word_union() + Union between an uint16_t 'word' attribute and a provided bit-field + structure that describe the contents of the word. +*/ +#define word_union(name, fields) \ + union \ + { \ + uint16_t word; \ + struct { fields } \ + __attribute__((packed)); \ + } __attribute__((packed)) name + +/* + lword_union() + Union between an uint32_t 'lword' attribute and a provided bit-field + structure that describe the contents of the longword. +*/ +#define lword_union(name, fields) \ + union \ + { \ + uint32_t lword; \ + struct { fields } \ + __attribute__((packed)); \ + } __attribute__((packed)) name + +#endif // _MODULES_MACROS_H diff --git a/include/modules/timer.h b/include/modules/timer.h new file mode 100644 index 0000000..3a8b187 --- /dev/null +++ b/include/modules/timer.h @@ -0,0 +1,64 @@ +#ifndef _MODULES_TIMER_H +#define _MODULES_TIMER_H + +#include +#include + +/* + mod_tmu_timer_t + Registers of a single timer. +*/ +typedef struct +{ + uint32_t TCOR; /* Timer constant register */ + uint32_t TCNT; /* Timer counter */ + + word_union(TCR, + uint :7; + uint UNF :1; /* Underflow flag */ + uint :2; + uint UNIE :1; /* Underflow interrupt enable */ + uint CKEG :2; /* Clock edge (SH7705) */ + uint TPSC :3; /* Timer prescaler */ + ); + + uint16_t :16; + +} __attribute__((packed, aligned(4))) mod_tmu_timer_t; + +/* + mod_tmu_tstr_t + The timer start register. +*/ +typedef struct +{ + byte_union(, + uint :5; + uint STR2 :1; /* Counter start 2 */ + uint STR1 :1; /* Counter start 1 */ + uint STR0 :1; /* Counter start 0 */ + ); + +} __attribute__((packed)) mod_tmu_tstr_t; + +/* + mod_tmu_t (resides into gint memory) + Basically three timer units and an additional register to control who's + running. +*/ +typedef struct +{ + /* Timer configurations */ + volatile mod_tmu_timer_t *timers[3]; + /* Timer start register */ + volatile mod_tmu_tstr_t *TSTR; + /* Timer input capture 2 (SH7705) */ + volatile const uint32_t *TCPR2; + +} mod_tmu_t; + +// The actual thing is there. It's initialized by gint at startup and contains +// addresses and information suitable for the current platform. +extern volatile mod_tmu_t TMU; + +#endif // _MODULES_TIMER diff --git a/include/rtc.h b/include/rtc.h index 48f72e9..e5ca263 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -9,38 +9,41 @@ #ifndef _RTC_H #define _RTC_H 1 +#include + //--- // Time access. //--- /* - struct RTCTime + rtc_time_t Defines a point in time. */ -struct RTCTime +typedef struct { - int seconds; // Seconds in range 0-59 - int minutes; // Minutes in range 0-59 - int hours; // Hours in range 0-23 - int month_day; // Day of month in range 1-31 - int month; // Month in range 0-11 - int year; // Years (full value) - int week_day; // Day of week in range 0(Sunday)-6(Saturday). -}; + uint8_t seconds; // Seconds in range 0-59 + uint8_t minutes; // Minutes in range 0-59 + uint8_t hours; // Hours in range 0-23 + uint8_t month_day; // Day of month in range 1-31 + uint8_t month; // Month in range 0-11 + uint8_t week_day; // Day of week in range 0(Sunday)-6(Saturday). + uint16_t year; // Years (around 2000) + +} rtc_time_t; /* rtc_getTime() Reads the current time from the RTC. There is no guarantee that the week day is correct (use the time API for that). */ -struct RTCTime rtc_getTime(void); +rtc_time_t rtc_getTime(void); /* rtc_setTime() Sets the time in the RTC registers. The week day is set to 0 if greater than 6. Other fields are not checked. */ -void rtc_setTime(struct RTCTime time); +void rtc_setTime(rtc_time_t time); @@ -49,11 +52,11 @@ void rtc_setTime(struct RTCTime time); //--- /* - enum RTCFrequency + rtc_frequency_t Describes the possible frequencies available for the real-time clock interrupt. */ -enum RTCFrequency +typedef enum { RTCFreq_500mHz = 7, RTCFreq_1Hz = 6, @@ -63,7 +66,8 @@ enum RTCFrequency RTCFreq_64Hz = 2, RTCFreq_256Hz = 1, RTCFreq_None = 0, -}; + +} rtc_frequency_t; /* rtc_cb_add() @@ -74,7 +78,7 @@ enum RTCFrequency The number of repeats may be set to 0, in which case the callback is called indefinitely unless the user calls rtc_cb_end(). */ -int rtc_cb_add(enum RTCFrequency freq, void (*function)(void), int repeats); +int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats); /* rtc_cb_end() @@ -93,29 +97,6 @@ void rtc_cb_end(int id); can set the function to NULL or the frequency to RTCFreq_None to temporarily disable the callback. */ -int rtc_cb_edit(int id, enum RTCFrequency new_freq, - void (*new_function)(void)); - - - -//--- -// Internal API. -// Referenced here for documentation purposes only. Do not call. -//--- - -/* - rtc_interrupt() - Handles an RTC interrupt by calling the callback. -*/ -void rtc_interrupt(void); -void rtc_interrupt_7705(void); -void rtc_interrupt_7305(void); - -/* - rtc_cb_interrupt() - Handles an RTC interrupt. Calls the RTC callbacks if necessary, and - updates the repeat counts. -*/ -void rtc_cb_interrupt(void); +int rtc_cb_edit(int id, rtc_frequency_t new_freq, void (*new_function)(void)); #endif // _RTC_H diff --git a/include/timer.h b/include/timer.h index e231cf8..3028e62 100644 --- a/include/timer.h +++ b/include/timer.h @@ -11,106 +11,157 @@ #define _TIMER_H 1 #include +#include +#include + +// The timer object is manipulated by the module; the user needs not access it +// directly. Its contents are defined in . +struct timer_t; +typedef struct timer_t timer_t; //--- -// Constants. +// Virtual timer API +// Gint allows users to create virtual timers with 1-ms precision to +// handle an important amount of timed events. The amount of virtual +// timers available is gapped by the TIMER_SLOTS parameter, but this +// parameter can be customized when building the library; thus, the +// amount of usable virtual timers is not limited. //--- -// Timer identifiers. -#define TIMER_0 0 -#define TIMER_TMU0 TIMER_0 -#define TIMER_1 1 -#define TIMER_TMU1 TIMER_1 -#define TIMER_2 2 -#define TIMER_TMU2 TIMER_2 -// Timer function identifiers. -#define TIMER_KEYBOARD TIMER_TMU0 -#define TIMER_GRAY TIMER_TMU1 -#define TIMER_USER TIMER_TMU2 +#ifndef TIMER_SLOTS +#define TIMER_SLOTS 16 +#endif -// Timer prescalers. -#define TIMER_Po_4 0 -#define TIMER_Po_16 1 -#define TIMER_Po_64 2 -#define TIMER_Po_256 3 -#define TIMER_TCLK 5 +/* + timer_create() + Basic timer configuration. This function creates a virtual timer and + configures its delay and repetition count. It returns a virtual timer + object that will be used by other functions. At most TIMER_SLOTS + virtual timers can be used at the same time: this function returns NULL + if there is no new slot available. + By default a virtual timer configured by timer_configure() will fire + ET_Timer events, which the user will need to catch. The user should + then execute some action. + There is another option: the user may call timer_attach() to attach a + callback to a virtual timer. A virtual timer which has a callback + attached will not fire any ET_Timer event and will call the callback + automatically instead. +*/ +timer_t *timer_create(int delay_ms, int repeats); + +/* + timer_destroy() + Destroys a virtual timer. This virtual timer pointer becomes invalid + and should not be used anymore. +*/ +void timer_destroy(timer_t *virtual_timer); //--- -// Public API. +// Hardware timer API +// Gint provides access to hardware timer. These timer offer more or less +// microsecond-level control. However, the user should ensure, when using +// hardware timers, that they are not overriding the configuration of +// timers that are already running -- gint won't check. //--- +/* + timer_hard_t + Available hardware timers. The user can use timer_user freely, but + timer_gray and timer_virtual should not be used as long as the gray + engine or virtual timers are running (respectively). +*/ +typedef enum +{ + timer_tmu0 = 0, + timer_virtual = timer_tmu0, + + timer_tmu1 = 1, + timer_gray = timer_tmu1, + + timer_tmu2 = 2, + timer_user = timer_tmu2, + +} timer_hard_t; + +/* + timer_input_t + Available input clocks for the hardware timer: + - Po/4 Peripheral clock (frequency divided by 4) + - Po/16 Peripheral clock (frequency divided by 16) + - Po/64 Peripheral clock (frequency divided by 64) + - Po/256 Peripheral clock (frequency divided by 256) + - TCLK External clock + I'm not totally sure there is any signal on the external clock, so + don't use it unless you know what you are doing. +*/ +typedef enum +{ + timer_Po_4 = 0, + timer_Po_16 = 1, + timer_Po_64 = 2, + timer_Po_256 = 3, + timer_tclk = 5, + +} timer_input_t; + +/* + htimer_setup() + Configures a hardware timer. By default hardware timers generates + ET_Timer events but catching them may take some time, especially if + there are other events waiting in the queue. For increased timing, and + for fast timers, the user should consider using callbacks instead. + Returns a hardware timer object. + Returns the correct timer structure if the requested timer is free and + NULL otherwise. +*/ +timer_t *htimer_setup(timer_hard_t id, uint32_t constant, timer_input_t input, + int repeats); + +/* + htimer_reload() + Reloads a hardware timer without starting or stopping it. +*/ +void htimer_reload(timer_hard_t id, uint32_t new_constant); + + + +//--- +// Common API +// The following functions work with both virtual and hardware timers. +//--- + +/* + timer_attach() + This function attaches a callback to a virtual or hardware timer. A + timer with a callback attached will stop firing ET_Timer events and + will call the callback function directly instead. + The type signature of the function should be as follows: + - void callback(void) if argument == NULL + - void callback(void *argument) if argument is non-NULL + In the latter case, the argument pointer will be passed to the callback + function. +*/ +void timer_attach(timer_t *timer, void *callback, void *argument); + /* timer_start() - Configures and starts a timer. The timer argument expects a timer name. - You can use TIMER_USER anytime. You may also use TIMER_GRAY if you're - not running the gray engine. - Unit names are defined in the clock.h header and must be one of the - following: - - Clock_us (microseconds) - - Clock_ms (milliseconds) - - Clock_s (seconds) - - Clock_Hz (hertz) - - Clock_kHz (kilohertz) - - Clock_MHz (megahertz) - The number of repeats may to set to 0. In this case, the timer will not - stop until timer_stop() is explicitly called. - The callback is expected to be a function of the following type: - - void callback(void) if data == NULL - - void callback(void *data) if data is non-NULL - In the latter case, the data pointer will be passed as argument to the - callback function. + Starts a virtual or hardware timer. If the timer has a callback + attached, then the callback function will start being called regularly; + otherwise, the timer will start pushing ET_Timer events to the event + queue. It is advised, not to change a timer's configuration while it's + running. */ -void timer_start(int timer, int delay_or_frequency, enum ClockUnit unit, - void *callback, void *data, int repeats); - -/* - timer_start2() - Basically the same as timer_start(), but uses a clock-count delay and a - prescaler. The possible values for the prescaler are dividers of the - peripheral clock frequency Po: - - TIMER_Po_4 - - TIMER_Po_16 - - TIMER_Po_64 - - TIMER_Po_256 - - TIMER_TCLK -*/ -void timer_start2(int timer, int delay, int prescaler, void *callback, - void *data, int repeats); +void timer_start(timer_t *timer); /* timer_stop() - Stops the given timer. This function may be called even if the timer is - not running. + Stops a timer. If the argument is virtual timer, it is paused and can + be started again later. + If it's a hardware timer, it is freed and made accessible to other + uses. It should be set up again before being started. */ -void timer_stop(int timer); - -/* - timer_reload() - Reloads the given timer with the supplied constant. Starts the timer if - it was stopped. -*/ -void timer_reload(int timer, int new_delay_or_frequency, enum ClockUnit unit); - -/* - timer_reload2() - Same as timer_reload(), but again uses the native clock count. The - prescaler may not be changed. -*/ -void timer_reload2(int timer, int new_delay); - - - -//--- -// Internal API. -// Referenced for documentation purposes only. Do not call. -//--- - -/* - timer_interrupt() - Handles the interrupt for the given timer. -*/ -void timer_interrupt(int timer); +void timer_stop(timer_t *timer); #endif // _TIMER_H diff --git a/src/clock/clock.c b/src/clock/clock.c index 06426eb..8a14274 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include @@ -19,9 +19,9 @@ static clock_config_t conf = { Several units can be used. Be aware that the result is approximate, and very high frequencies or very short delays will yield important errors. */ -int clock_setting(int duration, enum ClockUnit unit) +uint32_t clock_setting(int duration, enum ClockUnit unit) { - if(conf.Pphi_f <= 0) return -1; + if(conf.Pphi_f <= 0) return 0xffffffff; uint64_t f = conf.Pphi_f >> 2; uint64_t result; @@ -74,30 +74,42 @@ void sleep(void) ); } -/* - sleep_ms(), sleep_us() - Sleeps for the given number of milliseconds / microseconds using the - TIMER_USER timer. The actual sleep time will always be slightly less - than requested. -*/ - -static void sleep_callback(void *flag) +static void sleep_callback(void *arg) { - *((int *)flag) = 1; + int *flag = arg; + *flag = 1; } +/* + sleep_ms() + Sleeps for the given number of milliseconds using a virtual timer. +*/ void sleep_ms(int ms_delay) { - sleep_us(1000 * ms_delay); + volatile int sleep_done = 0; + + timer_t *timer = timer_create(ms_delay, 1); + timer_attach(timer, sleep_callback, (void *)&sleep_done); + timer_start(timer); + + while(!sleep_done) sleep(); } + +/* + sleep_us() + Sleeps for the given number of microseconds using the hardware timer + timer_user. +*/ void sleep_us(int us_delay) { volatile int sleep_done = 0; - timer_start(TIMER_USER, us_delay, Clock_us, sleep_callback, - (void *)&sleep_done, 1); + const uint32_t constant = clock_setting(us_delay, Clock_us); - do sleep(); - while(!sleep_done); + timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1); + timer_attach(timer, sleep_callback, (void *)&sleep_done); + timer_start(timer); + + while(!sleep_done) sleep(); } @@ -109,6 +121,7 @@ void sleep_us(int us_delay) // Indicates whether the measurements are finished. static volatile int clock_measure_done = 0; // Once again SH7705 and SH7305 need different methods... +static timer_t *htimer_7705 = NULL; static int cb_id_7705 = -1; static void clock_measure_7705(void); static void clock_compute_7305(void); @@ -128,25 +141,8 @@ void clock_measure(void) // P_phi using a timer/RTC combination, and we deduce CKIO. if(isSH3()) { - // We prepare the timer manually, without starting it, so that - // we only have to push the running bit to start it when the - // measurements begin. This might look of little effect but it - // makes the precision jump from ~97% to more than 99%. - volatile struct mod_tmu *tmu; - timer_get(TIMER_USER, &tmu, NULL); - - tmu->TCOR = 0xffffffff; - tmu->TCNT = tmu->TCOR; - tmu->TCR.TPSC = TIMER_Po_4; - - tmu->TCR.UNF = 0; - tmu->TCR.UNIE = 1; - tmu->TCR.CKEG = 0; - - timers[TIMER_USER].callback = NULL; - timers[TIMER_USER].data = NULL; - timers[TIMER_USER].repeats = 0; - + htimer_7705 = htimer_setup(timer_user, 0xffffffff, timer_Po_4, + 1); cb_id_7705 = rtc_cb_add(RTCFreq_256Hz, clock_measure_7705, 0); } @@ -226,7 +222,7 @@ static void clock_compute_7305(void) Given the number of P_phi / 4 timer ticks elapsed between two RTC 256 Hz interrupts, determines the clock configuration. */ -static void clock_measure_7705_finalize(int elapsed) +static void clock_measure_7705_finalize(uint32_t elapsed) { volatile unsigned int *FRQCR = (void *)0xffffff80; @@ -254,13 +250,10 @@ static void clock_measure_7705_finalize(int elapsed) */ static void clock_measure_7705_callback(void) { - timer_stop(TIMER_USER); + timer_stop(htimer_7705); rtc_cb_end(cb_id_7705); - volatile struct mod_tmu *tmu; - timer_get(TIMER_USER, &tmu, NULL); - int elapsed = 0xffffffff - tmu->TCNT; - + uint32_t elapsed = 0xffffffff - TMU.timers[timer_user]->TCNT; clock_measure_7705_finalize(elapsed); clock_measure_done = 1; } @@ -274,9 +267,6 @@ static void clock_measure_7705_callback(void) */ static void clock_measure_7705(void) { - volatile unsigned char *tstr; - timer_get(TIMER_USER, NULL, &tstr); - - *tstr |= (1 << TIMER_USER); + timer_start(htimer_7705); rtc_cb_edit(cb_id_7705, RTCFreq_256Hz, clock_measure_7705_callback); } diff --git a/src/core/crt0.c b/src/core/crt0.c index 19cf44d..a343a98 100644 --- a/src/core/crt0.c +++ b/src/core/crt0.c @@ -8,6 +8,7 @@ #include #include #include +#include int main(void); @@ -58,7 +59,7 @@ __attribute__((section(".pretext.entry"))) int start(void) dg->stage = stage_startup; // Exception records: what kind of exceptions / TLB faults / interrupts - // occured last to get some trace in case of crash. + // occurred last to get some trace in case of crash. dg->excepts = 0; for(size_t i = 0; i < sizeof dg->except_vect; i++) dg->except_vect[i] = 0; @@ -67,7 +68,7 @@ __attribute__((section(".pretext.entry"))) int start(void) dg->expevt = 0x00000000; dg->tea = 0x00000000; - // Memory map: provides information about alignement and the relative + // Memory map: provides information about alignment and the relative // size and position of each section, as well as read-only / read-write // types of addresses. dg->section_text.address = (uint32_t)&btext; @@ -107,7 +108,10 @@ __attribute__((section(".pretext.entry"))) int start(void) dg->stage = stage_mmu; #endif - // Initializing gint. + // Detecting the MPU type, initializing the register module addresses, + // and initializing gint. + mpu_init(); + mod_init(); gint_init(); #ifdef GINT_DIAGNOSTICS diff --git a/src/core/init_quit.c b/src/core/init_quit.c index 8ecea6b..ac16dd0 100644 --- a/src/core/init_quit.c +++ b/src/core/init_quit.c @@ -29,11 +29,6 @@ void gint_init(void) uint32_t *ptr = &bgint; uint32_t *src = &gint_data; - // This initialization routine is usually called before any - // constructor. We want to ensure that the MPU type is detected, but - // mpu_init() hasn't been called yet. - mpu_init(); - // Loading the interrupt handler into the memory. while(ptr < &egint) *ptr++ = *src++; diff --git a/src/core/interrupts.c b/src/core/interrupts.c index d056774..eff3d94 100644 --- a/src/core/interrupts.c +++ b/src/core/interrupts.c @@ -1,6 +1,6 @@ #include -#include -#include +#include +#include /* inth_timer_underflow() @@ -17,5 +17,5 @@ void inth_timer_underflow(uint32_t channel) */ void inth_rtc_periodic(void) { - rtc_interrupt(); + rtc_periodic_interrupt(); } diff --git a/src/core/modules.c b/src/core/modules.c new file mode 100644 index 0000000..aad3491 --- /dev/null +++ b/src/core/modules.c @@ -0,0 +1,49 @@ +#include +#include +#include + +//--- +// Structure information +// Here resides most of the platform-dependent register configuration. +// Module structures are arranged to mask as much as possible hardware +// differences to the user. When it becomes impossible to do so at +// compile-time, gint provides functions to ensure that the user does not +// confront to the hardware directly. +//--- + +volatile mod_tmu_t TMU; + + + +//--- +// Initializer +//--- + +static void mod_init_7705(void) +{ + TMU.timers[0] = (void *)0xfffffe94; + TMU.timers[1] = (void *)0xfffffea0; + TMU.timers[2] = (void *)0xfffffeac; + TMU.TSTR = (void *)0xfffffe92; + TMU.TCPR2 = (void *)0xfffffeb8; +} + +static void mod_init_7305(void) +{ + TMU.timers[0] = (void *)0xa4490008; + TMU.timers[1] = (void *)0xa4490014; + TMU.timers[2] = (void *)0xa4490020; + TMU.TSTR = (void *)0xa4490004; + TMU.TCPR2 = NULL; +} + +/* + mod_init() + Initializes the module data to make register access cross-platform. The + MPU needs to have been detected or this function will yield wrong + results. +*/ +void mod_init(void) +{ + isSH3() ? mod_init_7705() : mod_init_7305(); +} diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index fab319e..2e843f3 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -19,7 +19,7 @@ static int delays[2]; static int runs = 0; -#define GRAY_PRESCALER TIMER_Po_64 +static timer_t *gray_timer = NULL; @@ -33,7 +33,8 @@ static int runs = 0; */ void gray_interrupt(void) { - timer_reload2(TIMER_GRAY, delays[(~current) & 1]); + htimer_reload(timer_gray, delays[(~current) & 1]); + screen_display(vrams[current]); current ^= 1; } @@ -68,8 +69,10 @@ void gray_start(void) { if(runs) return; - timer_start2(TIMER_GRAY, delays[0], GRAY_PRESCALER, gray_interrupt, - NULL, 0); + gray_timer = htimer_setup(timer_gray, delays[0], timer_Po_64, 0); + timer_attach(gray_timer, gray_interrupt, NULL); + timer_start(gray_timer); + current &= 1; runs = 1; } @@ -81,9 +84,7 @@ void gray_start(void) */ void gray_stop(void) { - if(!runs) return; - - timer_stop(TIMER_GRAY); + timer_stop(gray_timer); runs = 0; display_useVRAM(display_getLocalVRAM()); diff --git a/src/rtc/rtc_callback.c b/src/rtc/rtc_callback.c index 635eafc..488d0ce 100644 --- a/src/rtc/rtc_callback.c +++ b/src/rtc/rtc_callback.c @@ -7,7 +7,7 @@ struct rtc_cb cb_array[RTC_CB_ARRAY_SIZE] = { 0 }; // Callback identifier (unique). static int unique_id = 1; // Current RTC interrupt frequency. -static enum RTCFrequency rtc_freq = RTCFreq_None; +static rtc_frequency_t rtc_freq = RTCFreq_None; // 256-Hz tick count. This counter is stopped when no callback is registered. static unsigned elapsed256 = 0; @@ -21,7 +21,7 @@ static unsigned elapsed256 = 0; */ static void rtc_cb_update(void) { - enum RTCFrequency max = RTCFreq_None; + rtc_frequency_t max = RTCFreq_None; int n; for(n = 0; n < RTC_CB_ARRAY_SIZE; n++) if(cb_array[n].id) @@ -46,7 +46,7 @@ static void rtc_cb_update(void) The number of repeats may be set to 0, in which case the callback is called indefinitely unless the user calls rtc_cb_end(). */ -int rtc_cb_add(enum RTCFrequency freq, void (*function)(void), int repeats) +int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats) { int n = 0; if(freq == RTCFreq_None || !function || repeats < 0) return -2; @@ -92,7 +92,7 @@ void rtc_cb_end(int id) -2 Invalid parameters This function never removes a callback. Call rtc_cb_end() for this. */ -int rtc_cb_edit(int id, enum RTCFrequency new_freq, +int rtc_cb_edit(int id, rtc_frequency_t new_freq, void (*new_function)(void)) { if(new_freq < 0 || new_freq > 7) return -2; diff --git a/src/rtc/rtc_getTime.c b/src/rtc/rtc_getTime.c index 5e9f653..5a460e9 100644 --- a/src/rtc/rtc_getTime.c +++ b/src/rtc/rtc_getTime.c @@ -21,18 +21,24 @@ static int integer16(int bcd) Reads the current time from the RTC. There is no guarantee that the week day is correct (use the time API for that). */ -struct RTCTime rtc_getTime(void) +rtc_time_t rtc_getTime(void) { volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305; - struct RTCTime time; + rtc_time_t time; - time.seconds = integer8(rtc->RSECCNT.BYTE); - time.minutes = integer8(rtc->RMINCNT.BYTE); - time.hours = integer8(rtc->RHRCNT.BYTE); - time.month_day = integer8(rtc->RDAYCNT.BYTE); - time.month = integer8(rtc->RMONCNT.BYTE); - time.year = integer16(rtc->RYRCNT.WORD); - time.week_day = rtc->RWKCNT; + do + { + rtc->RCR1.CF = 0; + + time.seconds = integer8(rtc->RSECCNT.BYTE); + time.minutes = integer8(rtc->RMINCNT.BYTE); + time.hours = integer8(rtc->RHRCNT.BYTE); + time.month_day = integer8(rtc->RDAYCNT.BYTE); + time.month = integer8(rtc->RMONCNT.BYTE); + time.year = integer16(rtc->RYRCNT.WORD); + time.week_day = rtc->RWKCNT; + } + while(rtc->RCR1.CF != 0); return time; } diff --git a/src/rtc/rtc_interrupt.c b/src/rtc/rtc_interrupt.c index 775a220..32a2c73 100644 --- a/src/rtc/rtc_interrupt.c +++ b/src/rtc/rtc_interrupt.c @@ -2,11 +2,13 @@ #include #include +int rtc_carry_flag = 0; + /* - rtc_interrupt() + rtc_periodic_interrupt() Handles an RTC interrupt by calling the callback. */ -void rtc_interrupt(void) +void rtc_periodic_interrupt(void) { rtc_cb_interrupt(); diff --git a/src/rtc/rtc_setTime.c b/src/rtc/rtc_setTime.c index d343c51..4e71e7f 100644 --- a/src/rtc/rtc_setTime.c +++ b/src/rtc/rtc_setTime.c @@ -22,15 +22,22 @@ static int bcd16(int integer) Sets the time in the RTC registers. The week day is set to 0 if greater than 6. Other fields are not checked. */ -void rtc_setTime(struct RTCTime time) +void rtc_setTime(rtc_time_t time) { volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305; + int wday = (time.week_day < 7) ? (time.week_day) : (0); - rtc->RSECCNT.BYTE = bcd8(time.seconds); - rtc->RMINCNT.BYTE = bcd8(time.minutes); - rtc->RHRCNT.BYTE = bcd8(time.hours); - rtc->RDAYCNT.BYTE = bcd8(time.month_day); - rtc->RMONCNT.BYTE = bcd8(time.month); - rtc->RYRCNT.WORD = bcd16(time.year); - rtc->RWKCNT = time.week_day < 7 ? time.week_day : 0; + do + { + rtc->RCR1.CF = 0; + + rtc->RSECCNT.BYTE = bcd8(time.seconds); + rtc->RMINCNT.BYTE = bcd8(time.minutes); + rtc->RHRCNT.BYTE = bcd8(time.hours); + rtc->RDAYCNT.BYTE = bcd8(time.month_day); + rtc->RMONCNT.BYTE = bcd8(time.month); + rtc->RYRCNT.WORD = bcd16(time.year); + rtc->RWKCNT = wday; + } + while(rtc->RCR1.CF != 0); } diff --git a/src/time/time.c b/src/time/time.c index 16189d4..e6a6727 100644 --- a/src/time/time.c +++ b/src/time/time.c @@ -12,7 +12,7 @@ */ time_t time(time_t *timeptr) { - struct RTCTime rtc = rtc_getTime(); + rtc_time_t rtc = rtc_getTime(); struct tm tm; time_t calendar; diff --git a/src/timer/common_api.c b/src/timer/common_api.c new file mode 100644 index 0000000..46b39fa --- /dev/null +++ b/src/timer/common_api.c @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +/* + timer_attach() + Attaches a callback to a virtual or hardware timer. +*/ +void timer_attach(timer_t *timer, void *callback, void *argument) +{ + if(!timer) return; + + timer->callback = callback; + timer->argument = argument; +} + +/* + timer_start() + Starts a virtual or hardware timer. If the timer has a callback + attached, then the callback function will start being called regularly; + otherwise, the timer will start pushing ET_Timer events to the event + queue. It is advised, not to change a timer's configuration while it's + running. +*/ +void timer_start(timer_t *timer) +{ + if(!timer) return; + timer->active = 1; + + if(timer->virtual) vtimer_updateOne(timer->ms_delay); + else TMU.TSTR->byte |= (1 << (timer - htimers)); +} + +/* + timer_stop() + Pauses a virtual or hardware timer. The timer stops counting and can be + started again later. +*/ +void timer_stop(timer_t *timer) +{ + if(!timer) return; + timer->active = 0; + + if(timer->virtual) vtimer_updateAll(); + else + { + TMU.TSTR->byte &= ~(1 << (timer - htimers)); + timer->used = 0; + } +} + +/* + timer_interrupt() + Handles the interrupt for the given timer channel. +*/ +void timer_interrupt(int channel) +{ + // Is this the virtual timer support? + if(htimers[channel].vsupport) vtimer_interrupt(); + else timer_callback_event(&htimers[channel]); + + // Clearing the interrupt flag. + TMU.timers[channel]->TCR.UNF = 0; +} + +/* + timer_callback_event() + Executes the callback of a timer, or pushes a new timer event depending + on the timer configuration. Also reduces the amount of repeats left and + clears the active flag (or stops the hardware timer) if this number + falls from one to zero. +*/ +void timer_callback_event(timer_t *timer) +{ + if(!timer) return; + + // Callback-type timers. + if(timer->callback) + { + if(!timer->argument) + { + void (*fun)(void) = timer->callback; + fun(); + } + else + { + void (*fun)(void *arg) = timer->callback; + fun(timer->argument); + } + } + + // Event-type timers. + else + { + event_t event = { + .type = ET_Timer, + .timer = timer + }; + event_push(event); + } + + // Reducing the number of repeats and stopping the timer if required. + if(timer->repeats_left > 0) + { + if(!--timer->repeats_left) + { + if(timer->virtual) timer->active = 0; + else timer_stop(timer); + } + } +} diff --git a/src/timer/hardware_timers.c b/src/timer/hardware_timers.c new file mode 100644 index 0000000..57e247f --- /dev/null +++ b/src/timer/hardware_timers.c @@ -0,0 +1,63 @@ +#include +#include +#include + +//--- +// Public API +//--- + +timer_t htimers[3] = { 0 }; + +/* + htimer_setup() + Configures a hardware timer. +*/ +timer_t *htimer_setup(timer_hard_t id, uint32_t constant, timer_input_t input, + int repeats) +{ + if(id < 0 || id >= 3 || htimers[id].used) return NULL; + timer_t *timer = &htimers[id]; + + // We don't care about this. + timer->ms_delay = 0; + timer->ms_elapsed = 0; + + timer->repeats_left = repeats; + + timer->used = 1; + timer->active = 0; + timer->virtual = 0; + timer->vsupport = 0; + timer->events = 0; + + timer->callback = NULL; + timer->argument = NULL; + + // Time to set up the real thing. + volatile mod_tmu_timer_t *tmu = TMU.timers[id]; + + tmu->TCOR = constant; + tmu->TCNT = constant; + tmu->TCR.TPSC = input; + + tmu->TCR.UNF = 0; // Clear the interrupt flag. + tmu->TCR.UNIE = 1; // Enable underflow interrupt. + tmu->TCR.CKEG = 0; // Count on rising edge (SH7705). + + return timer; +} + +/* + htimer_reload() + Reloads a hardware timer without starting or stopping it. +*/ +void htimer_reload(timer_hard_t id, uint32_t new_constant) +{ + volatile mod_tmu_timer_t *tmu = TMU.timers[id]; + + // This is not much but we want to make the transition as swift as + // possible and I can prove that at least one cycle will be lost in + // processor operation. Thus... + tmu->TCNT = new_constant - 1; + tmu->TCOR = new_constant; +} diff --git a/src/timer/timer_get.c b/src/timer/timer_get.c deleted file mode 100644 index 9174164..0000000 --- a/src/timer/timer_get.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -/* - timer_get() - Returns the timer and TSTR register addresses. -*/ -void timer_get(int timer, volatile struct mod_tmu **tmu, - volatile unsigned char **tstr) -{ - // Using SH7705 information for SH-3-based MPUs. - if(isSH3()) - { - if(tstr) *tstr = (volatile unsigned char *)0xfffffe92; - if(tmu) *tmu = (volatile struct mod_tmu *) - (0xfffffe94 + 12 * timer); - } - // Assuming SH7305 by default. - else - { - if(tstr) *tstr = (volatile unsigned char *)0xa4490004; - if(tmu) *tmu = (volatile struct mod_tmu *) - (0xa4490008 + 12 * timer); - } -} diff --git a/src/timer/timer_interrupt.c b/src/timer/timer_interrupt.c deleted file mode 100644 index 5b183f4..0000000 --- a/src/timer/timer_interrupt.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include - -#include - -struct Timer timers[3] = { - { .callback = NULL, .data = NULL, .repeats = 0 }, - { .callback = NULL, .data = NULL, .repeats = 0 }, - { .callback = NULL, .data = NULL, .repeats = 0 }, -}; - -/* - timer_interrupt() - Handles the interrupt for the given timer. -*/ -void timer_interrupt(int timer) -{ - volatile struct mod_tmu *tmu; - timer_get(timer, &tmu, NULL); - - tmu->TCR.UNF = 0; - - if(timers[timer].callback) - { - if(timers[timer].data) - { - void (*fun)(void *data) = timers[timer].callback; - fun(timers[timer].data); - } - else - { - void (*fun)(void) = timers[timer].callback; - fun(); - } - } - - // Reducing the number of repetitions left, if not infinite. - if(!timers[timer].repeats) return; - if(timers[timer].repeats == 1) timer_stop(timer); - else timers[timer].repeats--; -} diff --git a/src/timer/timer_reload.c b/src/timer/timer_reload.c deleted file mode 100644 index 7d6bbe8..0000000 --- a/src/timer/timer_reload.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include - -/* - timer_reload() - Reloads the given timer with the supplied constant. Starts the timer if - it was stopped. -*/ -void timer_reload(int timer, int new_delay_or_frequency, enum ClockUnit unit) -{ - timer_reload2(timer, clock_setting(new_delay_or_frequency, unit)); -} - -/* - timer_reload2() - Same as timer_reload(), but again uses the native clock count. The - prescaler may not be changed. -*/ -void timer_reload2(int timer, int new_delay) -{ - volatile struct mod_tmu *tmu; - volatile unsigned char *tstr; - int byte = (1 << timer); - timer_get(timer, &tmu, &tstr); - - // Setting the constant and the delay. - tmu->TCOR = new_delay; - tmu->TCNT = new_delay; - - // Starting the timer. - *tstr |= byte; -} diff --git a/src/timer/timer_start.c b/src/timer/timer_start.c deleted file mode 100644 index 0792702..0000000 --- a/src/timer/timer_start.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include - -/* - timer_start2() - Configures and starts a time using a clock count and a prescaler. -*/ -void timer_start2(int timer, int delay, int prescaler, void *callback, - void *data, int repeats) -{ - volatile struct mod_tmu *tmu; - volatile unsigned char *tstr; - int byte = (1 << timer); - - timer_get(timer, &tmu, &tstr); - - // Loading the counter, the constant and the prescaler/ - tmu->TCOR = delay; - tmu->TCNT = delay; - tmu->TCR.TPSC = prescaler; - - // Resetting underflow flag and enabling interruptions. - tmu->TCR.UNF = 0; - tmu->TCR.UNIE = 1; - - // Counting on rising edge (ignored on SH7305). - tmu->TCR.CKEG = 0; - - // Loading the structure information. - timers[timer].callback = callback; - timers[timer].data = data; - timers[timer].repeats = repeats; - - // Starting the timer. - *tstr |= byte; -} - -/* - timer_start() - Configures and starts a timer using a delay, or a frequency, and the - associated unit. -*/ -void timer_start(int timer, int delay, enum ClockUnit unit, void *callback, - void *data, int repeats) -{ - timer_start2(timer, clock_setting(delay, unit), TIMER_Po_4, callback, - data, repeats); -} diff --git a/src/timer/timer_stop.c b/src/timer/timer_stop.c deleted file mode 100644 index 083c4a4..0000000 --- a/src/timer/timer_stop.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include - -#include - -/* - timer_stop() - Stops the given timer. This function may be called even if the timer is - not running. -*/ -void timer_stop(int timer) -{ - volatile unsigned char *tstr; - int byte = (1 << timer); - - timer_get(timer, NULL, &tstr); - *tstr &= ~byte; -} diff --git a/src/timer/virtual_timers.c b/src/timer/virtual_timers.c new file mode 100644 index 0000000..518c5fd --- /dev/null +++ b/src/timer/virtual_timers.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include + +//--- +// Public API +//--- + +timer_t vtimers[TIMER_SLOTS] = { 0 }; + +/* + timer_create() + Creates a virtual timer and configures its delay and repetition count. +*/ +timer_t *timer_create(int ms_delay, int repeats) +{ + if(ms_delay <= 0) return NULL; + timer_t *timer = vtimers; + + // Finding an available virtual slot. + while(timer < vtimers + TIMER_SLOTS && timer->used) timer++; + if(timer >= vtimers + TIMER_SLOTS) return NULL; + + timer->ms_delay = ms_delay; + timer->ms_elapsed = 0; + timer->repeats_left = repeats; + + timer->used = 1; + timer->active = 0; + timer->virtual = 1; + timer->vsupport = 0; + timer->events = 0; + + timer->callback = NULL; + timer->argument = NULL; + + return timer; +} + +/* + timer_destroy() + Destroys a virtual timer. This virtual timer pointer becomes invalid + and should not be used anymore. +*/ +void timer_destroy(timer_t *timer) +{ + timer->events = 0; + timer->active = 0; + timer->used = 0; + + vtimer_updateAll(); +} + + + +//--- +// Virtual timers management +//--- + +static int current_delay = 0; +static uint32_t current_constant = 0; + +/* + vtimer_interrupt() + Interrupt handling subsystem for the virtual timers. +*/ +void vtimer_interrupt(void) +{ + // Do we need to recompute the hardware support frequency? + int recalc = 0; + + // No timer is running. + if(!current_delay) return; + + // Update them all and call the required callbacks. Stop the ones that + // have been running for long enough. + for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++) + if(timer->used) + { + timer->ms_elapsed += current_delay; + + // This should happen only once but in case there is a problem, + // this loop will ensure that at least the correct amount of + // callbacks is executed. + // We could divide but it would be slower. + while(timer->ms_elapsed >= timer->ms_delay) + { + // We don't call the callbacks now because we may need + // to update the timer frequency later and we want to + // get there asap. + timer->events++; + timer->ms_elapsed -= timer->ms_delay; + } + + // We would need to stop one virtual timer. + if(timer->repeats_left > 0 && timer->repeats_left <= + timer->events) recalc = 1; + } + + if(recalc) vtimer_updateAll(); + + for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++) + if(timer->used) + { + while(timer->active && timer->events) + { + timer_callback_event(timer); + timer->events--; + } + timer->used = timer->active; + } +} + +/* + vtimer_update() + Swiftly updates the hardware support counter and constant to keep up + with new kinds of timers being added while disturbing the counting flow + as little as possible. +*/ +static void vtimer_update(int new_delay) +{ + volatile mod_tmu_timer_t *tmu = TMU.timers[timer_virtual]; + timer_t *timer = &htimers[timer_virtual]; + + if(new_delay == current_delay) return; + + if(!new_delay) + { + current_delay = 0; + current_constant = 0; + + // At this point the virtual support timer was running and has + // been stopped, thus the hardware timer structure is filled + // properly so we can use this function. + timer_stop(timer); + return; + } + uint32_t new_constant = clock_setting(new_delay, Clock_ms); + + // The transition needs to be as smooth as possible. We have probably + // spent a lot of time calculating this new GCD delay so we want to + // take into consideration the current value of the timer counter. + // The -1 is here to take into account the time between reading and + // writing the new constant (~10 I_phi cycles thus usually 5 P_phi + // cycles; given this timer runs on P_phi / 4 this makes 1 unit). + // Here we suppose that the new TCNT is positive, i.e that the update + // happened in less than 1 ms. This is reasonable as long as this + // happens *before* calling callbacks. + tmu->TCNT = new_constant - (current_constant - tmu->TCNT - 1); + tmu->TCOR = new_constant; + + if(!current_delay) + { + // Ups, we've been using a random counter; the timer's not + // running. + tmu->TCNT = tmu->TCOR; + // Set it up then. + tmu->TCR.TPSC = timer_Po_4; + tmu->TCR.UNF = 0; + tmu->TCR.UNIE = 1; + tmu->TCR.CKEG = 0; + // Tell them that the virtual timer support is running there. + timer->used = 1; + timer->active = 0; + timer->virtual = 0; + timer->vsupport = 1; + timer->events = 0; + // And let's roll! + timer_start(timer); + } + + current_delay = new_delay; + current_constant = new_constant; +} + +/* + gcd() + Well, the Euclidean algorithm. That's a O(ln(a)) & O(ln(b)) FWIW. +*/ +static int gcd(int a, int b) +{ + while(b) + { + int r = a % b; + a = b; + b = r; + } + + return a; +} + +/* + vtimer_updateOne() + Update the virtual timer hardware support timer, knowing that a virtual + timer with the given delay has been started. +*/ +void vtimer_updateOne(int additional_delay) +{ + vtimer_update(gcd(current_delay, additional_delay)); +} + +/* + vtimer_updateAll() + Updates the virtual timer hardware support after computing the GCD of + all virtual timers delays. This computation is rather long (especially + this modulo can afaik only be achieved using division on SuperH and + it's a 70-cycle operation) so it should be avoided when possible. +*/ + +void vtimer_updateAll(void) +{ + int gcd_delay = 0; + + for(timer_t *timer = vtimers; timer < vtimers + TIMER_SLOTS; timer++) + if(timer->used && timer->active && (timer->repeats_left <= 0 || + timer->repeats_left > timer->events)) + { + gcd_delay = gcd(gcd_delay, timer->ms_delay); + } + + vtimer_update(gcd_delay); +} From c8170b165adec16d8a790022c2f1f61418666061 Mon Sep 17 00:00:00 2001 From: lephe Date: Sun, 26 Mar 2017 18:38:32 +0200 Subject: [PATCH 07/24] Some quality review. Made keyboard time milliseconds. --- Makefile | 2 +- TODO | 4 +- demo/gintdemo.c | 10 +- demo/test_bopti.c | 28 +- demo/test_gray.c | 8 +- demo/test_keyboard.c | 27 +- demo/test_rtc.c | 18 +- demo/test_tales.c | 26 +- demo/test_timer.c | 20 +- include/alloca.h | 6 +- include/bopti.h | 67 +++++ include/clock.h | 55 ++-- include/ctype.h | 85 ++---- include/display.h | 158 ++++++----- include/events.h | 44 +-- include/gint.h | 6 +- include/gray.h | 60 ++--- include/internals/bopti.h | 6 +- include/internals/display.h | 51 ++-- include/internals/gint.h | 16 +- include/internals/interrupt_maps.h | 4 +- include/internals/keyboard.h | 7 +- include/internals/mmu.h | 4 +- include/internals/stdio.h | 2 +- include/internals/tales.h | 7 +- include/internals/time.h | 2 +- include/keyboard.h | 358 ++++++++++++++----------- include/mpu.h | 2 +- include/rtc.h | 2 +- include/screen.h | 2 +- include/setjmp.h | 2 +- include/stdio.h | 2 +- include/stdlib.h | 2 +- include/string.h | 2 +- include/tales.h | 4 +- include/time.h | 2 +- include/timer.h | 12 +- src/bopti/bopti_internals.c | 4 +- src/bopti/dimage.c | 6 +- src/bopti/gimage.c | 6 +- src/clock/clock.c | 16 +- src/core/exceptions.c | 2 +- src/ctype/ctype_classes.c | 43 +++ src/ctype/ctype_functions.c | 71 +++++ src/display/dclear.c | 26 +- src/display/dclear_area.c | 21 -- src/display/display_vram.c | 45 ++-- src/display/dline.c | 90 ++++--- src/display/dpixel.c | 33 +-- src/display/drect.c | 59 ++++ src/display/dreverse_area.c | 21 -- src/display/getMasks.c | 18 +- src/events/event_get.c | 4 +- src/events/event_push.c | 2 +- src/gray/gclear.c | 27 +- src/gray/gclear_area.c | 15 -- src/gray/gline.c | 19 +- src/gray/gpixel.c | 68 +++-- src/gray/gray_engine.c | 20 +- src/gray/grect.c | 70 +++++ src/gray/greverse_area.c | 15 -- src/keyboard/getkey.c | 161 ++++++----- src/keyboard/{keychar.c => key_char.c} | 14 +- src/keyboard/key_id.c | 19 ++ src/keyboard/key_type.c | 24 ++ src/keyboard/keyboard_config.c | 20 -- src/keyboard/keyboard_core.c | 181 +++++++++++++ src/keyboard/keyboard_interrupt.c | 132 --------- src/keyboard/keyboard_misc.c | 25 -- src/keyboard/keyboard_sh7305.c | 21 +- src/keyboard/keyid.c | 17 -- src/keyboard/keytype.c | 17 -- src/keyboard/multigetkey.c | 4 +- src/string/ctype.c | 64 ----- src/tales/tales_configuration.c | 4 +- src/tales/tales_gray.c | 67 +++-- src/tales/tales_internals.c | 49 ++-- src/timer/common_api.c | 2 +- src/timer/virtual_timers.c | 22 +- 79 files changed, 1482 insertions(+), 1175 deletions(-) create mode 100644 include/bopti.h create mode 100644 src/ctype/ctype_classes.c create mode 100644 src/ctype/ctype_functions.c delete mode 100644 src/display/dclear_area.c create mode 100644 src/display/drect.c delete mode 100644 src/display/dreverse_area.c delete mode 100644 src/gray/gclear_area.c create mode 100644 src/gray/grect.c delete mode 100644 src/gray/greverse_area.c rename src/keyboard/{keychar.c => key_char.c} (67%) create mode 100644 src/keyboard/key_id.c create mode 100644 src/keyboard/key_type.c delete mode 100644 src/keyboard/keyboard_config.c create mode 100644 src/keyboard/keyboard_core.c delete mode 100644 src/keyboard/keyboard_interrupt.c delete mode 100644 src/keyboard/keyboard_misc.c delete mode 100644 src/keyboard/keyid.c delete mode 100644 src/keyboard/keytype.c delete mode 100644 src/string/ctype.c diff --git a/Makefile b/Makefile index 9e84bf3..04677b1 100755 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ include Makefile.cfg # Modules modules-gint = bopti clock core display events gray keyboard mmu rtc \ screen tales timer -modules-libc = setjmp stdio stdlib string time +modules-libc = ctype setjmp stdio stdlib string time # Targets target-lib = libgint.a diff --git a/TODO b/TODO index 45638bd..9134ce0 100644 --- a/TODO +++ b/TODO @@ -23,8 +23,8 @@ Larger improvements: * core: Allow return to menu - serial: Implement a driver - usb: Implement a driver -- esper: Cleaner playback, synthetizing -- clock: Handle overclocking (relaunch clocks when overclocking) +- esper: Cleaner playback, synthesizing +- clock: Handle overclock (relaunch clocks when overclocking) - project: Unify this hellish mess of register access! Things to investigate: diff --git a/demo/gintdemo.c b/demo/gintdemo.c index bbd1785..98d1ac5 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -288,7 +288,7 @@ void main_menu(int *category, int *app) // Quite a few things to declare... //--- - extern Image res_opt_menu; + extern image_t res_opt_menu; const char *mpu, *mpu_names[] = { "Unknown", @@ -332,7 +332,7 @@ void main_menu(int *category, int *app) int i; mpu = mpu_names[MPU_CURRENT < 5 ? MPU_CURRENT : 5]; - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); while(1) { @@ -386,8 +386,8 @@ void main_menu(int *category, int *app) if(scroll > 0) locate(20, 2, "\x0d"); if(scroll + 6 < list_len) locate(20, 7, "\x0e"); - dreverse_area(0, 8 * (index - scroll) + 8, 127, - 8 * (index - scroll) + 15); + drect(0, 8 * (index - scroll) + 8, 127, + 8 * (index - scroll) + 15, color_invert); } dupdate(); @@ -495,8 +495,6 @@ int main(void) { int category, app; - sleep_ms(2000); - while(1) { main_menu(&category, &app); diff --git a/demo/test_bopti.c b/demo/test_bopti.c index 818bdb6..a908823 100644 --- a/demo/test_bopti.c +++ b/demo/test_bopti.c @@ -23,7 +23,7 @@ --------------------------------------------------------- */ -static void getwh(Image *img, int *width, int *height) +static void getwh(image_t *img, int *width, int *height) { const uint8_t *data; @@ -42,7 +42,7 @@ static void getwh(Image *img, int *width, int *height) *height = (data[2] << 8) | data[3]; } -static void getxy(Image *img, int *x, int *y) +static void getxy(image_t *img, int *x, int *y) { int width, height; @@ -51,10 +51,10 @@ static void getxy(Image *img, int *x, int *y) *y = 28 - (height >> 1); } -static Image *select(Image *current) +static image_t *select(image_t *current) { - extern Image res_bopti_thumbs; - extern Image + extern image_t res_bopti_thumbs; + extern image_t res_items, res_sprites, res_swords, @@ -62,7 +62,7 @@ static Image *select(Image *current) res_isometric; struct { - Image *img; + image_t *img; const char *name; const char *info; } images[] = { @@ -74,7 +74,7 @@ static Image *select(Image *current) { NULL, NULL, NULL } }; - Image *thumbs = &res_bopti_thumbs; + image_t *thumbs = &res_bopti_thumbs; int items = 0; static int row = 0; int leave = 1, i; @@ -106,7 +106,7 @@ static Image *select(Image *current) } } - greverse_area(0, 8 * row + 8, 128, 8 * row + 23); + grect(0, 8 * row + 8, 128, 8 * row + 23, color_invert); gupdate(); do @@ -138,8 +138,8 @@ static Image *select(Image *current) void test_bopti(void) { - extern Image res_opt_bitmap; - Image *img = NULL; + extern image_t res_opt_bitmap; + image_t *img = NULL; int leave = 1; int black_bg = 0; @@ -152,10 +152,10 @@ void test_bopti(void) gray_start(); gclear(); - if(black_bg) greverse_area(0, 0, 127, 63); + if(black_bg) grect(0, 0, 127, 63, color_invert); if(img) gimage(x, y, img); - gclear_area(0, 55, 127, 63); + grect(0, 55, 127, 63, color_white); gimage(0, 56, &res_opt_bitmap); gupdate(); } @@ -164,10 +164,10 @@ void test_bopti(void) gray_stop(); dclear(); - if(black_bg) dreverse_area(0, 0, 127, 63); + if(black_bg) drect(0, 0, 127, 63, color_invert); if(img) dimage(x, y, img); - dclear_area(0, 55, 127, 63); + drect(0, 55, 127, 63, color_white); dimage(0, 56, &res_opt_bitmap); dupdate(); } diff --git a/demo/test_gray.c b/demo/test_gray.c index b562f1f..4a1ac03 100644 --- a/demo/test_gray.c +++ b/demo/test_gray.c @@ -10,9 +10,9 @@ static void draw(int delay1, int delay2, int selected) { - extern Image res_opt_gray; - unsigned int *vl = gray_lightVRAM(); - unsigned int *vd = gray_darkVRAM(); + extern image_t res_opt_gray; + uint32_t *vl = gray_lightVRAM(); + uint32_t *vd = gray_darkVRAM(); gclear(); locate(1, 1, "Gray engine"); @@ -58,7 +58,7 @@ void test_gray(void) } changed = 0; - key = getkey_opt(Getkey_RepeatArrowKeys, 1); + key = getkey_opt(getkey_repeat_arrow_keys, 25); if(key == KEY_EXIT) break; changed = 1; diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c index 87e3ead..2046118 100644 --- a/demo/test_keyboard.c +++ b/demo/test_keyboard.c @@ -31,24 +31,24 @@ static void draw_keyboard(volatile uint8_t *state) { for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++) if(abs(k) + abs(l) <= 2) - dpixel(x + k, y + l, Color_Black); + dpixel(x + k, y + l, color_black); } // Drawing a square border otherwise. else { for(k = -1; k <= 1; k++) for(l = -1; l <= 1; l++) - if(k || l) dpixel(x + k, y + l, Color_Black); + if(k || l) dpixel(x + k, y + l, color_black); } } // Binding the arrow keys together for a more visual thing. - dpixel(28, 19, Color_Black); dpixel(29, 19, Color_Black); - dpixel(28, 24, Color_Black); dpixel(29, 24, Color_Black); - dpixel(26, 21, Color_Black); dpixel(26, 22, Color_Black); - dpixel(31, 21, Color_Black); dpixel(31, 22, Color_Black); + dpixel(28, 19, color_black); dpixel(29, 19, color_black); + dpixel(28, 24, color_black); dpixel(29, 24, color_black); + dpixel(26, 21, color_black); dpixel(26, 22, color_black); + dpixel(31, 21, color_black); dpixel(31, 22, color_black); // An horizontal line to separate parts of the keyboard. - dline(5, 28, 32, 28, Color_Black); + dline(5, 28, 32, 28, color_black); } typedef struct { @@ -64,7 +64,7 @@ static void push_history(enhanced_event_t *history, int size, event_t event) // Determining where the history ends. int length = 0; - while(length < size && history[length].type != ET_None) length++; + while(length < size && history[length].type != event_none) length++; // Checking if the previous event is being repeated. if(length > 0 && event_eq(history[length - 1], event)) @@ -105,10 +105,10 @@ static void draw_events(enhanced_event_t *history, int size) "None ", "User ", "Press", "Rept.", "Rel. ", "Timer" }; - for(int i = 0; i < size && history[i].type != ET_None; i++) + for(int i = 0; i < size && history[i].type != event_none; i++) { print(8, 3 + i, "%s %s", event_names[history[i].type], - key_names[keyid(history[i].key)]); + key_names[key_id(history[i].key)]); if(history[i].repeats > 1) print(19, 3 + i, "%d", history[i].repeats); } @@ -124,18 +124,19 @@ void test_keyboard_events(void) int history_size = 5; event_t event; - for(int i = 0; i < history_size; i++) history[i].type = ET_None; + for(int i = 0; i < history_size; i++) history[i].type = event_none; while(1) { dclear(); locate(1, 1, "Keyboard and events"); - draw_keyboard(keystate()); + draw_keyboard(keyboard_stateBuffer()); draw_events(history, history_size); dupdate(); event = waitevent(); - if(event.type == ET_KeyPress && event.key == KEY_EXIT) break; + if(event.type == event_key_press && event.key == KEY_EXIT) + break; push_history(history, history_size, event); } } diff --git a/demo/test_rtc.c b/demo/test_rtc.c index 053e218..1211aed 100644 --- a/demo/test_rtc.c +++ b/demo/test_rtc.c @@ -18,7 +18,7 @@ static void draw(rtc_time_t time) { - extern Image res_rtc_segments; + extern image_t res_rtc_segments; const char *days[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" @@ -42,7 +42,7 @@ static void draw(rtc_time_t time) 12 * digits[i], 0, 11, 19); // Drawing ':' between pairs of digits. for(i = 0; i < 16; i++) dpixel(47 + 32 * (i >= 8) + (i & 1), - 14 + 5 * !!(i & 4) + !!(i & 2), Color_Black); + 14 + 5 * !!(i & 4) + !!(i & 2), color_black); // This should print time.year + 1900 but for the sake of this demo we // have tweaked the field so that it already contains time.year + 1900. @@ -52,7 +52,7 @@ static void draw(rtc_time_t time) static void callback(void) { - extern Image res_opt_rtc; + extern image_t res_opt_rtc; rtc_time_t time = rtc_getTime(); dclear(); @@ -115,8 +115,8 @@ static void set_region(rtc_time_t *time, int region, int value) static void set(void) { - extern Image res_opt_rtc; - Image *opt = &res_opt_rtc; + extern image_t res_opt_rtc; + image_t *opt = &res_opt_rtc; struct { int x, y; @@ -136,8 +136,8 @@ static void set(void) { dclear(); draw(time); - dreverse_area(regions[n].x, regions[n].y, regions[n].x - + regions[n].w - 1, regions[n].y + regions[n].h - 1); + drect(regions[n].x, regions[n].y, regions[n].x + regions[n].w + - 1, regions[n].y + regions[n].h - 1, color_invert); if(n == 6) dimage_part(0, 56, opt, 0, 9 * (1 + slide), 128, 8); if(n == 7) dimage_part(0, 56, opt, 0, 9 * (3 + slide), 128, 8); @@ -191,9 +191,9 @@ static void set(void) else leave = 0; } - else if(isdigit(keychar(key))) // Numbers + else if(isdigit(key_char(key))) // Numbers { - int val = keychar(key) - '0'; + int val = key_char(key) - '0'; int ok = 1; if(n == 0) ok = (val <= 2); diff --git a/demo/test_tales.c b/demo/test_tales.c index 644fc23..135925d 100644 --- a/demo/test_tales.c +++ b/demo/test_tales.c @@ -27,7 +27,7 @@ static Font *select(Font *current) while(1) { - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); dclear(); locate(1, 1, "Select a font:"); @@ -39,18 +39,18 @@ static Font *select(Font *current) int height = fonts[i].font->line_height; int y = (i + 2) * 8 - 8 + ((7 - height) >> 1); - text_configure(fonts[i].font, Color_Black); + text_configure(fonts[i].font, color_black); dtext(7, y, fonts[i].name); } else { - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); locate(2, i + 2, fonts[i].name); } } - dreverse_area(0, 8 * row + 8, 128, 8 * row + 15); + drect(0, 8 * row + 8, 128, 8 * row + 15, color_invert); dupdate(); do @@ -80,9 +80,9 @@ static Font *select(Font *current) void test_tales(void) { - enum Color colors[] = { Color_Black, Color_Dark, Color_Light, - Color_White, Color_Invert }; - extern Image res_opt_tales; + color_t colors[] = { color_black, color_dark, color_light, color_white, + color_invert }; + extern image_t res_opt_tales; Font *font = NULL; int black_bg = 0; @@ -94,7 +94,7 @@ void test_tales(void) while(1) { gclear(); - if(black_bg) greverse_area(0, 0, 127, 54); + if(black_bg) grect(0, 0, 127, 54, color_invert); if(font) { @@ -119,10 +119,10 @@ void test_tales(void) gimage(0, 56, &res_opt_tales); x = 45 + 8 * color; - gline(x, 57, x + 5, 57, Color_Black); - gline(x, 57, x, 62, Color_Black); - gline(x + 5, 57, x + 5, 62, Color_Black); - gline(x, 62, x + 5, 62, Color_Black); + gline(x, 57, x + 5, 57, color_black); + gline(x, 57, x, 62, color_black); + gline(x + 5, 57, x + 5, 62, color_black); + gline(x, 62, x + 5, 62, color_black); gupdate(); @@ -146,7 +146,7 @@ void test_tales(void) case KEY_EXIT: gray_stop(); - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); return; default: leave = 0; diff --git a/demo/test_timer.c b/demo/test_timer.c index a830677..d64e6c0 100644 --- a/demo/test_timer.c +++ b/demo/test_timer.c @@ -36,7 +36,7 @@ static void timing_timer(void) static void timing_start(void) { - uint32_t delay = clock_setting(64, Clock_Hz); + uint32_t delay = clock_setting(64, clock_Hz); htimer = htimer_setup(timer_user, delay, timer_Po_4, 0); timer_attach(htimer, timing_timer, NULL); timer_start(htimer); @@ -59,8 +59,8 @@ static void timing_start(void) */ static void small_text(int x, int y, const char *text, int alignment) { - extern Image res_clock_chars; - Image *chars = &res_clock_chars; + extern image_t res_clock_chars; + image_t *chars = &res_clock_chars; const char *table = "0123456789kMHz*/"; if(alignment) x -= 2 * strlen(text) - 1, y -= 2; @@ -133,9 +133,9 @@ static void display_freq(int x, int y, int freq) */ static void draw(int tab) { - extern Image res_opt_timer; - extern Image res_clock_7705; - extern Image res_clock_7305; + extern image_t res_opt_timer; + extern image_t res_clock_7705; + extern image_t res_clock_7305; char buffer[16]; @@ -163,14 +163,14 @@ static void draw(int tab) small_text(84, 34, buffer, 1); if(conf.Iphi_div1 == 1) - dline(85, 43, 99, 43, Color_Black); + dline(85, 43, 99, 43, color_black); else { sprintf(buffer, "/%d", conf.Iphi_div1); small_text(89, 41, buffer, 0); } if(conf.Pphi_div1 == 1) - dline(85, 50, 99, 50, Color_Black); + dline(85, 50, 99, 50, color_black); else { sprintf(buffer, "/%d", conf.Pphi_div1); @@ -249,13 +249,13 @@ void test_timer(void) elapsed_rtc = -1; cb_id = rtc_cb_add(RTCFreq_64Hz, timing_start, 0); - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); while(1) { draw(tab); - switch(getkey_opt(Getkey_NoOption, 1)) + switch(getkey_opt(getkey_none, 25)) { case KEY_EXIT: rtc_cb_end(cb_id); diff --git a/include/alloca.h b/include/alloca.h index 37c88e7..2011266 100644 --- a/include/alloca.h +++ b/include/alloca.h @@ -3,12 +3,14 @@ // standard library module: alloca // // Allows dynamic memory allocation on the stack. Memory is automatically -// freed when the calling function exits. +// freed when the calling function exits, but this function suffers from +// risks of stack overflow; make sure you don't inline functions that use +// alloca or allocate more than a few hundred bytes with it. // //--- #ifndef _ALLOCA_H -#define _ALLOCA_H 1 +#define _ALLOCA_H #include diff --git a/include/bopti.h b/include/bopti.h new file mode 100644 index 0000000..8889359 --- /dev/null +++ b/include/bopti.h @@ -0,0 +1,67 @@ +//--- +// +// gint drawing module: bopti +// +// This module is a powerful bitmap renderer. It *heavily* relies on the +// line-based structure of the video RAM as well as the high density of +// information. A single CPU access (longword operation) can affect 32 +// pixels at once, which is crucial for performance. The same goes for all +// other drawing modules, but this one typically has 350 lines of code +// just to wrap these longword accesses -- and it's blazingly fast. +// +//--- + +#ifndef _BOPTI_H +#define _BOPTI_H + +/* + image_t + This structure holds meta-data of a bitmap encoded with fxconv. Data is + accessed using longword operations for performance considerations, + which requires that the all fields of the structure be properly aligned + and of a correct size. +*/ +typedef struct +{ + uint8_t magic; + uint8_t format; + + uint8_t width; + uint8_t height; + + const uint32_t data[]; + +} __attribute__((packed, aligned(4))) image_t; + +/* + dimage() + Displays a monochrome image in the vram. This function does a real lot + of optimization. +*/ +void dimage(int x, int y, image_t *image); + +/* + dimage_part() + Draws a portion of an image, defined by its bounding rectangle. + Point (left, top) is included, but (left + width, top + height) is + excluded. +*/ +void dimage_part(int x, int y, image_t *img, int left, int top, int width, + int height); + +/* + gimage() + Displays a gray image in the dual-vram. +*/ +void gimage(int x, int y, image_t *image); + +/* + gimage_part() + Draws a portion of a gray image, defined by its bounding rectangle. + Point (left, top) is included, but (left + width, top + height) is + excluded. +*/ +void gimage_part(int x, int y, image_t *image, int left, int top, int width, + int height); + +#endif // _BOPTI_H diff --git a/include/clock.h b/include/clock.h index 00822e8..571b56c 100644 --- a/include/clock.h +++ b/include/clock.h @@ -2,8 +2,14 @@ // // gint core module: clock // -// Measures the frequency of the MPU clocks. This module assumes that the -// clock mode is 3 on SH7305 (as does FTune). +// This module interfaces with the MPU clocks and is used to measure the +// clock frequencies at the beginning of execution. At this stage, it +// assumes that clock mode 3 is used on SH7305 (as does FTune), because +// there doesn't seem to be a way of getting this information. +// +// It also provides some sleep and time conversion functions, and access +// to how the clocks are configured. In the future, it would be the module +// that supports overclock. // //--- @@ -18,7 +24,12 @@ /* sleep() - Puts the processor to sleep until an interrupt request is issued. + Puts the processor to sleep until an interrupt request is accepted. + This function should be called every time the program because idle + because it doesn't have anything to do -- between two game frames or + while waiting for a keyboard event. + This function is called by getkey_opt(), getkey(), waitevent(), this + module's sleep functions among others. */ void sleep(void); @@ -41,17 +52,29 @@ void sleep_us(int us_delay); // Clock management. //--- -enum ClockUnit +/* + clock_unit_t + Enumerated type used by the time conversion functions. It indicates the + type (delay or frequency) of a parameter. +*/ +typedef enum { - Clock_us = 0, - Clock_ms = 1, - Clock_s = 2, + clock_us = 0, + clock_ms = 1, + clock_s = 2, - Clock_Hz = 10, - Clock_kHz = 11, - Clock_MHz = 12, -}; + clock_Hz = 10, + clock_kHz = 11, + clock_MHz = 12, +} clock_unit_t; + +/* + clock_config_t + A copy of the Clock Pulse Generator (CPG) configuration. Be sure to + check which MPU the program is running on (using ) to access the + right fields. +*/ typedef struct { union @@ -75,9 +98,9 @@ typedef struct int RTCCLK_f; // SH7305 }; - int Bphi_f; - int Iphi_f; - int Pphi_f; + int Bphi_f; // Bus clock frequency + int Iphi_f; // Processor clock frequency + int Pphi_f; // Peripheral clock frequency } clock_config_t; @@ -86,10 +109,8 @@ typedef struct Returns the P_phi / 4 timer setting that will last for the given time. Several units can be used. Be aware that the result is approximate, and very high frequencies or very short delays will yield important errors. - Normally you need not use this function when setting up timers because - timer_start() handles this conversion for you. */ -uint32_t clock_setting(int duration, enum ClockUnit unit); +uint32_t clock_setting(int duration, clock_unit_t unit); /* clock_config() diff --git a/include/ctype.h b/include/ctype.h index 23ceac4..3037ccd 100644 --- a/include/ctype.h +++ b/include/ctype.h @@ -7,78 +7,29 @@ //--- #ifndef _CTYPE_H -#define _CTYPE_H 1 +#define _CTYPE_H #include -//--- -// Character classes. -//--- - extern uint8_t ctype_classes[0x80]; -__attribute__((always_inline)) static inline int isalnum(int c) { - return ctype_classes[c] & 0xf0; -} +// Character classes. +#define isalnum(c) (ctype_classes[(int)(c)] & 0xf0) +#define isalpha(c) (ctype_classes[(int)(c)] & 0x30) +#define iscntrl(c) (ctype_classes[(int)(c)] & 0x01) +#define isdigit(c) (ctype_classes[(int)(c)] & 0x40) +#define isgraph(c) (ctype_classes[(int)(c)] & 0xf4) +#define islower(c) (ctype_classes[(int)(c)] & 0x10) +#define isprint(c) (ctype_classes[(int)(c)] & 0x08) +#define ispunct(c) (ctype_classes[(int)(c)] & 0x04) +#define isspace(c) (ctype_classes[(int)(c)] & 0x02) +#define isupper(c) (ctype_classes[(int)(c)] & 0x20) +#define isxdigit(c) (ctype_classes[(int)(c)] & 0x80) +#define isascii(c) ((unsigned)c <= 0x7f) +#define isblank(c) (c == '\t' || c == ' ') -__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 ((unsigned)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); -} +// Character manipulation. +#define tolower(c) ((c) | isupper(c)) +#define toupper(c) ((c) & ~(islower(c) << 1)) #endif // _CTYPE_H diff --git a/include/display.h b/include/display.h index c5dba6e..924c6dc 100644 --- a/include/display.h +++ b/include/display.h @@ -2,94 +2,107 @@ // // gint drawing module: display // -// Handles vram manipulation and drawing for plain monochrome display. +// This module does most of the monochrome drawing. It manages the video +// memory although image rendering and text rendering, as complex tasks, +// are left to other modules (bopti and tales, respectively). // //--- #ifndef _DISPLAY_H -#define _DISPLAY_H 1 +#define _DISPLAY_H #include #include - //--- -// Heading declarations. +// Drawing-related types and constants. //--- -enum Color -{ - Color_White = 0, - Color_Light = 1, - Color_Dark = 2, - Color_Black = 3, - Color_None = 4, - Color_Invert = 5, -}; - -// This header needs enum Color to be defined. -#include +#define DWIDTH 128 /* Width of the screen */ +#define DHEIGHT 64 /* Height of the screen */ /* - struct Image - This structure holds information about a bitmap encoded with fxconv. - Data is accessed using longword operations, which *requires* many - sizes to be multiples of 4 (structure alignment, data alignment, layer - size, ...). + color_t + Defines all colors that the library knows about: + - white is exactly what you think it is; + - light is a light gray used by the gray module; + - dark is a dark gray, also used by the gray engine; + - black is nothing more than black; (sorry) + - none means transparent, but is shorter to write. + There are also some transformation-associated colors: + - invert reverses the intensity of the color (white -> black, dark -> + light, etc); + - lighten is some kind of partially-transparent white. It lightens the + color which it is drawn onto (black -> dark, light -> light); + - lighten2 is the same as lighten, except it lightens more (black -> + light, light -> white); + - darken is the exact opposite of lighten (light -> dark, black -> + black). + - darken2 is the same to darken as lighten2 to lighten (white -> dark, + dark -> black); + All transformations except invert only operate when the gray engine is + running. */ -struct Image +typedef enum { - uint8_t magic; - uint8_t format; + color_white = 0, + color_light = 1, + color_dark = 2, + color_black = 3, + color_none = 4, - uint8_t width; - uint8_t height; + color_invert = 5, + color_lighten = 6, + color_lighten2 = 7, + color_darken = 8, + color_darken2 = 9, - const uint32_t data[]; +} color_t; -} __attribute__((packed, aligned(4))); -// Useful shorthand for user code. -typedef struct Image Image; +// The bopti module provides bitmap rendering functions. +#include - - -// A few other constants. -#define DISPLAY_WIDTH 128 -#define DISPLAY_HEIGHT 64 +// The tales module provides text rendering functions but requires the color_t +// type definition. +#include //--- -// Generic functions. +// Video RAM management. //--- /* display_getLocalVRAM() - Returns the local video ram address. This function always return the - same address. - The buffer returned by this function should not be used directly when - running the gray engine. + Returns gint's local video RAM address. Gint does not use the system's + buffer because it is misaligned. This function always returns the same + address. Both the display and the gray module heavily use this buffer; + make sure you don't interfere with them if you access it. + This function does not necessarily returns the video ram that is + currently in use; call display_getCurrentVRAM() for this. */ -void *display_getLocalVRAM(void); +uint32_t *display_getLocalVRAM(void); /* display_getCurrentVRAM() - Returns the current video ram. This function usually returns the - parameter of the last call to display_useVRAM(), unless the gray engine - is running (in which case the result is undefined). Returns the local - vram address by default. + Returns the current monochrome video ram buffer. This function usually + returns the parameter of the last call to display_useVRAM(), or the + local vram address (which is default when the library starts). + The return value of this function is undefined if the gray engine is + running. */ -void *display_getCurrentVRAM(void); +uint32_t *display_getCurrentVRAM(void); /* display_useVRAM() - Changes the current video ram address. The argument MUST be a 4- - aligned 1024-byte buffer; otherwise any drawing operation will crash - the program. + Changes the current monochrome video ram address. The argument must be + a 4-aligned 1024-byte buffer because the library's design requires it. + This function refuses misaligned buffers but trusts that enough space + is available; failing to provide enough memory may crash the program. This function will most likely have no effect when running the gray engine. */ -void display_useVRAM(void *vram); +void display_useVRAM(uint32_t *vram); @@ -99,30 +112,24 @@ void display_useVRAM(void *vram); /* dupdate() - Displays the vram on the physical screen. Does nothing when the gray - engine is running. + Pushes the video RAM to the physical screen. This function also works + when the gray engine is running, but that's probably not what you want. */ void dupdate(void); /* dclear() - Clears the whole video ram. + Clears the whole video ram, making all pixels white. */ void dclear(void); /* - dclear_area() - Clears an area of the video ram. Both (x1, y1) and (x2, y2) are - cleared. + drect() + Draws a rectangle on the screen. This function can use any color which + is not associated with the gray engine, including the reverse operator. + Both end points (x1, y1) and (x2, y2) are affected as well. */ -void dclear_area(int x1, int y1, int x2, int y2); - -/* - dreverse_area() - Reverses an area of the vram. (x1, y1) and (x2, y2) are reversed as - well. -*/ -void dreverse_area(int x1, int y1, int x2, int y2); +void drect(int x1, int y1, int x2, int y2, color_t operator); @@ -132,9 +139,10 @@ void dreverse_area(int x1, int y1, int x2, int y2); /* dpixel() - Puts a pixel in the vram. + Changes a pixel's color in the video ram. The result may depend on the + current color of the pixel. */ -void dpixel(int x, int y, enum Color color); +void dpixel(size_t x, size_t y, color_t operator); /* dline() @@ -143,22 +151,6 @@ void dpixel(int x, int y, enum Color color); Uses an algorithm written by PierrotLL for MonochromeLib. */ -void dline(int x1, int y1, int x2, int y2, enum Color color); - -/* - dimage() - Displays a monochrome image in the vram. Does a real lot of - optimization. -*/ -void dimage(int x, int y, struct Image *image); - -/* - dimage_part() - Draws a portion of an image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void dimage_part(int x, int y, struct Image *img, int left, int top, - int width, int height); +void dline(int x1, int y1, int x2, int y2, color_t operator); #endif // _DISPLAY_H diff --git a/include/events.h b/include/events.h index 8c1f285..d88c98e 100644 --- a/include/events.h +++ b/include/events.h @@ -2,7 +2,17 @@ // // gint core module: events // -// Finally some user-friendly API. +// Finally some user-friendly API. This module is in charge of managing +// the event queue. The waitevent() function should be particularly useful +// in program main loops to record key presses and releases in real-time +// games. +// +// Other functions such as the getkey() of the keyboard module provide +// more advanced features such as SHIFT and ALPHA modifiers, backlight +// control for instance; these functions rely on this module and they +// ignore all events that they do not handle. If you want to catch several +// types of events (eg. keyboard and serial communication), then you need +// to use directly this module. // //--- @@ -17,30 +27,24 @@ */ typedef enum { - EventType_None = 0, - ET_None = EventType_None, + // Specific events. + event_none = 0, + event_user = 1, - EventType_User = 1, - ET_User = EventType_User, + // Keyboard events. + event_key_press = 2, + event_key_repeat = 3, + event_key_release = 4, - EventType_KeyPressed = 2, - ET_KeyPress = EventType_KeyPressed, - - EventType_KeyRepeated = 3, - ET_KeyRepeat = EventType_KeyRepeated, - - EventType_KeyReleased = 4, - ET_KeyRel = EventType_KeyReleased, - - EventType_TimerUnderflow = 5, - ET_Timer = EventType_TimerUnderflow, + // Other events. + event_timer_underflow = 5, } event_type_t; /* event_t Wake up, something's going on. The union member that holds information - about the event is implicitly defined by the type attribute. + about the event is specified by the type attribute. */ typedef struct { @@ -48,11 +52,11 @@ typedef struct union { - // For ET_User. + // For event_user. void *data; - // For ET_KeyPress, ET_KeyRepeat and ET_KeyRel. + // For event_key_press, event_key_repeat and event_key_release. int key; - // For ET_Timer. + // For event_timer_underflow. timer_t *timer; }; diff --git a/include/gint.h b/include/gint.h index 85fc84f..887b960 100644 --- a/include/gint.h +++ b/include/gint.h @@ -2,8 +2,10 @@ // // gint core module: interrupt handler // -// Central point of the library. Controls the interrupt handler and -// defines a few functions to configure callbacks for some interrupts. +// This module is the core of the gint library. It controls the interrupt +// handler, allows the user to customize interrupt management, provides +// peripheral register access and some information about the runtime +// environment. // //--- diff --git a/include/gray.h b/include/gray.h index bbae4f0..a4e3233 100644 --- a/include/gray.h +++ b/include/gray.h @@ -7,10 +7,14 @@ //--- #ifndef _GRAY_H -#define _GRAY_H 1 +#define _GRAY_H +#include #include +// This module provides bitmap rendering. +#include + //--- // Engine control. //--- @@ -39,13 +43,13 @@ void gray_stop(void); gray_lightVRAM() Returns the module's light gray vram address. */ -void *gray_lightVRAM(void); +uint32_t *gray_lightVRAM(void); /* gray_darkVRAM() Returns the module's dark gray vram address. */ -void *gray_darkVRAM(void); +uint32_t *gray_darkVRAM(void); /* gray_getDelays() @@ -81,29 +85,26 @@ void gray_setDelays(int light, int dark); /* gupdate() - Swaps the vram buffer sets. + Swaps the vram buffer sets. You need to call this function each time + you finish drawing something in the video ram. Unlike the monochrome + function dupdate(), gupdate() only does a quick operation indicating + that drawing and exposed buffers have been swapped, but nothing on the + screen will change until the gray timer fires. */ void gupdate(void); /* gclear() - Clears the video ram. + Clears the gray video ram, making all pixels white. */ void gclear(void); /* - gclear_area() - Clears an area of the video ram. End points (x1, y1) and (x2, y2) are - included. + grect() + Draws a rectangle in the gray video ram; this function accepts all + values of the color_t type, including gray operators. */ -void gclear_area(int x1, int y1, int x2, int y2); - -/* - greverse_area() - Reverses an area of the vram. End points (x1, y1) and (x2, y2) are - included. -*/ -void greverse_area(int x1, int y1, int x2, int y2); +void grect(int x1, int y1, int x2, int y2, color_t operator); @@ -113,29 +114,18 @@ void greverse_area(int x1, int y1, int x2, int y2); /* gpixel() - Puts a pixel in the vram. + Puts a pixel in the vram. This function accepts all values of the + color_t type, including gray operators. */ -void gpixel(int x, int y, enum Color color); +void gpixel(size_t x, size_t y, color_t operator); /* gline() - Draws a line in the vram. Automatically optimizes special cases. + Draws a line in the vram while automatically optimizing special cases. + This function supports all plain colors from the color_t type, but not + the gray operators. If you need them for horizontal or vertical lines, + you may want to use grect() as a replacement. */ -void gline(int x1, int y1, int x2, int y2, enum Color color); - -/* - gimage() - Displays a gray image in the vram. -*/ -void gimage(int x, int y, struct Image *image); - -/* - gimage_part() - Draws a portion of a gray image, defined by its bounding rectangle. - Point (left, top) is included, but (left + width, top + height) is - excluded. -*/ -void gimage_part(int x, int y, struct Image *image, int left, int top, - int width, int height); +void gline(int x1, int y1, int x2, int y2, color_t operator); #endif // _GRAY_H diff --git a/include/internals/bopti.h b/include/internals/bopti.h index d120db8..31a87e0 100644 --- a/include/internals/bopti.h +++ b/include/internals/bopti.h @@ -15,7 +15,7 @@ //--- #ifndef _INTERNALS_BOPTI_H -#define _INTERNALS_BOPTI_H 1 +#define _INTERNALS_BOPTI_H #include #include @@ -86,7 +86,7 @@ struct Command // The video ram addresses are set by the public functions and used internally // by the module. // Monochrome video ram, light and dark buffers (in this order). -extern int *bopti_vram, *bopti_v1, *bopti_v2; +extern uint32_t *bopti_vram, *bopti_v1, *bopti_v2; @@ -155,6 +155,6 @@ void bopti(const unsigned char *layer, struct Structure *s, struct Command *c); getStructure() Determines the image size and data pointer. */ -void getStructure(struct Image *img, struct Structure *structure); +void getStructure(image_t *img, struct Structure *structure); #endif // _INTERNALS_BOPTI_H diff --git a/include/internals/display.h b/include/internals/display.h index d862f58..8157b5b 100644 --- a/include/internals/display.h +++ b/include/internals/display.h @@ -1,47 +1,40 @@ -//--- -// -// gint drawing module: display -// -// Handles vram manipulation and drawing. -// -//--- - #ifndef _INTERNALS_DISPLAY_H -#define _INTERNALS_DISPLAY_H 1 +#define _INTERNALS_DISPLAY_H #include +#include -extern int *vram; +extern uint32_t *vram; //--- // Rectangle masks. // // The concept of 'rectangle masks' is used several times in this module. -// It is based on the fact that an operation that affects a rectangle acts -// the same on all its lines. Therefore the behavior of the operation is -// determined by its behavior on a single line, which is represented using -// 'masks' whose bits indicate whether a pixel is affected (1) or not (0). +// It relies on the fact that operations affecting a rectangle act the +// same for all lines, and line operation is very optimized. A rectangle +// mask is a set of integers, where each bit indicate whether a specific +// pixel is affected (1) by the operation, or not (0). // -// For example when clearing the screen rectangle (16, 16, 112, 48), the -// masks will represent information '16 to 112 on x-axis', and will hold -// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These -// masks can then be used by setting vram[offset] &= ~masks[i]. This -// appears to be very flexible : for instance, vram[offset] ^= masks[i] -// will reverse the pixels in the same rectangle. -// -// This technique can also be used in more subtle cases with more complex -// patterns, but within this module it is unlikely to happen. +// For example to clear a rectangle such as (14, 16, 112, 48), the masks +// will need to hold 0003ffff ffffffff ffffffff ffff0000. Bitwise- +// combining them with video ram long entries yields very good performance +// as compared to operation on single pixels. Each bitwise operation will +// produce different results, which is very flexible. // +// This technique can also be used in subtle cases with patterns more +// complicated than rectangles, but within this module this is unlikely to +// happen. //--- /* adjustRectangle() Adjusts the given rectangle coordinates to ensure that : - - the rectangle is entirely contained in the screen - - x1 < x2 - - y1 < y2 - which is needed when working with screen rectangles. - Returns non-zero if the rectangle is outside the screen. + - the rectangle is entirely contained in the screen; + - x1 < x2; + - y1 < y2, + which is needed when working with screen rectangles. Returns non-zero + if the rectangle is outside the screen, which usually means there is + nothing to do. */ int adjustRectangle(int *x1, int *y1, int *x2, int *y2); @@ -51,6 +44,6 @@ int adjustRectangle(int *x1, int *y1, int *x2, int *y2); and x2 (both included). The four masks are stored in the third argument (seen as an array). */ -void getMasks(int x1, int x2, uint32_t *masks); +void getMasks(size_t x1, size_t x2, uint32_t *masks); #endif // _INTERNALS_DISPLAY_H diff --git a/include/internals/gint.h b/include/internals/gint.h index 91a07a7..601ff70 100644 --- a/include/internals/gint.h +++ b/include/internals/gint.h @@ -1,11 +1,11 @@ #ifndef _INTERNALS_GINT_H -#define _INTERNALS_GINT_H 1 +#define _INTERNALS_GINT_H #include #include //--- -// Interrupt handlers +// Interrupt handlers. //--- // General exception handler. @@ -18,7 +18,7 @@ void gint_int(void); //--- -// Assembler-level VBR management +// Assembler-level VBR management. //--- /* @@ -37,7 +37,7 @@ void gint_setvbr(uint32_t vbr, void (*setup)(void)); //--- -// Initialization and termination routines +// Initialization and termination routines. //--- /* @@ -58,8 +58,8 @@ void gint_quit(void); Saves many registers into a buffer to ensure that the system is not upset by gint's configuration when the application ends. */ -//void gint_save_7705(gint_save_buffer_t *buffer); -//void gint_save_7305(gint_save_buffer_t *buffer); +// void gint_save_7705(gint_save_buffer_t *buffer); +// void gint_save_7305(gint_save_buffer_t *buffer); /* gint_setup() @@ -74,8 +74,8 @@ void gint_setup_7305(void); Restores the parameters saved in a save buffer to give back the interrupt control to the system. */ -//void gint_restore_7705(gint_save_buffer_t *buffer); -//void gint_restore_7305(gint_save_buffer_t *buffer); +// void gint_restore_7705(gint_save_buffer_t *buffer); +// void gint_restore_7305(gint_save_buffer_t *buffer); /* gint_reg() diff --git a/include/internals/interrupt_maps.h b/include/internals/interrupt_maps.h index ac66baf..a1d716b 100644 --- a/include/internals/interrupt_maps.h +++ b/include/internals/interrupt_maps.h @@ -5,7 +5,7 @@ #include //--- -// Interrupt handlers +// Interrupt handlers. //--- /* @@ -60,7 +60,7 @@ extern gint_interrupt_handler_t gint_handlers[]; //--- -// Interrupt maps +// Interrupt maps. //--- /* diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h index 10bd564..738c1ee 100644 --- a/include/internals/keyboard.h +++ b/include/internals/keyboard.h @@ -2,6 +2,7 @@ #define _INTERNALS_KEYBOARD_H #include +#include #include // Keyboard variables. @@ -10,10 +11,10 @@ extern volatile int interrupt_flag; // Key statistics. extern int repeat_first, repeat_next; -extern int last_key, last_repeats, last_events; +extern int last_key, last_repeats, last_time; -// RTC callback id. -extern unsigned cb_id; +// Virtual timer object. +extern timer_t *vtimer; /* getPressedKey() diff --git a/include/internals/mmu.h b/include/internals/mmu.h index 23aa121..ced7211 100644 --- a/include/internals/mmu.h +++ b/include/internals/mmu.h @@ -8,8 +8,8 @@ // //--- -#ifndef _MMU_H -#define _MMU_H 1 +#ifndef _INTERNALS_MMU_H +#define _INTERNALS_MMU_H /* mmu_pseudoTLBInit() diff --git a/include/internals/stdio.h b/include/internals/stdio.h index bf68b81..67563d4 100644 --- a/include/internals/stdio.h +++ b/include/internals/stdio.h @@ -8,7 +8,7 @@ //--- #ifndef _INTERNALS_STDIO_H -#define _INTERNALS_STDIO_H 1 +#define _INTERNALS_STDIO_H #include #include diff --git a/include/internals/tales.h b/include/internals/tales.h index 40342c1..166f1ad 100644 --- a/include/internals/tales.h +++ b/include/internals/tales.h @@ -1,5 +1,5 @@ #ifndef _INTERNALS_TALES_H -#define _INTERNALS_TALES_H 1 +#define _INTERNALS_TALES_H #include #include @@ -7,13 +7,14 @@ #define OPERATE_ARGS uint32_t *operators, int height, int x, int y extern struct Font *font; -extern enum Color color; +extern color_t operator; /* tales_init() Configures tales with the default font (which is part of gint). */ -void tales_init(void) __attribute__((constructor)); +__attribute__((constructor)) +void tales_init(void); /* getCharacterIndex() diff --git a/include/internals/time.h b/include/internals/time.h index 236dcf6..aef910b 100644 --- a/include/internals/time.h +++ b/include/internals/time.h @@ -1,5 +1,5 @@ #ifndef _INTERNALS_TIME_H -#define _INTERNALS_TIME_H 1 +#define _INTERNALS_TIME_H /* isLeap() diff --git a/include/keyboard.h b/include/keyboard.h index ea27483..f788c13 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -11,7 +11,7 @@ //--- #ifndef _KEYBOARD_H -#define _KEYBOARD_H 1 +#define _KEYBOARD_H #include #include @@ -20,92 +20,87 @@ // Keycodes and related. //--- -// The following codes are gint matrix codes. They are not compatible with the -// system's. - -#define KEY_F1 0x69 -#define KEY_F2 0x59 -#define KEY_F3 0x49 -#define KEY_F4 0x39 -#define KEY_F4 0x39 -#define KEY_F5 0x29 -#define KEY_F6 0x19 - -#define KEY_SHIFT 0x68 -#define KEY_OPTN 0x58 -#define KEY_VARS 0x48 -#define KEY_MENU 0x38 -#define KEY_LEFT 0x28 -#define KEY_UP 0x18 - -#define KEY_ALPHA 0x67 -#define KEY_SQUARE 0x57 -#define KEY_POWER 0x47 -#define KEY_EXIT 0x37 -#define KEY_DOWN 0x27 -#define KEY_RIGHT 0x17 - -#define KEY_XOT 0x66 -#define KEY_LOG 0x56 -#define KEY_LN 0x46 -#define KEY_SIN 0x36 -#define KEY_COS 0x26 -#define KEY_TAN 0x16 - -#define KEY_FRAC 0x65 -#define KEY_FD 0x55 -#define KEY_LEFTP 0x45 -#define KEY_RIGHTP 0x35 -#define KEY_COMMA 0x25 -#define KEY_ARROW 0x15 - -#define KEY_7 0x64 -#define KEY_8 0x54 -#define KEY_9 0x44 -#define KEY_DEL 0x34 -#define KEY_AC_ON 0x24 - -#define KEY_4 0x63 -#define KEY_5 0x53 -#define KEY_6 0x43 -#define KEY_MUL 0x33 -#define KEY_DIV 0x23 - -#define KEY_1 0x62 -#define KEY_2 0x52 -#define KEY_3 0x42 -#define KEY_PLUS 0x32 -#define KEY_MINUS 0x22 - -#define KEY_0 0x61 -#define KEY_DOT 0x51 -#define KEY_EXP 0x41 -#define KEY_NEG 0x31 -#define KEY_EXE 0x21 - -// Key modifiers. -#define MOD_SHIFT 0x80 -#define MOD_ALPHA 0x100 -#define MOD_CLEAR ~(MOD_SHIFT | MOD_ALPHA) - -// Key events. -#define KEY_NONE 0x00 -#define KEY_NOEVENT 0xff - /* - enum KeyboardFrequency - Possible values for the keyboard frequency. + key_t + The following codes are gint matrix codes. They are not compatible with + the system's. Some keycodes are special event codes; all others are + made of a key identifier and possibly one or more modifiers. + Binary-and a keycode with MOD_CLEAR to remove the modifiers; this will + not work with special event codes. */ -enum KeyboardFrequency +typedef enum { - KeyboardFreq_500mHz = RTCFreq_500mHz, - KeyboardFreq_1Hz = RTCFreq_1Hz, - KeyboardFreq_2Hz = RTCFreq_2Hz, - KeyboardFreq_4Hz = RTCFreq_4Hz, - KeyboardFreq_16Hz = RTCFreq_16Hz, - KeyboardFreq_64Hz = RTCFreq_64Hz, - KeyboardFreq_256Hz = RTCFreq_256Hz, -}; + // Special events codes. + KEY_NONE = 0x00, + KEY_NOEVENT = 0xff, + + // Key modifiers. + MOD_SHIFT = 0x80, + MOD_ALPHA = 0x100, + MOD_CLEAR = ~(MOD_SHIFT | MOD_ALPHA), + + // Key identifiers. + + KEY_F1 = 0x69, + KEY_F2 = 0x59, + KEY_F3 = 0x49, + KEY_F4 = 0x39, + KEY_F5 = 0x29, + KEY_F6 = 0x19, + + KEY_SHIFT = 0x68, + KEY_OPTN = 0x58, + KEY_VARS = 0x48, + KEY_MENU = 0x38, + KEY_LEFT = 0x28, + KEY_UP = 0x18, + + KEY_ALPHA = 0x67, + KEY_SQUARE = 0x57, + KEY_POWER = 0x47, + KEY_EXIT = 0x37, + KEY_DOWN = 0x27, + KEY_RIGHT = 0x17, + + KEY_XOT = 0x66, + KEY_LOG = 0x56, + KEY_LN = 0x46, + KEY_SIN = 0x36, + KEY_COS = 0x26, + KEY_TAN = 0x16, + + KEY_FRAC = 0x65, + KEY_FD = 0x55, + KEY_LEFTP = 0x45, + KEY_RIGHTP = 0x35, + KEY_COMMA = 0x25, + KEY_ARROW = 0x15, + + KEY_7 = 0x64, + KEY_8 = 0x54, + KEY_9 = 0x44, + KEY_DEL = 0x34, + KEY_AC_ON = 0x24, + + KEY_4 = 0x63, + KEY_5 = 0x53, + KEY_6 = 0x43, + KEY_MUL = 0x33, + KEY_DIV = 0x23, + + KEY_1 = 0x62, + KEY_2 = 0x52, + KEY_3 = 0x42, + KEY_PLUS = 0x32, + KEY_MINUS = 0x22, + + KEY_0 = 0x61, + KEY_DOT = 0x51, + KEY_EXP = 0x41, + KEY_NEG = 0x31, + KEY_EXE = 0x21, + +} key_t; @@ -114,23 +109,31 @@ enum KeyboardFrequency //--- /* - keyboard_setFrequency() - Sets the keyboard frequency. The default frequency is 16 Hz. Very few - applications will need to change this setting. - At low frequencies, you will miss key hits. At high frequencies, you - may lose execution power. + keyboard_setAnalysisDelay() + Sets the keyboard analysis delay, that is, the delay (in ms) between + two keyboard analyzes. If a key is pressed then released in the lapse + between two analyzes, the program won't notice anything. On the other + hand, if the program spends too much time reading the keyboard, it will + lose a bit of execution power. + The default frequency is about 40 Hz; very few programs will need to + change this setting. Please note that the repeat delays should be + multiples of the analysis delay for better accuracy. */ -void keyboard_setFrequency(enum KeyboardFrequency frequency); +void keyboard_setAnalysisDelay(int analysis_delay_ms); /* keyboard_setRepeatRate() Sets the default repeat rate for key events. The delay before the first repeat may have a different value (usually longer). The unit for the - argument is the keyboard period. For example at 32 Hz, values of - (20, 4) will imitate the system default. - Set to 0 to disable repetition. If first = 0, no repetition will be - allowed. If first != 0 and next = 0, only one repetition will be - allowed. + argument is ms, but the repeat events themselves may only be fired when + a keyboard analysis is performed; which means that for better accuracy, + these delays should be a multiple of the keyboard period. The keyboard + period may be changed by calling keyboard_setAnalysisDelay(). + For instance, delays of (625 ms, 125 ms) will imitate the system's + default setting. + You can disable repetitions by passing 0 as arguments: + - if first = 0, no repetition will ever occur; + - if first != 0 and next = 0, only one repetition will occur. */ void keyboard_setRepeatRate(int first, int next); @@ -141,57 +144,74 @@ void keyboard_setRepeatRate(int first, int next); //--- /* - enum GetKeyOpt - Options available for use with getkey_opt(). + getkey_opt_t + Options available to customize the behavior of the getkey_opt() + function. */ -enum GetkeyOpt +typedef enum { - Getkey_NoOption = 0x00, + getkey_none = 0x00, - // Consider [SHIFT] and [ALPHA] as modifiers instead of returning - // KEY_SHIFT and KEY_ALPHA. - Getkey_ShiftModifier = 0x01, - Getkey_AlphaModifier = 0x02, + // Consider [SHIFT] and [ALPHA] as modifiers. Returns key identifiers + // with MOD_SHIFT and MOD_ALPHA flags instead of returning KEY_SHIFT + // and KEY_ALPHA. + getkey_shift_modifier = 0x01, + getkey_alpha_modifier = 0x02, - // Allow changing the backlight status on [SHIFT] + [OPTN]. - Getkey_ManageBacklight = 0x04, + // Allow changing the backlight status on [SHIFT] + [OPTN] on + // compatible models. + getkey_manage_backlight = 0x04, - // Key repetition. Notice that modifiers will never be repeated. - Getkey_RepeatArrowKeys = 0x10, - Getkey_RepeatCharKeys = 0x20, - Getkey_RepeatCtrlKeys = 0x40, - Getkey_RepeatFuncKeys = 0x80, + // Allow key repetition. This option does not control the generation of + // repeat events (use keyboard_setRepeatRate() for this) but filters + // them. Please note that modifiers will never be repeated, even when + // pressed continuously. + getkey_repeat_arrow_keys = 0x10, + getkey_repeat_char_keys = 0x20, + getkey_repeat_ctrl_keys = 0x40, + getkey_repeat_func_keys = 0x80, // Shorthand for the four previous properties. - Getkey_RepeatAllKeys = 0xf0, -}; + getkey_repeat_all_keys = 0xf0, + +} getkey_option_t; /* getkey() - Blocking function with auto-repeat and SHIFT modifying functionalities. - Reproduces the behavior of the system's GetKey(). Returns the matrix - code with a possible MOD_SHIFT bit. + Blocking function with auto-repeat that heeds for the SHIFT and ALPHA + modifiers. In short, this function reproduces the behavior of the + system's GetKey() function. It returns a matrix code, possibly with + modifier bits. + This function does not return until a key is pressed. */ int getkey(void); /* getkey_opt() - Enhances getkey() with most general functionalities. An OR-combination - of options may be given as first argument. - If max_cycles is non-zero and positive, getkey_opt() will return - KEY_NOEVENT if no event occurs during max_cycle analyzes. + Enhances getkey() with more general functionalities. An OR-combination + of options of type getkey_option_t may be given as first argument. + If delay is non-zero and positive, getkey_opt() will return KEY_NOEVENT + if no event occurs during the given delay. Please note that this + function can only ever return after a keyboard analysis is performed; + the actual delay may exceed the requested time if it's not a multiple + of the keyboard period (which can be changed by calling + keyboard_setAnalysisDelay()). Like getkey(), returns the pressed key matrix code, possibly with modifiers depending on the options. */ -int getkey_opt(enum GetkeyOpt options, int max_cycles); +int getkey_opt(getkey_option_t options, int delay_ms); /* multigetkey() Listens the keyboard for simultaneous key hits. This functions fills - array `keys` with `count` keycodes, adding KEY_NONE at the end if - less than `count` keys are pressed. - If `max_cycles` is non-zero and nothing happens after `max_cycles` - cycles, this function returns an array of KEY_NONE. + the 'keys' array with 'count' keycodes, padding with KEY_NONE values at + the end if less that 'count' keys are detected. + If 'delay_ms' is positive and nothing happens during this delay, this + function returns an array of KEY_NONE. Please note that the delay + detection suffers the same limitation as getkey_opt(). + + This function suffers from severe limitations and may not be very + convenient to use. For more accuracy, consider using the event system. WARNING: Because of hardware limitations, this function generally yields poor @@ -202,7 +222,7 @@ int getkey_opt(enum GetkeyOpt options, int max_cycles); The results are guaranteed to be exact if two keys or less are pressed. With three keys or more, column effects (on SH4) and rectangle effects (on both platforms) mess up the results by making this function think - that some keys, which are actually unpressed, are pressed. + that some keys, which are actually released, are pressed. This function is designed to make combinations of one or two arrow keys with another key as viable as possible. On SH4, this works pretty well @@ -215,25 +235,26 @@ int getkey_opt(enum GetkeyOpt options, int max_cycles); incorrect results. Please do not expect multigetkey() to work as an ideal multi-key analyzer. */ -void multigetkey(int *keys, int count, int max_cycles); +void multigetkey(int *keys, int count, int delay_ms); /* - keylast() - Returns the matrix code of the last pressed key. If repeat_count is - non-NULL, it is set to the number of repetitions. -*/ -int keylast(int *repeat_count); + keyboard_stateBuffer() -/* - keystate() Returns the address of the keyboard state array. The keyboard state consists in 10 bytes, in which every key is represented as a bit. The returned address is the original buffer address. You should avoid editing the array. It wouldn't influence the behavior of the keyboard - functions, but the buffer data is very volatile. Therefore, data - written to the buffer could be replaced anytime. + functions, but the buffer data is very volatile and any data written to + it could be replaced anytime without prior notice. + + If the user wishes to do really advanced keyboard management that they + can't achieve it using the library, they can access this buffer. + Updates of this buffer's contents can be detected by watching the + 'interrupt_flag' variable defined in internals/keyboard.h. However, the + library will continue firing events so the user needs to catch them and + ignore them. */ -volatile uint8_t *keystate(void); +volatile uint8_t *keyboard_stateBuffer(void); @@ -241,32 +262,61 @@ volatile uint8_t *keystate(void); // Key analysis. //--- -enum KeyType -{ - KeyType_Arrow = 1, - KeyType_Character = 2, - KeyType_Control = 4, - KeyType_Function = 8, -}; - /* keyid() - Returns a non-matrix key code that can be used for array subscript. - Ignores modifiers. + Transforms a key identifier and returns a key code that is more + convenient for array subscript that the original matrix codes. The new + codes are laid out the following way: + + +0 +1 +2 +3 +4 +5 + ------------------------------------ + +0 | F1 F2 F3 F4 F5 F6 + +6 | SHIFT OPTN VARS MENU Left Top + +12 | ALPHA x^2 ^ EXIT Down Right + +18 | X,O,T log ln sin cos tan + +24 | Frac F<>D ( ) , -> + +30 | 7 8 9 DEL AC/ON + +36 | 4 5 6 * / + +42 | 1 2 3 + - + +48 | 0 . x10^ (-) EXE + + The returned key code is the sum of the line and column headings. For + instance key_id(KEY_SIN) would be 18 + 3 = 21. Please note that there + are a few holes in the numbering. + This function ignores modifiers and returns -1 on error. */ -int keyid(int key); +int key_id(int matrix_key); /* - keychar() - Returns the ASCII character associated with a character key; 0 for - other keys. + key_char() + Returns the ASCII character associated with a character key, and 0 for + other keys. This function expects a matrix code and not a key_id() + code, and heeds for the ALPHA modifier. */ -int keychar(int key); +int key_char(int matrix_key); /* - keytype() - Returns a key's type. Ignores modifiers. + key_type_t + Categorizes the keyboard's keys into several types: + - Arrow keys only include the REPLAY pad; + - Function keys only include the F1 .. F6 keys; + - Character keys are those which input characters; + - Control characters are all others. */ -enum KeyType keytype(int key); +typedef enum +{ + key_type_arrow = 1, + key_type_character = 2, + key_type_control = 4, + key_type_function = 8, + +} key_type_t; + +/* + key_type() + Returns a key's type. This functions ignores modifiers and expects + matrix codes as argument, not key_id() codes. +*/ +key_type_t key_type(int matrix_key); #endif // _KEYBOARD_H diff --git a/include/mpu.h b/include/mpu.h index e789cdd..dd183bc 100644 --- a/include/mpu.h +++ b/include/mpu.h @@ -27,7 +27,7 @@ //--- #ifndef _MPU_H -#define _MPU_H 1 +#define _MPU_H /* mpu_t diff --git a/include/rtc.h b/include/rtc.h index e5ca263..771ac2c 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -7,7 +7,7 @@ //--- #ifndef _RTC_H -#define _RTC_H 1 +#define _RTC_H #include diff --git a/include/screen.h b/include/screen.h index 7f7e48c..ec459a5 100644 --- a/include/screen.h +++ b/include/screen.h @@ -12,7 +12,7 @@ //--- #ifndef _SCREEN_H -#define _SCREEN_H 1 +#define _SCREEN_H /* screen_display() diff --git a/include/setjmp.h b/include/setjmp.h index 54dceea..d9ad7d8 100644 --- a/include/setjmp.h +++ b/include/setjmp.h @@ -8,7 +8,7 @@ //--- #ifndef _SETJMP_H -#define _SETJMP_H 1 +#define _SETJMP_H // There are 16 CPU registers that *must* be saved to ensure a basically // safe jump. diff --git a/include/stdio.h b/include/stdio.h index f19ecdb..ba27de6 100644 --- a/include/stdio.h +++ b/include/stdio.h @@ -8,7 +8,7 @@ //--- #ifndef _STDIO_H -#define _STDIO_H 1 +#define _STDIO_H #include #include diff --git a/include/stdlib.h b/include/stdlib.h index 5904525..59b6272 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -8,7 +8,7 @@ //--- #ifndef _STDLIB_H -#define _STDLIB_H 1 +#define _STDLIB_H //--- // Common definitions. diff --git a/include/string.h b/include/string.h index 9fdbf2a..70ac65a 100644 --- a/include/string.h +++ b/include/string.h @@ -8,7 +8,7 @@ //--- #ifndef _STRING_H -#define _STRING_H 1 +#define _STRING_H #include diff --git a/include/tales.h b/include/tales.h index 501e263..42249ef 100644 --- a/include/tales.h +++ b/include/tales.h @@ -8,7 +8,7 @@ //--- #ifndef _TALES_H -#define _TALES_H 1 +#define _TALES_H #include #include @@ -95,7 +95,7 @@ typedef struct Font Font; Sets the font and color to use for subsequent text operations. Pass font = NULL to use the default font. */ -void text_configure(struct Font *font, enum Color color); +void text_configure(struct Font *font, color_t operator); /* dtext() diff --git a/include/time.h b/include/time.h index 107e4fc..8833524 100644 --- a/include/time.h +++ b/include/time.h @@ -7,7 +7,7 @@ //--- #ifndef _TIME_H -#define _TIME_H 1 +#define _TIME_H #include diff --git a/include/timer.h b/include/timer.h index 3028e62..f6cad24 100644 --- a/include/timer.h +++ b/include/timer.h @@ -8,7 +8,7 @@ //--- #ifndef _TIMER_H -#define _TIMER_H 1 +#define _TIMER_H #include #include @@ -49,6 +49,16 @@ typedef struct timer_t timer_t; */ timer_t *timer_create(int delay_ms, int repeats); +/* + timer_reload() + Changes a virtual timer's delay. The timer is not stopped nor started: + it keeps running or waiting. Events that were waiting to be handled are + dropped and the number of repeats left is not changed. The timer + restarts counting from 0 regardless of how much time had elapsed since + it last fired. +*/ +void timer_reload(timer_t *timer, int new_ms_delay); + /* timer_destroy() Destroys a virtual timer. This virtual timer pointer becomes invalid diff --git a/src/bopti/bopti_internals.c b/src/bopti/bopti_internals.c index 4b6503f..c42d128 100644 --- a/src/bopti/bopti_internals.c +++ b/src/bopti/bopti_internals.c @@ -1,7 +1,7 @@ #include // Monochrome video ram, light and dark buffers (in this order). -int *bopti_vram, *bopti_v1, *bopti_v2; +uint32_t *bopti_vram, *bopti_v1, *bopti_v2; /* bopti_op() @@ -285,7 +285,7 @@ void bopti(const unsigned char *layer, struct Structure *s, struct Command *c) getStructure() Determines the image size and data pointer. */ -void getStructure(struct Image *img, struct Structure *s) +void getStructure(image_t *img, struct Structure *s) { int column_count, end, end_bytes, layer; diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c index 104aee8..3d05d72 100644 --- a/src/bopti/dimage.c +++ b/src/bopti/dimage.c @@ -7,8 +7,8 @@ Point (left, top) is included, but (left + width, top + height) is excluded. */ -void dimage_part(int x, int y, struct Image *img, int left, int top, - int width, int height) +void dimage_part(int x, int y, image_t *img, int left, int top, int width, + int height) { if(!img || img->magic != 0x01) return; @@ -70,7 +70,7 @@ void dimage_part(int x, int y, struct Image *img, int left, int top, dimage() Displays a monochrome image in the video ram. */ -void dimage(int x, int y, struct Image *img) +void dimage(int x, int y, image_t *img) { dimage_part(x, y, img, 0, 0, -1, -1); } diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c index 1bd4b5c..c6c51c0 100644 --- a/src/bopti/gimage.c +++ b/src/bopti/gimage.c @@ -8,8 +8,8 @@ Point (left, top) is included, but (left + width, top + height) is excluded. */ -void gimage_part(int x, int y, struct Image *img, int left, int top, - int width, int height) +void gimage_part(int x, int y, image_t *img, int left, int top, int width, + int height) { if(!img || img->magic != 0x01) return; @@ -71,7 +71,7 @@ void gimage_part(int x, int y, struct Image *img, int left, int top, gimage() Displays a gray image in the video ram. */ -void gimage(int x, int y, struct Image *img) +void gimage(int x, int y, image_t *img) { gimage_part(x, y, img, 0, 0, -1, -1); } diff --git a/src/clock/clock.c b/src/clock/clock.c index 8a14274..9da10f8 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -19,7 +19,7 @@ static clock_config_t conf = { Several units can be used. Be aware that the result is approximate, and very high frequencies or very short delays will yield important errors. */ -uint32_t clock_setting(int duration, enum ClockUnit unit) +uint32_t clock_setting(int duration, clock_unit_t unit) { if(conf.Pphi_f <= 0) return 0xffffffff; uint64_t f = conf.Pphi_f >> 2; @@ -27,23 +27,23 @@ uint32_t clock_setting(int duration, enum ClockUnit unit) switch(unit) { - case Clock_us: + case clock_us: result = (duration * f) / 1000000; break; - case Clock_ms: + case clock_ms: result = (duration * f) / 1000; break; - case Clock_s: + case clock_s: result = (duration * f); break; - case Clock_Hz: + case clock_Hz: result = f / duration; break; - case Clock_kHz: + case clock_kHz: result = f / (duration * 1000); break; - case Clock_MHz: + case clock_MHz: result = f / (duration * 1000000); break; @@ -103,7 +103,7 @@ void sleep_ms(int ms_delay) void sleep_us(int us_delay) { volatile int sleep_done = 0; - const uint32_t constant = clock_setting(us_delay, Clock_us); + const uint32_t constant = clock_setting(us_delay, clock_us); timer_t *timer = htimer_setup(timer_user, constant, timer_Po_4, 1); timer_attach(timer, sleep_callback, (void *)&sleep_done); diff --git a/src/core/exceptions.c b/src/core/exceptions.c index ca57cc0..9491221 100644 --- a/src/core/exceptions.c +++ b/src/core/exceptions.c @@ -37,7 +37,7 @@ static void show_error(const char *name, uint32_t *access_mode, uint32_t *tea, __asm__("stc ssr, %0" : "=rm"(ssr)); dclear(); - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); print(3, 1, "EXCEPTION RAISED!"); for(int i = 0; i < 36; i++) vram[i] = ~vram[i]; diff --git a/src/ctype/ctype_classes.c b/src/ctype/ctype_classes.c new file mode 100644 index 0000000..88ff792 --- /dev/null +++ b/src/ctype/ctype_classes.c @@ -0,0 +1,43 @@ +#include + +// Let's save up some space and readability (That's Cake's idea, its a bit of a +// preprocessor trick but a rather nice trick). +#define r4(x) (x), (x), (x), (x) +#define r5(x) r4(x), (x) +#define r6(x) r5(x), (x) +#define r7(x) r6(x), (x) +#define r9(x) r7(x), (x), (x) +#define r10(x) r6(x), r4(x) +#define r15(x) r10(x), r4(x), (x) +#define r18(x) r9(x), r9(x) +#define r20(x) r10(x), r10(x) + +enum { + cntrl = 0x01, + space = 0x02, + punct = 0x04, + print = 0x08, + upper = 0x20, + lower = 0x10, + digit = 0x40, + xdigt = 0x80, +}; + +uint8_t ctype_classes[0x80] = { + // Control characters. + r9(cntrl), r5(cntrl | space), r18(cntrl), + // Space and some punctuation. + space | print, r15(punct | print), + // Decimal digits. + r10(digit | xdigt | print), + // Some punctuation. + r7(punct | print), + // Uppercase alphabet. + r6(upper | xdigt | print), r20(upper | print), + // Other punctuation symbols. + r6(punct | print), + // Lowercase alphabet. + r6(lower | xdigt | print), r20(lower | print), + // Last punctuation characters and DEL. + r4(punct | print), cntrl, +}; diff --git a/src/ctype/ctype_functions.c b/src/ctype/ctype_functions.c new file mode 100644 index 0000000..f4697ac --- /dev/null +++ b/src/ctype/ctype_functions.c @@ -0,0 +1,71 @@ +//--- +// Character type functions. +// Normally this functions need not be linked because there are macros to +// optimize performance, but we still need them to get some pointers. +//--- + +// We don't want to include because it defines all the macros... +#include +extern uint8_t ctype_classes[0x80]; + +#define _inline __attribute__((always_inline)) inline + +_inline int isalnum(int c) { + return ctype_classes[c] & 0xf0; +} + +_inline int isalpha(int c) { + return ctype_classes[c] & 0x30; +} + +_inline int iscntrl(int c) { + return ctype_classes[c] & 0x01; +} + +_inline int isdigit(int c) { + return ctype_classes[c] & 0x40; +} + +_inline int isgraph(int c) { + return ctype_classes[c] & 0xf4; +} + +_inline int islower(int c) { + return ctype_classes[c] & 0x10; +} + +_inline int isprint(int c) { + return ctype_classes[c] & 0x08; +} + +_inline int ispunct(int c) { + return ctype_classes[c] & 0x04; +} + +_inline int isspace(int c) { + return ctype_classes[c] & 0x02; +} + +_inline int isupper(int c) { + return ctype_classes[c] & 0x20; +} + +_inline int isxdigit(int c) { + return ctype_classes[c] & 0x80; +} + +_inline int isascii(int c) { + return ((unsigned)c <= 0x7f); +} + +_inline int isblank(int c) { + return (c == '\t' || c == ' '); +} + +_inline int tolower(int c) { + return c | isupper(c); +} + +_inline int toupper(int c) { + return c & ~(islower(c) << 1); +} diff --git a/src/display/dclear.c b/src/display/dclear.c index ed45a6d..6fadb5a 100644 --- a/src/display/dclear.c +++ b/src/display/dclear.c @@ -3,10 +3,30 @@ /* dclear() - Clears the whole vram. + Clears the whole vram, making all pixels white. */ void dclear(void) { - int i; - for(i = 0; i < 256; i++) vram[i] = 0; + // I tend to use pre-decrement more than post-increment. + uint32_t *index = vram + 256; + + while(index > vram) + { + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + *--index = 0; + } } diff --git a/src/display/dclear_area.c b/src/display/dclear_area.c deleted file mode 100644 index 4cb4a70..0000000 --- a/src/display/dclear_area.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -/* - dclear_area() - Clears an area of the vram using rectangle masks. Both (x1, y1) and - (x2, y2) are cleared. -*/ -void dclear_area(int x1, int y1, int x2, int y2) -{ - uint32_t masks[4]; - if(adjustRectangle(&x1, &y1, &x2, &y2)) return; - getMasks(x1, x2, masks); - - int begin = y1 << 2; - int end = (y2 + 1) << 2; - int i; - - for(i = 0; i < 4; i++) masks[i] = ~masks[i]; - for(i = begin; i < end; i++) vram[i] &= masks[i & 3]; -} diff --git a/src/display/display_vram.c b/src/display/display_vram.c index 2dc3763..34ab168 100644 --- a/src/display/display_vram.c +++ b/src/display/display_vram.c @@ -1,43 +1,48 @@ #include -// Program video ram. It resides in .bss section, therefore it is cleared at +// Program video ram. It resides in BSS section, therefore it is cleared at // program initialization and stripped from the executable file. -static int local_vram[256]; -int *vram = local_vram; +static uint32_t local_vram[256]; +uint32_t *vram = local_vram; /* display_getLocalVRAM() - Returns the local video ram address. This function always return the - same address. - The buffer returned by this function should not be used directly when - running the gray engine. + Returns gint's local video RAM address. Gint does not use the system's + buffer because it is misaligned. This function always returns the same + address. Both the display and the gray module heavily use this buffer; + make sure you don't interfere with them if you access it. + This function does not necessarily returns the video ram that is + currently in use; call display_getCurrentVRAM() for this. */ -inline void *display_getLocalVRAM(void) +inline uint32_t *display_getLocalVRAM(void) { - return (void *)local_vram; + return local_vram; } /* display_getCurrentVRAM() - Returns the current video ram. This function usually returns the - parameter of the last call to display_useVRAM(), unless the gray engine - is running (in which case the result is undefined). Returns the local - vram address by default. + Returns the current monochrome video ram buffer. This function usually + returns the parameter of the last call to display_useVRAM(), or the + local vram address (which is default when the library starts). + The return value of this function is undefined if the gray engine is + running. */ -inline void *display_getCurrentVRAM(void) +inline uint32_t *display_getCurrentVRAM(void) { - return (void *)vram; + return vram; } /* display_useVRAM() - Changes the current video ram address. The argument MUST be a 4- - aligned 1024-byte buffer ; otherwise any drawing operation will crash - the program. + Changes the current monochrome video ram address. The argument must be + a 4-aligned 1024-byte buffer because the library's design requires it. + This function refuses misaligned buffers but trusts that enough space + is available; failing to provide enough memory may crash the program. This function will most likely have no effect when running the gray engine. */ -inline void display_useVRAM(void *ptr) +inline void display_useVRAM(uint32_t *ptr) { - vram = (int *)ptr; + if((intptr_t)ptr & 3) return; + vram = ptr; } diff --git a/src/display/dline.c b/src/display/dline.c index e6e1422..95d57b6 100644 --- a/src/display/dline.c +++ b/src/display/dline.c @@ -1,38 +1,41 @@ #include #include -#define sgn(x) ((x) < 0 ? -1 : 1) -#define abs(x) ((x) < 0 ? -(x) : (x)) -#define rnd(x) ((int)((x) + 0.5)) - /* - dline() - Draws a line on the screen. Automatically optimizes horizontal and - vertical lines. + dhline() + Optimized procedure for drawing an horizontal line. Uses a rectangle + mask. */ - -static void dhline(int x1, int x2, int y, enum Color color) +static void dhline(size_t x1, size_t x2, int y, color_t operator) { uint32_t masks[4]; - int offset = y << 2; - int i; + uint32_t *video = vram + (y << 2) + 4; // Swapping x1 and x2 if needed. if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2; getMasks(x1, x2, masks); - switch(color) + switch(operator) { - case Color_White: - for(i = 0; i < 4; i++) vram[offset + i] &= ~masks[i]; + case color_white: + *--video &= ~masks[3]; + *--video &= ~masks[2]; + *--video &= ~masks[1]; + *--video &= ~masks[0]; break; - case Color_Black: - for(i = 0; i < 4; i++) vram[offset + i] |= masks[i]; + case color_black: + *--video |= masks[3]; + *--video |= masks[2]; + *--video |= masks[1]; + *--video |= masks[0]; break; - case Color_Invert: - for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i]; + case color_invert: + *--video ^= masks[3]; + *--video ^= masks[2]; + *--video ^= masks[1]; + *--video ^= masks[0]; break; default: @@ -40,24 +43,30 @@ static void dhline(int x1, int x2, int y, enum Color color) } } -static void dvline(int y1, int y2, int x, enum Color color) +/* + dvline() + Optimized procedure for drawing a vertical line. This one is far less + powerful than dhline() because the video ram is essentially line- + oriented. It also uses a mask. +*/ +static void dvline(int y1, int y2, int x, color_t operator) { - int offset = (y1 << 2) + (x >> 5); - int end = (y2 << 2) + (x >> 5); - int mask = 0x80000000 >> (x & 31); + uint32_t *base = vram + (y1 << 2) + (x >> 5); + uint32_t *video = vram + (y2 << 2) + (x >> 5) + 4; + uint32_t mask = 0x80000000 >> (x & 31); - switch(color) + switch(operator) { - case Color_White: - while(offset <= end) vram[offset] &= ~mask, offset += 4; + case color_white: + while(video > base) video -= 4, *video &= ~mask; break; - case Color_Black: - while(offset <= end) vram[offset] |= mask, offset += 4; + case color_black: + while(video > base) video -= 4, *video |= mask; break; - case Color_Invert: - while(offset <= end) vram[offset] ^= mask, offset += 4; + case color_invert: + while(video > base) video -= 4, *video ^= mask; break; default: @@ -65,19 +74,28 @@ static void dvline(int y1, int y2, int x, enum Color color) } } -void dline(int x1, int y1, int x2, int y2, enum Color color) +#define sgn(x) ((x) < 0 ? -1 : 1) +#define abs(x) ((x) < 0 ? -(x) : (x)) + +/* + dline() + Line drawing algorithm more or less directly taken for MonochromeLib. + Thanks PierrotLL for this. Relies on dhline() and dvline() for specific + cases. +*/ +void dline(int x1, int y1, int x2, int y2, color_t operator) { if(adjustRectangle(&x1, &y1, &x2, &y2)) return; // Possible optimizations. if(y1 == y2) { - dhline(x1, x2, y1, color); + dhline(x1, x2, y1, operator); return; } if(x1 == x2) { - dvline(y1, y2, x1, color); + dvline(y1, y2, x1, operator); return; } @@ -87,7 +105,7 @@ void dline(int x1, int y1, int x2, int y2, enum Color color) dx = abs(dx), dy = abs(dy); - dpixel(x1, y1, color); + dpixel(x1, y1, operator); if(dx >= dy) { @@ -97,7 +115,7 @@ void dline(int x1, int y1, int x2, int y2, enum Color color) x += sx; cumul += dy; if(cumul > dx) cumul -= dx, y += sy; - dpixel(x, y, color); + dpixel(x, y, operator); } } else @@ -108,9 +126,9 @@ void dline(int x1, int y1, int x2, int y2, enum Color color) y += sy; cumul += dx; if(cumul > dy) cumul -= dy, x += sx; - dpixel(x, y, color); + dpixel(x, y, operator); } } - dpixel(x2, y2, color); + dpixel(x2, y2, operator); } diff --git a/src/display/dpixel.c b/src/display/dpixel.c index 60c3a06..8423bff 100644 --- a/src/display/dpixel.c +++ b/src/display/dpixel.c @@ -3,30 +3,23 @@ /* dpixel() - Puts a pixel in the vram. + Changes a pixel's color in the video ram. The result may depend on the + current color of the pixel. */ -void dpixel(int x, int y, enum Color color) +void dpixel(size_t x, size_t y, color_t operator) { - if((unsigned int)x > 127 || (unsigned int)y > 63) return; + // Let's be honest, all this module's code *heavily* relies on the + // screen dimension in the end, so it's not that big a deal. + if(x > 127 || y > 63) return; - int offset = (y << 2) + (x >> 5); - int mask = 0x80000000 >> (x & 31); + uint32_t *video = vram + (y << 2) + (x >> 5); + uint32_t mask = 0x80000000 >> (x & 31); - switch(color) + switch(operator) { - case Color_White: - vram[offset] &= ~mask; - break; - - case Color_Black: - vram[offset] |= mask; - break; - - case Color_Invert: - vram[offset] ^= mask; - break; - - default: - break; + case color_white: *video &= ~mask; break; + case color_black: *video |= mask; break; + case color_invert: *video ^= mask; break; + default: return; } } diff --git a/src/display/drect.c b/src/display/drect.c new file mode 100644 index 0000000..5e4d02b --- /dev/null +++ b/src/display/drect.c @@ -0,0 +1,59 @@ +#include +#include + +/* + drect() + Draws a rectangle on the screen. This function can use any color which + is not associated with the gray engine, including the reverse operator. +*/ +void drect(int x1, int y1, int x2, int y2, color_t operator) +{ + // Avoid wasting time if the requested operation is invalid here. + if(operator != color_white && operator != color_black + && operator != color_invert) return; + // Make sure the coordinates are in the right order, and that the + // requested rectangle crosses the screen. + if(adjustRectangle(&x1, &y1, &x2, &y2)) return; + + uint32_t masks[4]; + getMasks(x1, x2, masks); + + uint32_t *base = vram + (y1 << 2); + uint32_t *video = vram + (y2 << 2) + 4; + + switch(operator) + { + case color_white: + while(video > base) + { + *--video &= ~masks[3]; + *--video &= ~masks[2]; + *--video &= ~masks[1]; + *--video &= ~masks[0]; + } + break; + + case color_black: + while(video > base) + { + *--video |= masks[3]; + *--video |= masks[2]; + *--video |= masks[1]; + *--video |= masks[0]; + } + break; + + case color_invert: + while(video > base) + { + *--video ^= masks[3]; + *--video ^= masks[2]; + *--video ^= masks[1]; + *--video ^= masks[0]; + } + break; + + // Avoid some warnings. + default: return; + } +} diff --git a/src/display/dreverse_area.c b/src/display/dreverse_area.c deleted file mode 100644 index 0288cbe..0000000 --- a/src/display/dreverse_area.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include - -/* - dreverse_area() - Reverses an area of the vram. This function is a simple application of - the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as - well. -*/ -void dreverse_area(int x1, int y1, int x2, int y2) -{ - uint32_t masks[4]; - if(adjustRectangle(&x1, &y1, &x2, &y2)) return; - getMasks(x1, x2, masks); - - int begin = y1 << 2; - int end = (y2 + 1) << 2; - int i; - - for(i = begin; i < end; i++) vram[i] ^= masks[i & 3]; -} diff --git a/src/display/getMasks.c b/src/display/getMasks.c index 6f3091c..740d04b 100644 --- a/src/display/getMasks.c +++ b/src/display/getMasks.c @@ -6,18 +6,18 @@ and x2 (both included). The four masks are stored in the third argument (seen as an array). */ -void getMasks(int x1, int x2, uint32_t *masks) +void getMasks(size_t x1, size_t x2, uint32_t *masks) { - // Indexes of the first and last longs that are non-blank. - int l1 = x1 >> 5; - int l2 = x2 >> 5; - int i = 0; + // Indexes of the first and last longs that are not empty. + size_t l1 = x1 >> 5; + size_t l2 = x2 >> 5; + size_t i = 0; // Setting the base masks. Those are the final values, except for the - // longs with indexes l1 and l2, that still need to be adjusted. - while(i < l1) masks[i++] = 0x00000000; - while(i <= l2) masks[i++] = 0xffffffff; - while(i < 4) masks[i++] = 0x00000000; + // longs between indexes l1 and l2, that still need to be adjusted. + while(i < l1) masks[i++] = 0x00000000; + while(i <= l2) masks[i++] = 0xffffffff; + while(i < 4) masks[i++] = 0x00000000; // Removing the long number information in x1 and x2 (that is, the // multiples of 32) to keep only the interesting information -- the diff --git a/src/events/event_get.c b/src/events/event_get.c index 5b2e707..62b4c7f 100644 --- a/src/events/event_get.c +++ b/src/events/event_get.c @@ -10,7 +10,7 @@ event_t pollevent(void) { event_t event = { - .type = ET_None + .type = event_none, }; if(queue_size <= 0) return event; @@ -33,6 +33,6 @@ event_t waitevent(void) { event_t event; - while((event = pollevent()).type == ET_None) sleep(); + while((event = pollevent()).type == event_none) sleep(); return event; } diff --git a/src/events/event_push.c b/src/events/event_push.c index 0e39b2c..fc01a21 100644 --- a/src/events/event_push.c +++ b/src/events/event_push.c @@ -14,7 +14,7 @@ volatile int queue_size = 0; int event_push(event_t event) { if(queue_size >= EVENTS_QUEUE_SIZE) return 1; - if(event.type == ET_None) return 2; + if(event.type == event_none) return 2; int index = queue_start + queue_size; if(index >= EVENTS_QUEUE_SIZE) index -= EVENTS_QUEUE_SIZE; diff --git a/src/gray/gclear.c b/src/gray/gclear.c index e3beb21..60571ec 100644 --- a/src/gray/gclear.c +++ b/src/gray/gclear.c @@ -6,9 +6,28 @@ */ void gclear(void) { - int *v1 = gray_lightVRAM(); - int *v2 = gray_darkVRAM(); - int i; + uint32_t *lbase = gray_lightVRAM(); + uint32_t *v1 = lbase + 256; + uint32_t *v2 = gray_darkVRAM() + 256; - for(i = 0; i < 256; i++) v1[i] = v2[i] = 0; + while(v1 > lbase) + { + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + *--v1 = 0; + + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + *--v2 = 0; + } } diff --git a/src/gray/gclear_area.c b/src/gray/gclear_area.c deleted file mode 100644 index 42c3f29..0000000 --- a/src/gray/gclear_area.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -/* - gclear_area() - Clears an area of the video ram. End points (x1, y1) and (x2, y2) are - included. -*/ -void gclear_area(int x1, int y1, int x2, int y2) -{ - display_useVRAM(gray_lightVRAM()); - dclear_area(x1, y1, x2, y2); - display_useVRAM(gray_darkVRAM()); - dclear_area(x1, y1, x2, y2); -} diff --git a/src/gray/gline.c b/src/gray/gline.c index 9b31480..1599f58 100644 --- a/src/gray/gline.c +++ b/src/gray/gline.c @@ -3,22 +3,23 @@ /* gline() - Draws a line in the vram. Automatically optimizes special cases. + Draws a line in the vram. Automatically optimizes special cases. This + function does not support the light operators. */ -void gline(int x1, int y1, int x2, int y2, enum Color color) +void gline(int x1, int y1, int x2, int y2, color_t operator) { - enum Color c1, c2; + color_t op1, op2; - if(color == Color_None) return; - else if(color == Color_Invert) c1 = c2 = Color_Invert; + if(operator == color_invert) op1 = op2 = color_invert; + else if(operator >= color_none) return; else { - c1 = 3 * (color & 1); - c2 = 3 * (color >> 1); + op1 = 3 * (operator & 1); + op2 = 3 * (operator >> 1); } display_useVRAM(gray_lightVRAM()); - dline(x1, y1, x2, y2, c1); + dline(x1, y1, x2, y2, op1); display_useVRAM(gray_darkVRAM()); - dline(x1, y1, x2, y2, c2); + dline(x1, y1, x2, y2, op2); } diff --git a/src/gray/gpixel.c b/src/gray/gpixel.c index be44071..eae2460 100644 --- a/src/gray/gpixel.c +++ b/src/gray/gpixel.c @@ -4,44 +4,56 @@ gpixel() Puts a pixel in the vram. */ -void gpixel(int x, int y, enum Color color) +void gpixel(size_t x, size_t y, color_t operator) { - if((unsigned int)x > 127 || (unsigned int)y > 63) return; + if(x > 127 || y > 63) return; - int offset = (y << 2) + (x >> 5); - int mask = 0x80000000 >> (x & 31); + uint32_t *light = gray_lightVRAM() + (y << 2) + (x >> 5); + uint32_t *dark = gray_darkVRAM() + (y << 2) + (x >> 5); + uint32_t mask = 0x80000000 >> (x & 31); - int *v1 = gray_lightVRAM(); - int *v2 = gray_lightVRAM(); - - switch(color) + switch(operator) { - case Color_White: - v1[offset] &= ~mask; - v2[offset] &= ~mask; + case color_white: + *light &= ~mask; + *dark &= ~mask; break; - - case Color_Light: - v1[offset] |= mask; - v2[offset] &= ~mask; + case color_light: + *light |= mask; + *dark &= ~mask; break; - - case Color_Dark: - v1[offset] &= ~mask; - v2[offset] |= mask; + case color_dark: + *light &= ~mask; + *dark |= mask; break; - - case Color_Black: - v1[offset] |= mask; - v2[offset] |= mask; + case color_black: + *light |= mask; + *dark |= mask; break; + case color_none: + return; - case Color_Invert: - v1[offset] ^= mask; - v2[offset] ^= mask; + case color_invert: + *light ^= mask; + *dark ^= mask; break; - - default: + case color_lighten:; + uint32_t old_light_1 = *light; + *light &= *dark | ~mask; + *dark = (old_light_1 | ~mask) & (mask ^ *dark); + break; + case color_lighten2: + *dark &= *light | ~mask; + *light &= ~mask; + break; + case color_darken:; + uint32_t old_light_2 = *light; + *light |= *dark & mask; + *dark = (old_light_2 & mask) | (mask ^ *dark); + break; + case color_darken2: + *dark |= *light | mask; + *light |= mask; break; } } diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index 2e843f3..e79af37 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -11,8 +11,8 @@ #include #include -static int internal_vrams[3][256]; -static const void *vrams[4]; +static uint32_t internal_vrams[3][256]; +static uint32_t *vrams[4]; static int current = 0; static int delays[2]; @@ -45,10 +45,10 @@ void gray_interrupt(void) */ __attribute__((constructor)) void gray_init(void) { - vrams[0] = (const void *)display_getLocalVRAM(); - vrams[1] = (const void *)internal_vrams[0]; - vrams[2] = (const void *)internal_vrams[1]; - vrams[3] = (const void *)internal_vrams[2]; + vrams[0] = display_getLocalVRAM(); + vrams[1] = internal_vrams[0]; + vrams[2] = internal_vrams[1]; + vrams[3] = internal_vrams[2]; delays[0] = 912; delays[1] = 1343; @@ -119,18 +119,18 @@ inline int gray_runs(void) gray_lightVRAM() Returns the module's gray vram address. */ -void *gray_lightVRAM(void) +uint32_t *gray_lightVRAM(void) { - return (void *)vrams[~current & 2]; + return vrams[~current & 2]; } /* gray_lightVRAM() Returns the module's dark vram address. */ -void *gray_darkVRAM(void) +uint32_t *gray_darkVRAM(void) { - return (void *)vrams[(~current & 2) | 1]; + return vrams[(~current & 2) | 1]; } /* diff --git a/src/gray/grect.c b/src/gray/grect.c new file mode 100644 index 0000000..0edfb1d --- /dev/null +++ b/src/gray/grect.c @@ -0,0 +1,70 @@ +#include +#include + +/* + grect() + Draws a rectangle on the screen. This function can use all colors. +*/ +void grect(int x1, int y1, int x2, int y2, color_t operator) +{ + if(operator == color_none) return; + if(adjustRectangle(&x1, &y1, &x2, &y2)) return; + + uint32_t masks[4]; + getMasks(x1, x2, masks); + + uint32_t *lbase = gray_lightVRAM() + (y1 << 2); + uint32_t *lvideo = gray_lightVRAM() + (y2 << 2) + 4; + uint32_t *dvideo = gray_darkVRAM() + (y2 << 2) + 4; + + // Doing things in this order will be slower, but man, I can't stand + // writing that many lines of code for such a simple task. It will be + // terribly heavy in the binary file... + while(lvideo > lbase) for(int i = 0; i < 4; i++) switch(operator) + { + case color_white: + *--lvideo &= ~masks[i]; + *--dvideo &= ~masks[i]; + break; + case color_light: + *--lvideo |= masks[i]; + *--dvideo &= ~masks[i]; + break; + case color_dark: + *--lvideo &= ~masks[i]; + *--dvideo |= masks[i]; + break; + case color_black: + *--lvideo |= masks[i]; + *--dvideo |= masks[i]; + break; + case color_none: return; + + case color_invert: + *--lvideo ^= masks[i]; + *--dvideo ^= masks[i]; + break; + case color_lighten:; + uint32_t light_1 = *lvideo; + dvideo--; + *--lvideo &= *dvideo | ~masks[i]; + *dvideo = (light_1 | ~masks[i]) & (masks[i] ^ *dvideo); + break; + case color_lighten2: + lvideo--; + *--dvideo &= *lvideo | ~masks[i]; + *lvideo &= ~masks[i]; + break; + case color_darken:; + uint32_t light_2 = *lvideo; + dvideo--; + *--lvideo |= *dvideo & masks[i]; + *dvideo = (light_2 & masks[i]) | (masks[i] ^ *dvideo); + break; + case color_darken2: + lvideo--; + *--dvideo |= *lvideo | masks[i]; + *lvideo |= masks[i]; + break; + } +} diff --git a/src/gray/greverse_area.c b/src/gray/greverse_area.c deleted file mode 100644 index 7a3fccd..0000000 --- a/src/gray/greverse_area.c +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include - -/* - greverse_area() - Reverses an area of the vram. End points (x1, y1) and (x2, y2) are - included. -*/ -void greverse_area(int x1, int y1, int x2, int y2) -{ - display_useVRAM(gray_lightVRAM()); - dreverse_area(x1, y1, x2, y2); - display_useVRAM(gray_darkVRAM()); - dreverse_area(x1, y1, x2, y2); -} diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c index 1cbfcad..5d3cc68 100644 --- a/src/keyboard/getkey.c +++ b/src/keyboard/getkey.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -11,10 +12,11 @@ int getkey(void) { return getkey_opt( - Getkey_ShiftModifier | - Getkey_AlphaModifier | - Getkey_ManageBacklight | - Getkey_RepeatArrowKeys, + getkey_shift_modifier | + getkey_alpha_modifier | + getkey_manage_backlight | + getkey_repeat_arrow_keys, + 0 ); } @@ -22,101 +24,92 @@ int getkey(void) /* getkey_opt() Enhances getkey() with more general functionalities. - If max_cycles is non-zero and positive, getkey_opt() will return - KEY_NOEVENT if no event occurs during max_cycle analysis. + If delay_ms is non-zero and positive, getkey_opt() will return + KEY_NOEVENT if no event occurs before delay_ms. */ -static void getkey_opt_wait(int *cycles) +static void getkey_opt_wait(int *delay_ms) { while(!interrupt_flag) sleep(); interrupt_flag = 0; - if(*cycles > 0) (*cycles)--; + if(*delay_ms > 0) + { + (*delay_ms) -= vtimer->ms_delay; + if(*delay_ms < 0) *delay_ms = 0; + } } -int getkey_opt(enum GetkeyOpt options, int cycles) +int getkey_opt(getkey_option_t options, int delay_ms) { event_t event; - int modifier = 0; - static int event_ref = 0; + int modifier = 0, key; + key_type_t type; - if(cycles <= 0) cycles = -1; - while(cycles != 0) + if(delay_ms <= 0) delay_ms = -1; + + while(delay_ms != 0) switch((event = pollevent()).type) { - event = pollevent(); - switch(event.type) + case event_none: + getkey_opt_wait(&delay_ms); + break; + + case event_key_press: + key = event.key; + + if(options & getkey_manage_backlight && key == KEY_OPTN + && (modifier & MOD_SHIFT)) { - case ET_None: - if(last_key == KEY_NONE) - { - getkey_opt_wait(&cycles); - continue; - } - - // Handling repetitions. - enum KeyType type = keytype(last_key); - if(!(options & (type << 4))) break; - - if(event_ref <= 0) - { - getkey_opt_wait(&cycles); - event_ref++; - continue; - } - - last_events++; - event_ref--; - - if(last_events >= (last_repeats ? repeat_next : - repeat_first)) - { - last_repeats++; - last_events = 0; - return last_key; - } - break; - - case ET_KeyPress: - ; - int key = event.key; - if(options & Getkey_ManageBacklight && key == KEY_OPTN - && modifier & MOD_SHIFT) - { - screen_toggleBacklight(); - modifier &= ~MOD_SHIFT; - continue; - } - if(options & Getkey_ShiftModifier && key == KEY_SHIFT) - { - modifier ^= MOD_SHIFT; - continue; - } - if(options & Getkey_AlphaModifier && key == KEY_ALPHA) - { - modifier ^= MOD_ALPHA; - continue; - } - - last_key = key; - last_repeats = 0; - last_events = 0; - event_ref = 0; - return key | modifier; - - case ET_KeyRel: - if(event.key != last_key) break; - last_key = KEY_NONE; - last_repeats = 0; - last_events = 0; - event_ref = 0; - break; - - default: - break; + screen_toggleBacklight(); + modifier &= ~MOD_SHIFT; + continue; } + if(options & getkey_shift_modifier && key == KEY_SHIFT) + { + modifier ^= MOD_SHIFT; + continue; + } + if(options & getkey_alpha_modifier && key == KEY_ALPHA) + { + modifier ^= MOD_ALPHA; + continue; + } + + last_key = key; + last_repeats = 0; + last_time = 0; + return key | modifier; + + case event_key_repeat: + key = event.key; + if(key != last_key) continue; + + // Checking that this type of repetition is allowed. + type = key_type(key); + if(!(options & (type << 4))) break; + + last_time += vtimer->ms_delay; + int cmp = last_repeats ? repeat_next : repeat_first; + + if(last_time >= cmp) + { + last_repeats++; + last_time -= cmp; + return last_key; + } + break; + + case event_key_release: + if(event.key != last_key) break; + last_key = KEY_NONE; + last_repeats = 0; + last_time = 0; + break; + + default: + break; } last_key = KEY_NONE; last_repeats = 0; - last_events = 0; - event_ref = 0; + last_time = 0; return KEY_NONE; } diff --git a/src/keyboard/keychar.c b/src/keyboard/key_char.c similarity index 67% rename from src/keyboard/keychar.c rename to src/keyboard/key_char.c index bd9e9cc..1e1ccd1 100644 --- a/src/keyboard/keychar.c +++ b/src/keyboard/key_char.c @@ -1,11 +1,12 @@ #include /* - keychar() - Returns the ASCII character associated with a key, or 0 for control - keys. + key_char() + Returns the ASCII character associated with a character key, and 0 for + other keys. This function expects a matrix code and not a key_id() + code, and heeds for the ALPHA modifier. */ -int keychar(int key) +int key_char(int matrix_key) { char flat[] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, @@ -30,7 +31,6 @@ int keychar(int key) 'Z', ' ', '"', 0x0, 0x0, 0x0 }; - int id = keyid(key); - - return (key & MOD_ALPHA) ? alpha[id] : flat[id]; + int id = key_id(matrix_key); + return (matrix_key & MOD_ALPHA) ? alpha[id] : flat[id]; } diff --git a/src/keyboard/key_id.c b/src/keyboard/key_id.c new file mode 100644 index 0000000..152208c --- /dev/null +++ b/src/keyboard/key_id.c @@ -0,0 +1,19 @@ +#include + +/* + keyid() + Transforms a key identifier and returns a key code that is more + convenient for array subscript that the original matrix codes. Please + note that there are a few holes in the numbering. + This function ignores modifiers and returns -1 on error. +*/ +int key_id(int matrix_key) +{ + if(matrix_key < 0) return -1; + matrix_key &= MOD_CLEAR; + + int row = 9 - (matrix_key & 0x0f); + int column = 6 - ((matrix_key & 0xf0) >> 4); + + return 6 * row + column; +} diff --git a/src/keyboard/key_type.c b/src/keyboard/key_type.c new file mode 100644 index 0000000..b29e617 --- /dev/null +++ b/src/keyboard/key_type.c @@ -0,0 +1,24 @@ +#include + +/* + key_type() + Returns a key's type. This functions ignores modifiers and expects + matrix codes as argument, not key_id() codes. +*/ +key_type_t key_type(int matrix_key) +{ + matrix_key &= MOD_CLEAR; + + // Arrow keys. + if(matrix_key == KEY_UP || matrix_key == KEY_RIGHT + || matrix_key == KEY_DOWN || matrix_key == KEY_LEFT) + { + return key_type_arrow; + } + + // Function keys (F1 .. F6) are ton only keys of the ninth row. + if((matrix_key & 0x0f) == 0x09) return key_type_function; + + // Then character keys are those that have an associated character. =p + return key_char(matrix_key) ? key_type_character : key_type_control; +} diff --git a/src/keyboard/keyboard_config.c b/src/keyboard/keyboard_config.c deleted file mode 100644 index e19347f..0000000 --- a/src/keyboard/keyboard_config.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include - -/* - keyboard_setRepeatRate() - Sets the default repeat rate for key events. The unit for the argument - is the keyboard period. - For example at 32 Hz, values of (20, 4) will imitate the system - default. -*/ -void keyboard_setRepeatRate(int first, int next) -{ - if(first < 0) first = 0; - if(next < 0) next = 0; - - repeat_first = first; - repeat_next = next; -} diff --git a/src/keyboard/keyboard_core.c b/src/keyboard/keyboard_core.c new file mode 100644 index 0000000..ea76d3c --- /dev/null +++ b/src/keyboard/keyboard_core.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include + +//--- +// Keyboard variables. +//--- + +// Current keyboard state: each element represents a row, and each bit a key, +// but not all bits are used. +volatile uint8_t keyboard_state[10] = { 0 }; +// Interrupt flag: set when an interrupt occurs, and cleared by functions such +// as getkey() that watch it (because such functions will wake up every time an +// interrupt occurs, and they need to know whether it was the keyboard). +volatile int interrupt_flag = 0; + +// Delays, in milliseconds, before repeating keys (the first repetition may +// have a different delay). +int repeat_first = 625, repeat_next = 125; + +// Which key was pressed last, how many times it has been repeated, and how +// much time (in milliseconds) has elapsed since it was last repeated. +int last_key = KEY_NONE, last_repeats = 0, last_time = 0; + +// Virtual timer object. +timer_t *vtimer = NULL; + + + +//--- +// Interrupt management. +//--- + +static inline void push_press(int keycode) +{ + event_t event = { + .type = event_key_press, + .key = keycode, + }; + event_push(event); +} + +static inline void push_repeat(int keycode) +{ + event_t event = { + .type = event_key_repeat, + .key = keycode, + }; + event_push(event); +} + +static inline void push_release(int keycode) +{ + event_t event = { + .type = event_key_release, + .key = keycode, + }; + event_push(event); +} + +/* + keyboard_interrupt() + Callback function for keyboard update; called by the timer manager when + the keyboard's virtual timer fires. Ideally this function should be + interrupt-driven but the PINT interrupts seem to fire continuously, + which is annoying. +*/ +void keyboard_interrupt(void) +{ + uint8_t state[10] = { 0 }; + + isSH3() ? keyboard_updateState_7705(state) + : keyboard_updateState_7305(state); + + // This procedure really needs to be speed-optimized... and it's hard + // because of this bit manipulation. This condition handles AC/ON. + if(keyboard_state[0] ^ state[0]) + { + uint8_t pressed = ~keyboard_state[0] & state[0]; + uint8_t released = keyboard_state[0] & ~state[0]; + + if(pressed & 1) push_press(KEY_AC_ON); + if(released & 1) push_release(KEY_AC_ON); + } + keyboard_state[0] = state[0]; + + for(int row = 1; row <= 9; row++) + { + uint8_t pressed = ~keyboard_state[row] & state[row]; + uint8_t repeated = keyboard_state[row] & state[row]; + uint8_t released = keyboard_state[row] & ~state[row]; + keyboard_state[row] = state[row]; + + // Make this a bit faster. + if(!(pressed | repeated | released)) continue; + + for(int column = 0; column < 8; column++) + { + if(pressed & 1) push_press ((column << 4) | row); + if(repeated & 1) push_repeat ((column << 4) | row); + if(released & 1) push_release((column << 4) | row); + + pressed >>= 1; + repeated >>= 1; + released >>= 1; + } + } + + // Signal the interrupt to the higher-level functions. + interrupt_flag = 1; +} + + + +//--- +// Keyboard configuration. +//--- + +/* + keyboard_init() + Starts the keyboard timer. +*/ +__attribute__((constructor)) void keyboard_init(void) +{ + keyboard_setRepeatRate(625, 125); + + vtimer = timer_create(25, 0); + timer_attach(vtimer, keyboard_interrupt, NULL); + timer_start(vtimer); +} + +/* + keyboard_setAnalysisDelay() + Sets the keyboard analysis delay, that is, the delay (in ms) between + two keyboard analyzes. Please note that the repeat delays should be + multiples of the analysis delay for better accuracy. +*/ +void keyboard_setAnalysisDelay(int analysis_delay_ms) +{ + if(analysis_delay_ms <= 0) return; + timer_reload(vtimer, analysis_delay_ms); +} + +/* + keyboard_setRepeatRate() + Sets the default repeat rate for key events. The delay before the first + repeat may have a different value (usually longer). The unit for the + argument is ms, but the repeat events themselves may only be fired when + a keyboard analysis is performed; which means that for better accuracy, + these delays should be a multiple of the keyboard period. + For instance, delays of (625 ms, 125 ms) will imitate the system's + default setting. +*/ +void keyboard_setRepeatRate(int first, int next) +{ + repeat_first = (first > 0) ? first : 0; + repeat_next = (next > 0) ? next : 0; +} + +/* + keyboard_quit() + Stops the keyboard timer. +*/ +__attribute__((destructor)) void keyboard_quit(void) +{ + timer_destroy(vtimer); + vtimer = NULL; +} + +/* + keyboard_stateBuffer() + Returns the address of the keyboard state array. The returned address + is the handler's buffer, therefore it contains volatile data. +*/ +volatile uint8_t *keyboard_stateBuffer(void) +{ + return keyboard_state; +} diff --git a/src/keyboard/keyboard_interrupt.c b/src/keyboard/keyboard_interrupt.c deleted file mode 100644 index 9cf854a..0000000 --- a/src/keyboard/keyboard_interrupt.c +++ /dev/null @@ -1,132 +0,0 @@ -#include -#include -#include -#include -#include -#include - -//--- -// Keyboard variables. -//--- - -// These ones get modified by interrupts. -volatile uint8_t keyboard_state[10] = { 0 }; -volatile int interrupt_flag = 0; - -// Key statistics. -int repeat_first = 10, repeat_next = 2; -int last_key = KEY_NONE, last_repeats = 0, last_events = 0; - -// RTC callback id. -unsigned cb_id; - - - -//--- -// Interrupt management. -//--- - -static void push_press(int keycode) -{ - event_t event = { - .type = ET_KeyPress, - .key = keycode, - }; - event_push(event); -} - -static void push_repeat(int keycode) -{ - event_t event = { - .type = ET_KeyRepeat, - .key = keycode, - }; - event_push(event); -} - -static void push_release(int keycode) -{ - event_t event = { - .type = ET_KeyRel, - .key = keycode, - }; - event_push(event); -} - -/* - keyboard_interrupt() - Callback for keyboard update. Allows keyboard analysis functions to - wake only when keyboard interrupts happen. -*/ -void keyboard_interrupt(void) -{ - uint8_t state[10] = { 0 }; - - isSH3() ? keyboard_updateState_7705(state) - : keyboard_updateState_7305(state) - ; - - // Try to minimize number of operations in common cases... this handles - // AC/ON. - if(keyboard_state[0] ^ state[0]) - { - uint8_t pressed = ~keyboard_state[0] & state[0]; - uint8_t released = keyboard_state[0] & ~state[0]; - - if(pressed & 1) push_press(KEY_AC_ON); - if(released & 1) push_release(KEY_AC_ON); - } - keyboard_state[0] = state[0]; - - for(int row = 1; row <= 9; row++) - { - uint8_t pressed = ~keyboard_state[row] & state[row]; - uint8_t repeated = keyboard_state[row] & state[row]; - uint8_t released = keyboard_state[row] & ~state[row]; - keyboard_state[row] = state[row]; - - // Fasten this a bit. - if(!(pressed | repeated | released)) continue; - - for(int column = 0; column < 8; column++) - { - if(pressed & 1) push_press ((column << 4) | row); - if(repeated & 1) push_repeat ((column << 4) | row); - if(released & 1) push_release((column << 4) | row); - - pressed >>= 1; - repeated >>= 1; - released >>= 1; - } - } - - interrupt_flag = 1; -} - -/* - keyboard_init() - Starts the keyboard timer. -*/ -__attribute__((constructor)) void keyboard_init(void) -{ - cb_id = rtc_cb_add(RTCFreq_16Hz, keyboard_interrupt, 0); -} - -/* - keyboard_setFrequency() - Sets the keyboard frequency. -*/ -void keyboard_setFrequency(enum KeyboardFrequency frequency) -{ - if(frequency < 1 || frequency > 7) return; - rtc_cb_edit(cb_id, frequency, keyboard_interrupt); -} - -/* - keyboard_quit() - Stops the keyboard timer. -*/ -__attribute__((destructor)) void keyboard_quit(void) -{ - rtc_cb_end(cb_id); -} diff --git a/src/keyboard/keyboard_misc.c b/src/keyboard/keyboard_misc.c deleted file mode 100644 index 6248a3d..0000000 --- a/src/keyboard/keyboard_misc.c +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include - -/* - keylast() - Returns the matrix code of the last pressed key. If repeat_count is - non-NULL, it is set to the number of repetitions. -*/ -int keylast(int *repeat_count) -{ - if(repeat_count) *repeat_count = last_repeats; - return last_key; -} - - - -/* - keystate() - Returns the address of the keyboard state array. The returned address - is the handler's buffer, therefore it contains volatile data. -*/ -volatile uint8_t *keystate(void) -{ - return keyboard_state; -} diff --git a/src/keyboard/keyboard_sh7305.c b/src/keyboard/keyboard_sh7305.c index 8eb0bca..cae413a 100644 --- a/src/keyboard/keyboard_sh7305.c +++ b/src/keyboard/keyboard_sh7305.c @@ -10,6 +10,7 @@ // //--- +#include #include #include <7305.h> @@ -61,20 +62,20 @@ static void kdelay(void) */ static int krow(int row) { - volatile unsigned short *injector1 = (unsigned short *)0xa4050116; - volatile unsigned char *data1 = (unsigned char *)0xa4050136; + volatile uint16_t *injector1 = (void *)0xa4050116; + volatile uint8_t *data1 = (void *)0xa4050136; - volatile unsigned short *injector2 = (unsigned short *)0xa4050118; - volatile unsigned char *data2 = (unsigned char *)0xa4050138; + volatile uint16_t *injector2 = (void *)0xa4050118; + volatile uint8_t *data2 = (void *)0xa4050138; - volatile unsigned short *detector = (unsigned short *)0xa405014c; - volatile unsigned char *keys = (unsigned char *)0xa405016c; + volatile uint16_t *detector = (void *)0xa405014c; + volatile uint8_t *keys = (void *)0xa405016c; - volatile unsigned char *key_register = (unsigned char *)0xa40501c6; -// volatile unsigned short *hizcrb = (unsigned short *)0xa405015a; + volatile uint8_t *key_register = (void *)0xa40501c6; +// volatile uint16_t *hizcrb = (void *)0xa405015a; - unsigned short smask; - unsigned char cmask; + uint16_t smask; + uint8_t cmask; int result = 0; if(row < 0 || row > 9) return 0; diff --git a/src/keyboard/keyid.c b/src/keyboard/keyid.c deleted file mode 100644 index c4ed014..0000000 --- a/src/keyboard/keyid.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -/* - keyid() - Returns a non-matrix key code that can be used for array subscript. - Ignores modifiers. -*/ -int keyid(int key) -{ - if(key < 0) return -1; - key &= MOD_CLEAR; - - int row = 9 - (key & 0x0f); - int column = 6 - ((key & 0xf0) >> 4); - - return 6 * row + column; -} diff --git a/src/keyboard/keytype.c b/src/keyboard/keytype.c deleted file mode 100644 index 9e3c0c9..0000000 --- a/src/keyboard/keytype.c +++ /dev/null @@ -1,17 +0,0 @@ -#include - -/* - keytype() - Returns a key's type. Ignores modifiers. -*/ -enum KeyType keytype(int key) -{ - key &= MOD_CLEAR; - - if(key == KEY_UP || key == KEY_RIGHT || key == KEY_DOWN || - key == KEY_LEFT) return KeyType_Arrow; - - if((key & 0x0f) == 0x09) return KeyType_Function; - - return keychar(key) ? KeyType_Character : KeyType_Control; -} diff --git a/src/keyboard/multigetkey.c b/src/keyboard/multigetkey.c index b1bc5e5..74f9b6c 100644 --- a/src/keyboard/multigetkey.c +++ b/src/keyboard/multigetkey.c @@ -40,7 +40,7 @@ void multigetkey(int *keys, int count, int cycles) { last_key = keys[0]; last_repeats = 0; - last_events = 0; + last_time = 0; } if(number) break; @@ -48,7 +48,7 @@ void multigetkey(int *keys, int count, int cycles) } do event = pollevent(); - while(event.type != ET_None); + while(event.type != event_none); return; } diff --git a/src/string/ctype.c b/src/string/ctype.c deleted file mode 100644 index 6b55781..0000000 --- a/src/string/ctype.c +++ /dev/null @@ -1,64 +0,0 @@ -#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/tales/tales_configuration.c b/src/tales/tales_configuration.c index 8b00345..81b9e32 100644 --- a/src/tales/tales_configuration.c +++ b/src/tales/tales_configuration.c @@ -5,11 +5,11 @@ text_configure() Sets the font and mode to use for the following print operations. */ -void text_configure(struct Font *next_font, enum Color next_color) +void text_configure(struct Font *next_font, color_t next_operator) { extern Font gint_font_system; if(next_font) font = next_font; else font = &gint_font_system; - color = next_color; + operator = next_operator; } diff --git a/src/tales/tales_gray.c b/src/tales/tales_gray.c index 4b9b02a..42918e8 100644 --- a/src/tales/tales_gray.c +++ b/src/tales/tales_gray.c @@ -4,42 +4,61 @@ void operate_gray(OPERATE_ARGS) { - int *vl = gray_lightVRAM(); - int *vd = gray_darkVRAM(); - int vram_offset = (x >> 5) + (y << 2); - uint32_t op; - int i; + size_t vram_offset = (x >> 5) + (y << 2); + uint32_t *light = gray_lightVRAM() + vram_offset; + uint32_t *dark = gray_darkVRAM() + vram_offset; + uint32_t op, old_light; - for(i = 0; i < height; i++) + for(int i = 0; i < height; i++) { op = operators[i]; - switch(color) + switch(operator) { - case Color_White: - vl[vram_offset] &= ~op; - vd[vram_offset] &= ~op; + case color_white: + *light &= ~op; + *dark &= ~op; break; - case Color_Light: - vl[vram_offset] |= op; - vd[vram_offset] &= ~op; + case color_light: + *light |= op; + *dark &= ~op; break; - case Color_Dark: - vl[vram_offset] &= ~op; - vd[vram_offset] |= op; + case color_dark: + *light &= ~op; + *dark |= op; break; - case Color_Black: - vl[vram_offset] |= op; - vd[vram_offset] |= op; + case color_black: + *light |= op; + *dark |= op; break; - case Color_Invert: - vl[vram_offset] ^= op; - vd[vram_offset] ^= op; + case color_none: + return; + + case color_invert: + *light ^= op; + *dark ^= op; break; - default: + case color_lighten: + old_light = *light; + *light &= *dark | ~op; + *dark = (old_light | ~op) & (op ^ *dark); + break; + case color_lighten2: + *dark &= *light | ~op; + *light &= ~op; + break; + case color_darken: + old_light = *light; + *light |= *dark & op; + *dark = (old_light & op) | (op ^ *dark); + break; + case color_darken2: + *dark |= *light | op; + *light |= op; break; } - vram_offset += 4; + light += 4; + dark += 4; } } diff --git a/src/tales/tales_internals.c b/src/tales/tales_internals.c index c12d2c5..e6dec37 100644 --- a/src/tales/tales_internals.c +++ b/src/tales/tales_internals.c @@ -5,7 +5,7 @@ #include struct Font *font; -enum Color color; +color_t operator; /* tales_init() @@ -13,7 +13,7 @@ enum Color color; */ void tales_init(void) { - text_configure(NULL, Color_Black); + text_configure(NULL, color_black); } /* @@ -93,31 +93,36 @@ int getCharacterIndex(int c) */ void operate_mono(OPERATE_ARGS) { - int *vram = display_getCurrentVRAM(); - int vram_offset = (x >> 5) + (y << 2); - uint32_t op; - int i; + uint32_t *vram = display_getCurrentVRAM(); + uint32_t *video = vram + (x >> 5) + (y << 2); - for(i = 0; i < height; i++) + switch(operator) { - op = operators[i]; - - switch(color) + case color_white: + for(int i = 0; i < height; i++) { - case Color_White: - vram[vram_offset] &= ~op; - break; - case Color_Black: - vram[vram_offset] |= op; - break; - case Color_Invert: - vram[vram_offset] ^= op; - break; - default: - break; + *video &= ~operators[i]; + video += 4; } + break; - vram_offset += 4; + case color_black: + for(int i = 0; i < height; i++) + { + *video |= operators[i]; + video += 4; + } + break; + + case color_invert: + for(int i = 0; i < height; i++) + { + *video ^= operators[i]; + video += 4; + } + break; + + default: return; } } diff --git a/src/timer/common_api.c b/src/timer/common_api.c index 46b39fa..d403e57 100644 --- a/src/timer/common_api.c +++ b/src/timer/common_api.c @@ -94,7 +94,7 @@ void timer_callback_event(timer_t *timer) else { event_t event = { - .type = ET_Timer, + .type = event_timer_underflow, .timer = timer }; event_push(event); diff --git a/src/timer/virtual_timers.c b/src/timer/virtual_timers.c index 518c5fd..b250065 100644 --- a/src/timer/virtual_timers.c +++ b/src/timer/virtual_timers.c @@ -38,6 +38,24 @@ timer_t *timer_create(int ms_delay, int repeats) return timer; } +/* + timer_reload() + Changes a virtual timer's delay. The timer is not stopped nor started: + it keeps running or waiting. Events that were waiting to be handled are + dropped and the number of repeats left is not changed. The timer + restarts counting from 0 regardless of how much time had elapsed since + it last fired. +*/ +void timer_reload(timer_t *timer, int new_ms_delay) +{ + if(!timer->virtual) return; + + timer->ms_delay = new_ms_delay; + timer->ms_elapsed = 0; + + vtimer_updateAll(); +} + /* timer_destroy() Destroys a virtual timer. This virtual timer pointer becomes invalid @@ -136,7 +154,7 @@ static void vtimer_update(int new_delay) timer_stop(timer); return; } - uint32_t new_constant = clock_setting(new_delay, Clock_ms); + uint32_t new_constant = clock_setting(new_delay, clock_ms); // The transition needs to be as smooth as possible. We have probably // spent a lot of time calculating this new GCD delay so we want to @@ -176,7 +194,7 @@ static void vtimer_update(int new_delay) /* gcd() - Well, the Euclidean algorithm. That's a O(ln(a)) & O(ln(b)) FWIW. + Well, the Euclidean algorithm. That's O(ln(max(a, b))) FWIW. */ static int gcd(int a, int b) { From acc0e5fcc168b6087e8ca3aed2f45ca1d48c635f Mon Sep 17 00:00:00 2001 From: lephe Date: Thu, 13 Apr 2017 21:59:13 +0200 Subject: [PATCH 08/24] More quality review, more registers saved at startup. --- Makefile | 34 +++++- TODO | 44 ++++---- demo/gintdemo.c | 4 +- demo/test_bopti.c | 6 +- demo/test_rtc.c | 40 +++---- demo/test_timer.c | 4 +- include/bopti.h | 14 ++- include/gint.h | 3 - include/internals/bopti.h | 130 ++++++++++++----------- include/internals/gint.h | 81 +++++++++++--- include/internals/keyboard.h | 5 +- include/internals/mmu.h | 4 +- include/internals/rtc.h | 125 ++-------------------- include/modules/macros.h | 9 ++ include/modules/rtc.h | 131 +++++++++++++++++++++++ include/modules/timer.h | 2 +- include/mpu.h | 2 +- include/rtc.h | 27 ++--- src/bopti/bopti_internals.c | 54 ++++------ src/bopti/dimage.c | 6 +- src/bopti/gimage.c | 4 +- src/clock/clock.c | 4 +- src/core/crt0.c | 20 ++-- src/core/exceptions.c | 4 +- src/core/gint.c | 13 +-- src/core/gint_sh7305.c | 189 ++++++++++++++++----------------- src/core/gint_sh7705.c | 136 +++++++++++++----------- src/core/gint_vbr.s | 2 +- src/core/init_quit.c | 17 ++- src/core/modules.c | 13 ++- src/core/mpu.c | 13 +-- src/keyboard/keyboard_sh7305.c | 5 +- src/rtc/rtc_callback.c | 16 +-- src/rtc/rtc_getTime.c | 34 +++--- src/rtc/rtc_interrupt.c | 6 +- src/rtc/rtc_setTime.c | 31 +++--- src/time/time.c | 3 +- version | 1 + 38 files changed, 673 insertions(+), 563 deletions(-) create mode 100644 include/modules/rtc.h create mode 100644 version diff --git a/Makefile b/Makefile index 04677b1..26db109 100755 --- a/Makefile +++ b/Makefile @@ -104,7 +104,7 @@ hdr-dep = $(wildcard include/*.h include/*/*.h) #--- -# Rule templates. +# Rule templates #--- # C source file template: @@ -129,7 +129,29 @@ endef #--- -# Building. +# Version management +#--- + +# Retrieve version information. +version_string := $(shell cat version | sed 's/[-.]/ /g') +version_type := $(word 1,$(version_string)) +version_major := $(word 2,$(version_string)) +version_minor := $(word 3,$(version_string)) +version_build := $(word 4,$(version_string)) + +# Bump build number and make up the new version integer. +version_build := $(shell echo $$(($(version_build) + 1))) +version_letter := $(shell echo -n $(version_type) | sed -r 's/^(.).*/\1/') +version_symbol := $(shell printf '0x%02x%01x%01x%04x' "'$(version_letter)'" \ + $(version_major) $(version_minor) $(version_build)) + +# Tell the linker to define the version symbol. +ldflags += -Wl,--defsym,GINT_VERSION=$(version_symbol) + + + +#--- +# Building #--- # Generic rules @@ -142,16 +164,20 @@ build: $(if $(VERBOSE),,@ printf '\e[35;1mdir \u00bb\e[0m mkdir $@\n') $(if $(VERBOSE),,@) mkdir -p $@ +version: $(obj-std) $(obj-lib) + @ echo '$(version_type)-$(version_major).$(version_minor)-$(version_build)' > $@ + + $(obj-std) $(obj-lib) $(demo-obj): | build -$(target-std): $(obj-std) +$(target-std): $(obj-std) version $(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 (' @ printf $$(stat -c %s $@) @ printf ' bytes)\n\n' -$(target-lib): $(config) $(target-std) $(obj-lib) +$(target-lib): $(config) $(target-std) $(obj-lib) version $(if $(VERBOSE),,@ printf '\e[35;1mlib \u00bb\e[0m ar $@\n') $(if $(VERBOSE),,@) $(ar) rcs $@ $(obj-lib) @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built libgint (' diff --git a/TODO b/TODO index 9134ce0..88f4c3c 100644 --- a/TODO +++ b/TODO @@ -1,37 +1,37 @@ Bugs to fix: - A few key hits ignored after leaving the application (could not reproduce) - Lost keyboard control at startup (could not reproduce) -- Influence of keyboard on some timers and RTC (maybe interrupt priority) - Alignment of ALL .data / .rodata files is required to ensure converted data is properly aligned +- Reported video ram overflow when rendering text at borderline positions -Simple improvements: -- demo: Try 284x124 at (-60, -28) (all disadvantages) -- display: Rectangle-based drawing functions -- time: Compute CLOCKS_PER_SEC -- string: Use cmp/str to implement memchr() (assembler examples) -- string: Do some tests for memcmp() -- core: Register more interrupts (and understand their parameters) -- project: Clean headers that have some internal definitions -- project: Check size of all library structures -Larger improvements: -- errno: Introduce errno and use it more or less everywhere -- bopti: Monochrome bitmaps blending modes -- bopti: Handle partial transparency -- core: Implement all callbacks and a complete user API -* core: Better save registers +Things to do before 1.0: +- bopti: Test partial transparency * core: Allow return to menu -- serial: Implement a driver -- usb: Implement a driver -- esper: Cleaner playback, synthesizing -- clock: Handle overclock (relaunch clocks when overclocking) +- demo: Try 284x124 at (-60, -28) (all disadvantages) +- display: Implement rectangle-based drawing functions +- project: Add a real build-based version system +- project: Check size of *all* library structures +- project: Clean headers that have some internal definitions - project: Unify this hellish mess of register access! +- time: Compute CLOCKS_PER_SEC + +Things to do later: +- bopti: Implement blending modes for monochrome bitmaps +- clock: Handle overclock (relaunch clocks when overclocking) +- core: Register more interrupts (and understand their parameters) +- core: Remove redundant code linked to environment saves +- core: Review interrupt system (again) - this one is too slow +- errno: Introduce errno and use it more or less everywhere +- esper: Cleaner playback, synthesizing +- serial: Implement a driver +- string: Use cmp/str to implement memchr() (assembler examples) +- string: Do some tests for memcmp() and memcpy() +- usb: Implement a driver Things to investigate: - Packed bit fields alignment - Registers that may need to be saved within setjmp() -- Registers that may need to be saved and restored by gint - Optimizing core/gint.c leads to raising of an illegal slot exception when running the interrupt handler, although it ends on rte; lds.l @r15+, mach, which is totally not an illegal slot. -- Check version registers on SH7705 diff --git a/demo/gintdemo.c b/demo/gintdemo.c index 98d1ac5..41e4d6e 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -345,8 +345,8 @@ void main_menu(int *category, int *app) switch(tab) { case 0: - locate(1, 1, "Demo application"); - print(2, 3, "gint version: %5s", GINT_VERSION_STR); + print(1, 1, "Demo application"); +// print(2, 3, "gint version: %5s", GINT_VERSION_STR); print(2, 4, "handler size: %5d", gint_size); print(2, 5, "mpu type: %7s", mpu); print(2, 6, "romdata: %08x", &romdata); diff --git a/demo/test_bopti.c b/demo/test_bopti.c index a908823..9b137c8 100644 --- a/demo/test_bopti.c +++ b/demo/test_bopti.c @@ -95,14 +95,10 @@ static image_t *select(image_t *current) 8 * i, 7, 7); if(i == row) { - print(2, 2 + i + 1, "%08x", (unsigned int) - images[i].img); -/* int width, height; getwh(images[i].img, &width, &height); print(2, 2 + i + 1, "%d\x04%d", width, height); locate(10, 2 + i + 1, images[i].info); -*/ } } @@ -147,7 +143,7 @@ void test_bopti(void) while(1) { - if(img && (img->format & Channel_Light)) + if(img && (img->format & channel_light)) { gray_start(); gclear(); diff --git a/demo/test_rtc.c b/demo/test_rtc.c index 1211aed..4b8fa65 100644 --- a/demo/test_rtc.c +++ b/demo/test_rtc.c @@ -14,9 +14,10 @@ */ #include +#include #include -static void draw(rtc_time_t time) +static void draw(rtc_time_t *time) { extern image_t res_rtc_segments; @@ -30,12 +31,12 @@ static void draw(rtc_time_t time) int digits[6]; int i; - digits[0] = time.hours / 10; - digits[1] = time.hours % 10; - digits[2] = time.minutes / 10; - digits[3] = time.minutes % 10; - digits[4] = time.seconds / 10; - digits[5] = time.seconds % 10; + digits[0] = time->hours / 10; + digits[1] = time->hours % 10; + digits[2] = time->minutes / 10; + digits[3] = time->minutes % 10; + digits[4] = time->seconds / 10; + digits[5] = time->seconds % 10; // Drawing digits. for(i = 0; i < 6; i++) dimage_part(x[i], 8, &res_rtc_segments, @@ -46,17 +47,18 @@ static void draw(rtc_time_t time) // This should print time.year + 1900 but for the sake of this demo we // have tweaked the field so that it already contains time.year + 1900. - print(4, 6, "%s %s %02d %4d", days[time.week_day], - months[time.month], time.month_day, time.year); + print(4, 6, "%s %s %02d %4d", days[time->week_day], + months[time->month], time->month_day, time->year); } static void callback(void) { extern image_t res_opt_rtc; - rtc_time_t time = rtc_getTime(); + rtc_time_t time; + rtc_getTime(&time); dclear(); - draw(time); + draw(&time); dimage_part(0, 56, &res_opt_rtc, 0, 0, 19, 8); dupdate(); } @@ -128,14 +130,16 @@ static void set(void) { 72, 39, 7, 9 }, { 84, 39, 7, 9 }, { 90, 39, 7, 9 }, { 96, 39, 7, 9 }, { 102, 39, 7, 9 }, }; - rtc_time_t time = rtc_getTime(); + rtc_time_t time; int region_count = 14; int n = 0, slide = 0, key, leave; + rtc_getTime(&time); + while(1) { dclear(); - draw(time); + draw(&time); drect(regions[n].x, regions[n].y, regions[n].x + regions[n].w - 1, regions[n].y + regions[n].h - 1, color_invert); @@ -157,7 +161,7 @@ static void set(void) slide = 0; if(++n == region_count) { - rtc_setTime(time); + rtc_setTime(&time); return; } } @@ -216,7 +220,7 @@ static void set(void) n++; if(n == region_count) { - rtc_setTime(time); + rtc_setTime(&time); return; } slide = 0; @@ -235,7 +239,7 @@ void test_rtc(void) { int key, cb_id; - cb_id = rtc_cb_add(RTCFreq_1Hz, callback, 0); + cb_id = rtc_cb_add(rtc_freq_1Hz, callback, 0); callback(); while(1) @@ -245,10 +249,10 @@ void test_rtc(void) if(key == KEY_EXIT) break; if(key == KEY_F1) { - rtc_cb_edit(cb_id, RTCFreq_None, NULL); + rtc_cb_edit(cb_id, rtc_freq_none, NULL); set(); callback(); - rtc_cb_edit(cb_id, RTCFreq_1Hz, callback); + rtc_cb_edit(cb_id, rtc_freq_1Hz, callback); } } diff --git a/demo/test_timer.c b/demo/test_timer.c index d64e6c0..011f365 100644 --- a/demo/test_timer.c +++ b/demo/test_timer.c @@ -41,7 +41,7 @@ static void timing_start(void) timer_attach(htimer, timing_timer, NULL); timer_start(htimer); - rtc_cb_edit(cb_id, RTCFreq_64Hz, timing_rtc); + rtc_cb_edit(cb_id, rtc_freq_64Hz, timing_rtc); elapsed_timer = 0; elapsed_rtc = 0; @@ -247,7 +247,7 @@ void test_timer(void) elapsed_timer = -1; elapsed_rtc = -1; - cb_id = rtc_cb_add(RTCFreq_64Hz, timing_start, 0); + cb_id = rtc_cb_add(rtc_freq_64Hz, timing_start, 0); text_configure(NULL, color_black); diff --git a/include/bopti.h b/include/bopti.h index 8889359..95465c7 100644 --- a/include/bopti.h +++ b/include/bopti.h @@ -46,8 +46,11 @@ void dimage(int x, int y, image_t *image); Point (left, top) is included, but (left + width, top + height) is excluded. */ -void dimage_part(int x, int y, image_t *img, int left, int top, int width, - int height); +void dimage_part( + int x, int y, + image_t *img, + int left, int top, int width, int height +); /* gimage() @@ -61,7 +64,10 @@ void gimage(int x, int y, image_t *image); Point (left, top) is included, but (left + width, top + height) is excluded. */ -void gimage_part(int x, int y, image_t *image, int left, int top, int width, - int height); +void gimage_part( + int x, int y, + image_t *image, + int left, int top, int width, int height +); #endif // _BOPTI_H diff --git a/include/gint.h b/include/gint.h index 887b960..57486a1 100644 --- a/include/gint.h +++ b/include/gint.h @@ -15,9 +15,6 @@ #include #include -#define GINT_VERSION 0x000100a3 -#define GINT_VERSION_STR "00.01" - //--- // System info provided by the library //--- diff --git a/include/internals/bopti.h b/include/internals/bopti.h index 31a87e0..2c93153 100644 --- a/include/internals/bopti.h +++ b/include/internals/bopti.h @@ -1,19 +1,3 @@ -//--- -// -// gint drawing module: bopti -// -// bopti does every job related to display images. There is only one -// public function, but there are lots of internal optimizations. -// -// Some bit-manipulation expressions may look written out of nowhere. The -// idea is always the same: get a part of the image in an 'operator', -// which is a 32-bit variable, shift this operator so that its bits -// correspond to the desired position for the bitmap on the screen, and -// edit the video-ram long entry which correspond to this position using -// a 'mask' that indicates which bits of the operator contain information. -// -//--- - #ifndef _INTERNALS_BOPTI_H #define _INTERNALS_BOPTI_H @@ -21,40 +5,45 @@ #include /* - enum Channel - Determines the kind of information written into a layer. Every image is - made of one or more channels. + channel_t + Indicates what operation a layer is made for. Each operation affects + the video ram differently (setting or clearing pixels, transparency, + etc). An image is made of several layers. */ -enum Channel +typedef enum { - Channel_FullAlpha = 0x01, - Channel_LightAlpha = 0x02, - Channel_DarkAlpha = 0x04, + channel_full_alpha = 0x01, + channel_light_alpha = 0x02, + channel_dark_alpha = 0x04, - Channel_Mono = 0x08, - Channel_Light = 0x10, - Channel_Dark = 0x20, -}; + channel_mono = 0x08, + channel_light = 0x10, + channel_dark = 0x20, + +} channel_t; /* - enum Format - Describes the various combination of channels allowed by bopti. + format_t + Describes the various combination of layer channels that are allowed by + bopti. Technically one could try other formats but they're not of much + use (transparent gray is even totally useless). */ -enum Format +typedef enum { - Format_Mono = Channel_Mono, - Format_MonoAlpha = Format_Mono | Channel_FullAlpha, - Format_Gray = Channel_Light | Channel_Dark, - Format_GrayAlpha = Format_Gray | Channel_FullAlpha, - Format_GreaterAlpha = Format_Mono | Channel_LightAlpha | - Channel_DarkAlpha -}; + format_mono = channel_mono, + format_mono_alpha = format_mono | channel_full_alpha, + format_gray = channel_light | channel_dark, + format_gray_alpha = format_gray | channel_full_alpha, + format_greater_alpha = format_mono | channel_light_alpha | + channel_dark_alpha +} format_t; /* - struct Structure - Describes an image's structure. + structure_t + Basically an image's dimensions, data pointer, and a few other useful + information such as the pitch in bytes. */ -struct Structure +typedef struct { int width, height; int layer_size; @@ -62,26 +51,37 @@ struct Structure const uint8_t *data; int columns; int end_size, end_bytes; -}; + +} structure_t; /* - struct Command - Contains a drawing operation's parameters. + command_t + The parameters of a drawing operation. A pointer to such a structure is + created by the public functions and passed down to the module's + sub-functions during rendering. */ -struct Command +typedef struct command_t { // Channel being drawn. - enum Channel channel; + channel_t channel; // Operation used (whether bopti_op_mono() or bopti_op_gray()). - void (*op)(int offset, uint32_t operator, struct Command *command); + void (*op)(int offset, uint32_t operator, struct command_t *command); // Portion of the bitmap which is drawn. 'top' and 'bottom' refer to // lines where 'left' and 'right' refer to column ids. int left, right, top, bottom; // Position of the bitmap on the screen. int x, y; - // Rectangle masks. + // Rectangle masks that define the drawing area. uint32_t masks[4]; -}; + // Video rams being used. + union { + // Different names, same fate. (Kingdom Hearts II) + uint32_t *vram; + uint32_t *v1; + }; + uint32_t *v2; + +} command_t; // The video ram addresses are set by the public functions and used internally // by the module. @@ -91,7 +91,7 @@ extern uint32_t *bopti_vram, *bopti_v1, *bopti_v2; //--- -// Some bopti routines. +// Internal bopti routines. //--- /* @@ -100,11 +100,12 @@ extern uint32_t *bopti_vram, *bopti_v1, *bopti_v2; image information. Since neutral bits are not the same for all operations, a mask is used to indicate which bits should be used for the operation. This mask is taken for the image's rectangle masks (see - module display for more information on rectangle masks). - Which operation is performed is determined by the channel setting. + the 'display' module internal header for more information on rectangle + masks). Which operation is performed is determined by the channel + setting of the command argument. */ -void bopti_op_mono(int offset, uint32_t operator, struct Command *c); -void bopti_op_gray(int offset, uint32_t operator, struct Command *c); +void bopti_op_mono(int offset, uint32_t operator, command_t *c); +void bopti_op_gray(int offset, uint32_t operator, command_t *c); /* bopti_grid() -- general form @@ -115,12 +116,11 @@ void bopti_op_gray(int offset, uint32_t operator, struct Command *c); The need for bopti_grid_a32() is not only linked to optimization, because bopti_grid() will perform a 32-bit shift when x is a multiple of 32, which is undefined behavior. - bopti_grid() calls bopti_grid_32() by default. + bopti_grid() automatically calls bopti_grid_a32() when required. */ void bopti_grid_a32(const uint32_t *layer, int columns, int height, - struct Command *c); -void bopti_grid(const uint32_t *layer, int columns, int height, - struct Command *c); + command_t *c); +void bopti_grid(const uint32_t *layer, int columns, int height, command_t *c); /* bopti_end_get() Returns an operator for the end of a line, whose width is lower than 32 @@ -138,23 +138,25 @@ uint32_t bopti_end_get2(const unsigned char **data); Draws the end of a layer, which can be considered as a whole layer whose with is lower than 32. (Actually is it lower or equal to 16; otherwise it would have been a column and the end would be empty). The - 'size' arguments is in bytes. + 'size' arguments is in bytes, thus 1 or 2. Unlike bopti_grid_a32(), bopti_end_nover() is not called automatically by bopti_end(). */ -void bopti_end_nover(const unsigned char *end, int size, struct Command *c); -void bopti_end(const unsigned char *end, int size, struct Command *c); +void bopti_end_nover(const unsigned char *end, int size, command_t *c); +void bopti_end(const unsigned char *end, int size, command_t *c); /* bopti() - Draws a layer in the video ram. + Draws a layer's data in the video ram areas specified in the command + argument. */ -void bopti(const unsigned char *layer, struct Structure *s, struct Command *c); +void bopti(const unsigned char *layer, structure_t *s, command_t *c); /* getStructure() - Determines the image size and data pointer. + Determines the image size (large images have a somehow different + structure), the data pointer and a few dimensions inside the image. */ -void getStructure(image_t *img, struct Structure *structure); +void getStructure(image_t *img, structure_t *structure); #endif // _INTERNALS_BOPTI_H diff --git a/include/internals/gint.h b/include/internals/gint.h index 601ff70..fd51e06 100644 --- a/include/internals/gint.h +++ b/include/internals/gint.h @@ -37,7 +37,7 @@ void gint_setvbr(uint32_t vbr, void (*setup)(void)); //--- -// Initialization and termination routines. +// Initialization and termination routines, environment saves. //--- /* @@ -53,29 +53,80 @@ void gint_init(void); */ void gint_quit(void); +#include +#include + +/* + environment_t + Structure where all registers used by gint are saved by default to + ensure that the operating system is not disturbed. +*/ +typedef struct +{ + // Interrupt controller. + uint16_t IPR[8]; + + // Real-Time Clock. + uint8_t RCR1, RCR2; + + // Timer Unit. + mod_tmu_timer_t TMU0, TMU1, TMU2; + uint8_t TSTR; + + // I/O ports for the keyboard driver. + uint16_t PACR, PBCR, PMCR; + uint8_t PADR, PBDR, PMDR; + +} environment_7705_t; + +typedef struct +{ + // Interrupt controller. + uint16_t IPR[12]; + + // Real-Time Clock. + uint8_t RCR1, RCR2; + + // Timer Unit. + mod_tmu_timer_t TMU0, TMU1, TMU2; + uint8_t TSTR; + + // I/O ports for the keyboard driver. + uint16_t PMCR, PNCR, PZCR; + uint8_t PMDR, PNDR, PZDR; + uint8_t key; + +} environment_7305_t; + +typedef union +{ + environment_7705_t env_7705; + environment_7305_t env_7305; + +} environment_t; + /* gint_save() - Saves many registers into a buffer to ensure that the system is not - upset by gint's configuration when the application ends. + Saves many registers into an internal environment buffer. */ -// void gint_save_7705(gint_save_buffer_t *buffer); -// void gint_save_7305(gint_save_buffer_t *buffer); +void gint_save_7705(environment_7705_t *env); +void gint_save_7305(environment_7305_t *env); /* - gint_setup() - Configures interrupt priorities and some parameters to allow gint to - take control of the interrupt flow. + gint_lock_and_setup() + Locks all interrupts (ie. disables them by default) and sets initial + values to all registers, allows specific interrupts, etc. */ -void gint_setup_7705(void); -void gint_setup_7305(void); +void gint_lock_and_setup_7705(void); +void gint_lock_and_setup_7305(void); /* - gint_restore() - Restores the parameters saved in a save buffer to give back the - interrupt control to the system. + gint_restore_and_unlock() + Restores the parameters saved in the environment buffer to give back + the interrupt control to the system. */ -// void gint_restore_7705(gint_save_buffer_t *buffer); -// void gint_restore_7305(gint_save_buffer_t *buffer); +void gint_restore_and_unlock_7705(environment_7705_t *env); +void gint_restore_and_unlock_7305(environment_7305_t *env); /* gint_reg() diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h index 738c1ee..2ed7f9e 100644 --- a/include/internals/keyboard.h +++ b/include/internals/keyboard.h @@ -5,11 +5,12 @@ #include #include -// Keyboard variables. +// Current keyboard state and keyboard interrupt flag. extern volatile uint8_t keyboard_state[10]; extern volatile int interrupt_flag; -// Key statistics. +// Delays (milliseconds) before repetitions, last key pressed, how many times +// it has been repeated already, time elapsed since last repetition (ms). extern int repeat_first, repeat_next; extern int last_key, last_repeats, last_time; diff --git a/include/internals/mmu.h b/include/internals/mmu.h index ced7211..1fcf6ac 100644 --- a/include/internals/mmu.h +++ b/include/internals/mmu.h @@ -3,8 +3,8 @@ // gint core module: mmu // // A wise application should avoid tampering with the system's -// configuration of the MMU and the TLB. This module implicitly calls the -// system but does nothing by itself. +// configuration of the MMU and the TLB. This module implicitly forces the +// system to load the required pages but does not interact with the TLB. // //--- diff --git a/include/internals/rtc.h b/include/internals/rtc.h index 4f3e348..4b9f9e0 100644 --- a/include/internals/rtc.h +++ b/include/internals/rtc.h @@ -9,20 +9,21 @@ #endif /* - struct rtc_cb - An RTC callback. + rtc_callback_t + An RTC callback with a unique id. */ -struct rtc_cb +typedef struct { rtc_frequency_t freq; int id; void (*callback)(void); int repeats; -}; + +} rtc_callback_t; // The callback array. -struct rtc_cb cb_array[RTC_CB_ARRAY_SIZE]; +rtc_callback_t cb_array[RTC_CB_ARRAY_SIZE]; /* rtc_perodic_interrupt() @@ -37,118 +38,4 @@ void rtc_periodic_interrupt(void); */ void rtc_cb_interrupt(void); -/* - struct mod_rtc - This structure describes the arrangement of RTC register in the memory. - Curious thing, on SH7705, registers RYRAR and RCR3 are at a completely - different address than the other ones. This module does not use these - registers, so they were not included in the structure. -*/ -#pragma pack(push, 1) - -struct mod_rtc -{ - unsigned char const R64CNT; - unsigned char _1; - - union { - unsigned char BYTE; - struct { - unsigned :1; - unsigned TENS :3; - unsigned ONES :4; - }; - } RSECCNT; - unsigned char _2; - - union { - unsigned char BYTE; - struct { - unsigned :1; - unsigned TENS :3; - unsigned ONES :4; - }; - } RMINCNT; - unsigned char _3; - - union { - unsigned char BYTE; - struct { - unsigned :2; - unsigned TENS :2; - unsigned ONES :4; - }; - } RHRCNT; - unsigned char _4; - - // 0 = Sunday, 1 = Monday, ..., 6 = Saturday, 7 = prohibited setting. - unsigned char RWKCNT; - unsigned char _5; - - union { - unsigned char BYTE; - struct { - unsigned :2; - unsigned TENS :2; - unsigned ONES :4; - }; - } RDAYCNT; - unsigned char _6; - - union { - unsigned char BYTE; - struct { - unsigned :3; - unsigned TENS :1; - unsigned ONES :4; - }; - } RMONCNT; - unsigned char _7; - - union { - unsigned short WORD; - struct { - unsigned THOUSANDS :4; - unsigned HUNDREDS :4; - unsigned TENS :4; - unsigned ONES :4; - }; - } RYRCNT; - unsigned char _8[12]; - - union { - unsigned char BYTE; - struct { - unsigned CF :1; - unsigned :2; - unsigned CIE :1; - unsigned AIE :1; - unsigned :2; - unsigned AF :1; - }; - } RCR1; - unsigned char _9; - - union { - unsigned char BYTE; - struct { - unsigned PEF :1; - unsigned PES :3; - unsigned :1; - unsigned ADJ :1; - unsigned RESET :1; - unsigned START :1; - }; - } RCR2; -} __attribute__((packed)); - -#pragma pack(pop) - -/* - We don't need to access the registers in a complicated way like the - function of the timer module. Let's make it simple. -*/ -#define RTC_SH7705 ((volatile struct mod_rtc *)0xfffffec0) -#define RTC_SH7305 ((volatile struct mod_rtc *)0xa413fec0) - #endif // _INTERNALS_RTC_H diff --git a/include/modules/macros.h b/include/modules/macros.h index 5f4403b..664fc7d 100644 --- a/include/modules/macros.h +++ b/include/modules/macros.h @@ -3,6 +3,15 @@ #include +// Padding is just empty space, we don't want to give it a name. There's also +// some subtle preprocessor trick to automatically add a (supposedly) unique +// name to each padding member. For instance the substitution may operate as: +// name(__LINE__) -> namesub(78) -> _##78 -> _78 +#define namesub(x) _##x +#define name(x) namesub(x) +#define pad(bytes) \ + uint8_t name(__LINE__)[bytes] \ + // Fixed-width types for bit field are totally meaningless. typedef unsigned uint; diff --git a/include/modules/rtc.h b/include/modules/rtc.h new file mode 100644 index 0000000..7eb7ef0 --- /dev/null +++ b/include/modules/rtc.h @@ -0,0 +1,131 @@ +#ifndef _MODULES_RTC_H +#define _MODULES_RTC_H + +#include +#include + +/* + mod_rtc_rcr1_t + First RTC configuration register. +*/ +typedef struct +{ + byte_union(, + uint CF :1; /* Carry flag */ + uint :2; + uint CIE :1; /* Carry interrupt enable */ + uint AIE :1; /* Alarm interrupt enable */ + uint :2; + uint AF :1; /* Alarm flag */ + ); + +} __attribute__((packed)) mod_rtc_rcr1_t; + +/* + mod_rtc_rcr2_t + Second RTC configuration register. +*/ +typedef struct +{ + byte_union(, + uint PEF :1; /* Periodic interrupt flag */ + uint PES :3; /* Periodic interrupt interval */ + uint :1; + uint ADJ :1; /* 30-second adjustment */ + uint RESET :1; /* Reset */ + uint START :1; /* Start bit */ + ); + +} __attribute__((packed)) mod_rtc_rcr2_t; + +/* + mod_rtc_rcr3_t + Third RTC configuration register. + +typedef struct +{ + byte_union(, + uint YAEN :1; + uint :7; + ); + +} __attribute__((packed)) mod_rtc_rcr3_t; +*/ + +/* + mod_rtc_time_t + A set of registers describing the current time in BCD format. +*/ +typedef struct +{ + const uint8_t R64CNT; /* More or less a 64-Hz counter */ + pad(1); + + byte_union(RSECCNT, /* Second count */ + uint :1; + uint TENS :3; + uint ONES :4; + ); + pad(1); + + byte_union(RMINCNT, /* Minute count */ + uint :1; + uint TENS :3; + uint ONES :4; + ); + pad(1); + + byte_union(RHRCNT, /* Hour count */ + uint :2; + uint TENS :2; + uint ONES :4; + ); + pad(1); + + /* 0 = Sunday .. 6 = Saturday, other settings are prohibited. */ + uint8_t RWKCNT; /* Day of week */ + pad(1); + + byte_union(RDAYCNT, /* Day count */ + uint :2; + uint TENS :2; + uint ONES :4; + ); + pad(1); + + byte_union(RMONCNT, /* Month count */ + uint :3; + uint TENS :1; + uint ONES :4; + ); + pad(1); + + word_union(RYRCNT, /* Year count */ + uint THOUSANDS :4; + uint HUNDREDS :4; + uint TENS :4; + uint ONES :4; + ); + +} __attribute__((packed, aligned(2))) mod_rtc_time_t; + +/* + mod_rtc_t (resides into gint memory) + Three control registers and timer information. +*/ +typedef struct +{ + /* RCR1, RCR2, and RCR3 are the same for both platforms. */ + volatile mod_rtc_rcr1_t *RCR1; + volatile mod_rtc_rcr2_t *RCR2; +// volatile mod_rtc_rcr3_t *RCR3; + /* Current time in register memory */ + volatile mod_rtc_time_t *time; + +} mod_rtc_t; + +// All you need to use is this structure, initialized by gint, which contains +// address that work with your execution platform. +extern mod_rtc_t RTC; + +#endif // _INTERNALS_RTC_H diff --git a/include/modules/timer.h b/include/modules/timer.h index 3a8b187..46657ae 100644 --- a/include/modules/timer.h +++ b/include/modules/timer.h @@ -59,6 +59,6 @@ typedef struct // The actual thing is there. It's initialized by gint at startup and contains // addresses and information suitable for the current platform. -extern volatile mod_tmu_t TMU; +extern mod_tmu_t TMU; #endif // _MODULES_TIMER diff --git a/include/mpu.h b/include/mpu.h index dd183bc..d3c751f 100644 --- a/include/mpu.h +++ b/include/mpu.h @@ -49,7 +49,7 @@ typedef enum // Global MPU variable, accessible for direct tests. Initialized at the // beginning of execution. -extern mpu_t MPU_CURRENT; +extern const mpu_t MPU_CURRENT; // Quick SH3 test. It is safer to assume that an unknown model is SH4 because // SH3-based models are not produced anymore. diff --git a/include/rtc.h b/include/rtc.h index 771ac2c..2c65876 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -2,7 +2,10 @@ // // gint core module: rtc // -// Manages RTC. This module is used behind standard module time. +// Manages the Real-Time Clock (RTC). This module provides access to the +// current time as well as regular callbacks at predefined frequencies, +// more or less like a timer. +// The standard time module is built upon this one. // //--- @@ -36,14 +39,14 @@ typedef struct Reads the current time from the RTC. There is no guarantee that the week day is correct (use the time API for that). */ -rtc_time_t rtc_getTime(void); +void rtc_getTime(rtc_time_t *time); /* rtc_setTime() Sets the time in the RTC registers. The week day is set to 0 if greater than 6. Other fields are not checked. */ -void rtc_setTime(rtc_time_t time); +void rtc_setTime(const rtc_time_t *time); @@ -58,14 +61,14 @@ void rtc_setTime(rtc_time_t time); */ typedef enum { - RTCFreq_500mHz = 7, - RTCFreq_1Hz = 6, - RTCFreq_2Hz = 5, - RTCFreq_4Hz = 4, - RTCFreq_16Hz = 3, - RTCFreq_64Hz = 2, - RTCFreq_256Hz = 1, - RTCFreq_None = 0, + rtc_freq_500mHz = 7, + rtc_freq_1Hz = 6, + rtc_freq_2Hz = 5, + rtc_freq_4Hz = 4, + rtc_freq_16Hz = 3, + rtc_freq_64Hz = 2, + rtc_freq_256Hz = 1, + rtc_freq_none = 0, } rtc_frequency_t; @@ -94,7 +97,7 @@ void rtc_cb_end(int id); -1 Callback does not exist -2 Invalid parameters This function never removes a callback. Call rtc_cb_end() for this. One - can set the function to NULL or the frequency to RTCFreq_None to + can set the function to NULL or the frequency to rtc_freq_none to temporarily disable the callback. */ int rtc_cb_edit(int id, rtc_frequency_t new_freq, void (*new_function)(void)); diff --git a/src/bopti/bopti_internals.c b/src/bopti/bopti_internals.c index c42d128..0469e81 100644 --- a/src/bopti/bopti_internals.c +++ b/src/bopti/bopti_internals.c @@ -12,17 +12,17 @@ uint32_t *bopti_vram, *bopti_v1, *bopti_v2; module display for more information on rectangle masks). Which operation is performed is determined by the channel setting. */ -void bopti_op_mono(int offset, uint32_t operator, struct Command *c) +void bopti_op_mono(int offset, uint32_t operator, command_t *c) { operator &= c->masks[offset & 3]; switch(c->channel) { - case Channel_FullAlpha: + case channel_full_alpha: bopti_vram[offset] &= ~operator; break; - case Channel_Mono: + case channel_mono: bopti_vram[offset] |= operator; break; @@ -30,31 +30,31 @@ void bopti_op_mono(int offset, uint32_t operator, struct Command *c) break; } } -void bopti_op_gray(int offset, uint32_t operator, struct Command *c) +void bopti_op_gray(int offset, uint32_t operator, command_t *c) { operator &= c->masks[offset & 3]; switch(c->channel) { - case Channel_FullAlpha: + case channel_full_alpha: bopti_v1[offset] &= ~operator; bopti_v2[offset] &= ~operator; break; - case Channel_LightAlpha: - case Channel_DarkAlpha: + case channel_light_alpha: + case channel_dark_alpha: break; - case Channel_Mono: + case channel_mono: bopti_v1[offset] |= operator; bopti_v2[offset] |= operator; break; - case Channel_Light: + case channel_light: bopti_v1[offset] |= operator; break; - case Channel_Dark: + case channel_dark: bopti_v2[offset] |= operator; break; } @@ -71,7 +71,7 @@ void bopti_op_gray(int offset, uint32_t operator, struct Command *c) of 32, which is undefined behavior. */ void bopti_grid_a32(const uint32_t *layer, int column_count, int height, - struct Command *c) + command_t *c) { int vram_column_offset = (c->y << 2) + (c->x >> 5); int vram_offset = vram_column_offset; @@ -91,7 +91,7 @@ void bopti_grid_a32(const uint32_t *layer, int column_count, int height, } } void bopti_grid(const uint32_t *layer, int column_count, int height, - struct Command *c) + command_t *c) { if(!column_count) return; if(!(c->x & 31)) @@ -177,9 +177,9 @@ uint32_t bopti_end_get2(const unsigned char **data) Draws the end of a layer, which can be considered as a whole layer whose with is lower than 32. (Actually is it lower or equal to 16; - otherwise it would have been a column and the end would be empty). + otherwise it would have been a column and the end would be empty.) */ -void bopti_end_nover(const unsigned char *end, int size, struct Command *c) +void bopti_end_nover(const unsigned char *end, int size, command_t *c) { uint32_t (*get)(const unsigned char **data) = (size == 2) ? bopti_end_get2 : bopti_end_get1; @@ -199,19 +199,17 @@ void bopti_end_nover(const unsigned char *end, int size, struct Command *c) { operator = (*get)(&end); operator <<= shift; - - (c->op)(vram_offset, operator, c); - + (c->op)(vram_offset, operator, c); vram_offset += 4; } } -void bopti_end(const unsigned char *end, int size, struct Command *c) +void bopti_end(const unsigned char *end, int size, command_t *c) { uint32_t (*get)(const unsigned char **data) = (size == 2) ? (bopti_end_get2) : (bopti_end_get1); int vram_offset = (c->y << 2) + (c->x >> 5); - uint32_t row_data, operator; + uint32_t row_data; int row; int shift_base = (32 - (size << 3)); @@ -225,15 +223,8 @@ void bopti_end(const unsigned char *end, int size, struct Command *c) for(row = c->top; row < c->bottom; row++) { row_data = (*get)(&end); - - operator = row_data >> shift1; - (c->op)(vram_offset, operator, c); - - - operator = row_data << shift2; - (c->op)(vram_offset + 1, operator, c); - - + (c->op)(vram_offset, row_data >> shift1, c); + (c->op)(vram_offset + 1, row_data << shift2, c); vram_offset += 4; } } @@ -248,7 +239,7 @@ void bopti_end(const unsigned char *end, int size, struct Command *c) bopti() Draws a layer in the video ram. */ -void bopti(const unsigned char *layer, struct Structure *s, struct Command *c) +void bopti(const unsigned char *layer, structure_t *s, command_t *c) { const unsigned char *grid, *end; int grid_columns, has_end; @@ -283,9 +274,10 @@ void bopti(const unsigned char *layer, struct Structure *s, struct Command *c) /* getStructure() - Determines the image size and data pointer. + Determines the image size (large images have a somehow different + structure), the data pointer and a few dimensions inside the image. */ -void getStructure(image_t *img, struct Structure *s) +void getStructure(image_t *img, structure_t *s) { int column_count, end, end_bytes, layer; diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c index 3d05d72..fe34859 100644 --- a/src/bopti/dimage.c +++ b/src/bopti/dimage.c @@ -12,12 +12,12 @@ void dimage_part(int x, int y, image_t *img, int left, int top, int width, { if(!img || img->magic != 0x01) return; - struct Structure s; - struct Command command; + structure_t s; + command_t command; int actual_width; int format = img->format, i = 0; - if(format != Format_Mono && format != Format_MonoAlpha) return; + if(format != format_mono && format != format_mono_alpha) return; getStructure(img, &s); if(width < 0) width = s.width; if(height < 0) height = s.height; diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c index c6c51c0..e570634 100644 --- a/src/bopti/gimage.c +++ b/src/bopti/gimage.c @@ -13,8 +13,8 @@ void gimage_part(int x, int y, image_t *img, int left, int top, int width, { if(!img || img->magic != 0x01) return; - struct Structure s; - struct Command command; + structure_t s; + command_t command; int actual_width; int format = img->format, i = 0; diff --git a/src/clock/clock.c b/src/clock/clock.c index 9da10f8..8084c4d 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -143,7 +143,7 @@ void clock_measure(void) { htimer_7705 = htimer_setup(timer_user, 0xffffffff, timer_Po_4, 1); - cb_id_7705 = rtc_cb_add(RTCFreq_256Hz, clock_measure_7705, 0); + cb_id_7705 = rtc_cb_add(rtc_freq_256Hz, clock_measure_7705, 0); } // On SH7305, assuming clock mode 3, we can compute the clock @@ -268,5 +268,5 @@ static void clock_measure_7705_callback(void) static void clock_measure_7705(void) { timer_start(htimer_7705); - rtc_cb_edit(cb_id_7705, RTCFreq_256Hz, clock_measure_7705_callback); + rtc_cb_edit(cb_id_7705, rtc_freq_256Hz, clock_measure_7705_callback); } diff --git a/src/core/crt0.c b/src/core/crt0.c index a343a98..c00aeb9 100644 --- a/src/core/crt0.c +++ b/src/core/crt0.c @@ -20,7 +20,7 @@ extern uint32_t etext, btext, // Location of .text section bdata, edata, // Location of .data section bbss, ebss, // Location of .bss section - bgint, egint, // Location of interrut handler and gint data + bgint, egint, // Location of interrupt handler and gint data gint_vbr, // VBR address romdata; // ROM address of .data section contents @@ -45,7 +45,7 @@ __attribute__((section(".pretext.entry"))) int start(void) { #ifdef GINT_DIAGNOSTICS - // Ok, so fill as much information as possible so that in case anything + // OK, so fill as much information as possible so that in case anything // goes wrong we can try to analyze what's been going on. volatile gint_diagnostics_t *dg = gint_diagnostics(); @@ -77,7 +77,7 @@ __attribute__((section(".pretext.entry"))) int start(void) dg->section_data.length = (uint32_t)&edata - (uint32_t)&bdata; dg->section_bss.address = (uint32_t)&bbss; dg->section_bss.length = (uint32_t)&ebss - (uint32_t)&bbss; - dg->romdata = (uint32_t)&romdata; + dg->romdata = (uint32_t)&romdata; // Basic information about the running library. dg->vbr_address = (uint32_t)&gint_vbr; @@ -108,10 +108,11 @@ __attribute__((section(".pretext.entry"))) int start(void) dg->stage = stage_mmu; #endif - // Detecting the MPU type, initializing the register module addresses, - // and initializing gint. - mpu_init(); - mod_init(); + // Initializing gint, which does several things: + // - Detect the MPU and platform + // - Initialize register addresses in a platform-independent way + // - Save the current environment information in a large buffer + // - Set up the interrupt handler and configure everything gint needs gint_init(); #ifdef GINT_DIAGNOSTICS @@ -148,16 +149,15 @@ __attribute__((section(".pretext.entry"))) int start(void) if(!x) exit_code = main(); + #ifdef GINT_DIAGNOSTICS dg->stage = stage_leaving; #endif /* TODO Flush and close opened streams. */ - // Calling exit handlers. + // Calling exit handlers and destructors. while(atexit_index > 0) (*atexit_handlers[--atexit_index])(); - - // Calling the destructors. fini(); #ifdef GINT_DIAGNOSTICS diff --git a/src/core/exceptions.c b/src/core/exceptions.c index 9491221..ad2ac4a 100644 --- a/src/core/exceptions.c +++ b/src/core/exceptions.c @@ -33,8 +33,8 @@ static void show_error(const char *name, uint32_t *access_mode, uint32_t *tea, { uint32_t *vram = display_getCurrentVRAM(); uint32_t spc, ssr; - __asm__("stc spc, %0" : "=rm"(spc)); - __asm__("stc ssr, %0" : "=rm"(ssr)); + __asm__("stc spc, %0" : "=r"(spc)); + __asm__("stc ssr, %0" : "=r"(ssr)); dclear(); text_configure(NULL, color_black); diff --git a/src/core/gint.c b/src/core/gint.c index 608bce5..6d309f9 100644 --- a/src/core/gint.c +++ b/src/core/gint.c @@ -1,12 +1,3 @@ -//--- -// -// gint core module: interrupt handler -// -// Central point of the library. Controls the interrupt handler and -// defines a few functions to configure callbacks for some interrupts. -// -//--- - #include #include #include @@ -110,10 +101,10 @@ void gint_invoke(uint8_t type, uint8_t subtype) uint16_t *pc; // Getting some initial information to work with. - __asm__("stc spc, %0" : "=rm"(pc)); + __asm__("stc spc, %0" : "=r"(pc)); tea = gint_reg(register_tea); tra = gint_reg(register_tra); - tcpr_2 = (void *)0xfffffeb8; + tcpr_2 = (void *)0xfffffeb8; /* SH7705 only */ // Building up the argument list. for(int i = 0; i < 3; i++) switch(gint_handlers[type].args[i]) diff --git a/src/core/gint_sh7305.c b/src/core/gint_sh7305.c index 7410084..9c54118 100644 --- a/src/core/gint_sh7305.c +++ b/src/core/gint_sh7305.c @@ -1,15 +1,6 @@ -//--- -// -// gint core module: sh7305 interrupt handler -// -// Of course all the work related to interrupts is heavily platform- -// dependent. This module handles interrupts and configures the MPU to -// save and restore the system's configuration when execution ends. -// -//--- - #include -#include +#include +#include #include #include #include <7305.h> @@ -38,108 +29,108 @@ volatile void *gint_reg_7305(gint_register_t reg) //--- -// Setup. +// Register saves, setup, interrupt locks, register restoration. //--- -static unsigned short ipr[12]; -static unsigned char rcr2; -// Saves of the keyboard registers. Could be better. -static unsigned short inj1, inj2, det; -static unsigned char data1, data2, keys, reg; - -static void gint_priority_lock_7305(void) +void gint_save_7305(environment_7305_t *e) { - // Saving the current interrupt priorities. - ipr[0] = INTX.IPRA.WORD; - ipr[1] = INTX.IPRB.WORD; - ipr[2] = INTX.IPRC.WORD; - ipr[3] = INTX.IPRD.WORD; - ipr[4] = INTX.IPRE.WORD; - ipr[5] = INTX.IPRF.WORD; - ipr[6] = INTX.IPRG.WORD; - ipr[7] = INTX.IPRH.WORD; - ipr[8] = INTX.IPRI.WORD; - ipr[9] = INTX.IPRJ.WORD; - ipr[10] = INTX.IPRK.WORD; - ipr[11] = INTX.IPRL.WORD; + // Saving interrupt priorities. + e->IPR[0] = INTX.IPRA.WORD; + e->IPR[1] = INTX.IPRB.WORD; + e->IPR[2] = INTX.IPRC.WORD; + e->IPR[3] = INTX.IPRD.WORD; + e->IPR[4] = INTX.IPRE.WORD; + e->IPR[5] = INTX.IPRF.WORD; + e->IPR[6] = INTX.IPRG.WORD; + e->IPR[7] = INTX.IPRH.WORD; + e->IPR[8] = INTX.IPRI.WORD; + e->IPR[9] = INTX.IPRJ.WORD; + e->IPR[10] = INTX.IPRK.WORD; + e->IPR[11] = INTX.IPRL.WORD; + // Saving RTC registers. + e->RCR1 = RTC.RCR1->byte; + e->RCR2 = RTC.RCR2->byte; + + // Saving TMU registers. + e->TMU0 = *(TMU.timers[0]); + e->TMU1 = *(TMU.timers[1]); + e->TMU2 = *(TMU.timers[2]); + e->TSTR = TMU.TSTR->byte; + + // Saving port data used to access the keyboard. + e->PMCR = *((volatile uint16_t *)0xa4050116); + e->PMDR = *((volatile uint8_t *)0xa4050136); + e->PNCR = *((volatile uint16_t *)0xa4050118); + e->PNDR = *((volatile uint8_t *)0xa4050138); + e->PZCR = *((volatile uint16_t *)0xa405014c); + e->PZDR = *((volatile uint8_t *)0xa405016c); + e->key = *((volatile uint8_t *)0xa40501c6); +} + +void gint_lock_and_setup_7305(void) +{ // Disabling everything by default to avoid freezing on non-handled // interrupts. - INTX.IPRA.WORD = 0x0000; - INTX.IPRB.WORD = 0x0000; - INTX.IPRC.WORD = 0x0000; - INTX.IPRD.WORD = 0x0000; - INTX.IPRE.WORD = 0x0000; - INTX.IPRF.WORD = 0x0000; - INTX.IPRG.WORD = 0x0000; - INTX.IPRH.WORD = 0x0000; - INTX.IPRI.WORD = 0x0000; - INTX.IPRJ.WORD = 0x0000; - INTX.IPRK.WORD = 0x0000; - INTX.IPRL.WORD = 0x0000; + INTX.IPRA.WORD = 0x0000; + INTX.IPRB.WORD = 0x0000; + INTX.IPRC.WORD = 0x0000; + INTX.IPRD.WORD = 0x0000; + INTX.IPRE.WORD = 0x0000; + INTX.IPRF.WORD = 0x0000; + INTX.IPRG.WORD = 0x0000; + INTX.IPRH.WORD = 0x0000; + INTX.IPRI.WORD = 0x0000; + INTX.IPRJ.WORD = 0x0000; + INTX.IPRK.WORD = 0x0000; + INTX.IPRL.WORD = 0x0000; - // Saving keyboard registers. - inj1 = *((volatile unsigned short *)0xa4050116); - data1 = *((volatile unsigned char *)0xa4050136); - inj2 = *((volatile unsigned short *)0xa4050118); - data2 = *((volatile unsigned char *)0xa4050138); - det = *((volatile unsigned short *)0xa405014c); - keys = *((volatile unsigned char *)0xa405016c); - reg = *((volatile unsigned char *)0xa40501c6); - - // Allowing RTC. Keyboard analysis is done regularly using a RTC - // because SH7305's special KEYSC interface does not allow us to clear - // the keyboard interrupt flags. + // Allowing RTC and timer to schedule automatic tasks such as keyboard + // analysis. INTX.IPRK._RTC = 10; INTX.IPRA.TMU0_0 = 12; INTX.IPRA.TMU0_1 = 12; INTX.IPRA.TMU0_2 = 12; + + // Don't enable the RTC interrupt by default. + RTC.RCR2->byte = 0x09; } -static void gint_priority_unlock_7305(void) +void gint_restore_and_unlock_7305(environment_7305_t *e) { // Restoring the interrupt priorities. - INTX.IPRA.WORD = ipr[0]; - INTX.IPRB.WORD = ipr[1]; - INTX.IPRC.WORD = ipr[2]; - INTX.IPRD.WORD = ipr[3]; - INTX.IPRE.WORD = ipr[4]; - INTX.IPRF.WORD = ipr[5]; - INTX.IPRG.WORD = ipr[6]; - INTX.IPRH.WORD = ipr[7]; - INTX.IPRI.WORD = ipr[8]; - INTX.IPRJ.WORD = ipr[9]; - INTX.IPRK.WORD = ipr[10]; - INTX.IPRL.WORD = ipr[11]; + INTX.IPRA.WORD = e->IPR[0]; + INTX.IPRB.WORD = e->IPR[1]; + INTX.IPRC.WORD = e->IPR[2]; + INTX.IPRD.WORD = e->IPR[3]; + INTX.IPRE.WORD = e->IPR[4]; + INTX.IPRF.WORD = e->IPR[5]; + INTX.IPRG.WORD = e->IPR[6]; + INTX.IPRH.WORD = e->IPR[7]; + INTX.IPRI.WORD = e->IPR[8]; + INTX.IPRJ.WORD = e->IPR[9]; + INTX.IPRK.WORD = e->IPR[10]; + INTX.IPRL.WORD = e->IPR[11]; - // Restoring keyboard registers. - *((volatile unsigned short *)0xa4050116) = inj1; - *((volatile unsigned char *)0xa4050136) = data1; - *((volatile unsigned short *)0xa4050118) = inj2; - *((volatile unsigned char *)0xa4050138) = data2; - *((volatile unsigned short *)0xa405014c) = det; - *((volatile unsigned char *)0xa405016c) = keys; - *((volatile unsigned char *)0xa40501c6) = reg; -} - -void gint_setup_7305(void) -{ - volatile struct mod_rtc *RTC = RTC_SH7305; - - gint_priority_lock_7305(); - - // Saving the RTC configuration. - rcr2 = RTC->RCR2.BYTE; - // Disabling RTC interrupts by default. - RTC->RCR2.BYTE = 0x09; -} - -void gint_stop_7305(void) -{ - volatile struct mod_rtc *RTC = RTC_SH7305; - - gint_priority_unlock_7305(); - - // Restoring the RTC configuration. - RTC->RCR2.BYTE = rcr2; + // Restoring RTC registers. + RTC.RCR1->byte = e->RCR1; + RTC.RCR2->byte = e->RCR2; + + // Restoring TMU registers. + *(TMU.timers[0]) = e->TMU0; + *(TMU.timers[1]) = e->TMU1; + *(TMU.timers[2]) = e->TMU2; + TMU.TSTR->byte = e->TSTR; + + // Restoring keyboard-related I/O port registers. However the backlight + // pin is in PNDR and we would like the backlight to persist when we + // leave the application, so we just keep this bit. + *((volatile uint16_t *)0xa4050116) = e->PMCR; + *((volatile uint8_t *)0xa4050136) = e->PMDR; + *((volatile uint16_t *)0xa4050118) = e->PNCR; + *((volatile uint8_t *)0xa4050138) &= 0x10; + *((volatile uint8_t *)0xa4050138) |= (e->PNDR & ~0x10); + *((volatile uint16_t *)0xa405014c) = e->PZCR; + *((volatile uint8_t *)0xa405016c) = e->PZDR; + *((volatile uint8_t *)0xa40501c6) = e->key; } diff --git a/src/core/gint_sh7705.c b/src/core/gint_sh7705.c index 9f5ac26..d19924d 100644 --- a/src/core/gint_sh7705.c +++ b/src/core/gint_sh7705.c @@ -1,15 +1,5 @@ -//--- -// -// gint core module: sh7705 interrupt handler -// -// Of course all the work related to interrupts is heavily platform- -// dependent. This module handles interrupts and configures the MPU to -// save and restore the system's configuration when execution ends. -// -//--- - #include -#include +#include #include #include #include <7705.h> @@ -38,74 +28,92 @@ volatile void *gint_reg_7705(gint_register_t reg) //--- -// Setup. +// Register saves, setup, interrupt locks, register restoration. //--- -static unsigned short iprs[8]; -static unsigned char rcr2; - -static void gint_priority_lock_7705(void) +void gint_save_7705(environment_7705_t *e) { // Saving the interrupt masks from registers IPRA to IPRH. - iprs[0] = INTC.IPRA.WORD; - iprs[1] = INTC.IPRB.WORD; - iprs[2] = INTX.IPRC.WORD; - iprs[3] = INTX.IPRD.WORD; - iprs[4] = INTX.IPRE.WORD; - iprs[5] = INTX.IPRF.WORD; - iprs[6] = INTX.IPRG.WORD; - iprs[7] = INTX.IPRH.WORD; + e->IPR[0] = INTC.IPRA.WORD; + e->IPR[1] = INTC.IPRB.WORD; + e->IPR[2] = INTX.IPRC.WORD; + e->IPR[3] = INTX.IPRD.WORD; + e->IPR[4] = INTX.IPRE.WORD; + e->IPR[5] = INTX.IPRF.WORD; + e->IPR[6] = INTX.IPRG.WORD; + e->IPR[7] = INTX.IPRH.WORD; + // Saving RTC registers. + e->RCR1 = RTC.RCR1->byte; + e->RCR2 = RTC.RCR2->byte; + + // Saving TMU registers. + e->TMU0 = *(TMU.timers[0]); + e->TMU1 = *(TMU.timers[1]); + e->TMU2 = *(TMU.timers[2]); + e->TSTR = TMU.TSTR->byte; + + // Saving port data used to access the keyboard. + e->PACR = PFC.PACR.WORD; + e->PADR = PA.DR.BYTE; + e->PBCR = PFC.PBCR.WORD; + e->PBDR = PB.DR.BYTE; + e->PMCR = PFC.PMCR.WORD; + e->PMDR = PM.DR.BYTE; +} + +void gint_lock_and_setup_7705(void) +{ // Disabling everything by default to avoid receiving an interrupt that // the handler doesn't handle, which would cause the user program to // freeze. - INTC.IPRA.WORD = 0x0000; - INTC.IPRB.WORD = 0x0000; - INTX.IPRC.WORD = 0x0000; - INTX.IPRD.WORD = 0x0000; - INTX.IPRE.WORD = 0x0000; - INTX.IPRF.WORD = 0x0000; - INTX.IPRG.WORD = 0x0000; - INTX.IPRH.WORD = 0x0000; + INTC.IPRA.WORD = 0x0000; + INTC.IPRB.WORD = 0x0000; + INTX.IPRC.WORD = 0x0000; + INTX.IPRD.WORD = 0x0000; + INTX.IPRE.WORD = 0x0000; + INTX.IPRF.WORD = 0x0000; + INTX.IPRG.WORD = 0x0000; + INTX.IPRH.WORD = 0x0000; - // Allowing RTC, which handles keyboard. + // Allowing RTC and timer (which handles keyboard and a whole bunch of + // other things). INTC.IPRA.BIT._RTC = 10; INTC.IPRA.BIT._TMU0 = 12; INTC.IPRA.BIT._TMU1 = 12; INTC.IPRA.BIT._TMU2 = 12; + + // Don't enable RTC periodic signals by default. + RTC.RCR2->byte = 0x09; } -static void gint_priority_unlock_7705(void) +void gint_restore_and_unlock_7705(environment_7705_t *e) { // Restoring the saved states. - INTC.IPRA.WORD = iprs[0]; - INTC.IPRB.WORD = iprs[1]; - INTX.IPRC.WORD = iprs[2]; - INTX.IPRD.WORD = iprs[3]; - INTX.IPRE.WORD = iprs[4]; - INTX.IPRF.WORD = iprs[5]; - INTX.IPRG.WORD = iprs[6]; - INTX.IPRH.WORD = iprs[7]; -} - -void gint_setup_7705(void) -{ - volatile struct mod_rtc *RTC = RTC_SH7705; - - gint_priority_lock_7705(); - - // Saving the RTC configuration. - rcr2 = RTC->RCR2.BYTE; - // Disabling RTC interrupts by default. - RTC->RCR2.BYTE = 0x09; -} - -void gint_stop_7705(void) -{ - volatile struct mod_rtc *RTC = RTC_SH7705; - - gint_priority_unlock_7705(); - - // Restoring the RTC configuration. - RTC->RCR2.BYTE = rcr2; + INTC.IPRA.WORD = e->IPR[0]; + INTC.IPRB.WORD = e->IPR[1]; + INTX.IPRC.WORD = e->IPR[2]; + INTX.IPRD.WORD = e->IPR[3]; + INTX.IPRE.WORD = e->IPR[4]; + INTX.IPRF.WORD = e->IPR[5]; + INTX.IPRG.WORD = e->IPR[6]; + INTX.IPRH.WORD = e->IPR[7]; + + // Restoring RTC registers. + RTC.RCR1->byte = e->RCR1; + RTC.RCR2->byte = e->RCR2; + + // Restoring TMU registers. + *(TMU.timers[0]) = e->TMU0; + *(TMU.timers[1]) = e->TMU1; + *(TMU.timers[2]) = e->TMU2; + TMU.TSTR->byte = e->TSTR; + + // Restoring keyboard-related I/O port registers. + PFC.PACR.WORD = e->PACR; + PA.DR.BYTE = e->PADR; + PFC.PBCR.WORD = e->PBCR; + PB.DR.BYTE = e->PBDR; + PFC.PMCR.WORD = e->PMCR; + PM.DR.BYTE = e->PMDR; } diff --git a/src/core/gint_vbr.s b/src/core/gint_vbr.s index 7253a91..067aadb 100644 --- a/src/core/gint_vbr.s +++ b/src/core/gint_vbr.s @@ -49,7 +49,7 @@ _gint_setvbr: jsr @r5 nop - /* Activating interrupts again. */ + /* Enabling interrupts again. */ mov.l sr_block, r0 not r0, r0 stc sr, r3 diff --git a/src/core/init_quit.c b/src/core/init_quit.c index ac16dd0..a50d287 100644 --- a/src/core/init_quit.c +++ b/src/core/init_quit.c @@ -1,9 +1,11 @@ #include #include +#include #include #include gint_info_t gint; +static environment_t env; //--- // Initialization routines @@ -16,10 +18,17 @@ gint_info_t gint; */ static void setup(void) { - isSH3() ? gint_setup_7705() : gint_setup_7305(); + isSH3() ? gint_lock_and_setup_7705() + : gint_lock_and_setup_7305(); } void gint_init(void) { + // Detecting the MPU type. I don't like const-casting but this is still + // better than allowing the user to change the variable by mistake. + *((mpu_t *)&MPU_CURRENT) = getMPU(); + // Loading the register addresses of the current platform. + mod_init(); + // Linker script symbols -- gint. extern uint32_t gint_vbr, @@ -32,6 +41,9 @@ void gint_init(void) // Loading the interrupt handler into the memory. while(ptr < &egint) *ptr++ = *src++; + isSH3() ? gint_save_7705(&env.env_7705) + : gint_save_7305(&env.env_7305); + // Installing gint's default exception/interrupt handlers. for(int i = 0; i < exc_type_max; i++) { @@ -59,7 +71,8 @@ void gint_init(void) */ static void stop(void) { - isSH3() ? gint_stop_7705() : gint_stop_7305(); + isSH3() ? gint_restore_and_unlock_7705(&env.env_7705) + : gint_restore_and_unlock_7305(&env.env_7305); } void gint_quit(void) { diff --git a/src/core/modules.c b/src/core/modules.c index aad3491..60cc146 100644 --- a/src/core/modules.c +++ b/src/core/modules.c @@ -1,4 +1,6 @@ #include +#include + #include #include @@ -11,7 +13,8 @@ // confront to the hardware directly. //--- -volatile mod_tmu_t TMU; +mod_tmu_t TMU; +mod_rtc_t RTC; @@ -26,6 +29,10 @@ static void mod_init_7705(void) TMU.timers[2] = (void *)0xfffffeac; TMU.TSTR = (void *)0xfffffe92; TMU.TCPR2 = (void *)0xfffffeb8; + + RTC.RCR1 = (void *)0xfffffedc; + RTC.RCR2 = (void *)0xfffffede; + RTC.time = (void *)0xfffffec0; } static void mod_init_7305(void) @@ -35,6 +42,10 @@ static void mod_init_7305(void) TMU.timers[2] = (void *)0xa4490020; TMU.TSTR = (void *)0xa4490004; TMU.TCPR2 = NULL; + + RTC.RCR1 = (void *)0xa413fedc; + RTC.RCR2 = (void *)0xa413fede; + RTC.time = (void *)0xa413fec0; } /* diff --git a/src/core/mpu.c b/src/core/mpu.c index 13f621c..a952e67 100644 --- a/src/core/mpu.c +++ b/src/core/mpu.c @@ -8,7 +8,7 @@ #include -mpu_t MPU_CURRENT; +const mpu_t MPU_CURRENT; /* getMPU() @@ -67,14 +67,3 @@ mpu_t getMPU(void) // By default, the MPU is unknown. return mpu_unknown; } - - - -/* - mpu_init() - Determines the MPU type and stores the result into MPU_CURRENT. -*/ -__attribute__((constructor)) void mpu_init(void) -{ - MPU_CURRENT = getMPU(); -} diff --git a/src/keyboard/keyboard_sh7305.c b/src/keyboard/keyboard_sh7305.c index cae413a..68e1fc8 100644 --- a/src/keyboard/keyboard_sh7305.c +++ b/src/keyboard/keyboard_sh7305.c @@ -30,6 +30,8 @@ many keys on the same column are pressed, other keys of the same column may be triggered. + (The following values do not apply to the latest tests, even if the + trend remains the same.) - Less Bad key detection. - 8 Very few column effects. Most often, three keys may be pressed simultaneously. However, [UP] has latencies and is globally not @@ -49,7 +51,8 @@ static void kdelay(void) __asm__ ( - "nop\n\t" + r4(r4("nop\n\t")) + r4(r4("nop\n\t")) ); #undef r4 diff --git a/src/rtc/rtc_callback.c b/src/rtc/rtc_callback.c index 488d0ce..1efeb24 100644 --- a/src/rtc/rtc_callback.c +++ b/src/rtc/rtc_callback.c @@ -1,13 +1,14 @@ #include +#include #include #include // Array holding callback informations. -struct rtc_cb cb_array[RTC_CB_ARRAY_SIZE] = { 0 }; +rtc_callback_t cb_array[RTC_CB_ARRAY_SIZE] = { 0 }; // Callback identifier (unique). static int unique_id = 1; // Current RTC interrupt frequency. -static rtc_frequency_t rtc_freq = RTCFreq_None; +static rtc_frequency_t rtc_freq = rtc_freq_none; // 256-Hz tick count. This counter is stopped when no callback is registered. static unsigned elapsed256 = 0; @@ -21,7 +22,7 @@ static unsigned elapsed256 = 0; */ static void rtc_cb_update(void) { - rtc_frequency_t max = RTCFreq_None; + rtc_frequency_t max = rtc_freq_none; int n; for(n = 0; n < RTC_CB_ARRAY_SIZE; n++) if(cb_array[n].id) @@ -33,8 +34,7 @@ static void rtc_cb_update(void) if(rtc_freq == max) return; rtc_freq = max; - volatile struct mod_rtc *RTC = isSH3() ? RTC_SH7705 : RTC_SH7305; - RTC->RCR2.BYTE = (rtc_freq << 4) | 0x09; + RTC.RCR2->byte = (rtc_freq << 4) | 0x09; } /* @@ -49,7 +49,7 @@ static void rtc_cb_update(void) int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats) { int n = 0; - if(freq == RTCFreq_None || !function || repeats < 0) return -2; + if(freq == rtc_freq_none || !function || repeats < 0) return -2; while(n < RTC_CB_ARRAY_SIZE && cb_array[n].id) n++; if(n >= RTC_CB_ARRAY_SIZE) return -1; @@ -77,7 +77,7 @@ void rtc_cb_end(int id) if(n >= RTC_CB_ARRAY_SIZE) return; cb_array[n].id = 0; - cb_array[n].freq = RTCFreq_None; + cb_array[n].freq = rtc_freq_none; cb_array[n].callback = NULL; cb_array[n].repeats = 0; @@ -134,7 +134,7 @@ void rtc_cb_interrupt(void) for(n = 0; n < RTC_CB_ARRAY_SIZE; n++) { - struct rtc_cb *cb = &cb_array[n]; + rtc_callback_t *cb = &cb_array[n]; if(!cb->id || !cb->freq) continue; // Only execute callback when the number of elapsed 256-Hz diff --git a/src/rtc/rtc_getTime.c b/src/rtc/rtc_getTime.c index 5a460e9..dab7074 100644 --- a/src/rtc/rtc_getTime.c +++ b/src/rtc/rtc_getTime.c @@ -1,16 +1,17 @@ #include +#include #include #include /* - integer() + integer8(), integer16() [static] Converts a BCD value to an integer. */ -static int integer8(int bcd) +static int integer8(uint8_t bcd) { return (bcd & 0x0f) + 10 * (bcd >> 4); } -static int integer16(int bcd) +static int integer16(uint16_t bcd) { return (bcd & 0xf) + 10 * ((bcd >> 4) & 0xf) + 100 * ((bcd >> 8) & 0xf) + 1000 * (bcd >> 12); @@ -19,26 +20,23 @@ static int integer16(int bcd) /* rtc_getTime() Reads the current time from the RTC. There is no guarantee that the - week day is correct (use the time API for that). + week day will contain a correct value (use the time API for that). */ -rtc_time_t rtc_getTime(void) +void rtc_getTime(rtc_time_t *time) { - volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305; - rtc_time_t time; + if(!time) return; do { - rtc->RCR1.CF = 0; + RTC.RCR1->CF = 0; - time.seconds = integer8(rtc->RSECCNT.BYTE); - time.minutes = integer8(rtc->RMINCNT.BYTE); - time.hours = integer8(rtc->RHRCNT.BYTE); - time.month_day = integer8(rtc->RDAYCNT.BYTE); - time.month = integer8(rtc->RMONCNT.BYTE); - time.year = integer16(rtc->RYRCNT.WORD); - time.week_day = rtc->RWKCNT; + time->seconds = integer8(RTC.time->RSECCNT.byte); + time->minutes = integer8(RTC.time->RMINCNT.byte); + time->hours = integer8(RTC.time->RHRCNT .byte); + time->month_day = integer8(RTC.time->RDAYCNT.byte); + time->month = integer8(RTC.time->RMONCNT.byte); + time->year = integer16(RTC.time->RYRCNT.word); + time->week_day = RTC.time->RWKCNT; } - while(rtc->RCR1.CF != 0); - - return time; + while(RTC.RCR1->CF != 0); } diff --git a/src/rtc/rtc_interrupt.c b/src/rtc/rtc_interrupt.c index 32a2c73..c3e0df6 100644 --- a/src/rtc/rtc_interrupt.c +++ b/src/rtc/rtc_interrupt.c @@ -1,9 +1,8 @@ #include +#include #include #include -int rtc_carry_flag = 0; - /* rtc_periodic_interrupt() Handles an RTC interrupt by calling the callback. @@ -12,6 +11,5 @@ void rtc_periodic_interrupt(void) { rtc_cb_interrupt(); - volatile struct mod_rtc *RTC = isSH3() ? RTC_SH7705 : RTC_SH7305; - RTC->RCR2.PEF = 0; + RTC.RCR2->PEF = 0; } diff --git a/src/rtc/rtc_setTime.c b/src/rtc/rtc_setTime.c index 4e71e7f..27c8a3a 100644 --- a/src/rtc/rtc_setTime.c +++ b/src/rtc/rtc_setTime.c @@ -1,17 +1,18 @@ #include +#include #include #include /* - bcd() + bcd8(), bcd16() [static] Converts an integer to a BCD value. */ -static int bcd8(int integer) +static uint8_t bcd8(int integer) { integer %= 100; return ((integer / 10) << 4) | (integer % 10); } -static int bcd16(int integer) +static uint16_t bcd16(int integer) { integer %= 10000; return (bcd8(integer / 100) << 8) | bcd8(integer % 100); @@ -22,22 +23,22 @@ static int bcd16(int integer) Sets the time in the RTC registers. The week day is set to 0 if greater than 6. Other fields are not checked. */ -void rtc_setTime(rtc_time_t time) +void rtc_setTime(const rtc_time_t *time) { - volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305; - int wday = (time.week_day < 7) ? (time.week_day) : (0); + if(!time) return; + int wday = (time->week_day < 7) ? (time->week_day) : (0); do { - rtc->RCR1.CF = 0; + RTC.RCR1->CF = 0; - rtc->RSECCNT.BYTE = bcd8(time.seconds); - rtc->RMINCNT.BYTE = bcd8(time.minutes); - rtc->RHRCNT.BYTE = bcd8(time.hours); - rtc->RDAYCNT.BYTE = bcd8(time.month_day); - rtc->RMONCNT.BYTE = bcd8(time.month); - rtc->RYRCNT.WORD = bcd16(time.year); - rtc->RWKCNT = wday; + RTC.time->RSECCNT.byte = bcd8(time->seconds); + RTC.time->RMINCNT.byte = bcd8(time->minutes); + RTC.time->RHRCNT .byte = bcd8(time->hours); + RTC.time->RDAYCNT.byte = bcd8(time->month_day); + RTC.time->RMONCNT.byte = bcd8(time->month); + RTC.time->RYRCNT .word = bcd16(time->year); + RTC.time->RWKCNT = wday; } - while(rtc->RCR1.CF != 0); + while(RTC.RCR1->CF != 0); } diff --git a/src/time/time.c b/src/time/time.c index e6a6727..e3edd16 100644 --- a/src/time/time.c +++ b/src/time/time.c @@ -12,10 +12,11 @@ */ time_t time(time_t *timeptr) { - rtc_time_t rtc = rtc_getTime(); + rtc_time_t rtc; struct tm tm; time_t calendar; + rtc_getTime(&rtc); tm.tm_sec = rtc.seconds; tm.tm_min = rtc.minutes; tm.tm_hour = rtc.hours; diff --git a/version b/version new file mode 100644 index 0000000..91cef21 --- /dev/null +++ b/version @@ -0,0 +1 @@ +beta-0.9-289 From a529a8236baa11fd4fd81efdff16b46f5b537927 Mon Sep 17 00:00:00 2001 From: lephe Date: Mon, 17 Apr 2017 12:56:27 +0200 Subject: [PATCH 09/24] Just a hotfix for dimage()... fxSDK's a bit late... --- Makefile | 27 ++++++++++++++++----------- demo/gintdemo.c | 12 +++++++++--- demo/test_tales.c | 4 +++- include/gint.h | 7 +++++++ src/bopti/dimage.c | 2 +- src/bopti/gimage.c | 2 +- src/core/crt0.c | 2 +- src/core/mpu.c | 18 +++++++++--------- src/ctype/ctype_classes.c | 4 ++-- src/display/adjustRectangle.c | 2 +- src/tales/tales_internals.c | 2 ++ version | 2 +- 12 files changed, 53 insertions(+), 31 deletions(-) diff --git a/Makefile b/Makefile index 26db109..611c024 100755 --- a/Makefile +++ b/Makefile @@ -133,20 +133,21 @@ endef #--- # Retrieve version information. -version_string := $(shell cat version | sed 's/[-.]/ /g') -version_type := $(word 1,$(version_string)) -version_major := $(word 2,$(version_string)) -version_minor := $(word 3,$(version_string)) -version_build := $(word 4,$(version_string)) +version_string = $(shell cat version | sed 's/[-.]/ /g') +version_type = $(word 1,$(version_string)) +version_major = $(word 2,$(version_string)) +version_minor = $(word 3,$(version_string)) +version_build = $(word 4,$(version_string)) -# Bump build number and make up the new version integer. -version_build := $(shell echo $$(($(version_build) + 1))) -version_letter := $(shell echo -n $(version_type) | sed -r 's/^(.).*/\1/') -version_symbol := $(shell printf '0x%02x%01x%01x%04x' "'$(version_letter)'" \ +# Make up the new version integer. +version_build_n = $(shell echo $$(($(version_build) + 1))) +version_letter = $(shell echo -n $(version_type) | sed -r 's/^(.).*/\1/') +version_symbol = $(shell printf '0x%02x%01x%01x%04x' "'$(version_letter)'" \ $(version_major) $(version_minor) $(version_build)) # Tell the linker to define the version symbol. -ldflags += -Wl,--defsym,GINT_VERSION=$(version_symbol) +demo-ldflags += -Wl,--defsym,_GINT_VERSION=$(version_symbol) +debug-ldflags += -Wl,--defsym,_GINT_VERSION=$(version_symbol) @@ -165,7 +166,7 @@ build: $(if $(VERBOSE),,@) mkdir -p $@ version: $(obj-std) $(obj-lib) - @ echo '$(version_type)-$(version_major).$(version_minor)-$(version_build)' > $@ + @ echo '$(version_type)-$(version_major).$(version_minor)-$(version_build_n)' > $@ $(obj-std) $(obj-lib) $(demo-obj): | build @@ -231,6 +232,10 @@ build/demo_%.c.o: demo/%.c $(hdr-dep) $(demo-dep) $(config) $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $<\n') $(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(demo-cflags) +build/demo_%.s.o: demo/%.s $(config) + $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m as $<\n') + $(if $(VERBOSE),,@) $(as) -c $< -o $@ + build/demo_font_%.bmp.o: demo/resources/font_%.bmp $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') $(if $(VERBOSE),,@) fxconv $< -o $@ --font -n $(patsubst demo/resources/%.bmp,res_%,$<) diff --git a/demo/gintdemo.c b/demo/gintdemo.c index 41e4d6e..7a4fee2 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -7,6 +7,7 @@ #include #include +#include @@ -324,7 +325,13 @@ void main_menu(int *category, int *app) extern unsigned int romdata; int gint_size = (char *)&egint - (char *)&bgint; - + // Building a version string. + char gint_version[20]; + uint32_t v = (uint32_t)&GINT_VERSION; + sprintf(gint_version, "%s-%d.%d-%d", + (v >> 24 == 'a') ? "alpha" : (v >> 24 == 'b') ? "beta" : + (v >> 24 == 'd') ? "dev" : (v >> 24 == 'r') ? "release" : "?", + (v >> 20) & 0x0f, (v >> 16) & 0x0f, v & 0xffff); static int tab = 0, index = 0, scroll = 0; // Set to 1 when interface has to be redrawn. @@ -345,8 +352,7 @@ void main_menu(int *category, int *app) switch(tab) { case 0: - print(1, 1, "Demo application"); -// print(2, 3, "gint version: %5s", GINT_VERSION_STR); + print(1, 1, "gint %s", gint_version); print(2, 4, "handler size: %5d", gint_size); print(2, 5, "mpu type: %7s", mpu); print(2, 6, "romdata: %08x", &romdata); diff --git a/demo/test_tales.c b/demo/test_tales.c index 135925d..8f26cc2 100644 --- a/demo/test_tales.c +++ b/demo/test_tales.c @@ -113,8 +113,10 @@ void test_tales(void) for(int j = 0; j < 16; j++) str[j] = 32 + (i << 4) + j; str[16] = 0; - gtext(2, 2 + i * height, str); + gtext(-3, -3 + i * height, str); } + gtext(120, 60, "Hello, World!"); + gtext(-40, 2, "Try"); gimage(0, 56, &res_opt_tales); diff --git a/include/gint.h b/include/gint.h index 57486a1..fc19d48 100644 --- a/include/gint.h +++ b/include/gint.h @@ -15,6 +15,13 @@ #include #include +// This one is defined by the linked and consists in four fields: +// - Version type, an ascii char ('a'lpha, 'b'eta, 'd'ev, 'r'elease), 8 bits +// - Major version number, 4 bits +// - Minor version numer, 4 bits +// - Build number, 16 bits +extern uint32_t GINT_VERSION; + //--- // System info provided by the library //--- diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c index fe34859..27944d2 100644 --- a/src/bopti/dimage.c +++ b/src/bopti/dimage.c @@ -10,7 +10,7 @@ void dimage_part(int x, int y, image_t *img, int left, int top, int width, int height) { - if(!img || img->magic != 0x01) return; + if(!img || img->magic != 0xb7) return; structure_t s; command_t command; diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c index e570634..8095be4 100644 --- a/src/bopti/gimage.c +++ b/src/bopti/gimage.c @@ -11,7 +11,7 @@ void gimage_part(int x, int y, image_t *img, int left, int top, int width, int height) { - if(!img || img->magic != 0x01) return; + if(!img || img->magic != 0xb7) return; structure_t s; command_t command; diff --git a/src/core/crt0.c b/src/core/crt0.c index c00aeb9..be9d0e4 100644 --- a/src/core/crt0.c +++ b/src/core/crt0.c @@ -55,7 +55,7 @@ __attribute__((section(".pretext.entry"))) int start(void) dg->magic = GINT_DIAGNOSTICS_MAGIC; dg->counter = dg->counter + 1; dg->mpu = mpu_unknown; - dg->version = GINT_VERSION; + dg->version = (uint32_t)&GINT_VERSION; dg->stage = stage_startup; // Exception records: what kind of exceptions / TLB faults / interrupts diff --git a/src/core/mpu.c b/src/core/mpu.c index a952e67..889e5a7 100644 --- a/src/core/mpu.c +++ b/src/core/mpu.c @@ -7,6 +7,7 @@ //--- #include +#include const mpu_t MPU_CURRENT; @@ -29,15 +30,14 @@ const mpu_t MPU_CURRENT; mpu_t getMPU(void) { // Processor version register. - volatile unsigned int *pvr = (unsigned int *)0xff000030; + volatile uint32_t *pvr = (void *)0xff000030; // Product version register. - volatile unsigned int *prr = (unsigned int *)0xff000044; + volatile uint32_t *prr = (void *)0xff000044; // Port L control register. - volatile unsigned short *plcr = (unsigned short *)0xa4000114; + volatile uint16_t *plcr = (void *)0xa4000114; // Saved value for PLCR. - unsigned short saved_plcr; - unsigned int tested_plcr; - + uint16_t saved_plcr; + uint16_t tested_plcr; // Looking for SH-3-based MPUs by testing PLCR writing access. saved_plcr = *plcr; @@ -50,12 +50,12 @@ mpu_t getMPU(void) if(tested_plcr == 0x0fff) return mpu_sh7355; // Looking for SH-4-based MPUs by testing the version registers. This - // needs to have the three upper bytes of the processor version - // register match 0x10300b : + // needs the three upper bytes of the processor version register to + // match 0x10300b : if((*pvr & 0xffffff00) != 0x10300b00) return mpu_unknown; // Now that we have an SH-4-based MPU, checking whether it is SH7305 or - // SH7724. + // SH7724, just for reference. switch(*prr & 0xfffffff0) { case 0x00002c00: diff --git a/src/ctype/ctype_classes.c b/src/ctype/ctype_classes.c index 88ff792..96ddd24 100644 --- a/src/ctype/ctype_classes.c +++ b/src/ctype/ctype_classes.c @@ -1,7 +1,7 @@ #include -// Let's save up some space and readability (That's Cake's idea, its a bit of a -// preprocessor trick but a rather nice trick). +// Let's save up some space and readability (that's Cake's idea, it's a bit of +// a preprocessor trick - but a rather nice trick). #define r4(x) (x), (x), (x), (x) #define r5(x) r4(x), (x) #define r6(x) r5(x), (x) diff --git a/src/display/adjustRectangle.c b/src/display/adjustRectangle.c index f46f9b2..5d2f482 100644 --- a/src/display/adjustRectangle.c +++ b/src/display/adjustRectangle.c @@ -7,7 +7,7 @@ - x1 < x2 - y1 < y2 which is needed when working with screen rectangles. - Returns non-zero if the rectangle is outside the screen. + Returns non-zero if the rectangle is entirely outside the screen. */ int adjustRectangle(int *x1, int *y1, int *x2, int *y2) { diff --git a/src/tales/tales_internals.c b/src/tales/tales_internals.c index e6dec37..b39b4a8 100644 --- a/src/tales/tales_internals.c +++ b/src/tales/tales_internals.c @@ -234,6 +234,8 @@ void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS)) // Computing the initial operator offset to have 32-aligned operators. // This allows to write directly video ram longs instead of having to // shift operators, and do all the vram operation twice. + // I double-checked that this operation is still valid when x is + // negative. available = 32 - (x & 31); x &= ~31; diff --git a/version b/version index 91cef21..dcdacc6 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-289 +beta-0.9-302 From a50ae4f38a36dc0194812be9875a21a117a06676 Mon Sep 17 00:00:00 2001 From: lephe Date: Mon, 17 Apr 2017 13:05:46 +0200 Subject: [PATCH 10/24] Erf. --- src/bopti/dimage.c | 2 +- src/bopti/gimage.c | 2 +- version | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c index 27944d2..fe34859 100644 --- a/src/bopti/dimage.c +++ b/src/bopti/dimage.c @@ -10,7 +10,7 @@ void dimage_part(int x, int y, image_t *img, int left, int top, int width, int height) { - if(!img || img->magic != 0xb7) return; + if(!img || img->magic != 0x01) return; structure_t s; command_t command; diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c index 8095be4..e570634 100644 --- a/src/bopti/gimage.c +++ b/src/bopti/gimage.c @@ -11,7 +11,7 @@ void gimage_part(int x, int y, image_t *img, int left, int top, int width, int height) { - if(!img || img->magic != 0xb7) return; + if(!img || img->magic != 0x01) return; structure_t s; command_t command; diff --git a/version b/version index dcdacc6..c06f383 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-302 +beta-0.9-303 From 8965590b12a4e87a2a70af4ad43c702ea597f5c4 Mon Sep 17 00:00:00 2001 From: lephe Date: Mon, 17 Apr 2017 13:39:28 +0200 Subject: [PATCH 11/24] Added a text_length() function. --- include/tales.h | 6 ++++++ src/tales/text_length.c | 21 +++++++++++++++++++++ version | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/tales/text_length.c diff --git a/include/tales.h b/include/tales.h index 42249ef..81a2f59 100644 --- a/include/tales.h +++ b/include/tales.h @@ -97,6 +97,12 @@ typedef struct Font Font; */ void text_configure(struct Font *font, color_t operator); +/* + text_length() + Computes the length of a string using the currently configured font. +*/ +size_t text_length(const char *str); + /* dtext() Prints the given string, without any analysis. diff --git a/src/tales/text_length.c b/src/tales/text_length.c new file mode 100644 index 0000000..2add43e --- /dev/null +++ b/src/tales/text_length.c @@ -0,0 +1,21 @@ +#include +#include + +/* + text_length() + Computes the length of a string using the currently configured font. +*/ +size_t text_length(const char *str) +{ + if(!str) return 0; + size_t len = 0; + + while(*str) + { + int index = getCharacterIndex(*str); + len += font->glyphs[index] >> 24; + if(*++str) len++; + } + + return len; +} diff --git a/version b/version index c06f383..1a72b1c 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-303 +beta-0.9-305 From a5a5ea6c20a5385f1c9020752064ba8d7819aaff Mon Sep 17 00:00:00 2001 From: lephe Date: Wed, 19 Apr 2017 19:57:31 +0200 Subject: [PATCH 12/24] Almost ended quality review, and fixed tales-related vram overflows. --- Makefile | 2 +- TODO | 9 ++-- demo/asm_syscalls.s | 56 +++++++++++++++++++++++++ demo/gintdemo.c | 49 ++++++++++++++++++++++ demo/test_keyboard.c | 9 ++-- demo/test_tales.c | 12 +++--- include/events.h | 19 ++++++++- include/extended/endian.h | 1 + include/gint.h | 9 +++- include/internals/stdio.h | 9 ---- include/internals/tales.h | 3 +- include/modules/macros.h | 4 +- include/mpu.h | 2 +- include/setjmp.h | 4 +- include/tales.h | 52 +++++++++++------------ include/time.h | 2 +- src/gray/gray_engine.c | 7 ++++ src/keyboard/getPressedKey.c | 24 ----------- src/keyboard/getkey.c | 6 +-- src/keyboard/keyboard_core.c | 24 ++++++++--- src/keyboard/keyboard_sh7305.c | 10 ++--- src/keyboard/keyboard_sh7705.c | 10 ++--- src/mmu/pseudoTLBInit.c | 18 ++++---- src/screen/screen_backlight.c | 9 ++-- src/screen/screen_display.c | 32 +++++++++++--- src/setjmp/setjmp.s | 33 +++++++-------- src/stdio/snprintf.c | 1 - src/stdlib/stdlib_rand.c | 1 + src/tales/tales_configuration.c | 4 +- src/tales/tales_gray.c | 11 +++-- src/tales/tales_internals.c | 74 ++++++++++++++++++--------------- src/time/mktime.c | 2 +- version | 2 +- 33 files changed, 327 insertions(+), 183 deletions(-) create mode 100644 demo/asm_syscalls.s delete mode 100644 src/keyboard/getPressedKey.c diff --git a/Makefile b/Makefile index 611c024..83a3ee9 100755 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ wr = g1a-wrapper # Flags for gint lib-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ - -Wall -Wextra @gcc.cfg + -Wall -Wextra @gcc.cfg -g0 # Demo application (could be done better) demo-src = $(notdir $(wildcard demo/*.[cs])) diff --git a/TODO b/TODO index 88f4c3c..a8403a7 100644 --- a/TODO +++ b/TODO @@ -3,14 +3,11 @@ Bugs to fix: - Lost keyboard control at startup (could not reproduce) - Alignment of ALL .data / .rodata files is required to ensure converted data is properly aligned -- Reported video ram overflow when rendering text at borderline positions Things to do before 1.0: - bopti: Test partial transparency * core: Allow return to menu - demo: Try 284x124 at (-60, -28) (all disadvantages) -- display: Implement rectangle-based drawing functions -- project: Add a real build-based version system - project: Check size of *all* library structures - project: Clean headers that have some internal definitions - project: Unify this hellish mess of register access! @@ -24,7 +21,13 @@ Things to do later: - core: Review interrupt system (again) - this one is too slow - errno: Introduce errno and use it more or less everywhere - esper: Cleaner playback, synthesizing +- events: Allow customization of keyboard event system (option to return + | events with modifiers, etc) +- events: Generate keyboard events on-the-fly by reading state arrays, + | allowing both a faster interrupt and avoiding supressing other + | events inside getkey() and multigetkey() - serial: Implement a driver +- stdio: More serious formatted printing functions - string: Use cmp/str to implement memchr() (assembler examples) - string: Do some tests for memcmp() and memcpy() - usb: Implement a driver diff --git a/demo/asm_syscalls.s b/demo/asm_syscalls.s new file mode 100644 index 0000000..69b4f10 --- /dev/null +++ b/demo/asm_syscalls.s @@ -0,0 +1,56 @@ + + # int BFile_Remove(const uint16_t *file) + .global _BFile_Remove + +_BFile_Remove: + mov.l 1f, r0 + mov.l 2f, r1 + jmp @r1 + mov #0, r5 +1: .long 0x0439 + + # int BFile_Create(const uint16_t *file, enum { file = 1, folder = 5 }, + # int *size) + .global _BFile_Create + +_BFile_Create: + mov.l 1f, r0 + mov.l 2f, r1 + jmp @r1 + nop +1: .long 0x0434 + + # int BFile_Open(const uint16_t *file, int mode) + .global _BFile_Open + +_BFile_Open: + mov.l 1f, r0 + mov.l 2f, r1 + jmp @r1 + mov #0, r6 +1: .long 0x042c + + # int BFile_Close(int handle) + .global _BFile_Close + +_BFile_Close: + mov.l 1f, r0 + mov.l 2f, r1 + jmp @r1 + nop +1: .long 0x042d + + # int BFile_Write(int handle, void *ram_buffer, int even_size) + .global _BFile_Write + +_BFile_Write: + mov.l 1f, r0 + mov.l 2f, r1 + jmp @r1 + nop +1: .long 0x0435 + + .align 4 + +# Syscall branch address +2: .long 0x80010070 diff --git a/demo/gintdemo.c b/demo/gintdemo.c index 7a4fee2..9532bdb 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -434,6 +434,10 @@ void main_menu(int *category, int *app) index = 0; scroll = 0; break; + case KEY_F6:; + void screen(void); + screen(); + break; case KEY_UP: if(list && list_len > 1) @@ -533,3 +537,48 @@ void crash(void) "trapa #1 " ); } + +void screen(void) +{ + enum { File = 1, Folder = 5 }; + enum { Read = 0x01, Write = 0x02, ReadWrite = Read | Write }; + + int BFile_Remove(const uint16_t *file); + int BFile_Create(const uint16_t *file, int type, int *size); + int BFile_Open(const uint16_t *file, int mode); + int BFile_Close(int handle); + int BFile_Write(int handle, const void *ram_buffer, int even_size); + + const uint8_t bmp_header[0x7e] = { + 0x42, 0x4d, 0x7e, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x6c, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x13, 0x0b, + 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x47, + 0x52, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, + }; + const uint16_t file[] = { '\\', '\\', 'f', 'l', 's', '0', '\\', 'g', + 's', 'c', 'r', 'e', 'e', 'n', '.', 'b', 'm', 'p', 0x00 }; + int size = 0x7e + 1024; + + BFile_Remove(file); + BFile_Create(file, File, &size); + int handle = BFile_Open(file, Write); + BFile_Write(handle, bmp_header, 0x7e); + void *vram = (void *)display_getCurrentVRAM() + 1024; + for(int i = 1; i <= 64; i++) + { + BFile_Write(handle, vram - 16 * i, 16); + } + BFile_Close(handle); +} diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c index 2046118..3012590 100644 --- a/demo/test_keyboard.c +++ b/demo/test_keyboard.c @@ -53,14 +53,15 @@ static void draw_keyboard(volatile uint8_t *state) typedef struct { event_type_t type; - int key; + uint32_t key; int repeats; } enhanced_event_t; static void push_history(enhanced_event_t *history, int size, event_t event) { - #define event_eq(x, y) ((x).type == (y).type && (x).key == (y).key) + #define event_eq(x, y) \ + ((x).type == (y).type && (x).key == (y).key.code) // Determining where the history ends. int length = 0; @@ -82,7 +83,7 @@ static void push_history(enhanced_event_t *history, int size, event_t event) // Adding a new entry to the history. history[length].type = event.type; - history[length].key = event.key; + history[length].key = event.key.code; history[length].repeats = 1; #undef event_eq @@ -135,7 +136,7 @@ void test_keyboard_events(void) dupdate(); event = waitevent(); - if(event.type == event_key_press && event.key == KEY_EXIT) + if(event.type == event_key_press && event.key.code == KEY_EXIT) break; push_history(history, history_size, event); } diff --git a/demo/test_tales.c b/demo/test_tales.c index 8f26cc2..1ad6a48 100644 --- a/demo/test_tales.c +++ b/demo/test_tales.c @@ -10,11 +10,11 @@ Displays some text using different modes and clipping options. */ -static Font *select(Font *current) +static font_t *select(font_t *current) { - extern Font res_font_modern; + extern font_t res_font_modern; struct { - Font *font; + font_t *font; const char *name; } fonts[] = { { NULL, "gint default" }, @@ -83,7 +83,7 @@ void test_tales(void) color_t colors[] = { color_black, color_dark, color_light, color_white, color_invert }; extern image_t res_opt_tales; - Font *font = NULL; + font_t *font = NULL; int black_bg = 0; int color = 0; @@ -113,10 +113,8 @@ void test_tales(void) for(int j = 0; j < 16; j++) str[j] = 32 + (i << 4) + j; str[16] = 0; - gtext(-3, -3 + i * height, str); + gtext(1, 1 + i * height, str); } - gtext(120, 60, "Hello, World!"); - gtext(-40, 2, "Try"); gimage(0, 56, &res_opt_tales); diff --git a/include/events.h b/include/events.h index d88c98e..a30729a 100644 --- a/include/events.h +++ b/include/events.h @@ -41,6 +41,23 @@ typedef enum } event_type_t; +/* + key_event_t + Keyboard events. "I think the user wants something." +*/ +typedef struct +{ + // This is the key code as defined in (a matrix code), and + // probably what you need. + uint32_t code; + // This is a "compact id" which can be used for array subscript. There + // are only a few holes in the "compact id" numbering. + uint32_t id; + // Character associated with the event key. + int character; + +} key_event_t; + /* event_t Wake up, something's going on. The union member that holds information @@ -55,7 +72,7 @@ typedef struct // For event_user. void *data; // For event_key_press, event_key_repeat and event_key_release. - int key; + key_event_t key; // For event_timer_underflow. timer_t *timer; }; diff --git a/include/extended/endian.h b/include/extended/endian.h index 8474a81..b6f5d6f 100644 --- a/include/extended/endian.h +++ b/include/extended/endian.h @@ -23,6 +23,7 @@ __attribute__((always_inline)) static inline uint32_t swap32(uint32_t longw) "swap.b r0, %0 \n\t" : "=r"(result) : "r"(longw) + : "r0" ); return result; } diff --git a/include/gint.h b/include/gint.h index fc19d48..ac264cb 100644 --- a/include/gint.h +++ b/include/gint.h @@ -15,11 +15,16 @@ #include #include -// This one is defined by the linked and consists in four fields: +// The version symbol is defined through the linker and consists of four +// fields: // - Version type, an ascii char ('a'lpha, 'b'eta, 'd'ev, 'r'elease), 8 bits // - Major version number, 4 bits -// - Minor version numer, 4 bits +// - Minor version number, 4 bits // - Build number, 16 bits +// Please note that the version number is the *ADDRESS* of GINT_VERSION, which +// you definitely want to cast to uint32_t. Evaluating GINT_VERSION is illegal +// (dereferencing a pointer which is actually a four-field version number just +// cannot work) and will certainly crash your program. extern uint32_t GINT_VERSION; //--- diff --git a/include/internals/stdio.h b/include/internals/stdio.h index 67563d4..7a67647 100644 --- a/include/internals/stdio.h +++ b/include/internals/stdio.h @@ -1,12 +1,3 @@ -//--- -// -// standard library module: stdio -// -// Handles most input/output for the program. This module does not -// interact with the file system directly. -// -//--- - #ifndef _INTERNALS_STDIO_H #define _INTERNALS_STDIO_H diff --git a/include/internals/tales.h b/include/internals/tales.h index 166f1ad..a599d4c 100644 --- a/include/internals/tales.h +++ b/include/internals/tales.h @@ -3,10 +3,11 @@ #include #include +#include #define OPERATE_ARGS uint32_t *operators, int height, int x, int y -extern struct Font *font; +extern font_t *font; extern color_t operator; /* diff --git a/include/modules/macros.h b/include/modules/macros.h index 664fc7d..f17e817 100644 --- a/include/modules/macros.h +++ b/include/modules/macros.h @@ -3,14 +3,14 @@ #include -// Padding is just empty space, we don't want to give it a name. There's also +// Padding is just empty space, we don't want to give it a type. There's also // some subtle preprocessor trick to automatically add a (supposedly) unique // name to each padding member. For instance the substitution may operate as: // name(__LINE__) -> namesub(78) -> _##78 -> _78 #define namesub(x) _##x #define name(x) namesub(x) #define pad(bytes) \ - uint8_t name(__LINE__)[bytes] \ + uint8_t name(__LINE__)[bytes] // Fixed-width types for bit field are totally meaningless. typedef unsigned uint; diff --git a/include/mpu.h b/include/mpu.h index d3c751f..d62919c 100644 --- a/include/mpu.h +++ b/include/mpu.h @@ -7,7 +7,7 @@ // which is stored in a global variable MPU_CURRENT and determined at // startup. // -// If you need to do MPU-dependant jobs, use isSH3() or (possibly) isSH4() +// If you need to do MPU-dependent jobs, use isSH3() or (possibly) isSH4() // instead of testing the value of MPU_CURRENT because these macros take // care of assuming unknown MPUs are SH4, which is the more reasonable // option. diff --git a/include/setjmp.h b/include/setjmp.h index d9ad7d8..b3aa872 100644 --- a/include/setjmp.h +++ b/include/setjmp.h @@ -10,9 +10,11 @@ #ifndef _SETJMP_H #define _SETJMP_H +#include + // There are 16 CPU registers that *must* be saved to ensure a basically // safe jump. -typedef unsigned int jmp_buf[16]; +typedef uint32_t jmp_buf[16]; diff --git a/include/tales.h b/include/tales.h index 81a2f59..2f189b7 100644 --- a/include/tales.h +++ b/include/tales.h @@ -19,42 +19,43 @@ //--- /* - enum ImageFormat + font_format_t This type holds information about the characters in the font. Each bit represents various characters, and the type itself is a combination of several of those bits. Bits represent the following characters (lsb right): -- -- -- non-print | special capitals lower numbers */ -enum FontFormat +typedef enum { - FontFormat_Unknown = 0x00, - FontFormat_Numeric = 0x01, - FontFormat_LowerCase = 0x02, - FontFormat_UpperCase = 0x04, - FontFormat_Letters = 0x06, - FontFormat_Common = 0x07, - FontFormat_Print = 0x0f, - FontFormat_Ascii = 0x1f, -}; + font_format_unknown = 0x00, + font_format_numeric = 0x01, + font_format_lower = 0x02, + font_format_upper = 0x04, + font_format_letters = 0x06, + font_format_common = 0x07, + font_format_print = 0x0f, + font_format_ascii = 0x1f, + +} font_format_t; /* - struct FontGlyph + font_glyph_t Holds a glyph's data. The width is used for spacing, and the raw data is encoded line after line, from to to bottom, by appending bits without consideration of the byte boundaries. This structure is actually never used, because data is read directly as a longword array (hence the 4-byte alignment). */ -struct FontGlyph +typedef struct { - unsigned char width; + uint8_t width; + const uint8_t data[]; - const unsigned char data[]; -} __attribute__((aligned(4))); +} __attribute__((packed, aligned(4))) font_glyph_t; /* - struct Font + font_t Holds a font's data. Data is accessed using longword operations, hence the 4-alignment attributes. The line height is the one given in the font image header line, which may be used by applications that write @@ -65,13 +66,12 @@ struct FontGlyph The name field may not be NUL-terminated when the name contains 28 characters. When the name is shorter, the field is padded with zeros. */ -struct Font +typedef struct { - unsigned char magic; - - unsigned char format; - unsigned char line_height; - unsigned char data_height; + uint8_t magic; + uint8_t format; + uint8_t line_height; + uint8_t data_height; // Warning : this field may not be NUL-terminated. char name[28]; @@ -80,9 +80,7 @@ struct Font __attribute__((aligned(4))) const uint32_t glyphs[]; -} __attribute__((aligned(4))); -// Useful shorthand for user code. -typedef struct Font Font; +} __attribute__((packed, aligned(4))) font_t; @@ -95,7 +93,7 @@ typedef struct Font Font; Sets the font and color to use for subsequent text operations. Pass font = NULL to use the default font. */ -void text_configure(struct Font *font, color_t operator); +void text_configure(font_t *font, color_t operator); /* text_length() diff --git a/include/time.h b/include/time.h index 8833524..9ec8298 100644 --- a/include/time.h +++ b/include/time.h @@ -36,7 +36,7 @@ struct tm clock_t Only used by clock(). */ -typedef signed int clock_t; +typedef signed int clock_t; /* time_t diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index e79af37..3d12bb1 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -11,14 +11,18 @@ #include #include +// Additional video rams used by the gray engine. static uint32_t internal_vrams[3][256]; static uint32_t *vrams[4]; +// Current vram set (0 or 1), delays of the light and dark frames respectively. static int current = 0; static int delays[2]; +// Is the engine currently running? static int runs = 0; +// Hardware timer used to run the engine. static timer_t *gray_timer = NULL; @@ -87,6 +91,9 @@ void gray_stop(void) timer_stop(gray_timer); runs = 0; + /* TODO This may not be very wise considering the fact that the user + may have specified another monochrome vram address. This raises again + the idea of a parameter stack. */ display_useVRAM(display_getLocalVRAM()); } diff --git a/src/keyboard/getPressedKey.c b/src/keyboard/getPressedKey.c deleted file mode 100644 index f7d7d06..0000000 --- a/src/keyboard/getPressedKey.c +++ /dev/null @@ -1,24 +0,0 @@ -#include - -/* - getPressedKey() - Finds a pressed key in the keyboard state and returns it. -*/ -int getPressedKey(volatile uint8_t *keyboard_state) -{ - int row = 1, column = 0; - int state; - if(keyboard_state[0] & 1) return KEY_AC_ON; - - while(row <= 9 && !keyboard_state[row]) row++; - if(row > 9) return KEY_NONE; - - state = keyboard_state[row]; - while(!(state & 1)) - { - state >>= 1; - column++; - } - - return (column << 4) | row; -} diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c index 5d3cc68..4889b77 100644 --- a/src/keyboard/getkey.c +++ b/src/keyboard/getkey.c @@ -53,7 +53,7 @@ int getkey_opt(getkey_option_t options, int delay_ms) break; case event_key_press: - key = event.key; + key = event.key.code; if(options & getkey_manage_backlight && key == KEY_OPTN && (modifier & MOD_SHIFT)) @@ -79,7 +79,7 @@ int getkey_opt(getkey_option_t options, int delay_ms) return key | modifier; case event_key_repeat: - key = event.key; + key = event.key.code; if(key != last_key) continue; // Checking that this type of repetition is allowed. @@ -98,7 +98,7 @@ int getkey_opt(getkey_option_t options, int delay_ms) break; case event_key_release: - if(event.key != last_key) break; + if((int)event.key.code != last_key) break; last_key = KEY_NONE; last_repeats = 0; last_time = 0; diff --git a/src/keyboard/keyboard_core.c b/src/keyboard/keyboard_core.c index ea76d3c..bb74325 100644 --- a/src/keyboard/keyboard_core.c +++ b/src/keyboard/keyboard_core.c @@ -36,27 +36,39 @@ timer_t *vtimer = NULL; static inline void push_press(int keycode) { + uint32_t id = key_id(keycode); + event_t event = { - .type = event_key_press, - .key = keycode, + .type = event_key_press, + .key.code = keycode, + .key.id = id, + .key.character = key_char(keycode), }; event_push(event); } static inline void push_repeat(int keycode) { + uint32_t id = key_id(keycode); + event_t event = { - .type = event_key_repeat, - .key = keycode, + .type = event_key_repeat, + .key.code = keycode, + .key.id = id, + .key.character = key_char(keycode), }; event_push(event); } static inline void push_release(int keycode) { + uint32_t id = key_id(keycode); + event_t event = { - .type = event_key_release, - .key = keycode, + .type = event_key_release, + .key.code = keycode, + .key.id = id, + .key.character = key_char(keycode), }; event_push(event); } diff --git a/src/keyboard/keyboard_sh7305.c b/src/keyboard/keyboard_sh7305.c index 68e1fc8..f1647d4 100644 --- a/src/keyboard/keyboard_sh7305.c +++ b/src/keyboard/keyboard_sh7305.c @@ -30,8 +30,8 @@ many keys on the same column are pressed, other keys of the same column may be triggered. - (The following values do not apply to the latest tests, even if the - trend remains the same.) + (The following values do not apply to the latest tests, but the trend + remains the same.) - Less Bad key detection. - 8 Very few column effects. Most often, three keys may be pressed simultaneously. However, [UP] has latencies and is globally not @@ -63,7 +63,7 @@ static void kdelay(void) Reads a keyboard row. Works like krow() for SH7705; see source file keyboard_7705.c for more details. */ -static int krow(int row) +static uint8_t krow(int row) { volatile uint16_t *injector1 = (void *)0xa4050116; volatile uint8_t *data1 = (void *)0xa4050136; @@ -79,9 +79,9 @@ static int krow(int row) uint16_t smask; uint8_t cmask; - int result = 0; + uint8_t result = 0; - if(row < 0 || row > 9) return 0; + if(row < 0 || row > 9) return 0x00; // Additional configuration for SH7305. *detector = 0xaaaa; diff --git a/src/keyboard/keyboard_sh7705.c b/src/keyboard/keyboard_sh7705.c index 20cd32c..a7fb0a3 100644 --- a/src/keyboard/keyboard_sh7705.c +++ b/src/keyboard/keyboard_sh7705.c @@ -66,16 +66,16 @@ static void kdelay(void) krow() Reads a keyboard row. */ -static int krow(int row) +static uint8_t krow(int row) { // '11' on the active row, '00' everywhere else. - unsigned short smask = 0x0003 << ((row % 8) * 2); + uint16_t smask = 0x0003 << ((row % 8) * 2); // '0' on the active row, '1' everywhere else. - unsigned char cmask = ~(1 << (row % 8)); + uint8_t cmask = ~(1 << (row % 8)); // Line results. - int result = 0; + uint8_t result = 0; - if(row < 0 || row > 9) return 0; + if(row < 0 || row > 9) return 0x00; // Initial configuration. PFC.PBCR.WORD = 0xaaaa; diff --git a/src/mmu/pseudoTLBInit.c b/src/mmu/pseudoTLBInit.c index b27d60d..c947651 100644 --- a/src/mmu/pseudoTLBInit.c +++ b/src/mmu/pseudoTLBInit.c @@ -1,6 +1,5 @@ #include - -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#include /* mmu_pseudoTLBInit() @@ -15,15 +14,16 @@ */ void mmu_pseudoTLBInit(void) { - extern unsigned int romdata; - unsigned int address = 0x00300000; - unsigned int x; + extern uint32_t romdata; + uint32_t limit = (uint32_t)&romdata; - while(address <= (unsigned int)&romdata) + uint32_t address = 0x00300000; + + // Access every page to make the system load everything in the TLB (and + // just hope it works and the add-in fits in). + while(address <= limit) { - x = *((volatile unsigned int *)address); + *((volatile uint32_t *)address); address += 0x1000; } } - -#pragma GCC diagnostic pop diff --git a/src/screen/screen_backlight.c b/src/screen/screen_backlight.c index 88e4078..e344212 100644 --- a/src/screen/screen_backlight.c +++ b/src/screen/screen_backlight.c @@ -1,4 +1,5 @@ #include +#include #include /* @@ -9,13 +10,13 @@ void screen_setBacklight(int on) { if(isSH3()) { - volatile unsigned char *PGDR = (void *)0xa400012c; + volatile uint8_t *PGDR = (void *)0xa400012c; if(on) *PGDR |= 0x80; else *PGDR &= ~0x80; } else { - volatile unsigned char *PNDR = (void *)0xa4050138; + volatile uint8_t *PNDR = (void *)0xa4050138; if(on) *PNDR |= 0x10; else *PNDR &= ~0x10; } @@ -29,12 +30,12 @@ void screen_toggleBacklight(void) { if(isSH3()) { - volatile unsigned char *PGDR = (void *)0xa400012c; + volatile uint8_t *PGDR = (void *)0xa400012c; *PGDR ^= 0x80; } else { - volatile unsigned char *PNDR = (void *)0xa4050138; + volatile uint8_t *PNDR = (void *)0xa4050138; *PNDR ^= 0x10; } } diff --git a/src/screen/screen_display.c b/src/screen/screen_display.c index c04d7d8..0d76d0e 100644 --- a/src/screen/screen_display.c +++ b/src/screen/screen_display.c @@ -1,4 +1,5 @@ #include +#include /* screen_display() @@ -9,12 +10,11 @@ */ void screen_display(const void *ptr) { - const char *vram = (const char *)ptr; - volatile char *selector = (char *)0xb4000000; - volatile char *data = (char *)0xb4010000; - int line, bytes; + volatile uint8_t *selector = (void *)0xb4000000; + volatile uint8_t *data = (void *)0xb4010000; + const uint8_t *vram = ptr; - for(line = 0; line < 64; line++) + for(int line = 0; line < 64; line++) { // Setting the x-address register. *selector = 4; @@ -29,7 +29,27 @@ void screen_display(const void *ptr) *data = 0; // Selecting data write register 7 and sending a line's bytes. + // We could loop but I suspect it will be faster to iterate. *selector = 7; - for(bytes = 0; bytes < 16; bytes++) *data = *vram++; + + /* TODO Manually load the video-ram line into the cache? */ + + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; + *data = *vram++; } } diff --git a/src/setjmp/setjmp.s b/src/setjmp/setjmp.s index 3b6f910..463673e 100644 --- a/src/setjmp/setjmp.s +++ b/src/setjmp/setjmp.s @@ -1,22 +1,19 @@ -/* - standard library module: setjmp - - Long jumps. The register contents are saved in a buffer when setjmp() - is called and restored at any time when longjmp() performs the jump. - - This is based on a trick that uses pr ; the user program is resumed - after the setjmp() call when longjmp() is invoked but this is not - setjmp() that returns. longjmp() restores the pr value that was saved - by setjmp() and performs an rts instruction. - - setjmp() returns 0 when called to set up the jump point and a non-zero - value when invoked through a long jump. If 0 is given as return value - to longjmp(), then 1 is returned. -*/ .global _setjmp .global _longjmp +/* + setjmp() + + This function is implemented using a trick that changes the value of + pr (so that it points just after the call to setjmp()) within the + longjmp() function, which lets the rts function perform the actual + jump. This value is obtained from the save buffer. + + setjmp() returns 0 when called to set up the jump point and a non-zero + value when invoked through a long jump. If 0 is provided as argument to + longjmp(), 1 is returned instead. +*/ _setjmp: /* Getting some free space. */ add #64, r4 @@ -42,8 +39,8 @@ _setjmp: sts.l pr, @-r4 /* This function always return 0. The cases where setjmp() seems to - return non-zero values, when a long jump has just been performed, is - actually handled by longjmp(). */ + return non-zero values, when a long jump has just been performed, are + those when the longjmp() function returns. */ rts mov #0, r0 @@ -52,7 +49,7 @@ _setjmp: _longjmp: /* Restoring the system and control registers. Restoring pr is actually what performs the jump -- and makes the user program think that - setjmp() has returned. */ + setjmp() has just returned. */ lds.l @r4+, pr lds.l @r4+, macl lds.l @r4+, mach diff --git a/src/stdio/snprintf.c b/src/stdio/snprintf.c index 6c5eb20..20c9c9b 100644 --- a/src/stdio/snprintf.c +++ b/src/stdio/snprintf.c @@ -13,7 +13,6 @@ int snprintf(char *str, size_t size, const char *format, ...) int x = __printf(size, format, args); memcpy(str, __stdio_buffer, x + 1); - va_end(args); return x; diff --git a/src/stdlib/stdlib_rand.c b/src/stdlib/stdlib_rand.c index d5c17a6..9cd7765 100644 --- a/src/stdlib/stdlib_rand.c +++ b/src/stdlib/stdlib_rand.c @@ -9,6 +9,7 @@ void srand(unsigned int new_seed) int rand(void) { + /* TODO Or maybe seed = (16807 * seed) % ((1 << 31) - 1); */ seed = seed * 1103515245 + 12345; return seed & 0x7fffffff; } diff --git a/src/tales/tales_configuration.c b/src/tales/tales_configuration.c index 81b9e32..6f099cc 100644 --- a/src/tales/tales_configuration.c +++ b/src/tales/tales_configuration.c @@ -5,9 +5,9 @@ text_configure() Sets the font and mode to use for the following print operations. */ -void text_configure(struct Font *next_font, color_t next_operator) +void text_configure(font_t *next_font, color_t next_operator) { - extern Font gint_font_system; + extern font_t gint_font_system; if(next_font) font = next_font; else font = &gint_font_system; diff --git a/src/tales/tales_gray.c b/src/tales/tales_gray.c index 42918e8..10589b1 100644 --- a/src/tales/tales_gray.c +++ b/src/tales/tales_gray.c @@ -4,12 +4,15 @@ void operate_gray(OPERATE_ARGS) { - size_t vram_offset = (x >> 5) + (y << 2); - uint32_t *light = gray_lightVRAM() + vram_offset; - uint32_t *dark = gray_darkVRAM() + vram_offset; + if(x < 0) return; + + int start = (y < 0) ? (-y) : (0); + size_t vram_offset = (x >> 5) + ((y + start) << 2); + uint32_t *light = (uint32_t *)gray_lightVRAM() + vram_offset; + uint32_t *dark = (uint32_t *)gray_darkVRAM() + vram_offset; uint32_t op, old_light; - for(int i = 0; i < height; i++) + for(int i = start; i < height; i++) { op = operators[i]; diff --git a/src/tales/tales_internals.c b/src/tales/tales_internals.c index b39b4a8..889fd7a 100644 --- a/src/tales/tales_internals.c +++ b/src/tales/tales_internals.c @@ -4,7 +4,7 @@ #include #include -struct Font *font; +font_t *font = NULL; color_t operator; /* @@ -27,8 +27,8 @@ int getCharacterIndex(int c) { const char *data = (const char *)&font->glyphs; int index, current; - int offset; int width, bits; + size_t offset; c &= 0x7f; @@ -36,33 +36,33 @@ int getCharacterIndex(int c) // Getting the character index in the glyph array. - if(font->format == FontFormat_Ascii) index = c; - else if(font->format == FontFormat_Print) index = c - 32; + if(font->format == font_format_ascii) index = c; + else if(font->format == font_format_print) index = c - 32; else switch(font->format) { - case FontFormat_Numeric: + case font_format_numeric: if(!isdigit(c)) return -1; index = c - '0'; break; - case FontFormat_LowerCase: + case font_format_lower: if(!islower(c)) return -1; index = c - 'a'; break; - case FontFormat_UpperCase: + case font_format_upper: if(!isupper(c)) return -1; index = c - 'A'; break; - case FontFormat_Letters: + case font_format_letters: if(!isalpha(c)) return -1; index = c - 'A' - ('a' - 'Z') * (c >= 'a'); break; - case FontFormat_Common: + case font_format_common: if(!isalnum(c)) return -1; index = c - '0' - ('A' - '9') * (c >= 'A') - ('a' - 'Z') * (c >= 'a'); break; - case FontFormat_Unknown: + case font_format_unknown: default: return -1; } @@ -93,13 +93,16 @@ int getCharacterIndex(int c) */ void operate_mono(OPERATE_ARGS) { + if(x < 0) return; + uint32_t *vram = display_getCurrentVRAM(); - uint32_t *video = vram + (x >> 5) + (y << 2); + int start = (y < 0) ? (-y) : (0); + uint32_t *video = vram + (x >> 5) + ((y + start) << 2); switch(operator) { case color_white: - for(int i = 0; i < height; i++) + for(int i = start; i < height; i++) { *video &= ~operators[i]; video += 4; @@ -107,7 +110,7 @@ void operate_mono(OPERATE_ARGS) break; case color_black: - for(int i = 0; i < height; i++) + for(int i = start; i < height; i++) { *video |= operators[i]; video += 4; @@ -115,7 +118,7 @@ void operate_mono(OPERATE_ARGS) break; case color_invert: - for(int i = 0; i < height; i++) + for(int i = start; i < height; i++) { *video ^= operators[i]; video += 4; @@ -163,7 +166,7 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph) shift = 32 - available; // Getting the next 'width' bits. In some cases these bits will - // intersect two different longs. + // intersect two different longs... line = data & glyph_mask; line = (shift >= 0) ? (line >> shift) : (line << -shift); operators[i] |= line; @@ -171,7 +174,7 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph) data <<= width; bits_available -= width; - // Continue until they do. + // ... continue looping until they do. if(bits_available >= 0) continue; // Computing a special mask that extracts just the number of @@ -180,6 +183,10 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph) data = glyph[data_index++]; shift += width + bits_available; + // In case this condition is not verified, the program invokes + // undefined behavior because of the bit shift. Anyway, it + // means that the end of the end of the operators was reached, + // in which case the function should not continue writing. if(shift <= 31) { line = data & partial_mask; @@ -202,34 +209,32 @@ int update(uint32_t *operators, int height, int available, uint32_t *glyph) */ void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS)) { + if(!font) return; + // Operator data, and number of available bits in the operators (which // is the same for all operators, since they are treated equally). uint32_t *operators; int available; - // Raw glyph data, each glyph being represented by one or several + // Raw glyph data, each glyph being represented as one or several // longwords, and an index in this array. uint32_t *data = (uint32_t *)font->glyphs; int index; - // Height of each glyph. This value is constant because the storage - // format requires it: it allows greater optimization. - int height; - - int i; + // Storage height of each glyph. This is a constant for all glyphs + // because the format required it. It makes this routine consequently + // faster. + int height = font->data_height; - if(!font) return; - // Allocating data. There will be one operator for each line. - height = font->data_height; if(x > 127 || y > 63 || y <= -height) return; if(y + height > 64) height = 64 - y; operators = alloca(height * sizeof(uint32_t)); - for(i = 0; i < height; i++) operators[i] = 0; if(!operators) return; + for(int i = 0; i < height; i++) operators[i] = 0; // Computing the initial operator offset to have 32-aligned operators. // This allows to write directly video ram longs instead of having to @@ -263,22 +268,23 @@ void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS)) x += 32; if(x > 96) break; - memset(operators, 0, height << 2); + for(int i = 0; i < height; i++) operators[i] = 0; if(available >= 0) { - available = 31 + available; + available += 31; continue; } - // Finishing update, in case it has been only partially done, - // because there was not enough bits available to fit all the - // information. Also adding a space, assuming that characters - // aren't more than 30 bits wide. + // Finishing update, in cases where it has been only partially + // done because there was not enough bits available to fit all + // the information. Also adding a space, assuming that + // characters aren't more than 30 bits wide. (=p) available += 32 + (data[index] >> 24); available = update(operators, height, available, data + index); available--; } - // Final operation. - if(x <= 96 && available < 32) (*op)(operators, height, x, y); + // Final operation. This condition allows a single bit of the operators + // to be used - that's because the loop will add a final spacing pixel. + if(x <= 96 && available < 31) (*op)(operators, height, x, y); } diff --git a/src/time/mktime.c b/src/time/mktime.c index 928cf6f..0ea26a9 100644 --- a/src/time/mktime.c +++ b/src/time/mktime.c @@ -53,7 +53,7 @@ time_t mktime(struct tm *time) time->tm_wday = (1 + days) % 7; // This RTC does not seem to have any DST feature. - time->tm_isdst = 0; + time->tm_isdst = 0; if(time->tm_year + 1900 < 1970) return (time_t)-1; diff --git a/version b/version index 1a72b1c..fa6bbd4 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-305 +beta-0.9-349 From 2f70525a3b1ba1b2807da722983d8b044d34ffe7 Mon Sep 17 00:00:00 2001 From: lephe Date: Sat, 22 Apr 2017 21:59:39 +0200 Subject: [PATCH 13/24] Interrupt Controller module draft, bopti bugfix. --- TODO | 1 + include/internals/bopti.h | 2 +- include/modules/interrupts.h | 489 +++++++++++++++++++++++++++++++++++ src/bopti/bopti_internals.c | 6 + src/bopti/dimage.c | 31 +-- src/bopti/gimage.c | 33 ++- src/core/gint_sh7705.c | 3 +- src/core/modules.c | 53 ++-- src/stdlib/stdlib_rand.c | 8 +- version | 2 +- 10 files changed, 573 insertions(+), 55 deletions(-) create mode 100644 include/modules/interrupts.h diff --git a/TODO b/TODO index a8403a7..6083b62 100644 --- a/TODO +++ b/TODO @@ -16,6 +16,7 @@ Things to do before 1.0: Things to do later: - bopti: Implement blending modes for monochrome bitmaps - clock: Handle overclock (relaunch clocks when overclocking) +- core: Change interrupt priority using the gint API - core: Register more interrupts (and understand their parameters) - core: Remove redundant code linked to environment saves - core: Review interrupt system (again) - this one is too slow diff --git a/include/internals/bopti.h b/include/internals/bopti.h index 2c93153..e1b11dd 100644 --- a/include/internals/bopti.h +++ b/include/internals/bopti.h @@ -75,7 +75,7 @@ typedef struct command_t uint32_t masks[4]; // Video rams being used. union { - // Different names, same fate. (Kingdom Hearts II) + // "Different names, same fate." (Kingdom Hearts II) uint32_t *vram; uint32_t *v1; }; diff --git a/include/modules/interrupts.h b/include/modules/interrupts.h new file mode 100644 index 0000000..47a0129 --- /dev/null +++ b/include/modules/interrupts.h @@ -0,0 +1,489 @@ +#ifndef _MODULES_INTERRUPTS +#define _MODULES_INTERRUPTS + +#include +#include + +//--- +// Interrupt controller. +// This part is difficult to handle, because the interrupt controllers of +// SH7705 and SH7305 MPUs have virtually nothing in common. I eventually +// decided to completely split it up into two kinds of structures and +// types. +// Use the gint API, not this module, for platform-independent interrupt +// management. +//--- + +//--- +// SH7705-related definitions. +//--- + +/* + mod_intc_ipc_7705_t + Interrupt priority controller. Just a bunch of 16-bit-registers that + handle the interrupt priority of all interrupt sources. + + Please note that because the interrupt priority controller of the + SH7705 MPU has registers scattered everywhere in the memory, its + structure below has a different pointer for each register. On the + opposite, the SH7305 registers are all in a contiguous area thus there + is only one pointer for the whole group. +*/ +typedef struct +{ + volatile word_union(*IPRA, + uint TMU0 :4; /* Timer 0 */ + uint TMU1 :4; /* Timer 1 */ + uint TMU2 :4; /* Timer 2 */ + uint RTC :4; /* Real-Time Clock */ + ); + + volatile word_union(*IPRB, + uint WDT :4; /* Watchdog Timer */ + uint REF :4; /* BSC Refresh Request, SDRAM (?) */ + uint :4; + uint :4; + ); + + volatile word_union(*IPRC, + uint IRQ3 :4; /* Interrupt request 3 */ + uint IRQ2 :4; /* Interrupt request 2 */ + uint IRQ1 :4; /* Interrupt request 1 */ + uint IRQ0 :4; /* Interrupt request 0 */ + ); + + volatile word_union(*IPRD, + uint PINT0_7 :4; /* External interrupt pins 0 to 7 */ + uint PINT8_15 :4; /* External interrupt pins 8 to 15 */ + uint IRQ5 :4; /* Interrupt request 5 */ + uint IRQ4 :4; /* Interrupt request 4 */ + ); + + volatile word_union(*IPRE, + uint DMAC :4; /* Direct Memory Access Controller */ + uint SCIF0 :4; /* Serial Communication Interface 0 */ + uint SCIF2 :4; /* Serial Communication Interface 2 */ + uint ADC :4; /* Analog/Decimal Converter */ + ); + + volatile word_union(*IPRF, + uint :4; + uint :4; + uint USB :4; /* USB Controller */ + uint :4; + ); + + volatile word_union(*IPRG, + uint TPU0 :4; /* Timer Pulse Unit 0 */ + uint TPU1 :4; /* Timer Pulse Unit 1 */ + uint :4; + uint :4; + ); + + volatile word_union(*IPRH, + uint TPU2 :4; /* Timer Pulse Unit 2 */ + uint TPU3 :4; /* Timer Pulse Unit 3 */ + uint :4; + uint :4; + ); + +} __attribute__((packed)) mod_intc_ipc_7705_t; + +/* + mod_intc_icr0_7705_t + Interrupt control register 0: configuration of the NMI interrupt. +*/ +typedef struct +{ + word_union(, + uint const NMIL :1; /* NMI Input Level */ + uint :6; + uint NMIE :1; /* NMI Edge Select */ + uint :8; + ); + +} __attribute__((packed, aligned(2))) mod_intc_icr0_7705_t; + +/* + mod_intc_icr1_7705_t + Interrupt control register 1: general interrupt configuration. +*/ +typedef struct +{ + word_union(, + uint MAI :1; /* Mask All Interrupts */ + uint IRQLVL :1; /* Interrupt Request Level Detect */ + uint BLMSK :1; /* Enable NMI when BL is set */ + uint :1; + uint IRQ5E :2; /* IRQ 5 Edge Detection */ + uint IRQ4E :2; + uint IRQ3E :2; + uint IRQ2E :2; + uint IRQ1E :2; + uint IRQ0E :2; + ); + +} __attribute__((packed, aligned(2))) mod_intc_icr1_7705_t; + +/* + mod_intc_icr2_7705_t + Interrupt control register 2: individual PINT interrupt management. +*/ +typedef struct +{ + word_union(, + uint PINT15 :1; /* PINT15 interrupt detection level */ + uint PINT14 :1; + uint PINT13 :1; + uint PINT12 :1; + uint PINT11 :1; + uint PINT10 :1; + uint PINT9 :1; + uint PINT8 :1; + uint PINT7 :1; + uint PINT6 :1; + uint PINT5 :1; + uint PINT4 :1; + uint PINT3 :1; + uint PINT2 :1; + uint PINT1 :1; + uint PINT0 :1; + ); + +} __attribute__((packed, aligned(2))) mod_intc_icr2_7705_t; + +/* + mod_intc_pinter_7705_t + PINTER register: individual masks for all PINT interrupts. +*/ +typedef struct +{ + word_union(, + uint PINT15 :1; /* PINT15 interrupt enable */ + uint PINT14 :1; + uint PINT13 :1; + uint PINT12 :1; + uint PINT11 :1; + uint PINT10 :1; + uint PINT9 :1; + uint PINT8 :1; + uint PINT7 :1; + uint PINT6 :1; + uint PINT5 :1; + uint PINT4 :1; + uint PINT3 :1; + uint PINT2 :1; + uint PINT1 :1; + uint PINT0 :1; + ); + +} __attribute__((packed, aligned(2))) mod_intc_pinter_7705_t; + +/* + mod_intc_irr0_7705_t + Interrupt Request Register 0: Indicates whether interrupt requests are + being input to the various interrupt lines. Also allows to clear the + IRQ request bits in edge-detection mode. +*/ +typedef struct +{ + byte_union(, + uint const PINT0_7R :1; /* PINT0-7 state */ + uint const PINT8_15R :1; /* PINT8-15 state */ + uint IRQ5 :1; /* IRQ5 request pin state */ + uint IRQ4 :1; + uint IRQ3 :1; + uint IRQ2 :1; + uint IRQ1 :1; + uint IRQ0 :1; + ); + +} __attribute__((packed)) mod_intc_irr0_7705_t; + +/* + mod_intc_irr1_7705_t + Interrupt Request Register 1: State of SCIF0 and DMAC requests. +*/ +typedef struct +{ + const byte_union(, + uint TXIOR :1; /* SCIF0 TXI interrupt */ + uint :1; + uint RXI0R :1; /* SCIF0 RXI interrupt */ + uint ERI0R :1; /* SCIF0 ERI interrupt */ + uint DEI3R :1; /* DMAC DEI3 interrupt */ + uint DEI2R :1; /* DMAC DEI2 interrupt */ + uint DEI1R :1; /* DMAC DEI1 interrupt */ + uint DEI0R :1; /* DMAC DEI0 interrupt */ + ); + +} __attribute__((packed)) mod_intc_irr1_7705_t; + +/* + mod_intc_irr2_7705_t + Interrupt Request Register 2: State of SCIF2 and ADC requests. +*/ +typedef struct +{ + const byte_union(, + uint :3; + uint ADIR :1; /* AD/C ADI interrupt */ + uint TXI2R :1; /* SCIF2 TXI interrupt */ + uint :1; + uint RXI2R :1; /* SCIF2 RXI interrupt */ + uint ERI2R :1; /* SCIF2 ERI interrupt */ + ); + +} __attribute__((packed, aligned(2))) mod_intc_irr2_7705_t; + +/* + mod_intc_7705_t + Finally the SH7705 interrupt controller. +*/ +typedef struct +{ + /* All interrupt priority registers */ + mod_intc_ipc_7705_t iprs; + + /* Control registers */ + volatile mod_intc_icr0_7705_t *ICR0; + volatile mod_intc_icr1_7705_t *ICR1; + volatile mod_intc_icr2_7705_t *ICR2; + + /* PINTER register */ + volatile mod_intc_pinter_7705_t *PINTER; + + /* Interrupt request registers */ + volatile mod_intc_irr0_7705_t *IRR0; + volatile mod_intc_irr1_7705_t *IRR1; + volatile mod_intc_irr2_7705_t *IRR2; + +} __attribute__((packed)) mod_intc_7705_t; + + + +//--- +// SH7305-related definitions. +//--- + +/* + mod_intc_ipc_7305_t + Interrupt priority controller, same idea as the previous one. + + Some of the interrupt fields of the SH7305 registers, the contents of + which have been directly taken from the SH7724 documentation, have been + left unnamed because the related peripheral modules are *very* unlikely + to even exist in the SH7305, let alone be of any use to us. +*/ +typedef struct +{ + word_union(IPRA, + uint TMU0_0 :4; /* TMU0 Channel 0 */ + uint TMU0_1 :4; /* TMU0 Channel 1 */ + uint TMU0_2 :4; /* TMU0 Channel 2 */ + uint IrDA :4; /* Infrared Communication */ + ); + pad(2); + + word_union(IPRB, + uint :4; /* JPEG Processing Unit */ + uint LCDC :4; /* LCD Controller */ + uint DMAC1A :4; /* Direct Memory Access Controller 1 */ + uint :4; /* Blending Engine Unit */ + ); + pad(2); + + word_union(IPRC, + uint TMU1_0 :4; /* TMU1 Channel 0 */ + uint TMU1_1 :4; /* TMU1 Channel 1 */ + uint TMU1_2 :4; /* TMU1 Channel 2 */ + uint :4; /* Sound Processing Unit */ + ); + pad(2); + + word_union(IPRD, + uint :4; + uint MMCIF :4; /* MultiMedia Card Interface */ + uint :4; + uint :4; /* ATAPI Interface */ + ); + pad(2); + + word_union(IPRE, + uint DMAC0A :4; /* Direct Memory Access Controller 0 */ + uint :4; /* Various graphical engines */ + uint SCIFA3 :4; /* SCIFA channel 3 interrupt */ + uint :4; /* Video Processing Unit */ + ); + pad(2); + + word_union(IPRF, + uint KEYSC :4; /* Key Scan Interface */ + uint DMACOB :4; /* DMAC0 transfer/error info */ + uint USB0_1 :4; /* USB controller */ + uint CMT :4; /* Compare Match Timer */ + ); + pad(2); + + word_union(IPRG, + uint SCIF0 :4; /* SCIF0 transfer/error info */ + uint SCIF1 :4; /* SCIF1 transfer/error info */ + uint SCIF2 :4; /* SCIF2 transfer/error info */ + uint :4; /* Video Engine Unit */ + ); + pad(2); + + word_union(IPRH, + uint MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */ + uint MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */ + uint :4; /* I2C Interface channel 0 */ + uint :4; /* I2C Interface channel 1 */ + ); + pad(2); + + word_union(IPRI, + uint SCIFA4 :4; /* SCIFA channel 4 interrupt */ + uint :4; /* MediaRAM InterConnected Buffers */ + uint :4; /* Transport Stream Interface */ + uint :4; /* 2D Graphics Accelerator & ICB */ + ); + pad(2); + + word_union(IPRJ, + uint :4; /* Capture Engine Unit */ + uint :4; /* Ethernet Memory Access Controller */ + uint FSI :4; /* FIFO-Buffered Serial Interface */ + uint SDHI1 :4; /* SD Card Host Interface channel 1 */ + ); + pad(2); + + word_union(IPRK, + uint RTC :4; /* Real-Time Clock */ + uint DMAC1B :4; /* DMAC1 transfer/error info */ + uint :4; /* MediaRAM InterConnected Buffers */ + uint SDHI0 :4; /* SD Card Host Interface channel 0 */ + ); + pad(2); + + word_union(IPRL, + uint SCIFA5 :4; /* SCIFA channel 5 interrupt */ + uint :4; + uint TPU :4; /* Timer-Pulse Unit */ + uint :4; /* Image Extraction DMAC */ + ); + pad(2); + +} __attribute((packed, aligned(4))) mod_intc_ipc_7305_t; + +/* + mod_intc_icr0_7305_t + Interrupt Control Register 0: Detection mode of external pins. +*/ +typedef struct +{ + word_union(, + uint const NMIL :1; /* NMI Input Level */ + uint MAI :1; /* Mask All Interrupts */ + uint :4; + uint NMIB :1; /* Enable NMI when BL is set */ + uint NMIE :1; /* NMI Edge Selection */ + uint :2; + uint LVLMODE :1; /* Level-Sensed IRQ Retention Mode */ + uint :5; + ); + +} __attribute__((packed, aligned(2))) mod_intc_icr0_7305_t; + +/* + mod_intc_icr1_7305_t + Interrupt Control Register 1: Manages detection of IRQ interrupts +*/ +typedef struct +{ + word_union(, + uint IRQ0 :2; /* IRQ0 Sense (Edge) Select */ + uint IRQ1 :2; + uint IRQ2 :2; + uint IRQ3 :2; + uint IRQ4 :2; + uint IRQ5 :2; + uint IRQ6 :2; + uint IRQ7 :2; + ); + +} __attribute__((packed, aligned(2))) mod_intc_icr1_7305_t; + +/* + mod_intc_intpri00_7305_t + Interrupt Priority 00: Priority settings for IRQ interrupts. +*/ +typedef struct +{ + lword_union(, + uint IRQ0 :4; /* IRQ0 Interrupt Priority */ + uint IRQ1 :4; + uint IRQ2 :4; + uint IRQ3 :4; + uint IRQ4 :4; + uint IRQ5 :4; + uint IRQ6 :4; + uint IRQ7 :4; + ); + +} __attribute((packed, aligned(4))) mod_intc_intpri00_7305_t; + +/* + mod_intc_intreq00_7305_t + Interrupt Request 00: Request information of IRQ interrupts. Each of + these bits indicates whether an interrupt is being input. + + mod_intc_intmsk00_7305_t + Interrupt Mask 00: Set interrupt mask for IRQ interrupts. Writing 0 to + these bits is ignored, writing 1 masks the interrupt. + + mod_intc_intmskclr00_7305_t + Interrupt Mask Clear 00: Clear interrupt mask for IRQ interrupts. + Writing 0 to these bits is ignored, writing 1 clears the mask. +*/ +typedef struct +{ + byte_union(, + uint IRQ0 :1; + uint IRQ1 :1; + uint IRQ2 :1; + uint IRQ3 :1; + uint IRQ4 :1; + uint IRQ5 :1; + uint IRQ6 :1; + uint IRQ7 :1; + ); + +} __attribute__((packed)) mod_intc_irq_7305_t; + +typedef mod_intc_irq_7305_t mod_intc_intreq00_7305_t; +typedef mod_intc_irq_7305_t mod_intc_intmsk00_7305_t; +typedef mod_intc_irq_7305_t mod_intc_intmskclr00_7305_t; + +/* + mod_intc_7305_t + Finally the whole interrupt controller. +*/ +typedef struct +{ + /* Control registers */ + volatile mod_intc_icr0_7305_t *ICR0; + volatile mod_intc_icr1_7305_t *ICR1; + + /* Interrupt priority registers */ + volatile mod_intc_intpri00_7305_t *INTPRI00; + volatile mod_intc_ipc_7305_t *iprs; + + /* Interrupt mask & mask clear registers */ + volatile mod_intc_intmsk00_7305_t *INTMSK00; +// volatile mod_intc_masks_7305_t *masks; + volatile mod_intc_intmskclr00_7305_t *INTMSKCLR00; +// volatile mod_intc_masks_clear_7305_t *masks_clear; + +} mod_intc_7305_t; + +#endif // _MODULE_INTERRUPTS diff --git a/src/bopti/bopti_internals.c b/src/bopti/bopti_internals.c index 0469e81..cfe5013 100644 --- a/src/bopti/bopti_internals.c +++ b/src/bopti/bopti_internals.c @@ -298,6 +298,12 @@ void getStructure(image_t *img, structure_t *s) s->height = img->height; s->data = (uint8_t *)img->data; + /* TODO This is the same as + column_count = (img->width + 15) >> 5; + end = (img->width & 0x0f); + end_bytes = (end + 7) >> 3; + */ + column_count = img->width >> 5; end = img->width & 31; end_bytes = diff --git a/src/bopti/dimage.c b/src/bopti/dimage.c index fe34859..d32c09f 100644 --- a/src/bopti/dimage.c +++ b/src/bopti/dimage.c @@ -14,8 +14,8 @@ void dimage_part(int x, int y, image_t *img, int left, int top, int width, structure_t s; command_t command; - int actual_width; - int format = img->format, i = 0; + int actual_width, actual_height; + int format = img->format; if(format != format_mono && format != format_mono_alpha) return; getStructure(img, &s); @@ -33,14 +33,17 @@ void dimage_part(int x, int y, image_t *img, int left, int top, int width, if(left + width > s.width) width = s.width - left; if(top + height > s.height) height = s.height - top; - if(x + left + width <= 0 || x > 127 || y + top + height <= 0 || y > 63) + if(x + width <= 0 || x > 127 || y + height <= 0 || y > 63 + || width <= 0 || height <= 0) return; command.top = (y < 0) ? (top - y) : top; - command.bottom = top + ((y + height > 64) ? (64 - y) : height); + actual_height = (y + height > 64) ? (64 - y) : height; + command.bottom = top + actual_height; + command.left = ((x < 0) ? (left - x) : left) >> 5; actual_width = (x + width > 128) ? (128 - x) : width; - command.right = ((left + actual_width + 31) >> 5) - 1; + command.right = (left + actual_width - 1) >> 5; command.op = bopti_op_mono; @@ -49,20 +52,14 @@ void dimage_part(int x, int y, image_t *img, int left, int top, int width, bopti_vram = display_getCurrentVRAM(); - while(format) + for(int i = 0; format; format >>= 1, i++) if(format & 1) { - if(format & 1) - { - command.x = x - left; - command.y = y - top; - command.channel = (1 << i); + command.x = x - left; + command.y = y - top; + command.channel = (1 << i); - bopti(s.data, &s, &command); - s.data += s.layer_size; - } - - format >>= 1; - i++; + bopti(s.data, &s, &command); + s.data += s.layer_size; } } diff --git a/src/bopti/gimage.c b/src/bopti/gimage.c index e570634..84dc241 100644 --- a/src/bopti/gimage.c +++ b/src/bopti/gimage.c @@ -15,8 +15,8 @@ void gimage_part(int x, int y, image_t *img, int left, int top, int width, structure_t s; command_t command; - int actual_width; - int format = img->format, i = 0; + int actual_width, actual_height; + int format = img->format; getStructure(img, &s); if(width < 0) width = s.width; @@ -33,14 +33,19 @@ void gimage_part(int x, int y, image_t *img, int left, int top, int width, if(left + width > s.width) width = s.width - left; if(top + height > s.height) height = s.height - top; - if(x + left + width <= 0 || x > 127 || y + top + height <= 0 || y > 63) + if(x + width <= 0 || x > 127 || y + height <= 0 || y > 63 + || width <= 0 || height <= 0) return; + // command.bottom is excluded... command.top = (y < 0) ? (top - y) : top; - command.bottom = top + ((y + height > 64) ? (64 - y) : height); + actual_height = (y + height > 64) ? (64 - y) : height; + command.bottom = top + actual_height; + + // ... but command.right is included. Great. command.left = ((x < 0) ? (left - x) : left) >> 5; actual_width = (x + width > 128) ? (128 - x) : width; - command.right = ((left + actual_width + 31) >> 5) - 1; + command.right = (left + actual_width - 1) >> 5; command.op = bopti_op_gray; @@ -50,20 +55,14 @@ void gimage_part(int x, int y, image_t *img, int left, int top, int width, bopti_v1 = gray_lightVRAM(); bopti_v2 = gray_darkVRAM(); - while(format) + for(int i = 0; format; format >>= 1, i++) if(format & 1) { - if(format & 1) - { - command.x = x - left; - command.y = y - top; - command.channel = (1 << i); + command.x = x - left; + command.y = y - top; + command.channel = (1 << i); - bopti(s.data, &s, &command); - s.data += s.layer_size; - } - - format >>= 1; - i++; + bopti(s.data, &s, &command); + s.data += s.layer_size; } } diff --git a/src/core/gint_sh7705.c b/src/core/gint_sh7705.c index d19924d..a79802c 100644 --- a/src/core/gint_sh7705.c +++ b/src/core/gint_sh7705.c @@ -1,9 +1,10 @@ #include #include +#include #include #include -#include <7705.h> #include +#include <7705.h> /* gint_reg() diff --git a/src/core/modules.c b/src/core/modules.c index 60cc146..827110c 100644 --- a/src/core/modules.c +++ b/src/core/modules.c @@ -24,28 +24,49 @@ mod_rtc_t RTC; static void mod_init_7705(void) { - TMU.timers[0] = (void *)0xfffffe94; - TMU.timers[1] = (void *)0xfffffea0; - TMU.timers[2] = (void *)0xfffffeac; - TMU.TSTR = (void *)0xfffffe92; - TMU.TCPR2 = (void *)0xfffffeb8; +/* + INTC._7705.iprs.IPRA = (void *)0xfffffee2; + INTC._7705.iprs.IPRB = (void *)0xfffffee4; + INTC._7705.iprs.IPRC = (void *)0xa4000016; + INTC._7705.iprs.IPRD = (void *)0xa4000018; + INTC._7705.iprs.IPRE = (void *)0xa400001a; + INTC._7705.iprs.IPRF = (void *)0xa4080000; + INTC._7705.iprs.IPRG = (void *)0xa4080002; + INTC._7705.iprs.IPRH = (void *)0xa4080004; - RTC.RCR1 = (void *)0xfffffedc; - RTC.RCR2 = (void *)0xfffffede; - RTC.time = (void *)0xfffffec0; + INTC._7705.ICR0 = (void *)0xfffffee0; + INTC._7705.ICR1 = (void *)0xa4000010; + INTC._7705.ICR2 = (void *)0xa4000012; + INTC._7705.PINTER = (void *)0xa4000014; + INTC._7705.IRR0 = (void *)0xa4000004; + INTC._7705.IRR1 = (void *)0xa4000006; + INTC._7705.IRR2 = (void *)0xa4000008; +*/ + + TMU.timers[0] = (void *)0xfffffe94; + TMU.timers[1] = (void *)0xfffffea0; + TMU.timers[2] = (void *)0xfffffeac; + TMU.TSTR = (void *)0xfffffe92; + TMU.TCPR2 = (void *)0xfffffeb8; + + RTC.RCR1 = (void *)0xfffffedc; + RTC.RCR2 = (void *)0xfffffede; + RTC.time = (void *)0xfffffec0; } static void mod_init_7305(void) { - TMU.timers[0] = (void *)0xa4490008; - TMU.timers[1] = (void *)0xa4490014; - TMU.timers[2] = (void *)0xa4490020; - TMU.TSTR = (void *)0xa4490004; - TMU.TCPR2 = NULL; +// INTC._7305.iprs = (void *)0xa4080000; - RTC.RCR1 = (void *)0xa413fedc; - RTC.RCR2 = (void *)0xa413fede; - RTC.time = (void *)0xa413fec0; + TMU.timers[0] = (void *)0xa4490008; + TMU.timers[1] = (void *)0xa4490014; + TMU.timers[2] = (void *)0xa4490020; + TMU.TSTR = (void *)0xa4490004; + TMU.TCPR2 = NULL; + + RTC.RCR1 = (void *)0xa413fedc; + RTC.RCR2 = (void *)0xa413fede; + RTC.time = (void *)0xa413fec0; } /* diff --git a/src/stdlib/stdlib_rand.c b/src/stdlib/stdlib_rand.c index 9cd7765..6afcc10 100644 --- a/src/stdlib/stdlib_rand.c +++ b/src/stdlib/stdlib_rand.c @@ -1,6 +1,7 @@ #include +#include -static unsigned int seed = 1; +static uint32_t seed = 1; void srand(unsigned int new_seed) { @@ -9,7 +10,10 @@ void srand(unsigned int new_seed) int rand(void) { + seed = 16807 * seed; + uint32_t top = ((uint32_t)1 << 31) - 1; + while(seed >= top) seed -= top; /* TODO Or maybe seed = (16807 * seed) % ((1 << 31) - 1); */ - seed = seed * 1103515245 + 12345; +// seed = seed * 1103515245 + 12345; return seed & 0x7fffffff; } diff --git a/version b/version index fa6bbd4..7409007 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-349 +beta-0.9-352 From cb4b5b7a39bdc5b9feb14881e1b8c26de4ab0ade Mon Sep 17 00:00:00 2001 From: lephe Date: Sun, 30 Apr 2017 18:24:35 +0200 Subject: [PATCH 14/24] Untested INTC module in /include/modules/interrupts.h --- include/modules/interrupts.h | 200 ++++++++++++++++++++++++++++++++++- src/core/modules.c | 16 ++- version | 2 +- 3 files changed, 212 insertions(+), 6 deletions(-) diff --git a/include/modules/interrupts.h b/include/modules/interrupts.h index 47a0129..41e6bae 100644 --- a/include/modules/interrupts.h +++ b/include/modules/interrupts.h @@ -464,6 +464,178 @@ typedef mod_intc_irq_7305_t mod_intc_intreq00_7305_t; typedef mod_intc_irq_7305_t mod_intc_intmsk00_7305_t; typedef mod_intc_irq_7305_t mod_intc_intmskclr00_7305_t; +/* + mod_intc_masks_7305_t + A set of bits to mask individual interrupts. + - Masking interrupts is realized by writing 1 to IMRs ; + - Clearing masks is realized by writing 1 to IMCRs ; + Using the wrong register set, such as writing 0 to IMRs to clear a + mask, will be ignored and have no effect. Reading from IMCRs yields an + undefined value. +*/ +typedef struct +{ + byte_union(IMR0, + uint :1; + uint TUNI1_2 :1; /* TMU1 overflow interrupts */ + uint TUNI1_1 :1; + uint TUNI1_0 :1; + uint SDHII3 :1; /* SD Card Host 1 interrupts */ + uint SDHII2 :1; + uint SDHII1 :1; + uint SDHII0 :1; + ); + pad(3); + + byte_union(IMR1, + uint :4; + uint DEI3 :1; /* DMAC0A interrupts */ + uint DEI2 :1; + uint DEI1 :1; + uint DEI0 :1; + ); + pad(3); + + byte_union(IMR2, + uint :7; + uint SCIFA0 :1; /* Asynchronous Serial interrupts */ + ); + pad(3); + + byte_union(IMR3, + uint DEI3 :1; /* DMAC1A interrupts */ + uint DEI2 :1; + uint DEI1 :1; + uint DEI0 :1; + uint :4; + ); + pad(3); + + byte_union(IMR4, + uint :1; + uint TUNI0_2 :1; /* TMU0 overflow interrupts */ + uint TUNI0_1 :1; + uint TUNI0_0 :1; + uint :3; + uint LCDCI :1; /* LCD Controller Interrupt */ + ); + pad(3); + + byte_union(IMR5, + uint KEYI :1; /* Key Interface */ + uint DADERR :1; /* DMAC0B interrupts */ + uint DEI5 :1; + uint DEI4 :1; + uint :1; + uint SCIF2 :1; /* Serial Communication Interface */ + uint SCIF1 :1; + uint SCIF0 :1; + ); + pad(3); + + byte_union(IMR6, + uint :2; + uint :1; + uint SCIFA4 :1; /* SCIFA4 interrupt */ + uint :1; + uint :1; + uint MSIOFI0 :1; /* Clock-synchronized SCIF channel 0 */ + uint MSIOFI1 :1; /* Clock-synchronized SCIF channel 1 */ + ); + pad(3); + + uint8_t IMR7; + pad(3); + + byte_union(IMR8, + uint SDHII3 :1; /* SD Card Host 0 interrupts */ + uint SDHII2 :1; + uint SDHII1 :1; + uint SDHII0 :1; + uint :2; + uint SCFIA5 :1; /* SCIFA5 interrupt */ + uint FSI :1; /* FIFO-Buffered Serial Interface */ + ); + pad(3); + + byte_union(IMR9, + uint :3; + uint CMTI :1; /* Compare Match Timer Interrupt */ + uint :1; + uint USI1 :1; /* USB1 */ + uint USI0 :1; /* USB0 */ + uint :1; + ); + pad(3); + + byte_union(IMR10, + uint :1; + uint DADERR :1; /* DMAC1B interrupts */ + uint DEI5 :1; + uint DEI4 :1; + uint :1; + uint ATI :1; /* RTC Alarm interrupt */ + uint PRI :1; /* RTC Periodic interrupt */ + uint CUI :1; /* RTC Carry interrupt */ + ); + pad(3); + + byte_union(IMR11, + uint :5; + uint TPUI :1; /* Timer-Pulse Unit */ + uint :2; + ); + pad(3); + + uint8_t IMR12; + char gap2[15]; + +} __attribute__((packed)) mod_intc_masks_7305_t; + +/* + mod_intc_userimask_7305_t + User Interrupt Mask: Specifies the minimum required level for + interrupts to be accepted. + + WARNING: Writing to this register requires the eight upper bits of the + operand (ie. the new value of USERIMASK) to be 0xa5; otherwise, the + write is ignored. To modify the value of this register, do not access + the bit field directly, backup the variable and modify it: + + void set_user_imask(int new_level) + { + mod_intc_userimask_7305_t mask = *(INTC._7305.USERIMASK); + mask._a5 = 0xa5; + mask.UIMASK = new_level & 0x0f; + *(INTC._7305.USERIMASK) = mask; + } +*/ +typedef struct +{ + lword_union(, + uint _a5 :8; /* Always set to 0xa5 before writing */ + uint :16; + uint UIMASK :4; /* User Interrupt Mask Level */ + uint :4; + ); + +} __attribute__((packed, aligned(4))) mod_intc_userimask_7305_t; + +/* + mod_intc_nmifcr_7305_t + NMI Flag Control Register: Indicates the state of the NMI pin and the + NMI interrupt request. +*/ +typedef struct +{ + byte_union(, + uint const NMIL :1; /* NMI Interupt Level */ + uint :14; + uint NMIFL :1; /* NMI Interrupt Request Flag */ + ); + +} __attribute__((packed, aligned(2))) mod_intc_nmifcr_7305_t; + /* mod_intc_7305_t Finally the whole interrupt controller. @@ -480,10 +652,34 @@ typedef struct /* Interrupt mask & mask clear registers */ volatile mod_intc_intmsk00_7305_t *INTMSK00; -// volatile mod_intc_masks_7305_t *masks; + volatile mod_intc_masks_7305_t *masks; volatile mod_intc_intmskclr00_7305_t *INTMSKCLR00; -// volatile mod_intc_masks_clear_7305_t *masks_clear; + volatile mod_intc_masks_7305_t *masks_clear; + + /* Other registers */ + volatile mod_intc_intreq00_7305_t *INTREQ00; + volatile mod_intc_userimask_7305_t *USERIMASK; + volatile mod_intc_nmifcr_7305_t *NMIFCR; } mod_intc_7305_t; + + +//--- +// Platform-independent structures. +// Unfortunately there is nothing here. Users willing to manage interrupts +// using the INTC register will have to handle explicitely both platforms. +//--- + +/* + mod_intc_t + Interrupt Controller. +*/ +typedef union +{ + mod_intc_7705_t _7705; + mod_intc_7305_t _7305; + +} __attribute__((packed)) mod_intc_t; + #endif // _MODULE_INTERRUPTS diff --git a/src/core/modules.c b/src/core/modules.c index 827110c..9e5c429 100644 --- a/src/core/modules.c +++ b/src/core/modules.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -15,6 +16,7 @@ mod_tmu_t TMU; mod_rtc_t RTC; +mod_intc_t INTC; @@ -24,7 +26,6 @@ mod_rtc_t RTC; static void mod_init_7705(void) { -/* INTC._7705.iprs.IPRA = (void *)0xfffffee2; INTC._7705.iprs.IPRB = (void *)0xfffffee4; INTC._7705.iprs.IPRC = (void *)0xa4000016; @@ -41,7 +42,6 @@ static void mod_init_7705(void) INTC._7705.IRR0 = (void *)0xa4000004; INTC._7705.IRR1 = (void *)0xa4000006; INTC._7705.IRR2 = (void *)0xa4000008; -*/ TMU.timers[0] = (void *)0xfffffe94; TMU.timers[1] = (void *)0xfffffea0; @@ -56,7 +56,17 @@ static void mod_init_7705(void) static void mod_init_7305(void) { -// INTC._7305.iprs = (void *)0xa4080000; + INTC._7305.ICR0 = (void *)0xa4140000; + INTC._7305.ICR1 = (void *)0xa414001c; + INTC._7305.INTPRI00 = (void *)0xa4140010; + INTC._7305.iprs = (void *)0xa4080000; + INTC._7305.INTMSK00 = (void *)0xa4140044; + INTC._7305.masks = (void *)0xa4080080; + INTC._7305.INTMSKCLR00 = (void *)0xa4140064; + INTC._7305.masks_clear = (void *)0xa40800c0; + INTC._7305.INTREQ00 = (void *)0xa4140024; + INTC._7305.USERIMASK = (void *)0xa4700000; + INTC._7305.NMIFCR = (void *)0xa41400c0; TMU.timers[0] = (void *)0xa4490008; TMU.timers[1] = (void *)0xa4490014; diff --git a/version b/version index 7409007..405a713 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-352 +beta-0.9-354 From c25d35c48fcd0f73ae26fcf249ba65ad02e1a09e Mon Sep 17 00:00:00 2001 From: lephe Date: Sat, 1 Jul 2017 16:07:14 +0200 Subject: [PATCH 15/24] Interrupt controller module description (WIP), task switch and return to menu. --- TODO | 3 +- demo/gintdemo.c | 22 +++++--- demo/gintdemo.ld | 2 +- demo/test_keyboard.c | 104 ++++++++++++++++++++++------------- include/gint.h | 12 ++++ include/gray.h | 8 +++ include/keyboard.h | 4 ++ include/modules/interrupts.h | 11 ++-- include/modules/macros.h | 4 +- src/core/crt0.c | 12 ---- src/core/gint.c | 2 +- src/core/gint_sh7705.c | 72 +++++++++++++----------- src/core/init_quit.c | 43 +++++++++++++++ src/core/syscalls.s | 29 ++++++++-- src/ctype/ctype_functions.c | 2 + src/gray/gray_engine.c | 11 ++++ src/keyboard/getkey.c | 14 +++++ src/keyboard/keyboard_core.c | 68 +++++++++++++++-------- src/rtc/rtc_callback.c | 2 +- version | 2 +- 20 files changed, 297 insertions(+), 130 deletions(-) diff --git a/TODO b/TODO index 6083b62..dca8224 100644 --- a/TODO +++ b/TODO @@ -6,11 +6,10 @@ Bugs to fix: Things to do before 1.0: - bopti: Test partial transparency -* core: Allow return to menu - demo: Try 284x124 at (-60, -28) (all disadvantages) - project: Check size of *all* library structures - project: Clean headers that have some internal definitions -- project: Unify this hellish mess of register access! +- project: Get rid of 7705.h (keyboard) and 7305.h (keyboard, gint) - time: Compute CLOCKS_PER_SEC Things to do later: diff --git a/demo/gintdemo.c b/demo/gintdemo.c index 9532bdb..cfc653d 100644 --- a/demo/gintdemo.c +++ b/demo/gintdemo.c @@ -277,6 +277,9 @@ void tlb_debug(void) } */ +#include +#include + /* main_menu() Displays the main menu and returns user's choice: 0 for [EXIT], @@ -301,7 +304,7 @@ void main_menu(int *category, int *app) }; const char *list_tests[] = { - "Keyboard and events", + "Keyboard & events", "Gray engine", "Image rendering", "Text rendering", @@ -353,9 +356,10 @@ void main_menu(int *category, int *app) { case 0: print(1, 1, "gint %s", gint_version); - print(2, 4, "handler size: %5d", gint_size); - print(2, 5, "mpu type: %7s", mpu); - print(2, 6, "romdata: %08x", &romdata); + print(2, 3, "MPU type %7s", mpu); + print(2, 4, "Add-in size %3dk", + ((uint32_t)&romdata - 0x00300000) >> 10); + print(2, 5, "gint size %5do", gint_size); list = NULL; break; @@ -408,9 +412,6 @@ void main_menu(int *category, int *app) switch(getkey()) { -// case KEY_7: -// crt0_system_menu(); -// break; case KEY_F1: if(!tab) break; tab = 0; @@ -434,6 +435,9 @@ void main_menu(int *category, int *app) index = 0; scroll = 0; break; + case KEY_F5: + gint_switch(); + break; case KEY_F6:; void screen(void); screen(); @@ -480,11 +484,11 @@ void main_menu(int *category, int *app) if(app) *app = index + 1; return; - case KEY_MENU: +/* case KEY_MENU: if(category) *category = 0; if(app) *app = 0; return; - +*/ default: leave = 0; } diff --git a/demo/gintdemo.ld b/demo/gintdemo.ld index 9111fcc..7a7d11e 100644 --- a/demo/gintdemo.ld +++ b/demo/gintdemo.ld @@ -3,7 +3,7 @@ output. Note how symbols romdata, bbss, ebss, bdata and edata are used in the initialization routine (crt0.c) to initialize the application. - Two ram areas are specified. The "real ram" is accessed direcly while + Two ram areas are specified. The "real ram" is accessed directly while the other area is virtualized. It is not possible to execute code in virtualized ram. */ diff --git a/demo/test_keyboard.c b/demo/test_keyboard.c index 3012590..02ea379 100644 --- a/demo/test_keyboard.c +++ b/demo/test_keyboard.c @@ -4,8 +4,9 @@ #include #include -static void draw_keyboard(volatile uint8_t *state) +static int draw_keyboard(volatile uint8_t *state) { + int pressed_keys = 0; int i, j, k, l; int x, y; @@ -29,6 +30,7 @@ static void draw_keyboard(volatile uint8_t *state) // Drawing a filled shape when the key is pressed. if(state[i] & (0x80 >> j)) { + pressed_keys++; for(k = -2; k <= 2; k++) for(l = -2; l <= 2; l++) if(abs(k) + abs(l) <= 2) dpixel(x + k, y + l, color_black); @@ -49,47 +51,61 @@ static void draw_keyboard(volatile uint8_t *state) // An horizontal line to separate parts of the keyboard. dline(5, 28, 32, 28, color_black); + + return pressed_keys; } typedef struct { - event_type_t type; uint32_t key; int repeats; +} history_key_t; -} enhanced_event_t; +typedef struct { + history_key_t *data; + int size; + int pressed; +} history_t; -static void push_history(enhanced_event_t *history, int size, event_t event) +static int pressed_keys = -1; +static int releases = 0; + +static void history_push(history_t *h, event_t event) { - #define event_eq(x, y) \ - ((x).type == (y).type && (x).key == (y).key.code) + // Only reset the number of pressed keys in the history when the actual + // number of pressed keys reaches 0. - // Determining where the history ends. - int length = 0; - while(length < size && history[length].type != event_none) length++; - - // Checking if the previous event is being repeated. - if(length > 0 && event_eq(history[length - 1], event)) + if(event.type == event_key_release) { - history[length - 1].repeats++; + pressed_keys--; + if(!pressed_keys) h->pressed = 0; return; } - // Making up some space if required. - if(length == size) + // Now we're sure a key was pressed or repeated. Check if it's in the + // last pressed elements of the history array, if so, update it. + // Otherwise make space for a new entry and add it. + + if(event.type == event_key_press) pressed_keys++; + + for(int i = h->size - h->pressed; i < h->size; i++) { - for(int i = 0; i < size - 1; i++) history[i] = history[i + 1]; - length = size - 1; + if(h->data[i].key == event.key.code) + { + h->data[i].repeats++; + return; + } } - // Adding a new entry to the history. - history[length].type = event.type; - history[length].key = event.key.code; - history[length].repeats = 1; + // If there are already h->size keys pressed, do not add more. + if(event.type == event_key_press) h->pressed++; + if(h->pressed > h->size) return; - #undef event_eq + for(int i = 0; i < h->size - 1; i++) h->data[i] = h->data[i + 1]; + h->data[h->size - 1].key = event.key.code; + h->data[h->size - 1].repeats = 1; } -static void draw_events(enhanced_event_t *history, int size) +static void draw_events(history_t *h) { const char *key_names[] = { "F1", "F2", "F3", "F4", "F5", "F6", @@ -102,17 +118,21 @@ static void draw_events(enhanced_event_t *history, int size) "1", "2", "3", "+", "-", NULL, "0", ".", "\x08", "(-)", "EXE", NULL }; - const char *event_names[] = { - "None ", "User ", "Press", "Rept.", "Rel. ", "Timer" - }; - for(int i = 0; i < size && history[i].type != event_none; i++) + int y = 3; + + print(8, 2, "%d %d %d", pressed_keys, h->pressed, releases); + + for(int i = 0; i < h->size; i++) { - print(8, 3 + i, "%s %s", event_names[history[i].type], - key_names[key_id(history[i].key)]); - if(history[i].repeats > 1) - print(19, 3 + i, "%d", history[i].repeats); + if(!h->data[i].key) continue; + print(8, y, "%s", key_names[key_id(h->data[i].key)]); + if(h->data[i].repeats > 1) + print(18, y, "%d", h->data[i].repeats); + y++; } + + if(h->pressed > h->size) print(8, 8, "(more)"); } /* @@ -121,23 +141,31 @@ static void draw_events(enhanced_event_t *history, int size) */ void test_keyboard_events(void) { - enhanced_event_t history[5]; - int history_size = 5; + history_key_t history_data[5] = { 0 }; + history_t history = { + .data = history_data, + .size = 5, + .pressed = 0, + }; event_t event; - for(int i = 0; i < history_size; i++) history[i].type = event_none; - while(1) { dclear(); locate(1, 1, "Keyboard and events"); - draw_keyboard(keyboard_stateBuffer()); - draw_events(history, history_size); + + // There may be more than zero keys pressed when this test + // starts. We need to detect this count automatically. + int x = draw_keyboard(keyboard_stateBuffer()); + if(pressed_keys < 0) pressed_keys = x; + + draw_events(&history); dupdate(); event = waitevent(); + if(event.type == event_key_release) releases++; if(event.type == event_key_press && event.key.code == KEY_EXIT) break; - push_history(history, history_size, event); + history_push(&history, event); } } diff --git a/include/gint.h b/include/gint.h index ac264cb..3060a89 100644 --- a/include/gint.h +++ b/include/gint.h @@ -236,4 +236,16 @@ typedef enum */ volatile void *gint_reg(gint_register_t reg); + + +//--- +// Other functions +//--- + +/* + gint_switch() + Temporarily returns to the system's main menu. +*/ +void gint_switch(void); + #endif // _GINT_H diff --git a/include/gray.h b/include/gray.h index a4e3233..86fa721 100644 --- a/include/gray.h +++ b/include/gray.h @@ -51,6 +51,14 @@ uint32_t *gray_lightVRAM(void); */ uint32_t *gray_darkVRAM(void); +/* + gray_currentVRAM() + Returns the currently displayed video ram (if the engine runs). Used + internally, but has no interest for the user. You don't want to draw to + this vram. +*/ +uint32_t *gray_currentVRAM(void); + /* gray_getDelays() Returns the gray engine delays. Pointers are not set if NULL. diff --git a/include/keyboard.h b/include/keyboard.h index f788c13..ea5cd7f 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -162,6 +162,10 @@ typedef enum // compatible models. getkey_manage_backlight = 0x04, + // Allow returning to menu using the [MENU] key. (This operation is not + // absolutely safe.) + getkey_task_switch = 0x08, + // Allow key repetition. This option does not control the generation of // repeat events (use keyboard_setRepeatRate() for this) but filters // them. Please note that modifiers will never be repeated, even when diff --git a/include/modules/interrupts.h b/include/modules/interrupts.h index 41e6bae..54b172e 100644 --- a/include/modules/interrupts.h +++ b/include/modules/interrupts.h @@ -590,7 +590,7 @@ typedef struct uint8_t IMR12; char gap2[15]; -} __attribute__((packed)) mod_intc_masks_7305_t; +} __attribute__((packed, aligned(4))) mod_intc_masks_7305_t; /* mod_intc_userimask_7305_t @@ -605,7 +605,7 @@ typedef struct void set_user_imask(int new_level) { mod_intc_userimask_7305_t mask = *(INTC._7305.USERIMASK); - mask._a5 = 0xa5; + mask._0xa5 = 0xa5; mask.UIMASK = new_level & 0x0f; *(INTC._7305.USERIMASK) = mask; } @@ -613,7 +613,7 @@ typedef struct typedef struct { lword_union(, - uint _a5 :8; /* Always set to 0xa5 before writing */ + uint _0xa5 :8; /* Always set to 0xa5 before writing */ uint :16; uint UIMASK :4; /* User Interrupt Mask Level */ uint :4; @@ -628,7 +628,7 @@ typedef struct */ typedef struct { - byte_union(, + word_union(, uint const NMIL :1; /* NMI Interupt Level */ uint :14; uint NMIFL :1; /* NMI Interrupt Request Flag */ @@ -682,4 +682,7 @@ typedef union } __attribute__((packed)) mod_intc_t; +// Here's what you'll need to use. +extern mod_intc_t INTC; + #endif // _MODULE_INTERRUPTS diff --git a/include/modules/macros.h b/include/modules/macros.h index f17e817..327dde7 100644 --- a/include/modules/macros.h +++ b/include/modules/macros.h @@ -39,7 +39,7 @@ typedef unsigned uint; uint16_t word; \ struct { fields } \ __attribute__((packed)); \ - } __attribute__((packed)) name + } __attribute__((packed, aligned(2))) name /* lword_union() @@ -52,6 +52,6 @@ typedef unsigned uint; uint32_t lword; \ struct { fields } \ __attribute__((packed)); \ - } __attribute__((packed)) name + } __attribute__((packed, aligned(4))) name #endif // _MODULES_MACROS_H diff --git a/src/core/crt0.c b/src/core/crt0.c index be9d0e4..618af26 100644 --- a/src/core/crt0.c +++ b/src/core/crt0.c @@ -140,8 +140,6 @@ __attribute__((section(".pretext.entry"))) int start(void) dg->stage = stage_running; #endif - - // Saving the execution state there. int x = setjmp(env); // If the program has just started, executing main(). Otherwise, the @@ -253,13 +251,3 @@ int atexit(void (*function)(void)) atexit_handlers[atexit_index++] = function; return 0; } - -void crt0_system_menu(void) -{ - fini(); - gint_quit(); - __system_menu(); - mmu_pseudoTLBInit(); - gint_init(); - init(); -} diff --git a/src/core/gint.c b/src/core/gint.c index 6d309f9..f213dcf 100644 --- a/src/core/gint.c +++ b/src/core/gint.c @@ -153,7 +153,7 @@ void gint_install(gint_interrupt_type_t type, void *function) /* gint_uninstall() - Uninstalls the exception or interrupt handler that was used for the + Un-installs the exception or interrupt handler that was used for the given interrupt, falling back to gint's default handler. */ void gint_uninstall(gint_interrupt_type_t type) diff --git a/src/core/gint_sh7705.c b/src/core/gint_sh7705.c index a79802c..80df397 100644 --- a/src/core/gint_sh7705.c +++ b/src/core/gint_sh7705.c @@ -4,7 +4,6 @@ #include #include #include -#include <7705.h> /* gint_reg() @@ -34,15 +33,18 @@ volatile void *gint_reg_7705(gint_register_t reg) void gint_save_7705(environment_7705_t *e) { + mod_intc_7705_t *intc = &INTC._7705; + mod_intc_ipc_7705_t *ipc = &intc->iprs; + // Saving the interrupt masks from registers IPRA to IPRH. - e->IPR[0] = INTC.IPRA.WORD; - e->IPR[1] = INTC.IPRB.WORD; - e->IPR[2] = INTX.IPRC.WORD; - e->IPR[3] = INTX.IPRD.WORD; - e->IPR[4] = INTX.IPRE.WORD; - e->IPR[5] = INTX.IPRF.WORD; - e->IPR[6] = INTX.IPRG.WORD; - e->IPR[7] = INTX.IPRH.WORD; + e->IPR[0] = ipc->IPRA->word; + e->IPR[1] = ipc->IPRB->word; + e->IPR[2] = ipc->IPRC->word; + e->IPR[3] = ipc->IPRD->word; + e->IPR[4] = ipc->IPRE->word; + e->IPR[5] = ipc->IPRF->word; + e->IPR[6] = ipc->IPRG->word; + e->IPR[7] = ipc->IPRH->word; // Saving RTC registers. e->RCR1 = RTC.RCR1->byte; @@ -54,35 +56,39 @@ void gint_save_7705(environment_7705_t *e) e->TMU2 = *(TMU.timers[2]); e->TSTR = TMU.TSTR->byte; - // Saving port data used to access the keyboard. +/* // Saving port data used to access the keyboard. e->PACR = PFC.PACR.WORD; e->PADR = PA.DR.BYTE; e->PBCR = PFC.PBCR.WORD; e->PBDR = PB.DR.BYTE; e->PMCR = PFC.PMCR.WORD; e->PMDR = PM.DR.BYTE; +*/ } void gint_lock_and_setup_7705(void) { + mod_intc_7705_t *intc = &INTC._7705; + mod_intc_ipc_7705_t *ipc = &intc->iprs; + // Disabling everything by default to avoid receiving an interrupt that // the handler doesn't handle, which would cause the user program to // freeze. - INTC.IPRA.WORD = 0x0000; - INTC.IPRB.WORD = 0x0000; - INTX.IPRC.WORD = 0x0000; - INTX.IPRD.WORD = 0x0000; - INTX.IPRE.WORD = 0x0000; - INTX.IPRF.WORD = 0x0000; - INTX.IPRG.WORD = 0x0000; - INTX.IPRH.WORD = 0x0000; + ipc->IPRA->word = 0x0000; + ipc->IPRB->word = 0x0000; + ipc->IPRC->word = 0x0000; + ipc->IPRD->word = 0x0000; + ipc->IPRE->word = 0x0000; + ipc->IPRF->word = 0x0000; + ipc->IPRG->word = 0x0000; + ipc->IPRH->word = 0x0000; // Allowing RTC and timer (which handles keyboard and a whole bunch of // other things). - INTC.IPRA.BIT._RTC = 10; - INTC.IPRA.BIT._TMU0 = 12; - INTC.IPRA.BIT._TMU1 = 12; - INTC.IPRA.BIT._TMU2 = 12; + ipc->IPRA->RTC = 10; + ipc->IPRA->TMU0 = 12; + ipc->IPRA->TMU1 = 12; + ipc->IPRA->TMU2 = 12; // Don't enable RTC periodic signals by default. RTC.RCR2->byte = 0x09; @@ -90,15 +96,18 @@ void gint_lock_and_setup_7705(void) void gint_restore_and_unlock_7705(environment_7705_t *e) { + mod_intc_7705_t *intc = &INTC._7705; + mod_intc_ipc_7705_t *ipc = &intc->iprs; + // Restoring the saved states. - INTC.IPRA.WORD = e->IPR[0]; - INTC.IPRB.WORD = e->IPR[1]; - INTX.IPRC.WORD = e->IPR[2]; - INTX.IPRD.WORD = e->IPR[3]; - INTX.IPRE.WORD = e->IPR[4]; - INTX.IPRF.WORD = e->IPR[5]; - INTX.IPRG.WORD = e->IPR[6]; - INTX.IPRH.WORD = e->IPR[7]; + ipc->IPRA->word = e->IPR[0]; + ipc->IPRB->word = e->IPR[1]; + ipc->IPRC->word = e->IPR[2]; + ipc->IPRD->word = e->IPR[3]; + ipc->IPRE->word = e->IPR[4]; + ipc->IPRF->word = e->IPR[5]; + ipc->IPRG->word = e->IPR[6]; + ipc->IPRH->word = e->IPR[7]; // Restoring RTC registers. RTC.RCR1->byte = e->RCR1; @@ -110,11 +119,12 @@ void gint_restore_and_unlock_7705(environment_7705_t *e) *(TMU.timers[2]) = e->TMU2; TMU.TSTR->byte = e->TSTR; - // Restoring keyboard-related I/O port registers. +/* // Restoring keyboard-related I/O port registers. PFC.PACR.WORD = e->PACR; PA.DR.BYTE = e->PADR; PFC.PBCR.WORD = e->PBCR; PB.DR.BYTE = e->PBDR; PFC.PMCR.WORD = e->PMCR; PM.DR.BYTE = e->PMDR; +*/ } diff --git a/src/core/init_quit.c b/src/core/init_quit.c index a50d287..6103f41 100644 --- a/src/core/init_quit.c +++ b/src/core/init_quit.c @@ -79,3 +79,46 @@ void gint_quit(void) // Restoring the system's VBR. gint_setvbr(gint.system_vbr, stop); } + + + +//--- +// System-switch technique +//--- + +#include +#include + +/* + gint_switch() + Temporarily returns to the system's main menu. +*/ +static environment_t switch_env; +static void switch_(void) +{ + isSH3() ? gint_restore_and_unlock_7705(&switch_env.env_7705) + : gint_restore_and_unlock_7305(&switch_env.env_7305); +} +void gint_switch(void) +{ + // Save the current environment in a special buffer, so that we can + // restore it if we ever come back to gint. + isSH3() ? gint_save_7705(&switch_env.env_7705) + : gint_save_7305(&switch_env.env_7305); + // Give the control back to the system. + gint_setvbr(gint.system_vbr, stop); + + // When returning to the add-in from the menu, the system displays the + // add-in's video ram again, but it's often blank. We thus need to copy + // the contents of the gint video ram to the system's. + void *vram = gray_runs() + ? gray_currentVRAM() + : display_getCurrentVRAM(); + + // Use system calls to put KEY_MENU in the key buffer and invoke + // GetKeyWait(), triggering the main menu. + __system_menu(vram); + + // If the user came back, restore the gint working environment. + gint_setvbr(gint.gint_vbr, switch_); +} diff --git a/src/core/syscalls.s b/src/core/syscalls.s index 0fdb38d..960c2de 100644 --- a/src/core/syscalls.s +++ b/src/core/syscalls.s @@ -4,7 +4,6 @@ All the system calls used by the library. Somehow "the less, the better". We have finally gotten rid of every obscure system-related syscalls! - Looks like an important step towards being completely free-standing :) */ .global ___malloc @@ -45,6 +44,18 @@ ___system_menu: sts.l pr, @-r15 add #-4, r15 + /* Copying gint's VRAM into the system's. */ + mov.l syscall_table, r1 + mov.l .syscall_vram, r0 + jsr @r1 + mov.l r4, @r15 /* gint video ram */ + mov r0, r4 + mov.l @r15, r5 + mov.w .vram_size, r6 + mov.l .memcpy, r1 + jsr @r1 + nop + /* Putting the matrix code in the key buffer. */ mov r15, r4 @@ -60,14 +71,14 @@ ___system_menu: mov r15, r4 /* column pointer */ add #-4, r15 mov r15, r5 /* row pointer */ - mov #-5, r15 - mov r15, r1 + add #-4, r15 + mov r15, r1 /* keycode pointer */ mov #2, r6 /* type of waiting */ mov #0, r7 /* timeout period */ - mov.l r1, @-r15 /* keycode pointer */ - mov #0, r2 - mov.l r2, @-r15 /* allow return to menu */ + mov.l r1, @-r15 + mov #0, r2 /* allow return to menu */ + mov.l r2, @-r15 mov.l syscall_table, r1 mov.l .syscall_getkeywait, r0 @@ -88,8 +99,14 @@ ___system_menu: .long 0x0247 .syscall_putcode: .long 0x024f +.syscall_vram: + .long 0x0135 .matrix_menu: .word 0x0308 +.vram_size: + .word 1024 +.memcpy: + .long _memcpy diff --git a/src/ctype/ctype_functions.c b/src/ctype/ctype_functions.c index f4697ac..ad1500b 100644 --- a/src/ctype/ctype_functions.c +++ b/src/ctype/ctype_functions.c @@ -69,3 +69,5 @@ _inline int tolower(int c) { _inline int toupper(int c) { return c & ~(islower(c) << 1); } + +#undef _inline diff --git a/src/gray/gray_engine.c b/src/gray/gray_engine.c index 3d12bb1..8e9d207 100644 --- a/src/gray/gray_engine.c +++ b/src/gray/gray_engine.c @@ -140,6 +140,17 @@ uint32_t *gray_darkVRAM(void) return vrams[(~current & 2) | 1]; } +/* + gray_currentVRAM() + Returns the currently displayed video ram (if the engine runs). Used + internally, but has no interest for the user. You don't want to draw to + this vram. +*/ +uint32_t *gray_currentVRAM(void) +{ + return vrams[current ^ 1]; +} + /* gray_getDelays() Returns the gray engine delays. Pointers are not set if NULL. diff --git a/src/keyboard/getkey.c b/src/keyboard/getkey.c index 4889b77..1fc3eb1 100644 --- a/src/keyboard/getkey.c +++ b/src/keyboard/getkey.c @@ -3,6 +3,7 @@ #include #include #include +#include /* getkey() @@ -15,6 +16,7 @@ int getkey(void) getkey_shift_modifier | getkey_alpha_modifier | getkey_manage_backlight | + getkey_task_switch | getkey_repeat_arrow_keys, 0 @@ -62,6 +64,11 @@ int getkey_opt(getkey_option_t options, int delay_ms) modifier &= ~MOD_SHIFT; continue; } + if((options & getkey_task_switch) && key == KEY_MENU + && !modifier) + { + continue; + } if(options & getkey_shift_modifier && key == KEY_SHIFT) { modifier ^= MOD_SHIFT; @@ -98,6 +105,13 @@ int getkey_opt(getkey_option_t options, int delay_ms) break; case event_key_release: + if((options & getkey_task_switch) && event.key.code == KEY_MENU + && !modifier) + { + gint_switch(); + continue; + } + if((int)event.key.code != last_key) break; last_key = KEY_NONE; last_repeats = 0; diff --git a/src/keyboard/keyboard_core.c b/src/keyboard/keyboard_core.c index bb74325..6e956f4 100644 --- a/src/keyboard/keyboard_core.c +++ b/src/keyboard/keyboard_core.c @@ -82,42 +82,66 @@ static inline void push_release(int keycode) */ void keyboard_interrupt(void) { - uint8_t state[10] = { 0 }; + // This procedure is critical for speed. If there's anything that could + // be optimized, please tell me. + // New keyboard state. + uint8_t state[10] = { 0 }; isSH3() ? keyboard_updateState_7705(state) : keyboard_updateState_7305(state); - // This procedure really needs to be speed-optimized... and it's hard - // because of this bit manipulation. This condition handles AC/ON. - if(keyboard_state[0] ^ state[0]) - { - uint8_t pressed = ~keyboard_state[0] & state[0]; - uint8_t released = keyboard_state[0] & ~state[0]; + // Event types associated with each old/new state pair (see later). + uint8_t events[4] = { + event_none, + event_key_release, + event_key_press, + event_key_repeat, + }; - if(pressed & 1) push_press(KEY_AC_ON); - if(released & 1) push_release(KEY_AC_ON); + // AC/ON has not a matrix code that corresponds to its location in the + // buffer, so we need to check it independently. + if(keyboard_state[0] | state[0]) + { + int kind = (state[0] << 1) | keyboard_state[0]; + + if(kind) + { + event_t event = { + .type = events[kind], + .key.code = KEY_AC_ON, + .key.id = key_id(KEY_AC_ON), + .key.character = key_char(KEY_AC_ON) + }; + event_push(event); + } } keyboard_state[0] = state[0]; for(int row = 1; row <= 9; row++) { - uint8_t pressed = ~keyboard_state[row] & state[row]; - uint8_t repeated = keyboard_state[row] & state[row]; - uint8_t released = keyboard_state[row] & ~state[row]; + // Shifting the new state will allow us to make up a 2-bit + // value for each key more easily, improving efficiency. + uint16_t old = keyboard_state[row]; + uint16_t new = state[row] << 1; + + if(!new && !old) continue; keyboard_state[row] = state[row]; - // Make this a bit faster. - if(!(pressed | repeated | released)) continue; - - for(int column = 0; column < 8; column++) + for(uint8_t code = row; code < (row | 0x80); code += 0x10) { - if(pressed & 1) push_press ((column << 4) | row); - if(repeated & 1) push_repeat ((column << 4) | row); - if(released & 1) push_release((column << 4) | row); + int kind = (new & 2) | (old & 1); + old >>= 1; + new >>= 1; - pressed >>= 1; - repeated >>= 1; - released >>= 1; + if(!kind) continue; + + event_t event = { + .type = events[kind], + .key.code = code, + .key.id = key_id(code), + .key.character = key_char(code) + }; + event_push(event); } } diff --git a/src/rtc/rtc_callback.c b/src/rtc/rtc_callback.c index 1efeb24..775910d 100644 --- a/src/rtc/rtc_callback.c +++ b/src/rtc/rtc_callback.c @@ -44,7 +44,7 @@ static void rtc_cb_update(void) -1 Array is full -2 Invalid parameter The number of repeats may be set to 0, in which case the callback is - called indefinitely unless the user calls rtc_cb_end(). + called indefinitely until the user calls rtc_cb_end(). */ int rtc_cb_add(rtc_frequency_t freq, void (*function)(void), int repeats) { diff --git a/version b/version index 405a713..18bb01c 100644 --- a/version +++ b/version @@ -1 +1 @@ -beta-0.9-354 +beta-0.9-410 From 7478a7768fbac97a189a72d8b768890653b673ae Mon Sep 17 00:00:00 2001 From: lephe Date: Wed, 5 Jul 2017 21:38:12 +0200 Subject: [PATCH 16/24] Startup logs instead of diagnostics, created .gint_bss, updated demo. --- Makefile | 41 +---- TODO | 14 +- configure | 24 ++- debug/addin.ld | 31 ---- debug/crt0.s | 172 ------------------ debug/gintdbg.c | 178 ------------------- debug/icon.bmp | Bin 1870 -> 0 bytes debug/include/dispbios.h | 97 ----------- debug/include/endian.h | 31 ---- debug/include/filebios.h | 116 ------------- debug/include/fxlib.h | 100 ----------- debug/include/keybios.h | 158 ----------------- debug/include/timer.h | 31 ---- debug/libfx.a | Bin 410910 -> 0 bytes debug/vsprintf.c | 160 ----------------- demo/gintdemo.c | 19 +- demo/gintdemo.ld | 71 ++++---- demo/resources/font_modern.bmp | Bin 24698 -> 24698 bytes demo/resources/opt_gray.bmp | Bin 3194 -> 3194 bytes demo/test_gray.c | 21 ++- demo/test_keyboard.c | 56 ++++-- include/events.h | 8 +- include/internals/gint.h | 100 +---------- include/internals/init.h | 37 ++++ include/internals/keyboard.h | 7 + include/internals/syscalls.h | 26 +++ include/internals/tales.h | 7 - include/internals/timer.h | 12 +- include/mpu.h | 6 +- src/core/crt0.c | 253 --------------------------- src/core/init_quit.c | 24 ++- src/core/mpu.c | 3 +- src/core/syscalls.s | 24 ++- src/display/display_vram.c | 11 +- src/gray/gray_engine.c | 42 +++-- src/init/crt0.c | 306 +++++++++++++++++++++++++++++++++ src/init/util.c | 90 ++++++++++ src/keyboard/getkey.c | 4 + src/keyboard/keyboard_core.c | 39 ----- src/keyboard/multigetkey.c | 54 ------ src/stdlib/free.c | 5 +- src/stdlib/malloc.c | 2 +- src/stdlib/realloc.c | 2 +- src/tales/tales_internals.c | 15 +- version | 2 +- 45 files changed, 689 insertions(+), 1710 deletions(-) delete mode 100644 debug/addin.ld delete mode 100644 debug/crt0.s delete mode 100644 debug/gintdbg.c delete mode 100644 debug/icon.bmp delete mode 100644 debug/include/dispbios.h delete mode 100644 debug/include/endian.h delete mode 100644 debug/include/filebios.h delete mode 100644 debug/include/fxlib.h delete mode 100644 debug/include/keybios.h delete mode 100644 debug/include/timer.h delete mode 100644 debug/libfx.a delete mode 100644 debug/vsprintf.c create mode 100644 include/internals/init.h create mode 100644 include/internals/syscalls.h delete mode 100644 src/core/crt0.c create mode 100644 src/init/crt0.c create mode 100644 src/init/util.c delete mode 100644 src/keyboard/multigetkey.c diff --git a/Makefile b/Makefile index 83a3ee9..5924ace 100755 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ include Makefile.cfg #--- # Modules -modules-gint = bopti clock core display events gray keyboard mmu rtc \ +modules-gint = bopti clock core display events gray init keyboard mmu rtc \ screen tales timer modules-libc = ctype setjmp stdio stdlib string time @@ -19,7 +19,6 @@ modules-libc = ctype setjmp stdio stdlib string time target-lib = libgint.a target-std = libc.a target-g1a = gintdemo.g1a -target-dbg = gintdbg.g1a # Tools cc = sh3eb-elf-gcc @@ -44,17 +43,6 @@ demo-ldflags = $(demo-cflags) -T demo/gintdemo.ld -L. -lgint -lc -lgcc demo-cflags = -m3 -mb -nostdlib -I include -ffreestanding -std=c11 -Os \ -Wall -Wextra -# Debugger application (displays past diagnostics without running gint) -debug-src = $(notdir $(wildcard debug/*.[cs])) -debug-dep = $(wildcard debug/*.h) -debug-icon = debug/icon.bmp -debug-obj = $(debug-src:%=build/debug_%.o) -debug-elf = build/gintdbg.elf -debug-bin = build/gintdbg.bin -debug-ldflags = $(debug-cflags) -T debug/addin.ld -L debug -lfx -lgcc -debug-cflags = -m3 -mb -nostdlib -ffreestanding -I debug/include -I \ - include -std=c11 -Os -Wall -Wextra - # Specific objects obj-lib-spec = build/display_font_system.bmp.o obj-std-spec = @@ -159,7 +147,7 @@ debug-ldflags += -Wl,--defsym,_GINT_VERSION=$(version_symbol) all-lib: $(config) $(target-std) $(target-lib) -all: $(config) $(target-std) $(target-lib) $(target-g1a) $(target-dbg) +all: $(config) $(target-std) $(target-lib) $(target-g1a) build: $(if $(VERBOSE),,@ printf '\e[35;1mdir \u00bb\e[0m mkdir $@\n') @@ -196,17 +184,6 @@ $(target-g1a): $(config) $(target-std) $(target-lib) $(demo-obj) @ printf $$(stat -c %s $@) @ printf ' bytes)\n\n' -$(target-dbg): $(config) $(debug-obj) - $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m ld -o $(debug-elf)\n') - $(if $(VERBOSE),,@) $(cc) -o $(debug-elf) $(debug-obj) $(debug-ldflags) - $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m objcopy -o $(debug-bin)\n') - $(if $(VERBOSE),,@) $(ob) -R .comment -R .bss -O binary $(debug-elf) $(debug-bin) - $(if $(VERBOSE),,@ printf '\e[35;1mexe \u00bb\e[0m g1a-wrapper -o $@\n') - $(if $(VERBOSE),,@) $(wr) $(debug-bin) -o $@ -i $(debug-icon) - @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built debug application (' - @ printf $$(stat -c %s $@) - @ printf ' bytes)\n\n' - # Automated rules $(foreach mod,$(modules), \ @@ -244,16 +221,6 @@ build/demo_%.bmp.o: demo/resources/%.bmp $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') $(if $(VERBOSE),,@) fxconv $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) -# Debug application - -build/debug_%.s.o: debug/%.s $(config) - $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m as $<\n') - $(if $(VERBOSE),,@) $(as) -c $< -o $@ - -build/debug_%.c.o: debug/%.c $(hdr-dep) $(debug-dep) $(config) - $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m cc $<\n') - $(if $(VERBOSE),,@) $(cc) -c $< -o $@ $(debug-cflags) - #--- @@ -282,9 +249,7 @@ endif install-demo: all p7 send -f $(target-g1a) -install-debug: all - p7 send -f $(target-dbg) .PHONY: all-lib all help .PHONY: clean mrproper distclean -.PHONY: install install-demo install-debug +.PHONY: install install-demo diff --git a/TODO b/TODO index dca8224..7a2c0ff 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,11 @@ Bugs to fix: -- A few key hits ignored after leaving the application (could not reproduce) -- Lost keyboard control at startup (could not reproduce) - Alignment of ALL .data / .rodata files is required to ensure converted data is properly aligned +- Unpreditcable crap happens when more than 10 keys are pressed simultaneously +- Ensure heap data is freed when a task-switch results in leaving the app Things to do before 1.0: +- init: Move qdiv10() somewhere else - bopti: Test partial transparency - demo: Try 284x124 at (-60, -28) (all disadvantages) - project: Check size of *all* library structures @@ -33,8 +34,15 @@ Things to do later: - usb: Implement a driver Things to investigate: -- Packed bit fields alignment - Registers that may need to be saved within setjmp() - Optimizing core/gint.c leads to raising of an illegal slot exception when running the interrupt handler, although it ends on rte; lds.l @r15+, mach, which is totally not an illegal slot. + +Possibly useful modules: +- DMAC +- SCIF, SCIFA +- TPU +- USB +- CMT on SH7305, WDT on SH7705 +- ACD (SH7705 only) diff --git a/configure b/configure index 5ff00f3..83c842f 100755 --- a/configure +++ b/configure @@ -7,9 +7,10 @@ declare -A conf # Behavior -conf[GINT_DIAGNOSTICS]= +conf[GINT_STARTUP_LOG]= conf[GINT_NO_SYSCALLS]= conf[GINT_EXTENDED_LIBC]= +conf[GINT_STATIC_GRAY]= # Size limits conf[ATEXIT_MAX]=16 @@ -37,15 +38,18 @@ help() Configuration script for the gint library. Options that affect the behavior of the library: - $Cr--diagnostics $Cg[default:$Cp false$Cg]$C0 - Use gint in debug mode, where the library outputs some diagnostics in - memory or briefly on screen to diagnose incompatibilites or crashes. + $Cr--startup-log $Cg[default:$Cp false$Cg]$C0 + Enable a on-screen log at startup if a key is kept pressed while launching + the add-in, allowing easy debug and crash diagnoses. $Cr--no-syscalls $Cg[default:$Cp false$Cg]$C0 - Never use syscalls. Expect some trouble with the malloc() function... do + Never use syscalls. Expect trouble with malloc() and the gray engine. Do not trigger this switch unless you know what you are doing. $Cr--extended-libc $Cg[default:$Cp false$Cg]$C0 Enable specific C99 headers/features that are normally not required by calculator programs. This may allow porting programs from other platforms. + $Cr--static-gray-engine $Cg[default:$Cp false$Cg]$C0 + Place the gray engine vram in static ram instead of using the heap. Always + use this option when using both the gray engine and --no-syscalls. Options that customize size limits: $Cr--atexit-max$C0=$Cy$Cg [default:$Cp 16$Cg]$C0 @@ -66,9 +70,10 @@ EOF fail=false for arg; do case "$arg" in -h | --help) help;; - --diagnostics) conf[GINT_DIAGNOSTICS]=true;; - --no-syscalls) conf[GINT_NO_SYSCALLS]=true;; - --extended-libc) conf[GINT_EXTENDED_LIBC]=true;; + --startup-log) conf[GINT_STARTUP_LOG]=true;; + --no-syscalls) conf[GINT_NO_SYSCALLS]=true;; + --extended-libc) conf[GINT_EXTENDED_LIBC]=true;; + --static-gray-engine) conf[GINT_STATIC_GRAY]=true;; --atexit-max=*) size=${arg#*=} @@ -103,9 +108,10 @@ esac; done output_config_gcc() { - [ "${conf[GINT_DIAGNOSTICS]}" != "" ] && echo "-D GINT_DIAGNOSTICS" + [ "${conf[GINT_STARTUP_LOG]}" != "" ] && echo "-D GINT_STARTUP_LOG" [ "${conf[GINT_NO_SYSCALLS]}" != "" ] && echo "-D GINT_NO_SYSCALLS" [ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "-D GINT_EXTENDED_LIBC" + [ "${conf[GINT_STATIC_GRAY]}" != "" ] && echo "-D GINT_STATIC_GRAY" echo "-D ATEXIT_MAX=${conf[ATEXIT_MAX]}" echo "-D TIMER_SLOTS=${conf[TIMER_SLOTS]}" diff --git a/debug/addin.ld b/debug/addin.ld deleted file mode 100644 index b712b1d..0000000 --- a/debug/addin.ld +++ /dev/null @@ -1,31 +0,0 @@ -OUTPUT_ARCH(sh3) -ENTRY(initialize) -MEMORY -{ - rom : o = 0x00300200, l = 512k - ram : o = 0x08100000, l = 64k /* pretty safe guess */ -} -SECTIONS -{ - .text : { - *(.pretext) /* init stuff */ - *(.text) - } > rom - .rodata : { - *(.rodata) - *(.rodata.str1.4) - _romdata = . ; /* symbol for initialization data */ - } > rom - .bss : { - _bbss = . ; - _bssdatasize = . ; - LONG(0); /* bssdatasize */ - *(.bss) *(COMMON); - _ebss = . ; - } > ram - .data : AT(_romdata) { - _bdata = . ; - *(.data); - _edata = . ; - } > ram -} diff --git a/debug/crt0.s b/debug/crt0.s deleted file mode 100644 index 93000cf..0000000 --- a/debug/crt0.s +++ /dev/null @@ -1,172 +0,0 @@ - .section .pretext - .global initialize -initialize: - sts.l pr, @-r15 - - ! set up TLB - mov.l Hmem_SetMMU, r3 - mov.l address_one, r4 ! 0x8102000 - mov.l address_two, r5 ! 0x8801E000 - jsr @r3 ! _Hmem_SetMMU - mov #108, r6 - - ! clear the BSS - mov.l bbss, r4 ! start - mov.l ebss, r5 ! end - bra L_check_bss - mov #0, r6 -L_zero_bss: - mov.l r6, @r4 ! zero and advance - add #4, r4 -L_check_bss: - cmp/hs r5, r4 - bf L_zero_bss - - ! Copy the .data - mov.l bdata, r4 ! dest - mov.l edata, r5 ! dest limit - mov.l romdata, r6 ! source - bra L_check_data - nop -L_copy_data: - mov.l @r6+, r3 - mov.l r3, @r4 - add #4, r4 -L_check_data: - cmp/hs r5, r4 - bf L_copy_data - - mov.l bbss, r4 - mov.l edata, r5 - sub r4, r5 ! size of .bss and .data sections - add #4, r5 - mov.l bssdatasize, r4 - mov.l r5, @r4 - - mov.l GLibAddinAplExecutionCheck, r2 - mov #0, r4 - mov #1, r5 - jsr @r2 ! _GLibAddinAplExecutionCheck(0,1,1); - mov r5, r6 - - mov.l CallbackAtQuitMainFunction, r3 - mov.l exit_handler, r4 - jsr @r3 ! _CallbackAtQuitMainFunction(&exit_handler) - nop - mov.l main, r3 - jmp @r3 ! _main() - lds.l @r15+, pr - -_exit_handler: - mov.l r14, @-r15 - mov.l r13, @-r15 - mov.l r12, @-r15 - sts.l pr, @-r15 - - mov.l Bdel_cychdr, r14 - jsr @r14 ! _Bdel_cychdr - mov #6, r4 - jsr @r14 ! _Bdel_cychdr - mov #7, r4 - jsr @r14 ! _Bdel_cychdr - mov #8, r4 - jsr @r14 ! _Bdel_cychdr - mov #9, r4 - jsr @r14 ! _Bdel_cychdr - mov #10, r4 - - mov.l BfileFLS_CloseFile, r12 - mov #4, r14 - mov #0, r13 -L_close_files: - jsr @r12 ! _BfileFLS_CloseFile - mov r13, r4 - add #1, r13 - cmp/ge r14, r13 - bf L_close_files - - mov.l flsFindClose, r12 - mov #0, r13 -L_close_finds: - jsr @r12 ! _flsFindClose - mov r13, r4 - add #1, r13 - cmp/ge r14, r13 - bf L_close_finds - - lds.l @r15+, pr - mov.l @r15+, r12 - mov.l @r15+, r13 - mov.l Bkey_Set_RepeatTime_Default, r2 - jmp @r2 ! _Bkey_Set_RepeatTime_Default - mov.l @r15+, r14 - -.align 4 -address_two: .long 0x8801E000 -address_one: .long 0x8102000 -Hmem_SetMMU: .long _Hmem_SetMMU -GLibAddinAplExecutionCheck: .long _GLibAddinAplExecutionCheck -CallbackAtQuitMainFunction: .long _CallbackAtQuitMainFunction -Bdel_cychdr: .long _Bdel_cychdr -BfileFLS_CloseFile: .long _BfileFLS_CloseFile -flsFindClose: .long _flsFindClose -Bkey_Set_RepeatTime_Default: .long _Bkey_Set_RepeatTime_Default -bbss: .long _bbss -ebss: .long _ebss -edata: .long _edata -bdata: .long _bdata -romdata: .long _romdata -bssdatasize: .long _bssdatasize - -exit_handler: .long _exit_handler -main: .long _main - -_Hmem_SetMMU: - mov.l sc_addr, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0x3FA - -_Bdel_cychdr: - mov.l sc_addr, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0x119 - -_BfileFLS_CloseFile: - mov.l sc_addr, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0x1E7 - -_Bkey_Set_RepeatTime_Default: - mov.l sc_addr, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0x244 - -_CallbackAtQuitMainFunction: - mov.l sc_addr, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0x494 - -_flsFindClose: - mov.l sc_addr, r2 - mov.l 1f, r0 - jmp @r2 - nop -1: .long 0x218 - -_GLibAddinAplExecutionCheck: - mov.l sc_addr, r2 - mov #0x13, r0 - jmp @r2 - nop -sc_addr: .long 0x80010070 -.end diff --git a/debug/gintdbg.c b/debug/gintdbg.c deleted file mode 100644 index 61c70af..0000000 --- a/debug/gintdbg.c +++ /dev/null @@ -1,178 +0,0 @@ -//--- -// -// gintdbg -// -// A simple debugger for gint applications, providing diagnoses to -// determine what went bad. -// -//--- - -// Just for structure definitions, gint does not run here. -#define GINT_DIAGNOSTICS -#include -#include - -#include -#include -#include - -// Some functions from other files... -int vsprintf(char *buffer, const char *format, va_list args); -int sprintf(char *buffer, const char *format, ...); - -//--- -// Some util... -//--- - -static int print_row = 1; - -void print(int col, const char *format, ...) -{ - int row = print_row; - print_row++; - if(row < 1 || row > 8) return; - - char buffer[256]; - va_list args; - va_start(args, format); - vsprintf(buffer, format, args); - va_end(args); - - locate(col, row); - Print((unsigned char *)buffer); -} - -void newline(void) -{ - print_row++; -} - -void nothing_found(void) -{ - unsigned int key; - - Bdisp_AllClr_VRAM(); - PopUpWin(6); - print_row = 2; - print(3, "Apparently there"); - print(3, "is no diagnostic."); - print(3, ""); - print(3, "Show anyway?"); - print(3, " Yes:[F1]"); - print(3, " No :[MENU]"); - - do GetKey(&key); - while(key != KEY_CTRL_F1); -} - -void show_diagnostics(void) -{ - volatile gint_diagnostics_t *dg = gint_diagnostics(); - - const char *stages[] = { - "Startup", "Sections", "MMU", "Gint", "Clock", "Constructors", - "Running", "Leaving", "Destructors", "Terminated", - }; - const char *mpus[] = { - "Unknown", "SH7337", "SH7355", "SH7305", "SH7724", - }; - - print(1, "Gint debugger (%d)", dg->counter); - newline(); - - print(1, "General information"); - print(2, "Magic 0x%02x", dg->magic); - print(2, "Stage %s", dg->stage <= 9 ? stages[dg->stage] : "-"); - if(dg->stage >= stage_gint) - { - print(2, "MPU %s", dg->mpu <= 4 ? mpus[dg->mpu] : "-"); - } - print(2, "Version %08x", dg->version); - newline(); - - print(1, "Memory map"); - print(2, "%08x romdata", dg->romdata); - print(2, "%08x vbr", dg->vbr_address); - print(2, "%08x:%05x text", dg->section_text.address, - dg->section_text.length); - print(2, "%08x:%05x data", dg->section_data.address, - dg->section_data.length); - print(2, "%08x:%05x bss", dg->section_bss.address, - dg->section_bss.length); - - print(2, "%08x:%05x gint", dg->section_gint.address, - dg->section_gint.length); - newline(); - - print(1, "Exception records"); - size_t len = sizeof dg->except_vect; - char line[19]; - for(size_t i = 0; i < len; i += 6) - { - for(size_t n = 0; n < 6 && i + n < len; n++) - { - size_t index = (dg->excepts + i + n) % len; - sprintf(line + 3 * n, " %02x", dg->except_vect[index]); - } - - print(1, "%s", line); - } - print(2, "SPC %08x", dg->spc); - print(2, "SSR %08x", dg->ssr); - print(2, "EXPEVT %08x", dg->expevt); - print(2, "TEA %08x", dg->tea); - newline(); - - if(dg->stage >= stage_clock) - { - print(1, "Clock frequencies"); - print(2, "Bus clock %d MHz", dg->Bphi_f); - print(2, "Peripheral %d MHz", dg->Pphi_f); - print(2, "Processor %d MHz", dg->Iphi_f); - newline(); - } -} - -/* - main() - Let's do this! -*/ -int main(void) -{ - volatile gint_diagnostics_t *dg = gint_diagnostics(); - unsigned int key; - - if(dg->magic != GINT_DIAGNOSTICS_MAGIC - || dg->stage > 9 - || dg->mpu > 4 - ) nothing_found(); - - int total_height = -1; - int y = 0; - - while(1) - { - Bdisp_AllClr_VRAM(); - print_row = 1 - y; - - show_diagnostics(); - if(total_height < 0) total_height = print_row - 1; - - // Drawing a scrollbar. - if(total_height > 8) - { - int base = (64 * y) / total_height; - int height = (64 * 8) / total_height; - Bdisp_DrawLineVRAM(127, base, 127, base + height); - } - Bdisp_PutDisp_DD(); - - do GetKey(&key); - while(key != KEY_CTRL_UP && key != KEY_CTRL_DOWN); - - if(key == KEY_CTRL_UP && y > 0) y--; - else if(key == KEY_CTRL_DOWN && y + 8 < total_height) y++; - } - - return 1; -} diff --git a/debug/icon.bmp b/debug/icon.bmp deleted file mode 100644 index 6240f6142013afb0b62eb2e7a8d0c96b8cdb2702..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1870 zcmb_bu@S;R3^Nok01Xwxq@jX>Iq2w^fC1SjI4@qWIG-mR`0kA?%d+8)?sVQ-io55l zzk|P>zm@Ghn>{yX4=09~qdQ)1887`@2Vvj$f%#wQ-oMApIl-7JjX?CnBofwin2LIJz^Ta(a3l_XCO0G1FlJD> 8)) -#define UtlSwapDword(l) (unsigned long)((((l) & 0x000000ff) << 24) | (((l) & 0x0000ff00) << 8) | (((l) & 0xff000000) >> 24) | (((l) & 0x00ff0000) >> 8)) -#define UtlSwapInteger(i) UtlSwapDword(i) -#define UtlSwapPointer(p) (void*)((((unsigned long)(p) & 0x000000ff) << 24) | (((unsigned long)(p) & 0x0000ff00) << 8) | (((unsigned long)(p) & 0xff000000) >> 24) | (((unsigned long)(p) & 0x00ff0000) >> 8)) - - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/debug/include/filebios.h b/debug/include/filebios.h deleted file mode 100644 index 276d9ce..0000000 --- a/debug/include/filebios.h +++ /dev/null @@ -1,116 +0,0 @@ -/*****************************************************************/ -/* */ -/* CASIO fx-9860G SDK Library */ -/* */ -/* File name : filebios.h */ -/* */ -/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */ -/* */ -/*****************************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __FILEBIOS_H__ -#define __FILEBIOS_H__ - - -// Defines - -#define FONTCHARACTER unsigned short - -#define _OPENMODE_READ 0x01 -#define _OPENMODE_READ_SHARE 0x80 -#define _OPENMODE_WRITE 0x02 -#define _OPENMODE_READWRITE 0x03 -#define _OPENMODE_READWRITE_SHARE 0x83 - -#define _CREATEMODE_BINARY 1 -#define _CREATEMODE_DIRECTORY 5 - -enum DEVICE_TYPE{ - DEVICE_MAIN_MEMORY, - DEVICE_STORAGE, - DEVICE_SD_CARD, // only fx-9860G SD model -}; - - -// File system standard error code -#define IML_FILEERR_NOERROR 0 -#define IML_FILEERR_ENTRYNOTFOUND -1 -#define IML_FILEERR_ILLEGALPARAM -2 -#define IML_FILEERR_ILLEGALPATH -3 -#define IML_FILEERR_DEVICEFULL -4 -#define IML_FILEERR_ILLEGALDEVICE -5 -#define IML_FILEERR_ILLEGALFILESYS -6 -#define IML_FILEERR_ILLEGALSYSTEM -7 -#define IML_FILEERR_ACCESSDENYED -8 -#define IML_FILEERR_ALREADYLOCKED -9 -#define IML_FILEERR_ILLEGALTASKID -10 -#define IML_FILEERR_PERMISSIONERROR -11 -#define IML_FILEERR_ENTRYFULL -12 -#define IML_FILEERR_ALREADYEXISTENTRY -13 -#define IML_FILEERR_READONLYFILE -14 -#define IML_FILEERR_ILLEGALFILTER -15 -#define IML_FILEERR_ENUMRATEEND -16 -#define IML_FILEERR_DEVICECHANGED -17 -//#define IML_FILEERR_NOTRECORDFILE -18 // Not used -#define IML_FILEERR_ILLEGALSEEKPOS -19 -#define IML_FILEERR_ILLEGALBLOCKFILE -20 -//#define IML_FILEERR_DEVICENOTEXIST -21 // Not used -//#define IML_FILEERR_ENDOFFILE -22 // Not used -#define IML_FILEERR_NOTMOUNTDEVICE -23 -#define IML_FILEERR_NOTUNMOUNTDEVICE -24 -#define IML_FILEERR_CANNOTLOCKSYSTEM -25 -#define IML_FILEERR_RECORDNOTFOUND -26 -//#define IML_FILEERR_NOTDUALRECORDFILE -27 // Not used -#define IML_FILEERR_NOTALARMSUPPORT -28 -#define IML_FILEERR_CANNOTADDALARM -29 -#define IML_FILEERR_FILEFINDUSED -30 -#define IML_FILEERR_DEVICEERROR -31 -#define IML_FILEERR_SYSTEMNOTLOCKED -32 -#define IML_FILEERR_DEVICENOTFOUND -33 -#define IML_FILEERR_FILETYPEMISMATCH -34 -#define IML_FILEERR_NOTEMPTY -35 -#define IML_FILEERR_BROKENSYSTEMDATA -36 -#define IML_FILEERR_MEDIANOTREADY -37 -#define IML_FILEERR_TOOMANYALARMITEM -38 -#define IML_FILEERR_SAMEALARMEXIST -39 -#define IML_FILEERR_ACCESSSWAPAREA -40 -#define IML_FILEERR_MULTIMEDIACARD -41 -#define IML_FILEERR_COPYPROTECTION -42 -#define IML_FILEERR_ILLEGALFILEDATA -43 - -// FILE_INFO.type -#define DT_DIRECTORY 0x0000 // Directory -#define DT_FILE 0x0001 // File -#define DT_ADDIN_APP 0x0002 // Add-In application -#define DT_EACT 0x0003 // eActivity -#define DT_LANGUAGE 0x0004 // Language -#define DT_BITMAP 0x0005 // Bitmap -#define DT_MAINMEM 0x0006 // Main Memory data -#define DT_TEMP 0x0007 // Temporary data -#define DT_DOT 0x0008 // . (Current directory) -#define DT_DOTDOT 0x0009 // .. (Parent directory) -#define DT_VOLUME 0x000A // Volume label - - -// Structs - -typedef struct tag_FILE_INFO -{ - unsigned short id; - unsigned short type; - unsigned long fsize; // File size - unsigned long dsize; // Data size - unsigned int property; // The file has not been completed, except when property is 0. - unsigned long address; -} FILE_INFO; - - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/debug/include/fxlib.h b/debug/include/fxlib.h deleted file mode 100644 index dc4534a..0000000 --- a/debug/include/fxlib.h +++ /dev/null @@ -1,100 +0,0 @@ -/*****************************************************************/ -/* */ -/* CASIO fx-9860G SDK Library */ -/* */ -/* File name : fxlib.h */ -/* */ -/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */ -/* */ -/*****************************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __FXLIB_H__ -#define __FXLIB_H__ - -#include "dispbios.h" -#include "filebios.h" -#include "keybios.h" - - -// Prototypes - -void Bdisp_AllClr_DD(void); -void Bdisp_AllClr_VRAM(void); -void Bdisp_AllClr_DDVRAM(void); -void Bdisp_AreaClr_DD(const DISPBOX *pArea); -void Bdisp_AreaClr_VRAM(const DISPBOX *pArea); -void Bdisp_AreaClr_DDVRAM(const DISPBOX *pArea); -void Bdisp_AreaReverseVRAM(int x1, int y1, int x2, int y2); -void Bdisp_GetDisp_DD(unsigned char *pData); -void Bdisp_GetDisp_VRAM(unsigned char *pData); -void Bdisp_PutDisp_DD(void); -void Bdisp_PutDispArea_DD(const DISPBOX *PutDispArea); -void Bdisp_SetPoint_DD(int x, int y, unsigned char point); -void Bdisp_SetPoint_VRAM(int x, int y, unsigned char point); -void Bdisp_SetPoint_DDVRAM(int x, int y, unsigned char point); -int Bdisp_GetPoint_VRAM(int x, int y); -void Bdisp_WriteGraph_DD(const DISPGRAPH *WriteGraph); -void Bdisp_WriteGraph_VRAM(const DISPGRAPH *WriteGraph); -void Bdisp_WriteGraph_DDVRAM(const DISPGRAPH *WriteGraph); -void Bdisp_ReadArea_DD(const DISPBOX *ReadArea, unsigned char *ReadData); -void Bdisp_ReadArea_VRAM(const DISPBOX *ReadArea, unsigned char *ReadData); -void Bdisp_DrawLineVRAM(int x1, int y1, int x2, int y2); -void Bdisp_ClearLineVRAM(int x1, int y1, int x2, int y2); - -void locate(int x, int y); -void Print(const unsigned char *str); -void PrintRev(const unsigned char *str); -void PrintC(const unsigned char *c); -void PrintRevC(const unsigned char *str); -void PrintLine(const unsigned char *str, int max); -void PrintRLine(const unsigned char *str, int max); -void PrintXY(int x, int y, const unsigned char *str, int type); -int PrintMini(int x, int y, const unsigned char *str, int type); -void SaveDisp(unsigned char num); -void RestoreDisp(unsigned char num); -void PopUpWin(int n); - -int Bfile_OpenFile(const FONTCHARACTER *filename, int mode); -int Bfile_OpenMainMemory(const unsigned char *name); -int Bfile_ReadFile(int HANDLE, void *buf, int size, int readpos); -int Bfile_WriteFile(int HANDLE, const void *buf, int size); -int Bfile_SeekFile(int HANDLE, int pos); -int Bfile_CloseFile(int HANDLE); -int Bfile_GetMediaFree(enum DEVICE_TYPE devicetype, int *freebytes); -int Bfile_GetFileSize(int HANDLE); -int Bfile_CreateFile(const FONTCHARACTER *filename, int size); -int Bfile_CreateDirectory(const FONTCHARACTER *pathname); -int Bfile_CreateMainMemory(const unsigned char *name); -int Bfile_RenameMainMemory(const unsigned char *oldname, const unsigned char *newname); -int Bfile_DeleteFile(const FONTCHARACTER *filename); -int Bfile_DeleteDirectory(const FONTCHARACTER *pathname); -int Bfile_DeleteMainMemory(const unsigned char *name); -int Bfile_FindFirst(const FONTCHARACTER *pathname, int *FindHandle, FONTCHARACTER *foundfile, FILE_INFO *fileinfo); -int Bfile_FindNext(int FindHandle, FONTCHARACTER *foundfile, FILE_INFO *fileinfo); -int Bfile_FindClose(int FindHandle); - -void Bkey_Set_RepeatTime(long FirstCount, long NextCount); -void Bkey_Get_RepeatTime(long *FirstCount, long *NextCount); -void Bkey_Set_RepeatTime_Default(void); -int GetKeyWait(int sel, int time, int menu, unsigned int *keycode); -int IsKeyDown(int keycode); -int IsKeyUp(int keycode); -int GetKey(unsigned int *keycode); - -int SetTimer(int ID, int elapse, void (*hander)(void)); -int KillTimer(int ID); -void Sleep(int millisecond); - -void SetQuitHandler(void (*callback)(void)); -int INIT_ADDIN_APPLICATION(int isAppli, unsigned short OptionNum); - - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/debug/include/keybios.h b/debug/include/keybios.h deleted file mode 100644 index 848fa5f..0000000 --- a/debug/include/keybios.h +++ /dev/null @@ -1,158 +0,0 @@ -/*****************************************************************/ -/* */ -/* CASIO fx-9860G SDK Library */ -/* */ -/* File name : keybios.h */ -/* */ -/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */ -/* */ -/*****************************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __KEYBIOS_H__ -#define __KEYBIOS_H__ - - -// Defines - -// Character codes -#define KEY_CHAR_0 0x30 -#define KEY_CHAR_1 0x31 -#define KEY_CHAR_2 0x32 -#define KEY_CHAR_3 0x33 -#define KEY_CHAR_4 0x34 -#define KEY_CHAR_5 0x35 -#define KEY_CHAR_6 0x36 -#define KEY_CHAR_7 0x37 -#define KEY_CHAR_8 0x38 -#define KEY_CHAR_9 0x39 -#define KEY_CHAR_DP 0x2e -#define KEY_CHAR_EXP 0x0f -#define KEY_CHAR_PMINUS 0x87 -#define KEY_CHAR_PLUS 0x89 -#define KEY_CHAR_MINUS 0x99 -#define KEY_CHAR_MULT 0xa9 -#define KEY_CHAR_DIV 0xb9 -#define KEY_CHAR_FRAC 0xbb -#define KEY_CHAR_LPAR 0x28 -#define KEY_CHAR_RPAR 0x29 -#define KEY_CHAR_COMMA 0x2c -#define KEY_CHAR_STORE 0x0e -#define KEY_CHAR_LOG 0x95 -#define KEY_CHAR_LN 0x85 -#define KEY_CHAR_SIN 0x81 -#define KEY_CHAR_COS 0x82 -#define KEY_CHAR_TAN 0x83 -#define KEY_CHAR_SQUARE 0x8b -#define KEY_CHAR_POW 0xa8 -#define KEY_CHAR_IMGNRY 0x7f50 -#define KEY_CHAR_LIST 0x7f51 -#define KEY_CHAR_MAT 0x7f40 -#define KEY_CHAR_EQUAL 0x3d -#define KEY_CHAR_PI 0xd0 -#define KEY_CHAR_ANS 0xc0 -#define KEY_CHAR_LBRCKT 0x5b -#define KEY_CHAR_RBRCKT 0x5d -#define KEY_CHAR_LBRACE 0x7b -#define KEY_CHAR_RBRACE 0x7d -#define KEY_CHAR_CR 0x0d -#define KEY_CHAR_CUBEROOT 0x96 -#define KEY_CHAR_RECIP 0x9b -#define KEY_CHAR_ANGLE 0x7f54 -#define KEY_CHAR_EXPN10 0xb5 -#define KEY_CHAR_EXPN 0xa5 -#define KEY_CHAR_ASIN 0x91 -#define KEY_CHAR_ACOS 0x92 -#define KEY_CHAR_ATAN 0x93 -#define KEY_CHAR_ROOT 0x86 -#define KEY_CHAR_POWROOT 0xb8 -#define KEY_CHAR_SPACE 0x20 -#define KEY_CHAR_DQUATE 0x22 -#define KEY_CHAR_VALR 0xcd -#define KEY_CHAR_THETA 0xce -#define KEY_CHAR_A 0x41 -#define KEY_CHAR_B 0x42 -#define KEY_CHAR_C 0x43 -#define KEY_CHAR_D 0x44 -#define KEY_CHAR_E 0x45 -#define KEY_CHAR_F 0x46 -#define KEY_CHAR_G 0x47 -#define KEY_CHAR_H 0x48 -#define KEY_CHAR_I 0x49 -#define KEY_CHAR_J 0x4a -#define KEY_CHAR_K 0x4b -#define KEY_CHAR_L 0x4c -#define KEY_CHAR_M 0x4d -#define KEY_CHAR_N 0x4e -#define KEY_CHAR_O 0x4f -#define KEY_CHAR_P 0x50 -#define KEY_CHAR_Q 0x51 -#define KEY_CHAR_R 0x52 -#define KEY_CHAR_S 0x53 -#define KEY_CHAR_T 0x54 -#define KEY_CHAR_U 0x55 -#define KEY_CHAR_V 0x56 -#define KEY_CHAR_W 0x57 -#define KEY_CHAR_X 0x58 -#define KEY_CHAR_Y 0x59 -#define KEY_CHAR_Z 0x5a - - -// Control codes -#define KEY_CTRL_NOP 0 -#define KEY_CTRL_EXE 30004 -#define KEY_CTRL_DEL 30025 -#define KEY_CTRL_AC 30015 -#define KEY_CTRL_FD 30046 -#define KEY_CTRL_XTT 30001 -#define KEY_CTRL_EXIT 30002 -#define KEY_CTRL_SHIFT 30006 -#define KEY_CTRL_ALPHA 30007 -#define KEY_CTRL_OPTN 30008 -#define KEY_CTRL_VARS 30016 -#define KEY_CTRL_UP 30018 -#define KEY_CTRL_DOWN 30023 -#define KEY_CTRL_LEFT 30020 -#define KEY_CTRL_RIGHT 30021 -#define KEY_CTRL_F1 30009 -#define KEY_CTRL_F2 30010 -#define KEY_CTRL_F3 30011 -#define KEY_CTRL_F4 30012 -#define KEY_CTRL_F5 30013 -#define KEY_CTRL_F6 30014 -#define KEY_CTRL_CATALOG 30100 -#define KEY_CTRL_CAPTURE 30055 -#define KEY_CTRL_CLIP 30050 -#define KEY_CTRL_PASTE 30036 -#define KEY_CTRL_INS 30033 -#define KEY_CTRL_MIXEDFRAC 30054 -#define KEY_CTRL_FRACCNVRT 30026 -#define KEY_CTRL_QUIT 30029 -#define KEY_CTRL_PRGM 30028 -#define KEY_CTRL_SETUP 30037 -#define KEY_CTRL_PAGEUP 30052 -#define KEY_CTRL_PAGEDOWN 30053 -#define KEY_CTRL_MENU 30003 -#define KEY_CTRL_RESERVE1 30060 -#define KEY_CTRL_RESERVE2 30061 -#define KEY_CTRL_RESERVE3 30062 - - -// in Bkey_GetKeyWait function -#define KEYWAIT_HALTON_TIMEROFF 0 -#define KEYWAIT_HALTOFF_TIMEROFF 1 -#define KEYWAIT_HALTON_TIMERON 2 - -#define KEYREP_NOEVENT 0 -#define KEYREP_KEYEVENT 1 -#define KEYREP_TIMEREVENT 2 - - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/debug/include/timer.h b/debug/include/timer.h deleted file mode 100644 index 286f665..0000000 --- a/debug/include/timer.h +++ /dev/null @@ -1,31 +0,0 @@ -/*****************************************************************/ -/* */ -/* CASIO fx-9860G SDK Library */ -/* */ -/* File name : timer.h */ -/* */ -/* Copyright (c) 2006 CASIO COMPUTER CO., LTD. */ -/* */ -/*****************************************************************/ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef __TIMER_H__ -#define __TIMER_H__ - - -// Defines - -#define ID_USER_TIMER1 1 -#define ID_USER_TIMER2 2 -#define ID_USER_TIMER3 3 -#define ID_USER_TIMER4 4 -#define ID_USER_TIMER5 5 - -#endif - -#ifdef __cplusplus -} -#endif diff --git a/debug/libfx.a b/debug/libfx.a deleted file mode 100644 index 5ef92aaf7eaf3afca041f8f64511d0caf86c42e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 410910 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 z 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