Initial commit. Includes interrupt handler, drivers for keyboard and screen, and parts of the drawing library.

This commit is contained in:
lephe 2016-05-05 11:49:05 +02:00
commit 0a7f22d4c1
43 changed files with 7074 additions and 0 deletions

13
.gitignore vendored Normal file
View file

@ -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

119
Makefile Normal file
View file

@ -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

7
TODO Normal file
View file

@ -0,0 +1,7 @@
- screen, display
- timer
- gray engine
- full rtc driver (time)
_ 7305.h
_ libc

311
addin.c Normal file
View file

@ -0,0 +1,311 @@
#include <stdlib.h>
#include <string.h>
#include <mpu.h>
#include <keyboard.h>
#include <display.h>
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<end_y ; i++)
{
if(begin_x < end_x)
{
line = bmp[i*(real_width>>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<end_x ; j++)
{
line = bmp[i*(real_width>>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<<real_width-width;
line <<= shift;
if(begin_x < end_x || bool1) screen[end_x] |= *p;
if(bool2) screen[end_x+1] |= *(p+1);
screen += 16;
}
}
*/
#include <stdint.h>
#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;
}

BIN
addin.g1a Normal file

Binary file not shown.

104
addin.ld Normal file
View file

@ -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
}

BIN
icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

914
include/7305.h Normal file
View file

@ -0,0 +1,914 @@
#ifndef _7305_H
#define _7305_H 1
/*
Double-underscore prefixed structures (e.g. __st_rtc_counter) are used
internally but are not meant to be used in user programs.
Underscore-prefixed names (e.g. _R64CNT) indicate write-only registers.
*/
#pragma pack(push, 1)
#define gap(n) unsigned: n << 3
//---
// Real-time clock.
//---
struct _st_rtc
{
unsigned char const R64CNT;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned :1;
unsigned TENS :3;
unsigned ONES :4;
};
} RSECCNT;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned :1;
unsigned TENS :3;
unsigned ONES :4;
};
} RMINCNT;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned :2;
unsigned TENS :2;
unsigned ONES :4;
};
} RHRCNT;
gap(1);
// 0=Sunday, 1=Monday, ..., 6=Saturday, 7=Reserved (prohibited).
unsigned char RWKCNT;
gap(1);
union {
unsigned char;
struct {
unsigned :2;
unsigned TENS :2;
unsigned ONES :4;
};
} RDAYCNT;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned :3;
unsigned TENS :1;
unsigned ONES :4;
};
} RMONCNT;
gap(1);
union {
unsigned short WORD;
struct {
unsigned THOUSANDS :4;
unsigned HUNDREDS :4;
unsigned TENS :4;
unsigned ONES :4;
};
} RYRCNT;
union {
unsigned char BYTE;
struct {
unsigned ENB :1;
unsigned TENS :3;
unsigned ONES :4;
};
} RSECAR;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned ENB :1;
unsigned TENS :3;
unsigned ONES :4;
};
} RMINAR;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned ENB :1;
unsigned :1;
unsigned TENS :2;
unsigned ONES :4;
};
} RHRAR;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned ENB :1;
unsigned :4;
// See RTC.RDAYCNT for day encoding.
unsigned DAY :3;
};
} RWKAR;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned ENB :1;
unsigned :1;
unsigned TENS :2;
unsigned ONES :4;
};
} RDAYAR;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned ENB :1;
unsigned :2;
unsigned TENS :1;
unsigned ONES :4;
};
} RMONAR;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned CF :1;
unsigned :2;
unsigned CIE :1;
unsigned AIE :1;
unsigned :2;
unsigned AF :1;
};
} RCR1;
gap(1);
union {
unsigned char BYTE;
struct {
unsigned PEF :1;
unsigned PES :3;
unsigned :1;
unsigned ADJ :1;
unsigned RESET :1;
unsigned START :1;
};
} RCR2;
gap(1);
union {
unsigned short WORD;
struct {
unsigned THOUSANDS :4;
unsigned HUNDREDS :4;
unsigned TENS :4;
unsigned ONES :4;
};
} RYRAR;
gap(2);
union {
unsigned char BYTE;
struct {
unsigned ENB :1;
unsigned :7;
}
} RCR3;
} __attribute__((packed));
//---
// Interrupt controller, part 1.
//---
struct _st_intc
{
union {
unsigned short WORD;
struct {
unsigned const NMIL :1;
unsigned MAI :1;
unsigned :4;
unsigned NMIB :1;
unsigned NMIE :1;
unsigned :2;
unsigned LVLMODE :1;
unsigned :5;
};
} ICR0;
char gap1[14];
union {
unsigned int LONG;
struct {
unsigned IRQ0 :4;
unsigned IRQ1 :4;
unsigned IRQ2 :4;
unsigned IRQ3 :4;
unsigned IRQ4 :4;
unsigned IRQ5 :4;
unsigned IRQ6 :4;
unsigned IRQ7 :4;
};
} INTPRI00;
char gap2[8];
union {
unsigned short WORD;
struct {
unsigned IRQ0S :2;
unsigned IRQ1S :2;
unsigned IRQ2S :2;
unsigned IRQ3S :2;
unsigned IRQ4S :2;
unsigned IRQ5S :2;
unsigned IRQ6S :2;
unsigned IRQ7S :2;
};
} ICR1;
char gap3[6];
union {
unsigned char BYTE;
struct {
unsigned IRQ0 :1;
unsigned IRQ1 :1;
unsigned IRQ2 :1;
unsigned IRQ3 :1;
unsigned IRQ4 :1;
unsigned IRQ5 :1;
unsigned IRQ6 :1;
unsigned IRQ7 :1;
};
} INTREQ00;
char gap4[31];
union {
unsigned char BYTE;
struct {
unsigned IRQ0 :1;
unsigned IRQ1 :1;
unsigned IRQ2 :1;
unsigned IRQ3 :1;
unsigned IRQ4 :1;
unsigned IRQ5 :1;
unsigned IRQ6 :1;
unsigned IRQ7 :1;
};
} INTMSK00;
char gap5[31];
union {
unsigned char BYTE;
struct {
unsigned _IRQ0 :1;
unsigned _IRQ1 :1;
unsigned _IRQ2 :1;
unsigned _IRQ3 :1;
unsigned _IRQ4 :1;
unsigned _IRQ5 :1;
unsigned _IRQ6 :1;
unsigned _IRQ7 :1;
};
} INTMSKCLR00;
char gap6[91];
union {
unsigned short WORD;
struct {
unsigned const NMIL :1;
unsigned :14;
unsigned NMIFL :1;
};
} NMIFCR;
char gap7[6029118];
union {
unsigned int LONG;
struct {
unsigned HEXA_A5 :8;
unsigned :16;
unsigned UIMASK :4;
unsigned :4;
};
} USERIMSK;
} __attribute__((packed));
//---
// Interrupt controller, part 2.
//---
struct _st_intx
{
union {
unsigned short WORD;
struct {
unsigned TMU0_0 :4;
unsigned TMU0_1 :4;
unsigned TMU0_2 :4;
unsigned IrDA :4;
};
} IPRA;
gap(2);
union {
unsigned short WORD;
struct {
unsigned JPU :4;
unsigned LCDC :4;
unsigned DMAC1A :4;
unsigned BEU2_1 :4;
};
} IPRB;
gap(2);
union {
unsigned short WORD;
struct {
unsigned TMU1_0 :4;
unsigned TMU1_1 :4;
unsigned TMU1_2 :4;
unsigned SPU :4;
};
} IPRC;
gap(2);
union {
unsigned short WORD;
struct {
unsigned :4;
unsigned MMCIF :4;
unsigned :4;
unsigned ATAPI :4;
};
} IPRD;
gap(2);
union {
unsigned short WORD;
struct {
unsigned DMAC0A :4;
unsigned VARIOUS :4;
unsigned SCIFA3 :4;
unsigned VPU5F :4;
};
} IPRE;
gap(2);
union {
unsigned short WORD;
struct {
unsigned KEYSC :4;
unsigned DMAC0B :4;
unsigned USB01 :4;
unsigned CMT :4;
};
} IPRF;
gap(2);
union {
unsigned short WORD;
struct {
unsigned SCIF0 :4;
unsigned SCIF1 :4;
unsigned SCIF2 :4;
unsigned VEU3F0 :4;
};
} IPRG;
gap(2);
union {
unsigned short WORD;
struct {
unsigned MSIOF0 :4;
unsigned MSIOF1 :4;
unsigned I2C1 :4;
unsigned I2C0 :4;
};
} IPRH;
gap(2);
union {
unsigned short WORD;
struct {
unsigned SCIFA4 :4;
unsigned ICB :4;
unsigned TSIF :4;
unsigned _2DG_ICB :4;
};
} IPRI;
gap(2);
union {
unsigned short WORD;
struct {
unsigned CEU2_1 :4;
unsigned EtherMAC :4;
unsigned FSI :4;
unsigned SDHI1 :4;
};
} IPRJ;
gap(2);
union {
unsigned short WORD;
struct {
unsigned _RTC :4;
unsigned DMAC1B :4;
unsigned ICB :4;
unsigned SDHI0 :4;
};
} IPRK;
gap(2);
union {
unsigned short WORD;
struct {
unsigned SCIFA5 :4;
unsigned :4;
unsigned TPU :4;
unsigned _2DDMAC :4;
};
} IPRL;
char gap1[82];
union
{
unsigned char BYTE;
struct {
unsigned :1;
unsigned TUNI2 :1;
unsigned TUNI1 :1;
unsigned TUNI0 :1;
unsigned SDHII3 :1;
unsigned SDHII2 :1;
unsigned SDHII1 :1;
unsigned SDHII0 :1;
};
} IMR0;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned VOUI :1;
unsigned VEU1I :1;
unsigned BEU0I :1;
unsigned CEUOI :1;
unsigned DEI3 :1;
unsigned DEI2 :1;
unsigned DEI1 :1;
unsigned DEI0 :1;
};
} IMR1;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :3;
unsigned VPUI :1;
unsigned ATAPI :1;
unsigned EtherMAC :1;
unsigned :1;
unsigned SCIFA0 :1;
};
} IMR2;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned DEI3 :1;
unsigned DEI2 :1;
unsigned DEI1 :1;
unsigned DEI0 :1;
unsigned :3;
unsigned IRDAI :1;
};
} IMR3;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :1;
unsigned TUNI2 :1;
unsigned TUNI1 :1;
unsigned TUNI0 :1;
unsigned JPUI :1;
unsigned :2;
unsigned LCDCI :1;
};
} IMR4;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned KEYI :1;
unsigned DADERR :1;
unsigned DEI5 :1;
unsigned DEI4 :1;
unsigned VEU0I :1;
unsigned SCIF2 :1;
unsigned SCIF1 :1;
unsigned SCIF0 :1;
};
} IMR5;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :2;
unsigned ICBI :1;
unsigned SCIFA4 :1;
unsigned CEU1I :1;
unsigned :1;
unsigned MSIOFI0 :1;
unsigned MSIOFI1 :1;
};
} IMR6;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned DTE0I :1;
unsigned WAITOI :1;
unsigned TACK0I :1;
unsigned AL0I :1;
unsigned DTE1I :1;
unsigned WAIT1I :1;
unsigned TACK1I :1;
unsigned AL1I :1;
};
} IMR7;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned SDHII3 :1;
unsigned SDHII2 :1;
unsigned SDHII1 :1;
unsigned SDHII0 :1;
unsigned :2;
unsigned SCFIA5 :1;
unsigned FSI :1;
};
} IMR8;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :3;
unsigned CMTI :1;
unsigned :1;
unsigned USI1 :1;
unsigned USI0 :1;
unsigned :1;
};
} IMR9;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :1;
unsigned DADERR :1;
unsigned DEI5 :1;
unsigned DEI4 :1;
unsigned :1;
unsigned ATI :1;
unsigned PRI :1;
unsigned CUI :1;
};
} IMR10;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned BRK :1;
unsigned CEI :1;
unsigned INI :1;
unsigned TRI :1;
unsigned :1;
unsigned TPUI :1;
unsigned LMBI :1;
unsigned TSIFI :1;
};
} IMR11;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :7;
unsigned _2DDMAC :1;
};
} IMR12;
char gap2[15];
union
{
unsigned char BYTE;
struct {
unsigned :1;
unsigned TUNI2 :1;
unsigned TUNI1 :1;
unsigned TUNI0 :1;
unsigned SDHII3 :1;
unsigned SDHII2 :1;
unsigned SDHII1 :1;
unsigned SDHII0 :1;
};
} _IMCR0;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned VOUI :1;
unsigned VEU1I :1;
unsigned BEU0I :1;
unsigned CEUOI :1;
unsigned DEI3 :1;
unsigned DEI2 :1;
unsigned DEI1 :1;
unsigned DEI0 :1;
};
} _IMCR1;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :3;
unsigned VPUI :1;
unsigned ATAPI :1;
unsigned EtherMAC :1;
unsigned :1;
unsigned SCIFA0 :1;
};
} _IMCR2;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned DEI3 :1;
unsigned DEI2 :1;
unsigned DEI1 :1;
unsigned DEI0 :1;
unsigned :3;
unsigned IRDAI :1;
};
} _IMCR3;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :1;
unsigned TUNI2 :1;
unsigned TUNI1 :1;
unsigned TUNI0 :1;
unsigned JPUI :1;
unsigned :2;
unsigned LCDCI :1;
};
} _IMCR4;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned KEYI :1;
unsigned DADERR :1;
unsigned DEI5 :1;
unsigned DEI4 :1;
unsigned VEU0I :1;
unsigned SCIF2 :1;
unsigned SCIF1 :1;
unsigned SCIF0 :1;
};
} _IMCR5;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :2;
unsigned ICBI :1;
unsigned SCIFA4 :1;
unsigned CEU1I :1;
unsigned :1;
unsigned MSIOFI0 :1;
unsigned MSIOFI1 :1;
};
} _IMCR6;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned DTE0I :1;
unsigned WAITOI :1;
unsigned TACK0I :1;
unsigned AL0I :1;
unsigned DTE1I :1;
unsigned WAIT1I :1;
unsigned TACK1I :1;
unsigned AL1I :1;
};
} _IMCR7;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned SDHII3 :1;
unsigned SDHII2 :1;
unsigned SDHII1 :1;
unsigned SDHII0 :1;
unsigned :2;
unsigned SCFIA5 :1;
unsigned FSI :1;
};
} _IMCR8;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :3;
unsigned CMTI :1;
unsigned :1;
unsigned USI1 :1;
unsigned USI0 :1;
unsigned :1;
};
} _IMCR9;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :1;
unsigned DADERR :1;
unsigned DEI5 :1;
unsigned DEI4 :1;
unsigned :1;
unsigned ATI :1;
unsigned PRI :1;
unsigned CUI :1;
};
} _IMCR10;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned BRK :1;
unsigned CEI :1;
unsigned INI :1;
unsigned TRI :1;
unsigned :1;
unsigned TPUI :1;
unsigned LMBI :1;
unsigned TSIFI :1;
};
} _IMCR11;
gap(3);
union
{
unsigned char BYTE;
struct {
unsigned :7;
unsigned _2DDMAC :1;
};
} _IMCR12;
} __attribute__((packed));
#define RTC (*(volatile struct _st_rtc *)0xa413fec0)
#define INTC (*(volatile struct _st_intc *)0xa4140000)
#define INTX (*(volatile struct _st_intx *)0xa4080000)
#pragma pack(pop)
#endif // _7305_H

