Re-structured files. Minor name changes. No code changed in this falsely huge commit.

This commit is contained in:
lephe 2016-07-06 11:28:51 +02:00
parent ab17532f67
commit d122624c56
90 changed files with 2713 additions and 3586 deletions

148
Makefile
View file

@ -1,141 +1,11 @@
#! /usr/bin/make -f ## Temporary things there
#--- # C source file template:
# fx-9860g lib Makefile. # $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
$(eval $(call rule-c-source,display,area))
#
# 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

141
Makefile.old Normal file
View 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

View file

@ -44,3 +44,19 @@ This will build the following components :
* `ginttest.g1a`, a test application * `ginttest.g1a`, a test application
The common `clean`, `mrproper`, and `distclean` rules will clean the directory. 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
View file

@ -12,6 +12,8 @@
@ vram overflow @ vram overflow
@ keyboard test threading interface @ keyboard test threading interface
+ use alloca() for tales
+ call exit handlers
+ compute frequencies + compute frequencies
+ gray text + gray text
+ effective rtc callback + effective rtc callback

View file

@ -1,11 +1,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <mpu.h> #include <mpu.h>
#include <keyboard.h> #include <keyboard.h>
#include <display.h> #include <display.h>
#include <timer.h> #include <timer.h>
#include <gray.h> #include <gray.h>
#include <tales.h>
#include <stdint.h> #include <stdint.h>
#include <7305.h> #include <7305.h>

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View file

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

Before

Width:  |  Height:  |  Size: 554 B

After

Width:  |  Height:  |  Size: 554 B

View file

Before

Width:  |  Height:  |  Size: 242 B

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

View file

@ -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 #ifndef _GINT_H
#define _GINT_H 1 #define _GINT_H 1
//--- //---
// Public API. // Interrupt handler control.
//--- //---
/* /*
gint_getVBR() gint_getVBR()
Returns the current vbr address. Returns the current vbr address.
@return vbr address currently in use.
*/ */
unsigned int gint_getVBR(void); unsigned int gint_getVBR(void);
@ -17,11 +24,15 @@ unsigned int gint_getVBR(void);
gint_systemVBR() gint_systemVBR()
Returns the vbr address used by the system (saved when execution Returns the vbr address used by the system (saved when execution
starts). starts).
@return vbr address used by the system.
*/ */
unsigned int gint_systemVBR(void); unsigned int gint_systemVBR(void);
//---
// Callback API.
//---
/* /*
enum RTCFrequency enum RTCFrequency
Describes the possible frequencies available for the real-time clock Describes the possible frequencies available for the real-time clock
@ -42,9 +53,6 @@ enum RTCFrequency
gint_setRTCCallback() gint_setRTCCallback()
Sets the callback function for the real-time clock interrupt. If Sets the callback function for the real-time clock interrupt. If
frequency is non-NULL, the clock frequency is set to the given value. 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); void gint_setRTCCallback(void (*callback)(void), enum RTCFrequency frequency);
@ -66,12 +74,8 @@ void (*gint_getRTCCallback(enum RTCFrequency *frequency))(void);
/* /*
gint_setVBR() gint_setVBR()
Sets the vbr address and does some configuration while interrupts are Sets the vbr address and calls the configuration function while
disabled. interrupts are disabled.
@arg new_vbr_address
@arg setup Will be called for configuration under interrupt-safe
environment.
*/ */
void gint_setVBR(unsigned int new_vbr_address, void (*setup)(void)); void gint_setVBR(unsigned int new_vbr_address, void (*setup)(void));
@ -116,8 +120,6 @@ void gint_7305(void) __attribute__((section(".gint.int")));
/* /*
gint_setRTCFrequency() gint_setRTCFrequency()
Sets the RTC interrupt frequency and enables interrupts. Sets the RTC interrupt frequency and enables interrupts.
@arg frequency
*/ */
void gint_setRTCFrequency_7705(enum RTCFrequency frequency); void gint_setRTCFrequency_7705(enum RTCFrequency frequency);
void gint_setRTCFrequency_7305(enum RTCFrequency frequency); void gint_setRTCFrequency_7305(enum RTCFrequency frequency);
@ -125,8 +127,6 @@ void gint_setRTCFrequency_7305(enum RTCFrequency frequency);
/* /*
gint_getRTCFrequency() gint_getRTCFrequency()
Returns the RTC interrupt frequency. Returns the RTC interrupt frequency.
@return RTC interrupt frequency.
*/ */
enum RTCFrequency gint_getRTCFrequency_7705(void); enum RTCFrequency gint_getRTCFrequency_7705(void);
enum RTCFrequency gint_getRTCFrequency_7305(void); enum RTCFrequency gint_getRTCFrequency_7305(void);

