From 0a7f22d4c1f8d34f2cc01193d629cbdb785526f6 Mon Sep 17 00:00:00 2001 From: lephe Date: Thu, 5 May 2016 11:49:05 +0200 Subject: [PATCH] Initial commit. Includes interrupt handler, drivers for keyboard and screen, and parts of the drawing library. --- .gitignore | 13 + Makefile | 119 ++++ TODO | 7 + addin.c | 311 ++++++++++ addin.g1a | Bin 0 -> 12280 bytes addin.ld | 104 ++++ icon.bmp | Bin 0 -> 1870 bytes include/7305.h | 914 ++++++++++++++++++++++++++++ include/7705.h | 1296 ++++++++++++++++++++++++++++++++++++++++ include/display.h | 181 ++++++ include/gint.h | 97 +++ include/gray.h | 57 ++ include/keyboard.h | 254 ++++++++ include/mpu.h | 52 ++ include/screen.h | 12 + include/setjmp.h | 31 + include/stdlib.h | 31 + include/string.h | 32 + include/timer.h | 73 +++ info | 16 + libc.a | Bin 0 -> 1770 bytes libgint.a | Bin 0 -> 22334 bytes sprites.bmp | Bin 0 -> 8850 bytes src/crt0.c | 145 +++++ src/display.c | 715 ++++++++++++++++++++++ src/gint.c | 107 ++++ src/gint_7305.c | 292 +++++++++ src/gint_7705.c | 244 ++++++++ src/gint_vbr.s | 71 +++ src/gray.c | 76 +++ src/keyboard.c | 444 ++++++++++++++ src/mpu.c | 75 +++ src/screen.c | 51 ++ src/setjmp.s | 82 +++ src/string.c | 134 +++++ src/syscalls.s | 37 ++ src/timer.c | 162 +++++ swords.bmp | Bin 0 -> 5770 bytes symbol.bmp | Bin 0 -> 554 bytes symbol2.bmp | Bin 0 -> 242 bytes syscall_0x24a.txt | 190 ++++++ syscall_0x24a_7705.txt | 558 +++++++++++++++++ syscall_0x24b.txt | 91 +++ 43 files changed, 7074 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 TODO create mode 100644 addin.c create mode 100644 addin.g1a create mode 100644 addin.ld create mode 100644 icon.bmp create mode 100644 include/7305.h create mode 100644 include/7705.h create mode 100644 include/display.h create mode 100644 include/gint.h create mode 100644 include/gray.h create mode 100644 include/keyboard.h create mode 100644 include/mpu.h create mode 100644 include/screen.h create mode 100644 include/setjmp.h create mode 100644 include/stdlib.h create mode 100644 include/string.h create mode 100644 include/timer.h create mode 100644 info create mode 100644 libc.a create mode 100644 libgint.a create mode 100644 sprites.bmp create mode 100644 src/crt0.c create mode 100644 src/display.c create mode 100644 src/gint.c create mode 100644 src/gint_7305.c create mode 100644 src/gint_7705.c create mode 100644 src/gint_vbr.s create mode 100644 src/gray.c create mode 100644 src/keyboard.c create mode 100644 src/mpu.c create mode 100644 src/screen.c create mode 100644 src/setjmp.s create mode 100644 src/string.c create mode 100644 src/syscalls.s create mode 100644 src/timer.c create mode 100644 swords.bmp create mode 100644 symbol.bmp create mode 100644 symbol2.bmp create mode 100644 syscall_0x24a.txt create mode 100644 syscall_0x24a_7705.txt create mode 100644 syscall_0x24b.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..767efe3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Sources for old gint versions. +gint.old.1/** +gint.old.2/** + +# Build directory +build/** + +# Sublime Text files +*.sublime-project +*.sublime-workspace + +# Object files. +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0a75411 --- /dev/null +++ b/Makefile @@ -0,0 +1,119 @@ +# +# fx-9860g lib Makefile. +# + +.PHONY: all clean fclean re install + + + +# +# Variables and configuration. +# + +# Tools +cc = sh3eb-elf-gcc +as = sh3eb-elf-as +ar = sh3eb-elf-ar +ob = sh3eb-elf-objcopy +wr = g1a-wrapper + +# Output files +g1a = addin.g1a +bin = build/addin.bin +elf = build/addin.elf + +# Command-line options +cflags = -m3 -mb -nostdlib -ffreestanding \ + -W -Wall -pedantic -std=c11 \ + -I . -isystem include +lib = -lgcc -L. -lgint -lc + + + +# +# Source and object files. +# + +# Gint library. +src-lib = crt0.c syscalls.s \ + gint.c gint_vbr.s gint_7705.c gint_7305.c \ + mpu.c keyboard.c screen.c display.c gray.c timer.c +hea-lib = 7305.h 7705.h gint.h \ + stdlib.h \ + mpu.h keyboard.h screen.h display.h gray.h timer.h +obj-lib = $(addprefix build/, $(addsuffix .o, $(src-lib))) +hdr-lib = $(addprefix include/, $(hea-lib)) + +# Standard library. +src-std = setjmp.s string.c +hea-std = setjmp.h string.h +obj-std = $(addprefix build/, $(addsuffix .o, $(src-std))) +hdr-std = $(addprefix include/, $(hea-std)) + +# Test application. +src-app = addin.c +res-app = icon.o swords.o sprites.o symbol.o symbol2.o + + +# +# Building rules. +# + +all: build libgint.a libc.a addin.g1a + +build: + mkdir -p build + +libgint.a: $(obj-lib) + $(ar) rcs libgint.a $(obj-lib) + @ echo "\033[32;1mLibrary file size: "`stat -c %s libgint.a` \ + "bytes\033[0m" + +libc.a: $(obj-std) + $(ar) rcs libc.a $(obj-std) + @ echo "\033[32;1mStandard file size: "`stat -c %s libc.a` \ + "bytes\033[0m" + +addin.g1a: libgint.a $(src-app) $(res-app) + $(cc) $(src-app) $(res-app) -T addin.ld -o $(elf) $(cflags) $(lib) + $(ob) -R .comment -R .bss -O binary $(elf) $(bin) + $(wr) $(bin) -o addin.g1a -i icon.bmp + @ echo "\033[32;1mBinary file size: "`stat -c %s $(bin)`" bytes\033[0m" +# @ sh3eb-elf-objdump -h build/addin.elf + +build/%.c.o: src/%.c $(hdr-lib) $(hdr-std) + $(cc) $(cflags) -O2 -c $< -o $@ +build/%.s.o: src/%.s + $(as) -c $^ -o $@ + +build/gint.c.o: src/gint.c $(hdr-lib) $(hdr-std) + $(cc) $(cflags) -c $< -o $@ + +%.c.o: %.c $(hdr-lib) $(hdr-std) + $(cc) $(cflags) -c $< -o $@ +%.s.o: %.s + $(as) -c $^ -o $@ +%.o: %.bmp + fxconv $^ + + + +# +# Cleaning rules. +# + +clean: + @ rm -f $(obj-lib) $(obj-std) $(obj-app) $(bin) $(elf) +fclean: clean + @ rm -f build/* + @ rm -f addin.g1a libc.a libgint.a +re: fclean all + + + +# +# Installing shorthand. +# + +install: + usb-connector SEND addin.g1a addin.g1a fls0 diff --git a/TODO b/TODO new file mode 100644 index 0000000..eb9bb0a --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ +- screen, display +- timer +- gray engine +- full rtc driver (time) + +_ 7305.h +_ libc diff --git a/addin.c b/addin.c new file mode 100644 index 0000000..5133959 --- /dev/null +++ b/addin.c @@ -0,0 +1,311 @@ +#include +#include +#include +#include +#include + +extern void __Print(const char *msg, int x, int y); +extern unsigned int gint_vbr, bgint, egint, gint_data; +#define print __Print + +void print_hex(unsigned int n, int x, int y) +{ + char ch[11] = "0x"; + int i; + + for(i = 0; i < 8; i++) + { + ch[9 - i] = (n & 0xf) + '0' + 39 * ((n & 0xf) > 9); + n >>= 4; + } + ch[10] = 0; + print(ch, x, y); +} +void print_bin(unsigned char n, int x, int y) +{ + char ch[9]; + int i; + + for(i = 0; i < 8;i ++) + { + ch[7 - i] = (n & 1) + '0'; + n >>= 1; + } + ch[8] = 0; + print(ch, x, y); +} + +void print_hexa(unsigned int n, int digits, int x, int y) +{ + char ch[20]; + int i; + + for(i = digits - 1; i >= 0; i--) + { + ch[i] = (n & 0xf) + '0' + 39 * ((n & 0xf) > 9); + n >>= 4; + } + + ch[digits] = 0; + print(ch, x, y); +} + +// Don't forget to enable key state debugging in the interrupt handler. +void keyboard_test(void) +{ + const char *names[] = { + "MPU_Unkown", + "MPU_SH7337", + "MPU_SH7355", + "MPU_SH7305", + "MPU_SH7724", + "Error" + }; + int x = 0; + char str[3]; + int keys[4] = { 0 }; + int i; + + print(names[MPU_CURRENT < 5 ? MPU_CURRENT : 5], 0, 0); + + print("gint size:", 0, 1); + print_hex(&egint - &bgint, 11, 1); + + while(1) + { + multigetkey(keys, 4, 0); + + if(keys[0] == KEY_EXIT && keys[1] == KEY_NONE) break; + + #define hexa(h) ('0' + (h) + 39 * ((h) > 9)) + + x = (x + 1) & 15; + str[0] = hexa(x); + str[1] = 0; + print(str, 20, 0); + + for(i = 0; i < 4; i++) + { + str[0] = hexa((keys[i] >> 4) & 0x0f); + str[1] = hexa(keys[i] & 0x0f); + str[2] = 0; + print(str, 19, i + 3); + } + + #undef hexa + } +} + +/* +const unsigned char data[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, 1, 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, +1, 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 unsigned char icon[76] = { +0, 0, 0, 0, 51, 156, 10, 68, 74, 82, 11, 68, 74, 82, 234, 196, 122, 82, 10, +68, 75, 156, 10, 68, 0, 0, 0, 4, 0, 254, 0, 4, 0, 130, 124, 4, 0, 130, 68, 4, +0, 2, 4, 4, 3, 238, 196, 4, 2, 170, 93, 252, 0, 254, 65, 252, 7, 40, 65, 252, +5, 232, 65, 252, 7, 15, 193, 252, 0, 0, 1, 252, 127, 255, 255, 252 }; + +char *ML_vram_adress(void) +{ + return display_getVRAM(); +} + +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 = 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< +#include <7305.h> + +/* +unsigned int exec(void (*f)(void)) +{ + int t, s, dt, ds; + + t = (int)RTC.R64CNT; + s = 10 * (RTC.RSECCNT.TENS) + RTC.RSECCNT.ONES; + + (*f)(); + + dt = (int)RTC.R64CNT - t; + ds = (10 * (RTC.RSECCNT.TENS) + RTC.RSECCNT.ONES) - s; + if(dt < 0) ds--, dt += 64; + + return (ds << 8) | dt; +} + +void btest_ml_icon(void) +{ + int i; + for(i = 0; i < 5000; i++) + { + ML_bmp_or_cl(icon, 0, 30, 30, 19); + } +} +void btest_gint_icon(void) +{ + extern Image binary_icon_start; + + int i; + for(i = 0; i < 5000; i++) + { + dimage(&binary_icon_start, 0, 0, Blend_Or); + } +} +*/ + +int main(void) +{ + extern Image binary_symbol_start; + extern Image binary_symbol2_start; + extern Image binary_icon_start; + extern Image binary_sprites_start; + extern Image binary_swords_start; + + extern const void *vrams[4]; + + Image *sybl = &binary_symbol_start; + Image *sybl2 = &binary_symbol2_start; + + dclear(); + dreverse_area(0, 0, 127, 30); + + dimage(sybl, 0, 0, Blend_Or); + dimage(sybl, 20, 0, Blend_And); + dimage(sybl, 40, 0, Blend_Or | Blend_And); + dimage(sybl, 90, 0, Blend_Or | Blend_Invert); + + dimage(sybl2, 0, 20, Blend_Or); + dimage(sybl2, 20, 20, Blend_And); + dimage(sybl2, 28, 20, Blend_And); + dimage(sybl2, 40, 20, Blend_Or | Blend_And); + dimage(sybl2, 90, 20, Blend_Or | Blend_Invert); + + dimage(&binary_icon_start, 2, 35, Blend_Or); + + dreverse_area(35, 31, 127, 63); + dimage(&binary_sprites_start, 50, 31, Blend_And); + + dupdate(); +/* + do key = getkey(); + while(key != KEY_EXE && key != KEY_EXIT); + if(key == KEY_EXIT) return 0; + + print("h'sszz 64z=1s", 0, 0); + print("ML", 14, 0); + print("gint", 17, 0); + print("---------------------", 0, 1); + + print("30*19 icon", 0, 2); + print(wait, 12, 2); + print_hexa(exec(btest_ml_icon), 4, 12, 2); + print(wait, 17, 2); + print_hexa(exec(btest_gint_icon), 4, 17, 2); +*/ + while(getkey() != KEY_EXE); + + dclear(); + dimage(&binary_swords_start, 20, 20, Blend_Or); + dupdate(); + + while(getkey() != KEY_EXE); + return 0; +} diff --git a/addin.g1a b/addin.g1a new file mode 100644 index 0000000000000000000000000000000000000000..422f6b622535548e0a9724593cff6c5a2fe68910 GIT binary patch literal 12280 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 literal 0 HcmV?d00001 diff --git a/addin.ld b/addin.ld new file mode 100644 index 0000000..512f5db --- /dev/null +++ b/addin.ld @@ -0,0 +1,104 @@ +/* + 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. It happens, if I'm not wrong, that the + "real ram" is accessed directly while the "common" ram is accessed + through the mmu. The interrupt handler resides in "real ram" because it + couldn't execute well in ram. While SH7335 and SH7355 had no problems, + executing the interrupt handler in the common ram on SH7305-based new + models caused trouble to the OS, apparently overwriting ram data. +*/ + +OUTPUT_ARCH(sh3) +ENTRY(_start) + +MEMORY +{ + rom : o = 0x00300200, l = 512k + ram : o = 0x08100000, l = 64k + /* The "real ram" accessible length remains unknown because some parts + are used by the system. At least 12k seem accessible. Use with care. */ + realram : o = 0x8800d000, l = 12k +} + +SECTIONS +{ + /* + ROM sections : binary code and read-only data. + */ + + .text : { + /* Initialization code. */ + *(.pretext.entry) + *(.pretext) + + _bctors = . ; + *(.ctors) + _ectors = . ; + _bdtors = . ; + *(.dtors) + _edtors = . ; + + *(.text) + *(.text.*) + } > rom + + .rodata : { + *(.rodata) + *(.rodata.*) + + _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. + */ + + .bss : { + _bbss = . ; + *(.bss) + _ebss = . ; + } > ram + + .data : AT(_romdata) ALIGN(4) { + _bdata = . ; + *(.data) + *(.data.*) + _edata = . ; + } > ram + + .cc : AT(_romdata + SIZEOF(.data)) ALIGN(4) { + *(.eh_frame) + *(.jcr) + + _gint_data = _romdata + SIZEOF(.data) + SIZEOF(.cc) ; + } > ram + + + + /* + Real RAM : interrupt handler. + */ + + .gint_int : AT(_gint_data) ALIGN(4) { + /* The vbr needs to be 0x100-aligned because of an ld issue. */ + . = ALIGN(0x100) ; + _gint_vbr = . ; + + _bgint = . ; + + /* Interrupt handler. */ + . = _gint_vbr + 0x600 ; + *(.gint.int.entry) + *(.gint.int) + + _egint = . ; + } > realram +} diff --git a/icon.bmp b/icon.bmp new file mode 100644 index 0000000000000000000000000000000000000000..c97606c7cb0c2bcf40cf8d23fa16df42da126d41 GIT binary patch literal 1870 zcmb_aI~Kw)3=0f!02U?=(}f8J&cVjU2{<74DN00y>$tRh{9asHmXy**cQ~$7sVn*! zXNR-FS)?s`O7~StHxT*M>h`Bg#dDuaC+z#)G5;Hp{dv%7dX0>*aW&j$ff!*~!h|Z5 zY7&GoAvu-Zn#2e&BYY`nqHjWNyAqRaroPHH{wEA$Lb4db&F^H*#*<+Lh0aYRnf5|z ztWug{!9*)NM@nd7w>-CELP1HBM8Ij=vG|_ + +// Common exit codes. +#define EXIT_SUCCESS 1 +#define EXIT_FAILURE 0 + + + +//--- +// 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. + + @arg status +*/ +void exit(int status); + +#endif // _STDLIB_H diff --git a/include/string.h b/include/string.h new file mode 100644 index 0000000..d74977a --- /dev/null +++ b/include/string.h @@ -0,0 +1,32 @@ +#ifndef _STRING_H +#define _STRING_H 1 + +#include + +//--- +// Memory manipulation. +//--- + +/* + memcpy() + 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. + + @arg destination + @arg source + @arg byte_number +*/ +void *memcpy(void *destination, const void *source, size_t byte_number); + +/* + memset() + Sets the contents of a memory area. A smart copy is performed. + + @arg area + @arg byte Byte to write in the area. + @arg byte_number +*/ +void *memset(void *destination, int byte, size_t byte_number); + +#endif // _STRING_H diff --git a/include/timer.h b/include/timer.h new file mode 100644 index 0000000..786b259 --- /dev/null +++ b/include/timer.h @@ -0,0 +1,73 @@ +#ifndef _TIMER_H +#define _TIMER_H 1 + +//--- +// Constants. +//--- + +// 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_GRAY TIMER_TMU0 +#define TIMER_USER1 TIMER_TMU1 +#define TIMER_USER2 TIMER_TMU2 + +// 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 + + + +//--- +// Public API. +//--- + +/* + timer_set() + Configures and starts a timer. + + @arg timer Timer identifier. Use only TIMER_USER1 and + TIMER_USER2. + @arg delay Delay before expiration, in clock counts. + @arg prescaler Clock prescaler value. Possible values are + TIMER_Po_4, TIMER_Po_16, TIMER_Po_64, + TIMER_Po_256 and TIMER_TCLK. + @arg callback Callback function. + @arg repetitions Number of repetitions, 0 for infinite. +*/ +void timer_set(int timer, int delay, int prescaler, void (*callback)(void), + int repetitions); + +/* + timer_stop() + Stops the given timer. This function may be called even if the timer is + not running. + + @arg timer Timer identifier. +*/ +void timer_stop(int timer); + + + +//--- +// Internal API. +// Referenced for documentation purposes only. Do not call. +//--- + +/* + timer_interrupt() + Handles the interrupt for the given timer. + + @timer Timer that generated the interrupt. +*/ +void timer_interrupt(int timer); + +#endif // _TIMER_H diff --git a/info b/info new file mode 100644 index 0000000..8a2dc33 --- /dev/null +++ b/info @@ -0,0 +1,16 @@ + +Experimental power reduction as function of the keyboard analysis frequency. + +SH3 + None 0 % + 4 Hz 2.8 % + 16 Hz 2.8 % + 64 Hz 2.8 % + 256 Hz 20.0 % + +SH4 + None 0 % + 4 Hz 1.8 % + 16 Hz 1.8 % + 64 Hz 1.8 % + 256 Hz 3.6 % diff --git a/libc.a b/libc.a new file mode 100644 index 0000000000000000000000000000000000000000..c4d5af7614c29a71f622bcbc46e4f1576930db0a GIT binary patch literal 1770 zcmb7E%}Z2K6uU`9*n{bI<3y=iD>DdjmJLoRPlS(9I4~ zPb?9M#d;#WeT;?8kb7k;9&_^520};;@Dq!<9YVOBv3#m9m6;=B)3Y-ZrjkizjJYK; z1`=X~SP5cYoV99KEA}$h6AnYU>Y9i_wRmsDE~z#=y?s}q9EE~5QE~+|w?hECnZ(R^ zC-IP)!YyG}*cLtt?}fL*YvHA^D)VwwmgFWmD06b7+)jL-i5!w!lo@4Kx%`lH5np)Ju6u&u5A;F^*ql2RZ20}{*Kg@w@P#nXw2zFCi20>V zLDNANa+XXxS|PPqAmX@I(1@t#^SH_XQyZk>?CR`KaB4%gj>4I1z}B&Mr015Xu1;PD zc;)zq>j$4LWd=+=~q*fhaceeOqw0l*Rw)l-Rnnb+wR3FJzG+t*} z%9z>&@t+HoFMJ|Ye2_L4%Y)uUI#{o-h~0XfPtT?67i*Xx9ElsV!yD?;TUv4i8`xR>Kf)T*`iK^ta%tAaM1qk?~-viWJAZoV^`HovPu;w^b#&u}Ph zp3CMt)z0OB2S~`m){l)Se zoD$EtR+}2tf`MTLe9)OsmDbAOfwQ@NWI&Rxv(AB``&}$9M#Zr6j~|o{o&YeFAMOx< zrW!*(*lxU`?Zy=vh&g=ukhTG2_&s9n8}PDeJ3up^yaP|)Dekdo%w{sF8F(*GLC4%@ zI_~-&dq)ZYW8e1K=qq%($eGEZ`+Q;^-d+609>1A)`2J!1(7opr@Wp{0a_XXV{SWjF X*w27Hz|P%2=H7GI#|2=A%>DiVQWgdW literal 0 HcmV?d00001 diff --git a/libgint.a b/libgint.a new file mode 100644 index 0000000000000000000000000000000000000000..e392e536edc6ceb46103c0f3de295d9ae8acf1ac GIT binary patch literal 22334 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 literal 0 HcmV?d00001 diff --git a/sprites.bmp b/sprites.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2cbe277077d5bb0d2d3cd8e7333500260b42b549 GIT binary patch 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 literal 0 HcmV?d00001 diff --git a/src/crt0.c b/src/crt0.c new file mode 100644 index 0000000..bbb3383 --- /dev/null +++ b/src/crt0.c @@ -0,0 +1,145 @@ +// Used by the exit()-family functions to save and restore the execution state. +#include +static jmp_buf env; +// Provides EXIT_SUCCESS and EXIT_FAILURE. +#include +// Provides gint initialization functionalities. +#include +// Provides memset() and memcpy(). +#include + +// Some syscall prototypes. +void __Hmem_SetMMU(unsigned int, unsigned int, int); +void __GLibAddinAplExecutionCheck(int, int, int); +int main(void); + +// Local functions. +static void init(void); +static void fini(void); + +// Symbols imported from the linker script. +extern unsigned int + romdata, + bbss, ebss, + bdata, edata; + +// This variable should be overwritten before being returned, so the default +// value doesn't matter much. +static int exit_code = EXIT_SUCCESS; + + + +/* + start() + + Program entry point. Loads the data section into the memory and invokes + main(). Also prepares the execution environment by initializing all the + modules. + + @return Execution status returned to the OS. +*/ + +int start(void) + __attribute__(( + section(".pretext.entry") + )); + +int start(void) +{ + // Linker symbols. + unsigned int *bss = &bbss; + unsigned int *data = &bdata, *src = &romdata; + int x; + + // Setting up the TLB. + __Hmem_SetMMU(0x08102000, 0x8801e000, 108); + + // Clearing the .bss section. + while(bss < &ebss) *bss++ = 0; + // Copying the .data section. + while(data < &edata) *data++ = *src++; + + __GLibAddinAplExecutionCheck(0, 1, 1); + + // Initializing everything. + init(); + gint_init(); + + + + // Saving the execution state there. + 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(); + + + + // Remember to flush and close opened streams. + + // Un-initializing everything. + gint_quit(); + fini(); + + return exit_code; +} + +static void init(void) +{ + extern void + (*bctors)(void), + (*ectors)(void); + void (**func)(void) = &bctors; + + // Calling the constructors. + while(func < &ectors) + { + (*(*func))(); + func++; + } +} + +static void fini(void) +{ + extern void + (*bdtors)(void), + (*edtors)(void); + void (**func)(void) = &bdtors; + + // Calling the destructors. + while(func < &edtors) + { + (*(*func))(); + func++; + } +} + + + +/* + abort() + + Immediately ends the program without invoking the exit handlers. +*/ + +void abort(void) +{ + exit_code = EXIT_FAILURE; + longjmp(env, 1); +} + +/* + exit() + + Ends the program and returns the given exit code status. + Calls exit handlers before returning. + + @arg status Exit status. +*/ + +void exit(int status) +{ + exit_code = status; + longjmp(env, 1); +} + diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..92b2315 --- /dev/null +++ b/src/display.c @@ -0,0 +1,715 @@ +/* + display + + Handles vram manipulation and drawing. + + :: Rectangle masks + + The concept of 'rectangle masks' is used several times in this module. + It consists in saying that an operation that affects a rectangle acts + the same on all the lines (considering that only the lines that + intersect the rectangle are changed) and therefore it is possible to + represent the behavior on a single line using 'masks' that indicate + whether a pixel is affected (1) 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, reversing a rectangle of + vram only needs vram[offset] ^= masks[i]. + + This technique can also be used in more subtle cases with more complex + patterns, but within this module it is unlikely to happen. +*/ + +#include +#include +#include +#include +#include + +// 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]; +static int *vram = local_vram; + +#define sgn(x) ((x) < 0 ? -1 : 1) +#define abs(x) ((x) < 0 ? -(x) : (x)) +#define rnd(x) ((int)((x) + 0.5)) + + + +//--- +// Local helper functions. +//--- + +/* + adjust() + Adjusts the given rectangle coordinates to ensure that : + - The rectangle is entirely contained in the screen, + - x1 < x2 and y1 < y2, + which is needed when working with screen rectangles. + + @arg x1 + @arg y1 + @arg x2 + @arg y2 +*/ +static void adjust(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 < 0) *x1 = 0; + if(*y1 < 0) *y1 = 0; + if(*x2 > 127) *x2 = 127; + if(*y2 > 63) *y2 = 63; + #undef swap +} + +/* + getmasks() + + Computes the rectangle masks needed to affect pixels located between x1 + and x2 (both included). + + @arg x1 + @arg x2 + @arg masks Four-integer-array pointer. +*/ +static void getmasks(int x1, int x2, unsigned int *masks) +{ + // Indexes of the first and last longs that are non-blank. + int l1 = x1 >> 5; + int l2 = x2 >> 5; + int 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; + + // 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 (actually it creates positive bits at the left but those + // ones are removed by the bitwise-and mask). + x2 = ~x2 & 31; + + // Setting the last masks. + masks[l1] &= (0xffffffff >> x1); + masks[l2] &= (0xffffffff << x2); +} + + + +//--- +// Generic functions. +//--- + +/* + display_getVRAM() + Returns the current video ram. + + @return Video ram address. +*/ +void *display_getLocalVRAM(void) +{ + return (void*)local_vram; +} + +/* + display_useVRAM() + Changes the current video ram address. Expects a *4-aligned* 1024-byte + buffer. + + @arg New video ram address. +*/ +void display_useVRAM(void *ptr) +{ + vram = (int *)ptr; +} + + + +//--- +// Global drawing functions. +//--- + +/* + dupdate() + Displays the vram on the physical screen. +*/ +void dupdate(void) +{ + screen_display((const void *)local_vram); +} + +/* + dclear() + Clears the whole vram. +*/ +void dclear(void) +{ + int i; + for(i = 0; i < 256; i++) vram[i] = 0; +} + +/* + dclear_area() + Clears an area of the vram using rectangle masks. + + @arg x1 + @arg y1 + @arg x2 + @arg y2 +*/ +void dclear_area(int x1, int y1, int x2, int y2) +{ + unsigned int masks[4]; + adjust(&x1, &y1, &x2, &y2); + getmasks(x1, x2, masks); + + int offset = y1 << 2; + int end = (y2 + 1) << 2; + int i; + + for(i = 0; i < 4; i++) masks[i] = ~masks[i]; + while(offset < end) vram[offset] &= masks[offset & 3], offset++; +} + +/* + dreverse_area() + Reverses an area of the vram. This function is a simple application of + the rectangle masks concept. + + @arg x1 + @arg y1 + @arg x2 + @arg y2 +*/ +void dreverse_area(int x1, int y1, int x2, int y2) +{ + unsigned int masks[4]; + adjust(&x1, &y1, &x2, &y2); + getmasks(x1, x2, masks); + + int offset = y1 << 2; + int end = (y2 + 1) << 2; + + while(offset < end) vram[offset] ^= masks[offset & 3], offset++; +} + + + +//--- +// Local drawing functions. +//--- + +/* + dpixel() + Puts a pixel on the screen. + + @arg x + @arg y + @arg color +*/ +void dpixel(int x, int y, enum Color color) +{ + if((unsigned int)x > 127 || (unsigned int)y > 63) return; + int offset = (y << 2) + (x >> 5); + int mask = 0x80000000 >> (x & 31); + + switch(color) + { + case Color_White: + vram[offset] &= ~mask; + break; + + case Color_Black: + vram[offset] |= mask; + break; + + case Color_None: + return; + + case Color_Invert: + vram[offset] ^= mask; + break; + } +} + +/* + dline() + Draws a line on the screen. Automatically optimizes horizontal and + vertical lines. + + @arg x1 + @arg y1 + @arg x2 + @arg y2 + @arg color +*/ + +static void dhline(int x1, int x2, int y, enum Color color) +{ + unsigned int masks[4]; + int offset = y << 2; + int i; + + getmasks(x1, x2, masks); + + switch(color) + { + case Color_White: + for(i = 0; i < 4; i++) vram[offset + i] &= ~masks[i]; + break; + + case Color_Black: + for(i = 0; i < 4; i++) vram[offset + i] |= masks[i]; + break; + + case Color_None: + return; + + case Color_Invert: + for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i]; + break; + } +} + +static void dvline(int y1, int y2, int x, enum Color color) +{ + int offset = (y1 << 2) + (x >> 5); + int end = (y2 << 2) + (x >> 5); + int mask = 0x80000000 >> (x & 31); + + switch(color) + { + case Color_White: + while(offset <= end) vram[offset] &= ~mask, offset += 4; + break; + + case Color_Black: + while(offset <= end) vram[offset] |= mask, offset += 4; + break; + + case Color_None: + return; + + case Color_Invert: + while(offset <= end) vram[offset] ^= mask, offset += 4; + break; + } +} + +void dline(int x1, int y1, int x2, int y2, enum Color color) +{ + adjust(&x1, &y1, &x2, &y2); + + // Possible optimizations. + if(y1 == y2) + { + dhline(x1, x2, y1, color); + return; + } + if(x1 == x2) + { + dvline(y1, y2, x1, color); + 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, color); + + 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, color); + } + } + else + { + cumul = dy >> 1; + for(i = 1; i < dy; i++) + { + y += sy; + cumul += dx; + if(cumul > dy) cumul -= dy, x += sx; + dpixel(x, y, color); + } + } + + dpixel(x2, y2, color); +} + + + +//--- +// Image drawing. There is only one public function dimage(), but there +// are lots of local methods and optimizations. +// +// Some expressions may look nonsense sometimes. The procedure is always +// the same : get a part of the image in an operator, shift it depending +// on the drawing x-coordinate, compute a mask that indicates which bits +// of the operator contain information, and modify a vram long using the +// operator. +//--- + +/* + bopti_op() + Operates on a vram long. The operator will often not contain 32 bits of + image information. In this case, the bits outside the image must be set + to 0 for Or and Invert operations... 1 for And operations. Which means + that the calling produre must indicate what part of the operator + belongs to the image, which is done through the image_mask argument. + + @arg offset Vram offset where edition is planned. + @arg operator Longword to operate with. + @arg image_mask Part of the operator that is inside the image. + @arg mode Operation mode. +*/ +static void bopti_op(int offset, uint32_t operator, uint32_t image_mask, + enum BlendingMode mode) +{ + if(mode & Blend_Checker) operator &= 0x55555555; + if(mode & Blend_Or) vram[offset] |= operator; + if(mode & Blend_Invert) vram[offset] ^= operator; + operator |= ~image_mask; + if(mode & Blend_And) vram[offset] &= operator; +} + +/* + bopti_grid() -- general form + bopti_grid_a32() -- when x is a multiple of 32 + + Draws a layer, whose width is a multiple of 32, in the vram. + The need for bopti_grid_a32() is not only linked to optimization, + because one of the bit shifts in bopti_grid() will reach 32 when x is + a multiple of 32, which is undefined behavior. + + @arg layer Raw column data (column data is located at the + beginning of layer data). + @arg column_number + @arg width + @arg height + @arg x + @arg y + @arg mode +*/ + +static void bopti_grid_a32(const uint32_t *layer, int column_number, int width, + int height, int x, int y, enum BlendingMode mode) +{ + int vram_column_offset = (y << 2) + (x >> 5); + int vram_offset = vram_column_offset; + + int column, line; + uint32_t operator, and_mask; + uint32_t rightest_and_mask; + + if(width & 31) rightest_and_mask = ~(0xffffffff >> (width & 31)); + else rightest_and_mask = 0xffffffff; + + for(column = 0; column < column_number; column++) + { + for(line = 0; line < height; line++) + { + operator = *layer++; + + and_mask = (column < column_number - 1) ? + (0xffffffff) : (rightest_and_mask); + bopti_op(vram_offset, operator, and_mask, mode); + vram_offset += 4; + } + + vram_column_offset++; + vram_offset = vram_column_offset; + } +} + +static void bopti_grid(const uint32_t *layer, int column_number, int width, + int height, int x, int y, enum BlendingMode mode) +{ + const uint32_t *p1, *p2; + uint32_t l1, l2; + int right_column, line; + + int vram_column_offset = (y << 2) + (x >> 5); + int vram_offset = vram_column_offset; + + int shift1 = 32 - (x & 31); + int shift2 = (x & 31); + int combined_shift_last = shift1 + 32 - (width & 31); + + uint32_t operator, and_mask; + uint32_t and_mask_0 = 0xffffffff >> shift2; + uint32_t and_mask_1 = (0xffffffff) << combined_shift_last; + + if(!column_number) return; + if(!(x & 31)) + { + bopti_grid_a32(layer, column_number, width, height, x, y, + mode); + return; + } + + // Initializing two pointers. Since the columns are written one after + // another, they will be updated directly to parse the whole grid. + p1 = layer - height; + p2 = layer; + + // Drawing vram longwords, using pairs of columns. + for(right_column = 0; right_column <= column_number; right_column++) + { + for(line = 0; line < height; line++) + { + l1 = (right_column > 0) ? (*p1) : (0); + l2 = (right_column < column_number) ? (*p2) : (0); + p1++, p2++; + + operator = (l1 << shift1) | (l2 >> shift2); + + and_mask = 0xffffffff; + if(!right_column) and_mask &= and_mask_0; + if(right_column == column_number) + and_mask &= and_mask_1; + + bopti_op(vram_offset, operator, and_mask, mode); + vram_offset += 4; + } + + vram_column_offset++; + vram_offset = vram_column_offset; + } +} + +/* + bopti_rest8() -- general form, width below 8 + bopti_rest8_nover() -- when the rest does not meet two longs + bopti_rest16() -- general form, width below 16 + bopti_rest16_nover() -- when the rest does not meet two longs + + Draw rests of row size of 8 and 16 bits, respectively. + + @arg rest Rest data, located at the end of the layer data. + @arh width + @arg height + @arg x + @arg y + @arg mode +*/ + +static void bopti_rest8_nover(const uint8_t *rest, int width, int height, + int x, int y, enum BlendingMode mode) +{ + int vram_offset = (y << 2) + (x >> 5); + int shift = x & 31; + + uint32_t operator; + uint32_t and_mask = ~(0xffffffff >> width) >> shift; + int line; + + for(line = 0; line < height; line++) + { + operator = *rest++; + // Optimization possible ? Probably not. + operator <<= 24; + operator >>= shift; + + bopti_op(vram_offset, operator, and_mask, mode); + vram_offset += 4; + } +} + +static void bopti_rest8(const uint8_t *rest, int width, int height, int x, + int y, enum BlendingMode mode) +{ + if((x & 31) + width < 32) + { + bopti_rest8_nover(rest, width, height, x, y, mode); + return; + } + + int vram_offset = (y << 2) + (x >> 5); + int shift1 = (x & 31) - 24; + int shift2 = 56 - (x & 31); + uint32_t and_mask_1 = 0xffffffff >> (x & 31); + uint32_t and_mask_2 = ~(0xffffffff >> ((x & 31) + width - 32)); + + uint32_t operator; + int line; + + for(line = 0; line < height; line++) + { + operator = *rest++; + + bopti_op(vram_offset, operator >> shift1, and_mask_1, mode); + bopti_op(vram_offset + 1, operator << shift2, and_mask_2, + mode); + vram_offset += 4; + } +} + +static void bopti_rest16_nover(const uint16_t *rest, int width, int height, + int x, int y, enum BlendingMode mode) +{ + int vram_offset = (y << 2) + (x >> 5); + int shift = x & 31; + + uint32_t operator; + uint32_t and_mask = ~(0xffffffff >> width) >> shift; + int line; + + for(line = 0; line < height; line++) + { + operator = *rest++; + // As far as I know, no, we can't optimize this into a single + // shift. + operator <<= 16; + operator >>= shift; + + bopti_op(vram_offset, operator, and_mask, mode); + vram_offset += 4; + } +} + +static void bopti_rest16(const uint16_t *rest, int width, int height, int x, + int y, enum BlendingMode mode) +{ + if((x & 31) + width < 32) + { + bopti_rest16_nover(rest, width, height, x, y, mode); + return; + } + + int vram_offset = (y << 2) + (x >> 5); + int shift1 = (x & 31) - 16; + int shift2 = 48 - (x & 31); + uint32_t and_mask_1 = 0xffffffff >> (x & 31); + uint32_t and_mask_2 = ~(0xffffffff >> ((x & 31) + width - 32)); + + uint32_t operator; + int line; + + for(line = 0; line < height; line++) + { + operator = *rest++; + + bopti_op(vram_offset, operator >> shift1, and_mask_1, mode); + bopti_op(vram_offset + 1, operator << shift2, and_mask_2, + mode); + vram_offset += 4; + } +} + +/* + bopti() + Draws an image layer in the video ram. + + @arg bitmap Raw layer data. + @arg x + @arg y + @arg width + @arg height + @arg mode +*/ +void bopti(const unsigned char *layer, int x, int y, int width, int height, + enum BlendingMode mode) +{ + int column_number = width >> 5; + int rest_width = width & 31; + int grid_width = width & ~31; + + if(rest_width > 16) + { + column_number++; + rest_width = 0; + grid_width = width; + } + + const unsigned char *rest = layer + ((column_number * height) << 2); + int rest_x = x + (width - rest_width); + + bopti_grid((const uint32_t *)layer, column_number, grid_width, height, + x, y, mode); + if(!rest_width) return; + + if(rest_width <= 8) + bopti_rest8((const uint8_t *)rest, rest_width, height, rest_x, + y, mode); + else + bopti_rest16((const uint16_t *)rest, rest_width, height, + rest_x, y, mode); +} + +/* + dimage() + Displays an image in the vram. + + @arg image + @arg x + @arg y + @arg mode +*/ + +void dimage(struct Image *image, int x, int y, enum BlendingMode mode) +{ + int width = image->width; + int height = image->height; + const unsigned char *data = (const unsigned char *)&(image->data); + + // Computing the layer size. + int columns = image->width >> 5; + int rest = image->width & 31; + int rest_size = + !rest ? 0 : + rest <= 8 ? 1 : + rest <= 16 ? 2 : + 4; + int layer_size = ((columns << 2) + rest_size) * image->height; + // The layer size must be a multiple of 4. + if(layer_size & 3) layer_size += 4 - (layer_size & 3); + + switch(image->format & ImageFormat_ColorMask) + { + case ImageFormat_Mono: + if(image->format & ImageFormat_Alpha) + { + bopti(data + layer_size, x, y, width, height, + Blend_And); + } + bopti(data, x, y, width, height, mode); + break; + + case ImageFormat_Gray: + if(image->format & ImageFormat_Alpha) + { + bopti(data + 2 * layer_size, x, y, width, height, + Blend_And); + } + + display_useVRAM(gray_darkVRAM()); + bopti(data, x, y, width, height, mode); + display_useVRAM(gray_lightVRAM()); + bopti(data + layer_size, x, y, width, height, mode); + break; + } +} diff --git a/src/gint.c b/src/gint.c new file mode 100644 index 0000000..19198c9 --- /dev/null +++ b/src/gint.c @@ -0,0 +1,107 @@ +#include +#include +#include + +//--- +// Local variables. +//--- + +static unsigned int + new_vbr, + sys_vbr; + + + +//--- +// Local functions. +//--- + +/* + 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_stop() + Un-configures the interrupt flow to give back the interrupt control to + the system. +*/ +static void gint_stop(void) +{ + if(isSH3()) + gint_stop_7705(); + else + gint_stop_7305(); +} + + + +//--- +// Public API. +//--- + +/* + gint_systemVBR() + Returns the vbr address used by the system (saved when execution + starts). + + @return vbr address used by the system. +*/ +unsigned int gint_systemVBR(void) +{ + return sys_vbr; +} + +/* + gint() + Handles interrupts. +*/ +void gint(void) +{ + if(isSH3()) + gint_7705(); + else + gint_7305(); +} + +/* + 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; + + // 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); +} diff --git a/src/gint_7305.c b/src/gint_7305.c new file mode 100644 index 0000000..0947749 --- /dev/null +++ b/src/gint_7305.c @@ -0,0 +1,292 @@ +#include +#include <7305.h> + +extern void print_hex(unsigned int value, int x, int y); +extern void print_bin(unsigned char value, int x, int y); + + + +//--- +// Interrupt codes. +//--- + +#define IC_RTC_PRI 0xaa0 +#define IC_KEYSC 0xbe0 + + + +//--- +// Keyboard management. +//--- + +extern volatile unsigned char keyboard_state[10]; +extern void keyboard_interrupt(void); + +/* + 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. + + - 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("nop\n\t") + r4("nop\n\t") + r4("nop\n\t") + ); + + #undef r4 +} + +/* + krow() + Reads a keyboard row. Works like krow() for SH7705. See gint_7705.c for + more details. + + @arg row Row to check (0 <= row <= 9). + @return Bit-based representation of pressed keys in the checked row. +*/ +static int krow(int row) +{ + volatile unsigned short *injector1 = (unsigned short *)0xa4050116; + volatile unsigned char *data1 = (unsigned char *)0xa4050136; + + volatile unsigned short *injector2 = (unsigned short *)0xa4050118; + volatile unsigned char *data2 = (unsigned char *)0xa4050138; + + volatile unsigned short *detector = (unsigned short *)0xa405014c; + volatile unsigned char *keys = (unsigned char *)0xa405016c; + + volatile unsigned char *key_register = (unsigned char *)0xa40501c6; +// volatile unsigned short *hizcrb = (unsigned short *)0xa405015a; + + unsigned short smask; + unsigned char cmask; + int result = 0; + + if(row < 0 || row > 9) return 0; + + // Additional configuration for SH7305. + *detector = 0xaaaa; + *key_register = 0xff; + *injector1 = (*injector1 & 0xf000) | 0x0555; + *injector2 = (*injector2 & 0xf000) | 0x0555; + *data1 |= 0x3f; + *data2 |= 0x3f; + kdelay(); + + if(row < 6) + { + smask = 0x0003 << (row * 2); + cmask = ~(1 << row); + + *injector1 = ((*injector1 & 0xf000) | 0x0aaa) ^ smask; + *injector2 = (*injector2 & 0xf000) | 0x0aaa; + kdelay(); + + *data1 = (*data1 & 0xc0) | cmask; + *data2 |= 0x3f; + kdelay(); + } + else + { + smask = 0x0003 << ((row - 6) * 2); + cmask = ~(1 << (row - 6)); + + *injector1 = (*injector1 & 0xf000) | 0x0aaa; + *injector2 = ((*injector2 & 0xf000) | 0x0aaa) ^ smask; + kdelay(); + + *data1 |= 0x3f; + *data2 = (*data2 & 0xc0) | cmask; + kdelay(); + } + + // Reading the keyboard row. + result = ~*keys; + kdelay(); + + // Re-initializing the port configuration and data. + *injector1 = (*injector1 & 0xf000) | 0x0aaa; + *injector2 = (*injector2 & 0xf000) | 0x0aaa; + kdelay(); + *injector1 = (*injector1 & 0xf000) | 0x0555; + *injector2 = (*injector2 & 0xf000) | 0x0555; + kdelay(); + *data1 &= 0xc0; + *data2 &= 0xc0; + + return result; +} + +/* + kstate() + Updates the keyboard state. +*/ +static void kstate(void) +{ + int i; + + for(i = 0; i < 10; i++) keyboard_state[i] = krow(i); + + keyboard_interrupt(); +} + + + +//--- +// Interrupt handler. +//--- + +void gint_7305(void) +{ + volatile unsigned int *intevt = (unsigned int *)0xff000028; + unsigned int code = *intevt; + + switch(code) + { + case IC_RTC_PRI: + // Clearing the interrupt flag. + RTC.RCR2.PEF = 0; + // Updating the keyboard state. + kstate(); +/* + print_bin(keyboard_state[0], 0, 2); + print_bin(keyboard_state[1], 0, 3); + print_bin(keyboard_state[2], 0, 4); + print_bin(keyboard_state[3], 0, 5); + print_bin(keyboard_state[4], 0, 6); + + print_bin(keyboard_state[5], 9, 2); + print_bin(keyboard_state[6], 9, 3); + print_bin(keyboard_state[7], 9, 4); + print_bin(keyboard_state[8], 9, 5); + print_bin(keyboard_state[9], 9, 6); +*/ + break; + } +} + + + +//--- +// Setup. +//--- + +static unsigned short ipr[12]; +static unsigned char rcr2; +// Saves of the keyboard registres. Could be better. +static unsigned short inj1, inj2, det; +static unsigned char data1, data2, keys, reg; + +static void gint_priority_lock_7305(void) +{ + // 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; + + // Disabling everything by default to avoid freezing on unhandled + // 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; + + // 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. + INTX.IPRK._RTC = GINT_INTP_RTC; +} + +static void gint_priority_unlock_7305(void) +{ + // 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]; + + // 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) +{ + gint_priority_lock_7305(); + + // Configuring the RTC to have a 16-Hz keyboard. + rcr2 = RTC.RCR2.BYTE; + RTC.RCR2.BYTE = 0x39; +} + +void gint_stop_7305(void) +{ + gint_priority_unlock_7305(); + + // Stopping the RTC interrupt. + RTC.RCR2.BYTE = rcr2; +} diff --git a/src/gint_7705.c b/src/gint_7705.c new file mode 100644 index 0000000..02ff8b2 --- /dev/null +++ b/src/gint_7705.c @@ -0,0 +1,244 @@ +#include +#include <7705.h> + +extern void print_hex(unsigned int value, int x, int y); +extern void print_bin(unsigned char value, int x, int y); + + + +//--- +// Interrupt codes. +//--- + +#define IC_RTC_PRI 0x4a0 +#define IC_PINT07 0x700 + + + +//--- +// Keyboard management. +//--- + +extern volatile unsigned char keyboard_state[10]; +extern void keyboard_interrupt(void); + +/* + kdelay() + Low-level sleep using the watchdog. +*/ +static void kdelay(void) +{ + #define r4(str) str str str str + + __asm__ + ( + r4("nop\n\t") + r4("nop\n\t") + r4("nop\n\t") + ); + + #undef r4 + +/* + 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. + + @arg row Row to check (0 <= row <= 9). + @return Bit-based representation of pressed keys in the checked row. +*/ +static int krow(int row) +{ + // '11' on the active row, '00' everywhere else. + unsigned short smask = 0x0003 << ((row % 8) * 2); + // '0' on the active row, '1' everywhere else. + unsigned char cmask = ~(1 << (row % 8)); + // Line results. + int result = 0; + + if(row < 0 || row > 9) return 0; + + // 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; +} + +/* + kstate() + Updates the keyboard state. +*/ +static void kstate(void) +{ + int i; + + for(i = 0; i < 10; i++) keyboard_state[i] = krow(i); + + keyboard_interrupt(); +} + + + +//--- +// Interrupt handler. +//--- + +void gint_7705(void) +{ + volatile unsigned int *intevt2 = (unsigned int *)0xa4000000; + unsigned int code = *intevt2; + + switch(code) + { + case IC_RTC_PRI: + // Clearing the interrupt flag. + RTC.RCR2.BIT.PEF = 0; + // Updating the keyboard state. + kstate(); +/* + print_bin(keyboard_state[0], 0, 2); + print_bin(keyboard_state[1], 0, 3); + print_bin(keyboard_state[2], 0, 4); + print_bin(keyboard_state[3], 0, 5); + print_bin(keyboard_state[4], 0, 6); + + print_bin(keyboard_state[5], 9, 2); + print_bin(keyboard_state[6], 9, 3); + print_bin(keyboard_state[7], 9, 4); + print_bin(keyboard_state[8], 9, 5); + print_bin(keyboard_state[9], 9, 6); +*/ + break; + } +} + + + +//--- +// Setup. +//--- + +static unsigned short iprs[8]; + +static void gint_priority_lock_7705(void) +{ + // 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; + + // 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; + + // Allowing RTC, which handles keyboard. + INTC.IPRA.BIT._RTC = GINT_INTP_RTC; +} + +static void gint_priority_unlock_7705(void) +{ + // 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) +{ + gint_priority_lock_7705(); + + // Configuring the RTC to have a 16-Hz keyboard. + RTC.RCR2.BYTE = 0x39; +} + +void gint_stop_7705(void) +{ + gint_priority_unlock_7705(); +} diff --git a/src/gint_vbr.s b/src/gint_vbr.s new file mode 100644 index 0000000..92a8a29 --- /dev/null +++ b/src/gint_vbr.s @@ -0,0 +1,71 @@ +/* + 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. + + @return vbr address currently in use. +*/ +_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. + + @arg New vbr address. + @arg 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 + + /* Activating 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/gray.c b/src/gray.c new file mode 100644 index 0000000..53bcb56 --- /dev/null +++ b/src/gray.c @@ -0,0 +1,76 @@ +#include +#include +#include + +static int internal_vrams[3][256]; +const void *vrams[4]; + +static int current = 0; + +/* + 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) +{ + display_useVRAM(display_getLocalVRAM()); +} + +/* + gray_lightVRAM() + Returns the module's gray vram address. +*/ +void *gray_lightVRAM(void) +{ + return (void *)vrams[current]; +} + +/* + gray_lightVRAM() + Returns the module's dark vram address. +*/ +void *gray_darkVRAM(void) +{ + return (void *)vrams[current + 1]; +} + +/* + gray_swap() + Swaps the vram buffers. +*/ +void gray_swap(void) +{ + current = (current + 2) & 3; +} + +/* + gray_interrupt() + Answers a timer interrupt. Swaps the two buffers. +*/ +void gray_interrupt(void) +{ + 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]; +} diff --git a/src/keyboard.c b/src/keyboard.c new file mode 100644 index 0000000..8b3985a --- /dev/null +++ b/src/keyboard.c @@ -0,0 +1,444 @@ +#include +#include + +//--- +// Keyboard variables. +//--- + +volatile unsigned char keyboard_state[10] = { 0 }; +static int repeat_first = 10, repeat_next = 2; +static int last_key = KEY_NONE, last_repeats = 0, last_events = 0; +static volatile int interrupt_flag = 0; + + + +//--- +// Auxiliary functions. +//--- + +/* + sleep() + Puts the CPU in sleep mode and waits for an interrupt to return. +*/ +void sleep(void) +{ + __asm__ + ( + "sleep\n\t" + ); +} + +/* + getPressedKey() + Finds a pressed key in the keyboard state and returns it. + + @return A pressed key. +*/ +int getPressedKey(void) +{ + 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; +} + +/* + getPressedKeys() + Find 'count' pressed keys in the keyboard state. + + @arg keys Will be filled. + @arg count Size of array. + + @return Number of actual pressed keys found. +*/ +int getPressedKeys(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; +} + +/* + keyboard_interrupt() + Callback for keyboard update. Allows keyboard analysis functions to + wake only when RTC interrupts happen. +*/ +void keyboard_interrupt(void) +{ + interrupt_flag = 1; +} + + + +//--- +// Keyboard configuration. +//--- + +/* + keyboard_setFrequency() + Sets the keyboard frequency. Does nothing when the argument is not a + valid KeyboardFrequency value. + + @arg frequency +*/ +void keyboard_setFrequency(enum KeyboardFrequency frequency) +{ + volatile unsigned char *rcr2; + + if(frequency < 1 || frequency > 7) return; + rcr2 = (unsigned char *)(isSH3() ? 0xfffffede : 0xa413fede); + + frequency <<= 4; + *rcr2 = (*rcr2 & 0x8f) | frequency; +} + +/* + keyboard_setRepeatRate() + Sets the default repeat rate for key events. The unit for the argument + is the keyboard period. + For example at 16 Hz, values of (10, 2) will imitate the system + default. + + @arg first Delay before first repeat, in keyboard period units. + @arg next Delay before following repeats, in keyboard period + units. +*/ +void keyboard_setRepeatRate(int first, int next) +{ + if(first < 0) first = 0; + if(next < 0) next = 0; + + repeat_first = first; + repeat_next = next; +} + + + +//--- +// Keyboard access. +//--- + +/* + keylast() + Returns the matrix code of the last pressed key. If repeat_count is + non-NULL, it is set to the number of repetitions. + + @arg repeat_count + @return Key matrix code. +*/ +int keylast(int *repeat_count) +{ + if(repeat_count) *repeat_count = last_repeats; + return last_key; +} + + + +/* + getkey() + Blocking function with auto-repeat and SHIFT modifying functionalities. + Roughly reproduces the behavior of the system's GetKey(). + + @return Pressed key matrix code. +*/ +int getkey(void) +{ + return getkey_opt( + Getkey_ShiftModifier | + Getkey_AlphaModifier | + + Getkey_RepeatArrowKeys, + + 0 + ); +} + + + +/* + getkey_opt() + Enhances getkey() with most general functionalities. + If max_cycles is non-zero and positive, getkey_opt() will return + KEY_NOEVENT if no event occurs during max_cycle analysis. + + @arg options OR-combination of GetkeyOpt values. + @arg max_cycles + + @return Pressed key matrix code. +*/ +int getkey_opt(enum GetkeyOpt options, int max_cycles) +{ + int key; + enum KeyType type; + int modifier = 0, last_modifier = KEY_NONE; + int r; + + if(!max_cycles) max_cycles = -1; + + while(max_cycles != 0) + { + while(!interrupt_flag) sleep(); + interrupt_flag = 0; + if(max_cycles > 0) max_cycles--; + + // Getting key and adding modifiers. + key = getPressedKey(); + + // Handling "no_key" event; + if(key == KEY_NONE) + { + // Condition for returning. + r = (last_key != KEY_NONE && + options & Getkey_ReleaseEvent); + + last_key = KEY_NONE; + last_modifier = KEY_NONE; + last_repeats = 0; + last_events = 0; + + if(r) return KEY_NONE; + } + + // Handling "new key" events. + else if(key != last_key) + { + if(options & Getkey_ShiftModifier && key == KEY_SHIFT) + { + if(last_modifier != KEY_SHIFT) + modifier ^= MOD_SHIFT; + last_modifier = KEY_SHIFT; + + continue; + } + if(options & Getkey_AlphaModifier && key == KEY_ALPHA) + { + if(last_modifier != KEY_ALPHA) + modifier ^= MOD_ALPHA; + last_modifier = KEY_ALPHA; + + continue; + } + + last_key = key; + last_repeats = 0; + last_events = 0; + + return key | modifier; + } + + // 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; +} + +/* + 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. + + @arg keys Key code array. + @arg count Maximum number of keys that will be read. + @arg max_cycles +*/ +void multigetkey(int *keys, int count, int max_cycles) +{ + int number; + + if(!max_cycles) max_cycles = -1; + + while(max_cycles != 0) + { + while(!interrupt_flag) sleep(); + interrupt_flag = 0; + if(max_cycles > 0) max_cycles--; + + number = getPressedKeys(keys, count); + if(number) return; + + // Handle 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... (no need to fill + // the array, it has already been done by getPressedKeys()). + return; +} + + + +//--- +// Key analysis. +//--- + +/* + keyid() + Returns a non-matrix key code that can be used for array subscript. + Ignores modifiers. + + @arg key + @return Modified keycode. +*/ +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; +} + +/* + keychar() + Returns the ASCII character associated with a key, or 0 for control + keys. + + @arg key +*/ +int keychar(int 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 = keyid(key); + + if(key & MOD_ALPHA) return alpha[id]; + return flat[id]; +} + +/* + keytype() + Returns a key's type. Ignores modifiers. + + @arg key + @return Key type. +*/ +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/mpu.c b/src/mpu.c new file mode 100644 index 0000000..ccc38d6 --- /dev/null +++ b/src/mpu.c @@ -0,0 +1,75 @@ +#include + +enum MPU MPU_CURRENT; + +/* + getMPU() + + Returns the MPU identifier of the calculator. + Thanks to SimonLothar for this function and related informations. + + Processor version register (PVR) and product control register (PRR) + hold information about the MPU version but are only accessible for + SH-4-based MPUs. + Therefore, 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. + + @return MPU identifier as integer value. +*/ + +enum MPU getMPU(void) +{ + // Processor version register. + volatile unsigned int *pvr = (unsigned int *)0xff000030; + // Product version register. + volatile unsigned int *prr = (unsigned int *)0xff000044; + // Port L control register. + volatile unsigned short *plcr = (unsigned short *)0xa4000114; + // Saved value for PLCR. + unsigned short saved_plcr; + unsigned int 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 to have the three upper bytes of the processor version + // register 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. + switch(*prr & 0xfffffff0) + { + case 0x00002c00: + return MPU_SH7305; + case 0x00002200: + return MPU_SH7724; + } + + // By default, the MPU is unknown. + return MPU_Unknown; +} + +static void mpu_init(void) + __attribute__(( + section(".pretext"), + constructor + )); + +static void mpu_init(void) +{ + MPU_CURRENT = getMPU(); +} diff --git a/src/screen.c b/src/screen.c new file mode 100644 index 0000000..e3ac5e6 --- /dev/null +++ b/src/screen.c @@ -0,0 +1,51 @@ +/* + screen.c + + This module is in charge of interaction with the physical screen. See + module 'display' 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. +*/ + +#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 computations. + + @arg vram 1024-byte video buffer. +*/ +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; + + for(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. + *selector = 7; + for(bytes = 0; bytes < 16; bytes++) *data = *vram++; + } +} diff --git a/src/setjmp.s b/src/setjmp.s new file mode 100644 index 0000000..27a070e --- /dev/null +++ b/src/setjmp.s @@ -0,0 +1,82 @@ +/* + This file implements long jumps. An example of their use is with crt0.c + and exit()-family functions that use them to restore the execution + state when leaving the program from an unknown location. + + The register contents are saved in a buffer when setjmp() is called and + restored at any time when longjmp() performs the jump, through an + exit() or abort() call, for instance. + + This is actually a question of playing with pr ; the user program is + resumed after the setjmp() call when longjmp() is invoked but setjmp() + has nothing to do with this operation. 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: + /* 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, is + actually handled by longjmp(). */ + rts + mov #0, r0 + + + +_longjmp: + /* Restoring the system and control registers. Restoring pr is actually + what performs the jump -- and makes the user program thinks that + setjmp() has 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/string.c b/src/string.c new file mode 100644 index 0000000..6edd334 --- /dev/null +++ b/src/string.c @@ -0,0 +1,134 @@ +#include +#include + +//--- +// Memory manipulation. +//--- + +/* + memcpy() + Copies a memory area. A smart copy is performed if possible. + + @arg destination + @arg source + @arg byte_number +*/ +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; +} + +/* + memset() + Sets the contents of a memory area. A smart copy is performed. + + @arg area + @arg byte Byte to write in the area. + @arg byte_number +*/ +void *memset(void *d, int byte, size_t byte_number) +{ + 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_number < 8) + { + while(byte_number) + { + *dest++ = byte; + byte_number--; + } + + return d; + } + + // Reaching a long offset. + while((intptr_t)dest & 3) + { + *dest++ = byte; + byte_number--; + } + // Copying using long operations. + while(byte_number >= 4) + { + *((uint32_t *)dest) = longword; + dest += 4; + byte_number -= 4; + } + // Ending the copy. + while(byte_number) + { + *dest++ = byte; + byte_number--; + } + + return d; +} diff --git a/src/syscalls.s b/src/syscalls.s new file mode 100644 index 0000000..c76d3e3 --- /dev/null +++ b/src/syscalls.s @@ -0,0 +1,37 @@ +/* + This file contains all the system calls used by the library. Maybe one + day we won't need them anymore ? +*/ + + .global ___Hmem_SetMMU + .global ___GLibAddinAplExecutionCheck + .global ___Print + + + +___Hmem_SetMMU: + mov.l syscall_table, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x3fa + +___GLibAddinAplExecutionCheck: + mov.l syscall_table, r2 + mov #0x13, r0 + jmp @r2 + nop + +___Print: + mov.l syscall_table, r2 + mov.l 1f, r0 + jmp @r2 + nop +1: .long 0x15d + + + + .align 4 + +syscall_table: + .long 0x80010070 diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..f05b1a0 --- /dev/null +++ b/src/timer.c @@ -0,0 +1,162 @@ +#include +#include +#include + +/* + struct Timer + This structure handles a running timer information. +*/ +struct Timer +{ + void (*callback)(void); + int repetitions; +}; + +// Static timers. +static struct Timer timers[3] = { { NULL, 0 }, { NULL, 0 }, { NULL, 0 } }; + +/* + struct mod_tmu + This structure allows access to a timer using its address. +*/ +struct mod_tmu +{ + // Timer constant register. + unsigned int TCOR; + // Timer counter. + unsigned int TCNT; + + // Timer control register. + union + { + unsigned short WORD; + struct + { + unsigned :7; + // Underflow flag. + unsigned UNF :1; + unsigned :2; + // Underflow interrupt enable. + unsigned UNIE :1; + // Clock edge, reserved on SH7305. + unsigned CKEG :2; + // Timer prescaler. + unsigned TPSC :3; + }; + } TCR; +}; + + + +/* + timer_get() + + Returns the timer and TSTR register addresses. + + @arg timer Timer id. + @arg tmu mod_tmu structure pointer address. + @arg tstr mod_tstr structure pointer address. +*/ +void timer_get(int timer, struct mod_tmu **tmu, unsigned char **tstr) +{ + // Using SH7705 information for SH-3-based MPUs. + if(MPU_CURRENT == MPU_SH7337 || MPU_CURRENT == MPU_SH7355) + { + if(tstr) *tstr = (unsigned char *)0xfffffe92; + if(tmu) *tmu = (struct mod_tmu *)0xfffffe94; + } + // Assuming SH7305 by default. + else + { + if(tstr) *tstr = (unsigned char *)0xa4490004; + if(tmu) *tmu = (struct mod_tmu *)0xa4490008; + } + + // Shifting tmu value to get to the timer-nth timer in the unit. + if(tmu) *tmu += timer; +} + +/* + timer_set() + Configures and starts a timer. + + @arg timer Timer identifier. + @arg delay Delay before expiration. + @arg prescaler Clock prescaler value. + @arg callback Callback function. + @arg repetitions Number of repetitions, 0 for infinite. +*/ +void timer_set(int timer, int delay, int prescaler, void (*callback)(void), + int repetitions) +{ + // Getting the timer address. Using a byte to alter TSTR. + struct mod_tmu *tmu; + unsigned char *tstr; + int byte = (1 << timer); + timer_get(timer, &tmu, &tstr); + + // Setting the constant register. + (*tmu).TCOR = delay; + // Loading the delay in the counter. + (*tmu).TCNT = delay; + + // Resetting underflow flag. + (*tmu).TCR.UNF = 0; + // Enabling interruptions on underflow. + (*tmu).TCR.UNIE = 1; + // Counting on rising edge. On SH7305 these two bits are reserved but + // writing 0 is ignored. + (*tmu).TCR.CKEG = 0; + // Setting the prescaler. + (*tmu).TCR.TPSC = prescaler; + + // Loading the structure information. + timers[timer].callback = callback; + timers[timer].repetitions = repetitions; + + // Starting the timer and returning. + *tstr |= byte; +} + +/* + timer_stop() + Stops the given timer. This function may be called even if the timer is + not running. + + @arg timer Timer to stop. +*/ +void timer_stop(int timer) +{ + // Getting TSTR address and the corresponding byte. + unsigned char *tstr; + int byte = (1 << timer); + timer_get(timer, NULL, &tstr); + + // Stopping the timer. + *tstr &= ~byte; +} + +/* + timer_interrupt() + Handles the interrupt for the given timer. + + @timer Timer that generated the interrupt. +*/ +void timer_interrupt(int timer) +{ + // Getting the timer address. + struct mod_tmu *tmu; + timer_get(timer, &tmu, NULL); + + // Resetting the interrupt flag. + (*tmu).TCR.UNF = 0; + + // Calling the callback function. + if(timers[timer].callback) timers[timer].callback(); + + // Reducing the number of repetitions left, if not infinite. + if(!timers[timer].repetitions) return; + // And stopping it if necessary. + if(timers[timer].repetitions == 1) timer_stop(timer); + else timers[timer].repetitions--; +} diff --git a/swords.bmp b/swords.bmp new file mode 100644 index 0000000000000000000000000000000000000000..695bc8d8093ea873847080f9dcc406fb1a4a743c GIT binary patch literal 5770 zcmd6pJ#HK^429*SbQ2&z>YT-mt26-?NReBl%?W&zy+e+X^Q`q~KKczK?MS03B6v*w zkRKnZ*|nd(y?iM)Uh41P_4!esPxW~yU(3g8OFjSm`1WAw<5o)il&9zKzj!ceKP1OW z1F@99KmX{{+gBf*(|?skPG;X*>lx?dsGimy{d=|-qugFB_AUN$xv0PGU9Z<0o<{Fm zycUOk!5d&Hhq-OfV|7k`%PL3DZQtv{T+dqbyPSb|FqIMcCo}}3=DW4-3i}Ip6`CeE z79)DF4xP6;2K8hP?_i?|d7RJlY|Y<$gYfB%UWmbh9}<_ ztZR=&zu+9ugIMM<4*Lrh&RFN*`v;bBJU-^aWj?j$$9IIW$UTwwA0FeqxA{9+{qsC@ zQxjO=(4sf#f1TDkIH-vObFeBKnCLO)9cGs!dWfg5vTRR!Ngv(kp2p=leas>u|5OP4&8Li8DV}p2Z_qx<{+XVv^QVIIhcNYL8RhxeuEj<4UxrfN_m8d0HIZi<;z47_WYu^UUrr$5>JCS{RdU zFkGjehaRa(&+|N3dRK@An+(yJ<8>V@#G;=VRznI(Qj4D3Mcfi zm)e`b_ZW<(hPn-$4S$d6H)BI1K$2^k@unm`Q#0}OH7y4?QG@Fb&R~z3AJ$^|&Mo#h zbf6|MSMTXSZ?v|k?@Z2QkF~$6dW?lT;yv+mHt=^L_hnU!@#e8!!lE)0LaHCJoS zzVPe}J!H224`H4W8(J3nf;_>Q=u1oAQ4Zpi7o0QdN^H$DGB+)iku{FQs?Yz-c?FEg BmC67B literal 0 HcmV?d00001 diff --git a/symbol.bmp b/symbol.bmp new file mode 100644 index 0000000000000000000000000000000000000000..74772f7625f807e0c91e1478a922af205d623c10 GIT binary patch literal 554 zcma)$F%Ez*3)cs6C;E;WY9*Tr zzmnP5vkE}V%XT#V_gjAx;&(gXkimUJX4+8A3o`Go+VC|J!g>I?uG$>-`28U)i(G>g dJ{*$Dlk+9)B5x>*LR~zJfYYMzT}cRPxDWe9w_E@K literal 0 HcmV?d00001 diff --git a/symbol2.bmp b/symbol2.bmp new file mode 100644 index 0000000000000000000000000000000000000000..949376085f1327189a40236ae9406bba668da36a GIT binary patch literal 242 zcmZ?r{lowPRX{2Sh}oc+kwF41Q2``{xgi)V03@8;gNk8nVreF<3jhEAk17rqfHT1I Xa2}We;Q>hq17-r82WMc&FfafBs%j;0 literal 0 HcmV?d00001 diff --git a/syscall_0x24a.txt b/syscall_0x24a.txt new file mode 100644 index 0000000..a85e926 --- /dev/null +++ b/syscall_0x24a.txt @@ -0,0 +1,190 @@ + +----------------------------------- +Syscall information +----------------------------------- + + Syscall id: 0x24a + Syscall address: 0x8003fc48 + + r4 {short *matrixcode} + + Syscall 0x24a redirects to 8003 f9d6. + + + +----------------------------------- +Stack +----------------------------------- + + ------ Pointer sent at <3f9fe> (also as first parameter) and <3fa12> + (12) Data from a44b 0000 + ------ Pointer sent at <3f9de> + (4) {short *matrixcode} at <3f9fe> + pr + r14 + ------ Bottom + + + +----------------------------------- +Syscall code +----------------------------------- + +# Saves some data. + 3f9d6: 2fe6 mov.l r14, @-r15 + 3f9d8: 4f22 sts.l pr, @-r15 + 3f9da: 7ff0 add #-16, r15 + +# First subroutine call: disables keyboard interrupts. +# Also pushes {short *matrixcode} on the stack. + 3f9dc: d128 mov.l #0x8003e264, r1 + 3f9de: 410b jsr @r1 + 3f9e0: 1f43 mov.l r4, @(12, r15) + +# Loads KEYSC data to the stack. +# Second subroutine call, with parameters : stack top and {short *matrixcode}. +# Checks whether the key given in the matrixcode was pressed. + 3f9e2: d528 mov.l #0xa44b0000, r5 + 3f9e4: 6451 mov.w @r5, r4 + 3f9e6: 2f41 mov.w r4, @r15 + 3f9e8: 64f3 mov r15, r4 + 3f9ea: 8551 mov.w @(2, r5), r0 + 3f9ec: 81f1 mov.w r0, @(2, r15) + 3f9ee: 8552 mov.w @(4, r5), r0 + 3f9f0: 81f2 mov.w r0, @(4, r15) + 3f9f2: 8553 mov.w @(6, r5), r0 + 3f9f4: 81f3 mov.w r0, @(6, r15) + 3f9f6: 8554 mov.w @(8, r5), r0 + 3f9f8: 81f4 mov.w r0, @(8, r15) + 3f9fa: 8555 mov.w @(10, r5), r0 + 3f9fc: 81f5 mov.w r0, @(10, r15) + 3f9fe: bd30 bsr <3f462> + 3fa00: 55f3 mov.l @(12, r15), r5 + +# Loads IPRF address to r6, sets the keyboard interrupt priority to 13 and +# performs a third subroutine call : enables keyboard interrupts (curiously +# the interrupt priority is set two times). +# Also saves the last subroutine result (key pressed) to r14. + 3fa02: d621 mov.l <3fa88>(#0xa4080014), r6 + 3fa04: 9113 mov.w <3fa2e>(#0x0fff), r1 + 3fa06: 9213 mov.w <3fa30>(#0xd000), r2 + 3fa08: 6e03 mov r0, r14 + 3fa0a: 6761 mov.w @r6, r7 + 3fa0c: 2719 and r1, r7 + 3fa0e: 272b or r2, r7 + 3fa10: d212 mov.l #0x8003e26c, r2 + 3fa12: 420b jsr @r2 + 3fa14: 2671 mov.w r7, @r6 + +# Restores the 'key pressed' result to r0, restores saved data and returns. + 3fa16: 60e3 mov r14, r0 + 3fa18: 7f10 add #16, r15 + 3fa1a: 4f26 lds.l @r15+, pr + 3fa1c: 000b rts + 3fa1e: 6ef6 mov.l @r15+, r14 + + + +----------------------------------- +Subroutine call at <3f9de> +- Denies keyboard interrupt in IMR5. +- Disables keyboard interrupt in IPRF. +----------------------------------- + + Call redirects to 8003 e278. + +# Loads IMR5 address to r4. + 3e278: d478 mov.l <3e45c>(#0xa4080094), r4 + 3e27a: e180 mov #-128, r1 + 3e27c: 955b mov.w <3e336>(#0x0fff), r5 +# Writes 0x80 to IMR5. + 3e27e: 2410 mov.b r1, @r4 + +# Loads IPRF address to r4. + 3e280: 7480 add #-128, r4 +# Sets the four upper bits of IPRF to 0. + 3e282: 6241 mov.w @r4, r2 + 3e284: 2259 and r5, r2 + 3e286: 000b rts + 3e288: 2421 mov.w r2, @r4 + + + +----------------------------------- +Subroutine call at <3f9fe> +- Detects whether a specific key is pressed using the KEYSC data loaded onto + the stack. +- Returns 1 if the key is pressed, 0 otherwise. +----------------------------------- + +Stack: + r4 (bottom stack address) + {short *matrixcode} + ------ Bottom stack address. + +# Pushes {short *matrixcode} on the stack and on top, pushes r4. +# Loads the key column to r2 and replaces r5 by #1 (r5 will then be shifted to +# be used as a mask in a KEYSC word). + 3f462: 7ff8 add #-8, r15 + 3f464: 1f51 mov.l r5, @(4, r15) + 3f466: 6250 mov.b @r5, r2 + 3f468: e501 mov #1, r5 + 3f46a: 51f1 mov.l @(4, r15), r1 + 3f46c: 622c extu.b r2, r2 + 3f46e: 2f42 mov.l r4, @r15 + +# Loads the key row to r0. + 3f470: 8411 mov.b @(1, r1), r0 + 3f472: 600c extu.b r0, r0 +# Shifts it right within r6 and tests the row's lower bit. + 3f474: 6603 mov r0, r6 + 3f476: 4621 shar r6 + 3f478: c801 tst #1, r0 + +# Shift r5 by its column number. +# If the row's lower bit was 1, then also shift r5 by 8 (because there are two +# rows for each word). + 3f47a: 8d01 bt/s <3f480> + 3f47c: 452d shld r2, r5 + 3f47e: 4518 shll8 r5 + +# Loads the bottom stack address to r1. + 3f480: 61f2 mov.l @r15, r1 +# Erases the even/odd bit of the row in r6 (because the KEYSC data is read in +# words so we don't want to get on a byte offset, and the lower bit has +# already been handled when shifting r5). + 3f482: 4600 shll r6 + +# Loads the final mask into r2. + 3f484: 625d extu.w r5, r2 + 3f486: 6063 mov r6, r0 +# Tests whether the key represented by the parameter matrixcode is pressed. + 3f488: 041d mov.w @(r0, r1), r4 + 3f48a: 2428 tst r2, r4 +# Returns the KEYSC bit, that is 1 if the key was pressed, 0 otherwise. + 3f48c: 0029 movt r0 + 3f48e: ca01 xor #1, r0 + 3f490: 000b rts + 3f492: 7f08 add #8, r15 + + + +----------------------------------- +Subroutine call at <3fa12> +- Sets the keyboard interrupt priority to 13. +- Clears the keyboard interrupt mask. +----------------------------------- + + Calls redirects to 8003 e28a. + + 3e28a: d475 mov.l #0xa4080014, r4 + 3e28c: 9753 mov.w #0x0fff, r7 + 3e28e: 9253 mov.w #0xd000, r2 + 3e290: e580 mov #-128, r5 + 3e292: 6141 mov.w @r4, r1 + 3e294: 2179 and r7, r1 + 3e296: 212b or r2, r1 + 3e298: 2411 mov.w r1, @r4 + 3e29a: d472 mov.l <3e464>(#0xa40800d4), r4 + 3e29c: 000b rts + 3e29e: 2450 mov.b r5, @r4 diff --git a/syscall_0x24a_7705.txt b/syscall_0x24a_7705.txt new file mode 100644 index 0000000..e1edb3b --- /dev/null +++ b/syscall_0x24a_7705.txt @@ -0,0 +1,558 @@ + +----------------------------------- +Syscall information +----------------------------------- + + Syscall id: 0x24a + Syscall address: 0x8003fa28 + + r4 {short *matrixcode} + + + +----------------------------------- +Stack +----------------------------------- + + #1 + pr + r8 + r9 + r10 + r11 + r12 + r13 + r14 + ------ Bottom + + + +----------------------------------- +Variables +----------------------------------- + + r14 Current row + r13 + r12 + r11 Delay routine address + r10 1 << r14 ? Could be the data register mask. + r9 Current column. + r8 {short *matrixcode} + + + +----------------------------------- +Syscall code +----------------------------------- + +# Saves r8-r14 to the stack. Loads data: +# r13 = 8, r12 = 1, r11 = 8003 e47a, r10 = 1, r9 = 0, r8 = {short *matrixcode}. + 3fa28: 2fe6 mov.l r14, @-r15 + 3fa2a: 2fd6 mov.l r13, @-r15 + 3fa2c: 2fc6 mov.l r12, @-r15 + 3fa2e: ed08 mov #8, r13 + 3fa30: 2fb6 mov.l r11, @-r15 + 3fa32: ec01 mov #1, r12 + 3fa34: 2fa6 mov.l r10, @-r15 + 3fa36: 6ac3 mov r12, r10 + 3fa38: 2f96 mov.l r9, @-r15 + 3fa3a: e900 mov #0, r9 + 3fa3c: db22 mov.l #0x8003e47a, r11 + 3fa3e: 2f86 mov.l r8, @-r15 + 3fa40: 4f22 sts.l pr, @-r15 + 3fa42: 6843 mov r4, r8 + +# r14 = 0, pushes #1 on the stack and branches to <3fb1a>. This branch does +# nothing if r14 is lower than 12 (thus here it does nothing). + 3fa44: 7ffc add #-4, r15 + 3fa46: 2fc2 mov.l r12, @r15 + 3fa48: a067 bra <3fb1a> + 3fa4a: 6e93 mov r9, r14 + +# Sets gbr = a400 0100: area where the port control registers are located. + 3fa4c: d21f mov.l #0xa4000100, r2 + 3fa4e: 421e ldc r2, gbr + +# B_CTRL = h'aaaa + 3fa50: d01f mov.l #0x0000aaaa, r0 + 3fa52: c101 mov.w r0, @(2, gbr) + +# M_CTRL = h'__55 + 3fa54: c50c mov.w @(24, gbr), r0 + 3fa56: d31f mov.l #0x0000ff00, r3 + 3fa58: 2039 and r3, r0 + 3fa5a: cbaa or #0x55, r0 + 3fa5c: c10c mov.w r0, @(24, gbr) + +# Delay. + 3fa5e: 4b0b jsr @r11 + 3fa60: e402 mov #2, r4 + +# Loads 801b e65c as a data segment base. It contains words in this order: +# aaa9 aaa6 aa9a aa6a ... 9aaa 6aaa 00a9 00a6 009a 006a. +# These words are the B_CTRL/M_CTRL values. +# Loads the r14-th word into B_CTRL (r14 <= 8) or the lowest byte of M_CTRL +# (r14 > 8). The row-to-check pin is configured as output, and all the others +# are configured as inputs. + 3fa62: 3ed3 cmp/ge r13, r14 + 3fa64: d41c mov.l #0x801be65c, r4 + 3fa66: 8904 bt <3fa72> + 3fa68: 60e3 mov r14, r0 + 3fa6a: 4000 shll r0 + 3fa6c: 004d mov.w @(r0, r4), r0 + 3fa6e: a009 bra <3fa84> + 3fa70: c101 mov.w r0, @(2, gbr) + 3fa72: 60e3 mov r14, r0 + 3fa74: 4000 shll r0 + 3fa76: 004d mov.w @(r0, r4), r0 + 3fa78: 6203 mov r0, r2 + 3fa7a: c50c mov.w @(24, gbr), r0 + 3fa7c: d315 mov.l #0x0000ff00, r3 + 3fa7e: 2039 and r3, r0 + 3fa80: 202b or r2, r0 + 3fa82: c10c mov.w r0, @(24, gbr) + +# Delay. + 3fa84: 4b0b jsr @r11 + 3fa86: e402 mov #2, r4 + +# Loads the port data registers section into gbr. + 3fa88: d314 mov.l #0xa4000120, r3 + 3fa8a: 431e ldc r3, gbr + 3fa8c: 3ed3 cmp/ge r13, r14 + 3fa8e: 8d27 bt/s <3fae0> + +# When the row-to-check is lower than or equal to 8. +# Writes ~r10 to B_DATA. Sets M_DATA to 0x-f. + 3fa90: 64a7 not r10, r4 + 3fa92: 6043 mov r4, r0 + 3fa94: c002 mov.b r0, @(2, gbr) + 3fa96: c418 mov.b @(24, gbr), r0 + 3fa98: c9f0 and #0xf0, r0 + 3fa9a: a026 bra <3faea> + 3fa9c: cb0f or #15, r0 + + 3fa9e: 0000 .word 0x0000 + 3faa0: 801b mov.b r0, @(11, r1) + 3faa2: e6dc mov #-36, r6 + 3faa4: 8006 mov.b r0, @(6, r0) + 3faa6: 9088 mov.w <3fbba>(#0xc10c), r0 + 3faa8: 8002 mov.b r0, @(2, r0) + 3faaa: 4e4e ldc r14, spc + 3faac: 8800 cmp/eq #0, r0 + 3faae: 77ed add #-19, r7 + 3fab0: 8003 mov.b r0, @(3, r0) + 3fab2: d446 mov.l <3fbcc>(#0x004da007), r4 + 3fab4: 8003 mov.b r0, @(3, r0) + 3fab6: da54 mov.l <3fc08>(#0x0000aaaa), r10 + 3fab8: 8003 mov.b r0, @(3, r0) + 3faba: de9c mov.l <3fd2c>(#0xee00eb0a), r14 + 3fabc: 8006 mov.b r0, @(6, r0) + 3fabe: 90d8 mov.w <3fc72>(#0x421e), r0 + 3fac0: 8006 mov.b r0, @(6, r0) + 3fac2: 2ed8 tst r13, r14 + 3fac4: 8800 cmp/eq #0, r0 + 3fac6: 7054 add #84, r0 + +# Data segment. + 3fac8: 8003 e47a + 3facc: a400 0100 + 3fad0: 0000 aaaa + 3fad4: 0000 ff00 + 3fad8: 801b e65c + 3fadc: a400 0120 + +# When the row-to-check is strictly greater than 8. +# Writes 0xff to B_DATA, and ~r10 to M_DATA. + 3fae0: 908e mov.w <3fc00>(#0x00ff), r0 + 3fae2: c002 mov.b r0, @(2, gbr) + 3fae4: c418 mov.b @(24, gbr), r0 + 3fae6: c9f0 and #0xf0, r0 + 3fae8: 204b or r4, r0 + +# Delay. + 3faea: c018 mov.b r0, @(24, gbr) + 3faec: 4b0b jsr @r11 + 3faee: e402 mov #2, r4 + +# Loads result in r5, as ~A_DATA. + 3faf0: c400 mov.b @(0, gbr), r0 + 3faf2: 6507 not r0, r5 + +# r9 = 0 the first time. Compares r12 with the result. If none of the bits of +# r12 are set in checked row, branches to <3fb04>. + 3faf4: 6493 mov r9, r4 + 3faf6: 635c extu.b r5, r3 + 3faf8: 23c8 tst r12, r3 + 3fafa: 8903 bt <3fb04> + +# r9 seems to be the key column and r14 the row. + 3fafc: 2840 mov.b r4, @r8 + 3fafe: 60e3 mov r14, r0 + 3fb00: a00f bra <3fb22> + 3fb02: 8081 mov.b r0, @(1, r8) + + 3fb04: 655c extu.b r5, r5 + 3fb06: 4501 shlr r5 + 3fb08: 7401 add #1, r4 + 3fb0a: 34d3 cmp/ge r13, r4 + 3fb0c: 8bf3 bf <3faf6> + 3fb0e: 4a00 shll r10 + 3fb10: 60e3 mov r14, r0 + 3fb12: 8807 cmp/eq #7, r0 + 3fb14: 8f01 bf/s <3fb1a> + 3fb16: 7e01 add #1, r14 + 3fb18: 6ac3 mov r12, r10 + 3fb1a: e30c mov #12, r3 + 3fb1c: 3e33 cmp/ge r3, r14 + 3fb1e: 8b95 bf <3fa4c> + 3fb20: 2f92 mov.l r9, @r15 + +# Ends the procedure and restores everything ? + +# B_CTRL = 0xaaaa. M_CTRL = 0x--aa. Delay. + 3fb22: d338 mov.l <3fc04>(#0xa4000100), r3 + 3fb24: d038 mov.l <3fc08>(#0x0000aaaa), r0 + 3fb26: 431e ldc r3, gbr + 3fb28: c101 mov.w r0, @(2, gbr) + 3fb2a: c50c mov.w @(24, gbr), r0 + 3fb2c: d237 mov.l <3fc0c>(#0x0000ff00), r2 + 3fb2e: 2029 and r2, r0 + 3fb30: cbaa or #0xaa, r0 + 3fb32: c10c mov.w r0, @(24, gbr) + 3fb34: 4b0b jsr @r11 + 3fb36: e402 mov #2, r4 + +# B_CTRL = 0x5555. M_CTRL = 0x--55. Delay. + 3fb38: 9063 mov.w <3fc02>(#0x5555), r0 + 3fb3a: c101 mov.w r0, @(2, gbr) + 3fb3c: c50c mov.w @(24, gbr), r0 + 3fb3e: d333 mov.l <3fc0c>(#0x0000ff00), r3 + 3fb40: 2039 and r3, r0 + 3fb42: cb55 or #0x55, r0 + 3fb44: c10c mov.w r0, @(24, gbr) + 3fb46: 4b0b jsr @r11 + 3fb48: e402 mov #2, r4 + +# B_DATA = 0x00. M_DATA = 0x-0. + 3fb4a: e000 mov #0, r0 + 3fb4c: d230 mov.l <3fc10>(#0xa4000120), r2 + 3fb4e: 421e ldc r2, gbr + 3fb50: c002 mov.b r0, @(2, gbr) + 3fb52: c418 mov.b @(24, gbr), r0 + 3fb54: c9f0 and #0xf0, r0 + 3fb56: c018 mov.b r0, @(24, gbr) + +# Returns the value at the top of the stack (originally 1). + 3fb58: 60f2 mov.l @r15, r0 + 3fb5a: 7f04 add #4, r15 + 3fb5c: 4f26 lds.l @r15+, pr + 3fb5e: 68f6 mov.l @r15+, r8 + 3fb60: 69f6 mov.l @r15+, r9 + 3fb62: 6af6 mov.l @r15+, r10 + 3fb64: 6bf6 mov.l @r15+, r11 + 3fb66: 6cf6 mov.l @r15+, r12 + 3fb68: 6df6 mov.l @r15+, r13 + 3fb6a: 000b rts + 3fb6c: 6ef6 mov.l @r15+, r14 + + + +----------------------------------- +First subroutine +----------------------------------- + +# Go back to <3fa4c> (first call to this subroutine) until r14 reaches 12. + 3fb1a: e30c mov #12, r3 + 3fb1c: 3e33 cmp/ge r3, r14 + 3fb1e: 8b95 bf <3fa4c> + + 3fb20: 2f92 mov.l r9, @r15 + 3fb22: d338 mov.l <3fc04>(#0xa4000100), r3 + 3fb24: d038 mov.l <3fc08>(#0x0000aaaa), r0 + 3fb26: 431e ldc r3, gbr + 3fb28: c101 mov.w r0, @(2, gbr) + 3fb2a: c50c mov.w @(24, gbr), r0 + 3fb2c: d237 mov.l <3fc0c>(#0x0000ff00), r2 + 3fb2e: 2029 and r2, r0 + 3fb30: cbaa or #-86, r0 + 3fb32: c10c mov.w r0, @(24, gbr) + 3fb34: 4b0b jsr @r11 + 3fb36: e402 mov #2, r4 + 3fb38: 9063 mov.w <3fc02>(#0x5555), r0 + 3fb3a: c101 mov.w r0, @(2, gbr) + 3fb3c: c50c mov.w @(24, gbr), r0 + 3fb3e: d333 mov.l <3fc0c>(#0x0000ff00), r3 + 3fb40: 2039 and r3, r0 + 3fb42: cb55 or #85, r0 + 3fb44: c10c mov.w r0, @(24, gbr) + 3fb46: 4b0b jsr @r11 + 3fb48: e402 mov #2, r4 + 3fb4a: e000 mov #0, r0 + 3fb4c: d230 mov.l <3fc10>(#0xa4000120), r2 + 3fb4e: 421e ldc r2, gbr + 3fb50: c002 mov.b r0, @(2, gbr) + 3fb52: c418 mov.b @(24, gbr), r0 + 3fb54: c9f0 and #-16, r0 + 3fb56: c018 mov.b r0, @(24, gbr) + 3fb58: 60f2 mov.l @r15, r0 + 3fb5a: 7f04 add #4, r15 + 3fb5c: 4f26 lds.l @r15+, pr + 3fb5e: 68f6 mov.l @r15+, r8 + 3fb60: 69f6 mov.l @r15+, r9 + 3fb62: 6af6 mov.l @r15+, r10 + 3fb64: 6bf6 mov.l @r15+, r11 + 3fb66: 6cf6 mov.l @r15+, r12 + 3fb68: 6df6 mov.l @r15+, r13 + 3fb6a: 000b rts + 3fb6c: 6ef6 mov.l @r15+, r14 + + + +----------------------------------- +Second subroutine +Just a delay ! If also configures IRQ0. +----------------------------------- + +Stack: + gbr + macl + pr + r14 + ------ Bottom + +# Saves r14, pr, macl and gbr to the stack. Saves parameter (r4 = 2) to r14. +# Loads data: r2 = 8006 31dc, r5 = 1 (parameter 'set' for syscall 0x3ed). + 3e47a: 0312 stc gbr, r3 + 3e47c: d235 mov.l <3e554>(#0x800631dc), r2 + 3e47e: e501 mov #1, r5 + 3e480: 2fe6 mov.l r14, @-r15 + 3e482: 6e43 mov r4, r14 + 3e484: 4f22 sts.l pr, @-r15 + 3e486: 4f12 sts.l macl, @-r15 + 3e488: 7ffc add #-4, r15 + 3e48a: 2f32 mov.l r3, @r15 + +# Sets the "watchdog occupied" status in the RAM interrupt status byte. + 3e48c: 420b jsr @r2 + 3e48e: e410 mov #16, r4 + +# Ensures 1 <= r14 <= 40 by setting r14 = 1 or 40 if needed. +# Here r14 is always 2. + 3e490: 4e15 cmp/pl r14 + 3e492: 8d01 bt/s <3e498> + 3e494: e428 mov #40, r4 + 3e496: ee01 mov #1, r14 + 3e498: 3e47 cmp/gt r4, r14 + 3e49a: 8b00 bf <3e49e> + 3e49c: 6e43 mov r4, r14 + +# r4 = ~((r14 * 92) >> 4) on a single byte, which is 244 when r14 = 2. +# 256 - r4 will be used as a delay for the watchdog timer. +# Sets gbr to 0xfffffee0 and disables the watchdog interrupt. + 3e49e: e45c mov #92, r4 + 3e4a0: 924b mov.w <3e53a>(#0xfee0), r2 + 3e4a2: e3fc mov #-4, r3 + 3e4a4: 0e47 mul.l r4, r14 + 3e4a6: 421e ldc r2, gbr + 3e4a8: 041a sts macl, r4 + 3e4aa: 443c shad r3, r4 + 3e4ac: 6447 not r4, r4 + 3e4ae: 644c extu.b r4, r4 + 3e4b0: c502 mov.w @(4, gbr), r0 + 3e4b2: 9343 mov.w <3e53c>(#0x0fff), r3 + 3e4b4: 2039 and r3, r0 + 3e4b6: c102 mov.w r0, @(4, gbr) + +# Loads the watchdog module base address into gbr. Resets everything in the +# watchdog configuration. Then loads r4 (here 244) to the counter, sets the +# frequency at Po/256, starts the timer and waits until it overflows. +# This is probably just a way of delaying port usage. + 3e4b8: d027 mov.l <3e558>(#0x0000a500), r0 + 3e4ba: e180 mov #-128, r1 + 3e4bc: 411e ldc r1, gbr + 3e4be: c103 mov.w r0, @(6, gbr) + 3e4c0: 903d mov.w <3e53e>(#0x5a00), r0 + 3e4c2: 204b or r4, r0 + 3e4c4: c102 mov.w r0, @(4, gbr) + 3e4c6: d025 mov.l <3e55c>(#0x0000a505), r0 + 3e4c8: c103 mov.w r0, @(6, gbr) + 3e4ca: d025 mov.l <3e560>(#0x0000a585), r0 + 3e4cc: c103 mov.w r0, @(6, gbr) + 3e4ce: e408 mov #8, r4 + 3e4d0: c406 mov.b @(6, gbr), r0 + 3e4d2: 600c extu.b r0, r0 + 3e4d4: 2048 tst r4, r0 + 3e4d6: 89fb bt <3e4d0> + +# Resets the overflow flag, then resets the whole configuration. + 3e4d8: c406 mov.b @(6, gbr), r0 + 3e4da: 600c extu.b r0, r0 + 3e4dc: d31e mov.l <3e558>(#0x0000a500), r3 + 3e4de: c9f7 and #0xf7, r0 + 3e4e0: 203b or r3, r0 + 3e4e2: c103 mov.w r0, @(6, gbr) + 3e4e4: 6033 mov r3, r0 + 3e4e6: c103 mov.w r0, @(6, gbr) + +# Resets the counter. + 3e4e8: 9029 mov.w <3e53e>(#0x5a00), r0 + 3e4ea: c102 mov.w r0, @(4, gbr) + +# Unsets the "watchdog occupied" bit in the RAM interrupt status byte. + 3e4ec: d219 mov.l <3e554>(#0x800631dc), r2 + 3e4ee: e500 mov #0, r5 + 3e4f0: 420b jsr @r2 + 3e4f2: e410 mov #16, r4 + +# Configures IRQ0 if possible (that is, if both the watchdog and the SD card +# are idle). + 3e4f4: d31b mov.l <3e564>(#0x8003dbec), r3 + 3e4f6: 430b jsr @r3 + 3e4f8: 0009 nop + +# Un-stacks everything and returns. + 3e4fa: 62f2 mov.l @r15, r2 + 3e4fc: 421e ldc r2, gbr + 3e4fe: 7f04 add #4, r15 + 3e500: 4f16 lds.l @r15+, macl + 3e502: 4f26 lds.l @r15+, pr + 3e504: 000b rts + 3e506: 6ef6 mov.l @r15+, r14 + + + +----------------------------------- +Third subroutine +----------------------------------- + +# Erases the lowest bit in an unknown byte (probably an extension of the +# interrupt status byte). Returns if this bit was 0. + 3dbec: 4f22 sts.l pr, @-r15 + 3dbee: e501 mov #1, r5 + 3dbf0: d349 mov.l <3dd18>(#0x80063236), r3 + 3dbf2: 430b jsr @r3 + 3dbf4: 6453 mov r5, r4 + 3dbf6: 8801 cmp/eq #1, r0 + 3dbf8: 8b07 bf <3dc0a> + +# Checks if the watchdog timer is occupied, or if the SD-card is busy (?). +# If any of them is in use, aborts. Otherwise controls is transferred to the +# extract below, as if called directly. +# Probably the SD-card has something to do with generating IRQ0 interrupts. + 3dbfa: e500 mov #0, r5 + 3dbfc: d347 mov.l <3dd1c>(#0x800631f6), r3 + 3dbfe: 430b jsr @r3 + 3dc00: e418 mov #24, r4 + 3dc02: 2008 tst r0, r0 + 3dc04: 8b01 bf <3dc0a> + 3dc06: af82 bra <3db0e> + 3dc08: 4f26 lds.l @r15+, pr + +# Returns. + 3dc0a: 4f26 lds.l @r15+, pr + 3dc0c: 000b rts + 3dc0e: 0009 nop + + Here's the extract of code that takes control when the interrupt conditions + required by the main procedure are fulfilled. + It configures IRQ0 interrupts. + +# Disables IRQ0 interrupt in IPRC. + 3db0e: d32d mov.l <3dbc4>(#0xa4000000), r3 + 3db10: 431e ldc r3, gbr + 3db12: c50b mov.w @(22, gbr), r0 + 3db14: d22e mov.l <3dbd0>(#0x0000fff0), r2 + 3db16: 2029 and r2, r0 + 3db18: c10b mov.w r0, @(22, gbr) + +# Sets PTH0 to 'other functions' mode, which is IRQ0 and IRL0 input for the +# interrupt controller. + 3db1a: d12e mov.l <3dbd4>(#0xa4000100), r1 + 3db1c: 411e ldc r1, gbr + 3db1e: c507 mov.w @(14, gbr), r0 + 3db20: d32d mov.l <3dbd8>(#0x0000fffc), r3 + 3db22: 2039 and r3, r0 + 3db24: c107 mov.w r0, @(14, gbr) + +# Sets IRQ0 detection mode to low level input in ICR1. + 3db26: d227 mov.l <3dbc4>(#0xa4000000), r2 + 3db28: 421e ldc r2, gbr + 3db2a: c508 mov.w @(16, gbr), r0 + 3db2c: 2039 and r3, r0 + 3db2e: cb02 or #2, r0 + 3db30: c108 mov.w r0, @(16, gbr) + +# Clears the IRQ0 interrupt flag. + 3db32: c404 mov.b @(4, gbr), r0 + 3db34: c9fe and #-2, r0 + 3db36: c004 mov.b r0, @(4, gbr) + +# Enables IRQ0 interrupts with priority 13 in IPRC. + 3db38: c50b mov.w @(22, gbr), r0 + 3db3a: d125 mov.l <3dbd0>(#0x0000fff0), r1 + 3db3c: 2019 and r1, r0 + 3db3e: cb0d or #13, r0 + 3db40: c10b mov.w r0, @(22, gbr) + +# Returns 1. + 3db42: 000b rts + 3db44: e001 mov #1, r0 + + Here is the first subroutine, that handles an unknown byte at 0x8800713d. + This byte may be an extension of the interrupt status byte. + + Returns 1 if a bit in the unknown byte matches the mask r4. + Also, if r5 = 1, erases the bits according to the mask. + +# Sets r6 to 1 if a bit in the mask is set in the unknown byte, 0 otherwise. +# Also sets r0 = r5. + 63236: 624c extu.b r4, r2 + 63238: d72a mov.l <632e4>(#0x8800713d), r7 + 6323a: 6370 mov.b @r7, r3 + 6323c: 633c extu.b r3, r3 + 6323e: 2328 tst r2, r3 + 63240: 8f02 bf/s <63248> + 63242: 6053 mov r5, r0 + 63244: a001 bra <6324a> + 63246: e600 mov #0, r6 + 63248: e601 mov #1, r6 + +# Uses the mask comparison result as return value. If the operation is 0, then +# stop there. + 6324a: 8801 cmp/eq #1, r0 + 6324c: 8f04 bf/s <63258> + 6324e: 6063 mov r6, r0 + +# Otherwise, erase the mask in the unknown byte. + 63250: 6370 mov.b @r7, r3 + 63252: 6447 not r4, r4 + 63254: 2349 and r4, r3 + 63256: 2730 mov.b r3, @r7 + 63258: 000b rts + 6325a: 0009 nop + + Here is the second subroutine. + It does exactly the same as the first, except that it uses the common + interrupt status byte at 0x8800713c. + It's the syscall 0x3ee. + + 631f6: 624c extu.b r4, r2 + 631f8: d739 mov.l <632e0>(#0x8800713c), r7 + 631fa: 6370 mov.b @r7, r3 + 631fc: 633c extu.b r3, r3 + 631fe: 2328 tst r2, r3 + 63200: 8f02 bf/s <63208> + 63202: 6053 mov r5, r0 + 63204: a001 bra <6320a> + 63206: e600 mov #0, r6 + 63208: e601 mov #1, r6 + 6320a: 8801 cmp/eq #1, r0 + 6320c: 8f04 bf/s <63218> + 6320e: 6063 mov r6, r0 + 63210: 6370 mov.b @r7, r3 + 63212: 6447 not r4, r4 + 63214: 2349 and r4, r3 + 63216: 2730 mov.b r3, @r7 + 63218: 000b rts + 6321a: 0009 nop diff --git a/syscall_0x24b.txt b/syscall_0x24b.txt new file mode 100644 index 0000000..035600b --- /dev/null +++ b/syscall_0x24b.txt @@ -0,0 +1,91 @@ +fxos lephe$ fxos disasm -s 0x24b sh7305.fls -l 200 +Syscall table: 0x801cdd84 +Syscall id: 0x24b +Syscall address: 0x4cfc0380 + +=================================== + +Stack state: +r4 s1: Parameter of call located at +pr +r14 +r13 +------ Bottom + +=================================== + +Missing information: + +- Function of (negative offset relative to ) +- Function of (negative offset relative to <3e>, does not seem to be + the same as , curiously) +- Function of <8003e8c8> (probably syscall, but not found) + +=================================== + +# +# Initialization. +# + +# Saves the registers. + 0: 2fd6 mov.l r13, @-r15 + 2: 2fe6 mov.l r14, @-r15 + 4: 4f22 sts.l pr, @-r15 + 6: 7ffc add #-4, r15 + 8: 2f42 mov.l r4, @r15 + +# Loads 1 into r13. If jump at <16> is performed, r13 is changed to 0. + a: ed01 mov #1, r13 +# r14 gets decremented whenever call at is looped (considering the +# documentation, it is probably the number of tries before the function +# gives up). + c: ee05 mov #5, r14 + + + +# +# Main loop, calls . No more than initial_r14 turns. +# + +# Calls (r4). + e: bf19 bsr + 10: 64f2 mov.l @r15, r4 + +# If result != 0, then <1a>, else <24>. + 12: 2008 tst r0, r0 + 14: 8b01 bf <1a> + 16: a005 bra <24> + 18: ed00 mov #0, r13 + + + +# +# When returns non-zero, calls <8003e8c8>, decrements r14 and +# loops. +# + +# Call returned non-zero (r13 = 1). +# Calls <8003e8c8>(10). + 1a: d22d mov.l (0x8003e8c8), r2 + 1c: 420b jsr @r2 + 1e: e40a mov #10, r4 +# Decrementing the number of tries before returning, and looping to if the +# number of tries has not been exceeded. + 20: 4e10 dt r14 + 22: 8bf4 bf + + + +# +# When returns zero or the number of tries has been exceeded, +# return from the function with the correct value. +# + +# Call returned zero (r13 = 0). Ends the function and returns 1 if +# the key is pressed, 0 otherwise. + 24: 60d3 mov r13, r0 + 26: 7f04 add #4, r15 + 28: 4f26 lds.l @r15+, pr + 2a: 6ef6 mov.l @r15+, r14 + 2c: 000b rts + 2e: 6df6 mov.l @r15+, r13