1296
include/7705.h Normal file

File diff suppressed because it is too large Load diff

181
include/display.h Normal file
View file

@ -0,0 +1,181 @@
#ifndef _DISPLAY_H
#define _DISPLAY_H 1
//---
// Types and constants.
//---
enum Color
{
Color_White = 0, // White (AND 0)
Color_Black = 1, // Black (OR 1)
Color_None = 2, // Transparent (NOP)
Color_Invert = 3, // Invert (XOR 1)
};
/*
enum BlendingMode
Describes the various blending modes available for drawing images.
Blending modes may be combined for special effects! For instance:
- Or Only sets black pixels.
- And Only erases white pixels.
- Or | And Fully draws the bitmap.
- Invert Inverts pixels where the bitmap is black.
- Or | Invert Erases black pixels.
Adding Checker to an existing combination limits the operation to
pixels (x, y) that satisfy (x + y) & 1 == 1.
Operations are done in the following order : Or - Invert - And.
*/
enum BlendingMode
{
Blend_Or = 0x01,
Blend_Invert = 0x02,
Blend_And = 0x04,
Blend_Checker = 0x10,
};
enum ImageFormat
{
ImageFormat_Mono = 0x01,
ImageFormat_Gray = 0x02,
ImageFormat_Alpha = 0x10,
ImageFormat_ColorMask = 0x0f,
};
struct Image
{
unsigned char width;
unsigned char height;
unsigned char format;
// Ensures data is 4-aligned.
unsigned char gap;
const unsigned char __attribute__((aligned(4))) data[];
} __attribute__((aligned(4)));
typedef struct Image Image;
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 64
//---
// Generic functions.
//---
/*
display_getLocalVRAM()
Returns the local video ram. This address should not be used directly
when running the gray engine.
@return Video ram address of the monochrome display module.
*/
void *display_getLocalVRAM(void);
/*
display_useVRAM()
Changes the current video ram address. Expects a *4-aligned* 1024-byte
buffer.
Do not use this function while running the gray engine.
@arg New video ram address.
*/
void display_useVRAM(void *vram);
//---
// Global drawing functions.
//---
/*
dupdate()
Displays the vram on the physical screen.
*/
void dupdate(void);
/*
dclear()
Clears the whole video ram.
*/
void dclear(void);
/*
dclear_area()
Clears an area of the video ram.
@arg x1
@arg y1
@arg x2
@arg y2
*/
void dclear_area(int x1, int y1, int x2, int y2);
/*
dreverse_area()
Reverses an area of the screen.
@arg x1
@arg y1
@arg x2
@arg y2
*/
void dreverse_area(int x1, int y1, int x2, int y2);
//---
// 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);
/*
dline()
Draws a line on the screen. Automatically optimizes horizontal and
vertical lines.
Uses an algorithm written by PierrotLL for MonochromeLib.
@arg x1
@arg y1
@arg x2
@arg y2
@arg color
*/
void dline(int x1, int y1, int x2, int y2, enum Color color);
//---
// Image drawing.
//---
/*
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);
#endif // _DISPLAY_H

97
include/gint.h Normal file
View file

@ -0,0 +1,97 @@
#ifndef _GINT_H
#define _GINT_H 1
//---
// Public API.
//---
/*
gint_getVBR()
Returns the current vbr address.
@return vbr address currently in use.
*/
unsigned int gint_getVBR(void);
/*
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);
//---
// Internal API.
// Referenced here for documentation purposes only.
// Do NOT call these functions, you'll most probably screw up the whole
// interrupt handling system.
//---
/*
gint_setVBR()
Sets the vbr address and does some configuration while interrupts are
disabled.
@arg new_vbr_address
@arg setup Will be called for configuration under interrupt-safe
environment.
*/
void gint_setVBR(unsigned int new_vbr_address, void (*setup)(void));
/*
gint_init()
Initializes gint. Loads the interrupt handler into the memory and sets
the new vbr address.
*/
void gint_init(void);
/*
gint_quit()
Stops gint. Restores the system's configuration and vbr address.
*/
void gint_quit(void);
/*
gint_setup()
Configures interrupt priorities and some parameters to allow gint to
take control of the interrupt flow.
*/
void gint_setup_7705(void);
void gint_setup_7305(void);
/*
gint_stop()
Un-configures the interrupt flow to give back the interrupt control to
the system.
*/
void gint_stop_7705(void);
void gint_stop_7305(void);
/*
gint()
Handles interrupts.
*/
void gint(void) __attribute__((
section(".gint.int.entry"),
interrupt_handler
));
void gint_7705(void) __attribute__((section(".gint.int")));
void gint_7305(void) __attribute__((section(".gint.int")));
//---
// Internal priority definitions.
//---
#define GINT_INTP_WDT 4
#define GINT_INTP_RTC 9
#define GINT_INTP_KEY 8
#endif // _GINT_H