View file

@ -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 #ifndef _KEYBOARD_H
#define _KEYBOARD_H 1 #define _KEYBOARD_H 1
@ -85,29 +97,25 @@
/* /*
keyboard_setFrequency() keyboard_setFrequency()
Sets the keyboard frequency. The default frequency is 32 Hz. Very few Sets the keyboard frequency. The default frequency is 32 Hz. The unit
Very few applications will need to change this setting. 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. The actual frequency is guaranteed to be greater than the argument.
Be aware that you will miss key hits at low frequencies. At higher Be aware that you will miss key hits at low frequencies. At higher
frequencies, you will lose important execution power. frequencies, you will lose important execution power.
@arg frequency Frequency in Hz (1 Hz = 1 event / second).
*/ */
// Currently not implemented. // Currently not implemented.
// void keyboard_setFrequency(int frequency); // void keyboard_setFrequency(int frequency);
/* /*
keyboard_setRepeatRate() keyboard_setRepeatRate()
Sets the default repeat rate for key events. The unit for the argument Sets the default repeat rate for key events. The delay before the first
is the keyboard period. For example at 32 Hz, values of (20, 4) will repeat may have a different value (usually longer). The unit for the
imitate the system default. 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 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. If first != 0 and next = 0, only one repetition will be
allowed. 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); void keyboard_setRepeatRate(int first, int next);
@ -146,10 +154,6 @@ enum GetkeyOpt
keylast() keylast()
Returns the matrix code of the last pressed key. If repeat_count is Returns the matrix code of the last pressed key. If repeat_count is
non-NULL, it is set to the number of repetitions. non-NULL, it is set to the number of repetitions.
@arg repeat_count
@return Key matrix code.
*/ */
int keylast(int *repeat_count); 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 editing the array. It wouldn't influence the behavior of the keyboard
functions, but the buffer data is very volatile. Therefore, data functions, but the buffer data is very volatile. Therefore, data
written to the buffer could be replaced anytime. written to the buffer could be replaced anytime.
@return 10-byte keyboard state buffer.
*/ */
volatile unsigned char *keystate(void); volatile unsigned char *keystate(void);
@ -222,10 +224,6 @@ enum KeyType
keyid() keyid()
Returns a non-matrix key code that can be used for array subscript. Returns a non-matrix key code that can be used for array subscript.
Ignores modifiers. Ignores modifiers.
@arg key
@return Modified keycode.
*/ */
int keyid(int key); int keyid(int key);
@ -233,20 +231,12 @@ int keyid(int key);
keychar() keychar()
Returns the ASCII character associated with a character key ; 0 for Returns the ASCII character associated with a character key ; 0 for
other keys. other keys.
@arg key
@return Associated character.
*/ */
int keychar(int key); int keychar(int key);
/* /*
keytype() keytype()
Returns a key's type. Ignores modifiers. Returns a key's type. Ignores modifiers.
@arg key
@return Key type.
*/ */
enum KeyType keytype(int key); enum KeyType keytype(int key);
@ -267,8 +257,6 @@ void keyboard_interrupt(void) __attribute__((section(".gint.int")));
/* /*
keyboard_updateState() keyboard_updateState()
Updates the keyboard state. Updates the keyboard state.
@arg state 10-byte state buffer.
*/ */
void keyboard_updateState_7705(volatile unsigned char *state) void keyboard_updateState_7705(volatile unsigned char *state)
__attribute__((section(".gint.int"))); __attribute__((section(".gint.int")));

View file

@ -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 #ifndef _MPU_H
#define _MPU_H 1 #define _MPU_H 1
//--- /*
// enum MPU enum MPU
// This type holds information about the calculator's MPU. This type holds information about the calculator's MPU.
//--- */
enum MPU enum MPU
{ {
MPU_Unknown = 0, MPU_Unknown = 0,
@ -19,15 +30,8 @@ enum MPU
MPU_SH7724 = 4 MPU_SH7724 = 4
}; };
// Global MPU variable, accessible for direct tests. Initialized at the
// beginning of execution.
//---
// MPU type tests.
// Prefer using an 'if(isSH3()) { ... } else { ... }' alternative for best
// results.
//---
// Global MPU variable, accessible for direct tests.
extern enum MPU MPU_CURRENT; extern enum MPU MPU_CURRENT;
// Quick SH3 test. It is safer to assume that an unknown model is SH4 because // 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() getMPU()
Determines the MPU type and returns it. MPU_CURRENT is not updated. Determines the MPU type and returns it. MPU_CURRENT is not updated.
@return MPU type.
*/ */
enum MPU getMPU(void); enum MPU getMPU(void);

