diff --git a/.gitignore b/.gitignore index 266d49b..41fea35 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,6 @@ LIBC libc.a libgint.a gintdemo.g1a + +# Configuration files +gcc.cfg diff --git a/Makefile b/Makefile old mode 100644 new mode 100755 index 54a2101..8d65b80 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ # Modules modules-gint = core clock keyboard mmu mpu rtc screen timer \ - bopti display gray tales + bopti display gray tales events modules-libc = setjmp string stdio stdlib time # Targets @@ -22,15 +22,15 @@ target-lib = libgint.a target-std = libc.a # Tools -cc = sh3eb-elf-gcc -as = sh3eb-elf-as -ar = sh3eb-elf-ar -ob = sh3eb-elf-objcopy -wr = g1a-wrapper +cc = sh3eb-elf-gcc +as = sh3eb-elf-as +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 + -W -Wall -Wextra -pedantic @gcc.cfg # Demo application (could be done better) demo-src = $(notdir $(wildcard demo/*.[cs])) @@ -47,7 +47,8 @@ demo-libs = -L. -lgint -lc -lgcc obj-lib-spec = build/display_font_system.bmp.o obj-std-spec = - +# Configuration files +config = gcc.cfg #--- # Automatic variables. @@ -61,6 +62,10 @@ define n endef +ifeq ("$(wildcard $(config))","") +$(error "Configuration files are missing. Did you ./configure?") +endif + # Module-scope variables. $(foreach mod, $(modules), $(eval \ mod-$(mod)-c = $(notdir $(wildcard src/$(mod)/*.c)) $n\ @@ -82,21 +87,28 @@ 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 # $3 dependencies define rule-c-source -build/$1_$2.o: src/$1/$2 $3 - $(cc) -c $$< -o $$@ $(cflags) -I src/$1 +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 endef # asm source file template: # $1 module name # $2 filename define rule-asm-source -build/$1_$2.o: src/$1/$2 - $(as) -c $$< -o $$@ +build/$1_$2.o: src/$1/$2 $(config) + $(if $(VERBOSE),,@ printf '\e[34;1msrc \u00bb\e[0m as $$<\n') + $(if $(VERBOSE),,@) $(as) -c $$< -o $$@ endef @@ -107,25 +119,36 @@ endef # Generic rules -all: build $(target-std) $(target-lib) $(target-g1a) - @echo "[ \033[32;1mOK\033[0m ] All done!" +all: $(config) build $(target-std) $(target-lib) $(target-g1a) + @ printf '\e[32;1mmsg \u00bb\e[0m All done!\n' build: - mkdir -p $@ + @ mkdir -p $@ -$(target-std): $(obj-std) - $(ar) rcs $@ $^ - @echo "[ \033[32;1mOK\033[0m ] libc: `stat -c %s $@` bytes" +$(target-std): $(config) $(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 (' + @ printf $$(stat -c %s $@) + @ printf ' bytes)\n\n' -$(target-lib): $(target-std) $(obj-lib) - $(ar) rcs $@ $(obj-lib) - @echo "[ \033[32;1mOK\033[0m ] libgint: `stat -c %s $@` bytes" +$(target-lib): $(config) $(target-std) $(obj-lib) + $(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 (' + @ printf $$(stat -c %s $@) + @ printf ' bytes)\n\n' -$(target-g1a): $(target-std) $(target-lib) $(demo-obj) - $(cc) -o $(demo-elf) $(cflags) -T $(demo-ld) $(demo-obj) $(demo-libs) - $(ob) -R .comment -R .bss -O binary $(demo-elf) $(demo-bin) - $(wr) $(demo-bin) -o $@ -i $(demo-icon) - @echo "[ \033[32;1mOK\033[0m ] demo app: `stat -c %s $@` bytes" +$(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),,@ 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') + $(if $(VERBOSE),,@) $(wr) $(demo-bin) -o $@ -i $(demo-icon) + @ printf '\e[32;1mmsg \u00bb\e[0m Succesfully built demo application (' + @ printf $$(stat -c %s $@) + @ printf ' bytes)\n\n' # Automated rules @@ -139,23 +162,28 @@ $(foreach mod,$(modules), \ # Specific rules # This one should not be optimized. It makes __attribute__((interrupt_handler)) -# buggy... maybe. Anyway there's a bug in this file. -build/core_gint.c.o: src/core/gint.c $(mod-core-dep) - $(cc) -c $< -o $@ $(cflags) -I src/core -O0 +# 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 build/display_font_system.bmp.o: src/display/font_system.bmp - fxconv $< -o $@ --font -n gint_font_system + $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') + $(if $(VERBOSE),,@) fxconv $< -o $@ --font -n gint_font_system # Demo application -build/demo_%.c.o: demo/%.c $(hdr-dep) $(demo-dep) - $(cc) -c $< -o $@ $(cflags) +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) build/demo_font_%.bmp.o: demo/resources/font_%.bmp - fxconv $< -o $@ --font -n $(patsubst demo/resources/%.bmp,res_%,$<) + $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') + $(if $(VERBOSE),,@) fxconv $< -o $@ --font -n $(patsubst demo/resources/%.bmp,res_%,$<) build/demo_%.bmp.o: demo/resources/%.bmp - fxconv $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) + $(if $(VERBOSE),,@ printf '\e[36;1mres \u00bb\e[0m fxconv $<\n') + $(if $(VERBOSE),,@) fxconv $< -o $@ -n $(patsubst demo/resources/%.bmp,res_%,$<) @@ -169,6 +197,7 @@ clean: mrproper: clean @ rm -f $(target-g1a) $(target-lib) $(target-std) @ rm -rf build + @ rm -f $(config) distclean: mrproper diff --git a/TODO b/TODO index 2e57e3f..70a42f7 100644 --- a/TODO +++ b/TODO @@ -25,3 +25,9 @@ Things to investigate: - 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) diff --git a/configure b/configure new file mode 100755 index 0000000..4f70807 --- /dev/null +++ b/configure @@ -0,0 +1,78 @@ +#! /bin/bash + +declare -A conf +conf[ATEXIT_MAX]=16 +conf[RTC_CB_ARRAY_SIZE]=5 +conf[EVENTS_QUEUE_SIZE]=64 +conf[GINT_NO_SYSCALLS]= +fail=false +output="gcc.cfg" + +help() +{ + cat << EOF +Configuration script for the gint library. + +Options that affect the behavior of the library: + --no-syscalls [default: false] + Never use syscalls. Expect some trouble with the malloc() function... + Do not trigger this option unless you know what you are doing. + +Options that customize size limits: + --atexit-max= [default: 16] + Number of exit handlers that can be registered by atexit(). + --rtc-callbacks= [default: 5] + Number of RTC callbacks that can be registered. + --events-queue-size= [default: 64] + Number of events simultaneously stored in the event queue. +EOF + + exit 0 +} + +for arg; do case "$arg" in + -h | --help) help;; + --no-syscalls) conf[GINT_NO_SYSCALLS]=true;; + + --atexit-max=*) + size=${arg#*=} + if [[ $size == +([0-9]) ]]; then + conf[ATEXIT_MAX]=$size + else echo "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" + 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" + fail=true; fi;; + + --atexit-max | --rtc-callbacks | --events-queue-size) + echo "error: syntax for $arg is $arg=";; + + *) + echo "error: unrecognized argument '$arg'"; fail=true;; +esac; done + +output_config() +{ + 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 $fail; then + echo "Configuration has not been modified." +else + output_config > $output + echo "Configuration details have been output to file $output." +fi diff --git a/include/events.h b/include/events.h new file mode 100644 index 0000000..01e5277 --- /dev/null +++ b/include/events.h @@ -0,0 +1,84 @@ +//--- +// +// gint core module: events +// +// Finally some user-friendly API. +// +//--- + +#ifndef _EVENTS_H +#define _EVENTS_H + +//--- +// Type definitions. +//--- + +/* + enum EventType + Something user programs will surely use most often. +*/ +enum EventType +{ + EventType_None = 0, + ET_None = EventType_None, + + EventType_User = 1, + ET_User = EventType_User, + + EventType_KeyPressed = 2, + ET_KeyPress = EventType_KeyPressed, + + EventType_KeyReleased = 3, + ET_KeyRel = EventType_KeyReleased, +}; + +/* + struct Event + Wake up, something's going on. The union member that holds information + about the event is implicitly defined by the type attribute. +*/ +struct Event +{ + enum EventType type; + + union + { + // For ET_User. + void *data; + // For ET_KeyPress and ET_KeyRel. + int key; + }; +}; + + + +//--- +// 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(struct Event event); + +/* + getevent() + 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); + +/* + 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); + +#endif // _EVENTS_H diff --git a/include/gint.h b/include/gint.h index 098f9da..c49cb5f 100644 --- a/include/gint.h +++ b/include/gint.h @@ -30,6 +30,18 @@ unsigned int gint_getVBR(void); */ 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)); + //--- @@ -85,6 +97,12 @@ const char *gint_strerror(int is_tlb); */ 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 diff --git a/include/internals/events.h b/include/internals/events.h new file mode 100644 index 0000000..976e352 --- /dev/null +++ b/include/internals/events.h @@ -0,0 +1,19 @@ +#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 struct Event event_queue[]; +extern volatile int queue_start; +extern volatile int queue_size; + +#endif // _INTERNALS_EVENT_H diff --git a/include/internals/keyboard.h b/include/internals/keyboard.h index 75f67fc..f982fcf 100644 --- a/include/internals/keyboard.h +++ b/include/internals/keyboard.h @@ -1,5 +1,5 @@ #ifndef _INTERNALS_KEYBOARD_H -#define _INTERNALS_KEYBOARD_H 1 +#define _INTERNALS_KEYBOARD_H #include diff --git a/include/internals/rtc.h b/include/internals/rtc.h index 43ccc94..228ce59 100644 --- a/include/internals/rtc.h +++ b/include/internals/rtc.h @@ -1,10 +1,12 @@ #ifndef _INTERNALS_RTC_H -#define _INTERNALS_RTC_H 1 +#define _INTERNALS_RTC_H #include #include +#ifndef RTC_CB_ARRAY_SIZE #define RTC_CB_ARRAY_SIZE 5 +#endif /* struct rtc_cb diff --git a/include/internals/tales.h b/include/internals/tales.h index 7f5dd26..40342c1 100644 --- a/include/internals/tales.h +++ b/include/internals/tales.h @@ -1,12 +1,3 @@ -//--- -// -// gint drawing module: tales -// -// Text displaying. Does some good optimization, though requires dynamic -// allocation. -// -//--- - #ifndef _INTERNALS_TALES_H #define _INTERNALS_TALES_H 1 diff --git a/include/keyboard.h b/include/keyboard.h index ad1086a..278d0d9 100644 --- a/include/keyboard.h +++ b/include/keyboard.h @@ -147,13 +147,10 @@ enum GetkeyOpt { Getkey_NoOption = 0x00, - // Return KEY_NONE when a key is released. - Getkey_ReleaseEvent = 0x01, - // Consider [SHIFT] and [ALPHA] as modifiers instead of returning // KEY_SHIFT and KEY_ALPHA. - Getkey_ShiftModifier = 0x02, - Getkey_AlphaModifier = 0x04, + Getkey_ShiftModifier = 0x01, + Getkey_AlphaModifier = 0x02, // Key repetition. Notice that modifiers will never be repeated. Getkey_RepeatArrowKeys = 0x10, @@ -219,7 +216,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 that are actually unpressed, are pressed. + that some keys, which are actually unpressed, 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 @@ -257,7 +254,7 @@ int keyid(int key); /* keychar() - Returns the ASCII character associated with a character key ; 0 for + Returns the ASCII character associated with a character key; 0 for other keys. */ int keychar(int key); diff --git a/include/stdlib.h b/include/stdlib.h index 1c2c520..5904525 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -22,7 +22,9 @@ #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 diff --git a/src/clock/clock.c b/src/clock/clock.c index f6a54f2..bc16418 100644 --- a/src/clock/clock.c +++ b/src/clock/clock.c @@ -189,9 +189,9 @@ static void clock_compute_7305(void) volatile unsigned int *FLLFRQ = (void *)0xa4150050; // Surely the documentation of SH7724 does not meet the specification - // if SH7305 for the PLL setting, because the register accepts other - // values than the ones specified by SH7724. The formula given by - // Sentaro21 (thanks again!) yields good results though. + // 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; diff --git a/src/core/crt0.c b/src/core/crt0.c index 15da267..d22de28 100644 --- a/src/core/crt0.c +++ b/src/core/crt0.c @@ -6,9 +6,7 @@ #include #include -void __Hmem_SetMMU(unsigned int, unsigned int, int); -void __GLibAddinAplExecutionCheck(int, int, int); -int main(void); +int main(void); static void init(void); static void fini(void); @@ -54,8 +52,6 @@ int start(void) // Copying the .data section. while(data < &edata) *data++ = *src++; - __GLibAddinAplExecutionCheck(0, 1, 1); - mmu_pseudoTLBInit(); // Initializing gint. diff --git a/src/core/gint.c b/src/core/gint.c index 4dcf136..28de9f2 100644 --- a/src/core/gint.c +++ b/src/core/gint.c @@ -16,6 +16,8 @@ static unsigned int new_vbr, sys_vbr; +static void (*default_handler)(int event_code) = NULL; + //--- @@ -64,6 +66,21 @@ 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 @@ -220,6 +237,21 @@ void gint_int(void) gint_int_7305(); } + + +//--- +// Internal API. +//--- + +/* + 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 diff --git a/src/core/syscalls.s b/src/core/syscalls.s index 55aa0ba..e4dd817 100644 --- a/src/core/syscalls.s +++ b/src/core/syscalls.s @@ -3,35 +3,31 @@ 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 ___GLibAddinAplExecutionCheck - .global _malloc - .global _free + .global ___malloc + .global ___free + .global ___realloc -___GLibAddinAplExecutionCheck: - mov.l syscall_table, r2 - mov #0x13, r0 - jmp @r2 - nop - -_malloc: +___malloc: mov.l syscall_table, r2 mov.l 1f, r0 jmp @r2 nop 1: .long 0xacd -_free: +___free: mov.l syscall_table, r2 mov.l 1f, r0 jmp @r2 nop 1: .long 0xacc -_realloc: +___realloc: mov.l syscall_table, r2 mov.l 1f, r0 jmp @r2 diff --git a/src/events/event_get.c b/src/events/event_get.c new file mode 100644 index 0000000..eceb1ff --- /dev/null +++ b/src/events/event_get.c @@ -0,0 +1,38 @@ +#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. +*/ +struct Event pollevent(void) +{ + struct Event event = { + .type = ET_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; +} + +/* + getevent() + 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) +{ + struct Event event; + + while((event = pollevent()).type == ET_None) sleep(); + return event; +} diff --git a/src/events/event_push.c b/src/events/event_push.c new file mode 100644 index 0000000..3c47d09 --- /dev/null +++ b/src/events/event_push.c @@ -0,0 +1,26 @@ +#include +#include + +volatile struct Event 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(struct Event event) +{ + if(queue_size >= EVENTS_QUEUE_SIZE) return 1; + if(event.type == ET_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/keyboard/getkey.c b/src/keyboard/getkey.c index 2cfc156..a1d2525 100644 --- a/src/keyboard/getkey.c +++ b/src/keyboard/getkey.c @@ -1,5 +1,6 @@ #include #include +#include /* getkey() @@ -18,96 +19,113 @@ int getkey(void) ); } - - /* getkey_opt() - Enhances getkey() with most general functionalities. + 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. */ -int getkey_opt(enum GetkeyOpt options, int max_cycles) +void getkey_opt_wait(int *cycles) { - int key; - enum KeyType type; - int modifier = 0, last_modifier = KEY_NONE; - int r; + while(!interrupt_flag) sleep(); + interrupt_flag = 0; - if(!max_cycles) max_cycles = -1; + if(*cycles > 0) (*cycles)--; +} +int getkey_opt(enum GetkeyOpt options, int cycles) +{ + struct Event event; + int modifier = 0; + static int event_ref = 0; - while(max_cycles != 0) + if(cycles <= 0) cycles = -1; + while(cycles != 0) { - while(!interrupt_flag) sleep(); - interrupt_flag = 0; - if(max_cycles > 0) max_cycles--; - - // Getting key and adding modifiers. - key = getPressedKey(keyboard_state); - - // Handling "no_key" event; - if(key == KEY_NONE) + event = pollevent(); + switch(event.type) { - // Condition for returning. - r = (last_key != KEY_NONE && - options & Getkey_ReleaseEvent); + case ET_None: + if(last_key == KEY_NONE) + { + getkey_opt_wait(&cycles); + continue; + } - last_key = KEY_NONE; - last_modifier = KEY_NONE; - last_repeats = 0; - last_events = 0; + // Handling repetitions. + enum KeyType type = keytype(last_key); + if(!(options & (type << 4))) break; - if(r) return KEY_NONE; - } + if(event_ref <= 0) + { + getkey_opt_wait(&cycles); + event_ref++; + continue; + } - // Handling "new key" events. - else if(key != last_key) - { + 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_ShiftModifier && key == KEY_SHIFT) { - if(last_modifier != KEY_SHIFT) - modifier ^= MOD_SHIFT; - last_modifier = KEY_SHIFT; - + modifier ^= MOD_SHIFT; continue; } if(options & Getkey_AlphaModifier && key == KEY_ALPHA) { - if(last_modifier != KEY_ALPHA) - modifier ^= MOD_ALPHA; - last_modifier = KEY_ALPHA; - + modifier ^= MOD_ALPHA; continue; } - last_key = key; - last_repeats = 0; - last_events = 0; - + 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; } + } + + last_key = KEY_NONE; + last_repeats = 0; + last_events = 0; + 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. - else - { - type = keytype(key); - // Checking whether this key type is repeated. - if(options & (type << 4)) - { - last_events++; - r = last_repeats ? repeat_next : repeat_first; - - if(last_events >= r) - { - last_repeats++; - last_events = 0; - - return key; - } - } - } } // 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 c5d103b..e98dc51 100644 --- a/src/keyboard/keyboard_interrupt.c +++ b/src/keyboard/keyboard_interrupt.c @@ -1,9 +1,9 @@ +#include #include #include #include #include - -#include +#include //--- // Keyboard variables. @@ -26,6 +26,24 @@ unsigned cb_id; // Interrupt management. //--- +static void push_press(int keycode) +{ + struct Event event = { + .type = ET_KeyPress, + .key = keycode, + }; + event_push(event); +} + +static void push_release(int keycode) +{ + struct Event event = { + .type = ET_KeyRel, + .key = keycode, + }; + event_push(event); +} + /* keyboard_interrupt() Callback for keyboard update. Allows keyboard analysis functions to @@ -33,10 +51,42 @@ unsigned cb_id; */ void keyboard_interrupt(void) { - if(isSH3()) - keyboard_updateState_7705(keyboard_state); - else - keyboard_updateState_7305(keyboard_state); + unsigned char 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]) + { + unsigned char pressed = ~keyboard_state[0] & state[0]; + unsigned char 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++) + { + unsigned char pressed = ~keyboard_state[row] & state[row]; + unsigned char released = keyboard_state[row] & ~state[row]; + keyboard_state[row] = state[row]; + + // Fasten this a bit. + if(!pressed && !released) continue; + + for(int column = 0; column < 8; column++) + { + if(pressed & 1) push_press ((column << 4) | row); + if(released & 1) push_release((column << 4) | row); + + pressed >>= 1; + released >>= 1; + } + } interrupt_flag = 1; } diff --git a/src/keyboard/keyboard_sh7305.c b/src/keyboard/keyboard_sh7305.c index 09ef109..ac740b6 100644 --- a/src/keyboard/keyboard_sh7305.c +++ b/src/keyboard/keyboard_sh7305.c @@ -138,12 +138,5 @@ static int krow(int row) */ void keyboard_updateState_7305(volatile unsigned char *keyboard_state) { - int i; - - keyboard_state[8] = krow(8); - for(i = 0; i < 10; i++) - { - if(i != 7 && i != 8) keyboard_state[i] = krow(i); - } - keyboard_state[7] = krow(7); + 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 7f0c75c..1ab1800 100644 --- a/src/keyboard/keyboard_sh7705.c +++ b/src/keyboard/keyboard_sh7705.c @@ -131,7 +131,5 @@ static int krow(int row) */ void keyboard_updateState_7705(volatile unsigned char *keyboard_state) { - int i; - - for(i = 0; i < 10; i++) keyboard_state[i] = krow(i); + for(int i = 0; i < 10; i++) keyboard_state[i] = krow(i); } diff --git a/src/mpu/gint_sh7305.c b/src/mpu/gint_sh7305.c index 963fb32..7327a38 100644 --- a/src/mpu/gint_sh7305.c +++ b/src/mpu/gint_sh7305.c @@ -55,6 +55,9 @@ void gint_int_7305(void) case IC_TMU0_TUNI2: timer_interrupt(TIMER_TMU2); break; + + default: + gint_callDefaultHandler(code); } } diff --git a/src/mpu/gint_sh7705.c b/src/mpu/gint_sh7705.c index a11f5fa..a1c19aa 100644 --- a/src/mpu/gint_sh7705.c +++ b/src/mpu/gint_sh7705.c @@ -55,6 +55,9 @@ void gint_int_7705(void) case IC_TMU2_TUNI2: timer_interrupt(TIMER_TMU2); break; + + default: + gint_callDefaultHandler(code); } } diff --git a/src/stdlib/free.c b/src/stdlib/free.c new file mode 100644 index 0000000..b51d876 --- /dev/null +++ b/src/stdlib/free.c @@ -0,0 +1,20 @@ +#include + +#pragma GCC diagnostic ignored "-Wunused-parameter" + +#ifndef GINT_NO_SYSCALLS +void __free(void *ptr); +#endif + +/* + free() + Frees a memory block allocated by malloc(), calloc() or realloc(). +*/ +void free(void *ptr) +{ + #ifndef GINT_NO_SYSCALLS + __free(ptr); + #endif +} + +#pragma GCC diagnostic pop diff --git a/src/stdlib/malloc.c b/src/stdlib/malloc.c new file mode 100644 index 0000000..92366bc --- /dev/null +++ b/src/stdlib/malloc.c @@ -0,0 +1,27 @@ +#include + +/* + malloc() + Allocates 'size' bytes and returns a pointer to a free memory area. + Returns NULL on error. +*/ + +#ifndef GINT_NO_SYSCALLS + +void *__malloc(size_t size); +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 new file mode 100644 index 0000000..b98530e --- /dev/null +++ b/src/stdlib/realloc.c @@ -0,0 +1,25 @@ +#include + +/* + realloc() + Reallocates a memory block and moves its data. +*/ + +#ifndef GINT_NO_SYSCALLS + +void *__realloc(void *ptr, size_t size); +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