57
include/gray.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef _GRAY_H
#define _GRAY_H 1
//---
// Public API.
//---
/*
gray_start()
Starts the gray engine. The control of the screen is transferred to the
gray engine.
*/
void gray_start(void);
/*
gray_stop()
Stops the gray engine. The monochrome display system takes control of
the video ram.
*/
void gray_stop(void);
/*
gray_lightVRAM()
Returns the module's gray vram address.
*/
void *gray_lightVRAM(void);
/*
gray_lightVRAM()
Returns the module's dark vram address.
*/
void *gray_darkVRAM(void);
//---
// Internal API.
// Referenced here for documentation purposes only. Do not call.
//--
/*
gray_swap()
Swaps the vram buffers.
*/
void gray_swap(void);
/*
gray_interrupt()
Answers a timer interrupt. Swaps the two buffers.
*/
void gray_interrupt(void) __attribute__((section(".gint.int")));
/*
gray_init()
Initializes the gray engine.
*/
void gray_init(void) __attribute__((constructor));
#endif // _GRAY_H

254
include/keyboard.h Normal file
View file

@ -0,0 +1,254 @@
#ifndef _KEYBOARD_H
#define _KEYBOARD_H 1
//---
// Keycodes and related.
//---
// The following codes are gint matrix codes. They are not compatible with the
// system's.
#define KEY_F1 0x69
#define KEY_F2 0x59
#define KEY_F3 0x49
#define KEY_F4 0x39
#define KEY_F4 0x39
#define KEY_F5 0x29
#define KEY_F6 0x19
#define KEY_SHIFT 0x68
#define KEY_OPTN 0x58
#define KEY_VARS 0x48
#define KEY_MENU 0x38
#define KEY_LEFT 0x28
#define KEY_UP 0x18
#define KEY_ALPHA 0x67
#define KEY_SQUARE 0x57
#define KEY_POWER 0x47
#define KEY_EXIT 0x37
#define KEY_DOWN 0x27
#define KEY_RIGHT 0x17
#define KEY_XOT 0x66
#define KEY_LOG 0x56
#define KEY_LN 0x46
#define KEY_SIN 0x36
#define KEY_COS 0x26
#define KEY_TAN 0x16
#define KEY_FRAC 0x65
#define KEY_FD 0x55
#define KEY_LEFTP 0x45
#define KEY_RIGHTP 0x35
#define KEY_COMMA 0x25
#define KEY_ARROW 0x15
#define KEY_7 0x64
#define KEY_8 0x54
#define KEY_9 0x44
#define KEY_DEL 0x34
#define KEY_AC_ON 0x24
#define KEY_4 0x63
#define KEY_5 0x53
#define KEY_6 0x43
#define KEY_MUL 0x33
#define KEY_DIV 0x23
#define KEY_1 0x62
#define KEY_2 0x52
#define KEY_3 0x42
#define KEY_PLUS 0x32
#define KEY_MINUS 0x22
#define KEY_0 0x61
#define KEY_DOT 0x51
#define KEY_EXP 0x41
#define KEY_NEG 0x31
#define KEY_EXE 0x21
// Key modifiers.
#define MOD_SHIFT 0x80
#define MOD_ALPHA 0x100
#define MOD_CLEAR ~(MOD_SHIFT | MOD_ALPHA)
// Key events.
#define KEY_NONE 0x00
#define KEY_NOEVENT 0xff
//---
// Keyboard configuration.
//---
/*
enum KeyboardFrequency
Describes the various frequencies available for the keyboard analysis.
Default frequency is 16 Hz (system frequency is about 40 Hz). Very few
applications will need to change this setting.
Be aware that you will miss key hits at low frequencies.
At high frequencies, you will lose important execution power. Some
loop-driven programs will freeze at 64 Hz because they will never
leave the interrupt handling routine. SH3 also have difficulties with
64 Hz.
*/
enum KeyboardFrequency {
KeyFreq_500mHz = 7,
KeyFreq_1Hz = 6,
KeyFreq_2Hz = 5,
KeyFreq_4Hz = 4,
KeyFreq_16Hz = 3,
KeyFreq_64Hz = 2,
KeyFreq_256Hz = 1,
};
/*
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);
/*
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.
Set to 0 to disable repetition. If first = 0, no repetition will be
allowed. If first != 0 and next = 0, only one repetition will be
allowed.
@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);
//---
// Keyboard access.
//---
/*
enum GetKeyOpt
Options available for use with getkey_opt().
*/
enum GetkeyOpt
{
Getkey_NoOption = 0x00,
// Return KEY_NONE when a key is released.
Getkey_ReleaseEvent = 0x01,
// Consider [SHIFT] and [ALPHA] as modifiers instead of returning
// KEY_SHIFT and KEY_ALPHA.
Getkey_ShiftModifier = 0x02,
Getkey_AlphaModifier = 0x04,
// Key repetition. Notice that modifiers will never be repeated.
Getkey_RepeatArrowKeys = 0x10,
Getkey_RepeatCharKeys = 0x20,
Getkey_RepeatCtrlKeys = 0x40,
Getkey_RepeatFuncKeys = 0x80,
// Shorthand for the four previous properties.
Getkey_RepeatAllKeys = 0xf0,
};
/*
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);
/*
getkey()
Blocking function with auto-repeat and SHIFT modifying functionalities.
Reproduces the behavior of the system's GetKey().
@return Pressed key matrix code.
*/
int getkey(void);
/*
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);
/*
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_NONE 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);
//---
// Key analysis.
//---
enum KeyType
{
KeyType_Arrow = 1,
KeyType_Character = 2,
KeyType_Control = 4,
KeyType_Function = 8,
};
/*
keyid()
Returns a non-matrix key code that can be used for array subscript.
Ignores modifiers.
@arg key
@return Modified keycode.
*/
int keyid(int key);
/*
keychar()
Returns the ASCII character associated with a character key ; 0 for
other keys.
@arg key
@return Key character.
*/
int keychar(int key);
/*
keytype()
Returns a key's type. Ignores modifiers.
@arg key
@return Key type.
*/
enum KeyType keytype(int key);
#endif // _KEYBOARD_H