View file

@ -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 #ifndef _SCREEN_H
#define _SCREEN_H 1 #define _SCREEN_H 1
//---
// Public API.
//---
/* /*
screen_display() screen_display()
Displays contents on the full screen. Expects a 1024-byte buffer. Displays contents on the full screen. Expects a 1024-byte buffer.
@arg vram 1024-byte video buffer.
*/ */
void screen_display(const void *vram); void screen_display(const void *vram);

View file

@ -1,3 +1,12 @@
//---
//
// gint drawing module: tales
//
// Text displaying. Does some good optimization, though requires dynamic
// allocation.
//
//---
#ifndef _TALES_H #ifndef _TALES_H
#define _TALES_H 1 #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. 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. 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 #endif // _TALES_H

View file

@ -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 #ifndef _TIMER_H
#define _TIMER_H 1 #define _TIMER_H 1
@ -32,37 +41,33 @@
/* /*
timer_start() timer_start()
Configures and starts a timer. 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
@arg timer Timer name. Use only TIMER_USER. You may use not running the gray engine.
TIMER_GRAY, if you're not running the gray The delay is in clock counts unit. The possible values for the
engine. prescaler are dividers of the peripheral clock frequency Po:
@arg delay Delay before expiration, in clock counts. - TIMER_Po_4
@arg prescaler Clock prescaler value. Possible values are - TIMER_Po_16
TIMER_Po_4, TIMER_Po_16, TIMER_Po_64, - TIMER_Po_64
TIMER_Po_256 and TIMER_TCLK. - TIMER_Po_256
@arg callback Callback function. - TIMER_TCLK
@arg repetitions Number of repetitions, 0 for infinite. 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), void timer_start(int timer, int delay, int prescaler, void (*callback)(void),
int repetitions); int repeats);
/* /*
timer_stop() timer_stop()
Stops the given timer. This function may be called even if the timer is Stops the given timer. This function may be called even if the timer is
not running. not running.
@arg timer Timer identifier.
*/ */
void timer_stop(int timer); void timer_stop(int timer);
/* /*
timer_reload() timer_reload()
Reloads the given timer with the given constant. Starts the timer if Reloads the given timer with the given constant. Starts the timer if
it was stopped. it was stopped. The new delay uses the same unit as in timer_start().
@arg timer Timer identifier.
@arg new_delay
*/ */
void timer_reload(int timer, int new_delay); void timer_reload(int timer, int new_delay);
@ -76,8 +81,6 @@ void timer_reload(int timer, int new_delay);
/* /*
timer_interrupt() timer_interrupt()
Handles the interrupt for the given timer. Handles the interrupt for the given timer.
@timer Timer that generated the interrupt.
*/ */
void timer_interrupt(int timer) __attribute__((section(".gint.int"))); void timer_interrupt(int timer) __attribute__((section(".gint.int")));

BIN
libc.a

Binary file not shown.

BIN
libgint.a

Binary file not shown.

View file

