Re-structured files. Minor name changes. No code changed in this falsely huge commit.
148
Makefile
|
@ -1,141 +1,11 @@
|
|||
#! /usr/bin/make -f
|
||||
## Temporary things there
|
||||
|
||||
#---
|
||||
# fx-9860g lib Makefile.
|
||||
#---
|
||||
# C source file template:
|
||||
# $1 module name
|
||||
# $2 file base name
|
||||
define rule-c-source
|
||||
build/$1_$2.c.o: src/$1/$2.c
|
||||
$(cc) $(cflags) -c $$< -o $$@
|
||||
endef
|
||||
|
||||
|
||||
|
||||
#
|
||||
# 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 = ginttest.g1a
|
||||
bin = build/ginttest.bin
|
||||
elf = build/ginttest.elf
|
||||
|
||||
# Command-line options
|
||||
cflags = -m3 -mb -nostdlib -ffreestanding -W -Wall \
|
||||
-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 tales.c \
|
||||
bopti.c
|
||||
hea-lib = 7305.h 7705.h gint.h \
|
||||
stdlib.h \
|
||||
mpu.h keyboard.h screen.h display.h gray.h timer.h tales.h
|
||||
obj-lib = $(patsubst %, build/%.o, $(src-lib))
|
||||
hdr-lib = $(patsubst %, include/%, $(hea-lib))
|
||||
|
||||
# Standard library.
|
||||
src-std = setjmp.s string.c
|
||||
hea-std = setjmp.h string.h ctype.h
|
||||
obj-std = $(patsubst %, build/%.o, $(src-std))
|
||||
hdr-std = $(patsubst %, include/%, $(hea-str))
|
||||
|
||||
# Test application.
|
||||
src-app = ginttest.c
|
||||
img-app = bitmap_opt.bmp swords.bmp sprites.bmp symbol.bmp symbol2.bmp \
|
||||
illustration.bmp
|
||||
res-app = $(patsubst %, build/%.o, $(img-app)) build/font.o
|
||||
|
||||
|
||||
#
|
||||
# Building rules.
|
||||
#
|
||||
|
||||
all: build libgint.a libc.a ginttest.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"
|
||||
|
||||
$(g1a): libgint.a $(src-app) $(res-app)
|
||||
$(cc) $(src-app) $(res-app) -T ginttest.ld -o $(elf) $(cflags) $(lib)
|
||||
$(ob) -R .comment -R .bss -O binary $(elf) $(bin)
|
||||
$(wr) $(bin) -o ginttest.g1a -i icon.bmp
|
||||
@ echo "\033[32;1mBinary file size: "`stat -c %s $(bin)`" bytes\033[0m"
|
||||
@ sh3eb-elf-objdump -h build/ginttest.elf
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Resource management.
|
||||
#
|
||||
|
||||
build/%.c.o: src/%.c $(hdr-lib) $(hdr-std)
|
||||
$(cc) $(cflags) -O2 -c $< -o $@
|
||||
|
||||
build/%.s.o: src/%.s
|
||||
$(as) -c $^ -o $@
|
||||
|
||||
build/%.bmp.o: resources/%.bmp
|
||||
fxconv $^ -o $@ --preview
|
||||
|
||||
build/font.o: resources/font.bmp
|
||||
fxconv --font $^ -o $@
|
||||
|
||||
# File gint.c should not be optimized... looks like attribute((interrupt_
|
||||
# handler)) doesn't like it. (It could be a gint bug also, I should check.)
|
||||
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 $@
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Cleaning rules.
|
||||
#
|
||||
|
||||
clean:
|
||||
@ rm -f $(obj-lib) $(obj-std) $(obj-app) $(res-app)
|
||||
@ rm -f $(bin) $(elf)
|
||||
mrproper: clean
|
||||
@ rm -f build/*
|
||||
@ rm -f ginttest.g1a libc.a libgint.a
|
||||
distclean: mrproper
|
||||
|
||||
re: distclean all
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Installing shorthand.
|
||||
#
|
||||
|
||||
install:
|
||||
usb-connector SEND ginttest.g1a ginttest.g1a fls0
|
||||
|
||||
|
||||
|
||||
.PHONY: all clean mrproper distclean re install
|
||||
$(eval $(call rule-c-source,display,area))
|
||||
|
|
141
Makefile.old
Normal file
|
@ -0,0 +1,141 @@
|
|||
#! /usr/bin/make -f
|
||||
|
||||
#---
|
||||
# fx-9860g lib Makefile.
|
||||
#---
|
||||
|
||||
|
||||
|
||||
#
|
||||
# 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 = ginttest.g1a
|
||||
bin = build/ginttest.bin
|
||||
elf = build/ginttest.elf
|
||||
|
||||
# Command-line options
|
||||
cflags = -m3 -mb -nostdlib -ffreestanding -W -Wall \
|
||||
-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 tales.c \
|
||||
bopti.c
|
||||
hea-lib = 7305.h 7705.h gint.h \
|
||||
stdlib.h \
|
||||
mpu.h keyboard.h screen.h display.h gray.h timer.h tales.h
|
||||
obj-lib = $(patsubst %, build/%.o, $(src-lib))
|
||||
hdr-lib = $(patsubst %, include/%, $(hea-lib))
|
||||
|
||||
# Standard library.
|
||||
src-std = setjmp.s string.c
|
||||
hea-std = setjmp.h string.h ctype.h
|
||||
obj-std = $(patsubst %, build/%.o, $(src-std))
|
||||
hdr-std = $(patsubst %, include/%, $(hea-str))
|
||||
|
||||
# Test application.
|
||||
src-app = ginttest.c
|
||||
img-app = bitmap_opt.bmp swords.bmp sprites.bmp symbol.bmp symbol2.bmp \
|
||||
illustration.bmp
|
||||
res-app = $(patsubst %, build/%.o, $(img-app)) build/font.o
|
||||
|
||||
|
||||
#
|
||||
# Building rules.
|
||||
#
|
||||
|
||||
all: build libgint.a libc.a ginttest.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"
|
||||
|
||||
$(g1a): libgint.a $(src-app) $(res-app)
|
||||
$(cc) $(src-app) $(res-app) -T ginttest.ld -o $(elf) $(cflags) $(lib)
|
||||
$(ob) -R .comment -R .bss -O binary $(elf) $(bin)
|
||||
$(wr) $(bin) -o ginttest.g1a -i icon.bmp
|
||||
@ echo "\033[32;1mBinary file size: "`stat -c %s $(bin)`" bytes\033[0m"
|
||||
@ sh3eb-elf-objdump -h build/ginttest.elf
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Resource management.
|
||||
#
|
||||
|
||||
build/%.c.o: src/%.c $(hdr-lib) $(hdr-std)
|
||||
$(cc) $(cflags) -O2 -c $< -o $@
|
||||
|
||||
build/%.s.o: src/%.s
|
||||
$(as) -c $^ -o $@
|
||||
|
||||
build/%.bmp.o: resources/%.bmp
|
||||
fxconv $^ -o $@ --preview
|
||||
|
||||
build/font.o: resources/font.bmp
|
||||
fxconv --font $^ -o $@
|
||||
|
||||
# File gint.c should not be optimized... looks like attribute((interrupt_
|
||||
# handler)) doesn't like it. (It could be a gint bug also, I should check.)
|
||||
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 $@
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Cleaning rules.
|
||||
#
|
||||
|
||||
clean:
|
||||
@ rm -f $(obj-lib) $(obj-std) $(obj-app) $(res-app)
|
||||
@ rm -f $(bin) $(elf)
|
||||
mrproper: clean
|
||||
@ rm -f build/*
|
||||
@ rm -f ginttest.g1a libc.a libgint.a
|
||||
distclean: mrproper
|
||||
|
||||
re: distclean all
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Installing shorthand.
|
||||
#
|
||||
|
||||
install:
|
||||
usb-connector SEND ginttest.g1a ginttest.g1a fls0
|
||||
|
||||
|
||||
|
||||
.PHONY: all clean mrproper distclean re install
|
16
README.md
|
@ -44,3 +44,19 @@ This will build the following components :
|
|||
* `ginttest.g1a`, a test application
|
||||
|
||||
The common `clean`, `mrproper`, and `distclean` rules will clean the directory.
|
||||
|
||||
|
||||
|
||||
Source organization
|
||||
-------------------
|
||||
|
||||
gint is made of *modules*. Each module has its own source directory (which is
|
||||
`/src/module`), and its associated header file in `/include`. A module folder
|
||||
contains three types of files :
|
||||
* Internal headers: shared only among the files of a module
|
||||
* Single-function source files: to avoid linking against the whole library,
|
||||
some functions have their own object files. Their names are the function
|
||||
names.
|
||||
* Other source files: contain multiple functions that always work together, or
|
||||
are lightweight enough not to be separated. Their names often begin with
|
||||
`module_`.
|
||||
|
|
2
TODO
|
@ -12,6 +12,8 @@
|
|||
@ vram overflow
|
||||
@ keyboard test threading interface
|
||||
|
||||
+ use alloca() for tales
|
||||
+ call exit handlers
|
||||
+ compute frequencies
|
||||
+ gray text
|
||||
+ effective rtc callback
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <mpu.h>
|
||||
#include <keyboard.h>
|
||||
#include <display.h>
|
||||
#include <timer.h>
|
||||
#include <gray.h>
|
||||
#include <tales.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <7305.h>
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 554 B After Width: | Height: | Size: 554 B |
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 242 B |
BIN
ginttest.g1a
|
@ -1,15 +1,22 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: interrupt handler
|
||||
//
|
||||
// Central point of the library. Controls the interrupt handler and
|
||||
// defines a few functions to configure callbacks for some interrupts.
|
||||
//
|
||||
//---
|
||||
|
||||
#ifndef _GINT_H
|
||||
#define _GINT_H 1
|
||||
|
||||
//---
|
||||
// Public API.
|
||||
// Interrupt handler control.
|
||||
//---
|
||||
|
||||
/*
|
||||
gint_getVBR()
|
||||
Returns the current vbr address.
|
||||
|
||||
@return vbr address currently in use.
|
||||
*/
|
||||
unsigned int gint_getVBR(void);
|
||||
|
||||
|
@ -17,11 +24,15 @@ 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);
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Callback API.
|
||||
//---
|
||||
|
||||
/*
|
||||
enum RTCFrequency
|
||||
Describes the possible frequencies available for the real-time clock
|
||||
|
@ -42,9 +53,6 @@ enum RTCFrequency
|
|||
gint_setRTCCallback()
|
||||
Sets the callback function for the real-time clock interrupt. If
|
||||
frequency is non-NULL, the clock frequency is set to the given value.
|
||||
|
||||
@arg callback Callback function.
|
||||
@arg frequency Interrupt frequency.
|
||||
*/
|
||||
void gint_setRTCCallback(void (*callback)(void), enum RTCFrequency frequency);
|
||||
|
||||
|
@ -66,12 +74,8 @@ void (*gint_getRTCCallback(enum RTCFrequency *frequency))(void);
|
|||
|
||||
/*
|
||||
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.
|
||||
Sets the vbr address and calls the configuration function while
|
||||
interrupts are disabled.
|
||||
*/
|
||||
void gint_setVBR(unsigned int new_vbr_address, void (*setup)(void));
|
||||
|
||||
|
@ -108,16 +112,14 @@ 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")));
|
||||
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")));
|
||||
|
||||
/*
|
||||
gint_setRTCFrequency()
|
||||
Sets the RTC interrupt frequency and enables interrupts.
|
||||
|
||||
@arg frequency
|
||||
*/
|
||||
void gint_setRTCFrequency_7705(enum RTCFrequency frequency);
|
||||
void gint_setRTCFrequency_7305(enum RTCFrequency frequency);
|
||||
|
@ -125,8 +127,6 @@ void gint_setRTCFrequency_7305(enum RTCFrequency frequency);
|
|||
/*
|
||||
gint_getRTCFrequency()
|
||||
Returns the RTC interrupt frequency.
|
||||
|
||||
@return RTC interrupt frequency.
|
||||
*/
|
||||
enum RTCFrequency gint_getRTCFrequency_7705(void);
|
||||
enum RTCFrequency gint_getRTCFrequency_7305(void);
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: keyboard analyzer
|
||||
//
|
||||
// Probably the most difficult hardware interaction. There is very few
|
||||
// documentation on how the system actually analyzes the keyboard. While
|
||||
// disassembling syscalls reveals the following procedure (which was
|
||||
// already documented by SimonLothar), there is nothing about the
|
||||
// detection problems of the multi-getkey system.
|
||||
//
|
||||
//---
|
||||
|
||||
#ifndef _KEYBOARD_H
|
||||
#define _KEYBOARD_H 1
|
||||
|
||||
|
@ -85,29 +97,25 @@
|
|||
|
||||
/*
|
||||
keyboard_setFrequency()
|
||||
Sets the keyboard frequency. The default frequency is 32 Hz. Very few
|
||||
Very few applications will need to change this setting.
|
||||
Sets the keyboard frequency. The default frequency is 32 Hz. The unit
|
||||
for the argument is Hz. Very few applications will need to change this
|
||||
setting.
|
||||
The actual frequency is guaranteed to be greater than the argument.
|
||||
Be aware that you will miss key hits at low frequencies. At higher
|
||||
frequencies, you will lose important execution power.
|
||||
|
||||
@arg frequency Frequency in Hz (1 Hz = 1 event / second).
|
||||
*/
|
||||
// Currently not implemented.
|
||||
// void keyboard_setFrequency(int frequency);
|
||||
|
||||
/*
|
||||
keyboard_setRepeatRate()
|
||||
Sets the default repeat rate for key events. The unit for the argument
|
||||
is the keyboard period. For example at 32 Hz, values of (20, 4) will
|
||||
imitate the system default.
|
||||
Sets the default repeat rate for key events. The delay before the first
|
||||
repeat may have a different value (usually longer). The unit for the
|
||||
argument is the keyboard period. For example at 32 Hz, values of
|
||||
(20, 4) will imitate the system default.
|
||||
Set to 0 to disable repetition. If first = 0, no repetition will be
|
||||
allowed. If first != 0 and next = 0, only one repetition will be
|
||||
allowed.
|
||||
|
||||
@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);
|
||||
|
||||
|
@ -146,10 +154,6 @@ enum GetkeyOpt
|
|||
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);
|
||||
|
||||
|
@ -161,8 +165,6 @@ int keylast(int *repeat_count);
|
|||
editing the array. It wouldn't influence the behavior of the keyboard
|
||||
functions, but the buffer data is very volatile. Therefore, data
|
||||
written to the buffer could be replaced anytime.
|
||||
|
||||
@return 10-byte keyboard state buffer.
|
||||
*/
|
||||
volatile unsigned char *keystate(void);
|
||||
|
||||
|
@ -222,10 +224,6 @@ enum KeyType
|
|||
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);
|
||||
|
||||
|
@ -233,20 +231,12 @@ int keyid(int key);
|
|||
keychar()
|
||||
Returns the ASCII character associated with a character key ; 0 for
|
||||
other keys.
|
||||
|
||||
@arg key
|
||||
|
||||
@return Associated character.
|
||||
*/
|
||||
int keychar(int key);
|
||||
|
||||
/*
|
||||
keytype()
|
||||
Returns a key's type. Ignores modifiers.
|
||||
|
||||
@arg key
|
||||
|
||||
@return Key type.
|
||||
*/
|
||||
enum KeyType keytype(int key);
|
||||
|
||||
|
@ -267,8 +257,6 @@ void keyboard_interrupt(void) __attribute__((section(".gint.int")));
|
|||
/*
|
||||
keyboard_updateState()
|
||||
Updates the keyboard state.
|
||||
|
||||
@arg state 10-byte state buffer.
|
||||
*/
|
||||
void keyboard_updateState_7705(volatile unsigned char *state)
|
||||
__attribute__((section(".gint.int")));
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: mpu
|
||||
//
|
||||
// Determines which kind of MPU is running the program. This module only
|
||||
// provides the following test:
|
||||
//
|
||||
// if(isSH3()) { ... }
|
||||
// else { ... }
|
||||
//
|
||||
//---
|
||||
|
||||
#ifndef _MPU_H
|
||||
#define _MPU_H 1
|
||||
|
||||
//---
|
||||
// enum MPU
|
||||
// This type holds information about the calculator's MPU.
|
||||
//---
|
||||
|
||||
/*
|
||||
enum MPU
|
||||
This type holds information about the calculator's MPU.
|
||||
*/
|
||||
enum MPU
|
||||
{
|
||||
MPU_Unknown = 0,
|
||||
|
@ -19,15 +30,8 @@ enum MPU
|
|||
MPU_SH7724 = 4
|
||||
};
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// MPU type tests.
|
||||
// Prefer using an 'if(isSH3()) { ... } else { ... }' alternative for best
|
||||
// results.
|
||||
//---
|
||||
|
||||
// Global MPU variable, accessible for direct tests.
|
||||
// Global MPU variable, accessible for direct tests. Initialized at the
|
||||
// beginning of execution.
|
||||
extern enum MPU MPU_CURRENT;
|
||||
|
||||
// Quick SH3 test. It is safer to assume that an unknown model is SH4 because
|
||||
|
@ -44,8 +48,6 @@ extern enum MPU MPU_CURRENT;
|
|||
/*
|
||||
getMPU()
|
||||
Determines the MPU type and returns it. MPU_CURRENT is not updated.
|
||||
|
||||
@return MPU type.
|
||||
*/
|
||||
enum MPU getMPU(void);
|
||||
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
//---
|
||||
//
|
||||
// gint display module: screen
|
||||
//
|
||||
// Interacts with the physical screen. See other display modules for video
|
||||
// ram management and drawing.
|
||||
//
|
||||
// The screen basically has two input values, which are a register
|
||||
// selector and the selected register's value. What this module does is
|
||||
// essentially selecting registers by setting *selector and assigning them
|
||||
// values by setting *data.
|
||||
//---
|
||||
|
||||
#ifndef _SCREEN_H
|
||||
#define _SCREEN_H 1
|
||||
|
||||
//---
|
||||
// Public API.
|
||||
//---
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
//---
|
||||
//
|
||||
// gint drawing module: tales
|
||||
//
|
||||
// Text displaying. Does some good optimization, though requires dynamic
|
||||
// allocation.
|
||||
//
|
||||
//---
|
||||
|
||||
#ifndef _TALES_H
|
||||
#define _TALES_H 1
|
||||
|
||||
|
@ -81,22 +90,21 @@ typedef struct Font Font;
|
|||
//---
|
||||
|
||||
/*
|
||||
print_configure()
|
||||
text_configure()
|
||||
Sets the font and mode to use for the following print operations.
|
||||
|
||||
@arg font
|
||||
@arg mode
|
||||
*/
|
||||
void print_configure(struct Font *font);
|
||||
void text_configure(struct Font *font);
|
||||
|
||||
/*
|
||||
print_raw()
|
||||
dtext()
|
||||
Prints the given string, without any analysis.
|
||||
|
||||
@arg str
|
||||
@arg x
|
||||
@arg y
|
||||
*/
|
||||
void print_raw(const char *str, int x, int y);
|
||||
void dtext(const char *str, int x, int y);
|
||||
|
||||
/*
|
||||
gtext()
|
||||
Prints the given raw string.
|
||||
*/
|
||||
void gtext(const char *str, int x, int y);
|
||||
|
||||
#endif // _TALES_H
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: timer
|
||||
//
|
||||
// Basic timer unit manipulation. Starts and stops timers with variable
|
||||
// number of repeats, and allows timer reloads without pause.
|
||||
//
|
||||
//---
|
||||
|
||||
#ifndef _TIMER_H
|
||||
#define _TIMER_H 1
|
||||
|
||||
|
@ -32,37 +41,33 @@
|
|||
|
||||
/*
|
||||
timer_start()
|
||||
Configures and starts a timer.
|
||||
|
||||
@arg timer Timer name. Use only TIMER_USER. You may use
|
||||
TIMER_GRAY, if you're not running the gray
|
||||
engine.
|
||||
@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.
|
||||
Configures and starts a timer. The timer argument expects a timer name.
|
||||
You can use TIMER_USER anytime. You may also use TIMER_GRAY if you're
|
||||
not running the gray engine.
|
||||
The delay is in clock counts unit. The possible values for the
|
||||
prescaler are dividers of the peripheral clock frequency Po:
|
||||
- TIMER_Po_4
|
||||
- TIMER_Po_16
|
||||
- TIMER_Po_64
|
||||
- TIMER_Po_256
|
||||
- TIMER_TCLK
|
||||
The number of repeats may to set to 0. In this case, the timer will not
|
||||
stop until timer_stop() is explicitly called.
|
||||
*/
|
||||
void timer_start(int timer, int delay, int prescaler, void (*callback)(void),
|
||||
int repetitions);
|
||||
int repeats);
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
/*
|
||||
timer_reload()
|
||||
Reloads the given timer with the given constant. Starts the timer if
|
||||
it was stopped.
|
||||
|
||||
@arg timer Timer identifier.
|
||||
@arg new_delay
|
||||
it was stopped. The new delay uses the same unit as in timer_start().
|
||||
*/
|
||||
void timer_reload(int timer, int new_delay);
|
||||
|
||||
|
@ -76,8 +81,6 @@ void timer_reload(int timer, int new_delay);
|
|||
/*
|
||||
timer_interrupt()
|
||||
Handles the interrupt for the given timer.
|
||||
|
||||
@timer Timer that generated the interrupt.
|
||||
*/
|
||||
void timer_interrupt(int timer) __attribute__((section(".gint.int")));
|
||||
|
||||
|
|
BIN
libc.a
BIN
libgint.a
|
@ -1,73 +1,4 @@
|
|||
//---
|
||||
//
|
||||
// gint drawing module: bopti
|
||||
//
|
||||
// bopti does every job related to display images. There is only one
|
||||
// public function, but there are lots of internal optimizations.
|
||||
//
|
||||
// Some bit-manipulation expressions may look written out of nowhere. The
|
||||
// idea is always the same: get a part of the image in an 'operator',
|
||||
// which is a 32-bit variable, shift this operator so that its bits
|
||||
// correspond to the desired position for the bitmap on the screen, and
|
||||
// edit the video-ram long entry which correspond to this position using
|
||||
// a 'mask' that indicates which bits of the operator contain information.
|
||||
//
|
||||
//---
|
||||
|
||||
#include <display.h>
|
||||
#include <stdint.h>
|
||||
#include <gray.h>
|
||||
|
||||
|
||||
//---
|
||||
// Heading declarations.
|
||||
//---
|
||||
|
||||
/*
|
||||
enum Channel
|
||||
Determines the kind of information written into a layer. Every image is
|
||||
made of one or more channels.
|
||||
*/
|
||||
enum Channel
|
||||
{
|
||||
Channel_Mono = 0x01,
|
||||
Channel_Light = 0x02,
|
||||
Channel_Dark = 0x04,
|
||||
|
||||
Channel_FullAlpha = 0x08,
|
||||
Channel_LightAlpha = 0x10,
|
||||
Channel_DarkAlpha = 0x20,
|
||||
};
|
||||
|
||||
/*
|
||||
enum Format
|
||||
Describes the various combination of channels allowed by bopti.
|
||||
*/
|
||||
enum Format
|
||||
{
|
||||
Format_Mono = Channel_Mono,
|
||||
Format_MonoAlpha = Format_Mono | Channel_FullAlpha,
|
||||
Format_Gray = Channel_Light | Channel_Dark,
|
||||
Format_GrayAlpha = Format_Gray | Channel_FullAlpha,
|
||||
Format_GreaterAlpha = Format_Mono | Channel_LightAlpha |
|
||||
Channel_DarkAlpha
|
||||
};
|
||||
|
||||
// These pointers are set by dimage() to avoid having to repeatedly determine
|
||||
// which video ram to use.
|
||||
static int *vram, *v1, *v2;
|
||||
// The following variables refer to parameters that do not change during the
|
||||
// drawing operation (at least for the time of a layer). They could be passed
|
||||
// on by every function from the module, but this would be heavy and useless.
|
||||
static enum Channel channel;
|
||||
static int height;
|
||||
static void (*op)(int offset, uint32_t operator, uint32_t op_mask);
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Drawing operation.
|
||||
//---
|
||||
#include <bopti_internals.h>
|
||||
|
||||
/*
|
||||
bopti_op()
|
||||
|
@ -306,7 +237,6 @@ static void bopti_end(const unsigned char *end, int x, int y, int width)
|
|||
bopti()
|
||||
Draws a layer in the video ram.
|
||||
*/
|
||||
|
||||
static void bopti(const unsigned char *layer, int x, int y, int columns,
|
||||
int end_size)
|
||||
{
|
||||
|
@ -360,74 +290,3 @@ static void getStructure(struct Image *img, int *width, int *height,
|
|||
if(end_size) *end_size = end;
|
||||
if(layer_size) *layer_size = layer;
|
||||
}
|
||||
|
||||
/*
|
||||
dimage()
|
||||
Displays a monochrome image in the video ram.
|
||||
*/
|
||||
void dimage(struct Image *img, int x, int y)
|
||||
{
|
||||
int width, layer_size, columns, end;
|
||||
int format = img->format, i = 0;
|
||||
const unsigned char *data;
|
||||
|
||||
if(img->magic != 0xb7) return;
|
||||
if(img->format != Format_Mono && img->format != Format_MonoAlpha)
|
||||
return;
|
||||
op = bopti_op_mono;
|
||||
|
||||
// 'height' refers to a static variable for this file.
|
||||
getStructure(img, &width, &height, &layer_size, &data, &columns, &end);
|
||||
|
||||
vram = display_getCurrentVRAM();
|
||||
|
||||
while(format)
|
||||
{
|
||||
// Drawing every layer, in order of formats.
|
||||
if(format & 1)
|
||||
{
|
||||
channel = (1 << i);
|
||||
bopti(data, x, y, columns, end);
|
||||
data += layer_size;
|
||||
}
|
||||
|
||||
format >>= 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
gimage()
|
||||
Displays a gray image in the video ram.
|
||||
*/
|
||||
void gimage(struct Image *img, int x, int y)
|
||||
{
|
||||
int width, layer_size, columns, end;
|
||||
int format = img->format, i = 0;
|
||||
const unsigned char *data;
|
||||
|
||||
if(img->magic != 0xb7) return;
|
||||
op = bopti_op_gray;
|
||||
|
||||
// 'height' refers to a static variable for this file.
|
||||
getStructure(img, &width, &height, &layer_size, &data, &columns, &end);
|
||||
|
||||
v1 = gray_lightVRAM();
|
||||
v2 = gray_darkVRAM();
|
||||
|
||||
while(format)
|
||||
{
|
||||
// Drawing every layer, in order of formats.
|
||||
if(format & 1)
|
||||
{
|
||||
channel = (1 << i);
|
||||
bopti(data, x, y, columns, end);
|
||||
data += layer_size;
|
||||
}
|
||||
|
||||
format >>= 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
127
src/bopti/bopti_internals.h
Normal file
|
@ -0,0 +1,127 @@
|
|||
//---
|
||||
//
|
||||
// gint drawing module: bopti
|
||||
//
|
||||
// bopti does every job related to display images. There is only one
|
||||
// public function, but there are lots of internal optimizations.
|
||||
//
|
||||
// Some bit-manipulation expressions may look written out of nowhere. The
|
||||
// idea is always the same: get a part of the image in an 'operator',
|
||||
// which is a 32-bit variable, shift this operator so that its bits
|
||||
// correspond to the desired position for the bitmap on the screen, and
|
||||
// edit the video-ram long entry which correspond to this position using
|
||||
// a 'mask' that indicates which bits of the operator contain information.
|
||||
//
|
||||
//---
|
||||
|
||||
#ifndef _BOPTI_INTERNALS_H
|
||||
#define _BOPTI_INTERNALS_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
enum Channel
|
||||
Determines the kind of information written into a layer. Every image is
|
||||
made of one or more channels.
|
||||
*/
|
||||
enum Channel
|
||||
{
|
||||
Channel_Mono = 0x01,
|
||||
Channel_Light = 0x02,
|
||||
Channel_Dark = 0x04,
|
||||
|
||||
Channel_FullAlpha = 0x08,
|
||||
Channel_LightAlpha = 0x10,
|
||||
Channel_DarkAlpha = 0x20,
|
||||
};
|
||||
|
||||
/*
|
||||
enum Format
|
||||
Describes the various combination of channels allowed by bopti.
|
||||
*/
|
||||
enum Format
|
||||
{
|
||||
Format_Mono = Channel_Mono,
|
||||
Format_MonoAlpha = Format_Mono | Channel_FullAlpha,
|
||||
Format_Gray = Channel_Light | Channel_Dark,
|
||||
Format_GrayAlpha = Format_Gray | Channel_FullAlpha,
|
||||
Format_GreaterAlpha = Format_Mono | Channel_LightAlpha |
|
||||
Channel_DarkAlpha
|
||||
};
|
||||
|
||||
// The following variables refer to parameters that do not change during the
|
||||
// drawing operation (at least for the time of a layer). They could be passed
|
||||
// on by every function from the module, but this would be heavy and useless.
|
||||
// Using global variables is not really a proper solution but it does simplify
|
||||
// code much.
|
||||
extern int *vram, *v1, *v2;
|
||||
extern enum Channel channel;
|
||||
extern int height;
|
||||
extern void (*op)(int offset, uint32_t operator, uint32_t op_mask);
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Some bopti routines.
|
||||
//---
|
||||
|
||||
/*
|
||||
bopti_op()
|
||||
Operates on a vram long. The operator will often not contain 32 bits of
|
||||
image information. Since neutral bits are not the same for all
|
||||
operations, the op_mask argument indicates which bits should be used
|
||||
for the operation. Which operation has to be done is determined by the
|
||||
channel setting.
|
||||
*/
|
||||
void bopti_op_mono(int offset, uint32_t operator, uint32_t op_mask);
|
||||
void bopti_op_gray(int offset, uint32_t operator, uint32_t op_mask);
|
||||
|
||||
/*
|
||||
bopti_grid() -- general form
|
||||
bopti_grid_a32() -- when x is a multiple of 32
|
||||
|
||||
Draws the grid at the beginning of a layer's data. The length of this
|
||||
grid is always a multiple of 32.
|
||||
The need for bopti_grid_a32() is not only linked to optimization,
|
||||
because bopti_grid() will perform a 32-bit shift when x is a multiple
|
||||
of 32, which is undefined behavior.
|
||||
*/
|
||||
void bopti_grid_a32(const uint32_t *layer, int x, int y, int column_count);
|
||||
void bopti_grid(const uint32_t *layer, int x, int y, int column_count);
|
||||
|
||||
/*
|
||||
bopti_end_get()
|
||||
Returns an operator for the end of a line, whose width is lower than 32
|
||||
(by design: otherwise, it would have been a column). The given pointer
|
||||
is read and updated so that it points to the next line at the end of
|
||||
the operation.
|
||||
*/
|
||||
uint32_t bopti_end_get1(const unsigned char **data);
|
||||
uint32_t bopti_end_get2(const unsigned char **data);
|
||||
|
||||
/*
|
||||
bopti_rest() -- general form
|
||||
bopti_rest_nover() -- when the end does not overlap two vram longs
|
||||
|
||||
Draws the end of a layer, which can be considered as a whole layer
|
||||
whose with is lower than 32. (Actually is it lower or equal to 16;
|
||||
otherwise it would have been a column and the end would be empty).
|
||||
*/
|
||||
void bopti_end_nover(const unsigned char *end, int x, int y, int width);
|
||||
void bopti_end(const unsigned char *end, int x, int y, int width);
|
||||
|
||||
/*
|
||||
bopti()
|
||||
Draws a layer in the video ram.
|
||||
*/
|
||||
void bopti(const unsigned char *layer, int x, int y, int columns,
|
||||
int end_size);
|
||||
|
||||
/*
|
||||
getStructure()
|
||||
Determines the image size and data pointer.
|
||||
*/
|
||||
void getStructure(struct Image *img, int *width, int *height, int *layer_size,
|
||||
const unsigned char **data, int *columns, int *end_size);
|
||||
|
||||
#endif // _BOPTI_INTERNALS_H
|
38
src/bopti/dimage.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <bopti_internals.h>
|
||||
#include <display.h>
|
||||
|
||||
/*
|
||||
dimage()
|
||||
Displays a monochrome image in the video ram.
|
||||
*/
|
||||
void dimage(struct Image *img, int x, int y)
|
||||
{
|
||||
int width, layer_size, columns, end;
|
||||
int format = img->format, i = 0;
|
||||
const unsigned char *data;
|
||||
|
||||
if(img->magic != 0xb7) return;
|
||||
if(img->format != Format_Mono && img->format != Format_MonoAlpha)
|
||||
return;
|
||||
op = bopti_op_mono;
|
||||
|
||||
// 'height' refers to a static variable for this file.
|
||||
getStructure(img, &width, &height, &layer_size, &data, &columns, &end);
|
||||
|
||||
vram = display_getCurrentVRAM();
|
||||
|
||||
while(format)
|
||||
{
|
||||
// Drawing every layer, in order of formats.
|
||||
if(format & 1)
|
||||
{
|
||||
channel = (1 << i);
|
||||
bopti(data, x, y, columns, end);
|
||||
data += layer_size;
|
||||
}
|
||||
|
||||
format >>= 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
37
src/bopti/gimage.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include <bopti_internals.h>
|
||||
#include <gray.h>
|
||||
|
||||
/*
|
||||
gimage()
|
||||
Displays a gray image in the video ram.
|
||||
*/
|
||||
void gimage(struct Image *img, int x, int y)
|
||||
{
|
||||
int width, layer_size, columns, end;
|
||||
int format = img->format, i = 0;
|
||||
const unsigned char *data;
|
||||
|
||||
if(img->magic != 0xb7) return;
|
||||
op = bopti_op_gray;
|
||||
|
||||
// 'height' refers to a static variable for this file.
|
||||
getStructure(img, &width, &height, &layer_size, &data, &columns, &end);
|
||||
|
||||
v1 = gray_lightVRAM();
|
||||
v2 = gray_darkVRAM();
|
||||
|
||||
while(format)
|
||||
{
|
||||
// Drawing every layer, in order of formats.
|
||||
if(format & 1)
|
||||
{
|
||||
channel = (1 << i);
|
||||
bopti(data, x, y, columns, end);
|
||||
data += layer_size;
|
||||
}
|
||||
|
||||
format >>= 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +1,12 @@
|
|||
// 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>
|
||||
#include <setjmp.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);
|
||||
|
||||
|
@ -26,17 +19,15 @@ extern unsigned int
|
|||
// This variable should be overwritten before being returned, so the default
|
||||
// value doesn't matter much.
|
||||
static int exit_code = EXIT_SUCCESS;
|
||||
static jmp_buf env;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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)
|
||||
|
@ -118,7 +109,6 @@ static void fini(void)
|
|||
|
||||
/*
|
||||
abort()
|
||||
|
||||
Immediately ends the program without invoking the exit handlers.
|
||||
*/
|
||||
|
||||
|
@ -130,11 +120,13 @@ void abort(void)
|
|||
|
||||
/*
|
||||
exit()
|
||||
|
||||
Ends the program and returns the given exit code status.
|
||||
Calls exit handlers before returning.
|
||||
|
||||
@arg status Exit status.
|
||||
Ends the program and returns the given exit code status. Calls exit
|
||||
handlers before returning.
|
||||
Usually exit() would ask the operating system to stop the process but
|
||||
the fx-9860G executes only one program at a time and calls it as a
|
||||
function. Reaching the program end point is therefore an efficient way
|
||||
of achieving this goal while minimizing interaction with the operating
|
||||
system.
|
||||
*/
|
||||
|
||||
void exit(int status)
|
|
@ -1,18 +1,20 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: interrupt handler
|
||||
//
|
||||
// Central point of the library. Controls the interrupt handler and
|
||||
// defines a few functions to configure callbacks for some interrupts.
|
||||
//
|
||||
//---
|
||||
|
||||
#include <gint.h>
|
||||
#include <mpu.h>
|
||||
#include <gray.h>
|
||||
#include <stddef.h>
|
||||
|
||||
//---
|
||||
// Local variables.
|
||||
//---
|
||||
|
||||
static unsigned int
|
||||
new_vbr,
|
||||
sys_vbr;
|
||||
|
||||
static void (*rtc_callback)(void) = NULL;
|
||||
|
||||
|
||||
|
||||
//---
|
||||
|
@ -55,50 +57,12 @@ static void gint_stop(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)
|
||||
inline unsigned int gint_systemVBR(void)
|
||||
{
|
||||
return sys_vbr;
|
||||
}
|
||||
|
||||
/*
|
||||
gint_setRTCCallback()
|
||||
Sets the callback function for the real-time clock interrupt. If
|
||||
frequency is non-NULL, the clock frequency is set to the given value.
|
||||
|
||||
@arg callback Callback function.
|
||||
@arg frequency Interrupt frequency.
|
||||
*/
|
||||
void gint_setRTCCallback(void (*callback)(void), enum RTCFrequency frequency)
|
||||
{
|
||||
if(frequency < 1 || frequency > 7) return;
|
||||
rtc_callback = callback;
|
||||
|
||||
if(isSH3())
|
||||
gint_setRTCFrequency_7705(frequency);
|
||||
else
|
||||
gint_setRTCFrequency_7305(frequency);
|
||||
}
|
||||
|
||||
/*
|
||||
gint_getRTCCallback()
|
||||
Returns the callback function. If frequency is non-NULL, it is set to
|
||||
the current frequency value.
|
||||
*/
|
||||
void (*gint_getRTCCallback(enum RTCFrequency *frequency))(void)
|
||||
{
|
||||
if(!frequency) return rtc_callback;
|
||||
|
||||
if(isSH3())
|
||||
*frequency = gint_getRTCFrequency_7705();
|
||||
else
|
||||
*frequency = gint_getRTCFrequency_7305();
|
||||
|
||||
return rtc_callback;
|
||||
}
|
||||
|
||||
/*
|
||||
gint()
|
||||
Handles interrupts.
|
40
src/core/gint_callback.c
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include <gint.h>
|
||||
#include <mpu.h>
|
||||
|
||||
static void (*rtc_callback)(void) = NULL;
|
||||
|
||||
/*
|
||||
gint_setRTCCallback()
|
||||
Sets the callback function for the real-time clock interrupt. If
|
||||
frequency is non-NULL, the clock frequency is set to the given value.
|
||||
|
||||
@arg callback Callback function.
|
||||
@arg frequency Interrupt frequency.
|
||||
*/
|
||||
void gint_setRTCCallback(void (*callback)(void), enum RTCFrequency frequency)
|
||||
{
|
||||
if(frequency < 1 || frequency > 7) return;
|
||||
rtc_callback = callback;
|
||||
|
||||
if(isSH3())
|
||||
gint_setRTCFrequency_7705(frequency);
|
||||
else
|
||||
gint_setRTCFrequency_7305(frequency);
|
||||
}
|
||||
|
||||
/*
|
||||
gint_getRTCCallback()
|
||||
Returns the callback function. If frequency is non-NULL, it is set to
|
||||
the current frequency value.
|
||||
*/
|
||||
void (*gint_getRTCCallback(enum RTCFrequency *frequency))(void)
|
||||
{
|
||||
if(!frequency) return rtc_callback;
|
||||
|
||||
if(isSH3())
|
||||
*frequency = gint_getRTCFrequency_7705();
|
||||
else
|
||||
*frequency = gint_getRTCFrequency_7305();
|
||||
|
||||
return rtc_callback;
|
||||
}
|
|
@ -13,8 +13,6 @@
|
|||
/*
|
||||
gint_getVBR()
|
||||
Returns the current vbr address.
|
||||
|
||||
@return vbr address currently in use.
|
||||
*/
|
||||
_gint_getVBR:
|
||||
rts
|
||||
|
@ -34,9 +32,6 @@ _gint_getVBR:
|
|||
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
|
|
@ -1,6 +1,8 @@
|
|||
/*
|
||||
This file contains all the system calls used by the library. Maybe one
|
||||
day we won't need them anymore ?
|
||||
gint core module: syscalls
|
||||
|
||||
All the system calls used by the library. Let's hope one day we won't
|
||||
depend on them anymore.
|
||||
*/
|
||||
|
||||
.global ___Hmem_SetMMU
|
473
src/display.c
|
@ -1,473 +0,0 @@
|
|||
//---
|
||||
//
|
||||
// gint drawing module: display
|
||||
//
|
||||
// Handles vram manipulation and drawing.
|
||||
//
|
||||
//
|
||||
// :: Rectangle masks
|
||||
//
|
||||
// The concept of 'rectangle masks' is used several times in this module.
|
||||
// It is based on the fact that an operation that affects a rectangle acts
|
||||
// the same on all its lines. Therefore the behavior of the operation is
|
||||
// determined by its behavior on a single line, which is represented using
|
||||
// 'masks' whose bits indicate whether a pixel is affected (1) or not (0).
|
||||
//
|
||||
// For example when clearing the screen rectangle (16, 16, 112, 48), the
|
||||
// masks will represent information '16 to 112 on x-axis', and will hold
|
||||
// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These
|
||||
// masks can then be used by setting vram[offset] &= ~masks[i]. This
|
||||
// appears to be very flexible : for instance, vram[offset] ^= masks[i]
|
||||
// will reverse the pixels in the same rectangle.
|
||||
//
|
||||
// This technique can also be used in more subtle cases with more complex
|
||||
// patterns, but within this module it is unlikely to happen.
|
||||
//
|
||||
//---
|
||||
|
||||
#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 functions.
|
||||
//---
|
||||
|
||||
/*
|
||||
adjust()
|
||||
Adjusts the given rectangle coordinates to ensure that :
|
||||
- the rectangle is entirely contained in the screen
|
||||
- x1 < x2
|
||||
- y1 < y2
|
||||
which is needed when working with screen rectangles.
|
||||
*/
|
||||
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). The four masks are stored in the third argument
|
||||
(seen as an array).
|
||||
*/
|
||||
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 (positive bits at the left are removed by the mask).
|
||||
x2 = ~x2 & 31;
|
||||
|
||||
// Setting the first and last masks.
|
||||
masks[l1] &= (0xffffffff >> x1);
|
||||
masks[l2] &= (0xffffffff << x2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Generic functions.
|
||||
//---
|
||||
|
||||
/*
|
||||
display_getLocalVRAM()
|
||||
Returns the local video ram address. This function always return the
|
||||
same address.
|
||||
The buffer returned by this function should not be used directly when
|
||||
running the gray engine.
|
||||
*/
|
||||
inline void *display_getLocalVRAM(void)
|
||||
{
|
||||
return (void *)local_vram;
|
||||
}
|
||||
|
||||
/*
|
||||
display_getCurrentVRAM()
|
||||
Returns the current video ram. This function usually returns the
|
||||
parameter of the last call to display_useVRAM(), unless the gray engine
|
||||
is running (in which case the result is undefined). Returns the local
|
||||
vram address by default.
|
||||
*/
|
||||
inline void *display_getCurrentVRAM(void)
|
||||
{
|
||||
return (void *)vram;
|
||||
}
|
||||
|
||||
/*
|
||||
display_useVRAM()
|
||||
Changes the current video ram address. The argument MUST be a 4-
|
||||
aligned 1024-byte buffer ; otherwise any drawing operation will crash
|
||||
the program.
|
||||
This function will most likely have no effect when running the gray
|
||||
engine.
|
||||
*/
|
||||
inline void display_useVRAM(void *ptr)
|
||||
{
|
||||
vram = (int *)ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Global drawing functions.
|
||||
//---
|
||||
|
||||
/*
|
||||
dupdate()
|
||||
Displays the vram on the physical screen.
|
||||
*/
|
||||
void dupdate(void)
|
||||
{
|
||||
if(gray_runs()) return;
|
||||
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. Both (x1, y1) and
|
||||
(x2, y2) are cleared.
|
||||
*/
|
||||
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 begin = y1 << 2;
|
||||
int end = (y2 + 1) << 2;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 4; i++) masks[i] = ~masks[i];
|
||||
for(i = begin; i < end; i++) vram[i] &= masks[i & 3];
|
||||
}
|
||||
|
||||
/*
|
||||
dreverse_area()
|
||||
Reverses an area of the vram. This function is a simple application of
|
||||
the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as
|
||||
well.
|
||||
*/
|
||||
void dreverse_area(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
unsigned int masks[4];
|
||||
adjust(&x1, &y1, &x2, &y2);
|
||||
getmasks(x1, x2, masks);
|
||||
|
||||
int begin = y1 << 2;
|
||||
int end = (y2 + 1) << 2;
|
||||
int i;
|
||||
|
||||
if(gray_runs())
|
||||
{
|
||||
int *v1 = gray_lightVRAM();
|
||||
int *v2 = gray_darkVRAM();
|
||||
|
||||
for(i = begin; i < end; i++)
|
||||
{
|
||||
v1[i] ^= masks[i & 3];
|
||||
v2[i] ^= masks[i & 3];
|
||||
}
|
||||
}
|
||||
else for(i = begin; i < end; i++)
|
||||
{
|
||||
vram[i] ^= masks[i & 3];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Local drawing functions.
|
||||
//---
|
||||
|
||||
/*
|
||||
dpixel()
|
||||
Puts a pixel in the vram.
|
||||
*/
|
||||
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_Invert:
|
||||
vram[offset] ^= mask;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
dline()
|
||||
Draws a line on the screen. Automatically optimizes horizontal and
|
||||
vertical lines.
|
||||
*/
|
||||
|
||||
static void dhline(int x1, int x2, int y, enum Color color)
|
||||
{
|
||||
unsigned int masks[4];
|
||||
int offset = y << 2;
|
||||
int i;
|
||||
|
||||
// Swapping x1 and x2 if needed.
|
||||
if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2;
|
||||
getmasks(x1, x2, masks);
|
||||
|
||||
int *v1 = gray_lightVRAM();
|
||||
int *v2 = gray_darkVRAM();
|
||||
|
||||
if(gray_runs()) switch(color)
|
||||
{
|
||||
case Color_White:
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
v1[offset + i] &= ~masks[i];
|
||||
v2[offset + i] &= ~masks[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case Color_Light:
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
v1[offset + i] |= masks[i];
|
||||
v2[offset + i] &= ~masks[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case Color_Dark:
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
v1[offset + i] &= ~masks[i];
|
||||
v2[offset + i] |= masks[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case Color_Black:
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
v1[offset + i] |= masks[i];
|
||||
v2[offset + i] |= masks[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case Color_Invert:
|
||||
for(i = 0; i < 4; i++)
|
||||
{
|
||||
v1[offset + i] ^= masks[i];
|
||||
v2[offset + i] ^= masks[i];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
else 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_Invert:
|
||||
for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i];
|
||||
break;
|
||||
|
||||
default:
|
||||
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);
|
||||
|
||||
int *v1 = gray_lightVRAM();
|
||||
int *v2 = gray_darkVRAM();
|
||||
|
||||
if(gray_runs()) switch(color)
|
||||
{
|
||||
case Color_White:
|
||||
while(offset <= end)
|
||||
{
|
||||
v1[offset] &= ~mask;
|
||||
v2[offset] &= ~mask;
|
||||
offset += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case Color_Light:
|
||||
while(offset <= end)
|
||||
{
|
||||
v1[offset] |= mask;
|
||||
v2[offset] &= ~mask;
|
||||
offset += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case Color_Dark:
|
||||
while(offset <= end)
|
||||
{
|
||||
v1[offset] &= ~mask;
|
||||
v2[offset] |= mask;
|
||||
offset += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case Color_Black:
|
||||
while(offset <= end)
|
||||
{
|
||||
v1[offset] |= mask;
|
||||
v2[offset] |= mask;
|
||||
offset += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
case Color_Invert:
|
||||
while(offset <= end)
|
||||
{
|
||||
v1[offset] ^= mask;
|
||||
v2[offset] ^= mask;
|
||||
offset += 4;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
else 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_Invert:
|
||||
while(offset <= end) vram[offset] ^= mask, offset += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
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);
|
||||
}
|
24
src/display/adjustRectangle.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <display_internals.h>
|
||||
|
||||
/*
|
||||
adjustRectangle()
|
||||
Adjusts the given rectangle coordinates to ensure that :
|
||||
- the rectangle is entirely contained in the screen
|
||||
- x1 < x2
|
||||
- y1 < y2
|
||||
which is needed when working with screen rectangles.
|
||||
*/
|
||||
void adjustRectangle(int *x1, int *y1, int *x2, int *y2)
|
||||
{
|
||||
#define swap(a, b) tmp = a, a = b, b = tmp
|
||||
int tmp;
|
||||
|
||||
if(*x2 < *x1) swap(*x1, *x2);
|
||||
if(*y2 < *y1) swap(*y1, *y2);
|
||||
|
||||
if(*x1 < 0) *x1 = 0;
|
||||
if(*y1 < 0) *y1 = 0;
|
||||
if(*x2 > 127) *x2 = 127;
|
||||
if(*y2 > 63) *y2 = 63;
|
||||
#undef swap
|
||||
}
|
12
src/display/dclear.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <display_internals.h>
|
||||
#include <display.h>
|
||||
|
||||
/*
|
||||
dclear()
|
||||
Clears the whole vram.
|
||||
*/
|
||||
void dclear(void)
|
||||
{
|
||||
int i;
|
||||
for(i = 0; i < 256; i++) vram[i] = 0;
|
||||
}
|
21
src/display/dclear_area.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <display_internals.h>
|
||||
#include <display.h>
|
||||
|
||||
/*
|
||||
dclear_area()
|
||||
Clears an area of the vram using rectangle masks. Both (x1, y1) and
|
||||
(x2, y2) are cleared.
|
||||
*/
|
||||
void dclear_area(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
unsigned int masks[4];
|
||||
adjustRectangle(&x1, &y1, &x2, &y2);
|
||||
getMasks(x1, x2, masks);
|
||||
|
||||
int begin = y1 << 2;
|
||||
int end = (y2 + 1) << 2;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 4; i++) masks[i] = ~masks[i];
|
||||
for(i = begin; i < end; i++) vram[i] &= masks[i & 3];
|
||||
}
|
52
src/display/display_internals.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
//---
|
||||
//
|
||||
// gint drawing module: display
|
||||
//
|
||||
// Handles vram manipulation and drawing.
|
||||
//
|
||||
//
|
||||
// :: Rectangle masks
|
||||
//
|
||||
// The concept of 'rectangle masks' is used several times in this module.
|
||||
// It is based on the fact that an operation that affects a rectangle acts
|
||||
// the same on all its lines. Therefore the behavior of the operation is
|
||||
// determined by its behavior on a single line, which is represented using
|
||||
// 'masks' whose bits indicate whether a pixel is affected (1) or not (0).
|
||||
//
|
||||
// For example when clearing the screen rectangle (16, 16, 112, 48), the
|
||||
// masks will represent information '16 to 112 on x-axis', and will hold
|
||||
// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These
|
||||
// masks can then be used by setting vram[offset] &= ~masks[i]. This
|
||||
// appears to be very flexible : for instance, vram[offset] ^= masks[i]
|
||||
// will reverse the pixels in the same rectangle.
|
||||
//
|
||||
// This technique can also be used in more subtle cases with more complex
|
||||
// patterns, but within this module it is unlikely to happen.
|
||||
//
|
||||
//---
|
||||
|
||||
#ifndef _DISPLAY_INTERNALS_H
|
||||
#define _DISPLAY_INTERNALS_H
|
||||
|
||||
extern int *vram;
|
||||
|
||||
/*
|
||||
adjustRectangle()
|
||||
Adjusts the given rectangle coordinates to ensure that :
|
||||
- the rectangle is entirely contained in the screen
|
||||
- x1 < x2
|
||||
- y1 < y2
|
||||
which is needed when working with screen rectangles.
|
||||
*/
|
||||
static void adjustRectangle(int *x1, int *y1, int *x2, int *y2);
|
||||
|
||||
/*
|
||||
getMasks()
|
||||
Computes the rectangle masks needed to affect pixels located between x1
|
||||
and x2 (both included). The four masks are stored in the third argument
|
||||
(seen as an array).
|
||||
See this module's internals header file for more information.
|
||||
*/
|
||||
static void getMasks(int x1, int x2, unsigned int *masks);
|
||||
|
||||
#endif // _DISPLAY_INTERNALS_H
|
43
src/display/display_vram.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <display.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];
|
||||
int *vram = local_vram;
|
||||
|
||||
/*
|
||||
display_getLocalVRAM()
|
||||
Returns the local video ram address. This function always return the
|
||||
same address.
|
||||
The buffer returned by this function should not be used directly when
|
||||
running the gray engine.
|
||||
*/
|
||||
inline void *display_getLocalVRAM(void)
|
||||
{
|
||||
return (void *)local_vram;
|
||||
}
|
||||
|
||||
/*
|
||||
display_getCurrentVRAM()
|
||||
Returns the current video ram. This function usually returns the
|
||||
parameter of the last call to display_useVRAM(), unless the gray engine
|
||||
is running (in which case the result is undefined). Returns the local
|
||||
vram address by default.
|
||||
*/
|
||||
inline void *display_getCurrentVRAM(void)
|
||||
{
|
||||
return (void *)vram;
|
||||
}
|
||||
|
||||
/*
|
||||
display_useVRAM()
|
||||
Changes the current video ram address. The argument MUST be a 4-
|
||||
aligned 1024-byte buffer ; otherwise any drawing operation will crash
|
||||
the program.
|
||||
This function will most likely have no effect when running the gray
|
||||
engine.
|
||||
*/
|
||||
inline void display_useVRAM(void *ptr)
|
||||
{
|
||||
vram = (int *)ptr;
|
||||
}
|
116
src/display/dline.c
Normal file
|
@ -0,0 +1,116 @@
|
|||
#include <display_internals.h>
|
||||
#include <display.h>
|
||||
|
||||
#define sgn(x) ((x) < 0 ? -1 : 1)
|
||||
#define abs(x) ((x) < 0 ? -(x) : (x))
|
||||
#define rnd(x) ((int)((x) + 0.5))
|
||||
|
||||
/*
|
||||
dline()
|
||||
Draws a line on the screen. Automatically optimizes horizontal and
|
||||
vertical lines.
|
||||
*/
|
||||
|
||||
static void dhline(int x1, int x2, int y, enum Color color)
|
||||
{
|
||||
unsigned int masks[4];
|
||||
int offset = y << 2;
|
||||
int i;
|
||||
|
||||
// Swapping x1 and x2 if needed.
|
||||
if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2;
|
||||
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_Invert:
|
||||
for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i];
|
||||
break;
|
||||
|
||||
default:
|
||||
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_Invert:
|
||||
while(offset <= end) vram[offset] ^= mask, offset += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void dline(int x1, int y1, int x2, int y2, enum Color color)
|
||||
{
|
||||
adjustRectangle(&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);
|
||||
}
|
32
src/display/dpixel.c
Normal file
|
@ -0,0 +1,32 @@
|
|||
#include <display_internals.h>
|
||||
#include <display.h>
|
||||
|
||||
/*
|
||||
dpixel()
|
||||
Puts a pixel in the vram.
|
||||
*/
|
||||
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_Invert:
|
||||
vram[offset] ^= mask;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
21
src/display/dreverse_area.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <display_internals.h>
|
||||
#include <display.h>
|
||||
|
||||
/*
|
||||
dreverse_area()
|
||||
Reverses an area of the vram. This function is a simple application of
|
||||
the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as
|
||||
well.
|
||||
*/
|
||||
void dreverse_area(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
unsigned int masks[4];
|
||||
adjustRectangle(&x1, &y1, &x2, &y2);
|
||||
getMasks(x1, x2, masks);
|
||||
|
||||
int begin = y1 << 2;
|
||||
int end = (y2 + 1) << 2;
|
||||
int i;
|
||||
|
||||
for(i = begin; i < end; i++) vram[i] ^= masks[i & 3];
|
||||
}
|
14
src/display/dupdate.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <display_internals.h>
|
||||
#include <display.h>
|
||||
#include <screen.h>
|
||||
#include <gray.h>
|
||||
|
||||
/*
|
||||
dupdate()
|
||||
Displays the vram on the physical screen.
|
||||
*/
|
||||
void dupdate(void)
|
||||
{
|
||||
if(gray_runs()) return;
|
||||
screen_display((const void *)vram);
|
||||
}
|
34
src/display/getMasks.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include <display_internals.h>
|
||||
|
||||
/*
|
||||
getMasks()
|
||||
Computes the rectangle masks needed to affect pixels located between x1
|
||||
and x2 (both included). The four masks are stored in the third argument
|
||||
(seen as an array).
|
||||
See this module's internals header file for more information.
|
||||
*/
|
||||
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 (positive bits at the left are removed by the mask).
|
||||
x2 = ~x2 & 31;
|
||||
|
||||
// Setting the first and last masks.
|
||||
masks[l1] &= (0xffffffff >> x1);
|
||||
masks[l2] &= (0xffffffff << x2);
|
||||
}
|
313
src/gint_7305.c
|
@ -1,313 +0,0 @@
|
|||
#include <gint.h>
|
||||
#include <timer.h>
|
||||
#include <keyboard.h>
|
||||
#include <7305.h>
|
||||
|
||||
//---
|
||||
// Interrupt codes.
|
||||
//---
|
||||
|
||||
#define IC_RTC_PRI 0xaa0
|
||||
#define IC_KEYSC 0xbe0
|
||||
#define IC_TMU0_TUNI0 0x400
|
||||
#define IC_TMU0_TUNI1 0x420
|
||||
#define IC_TMU0_TUNI2 0x440
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Various MPU-dependent procedures.
|
||||
//---
|
||||
|
||||
/*
|
||||
gint_setRTCFrequency()
|
||||
Sets the RTC interrupt frequency and enables interrupts.
|
||||
|
||||
@arg frequency
|
||||
*/
|
||||
void gint_setRTCFrequency_7305(enum RTCFrequency frequency)
|
||||
{
|
||||
if(frequency < 1 || frequency > 7) return;
|
||||
RTC.RCR2.BYTE = (frequency << 4) | 0x09;
|
||||
}
|
||||
|
||||
/*
|
||||
gint_getRTCFrequency()
|
||||
Returns the RTC interrupt frequency.
|
||||
|
||||
@return RTC interrupt frequency.
|
||||
*/
|
||||
enum RTCFrequency gint_getRTCFrequency_7305(void)
|
||||
{
|
||||
return (RTC.RCR2.BYTE & 0x70) >> 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Keyboard management.
|
||||
//---
|
||||
|
||||
/*
|
||||
kdelay()
|
||||
Should sleep during a few milliseconds. Well...
|
||||
This delay has a great influence on key detection. Here are a few
|
||||
observations of what happens with common numbers of "nop" (without
|
||||
overclock). Take care of the effect of overclock !
|
||||
|
||||
Column effects
|
||||
May have something to do with register HIZCRB not being used here. When
|
||||
many keys on the same column are pressed, other keys of the same column
|
||||
may be triggered.
|
||||
|
||||
- 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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_updateState()
|
||||
Updates the keyboard state.
|
||||
*/
|
||||
void keyboard_updateState_7305(volatile unsigned char *keyboard_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 10; i++) keyboard_state[i] = krow(i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Interrupt handler.
|
||||
//---
|
||||
|
||||
void gint_7305(void)
|
||||
{
|
||||
volatile unsigned int *intevt = (unsigned int *)0xff000028;
|
||||
unsigned int code = *intevt;
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case IC_RTC_PRI:
|
||||
RTC.RCR2.PEF = 0;
|
||||
break;
|
||||
|
||||
case IC_TMU0_TUNI0:
|
||||
timer_interrupt(TIMER_TMU0);
|
||||
break;
|
||||
|
||||
case IC_TMU0_TUNI1:
|
||||
timer_interrupt(TIMER_TMU1);
|
||||
break;
|
||||
|
||||
case IC_TMU0_TUNI2:
|
||||
timer_interrupt(TIMER_TMU2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// 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;
|
||||
INTX.IPRA.TMU0_0 = GINT_INTP_KEY;
|
||||
INTX.IPRA.TMU0_1 = GINT_INTP_GRAY;
|
||||
INTX.IPRA.TMU0_2 = GINT_INTP_TIMER;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Saving the RTC configuration.
|
||||
rcr2 = RTC.RCR2.BYTE;
|
||||
// Disabling RTC interrupts by default.
|
||||
RTC.RCR2.BYTE = 0x09;
|
||||
}
|
||||
|
||||
void gint_stop_7305(void)
|
||||
{
|
||||
gint_priority_unlock_7305();
|
||||
|
||||
// Restoring the RTC configuration.
|
||||
RTC.RCR2.BYTE = rcr2;
|
||||
}
|
276
src/gint_7705.c
|
@ -1,276 +0,0 @@
|
|||
#include <gint.h>
|
||||
#include <timer.h>
|
||||
#include <keyboard.h>
|
||||
#include <7705.h>
|
||||
|
||||
//---
|
||||
// Interrupt codes.
|
||||
//---
|
||||
|
||||
#define IC_RTC_PRI 0x4a0
|
||||
#define IC_PINT07 0x700
|
||||
#define IC_TMU0_TUNI0 0x400
|
||||
#define IC_TMU1_TUNI1 0x420
|
||||
#define IC_TMU2_TUNI2 0x440
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Various MPU-dependent procedures.
|
||||
//---
|
||||
|
||||
/*
|
||||
gint_setRTCFrequency()
|
||||
Sets the RTC interrupt frequency and enables interrupts.
|
||||
|
||||
@arg frequency
|
||||
*/
|
||||
void gint_setRTCFrequency_7705(enum RTCFrequency frequency)
|
||||
{
|
||||
if(frequency < 1 || frequency > 7) return;
|
||||
RTC.RCR2.BYTE = (frequency << 4) | 0x09;
|
||||
}
|
||||
|
||||
/*
|
||||
gint_getRTCFrequency()
|
||||
Returns the RTC interrupt frequency.
|
||||
|
||||
@return RTC interrupt frequency.
|
||||
*/
|
||||
enum RTCFrequency gint_getRTCFrequency_7705(void)
|
||||
{
|
||||
return (RTC.RCR2.BYTE & 0x70) >> 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Keyboard management.
|
||||
//---
|
||||
|
||||
/*
|
||||
kdelay()
|
||||
Used to be a low-level sleep using the watchdog, as in the system. This
|
||||
way seems OK at least, and it doesn't create column effects as for
|
||||
SH7305.
|
||||
*/
|
||||
static void kdelay(void)
|
||||
{
|
||||
#define r4(str) str str str str
|
||||
|
||||
__asm__
|
||||
(
|
||||
r4("nop\n\t")
|
||||
r4("nop\n\t")
|
||||
r4("nop\n\t")
|
||||
);
|
||||
|
||||
#undef r4
|
||||
|
||||
/* Watchdog version.
|
||||
const int delay = 0xf4;
|
||||
|
||||
// Disabling the watchdog timer interrupt and resetting the
|
||||
// configuration. Setting the delay.
|
||||
INTC.IPRB.BIT._WDT = 0;
|
||||
WDT.WTCSR.WRITE = 0xa500;
|
||||
WDT.WTCNT.WRITE = 0x5a00 | (delay & 0xff);
|
||||
|
||||
// Counting on Po/256.
|
||||
WDT.WTCSR.WRITE = 0xa505;
|
||||
// Starting the timer (sets again to Po/256).
|
||||
WDT.WTCSR.WRITE = 0xa585;
|
||||
|
||||
// Waiting until it overflows (delaying), then clearing the overflow
|
||||
// flag.
|
||||
while((WDT.WTCSR.READ.BYTE & 0x08) == 0);
|
||||
WDT.WTCSR.WRITE = 0xa500 | (WDT.WTCSR.READ.BYTE & 0xf7);
|
||||
|
||||
// Resetting the configuration and the counter.
|
||||
WDT.WTCSR.WRITE = 0xa500;
|
||||
WDT.WTCSR.WRITE = 0x5a00;
|
||||
|
||||
// Enabling back the watchdog timer interrupt.
|
||||
INTC.IPRB.BIT._WDT = GINT_INTP_WDT;
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
krow()
|
||||
Reads a keyboard row.
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_updateState()
|
||||
Updates the keyboard state.
|
||||
*/
|
||||
void keyboard_updateState_7705(volatile unsigned char *keyboard_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 10; i++) keyboard_state[i] = krow(i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Interrupt handler.
|
||||
//---
|
||||
|
||||
void gint_7705(void)
|
||||
{
|
||||
volatile unsigned int *intevt2 = (unsigned int *)0xa4000000;
|
||||
unsigned int code = *intevt2;
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case IC_RTC_PRI:
|
||||
RTC.RCR2.BIT.PEF = 0;
|
||||
break;
|
||||
|
||||
case IC_TMU0_TUNI0:
|
||||
timer_interrupt(TIMER_TMU0);
|
||||
break;
|
||||
|
||||
case IC_TMU1_TUNI1:
|
||||
timer_interrupt(TIMER_TMU1);
|
||||
break;
|
||||
|
||||
case IC_TMU2_TUNI2:
|
||||
timer_interrupt(TIMER_TMU2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Setup.
|
||||
//---
|
||||
|
||||
static unsigned short iprs[8];
|
||||
static unsigned char rcr2;
|
||||
|
||||
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;
|
||||
INTC.IPRA.BIT._TMU0 = GINT_INTP_KEY;
|
||||
INTC.IPRA.BIT._TMU1 = GINT_INTP_GRAY;
|
||||
INTC.IPRA.BIT._TMU2 = GINT_INTP_TIMER;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Saving the RTC configuration.
|
||||
rcr2 = RTC.RCR2.BYTE;
|
||||
// Disabling RTC interrupts by default.
|
||||
RTC.RCR2.BYTE = 0x09;
|
||||
}
|
||||
|
||||
void gint_stop_7705(void)
|
||||
{
|
||||
gint_priority_unlock_7705();
|
||||
|
||||
// Restoring the RTC configuration.
|
||||
RTC.RCR2.BYTE = rcr2;
|
||||
}
|
14
src/gray/gclear.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <gray.h>
|
||||
|
||||
/*
|
||||
gclear()
|
||||
Clears the video ram.
|
||||
*/
|
||||
void gclear(void)
|
||||
{
|
||||
int *v1 = gray_lightVRAM();
|
||||
int *v2 = gray_darkVRAM();
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 256; i++) v1[i] = v2[i] = 0;
|
||||
}
|
15
src/gray/gclear_area.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include <gray.h>
|
||||
#include <display.h>
|
||||
|
||||
/*
|
||||
gclear_area()
|
||||
Clears an area of the video ram. End points (x1, y1) and (x2, y2) are
|
||||
included.
|
||||
*/
|
||||
void gclear_area(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
display_useVRAM(gray_lightVRAM());
|
||||
dclear_area(x1, y1, x2, y2);
|
||||
display_useVRAM(gray_darkVRAM());
|
||||
dclear_area(x1, y1, x2, y2);
|
||||
}
|
20
src/gray/gline.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <gray.h>
|
||||
#include <display.h>
|
||||
|
||||
/*
|
||||
gline()
|
||||
Draws a line in the vram. Automatically optimizes special cases.
|
||||
*/
|
||||
void gline(int x1, int y1, int x2, int y2, enum Color color)
|
||||
{
|
||||
enum Color c1, c2;
|
||||
|
||||
if(color == Color_None) return;
|
||||
else if(color == Color_Invert) c1 = c2 = Color_Invert;
|
||||
else c1 = color & 1, c2 = color >> 1;
|
||||
|
||||
display_useVRAM(gray_lightVRAM());
|
||||
dline(x1, y1, x2, y2, c1);
|
||||
display_useVRAM(gray_darkVRAM());
|
||||
dline(x1, y1, x2, y2, c2);
|
||||
}
|
47
src/gray/gpixel.c
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include <gray.h>
|
||||
|
||||
/*
|
||||
gpixel()
|
||||
Puts a pixel in the vram.
|
||||
*/
|
||||
void gpixel(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);
|
||||
|
||||
int *v1 = gray_lightVRAM();
|
||||
int *v2 = gray_lightVRAM();
|
||||
|
||||
switch(color)
|
||||
{
|
||||
case Color_White:
|
||||
v1[offset] &= ~mask;
|
||||
v2[offset] &= ~mask;
|
||||
break;
|
||||
|
||||
case Color_Light:
|
||||
v1[offset] |= mask;
|
||||
v2[offset] &= ~mask;
|
||||
break;
|
||||
|
||||
case Color_Dark:
|
||||
v1[offset] &= ~mask;
|
||||
v2[offset] |= mask;
|
||||
break;
|
||||
|
||||
case Color_Black:
|
||||
v1[offset] |= mask;
|
||||
v2[offset] |= mask;
|
||||
break;
|
||||
|
||||
case Color_Invert:
|
||||
v1[offset] ^= mask;
|
||||
v2[offset] ^= mask;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
//
|
||||
//---
|
||||
|
||||
#include <display.h>
|
||||
#include <gray.h>
|
||||
#include <screen.h>
|
||||
#include <timer.h>
|
||||
|
@ -27,15 +26,6 @@ static int runs = 0;
|
|||
// Engine control.
|
||||
//---
|
||||
|
||||
/*
|
||||
gray_runs()
|
||||
Returns 1 if the gray engine is running, 0 otherwise.
|
||||
*/
|
||||
inline int gray_runs(void)
|
||||
{
|
||||
return runs;
|
||||
}
|
||||
|
||||
/*
|
||||
gray_start()
|
||||
Starts the gray engine. The control of the screen is transferred to the
|
||||
|
@ -60,6 +50,31 @@ void gray_stop(void)
|
|||
display_useVRAM(display_getLocalVRAM());
|
||||
}
|
||||
|
||||
/*
|
||||
gray_setDelays()
|
||||
Changes the gray engine delays.
|
||||
*/
|
||||
void gray_setDelays(int light, int dark)
|
||||
{
|
||||
delays[0] = light;
|
||||
delays[1] = dark;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Engine information.
|
||||
//---
|
||||
|
||||
/*
|
||||
gray_runs()
|
||||
Returns 1 if the gray engine is running, 0 otherwise.
|
||||
*/
|
||||
inline int gray_runs(void)
|
||||
{
|
||||
return runs;
|
||||
}
|
||||
|
||||
/*
|
||||
gray_lightVRAM()
|
||||
Returns the module's gray vram address.
|
||||
|
@ -88,20 +103,25 @@ void gray_getDelays(int *light, int *dark)
|
|||
if(dark) *dark = delays[1];
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Drawing.
|
||||
//---
|
||||
|
||||
/*
|
||||
gray_setDelays()
|
||||
Changes the gray engine delays.
|
||||
gupdate()
|
||||
Swaps the vram buffer sets.
|
||||
*/
|
||||
void gray_setDelays(int light, int dark)
|
||||
inline void gupdate(void)
|
||||
{
|
||||
delays[0] = light;
|
||||
delays[1] = dark;
|
||||
current ^= 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Internal API.
|
||||
// Interrupt control and initialization.
|
||||
//---
|
||||
|
||||
/*
|
||||
|
@ -129,127 +149,3 @@ void gray_init(void)
|
|||
delays[0] = 900;
|
||||
delays[1] = 1000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Global drawing functions
|
||||
//---
|
||||
|
||||
/*
|
||||
gupdate()
|
||||
Swaps the vram buffer sets.
|
||||
*/
|
||||
inline void gupdate(void)
|
||||
{
|
||||
current ^= 2;
|
||||
}
|
||||
|
||||
/*
|
||||
gclear()
|
||||
Clears the video ram.
|
||||
*/
|
||||
void gclear(void)
|
||||
{
|
||||
int *v1 = gray_lightVRAM();
|
||||
int *v2 = gray_darkVRAM();
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 256; i++) v1[i] = v2[i] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
gclear_area()
|
||||
Clears an area of the video ram. End points (x1, y1) and (x2, y2) are
|
||||
included.
|
||||
*/
|
||||
void gclear_area(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
display_useVRAM(gray_lightVRAM());
|
||||
dclear_area(x1, y1, x2, y2);
|
||||
display_useVRAM(gray_darkVRAM());
|
||||
dclear_area(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
/*
|
||||
greverse_area()
|
||||
Reverses an area of the vram. End points (x1, y1) and (x2, y2) are
|
||||
included.
|
||||
*/
|
||||
void greverse_area(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
display_useVRAM(gray_lightVRAM());
|
||||
dreverse_area(x1, y1, x2, y2);
|
||||
display_useVRAM(gray_darkVRAM());
|
||||
dreverse_area(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Local drawing functions.
|
||||
//---
|
||||
|
||||
/*
|
||||
gpixel()
|
||||
Puts a pixel in the vram.
|
||||
*/
|
||||
void gpixel(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);
|
||||
|
||||
int *v1 = gray_lightVRAM();
|
||||
int *v2 = gray_lightVRAM();
|
||||
|
||||
switch(color)
|
||||
{
|
||||
case Color_White:
|
||||
v1[offset] &= ~mask;
|
||||
v2[offset] &= ~mask;
|
||||
break;
|
||||
|
||||
case Color_Light:
|
||||
v1[offset] |= mask;
|
||||
v2[offset] &= ~mask;
|
||||
break;
|
||||
|
||||
case Color_Dark:
|
||||
v1[offset] &= ~mask;
|
||||
v2[offset] |= mask;
|
||||
break;
|
||||
|
||||
case Color_Black:
|
||||
v1[offset] |= mask;
|
||||
v2[offset] |= mask;
|
||||
break;
|
||||
|
||||
case Color_Invert:
|
||||
v1[offset] ^= mask;
|
||||
v2[offset] ^= mask;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
gline()
|
||||
Draws a line in the vram. Automatically optimizes special cases.
|
||||
*/
|
||||
void gline(int x1, int y1, int x2, int y2, enum Color color)
|
||||
{
|
||||
enum Color c1, c2;
|
||||
|
||||
if(color == Color_None) return;
|
||||
else if(color == Color_Invert) c1 = c2 = Color_Invert;
|
||||
else c1 = color & 1, c2 = color >> 1;
|
||||
|
||||
display_useVRAM(gray_lightVRAM());
|
||||
dline(x1, y1, x2, y2, c1);
|
||||
display_useVRAM(gray_darkVRAM());
|
||||
dline(x1, y1, x2, y2, c2);
|
||||
}
|
15
src/gray/greverse_area.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include <gray.h>
|
||||
#include <display.h>
|
||||
|
||||
/*
|
||||
greverse_area()
|
||||
Reverses an area of the vram. End points (x1, y1) and (x2, y2) are
|
||||
included.
|
||||
*/
|
||||
void greverse_area(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
display_useVRAM(gray_lightVRAM());
|
||||
dreverse_area(x1, y1, x2, y2);
|
||||
display_useVRAM(gray_darkVRAM());
|
||||
dreverse_area(x1, y1, x2, y2);
|
||||
}
|
502
src/keyboard.c
|
@ -1,502 +0,0 @@
|
|||
#include <keyboard.h>
|
||||
#include <timer.h>
|
||||
#include <mpu.h>
|
||||
|
||||
//---
|
||||
// Keyboard variables.
|
||||
//---
|
||||
|
||||
// These ones get modified by interrupts.
|
||||
static volatile unsigned char keyboard_state[10] = { 0 };
|
||||
static volatile int interrupt_flag = 0;
|
||||
|
||||
// Key statistics.
|
||||
static int repeat_first = 10, repeat_next = 2;
|
||||
static int last_key = KEY_NONE, last_repeats = 0, last_events = 0;
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Auxiliary functions.
|
||||
//---
|
||||
|
||||
/*
|
||||
sleep()
|
||||
Puts the CPU to sleep and waits for an interrupt.
|
||||
*/
|
||||
static void sleep(void)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"sleep\n\t"
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
getPressedKey()
|
||||
Finds a pressed key in the keyboard state and returns it.
|
||||
|
||||
@return A pressed key.
|
||||
*/
|
||||
static 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.
|
||||
*/
|
||||
static 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Interrupt management.
|
||||
//---
|
||||
|
||||
/*
|
||||
keyboard_interrupt()
|
||||
Callback for keyboard update. Allows keyboard analysis functions to
|
||||
wake only when keyboard interrupts happen.
|
||||
*/
|
||||
void keyboard_interrupt(void)
|
||||
{
|
||||
if(isSH3())
|
||||
keyboard_updateState_7705(keyboard_state);
|
||||
else
|
||||
keyboard_updateState_7305(keyboard_state);
|
||||
|
||||
interrupt_flag = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_init()
|
||||
Starts the keyboard timer.
|
||||
*/
|
||||
void keyboard_init(void)
|
||||
{
|
||||
timer_start(TIMER_KEYBOARD, 1700, TIMER_Po_256, keyboard_interrupt,
|
||||
0);
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_quit()
|
||||
Stops the keyboard timer.
|
||||
*/
|
||||
void keyboard_quit(void)
|
||||
{
|
||||
timer_stop(TIMER_KEYBOARD);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Keyboard configuration.
|
||||
//---
|
||||
|
||||
/*
|
||||
keyboard_setFrequency()
|
||||
Sets the keyboard frequency.
|
||||
|
||||
@arg frequency In Hz.
|
||||
*/
|
||||
void keyboard_setFrequency(int 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
keystate()
|
||||
Returns the address of the keyboard state array. The returned address
|
||||
is the handler's buffer, therefore it contains volatile data.
|
||||
|
||||
@return 10-byte keyboard state buffer.
|
||||
*/
|
||||
volatile unsigned char *keystate(void)
|
||||
{
|
||||
return keyboard_state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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);
|
||||
|
||||
// We need to update the last key data, in case multigetkey()
|
||||
// returns a single key, and getkey() is called a short time
|
||||
// after. Otherwise getkey() could re-send an event for this
|
||||
// key.
|
||||
if(number == 1)
|
||||
{
|
||||
last_key = keys[0];
|
||||
last_repeats = 0;
|
||||
last_events = 0;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@return Associated character.
|
||||
*/
|
||||
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;
|
||||
}
|
24
src/keyboard/getPressedKey.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <keyboard_internals.h>
|
||||
|
||||
/*
|
||||
getPressedKey()
|
||||
Finds a pressed key in the keyboard state and returns it.
|
||||
*/
|
||||
int getPressedKey(volatile unsigned char *keyboard_state)
|
||||
{
|
||||
int row = 1, column = 0;
|
||||
int state;
|
||||
if(keyboard_state[0] & 1) return KEY_AC_ON;
|
||||
|
||||
while(row <= 9 && !keyboard_state[row]) row++;
|
||||
if(row > 9) return KEY_NONE;
|
||||
|
||||
state = keyboard_state[row];
|
||||
while(!(state & 1))
|
||||
{
|
||||
state >>= 1;
|
||||
column++;
|
||||
}
|
||||
|
||||
return (column << 4) | row;
|
||||
}
|
54
src/keyboard/getPressedKeys.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include <keyboard_internals.h>
|
||||
|
||||
/*
|
||||
getPressedKeys()
|
||||
Find 'count' pressed keys in the keyboard state and fills the 'keys'
|
||||
array. Returns the number of actually-pressed keys found.
|
||||
*/
|
||||
int getPressedKeys(volatile unsigned char *keyboard_state, int *keys,
|
||||
int count)
|
||||
{
|
||||
int row = 1, column;
|
||||
int found = 0, actually_pressed;
|
||||
int state;
|
||||
if(count <= 0) return 0;
|
||||
|
||||
if(keyboard_state[0] & 1)
|
||||
{
|
||||
keys[found++] = KEY_AC_ON;
|
||||
count--;
|
||||
}
|
||||
|
||||
while(count && row <= 9)
|
||||
{
|
||||
while(row <= 9 && !keyboard_state[row]) row++;
|
||||
if(row > 9) break;
|
||||
|
||||
state = keyboard_state[row];
|
||||
column = 0;
|
||||
|
||||
while(count && column < 8)
|
||||
{
|
||||
if(state & 1)
|
||||
{
|
||||
keys[found++] = (column << 4) | row;
|
||||
count--;
|
||||
}
|
||||
|
||||
state >>= 1;
|
||||
column++;
|
||||
}
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
actually_pressed = found;
|
||||
|
||||
while(count)
|
||||
{
|
||||
keys[found++] = KEY_NONE;
|
||||
count--;
|
||||
}
|
||||
|
||||
return actually_pressed;
|
||||
}
|
113
src/keyboard/getkey.c
Normal file
|
@ -0,0 +1,113 @@
|
|||
#include <keyboard.h>
|
||||
#include <keyboard_internals.h>
|
||||
|
||||
/*
|
||||
getkey()
|
||||
Blocking function with auto-repeat and SHIFT modifying functionalities.
|
||||
Roughly reproduces the behavior of the system's GetKey().
|
||||
*/
|
||||
int getkey(void)
|
||||
{
|
||||
return getkey_opt(
|
||||
Getkey_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.
|
||||
*/
|
||||
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(keyboard_state);
|
||||
|
||||
// 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;
|
||||
}
|
26
src/keyboard/keyboard_config.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <keyboard.h>
|
||||
#include <keyboard_internals.h>
|
||||
|
||||
/*
|
||||
keyboard_setFrequency()
|
||||
Sets the keyboard frequency (in Hz).
|
||||
*/
|
||||
void keyboard_setFrequency(int frequency)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_setRepeatRate()
|
||||
Sets the default repeat rate for key events. The unit for the argument
|
||||
is the keyboard period.
|
||||
For example at 32 Hz, values of (20, 4) will imitate the system
|
||||
default.
|
||||
*/
|
||||
void keyboard_setRepeatRate(int first, int next)
|
||||
{
|
||||
if(first < 0) first = 0;
|
||||
if(next < 0) next = 0;
|
||||
|
||||
repeat_first = first;
|
||||
repeat_next = next;
|
||||
}
|
34
src/keyboard/keyboard_internals.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef _KEYBOARD_INTERNALS_H
|
||||
#define _KEYBOARD_INTERNALS_H 1
|
||||
|
||||
#include <keyboard.h>
|
||||
|
||||
// Keyboard variables.
|
||||
extern volatile unsigned char keyboard_state[10];
|
||||
extern volatile int interrupt_flag = 0;
|
||||
|
||||
// Key statistics.
|
||||
extern int repeat_first, repeat_next;
|
||||
extern int last_key, last_repeats, last_events;
|
||||
|
||||
/*
|
||||
sleep()
|
||||
Puts the CPU into sleep until an interrupt request is accepted.
|
||||
*/
|
||||
void sleep(void);
|
||||
|
||||
/*
|
||||
getPressedKey()
|
||||
Finds a pressed key in the keyboard state and returns it.
|
||||
*/
|
||||
int getPressedKey(volatile unsigned char *keyboard_state);
|
||||
|
||||
/*
|
||||
getPressedKeys()
|
||||
Find 'count' pressed keys in the keyboard state and fills the 'keys'
|
||||
array. Returns the number of actually-pressed keys found.
|
||||
*/
|
||||
int getPressedKeys(volatile unsigned char *keyboard_state, int *keys,
|
||||
int count);
|
||||
|
||||
#endif // _KEYBOARD_INTERNALS_H
|
56
src/keyboard/keyboard_interrupt.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include <keyboard.h>
|
||||
#include <timer.h>
|
||||
#include <mpu.h>
|
||||
|
||||
#include <keyboard_internals.h>
|
||||
|
||||
//---
|
||||
// Keyboard variables.
|
||||
//---
|
||||
|
||||
// These ones get modified by interrupts.
|
||||
volatile unsigned char keyboard_state[10] = { 0 };
|
||||
volatile int interrupt_flag = 0;
|
||||
|
||||
// Key statistics.
|
||||
int repeat_first = 10, repeat_next = 2;
|
||||
int last_key = KEY_NONE, last_repeats = 0, last_events = 0;
|
||||
|
||||
|
||||
//---
|
||||
// Interrupt management.
|
||||
//---
|
||||
|
||||
/*
|
||||
keyboard_interrupt()
|
||||
Callback for keyboard update. Allows keyboard analysis functions to
|
||||
wake only when keyboard interrupts happen.
|
||||
*/
|
||||
void keyboard_interrupt(void)
|
||||
{
|
||||
if(isSH3())
|
||||
keyboard_updateState_7705(keyboard_state);
|
||||
else
|
||||
keyboard_updateState_7305(keyboard_state);
|
||||
|
||||
interrupt_flag = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_init()
|
||||
Starts the keyboard timer.
|
||||
*/
|
||||
void keyboard_init(void)
|
||||
{
|
||||
timer_start(TIMER_KEYBOARD, 1700, TIMER_Po_256, keyboard_interrupt,
|
||||
0);
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_quit()
|
||||
Stops the keyboard timer.
|
||||
*/
|
||||
void keyboard_quit(void)
|
||||
{
|
||||
timer_stop(TIMER_KEYBOARD);
|
||||
}
|
25
src/keyboard/keyboard_misc.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include <keyboard.h>
|
||||
#include <keyboard_internals.h>
|
||||
|
||||
/*
|
||||
keylast()
|
||||
Returns the matrix code of the last pressed key. If repeat_count is
|
||||
non-NULL, it is set to the number of repetitions.
|
||||
*/
|
||||
int keylast(int *repeat_count)
|
||||
{
|
||||
if(repeat_count) *repeat_count = last_repeats;
|
||||
return last_key;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
keystate()
|
||||
Returns the address of the keyboard state array. The returned address
|
||||
is the handler's buffer, therefore it contains volatile data.
|
||||
*/
|
||||
volatile unsigned char *keystate(void)
|
||||
{
|
||||
return keyboard_state;
|
||||
}
|
146
src/keyboard/keyboard_sh7305.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: keyboard analyzer
|
||||
//
|
||||
// Probably the most difficult hardware interaction. There is very few
|
||||
// documentation on how the system actually analyzes the keyboard. While
|
||||
// disassembling syscalls reveals the following procedure (which was
|
||||
// already documented by SimonLothar), there is nothing about the
|
||||
// detection problems of the multi-getkey system.
|
||||
//
|
||||
//---
|
||||
|
||||
#include <keyboard.h>
|
||||
#include <7305.h>
|
||||
|
||||
//---
|
||||
// Keyboard management.
|
||||
//---
|
||||
|
||||
/*
|
||||
kdelay()
|
||||
Should sleep during a few milliseconds. Well...
|
||||
This delay has a great influence on key detection. Here are a few
|
||||
observations of what happens with common numbers of "nop" (without
|
||||
overclock). Take care of the effect of overclock !
|
||||
|
||||
Column effects
|
||||
May have something to do with register HIZCRB not being used here. When
|
||||
many keys on the same column are pressed, other keys of the same column
|
||||
may be triggered.
|
||||
|
||||
- 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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_updateState()
|
||||
Updates the keyboard state.
|
||||
*/
|
||||
void keyboard_updateState_7305(volatile unsigned char *keyboard_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 10; i++) keyboard_state[i] = krow(i);
|
||||
}
|
139
src/keyboard/keyboard_sh7705.c
Normal file
|
@ -0,0 +1,139 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: keyboard analyzer
|
||||
//
|
||||
// Probably the most difficult hardware interaction. There is very few
|
||||
// documentation on how the system actually analyzes the keyboard. While
|
||||
// disassembling syscalls reveals the following procedure (which was
|
||||
// already documented by SimonLothar), there is nothing about the
|
||||
// detection problems of the multi-getkey system.
|
||||
//
|
||||
//---
|
||||
|
||||
#include <keyboard.h>
|
||||
#include <7705.h>
|
||||
|
||||
//---
|
||||
// Keyboard management.
|
||||
//---
|
||||
|
||||
/*
|
||||
kdelay()
|
||||
Used to be a low-level sleep using the watchdog, as in the system. This
|
||||
way seems OK at least, and it doesn't create column effects as for
|
||||
SH7305.
|
||||
*/
|
||||
static void kdelay(void)
|
||||
{
|
||||
#define r4(str) str str str str
|
||||
|
||||
__asm__
|
||||
(
|
||||
r4("nop\n\t")
|
||||
r4("nop\n\t")
|
||||
r4("nop\n\t")
|
||||
);
|
||||
|
||||
#undef r4
|
||||
|
||||
/* Watchdog delay version.
|
||||
const int delay = 0xf4;
|
||||
|
||||
// Disabling the watchdog timer interrupt and resetting the
|
||||
// configuration. Setting the delay.
|
||||
INTC.IPRB.BIT._WDT = 0;
|
||||
WDT.WTCSR.WRITE = 0xa500;
|
||||
WDT.WTCNT.WRITE = 0x5a00 | (delay & 0xff);
|
||||
|
||||
// Counting on Po/256.
|
||||
WDT.WTCSR.WRITE = 0xa505;
|
||||
// Starting the timer (sets again to Po/256).
|
||||
WDT.WTCSR.WRITE = 0xa585;
|
||||
|
||||
// Waiting until it overflows (delaying), then clearing the overflow
|
||||
// flag.
|
||||
while((WDT.WTCSR.READ.BYTE & 0x08) == 0);
|
||||
WDT.WTCSR.WRITE = 0xa500 | (WDT.WTCSR.READ.BYTE & 0xf7);
|
||||
|
||||
// Resetting the configuration and the counter.
|
||||
WDT.WTCSR.WRITE = 0xa500;
|
||||
WDT.WTCSR.WRITE = 0x5a00;
|
||||
|
||||
// Enabling back the watchdog timer interrupt.
|
||||
INTC.IPRB.BIT._WDT = GINT_INTP_WDT;
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
krow()
|
||||
Reads a keyboard row.
|
||||
*/
|
||||
static 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;
|
||||
}
|
||||
|
||||
/*
|
||||
keyboard_updateState()
|
||||
Updates the keyboard state.
|
||||
*/
|
||||
void keyboard_updateState_7705(volatile unsigned char *keyboard_state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 10; i++) keyboard_state[i] = krow(i);
|
||||
}
|
36
src/keyboard/keychar.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <keyboard.h>
|
||||
|
||||
/*
|
||||
keychar()
|
||||
Returns the ASCII character associated with a key, or 0 for control
|
||||
keys.
|
||||
*/
|
||||
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);
|
||||
|
||||
return (key & MOD_ALPHA) ? alpha[id] : flat[id];
|
||||
}
|
17
src/keyboard/keyid.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <keyboard.h>
|
||||
|
||||
/*
|
||||
keyid()
|
||||
Returns a non-matrix key code that can be used for array subscript.
|
||||
Ignores modifiers.
|
||||
*/
|
||||
int keyid(int key)
|
||||
{
|
||||
if(key < 0) return -1;
|
||||
key &= MOD_CLEAR;
|
||||
|
||||
int row = 9 - (key & 0x0f);
|
||||
int column = 6 - ((key & 0xf0) >> 4);
|
||||
|
||||
return 6 * row + column;
|
||||
}
|
17
src/keyboard/keytype.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <keyboard.h>
|
||||
|
||||
/*
|
||||
keytype()
|
||||
Returns a key's type. Ignores modifiers.
|
||||
*/
|
||||
enum KeyType keytype(int key)
|
||||
{
|
||||
key &= MOD_CLEAR;
|
||||
|
||||
if(key == KEY_UP || key == KEY_RIGHT || key == KEY_DOWN ||
|
||||
key == KEY_LEFT) return KeyType_Arrow;
|
||||
|
||||
if((key & 0x0f) == 0x09) return KeyType_Function;
|
||||
|
||||
return keychar(key) ? KeyType_Character : KeyType_Control;
|
||||
}
|
46
src/keyboard/multigetkey.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include <keyboard.h>
|
||||
#include <keyboard_internals.h>
|
||||
|
||||
/*
|
||||
multigetkey()
|
||||
Listens the keyboard for simultaneous key hits. Uses the same options
|
||||
as getkey_opt().
|
||||
multigetkey() fills the 'keys' array with 'count' key codes, adding
|
||||
KEY_NOKEY if less than 'count' keys are pressed.
|
||||
Be aware that rectangle and column effects can make multigetkey() read
|
||||
unpressed keys as pressed (see documentation for more information).
|
||||
Setting count = 3 is generally safe.
|
||||
The function returns after 'max_cycles' if no key is pressed.
|
||||
*/
|
||||
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(keyboard_state, keys, count);
|
||||
|
||||
// We need to update the last key data, in case multigetkey()
|
||||
// returns a single key, and getkey() is called a short time
|
||||
// after. Otherwise getkey() could re-send an event for this
|
||||
// key.
|
||||
if(number == 1)
|
||||
{
|
||||
last_key = keys[0];
|
||||
last_repeats = 0;
|
||||
last_events = 0;
|
||||
}
|
||||
|
||||
if(number) return;
|
||||
}
|
||||
|
||||
// When no key was pressed during the given delay... (no need to fill
|
||||
// the array, it has already been done by getPressedKeys()).
|
||||
return;
|
||||
}
|
13
src/keyboard/sleep.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include <keyboard_internals.h>
|
||||
|
||||
/*
|
||||
sleep()
|
||||
Puts the CPU to sleep and waits for an interrupt.
|
||||
*/
|
||||
void sleep(void)
|
||||
{
|
||||
__asm__
|
||||
(
|
||||
"sleep\n\t"
|
||||
);
|
||||
}
|
159
src/mpu/gint_sh7305.c
Normal file
|
@ -0,0 +1,159 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: sh7305 interrupt handler
|
||||
//
|
||||
// Of course all the work related to interrupts is heavily platform-
|
||||
// dependent. This module handles interrupts and configures the MPU to
|
||||
// save and restore the system's configuration when execution ends.
|
||||
//
|
||||
//---
|
||||
|
||||
#include <gint.h>
|
||||
#include <timer.h>
|
||||
#include <7305.h>
|
||||
|
||||
//---
|
||||
// Interrupt codes.
|
||||
//---
|
||||
|
||||
#define IC_RTC_PRI 0xaa0
|
||||
#define IC_KEYSC 0xbe0
|
||||
#define IC_TMU0_TUNI0 0x400
|
||||
#define IC_TMU0_TUNI1 0x420
|
||||
#define IC_TMU0_TUNI2 0x440
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Interrupt handler.
|
||||
//---
|
||||
|
||||
void gint_7305(void)
|
||||
{
|
||||
volatile unsigned int *intevt = (unsigned int *)0xff000028;
|
||||
unsigned int code = *intevt;
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case IC_RTC_PRI:
|
||||
RTC.RCR2.PEF = 0;
|
||||
break;
|
||||
|
||||
case IC_TMU0_TUNI0:
|
||||
timer_interrupt(TIMER_TMU0);
|
||||
break;
|
||||
|
||||
case IC_TMU0_TUNI1:
|
||||
timer_interrupt(TIMER_TMU1);
|
||||
break;
|
||||
|
||||
case IC_TMU0_TUNI2:
|
||||
timer_interrupt(TIMER_TMU2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Setup.
|
||||
//---
|
||||
|
||||
static unsigned short ipr[12];
|
||||
static unsigned char rcr2;
|
||||
// Saves of the keyboard registers. Could be better.
|
||||
static unsigned short inj1, inj2, det;
|
||||
static unsigned char data1, data2, keys, reg;
|
||||
|
||||
static void gint_priority_lock_7305(void)
|
||||
{
|
||||
// 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 non-handled
|
||||
// interrupts.
|
||||
INTX.IPRA.WORD = 0x0000;
|
||||
INTX.IPRB.WORD = 0x0000;
|
||||
INTX.IPRC.WORD = 0x0000;
|
||||
INTX.IPRD.WORD = 0x0000;
|
||||
INTX.IPRE.WORD = 0x0000;
|
||||
INTX.IPRF.WORD = 0x0000;
|
||||
INTX.IPRG.WORD = 0x0000;
|
||||
INTX.IPRH.WORD = 0x0000;
|
||||
INTX.IPRI.WORD = 0x0000;
|
||||
INTX.IPRJ.WORD = 0x0000;
|
||||
INTX.IPRK.WORD = 0x0000;
|
||||
INTX.IPRL.WORD = 0x0000;
|
||||
|
||||
// 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;
|
||||
INTX.IPRA.TMU0_0 = GINT_INTP_KEY;
|
||||
INTX.IPRA.TMU0_1 = GINT_INTP_GRAY;
|
||||
INTX.IPRA.TMU0_2 = GINT_INTP_TIMER;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Saving the RTC configuration.
|
||||
rcr2 = RTC.RCR2.BYTE;
|
||||
// Disabling RTC interrupts by default.
|
||||
RTC.RCR2.BYTE = 0x09;
|
||||
}
|
||||
|
||||
void gint_stop_7305(void)
|
||||
{
|
||||
gint_priority_unlock_7305();
|
||||
|
||||
// Restoring the RTC configuration.
|
||||
RTC.RCR2.BYTE = rcr2;
|
||||
}
|
125
src/mpu/gint_sh7705.c
Normal file
|
@ -0,0 +1,125 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: sh7705 interrupt handler
|
||||
//
|
||||
// Of course all the work related to interrupts is heavily platform-
|
||||
// dependent. This module handles interrupts and configures the MPU to
|
||||
// save and restore the system's configuration when execution ends.
|
||||
//
|
||||
//---
|
||||
|
||||
#include <gint.h>
|
||||
#include <timer.h>
|
||||
#include <7705.h>
|
||||
|
||||
//---
|
||||
// Interrupt codes.
|
||||
//---
|
||||
|
||||
#define IC_RTC_PRI 0x4a0
|
||||
#define IC_PINT07 0x700
|
||||
#define IC_TMU0_TUNI0 0x400
|
||||
#define IC_TMU1_TUNI1 0x420
|
||||
#define IC_TMU2_TUNI2 0x440
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Interrupt handler.
|
||||
//---
|
||||
|
||||
void gint_7705(void)
|
||||
{
|
||||
volatile unsigned int *intevt2 = (unsigned int *)0xa4000000;
|
||||
unsigned int code = *intevt2;
|
||||
|
||||
switch(code)
|
||||
{
|
||||
case IC_RTC_PRI:
|
||||
RTC.RCR2.BIT.PEF = 0;
|
||||
break;
|
||||
|
||||
case IC_TMU0_TUNI0:
|
||||
timer_interrupt(TIMER_TMU0);
|
||||
break;
|
||||
|
||||
case IC_TMU1_TUNI1:
|
||||
timer_interrupt(TIMER_TMU1);
|
||||
break;
|
||||
|
||||
case IC_TMU2_TUNI2:
|
||||
timer_interrupt(TIMER_TMU2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Setup.
|
||||
//---
|
||||
|
||||
static unsigned short iprs[8];
|
||||
static unsigned char rcr2;
|
||||
|
||||
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;
|
||||
INTC.IPRA.BIT._TMU0 = GINT_INTP_KEY;
|
||||
INTC.IPRA.BIT._TMU1 = GINT_INTP_GRAY;
|
||||
INTC.IPRA.BIT._TMU2 = GINT_INTP_TIMER;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Saving the RTC configuration.
|
||||
rcr2 = RTC.RCR2.BYTE;
|
||||
// Disabling RTC interrupts by default.
|
||||
RTC.RCR2.BYTE = 0x09;
|
||||
}
|
||||
|
||||
void gint_stop_7705(void)
|
||||
{
|
||||
gint_priority_unlock_7705();
|
||||
|
||||
// Restoring the RTC configuration.
|
||||
RTC.RCR2.BYTE = rcr2;
|
||||
}
|
|
@ -1,3 +1,11 @@
|
|||
//---
|
||||
//
|
||||
// gint core module: mpu
|
||||
//
|
||||
// Determines which kind of MPU is running the program.
|
||||
//
|
||||
//---
|
||||
|
||||
#include <mpu.h>
|
||||
|
||||
enum MPU MPU_CURRENT;
|
||||
|
@ -6,21 +14,18 @@ enum MPU MPU_CURRENT;
|
|||
getMPU()
|
||||
|
||||
Returns the MPU identifier of the calculator.
|
||||
Thanks to SimonLothar for this function and related informations.
|
||||
Thanks to SimonLothar for this function and related information.
|
||||
|
||||
Processor version register (PVR) and product control register (PRR)
|
||||
hold information about the MPU version but are only accessible for
|
||||
hold information about the MPU version, they but are only accessible on
|
||||
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.
|
||||
To detect SH-3-based MPUs, this function uses port L control register
|
||||
(PLCR), whose bits 8 to 15 cannot be set with SH7337 where bits 8 to 11
|
||||
can be set with SH7355.
|
||||
|
||||
Additionally, the CPU core ID register (CPIDR) at 0xff000048 returns 1
|
||||
on SH7305.
|
||||
|
||||
@return MPU identifier as integer value.
|
||||
*/
|
||||
|
||||
enum MPU getMPU(void)
|
||||
{
|
||||
// Processor version register.
|
27
src/mpu/various_7305.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <gint.h>
|
||||
#include <timer.h>
|
||||
#include <keyboard.h>
|
||||
#include <7305.h>
|
||||
|
||||
//---
|
||||
// Various MPU-dependent procedures.
|
||||
//---
|
||||
|
||||
/*
|
||||
gint_setRTCFrequency()
|
||||
Sets the RTC interrupt frequency and enables interrupts.
|
||||
*/
|
||||
void gint_setRTCFrequency_7305(enum RTCFrequency frequency)
|
||||
{
|
||||
if(frequency < 1 || frequency > 7) return;
|
||||
RTC.RCR2.BYTE = (frequency << 4) | 0x09;
|
||||
}
|
||||
|
||||
/*
|
||||
gint_getRTCFrequency()
|
||||
Returns the RTC interrupt frequency.
|
||||
*/
|
||||
enum RTCFrequency gint_getRTCFrequency_7305(void)
|
||||
{
|
||||
return (RTC.RCR2.BYTE & 0x70) >> 4;
|
||||
}
|
27
src/mpu/various_7705.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <gint.h>
|
||||
#include <timer.h>
|
||||
#include <keyboard.h>
|
||||
#include <7705.h>
|
||||
|
||||
//---
|
||||
// Various MPU-dependent procedures.
|
||||
//---
|
||||
|
||||
/*
|
||||
gint_setRTCFrequency()
|
||||
Sets the RTC interrupt frequency and enables interrupts.
|
||||
*/
|
||||
void gint_setRTCFrequency_7705(enum RTCFrequency frequency)
|
||||
{
|
||||
if(frequency < 1 || frequency > 7) return;
|
||||
RTC.RCR2.BYTE = (frequency << 4) | 0x09;
|
||||
}
|
||||
|
||||
/*
|
||||
gint_getRTCFrequency()
|
||||
Returns the RTC interrupt frequency.
|
||||
*/
|
||||
enum RTCFrequency gint_getRTCFrequency_7705(void)
|
||||
{
|
||||
return (RTC.RCR2.BYTE & 0x70) >> 4;
|
||||
}
|
|
@ -1,27 +1,11 @@
|
|||
/*
|
||||
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.
|
||||
vram-related operations use longword-base operations.
|
||||
*/
|
||||
void screen_display(const void *ptr)
|
||||
{
|
|
@ -1,16 +1,13 @@
|
|||
/*
|
||||
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.
|
||||
gint standard module: setjmp
|
||||
|
||||
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.
|
||||
Long jumps. The register contents are saved in a buffer when setjmp()
|
||||
is called and restored at any time when longjmp() performs the jump.
|
||||
|
||||
This is 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.
|
||||
This is based on a trick that uses pr ; the user program is resumed
|
||||
after the setjmp() call when longjmp() is invoked but this is not
|
||||
setjmp() that returns. longjmp() restores the pr value that was saved
|
||||
by setjmp() and performs an rts instruction.
|
||||
|
||||
setjmp() returns 0 when called to set up the jump point and a non-zero
|
||||
value when invoked through a long jump. If 0 is given as return value
|
||||
|
@ -54,7 +51,7 @@ _setjmp:
|
|||
|
||||
_longjmp:
|
||||
/* Restoring the system and control registers. Restoring pr is actually
|
||||
what performs the jump -- and makes the user program thinks that
|
||||
what performs the jump -- and makes the user program think that
|
||||
setjmp() has returned. */
|
||||
lds.l @r4+, pr
|
||||
lds.l @r4+, macl
|
|
@ -1,17 +1,9 @@
|
|||
#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)
|
||||
{
|
||||
|
@ -82,53 +74,3 @@ void *memcpy(void *d, const void *s, size_t 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;
|
||||
}
|
48
src/string/memset.c
Normal file
|
@ -0,0 +1,48 @@
|
|||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
memset()
|
||||
Sets the contents of a memory area. A smart copy is performed.
|
||||
*/
|
||||
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;
|
||||
}
|
290
src/tales.c
|
@ -1,290 +0,0 @@
|
|||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <tales.h>
|
||||
|
||||
static struct Font *font;
|
||||
|
||||
//---
|
||||
// Local functions.
|
||||
//---
|
||||
|
||||
/*
|
||||
getCharacterIndex()
|
||||
Returns the index of a character in a font data area depending on the
|
||||
font format and the size of the characters.
|
||||
|
||||
@arg character
|
||||
|
||||
@return Index in data area (as long array). Returns -1 when the
|
||||
character does not belong to the font format set.
|
||||
*/
|
||||
static int getCharacterIndex(int c)
|
||||
{
|
||||
const char *data = (const char *)&font->glyphs;
|
||||
int index, current;
|
||||
int offset;
|
||||
int width, bits;
|
||||
|
||||
c &= 0x7f;
|
||||
|
||||
|
||||
|
||||
// Getting the character index in the glyph array.
|
||||
|
||||
if(font->format == FontFormat_Ascii) index = c;
|
||||
else if(font->format == FontFormat_Print) index = c - 32;
|
||||
|
||||
else switch(font->format)
|
||||
{
|
||||
case FontFormat_Numeric:
|
||||
if(!isdigit(c)) return -1;
|
||||
index = c - '0';
|
||||
break;
|
||||
case FontFormat_LowerCase:
|
||||
if(!islower(c)) return -1;
|
||||
index = c - 'a';
|
||||
break;
|
||||
case FontFormat_UpperCase:
|
||||
if(!isupper(c)) return -1;
|
||||
index = c - 'A';
|
||||
break;
|
||||
case FontFormat_Letters:
|
||||
if(!isalpha(c)) return -1;
|
||||
index = c - 'A' - ('a' - 'z') * (c >= 'a');
|
||||
break;
|
||||
case FontFormat_Common:
|
||||
if(!isalnum(c)) return -1;
|
||||
index = c - '0' - ('A' - '9') * (c >= 'A') -
|
||||
('a' - 'z') * (c >= 'a');
|
||||
break;
|
||||
case FontFormat_Unknown:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Reaching the character offset.
|
||||
|
||||
current = index & ~7;
|
||||
offset = font->index[current >> 3];
|
||||
|
||||
while(current < index)
|
||||
{
|
||||
width = data[offset << 2];
|
||||
bits = font->data_height * width + 8;
|
||||
|
||||
offset += (bits + 31) >> 5;
|
||||
current++;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/*
|
||||
operate()
|
||||
Operates on the vram using the given operators. The x-coordinate should
|
||||
be a multiple of 32.
|
||||
|
||||
@arg operators Operator array.
|
||||
@arg height Number of operators (height of text).
|
||||
@arg x
|
||||
@arg y
|
||||
*/
|
||||
static void operate(uint32_t *operators, int height, int x, int y)
|
||||
{
|
||||
int *vram = display_getCurrentVRAM();
|
||||
int vram_offset = (x >> 5) + (y << 2);
|
||||
int i;
|
||||
|
||||
for(i = 0; i < height; i++)
|
||||
{
|
||||
// TODO BLENDING MODES //
|
||||
vram[vram_offset] |= operators[i];
|
||||
|
||||
vram_offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
update()
|
||||
Updates the operators using the given glyph. The operation will not be
|
||||
complete if there are not enough bits available in the operator data.
|
||||
In this case the offset will become negative, which means that the
|
||||
calling procedure has to call operate() and re-call update().
|
||||
|
||||
@arg operators Operator array.
|
||||
@arg height Number of operators.
|
||||
@arg available Number of free bits in the operators (lower
|
||||
bits).
|
||||
@arg glyph Glyph data, including meta-data.
|
||||
|
||||
@return Number of bits available after the operation. May be negative:
|
||||
in this case, call operate() and update() again.
|
||||
*/
|
||||
static int update(uint32_t *operators, int height, int available,
|
||||
uint32_t *glyph)
|
||||
{
|
||||
// Glyph width.
|
||||
int width = glyph[0] >> 24;
|
||||
int i;
|
||||
|
||||
// The glyph mask extracts 'width' bits at the left. The partial mask
|
||||
// is used when there are less than 'width' bits available in the
|
||||
// current data longword.
|
||||
uint32_t glyph_mask = 0xffffffff << (32 - width);
|
||||
uint32_t partial_mask;
|
||||
|
||||
int shift;
|
||||
uint32_t line;
|
||||
|
||||
// Current data longword, next data array index, and number of bits
|
||||
// still available in 'data'.
|
||||
uint32_t data = glyph[0] << 8;
|
||||
int data_index = 1;
|
||||
int bits_available = 24;
|
||||
|
||||
for(i = 0; i < height; i++)
|
||||
{
|
||||
shift = 32 - available;
|
||||
|
||||
// Getting the next 'width' bits. In some cases these bits will
|
||||
// intersect two different longs.
|
||||
line = data & glyph_mask;
|
||||
line = (shift >= 0) ? (line >> shift) : (line << -shift);
|
||||
operators[i] |= line;
|
||||
|
||||
data <<= width;
|
||||
bits_available -= width;
|
||||
|
||||
// Continue until they do.
|
||||
if(bits_available >= 0) continue;
|
||||
|
||||
// Computing a special mask that extracts just the number of
|
||||
// bits missing, and loading a new data longword.
|
||||
partial_mask = 0xffffffff << (32 + bits_available);
|
||||
data = glyph[data_index++];
|
||||
shift += width + bits_available;
|
||||
|
||||
if(shift <= 31)
|
||||
{
|
||||
line = data & partial_mask;
|
||||
line = (shift >= 0) ? (line >> shift) :
|
||||
(line << -shift);
|
||||
operators[i] |= line;
|
||||
}
|
||||
|
||||
data <<= -bits_available;
|
||||
bits_available += 32;
|
||||
}
|
||||
|
||||
return available - width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Public API.
|
||||
//---
|
||||
|
||||
/*
|
||||
print_configure()
|
||||
Sets the font and mode to use for the following print operations.
|
||||
|
||||
@arg font
|
||||
@arg mode
|
||||
*/
|
||||
void print_configure(struct Font *next_font)
|
||||
{
|
||||
font = next_font;
|
||||
}
|
||||
|
||||
/*
|
||||
print_raw()
|
||||
Prints the given string, without any analysis.
|
||||
|
||||
@arg str
|
||||
@arg x
|
||||
@arg y
|
||||
*/
|
||||
void print_raw(const char *str, int x, int y)
|
||||
{
|
||||
// Operator data, and number of available bits in the operators (which
|
||||
// is the same for all operators, since they are treated equally).
|
||||
uint32_t *operators;
|
||||
int available;
|
||||
|
||||
// Raw glyph data, each glyph being represented by one or several
|
||||
// longwords, and an index in this array.
|
||||
uint32_t *data = (uint32_t *)font->glyphs;
|
||||
int index;
|
||||
|
||||
// Height of each glyph. This value is constant because the storage
|
||||
// format requires it: it allows greater optimization.
|
||||
int height;
|
||||
|
||||
|
||||
|
||||
if(!font) return;
|
||||
|
||||
// Allocating data. There will be one operator for each line.
|
||||
height = font->data_height;
|
||||
if(x > 127 || y > 63 || y <= -height) return;
|
||||
|
||||
operators = calloc(height, sizeof(uint32_t));
|
||||
if(!operators) return;
|
||||
|
||||
// Computing the initial operator offset to have 32-aligned operators.
|
||||
// This allows to write directly video ram longs instead of having to
|
||||
// shift operators, and do all the vram operation twice.
|
||||
available = 32 - (x & 31);
|
||||
x &= ~31;
|
||||
|
||||
// Displaying character after another.
|
||||
while(*str)
|
||||
{
|
||||
index = getCharacterIndex(*str++);
|
||||
if(index < 0) continue;
|
||||
|
||||
// Updating the operators.
|
||||
available = update(operators, height, available, data + index);
|
||||
|
||||
// Continue until operators are full (this includes an
|
||||
// additional bit to add a space between each character).
|
||||
if(available > 1)
|
||||
{
|
||||
available--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// When operators are full, updating the video ram and
|
||||
// preparing the operators for another row.
|
||||
|
||||
operate(operators, height, x, y);
|
||||
x += 32;
|
||||
if(x > 96) break;
|
||||
|
||||
memset(operators, 0, height << 2);
|
||||
if(available >= 0)
|
||||
{
|
||||
available = 31 + available;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finishing update, in case it has been only partially done,
|
||||
// because there was not enough bits available to fit all the
|
||||
// information. Also adding a space, assuming that characters
|
||||
// aren't more than 30 bits wide.
|
||||
available += 32 + (data[index] >> 24);
|
||||
available = update(operators, height, available, data + index);
|
||||
available--;
|
||||
}
|
||||
|
||||
// Final operation.
|
||||
if(x <= 96 && available < 32) operate(operators, height, x, y);
|
||||
|
||||
free(operators);
|
||||
}
|
88
src/tales/dtext.c
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include <tales_internals.h>
|
||||
#include <tales.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
dtext()
|
||||
Prints the given string, without any analysis.
|
||||
*/
|
||||
void dtext(const char *str, int x, int y)
|
||||
{
|
||||
// Operator data, and number of available bits in the operators (which
|
||||
// is the same for all operators, since they are treated equally).
|
||||
uint32_t *operators;
|
||||
int available;
|
||||
|
||||
// Raw glyph data, each glyph being represented by one or several
|
||||
// longwords, and an index in this array.
|
||||
uint32_t *data = (uint32_t *)font->glyphs;
|
||||
int index;
|
||||
|
||||
// Height of each glyph. This value is constant because the storage
|
||||
// format requires it: it allows greater optimization.
|
||||
int height;
|
||||
|
||||
|
||||
|
||||
if(!font) return;
|
||||
|
||||
// Allocating data. There will be one operator for each line.
|
||||
height = font->data_height;
|
||||
if(x > 127 || y > 63 || y <= -height) return;
|
||||
|
||||
operators = calloc(height, sizeof(uint32_t));
|
||||
if(!operators) return;
|
||||
|
||||
// Computing the initial operator offset to have 32-aligned operators.
|
||||
// This allows to write directly video ram longs instead of having to
|
||||
// shift operators, and do all the vram operation twice.
|
||||
available = 32 - (x & 31);
|
||||
x &= ~31;
|
||||
|
||||
// Displaying character after another.
|
||||
while(*str)
|
||||
{
|
||||
index = getCharacterIndex(*str++);
|
||||
if(index < 0) continue;
|
||||
|
||||
// Updating the operators.
|
||||
available = update(operators, height, available, data + index);
|
||||
|
||||
// Continue until operators are full (this includes an
|
||||
// additional bit to add a space between each character).
|
||||
if(available > 1)
|
||||
{
|
||||
available--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// When operators are full, updating the video ram and
|
||||
// preparing the operators for another row.
|
||||
|
||||
operate(operators, height, x, y);
|
||||
x += 32;
|
||||
if(x > 96) break;
|
||||
|
||||
memset(operators, 0, height << 2);
|
||||
if(available >= 0)
|
||||
{
|
||||
available = 31 + available;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Finishing update, in case it has been only partially done,
|
||||
// because there was not enough bits available to fit all the
|
||||
// information. Also adding a space, assuming that characters
|
||||
// aren't more than 30 bits wide.
|
||||
available += 32 + (data[index] >> 24);
|
||||
available = update(operators, height, available, data + index);
|
||||
available--;
|
||||
}
|
||||
|
||||
// Final operation.
|
||||
if(x <= 96 && available < 32) operate(operators, height, x, y);
|
||||
|
||||
free(operators);
|
||||
}
|
11
src/tales/tales_configuration.c
Normal file
|
@ -0,0 +1,11 @@
|
|||
#include <tales_internals.h>
|
||||
#include <tales.h>
|
||||
|
||||
/*
|
||||
text_configure()
|
||||
Sets the font and mode to use for the following print operations.
|
||||
*/
|
||||
void text_configure(struct Font *font)
|
||||
{
|
||||
font = next_font;
|
||||
}
|
164
src/tales/tales_internals.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include <tales_internals.h>
|
||||
#include <ctype.h>
|
||||
|
||||
struct Font *font;
|
||||
|
||||
/*
|
||||
getCharacterIndex()
|
||||
Returns the index of a character in a font data area depending on the
|
||||
font format and the size of the characters. Returns the index in the
|
||||
data area, as long array, or -1 when the character does not belong to
|
||||
the font format set.
|
||||
*/
|
||||
int getCharacterIndex(int c)
|
||||
{
|
||||
const char *data = (const char *)&font->glyphs;
|
||||
int index, current;
|
||||
int offset;
|
||||
int width, bits;
|
||||
|
||||
c &= 0x7f;
|
||||
|
||||
|
||||
|
||||
// Getting the character index in the glyph array.
|
||||
|
||||
if(font->format == FontFormat_Ascii) index = c;
|
||||
else if(font->format == FontFormat_Print) index = c - 32;
|
||||
|
||||
else switch(font->format)
|
||||
{
|
||||
case FontFormat_Numeric:
|
||||
if(!isdigit(c)) return -1;
|
||||
index = c - '0';
|
||||
break;
|
||||
case FontFormat_LowerCase:
|
||||
if(!islower(c)) return -1;
|
||||
index = c - 'a';
|
||||
break;
|
||||
case FontFormat_UpperCase:
|
||||
if(!isupper(c)) return -1;
|
||||
index = c - 'A';
|
||||
break;
|
||||
case FontFormat_Letters:
|
||||
if(!isalpha(c)) return -1;
|
||||
index = c - 'A' - ('a' - 'z') * (c >= 'a');
|
||||
break;
|
||||
case FontFormat_Common:
|
||||
if(!isalnum(c)) return -1;
|
||||
index = c - '0' - ('A' - '9') * (c >= 'A') -
|
||||
('a' - 'z') * (c >= 'a');
|
||||
break;
|
||||
case FontFormat_Unknown:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Reaching the character offset.
|
||||
|
||||
current = index & ~7;
|
||||
offset = font->index[current >> 3];
|
||||
|
||||
while(current < index)
|
||||
{
|
||||
width = data[offset << 2];
|
||||
bits = font->data_height * width + 8;
|
||||
|
||||
offset += (bits + 31) >> 5;
|
||||
current++;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/*
|
||||
operate()
|
||||
Operates on the vram using the given operators. The x-coordinate should
|
||||
be a multiple of 32. There should be `height` operators.
|
||||
*/
|
||||
void operate(uint32_t *operators, int height, int x, int y)
|
||||
{
|
||||
int *vram = display_getCurrentVRAM();
|
||||
int vram_offset = (x >> 5) + (y << 2);
|
||||
int i;
|
||||
|
||||
for(i = 0; i < height; i++)
|
||||
{
|
||||
// TODO BLENDING MODES //
|
||||
vram[vram_offset] |= operators[i];
|
||||
|
||||
vram_offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
update()
|
||||
Updates the operators using the given glyph. The operation will not be
|
||||
complete if there are not enough bits available in the operator data.
|
||||
In this case the offset will become negative, which means that the
|
||||
calling procedure has to call operate() and re-call update().
|
||||
`available` represents the number of free bits in the operators (lower
|
||||
bits).
|
||||
Returns the number of bits available after the operation. If it's
|
||||
negative, call operate() and update() again.
|
||||
*/
|
||||
int update(uint32_t *operators, int height, int available,
|
||||
uint32_t *glyph)
|
||||
{
|
||||
// Glyph width.
|
||||
int width = glyph[0] >> 24;
|
||||
int i;
|
||||
|
||||
// The glyph mask extracts 'width' bits at the left. The partial mask
|
||||
// is used when there are less than 'width' bits available in the
|
||||
// current data longword.
|
||||
uint32_t glyph_mask = 0xffffffff << (32 - width);
|
||||
uint32_t partial_mask;
|
||||
|
||||
int shift;
|
||||
uint32_t line;
|
||||
|
||||
// Current data longword, next data array index, and number of bits
|
||||
// still available in 'data'.
|
||||
uint32_t data = glyph[0] << 8;
|
||||
int data_index = 1;
|
||||
int bits_available = 24;
|
||||
|
||||
for(i = 0; i < height; i++)
|
||||
{
|
||||
shift = 32 - available;
|
||||
|
||||
// Getting the next 'width' bits. In some cases these bits will
|
||||
// intersect two different longs.
|
||||
line = data & glyph_mask;
|
||||
line = (shift >= 0) ? (line >> shift) : (line << -shift);
|
||||
operators[i] |= line;
|
||||
|
||||
data <<= width;
|
||||
bits_available -= width;
|
||||
|
||||
// Continue until they do.
|
||||
if(bits_available >= 0) continue;
|
||||
|
||||
// Computing a special mask that extracts just the number of
|
||||
// bits missing, and loading a new data longword.
|
||||
partial_mask = 0xffffffff << (32 + bits_available);
|
||||
data = glyph[data_index++];
|
||||
shift += width + bits_available;
|
||||
|
||||
if(shift <= 31)
|
||||
{
|
||||
line = data & partial_mask;
|
||||
line = (shift >= 0) ? (line >> shift) :
|
||||
(line << -shift);
|
||||
operators[i] |= line;
|
||||
}
|
||||
|
||||
data <<= -bits_available;
|
||||
bits_available += 32;
|
||||
}
|
||||
|
||||
return available - width;
|
||||
}
|
47
src/tales/tales_internals.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
//---
|
||||
//
|
||||
// gint drawing module: tales
|
||||
//
|
||||
// Text displaying. Does some good optimization, though requires dynamic
|
||||
// allocation.
|
||||
//
|
||||
//---
|
||||
|
||||
#ifndef _TALES_INTERNALS_H
|
||||
#define _TALES_INTERNALS_H 1
|
||||
|
||||
#include <tales.h>
|
||||
#include <stdint.h>
|
||||
|
||||
extern struct Font *font;
|
||||
|
||||
/*
|
||||
getCharacterIndex()
|
||||
Returns the index of a character in a font data area depending on the
|
||||
font format and the size of the characters. Returns the index in the
|
||||
data area, as long array, or -1 when the character does not belong to
|
||||
the font format set.
|
||||
*/
|
||||
int getCharacterIndex(int c);
|
||||
|
||||
/*
|
||||
operate()
|
||||
Operates on the vram using the given operators. The x-coordinate should
|
||||
be a multiple of 32. There should be `height` operators.
|
||||
*/
|
||||
void operate(uint32_t *operators, int height, int x, int y);
|
||||
|
||||
/*
|
||||
update()
|
||||
Updates the operators using the given glyph. The operation will not be
|
||||
complete if there are not enough bits available in the operator data.
|
||||
In this case the offset will become negative, which means that the
|
||||
calling procedure has to call operate() and re-call update().
|
||||
`available` represents the number of free bits in the operators (lower
|
||||
bits).
|
||||
Returns the number of bits available after the operation. If it's
|
||||
negative, call operate() and update() again.
|
||||
*/
|
||||
int update(uint32_t *operators, int height, int available, uint32_t *glyph);
|
||||
|
||||
#endif // _TALES_INTERNALS_H
|
201
src/timer.c
|
@ -1,201 +0,0 @@
|
|||
#include <timer.h>
|
||||
#include <mpu.h>
|
||||
#include <stddef.h>
|
||||
|
||||
//---
|
||||
// Internal declarations.
|
||||
// Timer structure and running information (callbacks, repeats etc.)
|
||||
//---
|
||||
|
||||
/*
|
||||
struct Timer
|
||||
This structure holds information for a running timer.
|
||||
*/
|
||||
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 holds information about the timer unit (peripheral
|
||||
module) registers.
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Internal API.
|
||||
//---
|
||||
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
static 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_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--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//---
|
||||
// Public API.
|
||||
//---
|
||||
|
||||
/*
|
||||
timer_start()
|
||||
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_start(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_reload()
|
||||
Reloads the given timer with the given constant. Starts the timer if
|
||||
it was stopped.
|
||||
|
||||
@arg timer Timer identifier.
|
||||
@arg new_delay
|
||||
*/
|
||||
void timer_reload(int timer, int new_delay)
|
||||
{
|
||||
struct mod_tmu *tmu;
|
||||
unsigned char *tstr;
|
||||
int byte = (1 << timer);
|
||||
timer_get(timer, &tmu, &tstr);
|
||||
|
||||
// Setting the constant and the delay.
|
||||
(*tmu).TCOR = new_delay;
|
||||
(*tmu).TCNT = new_delay;
|
||||
|
||||
// Starting the timer.
|
||||
*tstr |= byte;
|
||||
}
|
25
src/timer/timer_get.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include <timer_internals.h>
|
||||
#include <mpu.h>
|
||||
|
||||
/*
|
||||
timer_get()
|
||||
Returns the timer and TSTR register addresses.
|
||||
*/
|
||||
void timer_get(int timer, struct mod_tmu **tmu, unsigned char **tstr)
|
||||
{
|
||||
// Using SH7705 information for SH-3-based MPUs.
|
||||
if(isSH3())
|
||||
{
|
||||
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;
|
||||
}
|
54
src/timer/timer_internals.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
#ifndef _TIMER_INTERNALS_H
|
||||
#define _TIMER_INTERNALS_H
|
||||
|
||||
/*
|
||||
struct Timer
|
||||
This structure holds information for a running timer.
|
||||
*/
|
||||
struct Timer
|
||||
{
|
||||
void (*callback)(void);
|
||||
int repeats;
|
||||
};
|
||||
|
||||
extern struct Timer timers[3];
|
||||
|
||||
/*
|
||||
struct mod_tmu
|
||||
This structure holds information about the timer unit (peripheral
|
||||
module) registers.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
void timer_get(int timer, struct mod_tmu **tmu, unsigned char **tstr);
|
||||
|
||||
#endif // _TIMER_INTERNALS_H
|
27
src/timer/timer_interrupt.c
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <timer.h>
|
||||
#include <timer_internals.h>
|
||||
|
||||
struct Timer timers[3] = { { NULL, 0 }, { NULL, 0 }, { NULL, 0 } };
|
||||
|
||||
/*
|
||||
timer_interrupt()
|
||||
Handles the interrupt for the given timer.
|
||||
*/
|
||||
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].repeats) return;
|
||||
// And stopping it if necessary.
|
||||
if(timers[timer].repeats == 1) timer_stop(timer);
|
||||
else timers[timer].repeats--;
|
||||
}
|
22
src/timer/timer_reload.c
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <timer.h>
|
||||
#include <timer_internals.h>
|
||||
|
||||
/*
|
||||
timer_reload()
|
||||
Reloads the given timer with the given constant. Starts the timer if
|
||||
it was stopped.
|
||||
*/
|
||||
void timer_reload(int timer, int new_delay)
|
||||
{
|
||||
struct mod_tmu *tmu;
|
||||
unsigned char *tstr;
|
||||
int byte = (1 << timer);
|
||||
timer_get(timer, &tmu, &tstr);
|
||||
|
||||
// Setting the constant and the delay.
|
||||
(*tmu).TCOR = new_delay;
|
||||
(*tmu).TCNT = new_delay;
|
||||
|
||||
// Starting the timer.
|
||||
*tstr |= byte;
|
||||
}
|
38
src/timer/timer_start.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include <timer.h>
|
||||
#include <timer_internals.h>
|
||||
|
||||
/*
|
||||
timer_start()
|
||||
Configures and starts a timer.
|
||||
*/
|
||||
void timer_start(int timer, int delay, int prescaler, void (*callback)(void),
|
||||
int repeats)
|
||||
{
|
||||
// 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].repeats = repeats;
|
||||
|
||||
// Starting the timer and returning.
|
||||
*tstr |= byte;
|
||||
}
|
18
src/timer/timer_stop.c
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include <timer.h>
|
||||
#include <timer_internals.h>
|
||||
|
||||
/*
|
||||
timer_stop()
|
||||
Stops the given timer. This function may be called even if the timer is
|
||||
not running.
|
||||
*/
|
||||
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;
|
||||
}
|
|
@ -1,190 +0,0 @@
|
|||
|
||||
-----------------------------------
|
||||
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
|
|
@ -1,558 +0,0 @@
|
|||
|
||||
-----------------------------------
|
||||
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
|
|
@ -1,91 +0,0 @@
|
|||
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
|