52
include/mpu.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef _MPU_H
#define _MPU_H 1
//---
// enum MPU
// This type holds information about the calculator's MPU.
//---
enum MPU
{
MPU_Unknown = 0,
// fx-9860G SH3.
MPU_SH7337 = 1,
// fx-9860G II SH3.
MPU_SH7355 = 2,
// fx-9860G II SH4.
MPU_SH7305 = 3,
// Just for reference.
MPU_SH7724 = 4
};
//---
// MPU type tests.
// Prefer using an 'if(isSH3()) { ... } else { ... }' alternative for best
// results.
//---
// Global MPU variable, accessible for direct tests.
extern enum MPU MPU_CURRENT;
// Quick SH3 test. It is safer to assume that an unknown model is SH4 because
// SH3-based models are not produced anymore.
#define isSH3() (MPU_CURRENT == MPU_SH7337 || MPU_CURRENT == MPU_SH7355)
#define isSH4() !isSH3()
//---
// Public API.
//---
/*
getMPU()
Determines the MPU type and returns it. MPU_CURRENT is not updated.
@return MPU type.
*/
enum MPU getMPU(void);
#endif // _MPU_H

12
include/screen.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef _SCREEN_H
#define _SCREEN_H 1
/*
screen_display()
Displays contents on the full screen. Expects a 1024-byte buffer.
@arg vram 1024-byte video buffer.
*/
void screen_display(const void *vram);
#endif