@ -1,73 +1,4 @@
//--- #include <bopti_internals.h>
//
// 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.
//---
/* /*
bopti_op() bopti_op()
@ -306,7 +237,6 @@ static void bopti_end(const unsigned char *end, int x, int y, int width)
bopti() bopti()
Draws a layer in the video ram. Draws a layer in the video ram.
*/ */
static void bopti(const unsigned char *layer, int x, int y, int columns, static void bopti(const unsigned char *layer, int x, int y, int columns,
int end_size) int end_size)
{ {
@ -360,74 +290,3 @@ static void getStructure(struct Image *img, int *width, int *height,
if(end_size) *end_size = end; if(end_size) *end_size = end;
if(layer_size) *layer_size = layer; 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
View 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
View 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
View 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++;
}
}

View file

@ -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> #include <stdlib.h>
// Provides gint initialization functionalities.
#include <gint.h> #include <gint.h>
// Provides memset() and memcpy().
#include <string.h> #include <string.h>
#include <setjmp.h>
// Some syscall prototypes.
void __Hmem_SetMMU(unsigned int, unsigned int, int); void __Hmem_SetMMU(unsigned int, unsigned int, int);
void __GLibAddinAplExecutionCheck(int, int, int); void __GLibAddinAplExecutionCheck(int, int, int);
int main(void); int main(void);
// Local functions.
static void init(void); static void init(void);
static void fini(void); static void fini(void);
@ -26,17 +19,15 @@ extern unsigned int
// This variable should be overwritten before being returned, so the default // This variable should be overwritten before being returned, so the default
// value doesn't matter much. // value doesn't matter much.
static int exit_code = EXIT_SUCCESS; static int exit_code = EXIT_SUCCESS;
static jmp_buf env;
/* /*
start() start()
Program entry point. Loads the data section into the memory and invokes Program entry point. Loads the data section into the memory and invokes
main(). Also prepares the execution environment by initializing all the main(). Also prepares the execution environment by initializing all the
modules. modules.
@return Execution status returned to the OS.
*/ */
int start(void) int start(void)
@ -118,7 +109,6 @@ static void fini(void)
/* /*
abort() abort()
Immediately ends the program without invoking the exit handlers. Immediately ends the program without invoking the exit handlers.
*/ */
@ -130,11 +120,13 @@ void abort(void)
/* /*
exit() exit()
Ends the program and returns the given exit code status. Calls exit
Ends the program and returns the given exit code status. handlers before returning.
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
@arg status Exit status. 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) void exit(int status)

View file

@ -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 <gint.h>
#include <mpu.h> #include <mpu.h>
#include <gray.h>
#include <stddef.h> #include <stddef.h>
//---
// Local variables.
//---
static unsigned int static unsigned int
new_vbr, new_vbr,
sys_vbr; sys_vbr;
static void (*rtc_callback)(void) = NULL;
//--- //---
@ -55,50 +57,12 @@ static void gint_stop(void)
gint_systemVBR() gint_systemVBR()
Returns the vbr address used by the system (saved when execution Returns the vbr address used by the system (saved when execution
starts). starts).
@return vbr address used by the system.
*/ */
unsigned int gint_systemVBR(void) inline unsigned int gint_systemVBR(void)
{ {
return sys_vbr; 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() gint()
Handles interrupts. Handles interrupts.

40
src/core/gint_callback.c Normal file
View 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;
}

View file

@ -13,8 +13,6 @@
/* /*
gint_getVBR() gint_getVBR()
Returns the current vbr address. Returns the current vbr address.
@return vbr address currently in use.
*/ */
_gint_getVBR: _gint_getVBR:
rts rts
@ -34,9 +32,6 @@ _gint_getVBR:
Therefore, we must set vbr *and* change interrupt priorities while Therefore, we must set vbr *and* change interrupt priorities while
having disabled all the interrupts in the status register. That's why having disabled all the interrupts in the status register. That's why
this function takes as parameter the priority management function. this function takes as parameter the priority management function.
@arg New vbr address.
@arg Priority management function.
*/ */
_gint_setVBR: _gint_setVBR:
sts.l pr, @-r15 sts.l pr, @-r15

View file

@ -1,6 +1,8 @@
/* /*
This file contains all the system calls used by the library. Maybe one gint core module: syscalls
day we won't need them anymore ?
All the system calls used by the library. Let's hope one day we won't
depend on them anymore.
*/ */
.global ___Hmem_SetMMU .global ___Hmem_SetMMU

View file

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

View 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
View 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
View 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];
}

View 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

View 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
View 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
View 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;
}
}

View 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
View 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
View 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);
}

View file

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

View file

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

View file