31
include/setjmp.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef _SETJMP_H
#define _SETJMP_H 1
// There are 16 CPU registers that *must* be saved to ensure a basically
// safe jump.
typedef unsigned int jmp_buf[16];
//---
// Long jump functions.
//---
/*
setjmp()
Configures a jump by saving data to the given jump buffer.
@arg env Empty jump buffer.
*/
int setjmp(jmp_buf env);
/*
longjmp()
Performs a long jump.
@arg env Jump buffer configure with setjmp().
@arg value setjmp() will return this integer after the jump.
*/
void longjmp(jmp_buf env, int value);
#endif // _SETJMP_H

31
include/stdlib.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef _STDLIB_H
#define _STDLIB_H 1
#include <stddef.h>
// 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

32
include/string.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef _STRING_H
#define _STRING_H 1
#include <stddef.h>
//---
// 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

73
include/timer.h Normal file
View file

@ -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

16
info Normal file
View file

@ -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 %

BIN
libc.a Normal file

Binary file not shown.

BIN
libgint.a Normal file

Binary file not shown.

BIN
sprites.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

145
src/crt0.c Normal file
View file

@ -0,0 +1,145 @@
// Used by the exit()-family functions to save and restore the execution state.
#include <setjmp.h>
static jmp_buf env;
// Provides EXIT_SUCCESS and EXIT_FAILURE.
#include <stdlib.h>
// Provides gint initialization functionalities.
#include <gint.h>
// Provides memset() and memcpy().
#include <string.h>
// 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);
}