@ -6,7 +6,6 @@
// //
//--- //---
#include <display.h>
#include <gray.h> #include <gray.h>
#include <screen.h> #include <screen.h>
#include <timer.h> #include <timer.h>
@ -27,15 +26,6 @@ static int runs = 0;
// Engine control. // Engine control.
//--- //---
/*
gray_runs()
Returns 1 if the gray engine is running, 0 otherwise.
*/
inline int gray_runs(void)
{
return runs;
}
/* /*
gray_start() gray_start()
Starts the gray engine. The control of the screen is transferred to the 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()); 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() gray_lightVRAM()
Returns the module's gray vram address. Returns the module's gray vram address.
@ -88,20 +103,25 @@ void gray_getDelays(int *light, int *dark)
if(dark) *dark = delays[1]; if(dark) *dark = delays[1];
} }
//---
// Drawing.
//---
/* /*
gray_setDelays() gupdate()
Changes the gray engine delays. Swaps the vram buffer sets.
*/ */
void gray_setDelays(int light, int dark) inline void gupdate(void)
{ {
delays[0] = light; current ^= 2;
delays[1] = dark;
} }
//--- //---
// Internal API. // Interrupt control and initialization.
//--- //---
/* /*
@ -129,127 +149,3 @@ void gray_init(void)
delays[0] = 900; delays[0] = 900;
delays[1] = 1000; 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
View 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);
}

View file

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

View 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;
}

View 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
View 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;
}

View 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;
}

View 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

View 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);
}

View 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;
}

View 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);
}

View 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
View 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
View 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
View 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;
}

View 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
View 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
View 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
View 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;
}

View file

@ -1,3 +1,11 @@
//---
//
// gint core module: mpu
//
// Determines which kind of MPU is running the program.
//
//---
#include <mpu.h> #include <mpu.h>
enum MPU MPU_CURRENT; enum MPU MPU_CURRENT;
@ -6,21 +14,18 @@ enum MPU MPU_CURRENT;
getMPU() getMPU()
Returns the MPU identifier of the calculator. 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) 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. SH-4-based MPUs.
Therefore, this function uses port L control register (PLCR), whose To detect SH-3-based MPUs, this function uses port L control register
bits 8 to 15 cannot be set with SH7337 where bits 8 to 11 can be set (PLCR), whose bits 8 to 15 cannot be set with SH7337 where bits 8 to 11
with SH7355. can be set with SH7355.
Additionally, the CPU core ID register (CPIDR) at 0xff000048 returns 1 Additionally, the CPU core ID register (CPIDR) at 0xff000048 returns 1
on SH7305. on SH7305.
@return MPU identifier as integer value.
*/ */
enum MPU getMPU(void) enum MPU getMPU(void)
{ {
// Processor version register. // Processor version register.

27
src/mpu/various_7305.c Normal file
View 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
View 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;
}

View file

@ -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> #include <screen.h>
/* /*
screen_display() screen_display()
Displays the given vram on the screen. Only bytes can be transferred Displays the given vram on the screen. Only bytes can be transferred
through the screen registers, which is unfortunate because most of the through the screen registers, which is unfortunate because most of the
vram-related operations use longword-base computations. vram-related operations use longword-base operations.
@arg vram 1024-byte video buffer.
*/ */
void screen_display(const void *ptr) void screen_display(const void *ptr)
{ {

View file

@ -1,16 +1,13 @@
/* /*
This file implements long jumps. An example of their use is with crt0.c gint standard module: setjmp
and exit()-family functions that use them to restore the execution
state when leaving the program from an unknown location.
The register contents are saved in a buffer when setjmp() is called and Long jumps. The register contents are saved in a buffer when setjmp()
restored at any time when longjmp() performs the jump, through an is called and restored at any time when longjmp() performs the jump.
exit() or abort() call, for instance.
This is actually a question of playing with pr ; the user program is This is based on a trick that uses pr ; the user program is resumed
resumed after the setjmp() call when longjmp() is invoked but setjmp() after the setjmp() call when longjmp() is invoked but this is not
has nothing to do with this operation. longjmp() restores the pr value setjmp() that returns. longjmp() restores the pr value that was saved
that was saved by setjmp() and performs an rts instruction. by setjmp() and performs an rts instruction.
setjmp() returns 0 when called to set up the jump point and a non-zero 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 value when invoked through a long jump. If 0 is given as return value
@ -54,7 +51,7 @@ _setjmp:
_longjmp: _longjmp:
/* Restoring the system and control registers. Restoring pr is actually /* 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. */ setjmp() has returned. */
lds.l @r4+, pr lds.l @r4+, pr
lds.l @r4+, macl lds.l @r4+, macl

View file

@ -1,17 +1,9 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
//---
// Memory manipulation.
//---
/* /*
memcpy() memcpy()
Copies a memory area. A smart copy is performed if possible. 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) 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; 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
View 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;
}

View file

@ -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
View 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);
}

View 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
View 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;
}

View 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

View file

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

View 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

View 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
View 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
View 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
View 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;
}

View file

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

View file

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

View file

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