715
src/display.c Normal file
View file

@ -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 <screen.h>
#include <display.h>
#include <string.h>
#include <stdint.h>
#include <gray.h>
// 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;
}
}

107
src/gint.c Normal file
View file

@ -0,0 +1,107 @@
#include <gint.h>
#include <mpu.h>
#include <gray.h>
//---
// 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);
}

292
src/gint_7305.c Normal file
View file

@ -0,0 +1,292 @@
#include <gint.h>
#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;
}

244
src/gint_7705.c Normal file
View file

@ -0,0 +1,244 @@
#include <gint.h>
#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();
}

71
src/gint_vbr.s Normal file
View file

@ -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)

76
src/gray.c Normal file
View file

@ -0,0 +1,76 @@
#include <display.h>
#include <gray.h>
#include <screen.h>
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];
}

444
src/keyboard.c Normal file
View file

@ -0,0 +1,444 @@
#include <keyboard.h>
#include <mpu.h>
//---
// 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;
}

75
src/mpu.c Normal file
View file

@ -0,0 +1,75 @@
#include <mpu.h>
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();
}

51
src/screen.c Normal file
View file

@ -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.h>
/*
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++;
}
}

82
src/setjmp.s Normal file
View file

@ -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

134
src/string.c Normal file
View file

@ -0,0 +1,134 @@
#include <string.h>
#include <stdint.h>
//---
// 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;
}

37
src/syscalls.s Normal file
View file

@ -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

162
src/timer.c Normal file
View file

@ -0,0 +1,162 @@
#include <timer.h>
#include <mpu.h>
#include <stddef.h>
/*
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--;
}

BIN
swords.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
symbol.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

BIN
symbol2.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

190
syscall_0x24a.txt Normal file
View file

@ -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

558
syscall_0x24a_7705.txt Normal file
View file

@ -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

91
syscall_0x24b.txt Normal file
View file

@ -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 <e>
pr
r14
r13
------ Bottom
===================================
Missing information:
- Function of <fffffe44> (negative offset relative to <e>)
- Function of <fffffdde> (negative offset relative to <3e>, does not seem to be
the same as <fffffe44>, 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 <e> 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 <fffffe44>. No more than initial_r14 turns.
#
# Calls <fffffe44>(r4).
e: bf19 bsr <fffffe44>
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 <fffffe44> returns non-zero, calls <8003e8c8>, decrements r14 and
# loops.
#
# Call <fffffe44> returned non-zero (r13 = 1).
# Calls <8003e8c8>(10).
1a: d22d mov.l <d0>(0x8003e8c8), r2
1c: 420b jsr @r2
1e: e40a mov #10, r4
# Decrementing the number of tries before returning, and looping to <e> if the
# number of tries has not been exceeded.
20: 4e10 dt r14
22: 8bf4 bf <e>
#
# When <fffffe44> returns zero or the number of tries has been exceeded,
# return from the function with the correct value.
#
# Call <fffffe44> 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