Re-structured files. Minor name changes. No code changed in this falsely huge commit.
148
Makefile
|
@ -1,141 +1,11 @@
|
||||||
#! /usr/bin/make -f
|
## Temporary things there
|
||||||
|
|
||||||
#---
|
# 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
|
@ -0,0 +1,141 @@
|
||||||
|
#! /usr/bin/make -f
|
||||||
|
|
||||||
|
#---
|
||||||
|
# fx-9860g lib Makefile.
|
||||||
|
#---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Variables and configuration.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
cc = sh3eb-elf-gcc
|
||||||
|
as = sh3eb-elf-as
|
||||||
|
ar = sh3eb-elf-ar
|
||||||
|
ob = sh3eb-elf-objcopy
|
||||||
|
wr = g1a-wrapper
|
||||||
|
|
||||||
|
# Output files
|
||||||
|
g1a = ginttest.g1a
|
||||||
|
bin = build/ginttest.bin
|
||||||
|
elf = build/ginttest.elf
|
||||||
|
|
||||||
|
# Command-line options
|
||||||
|
cflags = -m3 -mb -nostdlib -ffreestanding -W -Wall \
|
||||||
|
-I . -isystem include
|
||||||
|
lib = -lgcc -L. -lgint -lc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Source and object files.
|
||||||
|
#
|
||||||
|
|
||||||
|
# Gint library.
|
||||||
|
src-lib = crt0.c syscalls.s \
|
||||||
|
gint.c gint_vbr.s gint_7705.c gint_7305.c \
|
||||||
|
mpu.c keyboard.c screen.c display.c gray.c timer.c tales.c \
|
||||||
|
bopti.c
|
||||||
|
hea-lib = 7305.h 7705.h gint.h \
|
||||||
|
stdlib.h \
|
||||||
|
mpu.h keyboard.h screen.h display.h gray.h timer.h tales.h
|
||||||
|
obj-lib = $(patsubst %, build/%.o, $(src-lib))
|
||||||
|
hdr-lib = $(patsubst %, include/%, $(hea-lib))
|
||||||
|
|
||||||
|
# Standard library.
|
||||||
|
src-std = setjmp.s string.c
|
||||||
|
hea-std = setjmp.h string.h ctype.h
|
||||||
|
obj-std = $(patsubst %, build/%.o, $(src-std))
|
||||||
|
hdr-std = $(patsubst %, include/%, $(hea-str))
|
||||||
|
|
||||||
|
# Test application.
|
||||||
|
src-app = ginttest.c
|
||||||
|
img-app = bitmap_opt.bmp swords.bmp sprites.bmp symbol.bmp symbol2.bmp \
|
||||||
|
illustration.bmp
|
||||||
|
res-app = $(patsubst %, build/%.o, $(img-app)) build/font.o
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Building rules.
|
||||||
|
#
|
||||||
|
|
||||||
|
all: build libgint.a libc.a ginttest.g1a
|
||||||
|
|
||||||
|
build:
|
||||||
|
mkdir -p build
|
||||||
|
|
||||||
|
libgint.a: $(obj-lib)
|
||||||
|
$(ar) rcs libgint.a $(obj-lib)
|
||||||
|
@ echo "\033[32;1mLibrary file size: "`stat -c %s libgint.a` \
|
||||||
|
"bytes\033[0m"
|
||||||
|
|
||||||
|
libc.a: $(obj-std)
|
||||||
|
$(ar) rcs libc.a $(obj-std)
|
||||||
|
@ echo "\033[32;1mStandard file size: "`stat -c %s libc.a` \
|
||||||
|
"bytes\033[0m"
|
||||||
|
|
||||||
|
$(g1a): libgint.a $(src-app) $(res-app)
|
||||||
|
$(cc) $(src-app) $(res-app) -T ginttest.ld -o $(elf) $(cflags) $(lib)
|
||||||
|
$(ob) -R .comment -R .bss -O binary $(elf) $(bin)
|
||||||
|
$(wr) $(bin) -o ginttest.g1a -i icon.bmp
|
||||||
|
@ echo "\033[32;1mBinary file size: "`stat -c %s $(bin)`" bytes\033[0m"
|
||||||
|
@ sh3eb-elf-objdump -h build/ginttest.elf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Resource management.
|
||||||
|
#
|
||||||
|
|
||||||
|
build/%.c.o: src/%.c $(hdr-lib) $(hdr-std)
|
||||||
|
$(cc) $(cflags) -O2 -c $< -o $@
|
||||||
|
|
||||||
|
build/%.s.o: src/%.s
|
||||||
|
$(as) -c $^ -o $@
|
||||||
|
|
||||||
|
build/%.bmp.o: resources/%.bmp
|
||||||
|
fxconv $^ -o $@ --preview
|
||||||
|
|
||||||
|
build/font.o: resources/font.bmp
|
||||||
|
fxconv --font $^ -o $@
|
||||||
|
|
||||||
|
# File gint.c should not be optimized... looks like attribute((interrupt_
|
||||||
|
# handler)) doesn't like it. (It could be a gint bug also, I should check.)
|
||||||
|
build/gint.c.o: src/gint.c $(hdr-lib) $(hdr-std)
|
||||||
|
$(cc) $(cflags) -c $< -o $@
|
||||||
|
|
||||||
|
%.c.o: %.c $(hdr-lib) $(hdr-std)
|
||||||
|
$(cc) $(cflags) -c $< -o $@
|
||||||
|
%.s.o: %.s
|
||||||
|
$(as) -c $^ -o $@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Cleaning rules.
|
||||||
|
#
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@ rm -f $(obj-lib) $(obj-std) $(obj-app) $(res-app)
|
||||||
|
@ rm -f $(bin) $(elf)
|
||||||
|
mrproper: clean
|
||||||
|
@ rm -f build/*
|
||||||
|
@ rm -f ginttest.g1a libc.a libgint.a
|
||||||
|
distclean: mrproper
|
||||||
|
|
||||||
|
re: distclean all
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Installing shorthand.
|
||||||
|
#
|
||||||
|
|
||||||
|
install:
|
||||||
|
usb-connector SEND ginttest.g1a ginttest.g1a fls0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: all clean mrproper distclean re install
|
16
README.md
|
@ -44,3 +44,19 @@ This will build the following components :
|
||||||
* `ginttest.g1a`, a test application
|
* `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
|
@ -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
|
||||||
|
|
|
@ -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>
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 554 B After Width: | Height: | Size: 554 B |
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 242 B |
BIN
ginttest.g1a
|
@ -1,15 +1,22 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint core module: interrupt handler
|
||||||
|
//
|
||||||
|
// Central point of the library. Controls the interrupt handler and
|
||||||
|
// defines a few functions to configure callbacks for some interrupts.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
#ifndef _GINT_H
|
#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));
|
||||||
|
|
||||||
|
@ -108,16 +112,14 @@ void gint_stop_7305(void);
|
||||||
gint()
|
gint()
|
||||||
Handles interrupts.
|
Handles interrupts.
|
||||||
*/
|
*/
|
||||||
void gint(void) __attribute__((section(".gint.int.entry"),
|
void gint(void) __attribute__((section(".gint.int.entry"),
|
||||||
interrupt_handler));
|
interrupt_handler));
|
||||||
void gint_7705(void) __attribute__((section(".gint.int")));
|
void gint_7705(void) __attribute__((section(".gint.int")));
|
||||||
void gint_7305(void) __attribute__((section(".gint.int")));
|
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);
|
||||||
|
|
|
@ -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")));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
BIN
libgint.a
|
@ -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
|
@ -0,0 +1,127 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint drawing module: bopti
|
||||||
|
//
|
||||||
|
// bopti does every job related to display images. There is only one
|
||||||
|
// public function, but there are lots of internal optimizations.
|
||||||
|
//
|
||||||
|
// Some bit-manipulation expressions may look written out of nowhere. The
|
||||||
|
// idea is always the same: get a part of the image in an 'operator',
|
||||||
|
// which is a 32-bit variable, shift this operator so that its bits
|
||||||
|
// correspond to the desired position for the bitmap on the screen, and
|
||||||
|
// edit the video-ram long entry which correspond to this position using
|
||||||
|
// a 'mask' that indicates which bits of the operator contain information.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef _BOPTI_INTERNALS_H
|
||||||
|
#define _BOPTI_INTERNALS_H 1
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
enum Channel
|
||||||
|
Determines the kind of information written into a layer. Every image is
|
||||||
|
made of one or more channels.
|
||||||
|
*/
|
||||||
|
enum Channel
|
||||||
|
{
|
||||||
|
Channel_Mono = 0x01,
|
||||||
|
Channel_Light = 0x02,
|
||||||
|
Channel_Dark = 0x04,
|
||||||
|
|
||||||
|
Channel_FullAlpha = 0x08,
|
||||||
|
Channel_LightAlpha = 0x10,
|
||||||
|
Channel_DarkAlpha = 0x20,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
enum Format
|
||||||
|
Describes the various combination of channels allowed by bopti.
|
||||||
|
*/
|
||||||
|
enum Format
|
||||||
|
{
|
||||||
|
Format_Mono = Channel_Mono,
|
||||||
|
Format_MonoAlpha = Format_Mono | Channel_FullAlpha,
|
||||||
|
Format_Gray = Channel_Light | Channel_Dark,
|
||||||
|
Format_GrayAlpha = Format_Gray | Channel_FullAlpha,
|
||||||
|
Format_GreaterAlpha = Format_Mono | Channel_LightAlpha |
|
||||||
|
Channel_DarkAlpha
|
||||||
|
};
|
||||||
|
|
||||||
|
// The following variables refer to parameters that do not change during the
|
||||||
|
// drawing operation (at least for the time of a layer). They could be passed
|
||||||
|
// on by every function from the module, but this would be heavy and useless.
|
||||||
|
// Using global variables is not really a proper solution but it does simplify
|
||||||
|
// code much.
|
||||||
|
extern int *vram, *v1, *v2;
|
||||||
|
extern enum Channel channel;
|
||||||
|
extern int height;
|
||||||
|
extern void (*op)(int offset, uint32_t operator, uint32_t op_mask);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Some bopti routines.
|
||||||
|
//---
|
||||||
|
|
||||||
|
/*
|
||||||
|
bopti_op()
|
||||||
|
Operates on a vram long. The operator will often not contain 32 bits of
|
||||||
|
image information. Since neutral bits are not the same for all
|
||||||
|
operations, the op_mask argument indicates which bits should be used
|
||||||
|
for the operation. Which operation has to be done is determined by the
|
||||||
|
channel setting.
|
||||||
|
*/
|
||||||
|
void bopti_op_mono(int offset, uint32_t operator, uint32_t op_mask);
|
||||||
|
void bopti_op_gray(int offset, uint32_t operator, uint32_t op_mask);
|
||||||
|
|
||||||
|
/*
|
||||||
|
bopti_grid() -- general form
|
||||||
|
bopti_grid_a32() -- when x is a multiple of 32
|
||||||
|
|
||||||
|
Draws the grid at the beginning of a layer's data. The length of this
|
||||||
|
grid is always a multiple of 32.
|
||||||
|
The need for bopti_grid_a32() is not only linked to optimization,
|
||||||
|
because bopti_grid() will perform a 32-bit shift when x is a multiple
|
||||||
|
of 32, which is undefined behavior.
|
||||||
|
*/
|
||||||
|
void bopti_grid_a32(const uint32_t *layer, int x, int y, int column_count);
|
||||||
|
void bopti_grid(const uint32_t *layer, int x, int y, int column_count);
|
||||||
|
|
||||||
|
/*
|
||||||
|
bopti_end_get()
|
||||||
|
Returns an operator for the end of a line, whose width is lower than 32
|
||||||
|
(by design: otherwise, it would have been a column). The given pointer
|
||||||
|
is read and updated so that it points to the next line at the end of
|
||||||
|
the operation.
|
||||||
|
*/
|
||||||
|
uint32_t bopti_end_get1(const unsigned char **data);
|
||||||
|
uint32_t bopti_end_get2(const unsigned char **data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
bopti_rest() -- general form
|
||||||
|
bopti_rest_nover() -- when the end does not overlap two vram longs
|
||||||
|
|
||||||
|
Draws the end of a layer, which can be considered as a whole layer
|
||||||
|
whose with is lower than 32. (Actually is it lower or equal to 16;
|
||||||
|
otherwise it would have been a column and the end would be empty).
|
||||||
|
*/
|
||||||
|
void bopti_end_nover(const unsigned char *end, int x, int y, int width);
|
||||||
|
void bopti_end(const unsigned char *end, int x, int y, int width);
|
||||||
|
|
||||||
|
/*
|
||||||
|
bopti()
|
||||||
|
Draws a layer in the video ram.
|
||||||
|
*/
|
||||||
|
void bopti(const unsigned char *layer, int x, int y, int columns,
|
||||||
|
int end_size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
getStructure()
|
||||||
|
Determines the image size and data pointer.
|
||||||
|
*/
|
||||||
|
void getStructure(struct Image *img, int *width, int *height, int *layer_size,
|
||||||
|
const unsigned char **data, int *columns, int *end_size);
|
||||||
|
|
||||||
|
#endif // _BOPTI_INTERNALS_H
|
38
src/bopti/dimage.c
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#include <bopti_internals.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
dimage()
|
||||||
|
Displays a monochrome image in the video ram.
|
||||||
|
*/
|
||||||
|
void dimage(struct Image *img, int x, int y)
|
||||||
|
{
|
||||||
|
int width, layer_size, columns, end;
|
||||||
|
int format = img->format, i = 0;
|
||||||
|
const unsigned char *data;
|
||||||
|
|
||||||
|
if(img->magic != 0xb7) return;
|
||||||
|
if(img->format != Format_Mono && img->format != Format_MonoAlpha)
|
||||||
|
return;
|
||||||
|
op = bopti_op_mono;
|
||||||
|
|
||||||
|
// 'height' refers to a static variable for this file.
|
||||||
|
getStructure(img, &width, &height, &layer_size, &data, &columns, &end);
|
||||||
|
|
||||||
|
vram = display_getCurrentVRAM();
|
||||||
|
|
||||||
|
while(format)
|
||||||
|
{
|
||||||
|
// Drawing every layer, in order of formats.
|
||||||
|
if(format & 1)
|
||||||
|
{
|
||||||
|
channel = (1 << i);
|
||||||
|
bopti(data, x, y, columns, end);
|
||||||
|
data += layer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
format >>= 1;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
src/bopti/gimage.c
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include <bopti_internals.h>
|
||||||
|
#include <gray.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
gimage()
|
||||||
|
Displays a gray image in the video ram.
|
||||||
|
*/
|
||||||
|
void gimage(struct Image *img, int x, int y)
|
||||||
|
{
|
||||||
|
int width, layer_size, columns, end;
|
||||||
|
int format = img->format, i = 0;
|
||||||
|
const unsigned char *data;
|
||||||
|
|
||||||
|
if(img->magic != 0xb7) return;
|
||||||
|
op = bopti_op_gray;
|
||||||
|
|
||||||
|
// 'height' refers to a static variable for this file.
|
||||||
|
getStructure(img, &width, &height, &layer_size, &data, &columns, &end);
|
||||||
|
|
||||||
|
v1 = gray_lightVRAM();
|
||||||
|
v2 = gray_darkVRAM();
|
||||||
|
|
||||||
|
while(format)
|
||||||
|
{
|
||||||
|
// Drawing every layer, in order of formats.
|
||||||
|
if(format & 1)
|
||||||
|
{
|
||||||
|
channel = (1 << i);
|
||||||
|
bopti(data, x, y, columns, end);
|
||||||
|
data += layer_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
format >>= 1;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,19 +1,12 @@
|
||||||
// Used by the exit()-family functions to save and restore the execution state.
|
|
||||||
#include <setjmp.h>
|
|
||||||
static jmp_buf env;
|
|
||||||
// Provides EXIT_SUCCESS and EXIT_FAILURE.
|
|
||||||
#include <stdlib.h>
|
#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)
|
|
@ -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
|
@ -0,0 +1,40 @@
|
||||||
|
#include <gint.h>
|
||||||
|
#include <mpu.h>
|
||||||
|
|
||||||
|
static void (*rtc_callback)(void) = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
gint_setRTCCallback()
|
||||||
|
Sets the callback function for the real-time clock interrupt. If
|
||||||
|
frequency is non-NULL, the clock frequency is set to the given value.
|
||||||
|
|
||||||
|
@arg callback Callback function.
|
||||||
|
@arg frequency Interrupt frequency.
|
||||||
|
*/
|
||||||
|
void gint_setRTCCallback(void (*callback)(void), enum RTCFrequency frequency)
|
||||||
|
{
|
||||||
|
if(frequency < 1 || frequency > 7) return;
|
||||||
|
rtc_callback = callback;
|
||||||
|
|
||||||
|
if(isSH3())
|
||||||
|
gint_setRTCFrequency_7705(frequency);
|
||||||
|
else
|
||||||
|
gint_setRTCFrequency_7305(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
gint_getRTCCallback()
|
||||||
|
Returns the callback function. If frequency is non-NULL, it is set to
|
||||||
|
the current frequency value.
|
||||||
|
*/
|
||||||
|
void (*gint_getRTCCallback(enum RTCFrequency *frequency))(void)
|
||||||
|
{
|
||||||
|
if(!frequency) return rtc_callback;
|
||||||
|
|
||||||
|
if(isSH3())
|
||||||
|
*frequency = gint_getRTCFrequency_7705();
|
||||||
|
else
|
||||||
|
*frequency = gint_getRTCFrequency_7305();
|
||||||
|
|
||||||
|
return rtc_callback;
|
||||||
|
}
|
|
@ -13,8 +13,6 @@
|
||||||
/*
|
/*
|
||||||
gint_getVBR()
|
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
|
|
@ -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
|
473
src/display.c
|
@ -1,473 +0,0 @@
|
||||||
//---
|
|
||||||
//
|
|
||||||
// gint drawing module: display
|
|
||||||
//
|
|
||||||
// Handles vram manipulation and drawing.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// :: Rectangle masks
|
|
||||||
//
|
|
||||||
// The concept of 'rectangle masks' is used several times in this module.
|
|
||||||
// It is based on the fact that an operation that affects a rectangle acts
|
|
||||||
// the same on all its lines. Therefore the behavior of the operation is
|
|
||||||
// determined by its behavior on a single line, which is represented using
|
|
||||||
// 'masks' whose bits indicate whether a pixel is affected (1) or not (0).
|
|
||||||
//
|
|
||||||
// For example when clearing the screen rectangle (16, 16, 112, 48), the
|
|
||||||
// masks will represent information '16 to 112 on x-axis', and will hold
|
|
||||||
// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These
|
|
||||||
// masks can then be used by setting vram[offset] &= ~masks[i]. This
|
|
||||||
// appears to be very flexible : for instance, vram[offset] ^= masks[i]
|
|
||||||
// will reverse the pixels in the same rectangle.
|
|
||||||
//
|
|
||||||
// This technique can also be used in more subtle cases with more complex
|
|
||||||
// patterns, but within this module it is unlikely to happen.
|
|
||||||
//
|
|
||||||
//---
|
|
||||||
|
|
||||||
#include <screen.h>
|
|
||||||
#include <display.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <gray.h>
|
|
||||||
|
|
||||||
// Program video ram. It resides in .bss section, therefore it is cleared at
|
|
||||||
// program initialization and stripped from the executable file.
|
|
||||||
static int local_vram[256];
|
|
||||||
static int *vram = local_vram;
|
|
||||||
|
|
||||||
#define sgn(x) ((x) < 0 ? -1 : 1)
|
|
||||||
#define abs(x) ((x) < 0 ? -(x) : (x))
|
|
||||||
#define rnd(x) ((int)((x) + 0.5))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Local functions.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
adjust()
|
|
||||||
Adjusts the given rectangle coordinates to ensure that :
|
|
||||||
- the rectangle is entirely contained in the screen
|
|
||||||
- x1 < x2
|
|
||||||
- y1 < y2
|
|
||||||
which is needed when working with screen rectangles.
|
|
||||||
*/
|
|
||||||
static void adjust(int *x1, int *y1, int *x2, int *y2)
|
|
||||||
{
|
|
||||||
#define swap(a, b) tmp = a, a = b, b = tmp
|
|
||||||
int tmp;
|
|
||||||
|
|
||||||
if(*x2 < *x1) swap(*x1, *x2);
|
|
||||||
if(*y2 < *y1) swap(*y1, *y2);
|
|
||||||
|
|
||||||
if(*x1 < 0) *x1 = 0;
|
|
||||||
if(*y1 < 0) *y1 = 0;
|
|
||||||
if(*x2 > 127) *x2 = 127;
|
|
||||||
if(*y2 > 63) *y2 = 63;
|
|
||||||
#undef swap
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
getmasks()
|
|
||||||
|
|
||||||
Computes the rectangle masks needed to affect pixels located between x1
|
|
||||||
and x2 (both included). The four masks are stored in the third argument
|
|
||||||
(seen as an array).
|
|
||||||
*/
|
|
||||||
static void getmasks(int x1, int x2, unsigned int *masks)
|
|
||||||
{
|
|
||||||
// Indexes of the first and last longs that are non-blank.
|
|
||||||
int l1 = x1 >> 5;
|
|
||||||
int l2 = x2 >> 5;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
// Setting the base masks. Those are the final values, except for the
|
|
||||||
// longs with indexes l1 and l2, that still need to be adjusted.
|
|
||||||
while(i < l1) masks[i++] = 0x00000000;
|
|
||||||
while(i <= l2) masks[i++] = 0xffffffff;
|
|
||||||
while(i < 4) masks[i++] = 0x00000000;
|
|
||||||
|
|
||||||
// Removing the long number information in x1 and x2 (that is, the
|
|
||||||
// multiples of 32) to keep only the interesting information -- the
|
|
||||||
// number of null bits to add in l1 and l2.
|
|
||||||
x1 &= 31;
|
|
||||||
// Inverting x2 is here the same as computing 32 - x, since 32 is a
|
|
||||||
// power of 2 (positive bits at the left are removed by the mask).
|
|
||||||
x2 = ~x2 & 31;
|
|
||||||
|
|
||||||
// Setting the first and last masks.
|
|
||||||
masks[l1] &= (0xffffffff >> x1);
|
|
||||||
masks[l2] &= (0xffffffff << x2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Generic functions.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
display_getLocalVRAM()
|
|
||||||
Returns the local video ram address. This function always return the
|
|
||||||
same address.
|
|
||||||
The buffer returned by this function should not be used directly when
|
|
||||||
running the gray engine.
|
|
||||||
*/
|
|
||||||
inline void *display_getLocalVRAM(void)
|
|
||||||
{
|
|
||||||
return (void *)local_vram;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
display_getCurrentVRAM()
|
|
||||||
Returns the current video ram. This function usually returns the
|
|
||||||
parameter of the last call to display_useVRAM(), unless the gray engine
|
|
||||||
is running (in which case the result is undefined). Returns the local
|
|
||||||
vram address by default.
|
|
||||||
*/
|
|
||||||
inline void *display_getCurrentVRAM(void)
|
|
||||||
{
|
|
||||||
return (void *)vram;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
display_useVRAM()
|
|
||||||
Changes the current video ram address. The argument MUST be a 4-
|
|
||||||
aligned 1024-byte buffer ; otherwise any drawing operation will crash
|
|
||||||
the program.
|
|
||||||
This function will most likely have no effect when running the gray
|
|
||||||
engine.
|
|
||||||
*/
|
|
||||||
inline void display_useVRAM(void *ptr)
|
|
||||||
{
|
|
||||||
vram = (int *)ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Global drawing functions.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
dupdate()
|
|
||||||
Displays the vram on the physical screen.
|
|
||||||
*/
|
|
||||||
void dupdate(void)
|
|
||||||
{
|
|
||||||
if(gray_runs()) return;
|
|
||||||
screen_display((const void *)local_vram);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dclear()
|
|
||||||
Clears the whole vram.
|
|
||||||
*/
|
|
||||||
void dclear(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < 256; i++) vram[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dclear_area()
|
|
||||||
Clears an area of the vram using rectangle masks. Both (x1, y1) and
|
|
||||||
(x2, y2) are cleared.
|
|
||||||
*/
|
|
||||||
void dclear_area(int x1, int y1, int x2, int y2)
|
|
||||||
{
|
|
||||||
unsigned int masks[4];
|
|
||||||
adjust(&x1, &y1, &x2, &y2);
|
|
||||||
getmasks(x1, x2, masks);
|
|
||||||
|
|
||||||
int begin = y1 << 2;
|
|
||||||
int end = (y2 + 1) << 2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for(i = 0; i < 4; i++) masks[i] = ~masks[i];
|
|
||||||
for(i = begin; i < end; i++) vram[i] &= masks[i & 3];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dreverse_area()
|
|
||||||
Reverses an area of the vram. This function is a simple application of
|
|
||||||
the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as
|
|
||||||
well.
|
|
||||||
*/
|
|
||||||
void dreverse_area(int x1, int y1, int x2, int y2)
|
|
||||||
{
|
|
||||||
unsigned int masks[4];
|
|
||||||
adjust(&x1, &y1, &x2, &y2);
|
|
||||||
getmasks(x1, x2, masks);
|
|
||||||
|
|
||||||
int begin = y1 << 2;
|
|
||||||
int end = (y2 + 1) << 2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if(gray_runs())
|
|
||||||
{
|
|
||||||
int *v1 = gray_lightVRAM();
|
|
||||||
int *v2 = gray_darkVRAM();
|
|
||||||
|
|
||||||
for(i = begin; i < end; i++)
|
|
||||||
{
|
|
||||||
v1[i] ^= masks[i & 3];
|
|
||||||
v2[i] ^= masks[i & 3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else for(i = begin; i < end; i++)
|
|
||||||
{
|
|
||||||
vram[i] ^= masks[i & 3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Local drawing functions.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
dpixel()
|
|
||||||
Puts a pixel in the vram.
|
|
||||||
*/
|
|
||||||
void dpixel(int x, int y, enum Color color)
|
|
||||||
{
|
|
||||||
if((unsigned int)x > 127 || (unsigned int)y > 63) return;
|
|
||||||
|
|
||||||
int offset = (y << 2) + (x >> 5);
|
|
||||||
int mask = 0x80000000 >> (x & 31);
|
|
||||||
|
|
||||||
switch(color)
|
|
||||||
{
|
|
||||||
case Color_White:
|
|
||||||
vram[offset] &= ~mask;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Black:
|
|
||||||
vram[offset] |= mask;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Invert:
|
|
||||||
vram[offset] ^= mask;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
dline()
|
|
||||||
Draws a line on the screen. Automatically optimizes horizontal and
|
|
||||||
vertical lines.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void dhline(int x1, int x2, int y, enum Color color)
|
|
||||||
{
|
|
||||||
unsigned int masks[4];
|
|
||||||
int offset = y << 2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// Swapping x1 and x2 if needed.
|
|
||||||
if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2;
|
|
||||||
getmasks(x1, x2, masks);
|
|
||||||
|
|
||||||
int *v1 = gray_lightVRAM();
|
|
||||||
int *v2 = gray_darkVRAM();
|
|
||||||
|
|
||||||
if(gray_runs()) switch(color)
|
|
||||||
{
|
|
||||||
case Color_White:
|
|
||||||
for(i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
v1[offset + i] &= ~masks[i];
|
|
||||||
v2[offset + i] &= ~masks[i];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Light:
|
|
||||||
for(i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
v1[offset + i] |= masks[i];
|
|
||||||
v2[offset + i] &= ~masks[i];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Dark:
|
|
||||||
for(i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
v1[offset + i] &= ~masks[i];
|
|
||||||
v2[offset + i] |= masks[i];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Black:
|
|
||||||
for(i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
v1[offset + i] |= masks[i];
|
|
||||||
v2[offset + i] |= masks[i];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Invert:
|
|
||||||
for(i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
v1[offset + i] ^= masks[i];
|
|
||||||
v2[offset + i] ^= masks[i];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
else switch(color)
|
|
||||||
{
|
|
||||||
case Color_White:
|
|
||||||
for(i = 0; i < 4; i++) vram[offset + i] &= ~masks[i];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Black:
|
|
||||||
for(i = 0; i < 4; i++) vram[offset + i] |= masks[i];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Invert:
|
|
||||||
for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i];
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dvline(int y1, int y2, int x, enum Color color)
|
|
||||||
{
|
|
||||||
int offset = (y1 << 2) + (x >> 5);
|
|
||||||
int end = (y2 << 2) + (x >> 5);
|
|
||||||
int mask = 0x80000000 >> (x & 31);
|
|
||||||
|
|
||||||
int *v1 = gray_lightVRAM();
|
|
||||||
int *v2 = gray_darkVRAM();
|
|
||||||
|
|
||||||
if(gray_runs()) switch(color)
|
|
||||||
{
|
|
||||||
case Color_White:
|
|
||||||
while(offset <= end)
|
|
||||||
{
|
|
||||||
v1[offset] &= ~mask;
|
|
||||||
v2[offset] &= ~mask;
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Light:
|
|
||||||
while(offset <= end)
|
|
||||||
{
|
|
||||||
v1[offset] |= mask;
|
|
||||||
v2[offset] &= ~mask;
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Dark:
|
|
||||||
while(offset <= end)
|
|
||||||
{
|
|
||||||
v1[offset] &= ~mask;
|
|
||||||
v2[offset] |= mask;
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Black:
|
|
||||||
while(offset <= end)
|
|
||||||
{
|
|
||||||
v1[offset] |= mask;
|
|
||||||
v2[offset] |= mask;
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Invert:
|
|
||||||
while(offset <= end)
|
|
||||||
{
|
|
||||||
v1[offset] ^= mask;
|
|
||||||
v2[offset] ^= mask;
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
else switch(color)
|
|
||||||
{
|
|
||||||
case Color_White:
|
|
||||||
while(offset <= end) vram[offset] &= ~mask, offset += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Black:
|
|
||||||
while(offset <= end) vram[offset] |= mask, offset += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Color_Invert:
|
|
||||||
while(offset <= end) vram[offset] ^= mask, offset += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dline(int x1, int y1, int x2, int y2, enum Color color)
|
|
||||||
{
|
|
||||||
adjust(&x1, &y1, &x2, &y2);
|
|
||||||
|
|
||||||
// Possible optimizations.
|
|
||||||
if(y1 == y2)
|
|
||||||
{
|
|
||||||
dhline(x1, x2, y1, color);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(x1 == x2)
|
|
||||||
{
|
|
||||||
dvline(y1, y2, x1, color);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i, x = x1, y = y1, cumul;
|
|
||||||
int dx = x2 - x1, dy = y2 - y1;
|
|
||||||
int sx = sgn(dx), sy = sgn(dy);
|
|
||||||
|
|
||||||
dx = abs(dx), dy = abs(dy);
|
|
||||||
|
|
||||||
dpixel(x1, y1, color);
|
|
||||||
|
|
||||||
if(dx >= dy)
|
|
||||||
{
|
|
||||||
cumul = dx >> 1;
|
|
||||||
for(i = 1; i < dx; i++)
|
|
||||||
{
|
|
||||||
x += sx;
|
|
||||||
cumul += dy;
|
|
||||||
if(cumul > dx) cumul -= dx, y += sy;
|
|
||||||
dpixel(x, y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cumul = dy >> 1;
|
|
||||||
for(i = 1; i < dy; i++)
|
|
||||||
{
|
|
||||||
y += sy;
|
|
||||||
cumul += dx;
|
|
||||||
if(cumul > dy) cumul -= dy, x += sx;
|
|
||||||
dpixel(x, y, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dpixel(x2, y2, color);
|
|
||||||
}
|
|
24
src/display/adjustRectangle.c
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include <display_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
adjustRectangle()
|
||||||
|
Adjusts the given rectangle coordinates to ensure that :
|
||||||
|
- the rectangle is entirely contained in the screen
|
||||||
|
- x1 < x2
|
||||||
|
- y1 < y2
|
||||||
|
which is needed when working with screen rectangles.
|
||||||
|
*/
|
||||||
|
void adjustRectangle(int *x1, int *y1, int *x2, int *y2)
|
||||||
|
{
|
||||||
|
#define swap(a, b) tmp = a, a = b, b = tmp
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
if(*x2 < *x1) swap(*x1, *x2);
|
||||||
|
if(*y2 < *y1) swap(*y1, *y2);
|
||||||
|
|
||||||
|
if(*x1 < 0) *x1 = 0;
|
||||||
|
if(*y1 < 0) *y1 = 0;
|
||||||
|
if(*x2 > 127) *x2 = 127;
|
||||||
|
if(*y2 > 63) *y2 = 63;
|
||||||
|
#undef swap
|
||||||
|
}
|
12
src/display/dclear.c
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include <display_internals.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
dclear()
|
||||||
|
Clears the whole vram.
|
||||||
|
*/
|
||||||
|
void dclear(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 256; i++) vram[i] = 0;
|
||||||
|
}
|
21
src/display/dclear_area.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include <display_internals.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
dclear_area()
|
||||||
|
Clears an area of the vram using rectangle masks. Both (x1, y1) and
|
||||||
|
(x2, y2) are cleared.
|
||||||
|
*/
|
||||||
|
void dclear_area(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
unsigned int masks[4];
|
||||||
|
adjustRectangle(&x1, &y1, &x2, &y2);
|
||||||
|
getMasks(x1, x2, masks);
|
||||||
|
|
||||||
|
int begin = y1 << 2;
|
||||||
|
int end = (y2 + 1) << 2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < 4; i++) masks[i] = ~masks[i];
|
||||||
|
for(i = begin; i < end; i++) vram[i] &= masks[i & 3];
|
||||||
|
}
|
52
src/display/display_internals.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint drawing module: display
|
||||||
|
//
|
||||||
|
// Handles vram manipulation and drawing.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// :: Rectangle masks
|
||||||
|
//
|
||||||
|
// The concept of 'rectangle masks' is used several times in this module.
|
||||||
|
// It is based on the fact that an operation that affects a rectangle acts
|
||||||
|
// the same on all its lines. Therefore the behavior of the operation is
|
||||||
|
// determined by its behavior on a single line, which is represented using
|
||||||
|
// 'masks' whose bits indicate whether a pixel is affected (1) or not (0).
|
||||||
|
//
|
||||||
|
// For example when clearing the screen rectangle (16, 16, 112, 48), the
|
||||||
|
// masks will represent information '16 to 112 on x-axis', and will hold
|
||||||
|
// the following values : 0000ffff, ffffffff, ffffffff and ffff0000. These
|
||||||
|
// masks can then be used by setting vram[offset] &= ~masks[i]. This
|
||||||
|
// appears to be very flexible : for instance, vram[offset] ^= masks[i]
|
||||||
|
// will reverse the pixels in the same rectangle.
|
||||||
|
//
|
||||||
|
// This technique can also be used in more subtle cases with more complex
|
||||||
|
// patterns, but within this module it is unlikely to happen.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef _DISPLAY_INTERNALS_H
|
||||||
|
#define _DISPLAY_INTERNALS_H
|
||||||
|
|
||||||
|
extern int *vram;
|
||||||
|
|
||||||
|
/*
|
||||||
|
adjustRectangle()
|
||||||
|
Adjusts the given rectangle coordinates to ensure that :
|
||||||
|
- the rectangle is entirely contained in the screen
|
||||||
|
- x1 < x2
|
||||||
|
- y1 < y2
|
||||||
|
which is needed when working with screen rectangles.
|
||||||
|
*/
|
||||||
|
static void adjustRectangle(int *x1, int *y1, int *x2, int *y2);
|
||||||
|
|
||||||
|
/*
|
||||||
|
getMasks()
|
||||||
|
Computes the rectangle masks needed to affect pixels located between x1
|
||||||
|
and x2 (both included). The four masks are stored in the third argument
|
||||||
|
(seen as an array).
|
||||||
|
See this module's internals header file for more information.
|
||||||
|
*/
|
||||||
|
static void getMasks(int x1, int x2, unsigned int *masks);
|
||||||
|
|
||||||
|
#endif // _DISPLAY_INTERNALS_H
|
43
src/display/display_vram.c
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
// Program video ram. It resides in .bss section, therefore it is cleared at
|
||||||
|
// program initialization and stripped from the executable file.
|
||||||
|
static int local_vram[256];
|
||||||
|
int *vram = local_vram;
|
||||||
|
|
||||||
|
/*
|
||||||
|
display_getLocalVRAM()
|
||||||
|
Returns the local video ram address. This function always return the
|
||||||
|
same address.
|
||||||
|
The buffer returned by this function should not be used directly when
|
||||||
|
running the gray engine.
|
||||||
|
*/
|
||||||
|
inline void *display_getLocalVRAM(void)
|
||||||
|
{
|
||||||
|
return (void *)local_vram;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
display_getCurrentVRAM()
|
||||||
|
Returns the current video ram. This function usually returns the
|
||||||
|
parameter of the last call to display_useVRAM(), unless the gray engine
|
||||||
|
is running (in which case the result is undefined). Returns the local
|
||||||
|
vram address by default.
|
||||||
|
*/
|
||||||
|
inline void *display_getCurrentVRAM(void)
|
||||||
|
{
|
||||||
|
return (void *)vram;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
display_useVRAM()
|
||||||
|
Changes the current video ram address. The argument MUST be a 4-
|
||||||
|
aligned 1024-byte buffer ; otherwise any drawing operation will crash
|
||||||
|
the program.
|
||||||
|
This function will most likely have no effect when running the gray
|
||||||
|
engine.
|
||||||
|
*/
|
||||||
|
inline void display_useVRAM(void *ptr)
|
||||||
|
{
|
||||||
|
vram = (int *)ptr;
|
||||||
|
}
|
116
src/display/dline.c
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
#include <display_internals.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
#define sgn(x) ((x) < 0 ? -1 : 1)
|
||||||
|
#define abs(x) ((x) < 0 ? -(x) : (x))
|
||||||
|
#define rnd(x) ((int)((x) + 0.5))
|
||||||
|
|
||||||
|
/*
|
||||||
|
dline()
|
||||||
|
Draws a line on the screen. Automatically optimizes horizontal and
|
||||||
|
vertical lines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void dhline(int x1, int x2, int y, enum Color color)
|
||||||
|
{
|
||||||
|
unsigned int masks[4];
|
||||||
|
int offset = y << 2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Swapping x1 and x2 if needed.
|
||||||
|
if(x1 > x2) x1 ^= x2, x2 ^= x1, x1 ^= x2;
|
||||||
|
getMasks(x1, x2, masks);
|
||||||
|
|
||||||
|
switch(color)
|
||||||
|
{
|
||||||
|
case Color_White:
|
||||||
|
for(i = 0; i < 4; i++) vram[offset + i] &= ~masks[i];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Black:
|
||||||
|
for(i = 0; i < 4; i++) vram[offset + i] |= masks[i];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Invert:
|
||||||
|
for(i = 0; i < 4; i++) vram[offset + i] ^= masks[i];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dvline(int y1, int y2, int x, enum Color color)
|
||||||
|
{
|
||||||
|
int offset = (y1 << 2) + (x >> 5);
|
||||||
|
int end = (y2 << 2) + (x >> 5);
|
||||||
|
int mask = 0x80000000 >> (x & 31);
|
||||||
|
|
||||||
|
switch(color)
|
||||||
|
{
|
||||||
|
case Color_White:
|
||||||
|
while(offset <= end) vram[offset] &= ~mask, offset += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Black:
|
||||||
|
while(offset <= end) vram[offset] |= mask, offset += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Invert:
|
||||||
|
while(offset <= end) vram[offset] ^= mask, offset += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void dline(int x1, int y1, int x2, int y2, enum Color color)
|
||||||
|
{
|
||||||
|
adjustRectangle(&x1, &y1, &x2, &y2);
|
||||||
|
|
||||||
|
// Possible optimizations.
|
||||||
|
if(y1 == y2)
|
||||||
|
{
|
||||||
|
dhline(x1, x2, y1, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(x1 == x2)
|
||||||
|
{
|
||||||
|
dvline(y1, y2, x1, color);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i, x = x1, y = y1, cumul;
|
||||||
|
int dx = x2 - x1, dy = y2 - y1;
|
||||||
|
int sx = sgn(dx), sy = sgn(dy);
|
||||||
|
|
||||||
|
dx = abs(dx), dy = abs(dy);
|
||||||
|
|
||||||
|
dpixel(x1, y1, color);
|
||||||
|
|
||||||
|
if(dx >= dy)
|
||||||
|
{
|
||||||
|
cumul = dx >> 1;
|
||||||
|
for(i = 1; i < dx; i++)
|
||||||
|
{
|
||||||
|
x += sx;
|
||||||
|
cumul += dy;
|
||||||
|
if(cumul > dx) cumul -= dx, y += sy;
|
||||||
|
dpixel(x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cumul = dy >> 1;
|
||||||
|
for(i = 1; i < dy; i++)
|
||||||
|
{
|
||||||
|
y += sy;
|
||||||
|
cumul += dx;
|
||||||
|
if(cumul > dy) cumul -= dy, x += sx;
|
||||||
|
dpixel(x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dpixel(x2, y2, color);
|
||||||
|
}
|
32
src/display/dpixel.c
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#include <display_internals.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
dpixel()
|
||||||
|
Puts a pixel in the vram.
|
||||||
|
*/
|
||||||
|
void dpixel(int x, int y, enum Color color)
|
||||||
|
{
|
||||||
|
if((unsigned int)x > 127 || (unsigned int)y > 63) return;
|
||||||
|
|
||||||
|
int offset = (y << 2) + (x >> 5);
|
||||||
|
int mask = 0x80000000 >> (x & 31);
|
||||||
|
|
||||||
|
switch(color)
|
||||||
|
{
|
||||||
|
case Color_White:
|
||||||
|
vram[offset] &= ~mask;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Black:
|
||||||
|
vram[offset] |= mask;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Invert:
|
||||||
|
vram[offset] ^= mask;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
21
src/display/dreverse_area.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include <display_internals.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
dreverse_area()
|
||||||
|
Reverses an area of the vram. This function is a simple application of
|
||||||
|
the rectangle masks concept. (x1, y1) and (x2, y2) are reversed as
|
||||||
|
well.
|
||||||
|
*/
|
||||||
|
void dreverse_area(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
unsigned int masks[4];
|
||||||
|
adjustRectangle(&x1, &y1, &x2, &y2);
|
||||||
|
getMasks(x1, x2, masks);
|
||||||
|
|
||||||
|
int begin = y1 << 2;
|
||||||
|
int end = (y2 + 1) << 2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = begin; i < end; i++) vram[i] ^= masks[i & 3];
|
||||||
|
}
|
14
src/display/dupdate.c
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <display_internals.h>
|
||||||
|
#include <display.h>
|
||||||
|
#include <screen.h>
|
||||||
|
#include <gray.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
dupdate()
|
||||||
|
Displays the vram on the physical screen.
|
||||||
|
*/
|
||||||
|
void dupdate(void)
|
||||||
|
{
|
||||||
|
if(gray_runs()) return;
|
||||||
|
screen_display((const void *)vram);
|
||||||
|
}
|
34
src/display/getMasks.c
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#include <display_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
getMasks()
|
||||||
|
Computes the rectangle masks needed to affect pixels located between x1
|
||||||
|
and x2 (both included). The four masks are stored in the third argument
|
||||||
|
(seen as an array).
|
||||||
|
See this module's internals header file for more information.
|
||||||
|
*/
|
||||||
|
void getMasks(int x1, int x2, unsigned int *masks)
|
||||||
|
{
|
||||||
|
// Indexes of the first and last longs that are non-blank.
|
||||||
|
int l1 = x1 >> 5;
|
||||||
|
int l2 = x2 >> 5;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
// Setting the base masks. Those are the final values, except for the
|
||||||
|
// longs with indexes l1 and l2, that still need to be adjusted.
|
||||||
|
while(i < l1) masks[i++] = 0x00000000;
|
||||||
|
while(i <= l2) masks[i++] = 0xffffffff;
|
||||||
|
while(i < 4) masks[i++] = 0x00000000;
|
||||||
|
|
||||||
|
// Removing the long number information in x1 and x2 (that is, the
|
||||||
|
// multiples of 32) to keep only the interesting information -- the
|
||||||
|
// number of null bits to add in l1 and l2.
|
||||||
|
x1 &= 31;
|
||||||
|
// Inverting x2 is here the same as computing 32 - x, since 32 is a
|
||||||
|
// power of 2 (positive bits at the left are removed by the mask).
|
||||||
|
x2 = ~x2 & 31;
|
||||||
|
|
||||||
|
// Setting the first and last masks.
|
||||||
|
masks[l1] &= (0xffffffff >> x1);
|
||||||
|
masks[l2] &= (0xffffffff << x2);
|
||||||
|
}
|
313
src/gint_7305.c
|
@ -1,313 +0,0 @@
|
||||||
#include <gint.h>
|
|
||||||
#include <timer.h>
|
|
||||||
#include <keyboard.h>
|
|
||||||
#include <7305.h>
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Interrupt codes.
|
|
||||||
//---
|
|
||||||
|
|
||||||
#define IC_RTC_PRI 0xaa0
|
|
||||||
#define IC_KEYSC 0xbe0
|
|
||||||
#define IC_TMU0_TUNI0 0x400
|
|
||||||
#define IC_TMU0_TUNI1 0x420
|
|
||||||
#define IC_TMU0_TUNI2 0x440
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Various MPU-dependent procedures.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
gint_setRTCFrequency()
|
|
||||||
Sets the RTC interrupt frequency and enables interrupts.
|
|
||||||
|
|
||||||
@arg frequency
|
|
||||||
*/
|
|
||||||
void gint_setRTCFrequency_7305(enum RTCFrequency frequency)
|
|
||||||
{
|
|
||||||
if(frequency < 1 || frequency > 7) return;
|
|
||||||
RTC.RCR2.BYTE = (frequency << 4) | 0x09;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
gint_getRTCFrequency()
|
|
||||||
Returns the RTC interrupt frequency.
|
|
||||||
|
|
||||||
@return RTC interrupt frequency.
|
|
||||||
*/
|
|
||||||
enum RTCFrequency gint_getRTCFrequency_7305(void)
|
|
||||||
{
|
|
||||||
return (RTC.RCR2.BYTE & 0x70) >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Keyboard management.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
kdelay()
|
|
||||||
Should sleep during a few milliseconds. Well...
|
|
||||||
This delay has a great influence on key detection. Here are a few
|
|
||||||
observations of what happens with common numbers of "nop" (without
|
|
||||||
overclock). Take care of the effect of overclock !
|
|
||||||
|
|
||||||
Column effects
|
|
||||||
May have something to do with register HIZCRB not being used here. When
|
|
||||||
many keys on the same column are pressed, other keys of the same column
|
|
||||||
may be triggered.
|
|
||||||
|
|
||||||
- Less Bad key detection.
|
|
||||||
- 8 Very few column effects. Most often, three keys may be pressed
|
|
||||||
simultaneously. However, [UP] has latencies and is globally not
|
|
||||||
detected.
|
|
||||||
- 12 Seems best. Every individual key is detected well. No column
|
|
||||||
effect observed before four keys.
|
|
||||||
- 16 Every single key is detected correctly. Pressing two keys on a
|
|
||||||
same column does not usually create a column effect. Three keys
|
|
||||||
almost always.
|
|
||||||
- More Does not improve single key detection, and increase column
|
|
||||||
effects. At 256 every single key press create a whole column
|
|
||||||
effect.
|
|
||||||
*/
|
|
||||||
static void kdelay(void)
|
|
||||||
{
|
|
||||||
#define r4(str) str str str str
|
|
||||||
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
r4("nop\n\t")
|
|
||||||
r4("nop\n\t")
|
|
||||||
r4("nop\n\t")
|
|
||||||
);
|
|
||||||
|
|
||||||
#undef r4
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
krow()
|
|
||||||
Reads a keyboard row. Works like krow() for SH7705. See gint_7705.c for
|
|
||||||
more details.
|
|
||||||
*/
|
|
||||||
static int krow(int row)
|
|
||||||
{
|
|
||||||
volatile unsigned short *injector1 = (unsigned short *)0xa4050116;
|
|
||||||
volatile unsigned char *data1 = (unsigned char *)0xa4050136;
|
|
||||||
|
|
||||||
volatile unsigned short *injector2 = (unsigned short *)0xa4050118;
|
|
||||||
volatile unsigned char *data2 = (unsigned char *)0xa4050138;
|
|
||||||
|
|
||||||
volatile unsigned short *detector = (unsigned short *)0xa405014c;
|
|
||||||
volatile unsigned char *keys = (unsigned char *)0xa405016c;
|
|
||||||
|
|
||||||
volatile unsigned char *key_register = (unsigned char *)0xa40501c6;
|
|
||||||
// volatile unsigned short *hizcrb = (unsigned short *)0xa405015a;
|
|
||||||
|
|
||||||
unsigned short smask;
|
|
||||||
unsigned char cmask;
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
if(row < 0 || row > 9) return 0;
|
|
||||||
|
|
||||||
// Additional configuration for SH7305.
|
|
||||||
*detector = 0xaaaa;
|
|
||||||
*key_register = 0xff;
|
|
||||||
*injector1 = (*injector1 & 0xf000) | 0x0555;
|
|
||||||
*injector2 = (*injector2 & 0xf000) | 0x0555;
|
|
||||||
*data1 |= 0x3f;
|
|
||||||
*data2 |= 0x3f;
|
|
||||||
kdelay();
|
|
||||||
|
|
||||||
if(row < 6)
|
|
||||||
{
|
|
||||||
smask = 0x0003 << (row * 2);
|
|
||||||
cmask = ~(1 << row);
|
|
||||||
|
|
||||||
*injector1 = ((*injector1 & 0xf000) | 0x0aaa) ^ smask;
|
|
||||||
*injector2 = (*injector2 & 0xf000) | 0x0aaa;
|
|
||||||
kdelay();
|
|
||||||
|
|
||||||
*data1 = (*data1 & 0xc0) | cmask;
|
|
||||||
*data2 |= 0x3f;
|
|
||||||
kdelay();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
smask = 0x0003 << ((row - 6) * 2);
|
|
||||||
cmask = ~(1 << (row - 6));
|
|
||||||
|
|
||||||
*injector1 = (*injector1 & 0xf000) | 0x0aaa;
|
|
||||||
*injector2 = ((*injector2 & 0xf000) | 0x0aaa) ^ smask;
|
|
||||||
kdelay();
|
|
||||||
|
|
||||||
*data1 |= 0x3f;
|
|
||||||
*data2 = (*data2 & 0xc0) | cmask;
|
|
||||||
kdelay();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading the keyboard row.
|
|
||||||
result = ~*keys;
|
|
||||||
kdelay();
|
|
||||||
|
|
||||||
// Re-initializing the port configuration and data.
|
|
||||||
*injector1 = (*injector1 & 0xf000) | 0x0aaa;
|
|
||||||
*injector2 = (*injector2 & 0xf000) | 0x0aaa;
|
|
||||||
kdelay();
|
|
||||||
*injector1 = (*injector1 & 0xf000) | 0x0555;
|
|
||||||
*injector2 = (*injector2 & 0xf000) | 0x0555;
|
|
||||||
kdelay();
|
|
||||||
*data1 &= 0xc0;
|
|
||||||
*data2 &= 0xc0;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
keyboard_updateState()
|
|
||||||
Updates the keyboard state.
|
|
||||||
*/
|
|
||||||
void keyboard_updateState_7305(volatile unsigned char *keyboard_state)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for(i = 0; i < 10; i++) keyboard_state[i] = krow(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Interrupt handler.
|
|
||||||
//---
|
|
||||||
|
|
||||||
void gint_7305(void)
|
|
||||||
{
|
|
||||||
volatile unsigned int *intevt = (unsigned int *)0xff000028;
|
|
||||||
unsigned int code = *intevt;
|
|
||||||
|
|
||||||
switch(code)
|
|
||||||
{
|
|
||||||
case IC_RTC_PRI:
|
|
||||||
RTC.RCR2.PEF = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IC_TMU0_TUNI0:
|
|
||||||
timer_interrupt(TIMER_TMU0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IC_TMU0_TUNI1:
|
|
||||||
timer_interrupt(TIMER_TMU1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IC_TMU0_TUNI2:
|
|
||||||
timer_interrupt(TIMER_TMU2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Setup.
|
|
||||||
//---
|
|
||||||
|
|
||||||
static unsigned short ipr[12];
|
|
||||||
static unsigned char rcr2;
|
|
||||||
// Saves of the keyboard registres. Could be better.
|
|
||||||
static unsigned short inj1, inj2, det;
|
|
||||||
static unsigned char data1, data2, keys, reg;
|
|
||||||
|
|
||||||
static void gint_priority_lock_7305(void)
|
|
||||||
{
|
|
||||||
// Saving the current interrupt priorities.
|
|
||||||
ipr[0] = INTX.IPRA.WORD;
|
|
||||||
ipr[1] = INTX.IPRB.WORD;
|
|
||||||
ipr[2] = INTX.IPRC.WORD;
|
|
||||||
ipr[3] = INTX.IPRD.WORD;
|
|
||||||
ipr[4] = INTX.IPRE.WORD;
|
|
||||||
ipr[5] = INTX.IPRF.WORD;
|
|
||||||
ipr[6] = INTX.IPRG.WORD;
|
|
||||||
ipr[7] = INTX.IPRH.WORD;
|
|
||||||
ipr[8] = INTX.IPRI.WORD;
|
|
||||||
ipr[9] = INTX.IPRJ.WORD;
|
|
||||||
ipr[10] = INTX.IPRK.WORD;
|
|
||||||
ipr[11] = INTX.IPRL.WORD;
|
|
||||||
|
|
||||||
// Disabling everything by default to avoid freezing on unhandled
|
|
||||||
// interrupts.
|
|
||||||
INTX.IPRA.WORD = 0x0000;
|
|
||||||
INTX.IPRB.WORD = 0x0000;
|
|
||||||
INTX.IPRC.WORD = 0x0000;
|
|
||||||
INTX.IPRD.WORD = 0x0000;
|
|
||||||
INTX.IPRE.WORD = 0x0000;
|
|
||||||
INTX.IPRF.WORD = 0x0000;
|
|
||||||
INTX.IPRG.WORD = 0x0000;
|
|
||||||
INTX.IPRH.WORD = 0x0000;
|
|
||||||
INTX.IPRI.WORD = 0x0000;
|
|
||||||
INTX.IPRJ.WORD = 0x0000;
|
|
||||||
INTX.IPRK.WORD = 0x0000;
|
|
||||||
INTX.IPRL.WORD = 0x0000;
|
|
||||||
|
|
||||||
// Saving keyboard registers.
|
|
||||||
inj1 = *((volatile unsigned short *)0xa4050116);
|
|
||||||
data1 = *((volatile unsigned char *)0xa4050136);
|
|
||||||
inj2 = *((volatile unsigned short *)0xa4050118);
|
|
||||||
data2 = *((volatile unsigned char *)0xa4050138);
|
|
||||||
det = *((volatile unsigned short *)0xa405014c);
|
|
||||||
keys = *((volatile unsigned char *)0xa405016c);
|
|
||||||
reg = *((volatile unsigned char *)0xa40501c6);
|
|
||||||
|
|
||||||
// Allowing RTC. Keyboard analysis is done regularly using a RTC
|
|
||||||
// because SH7305's special KEYSC interface does not allow us to clear
|
|
||||||
// the keyboard interrupt flags.
|
|
||||||
INTX.IPRK._RTC = GINT_INTP_RTC;
|
|
||||||
INTX.IPRA.TMU0_0 = GINT_INTP_KEY;
|
|
||||||
INTX.IPRA.TMU0_1 = GINT_INTP_GRAY;
|
|
||||||
INTX.IPRA.TMU0_2 = GINT_INTP_TIMER;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gint_priority_unlock_7305(void)
|
|
||||||
{
|
|
||||||
// Restoring the interrupt priorities.
|
|
||||||
INTX.IPRA.WORD = ipr[0];
|
|
||||||
INTX.IPRB.WORD = ipr[1];
|
|
||||||
INTX.IPRC.WORD = ipr[2];
|
|
||||||
INTX.IPRD.WORD = ipr[3];
|
|
||||||
INTX.IPRE.WORD = ipr[4];
|
|
||||||
INTX.IPRF.WORD = ipr[5];
|
|
||||||
INTX.IPRG.WORD = ipr[6];
|
|
||||||
INTX.IPRH.WORD = ipr[7];
|
|
||||||
INTX.IPRI.WORD = ipr[8];
|
|
||||||
INTX.IPRJ.WORD = ipr[9];
|
|
||||||
INTX.IPRK.WORD = ipr[10];
|
|
||||||
INTX.IPRL.WORD = ipr[11];
|
|
||||||
|
|
||||||
// Restoring keyboard registers.
|
|
||||||
*((volatile unsigned short *)0xa4050116) = inj1;
|
|
||||||
*((volatile unsigned char *)0xa4050136) = data1;
|
|
||||||
*((volatile unsigned short *)0xa4050118) = inj2;
|
|
||||||
*((volatile unsigned char *)0xa4050138) = data2;
|
|
||||||
*((volatile unsigned short *)0xa405014c) = det;
|
|
||||||
*((volatile unsigned char *)0xa405016c) = keys;
|
|
||||||
*((volatile unsigned char *)0xa40501c6) = reg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gint_setup_7305(void)
|
|
||||||
{
|
|
||||||
gint_priority_lock_7305();
|
|
||||||
|
|
||||||
// Saving the RTC configuration.
|
|
||||||
rcr2 = RTC.RCR2.BYTE;
|
|
||||||
// Disabling RTC interrupts by default.
|
|
||||||
RTC.RCR2.BYTE = 0x09;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gint_stop_7305(void)
|
|
||||||
{
|
|
||||||
gint_priority_unlock_7305();
|
|
||||||
|
|
||||||
// Restoring the RTC configuration.
|
|
||||||
RTC.RCR2.BYTE = rcr2;
|
|
||||||
}
|
|
276
src/gint_7705.c
|
@ -1,276 +0,0 @@
|
||||||
#include <gint.h>
|
|
||||||
#include <timer.h>
|
|
||||||
#include <keyboard.h>
|
|
||||||
#include <7705.h>
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Interrupt codes.
|
|
||||||
//---
|
|
||||||
|
|
||||||
#define IC_RTC_PRI 0x4a0
|
|
||||||
#define IC_PINT07 0x700
|
|
||||||
#define IC_TMU0_TUNI0 0x400
|
|
||||||
#define IC_TMU1_TUNI1 0x420
|
|
||||||
#define IC_TMU2_TUNI2 0x440
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Various MPU-dependent procedures.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
gint_setRTCFrequency()
|
|
||||||
Sets the RTC interrupt frequency and enables interrupts.
|
|
||||||
|
|
||||||
@arg frequency
|
|
||||||
*/
|
|
||||||
void gint_setRTCFrequency_7705(enum RTCFrequency frequency)
|
|
||||||
{
|
|
||||||
if(frequency < 1 || frequency > 7) return;
|
|
||||||
RTC.RCR2.BYTE = (frequency << 4) | 0x09;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
gint_getRTCFrequency()
|
|
||||||
Returns the RTC interrupt frequency.
|
|
||||||
|
|
||||||
@return RTC interrupt frequency.
|
|
||||||
*/
|
|
||||||
enum RTCFrequency gint_getRTCFrequency_7705(void)
|
|
||||||
{
|
|
||||||
return (RTC.RCR2.BYTE & 0x70) >> 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Keyboard management.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
kdelay()
|
|
||||||
Used to be a low-level sleep using the watchdog, as in the system. This
|
|
||||||
way seems OK at least, and it doesn't create column effects as for
|
|
||||||
SH7305.
|
|
||||||
*/
|
|
||||||
static void kdelay(void)
|
|
||||||
{
|
|
||||||
#define r4(str) str str str str
|
|
||||||
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
r4("nop\n\t")
|
|
||||||
r4("nop\n\t")
|
|
||||||
r4("nop\n\t")
|
|
||||||
);
|
|
||||||
|
|
||||||
#undef r4
|
|
||||||
|
|
||||||
/* Watchdog version.
|
|
||||||
const int delay = 0xf4;
|
|
||||||
|
|
||||||
// Disabling the watchdog timer interrupt and resetting the
|
|
||||||
// configuration. Setting the delay.
|
|
||||||
INTC.IPRB.BIT._WDT = 0;
|
|
||||||
WDT.WTCSR.WRITE = 0xa500;
|
|
||||||
WDT.WTCNT.WRITE = 0x5a00 | (delay & 0xff);
|
|
||||||
|
|
||||||
// Counting on Po/256.
|
|
||||||
WDT.WTCSR.WRITE = 0xa505;
|
|
||||||
// Starting the timer (sets again to Po/256).
|
|
||||||
WDT.WTCSR.WRITE = 0xa585;
|
|
||||||
|
|
||||||
// Waiting until it overflows (delaying), then clearing the overflow
|
|
||||||
// flag.
|
|
||||||
while((WDT.WTCSR.READ.BYTE & 0x08) == 0);
|
|
||||||
WDT.WTCSR.WRITE = 0xa500 | (WDT.WTCSR.READ.BYTE & 0xf7);
|
|
||||||
|
|
||||||
// Resetting the configuration and the counter.
|
|
||||||
WDT.WTCSR.WRITE = 0xa500;
|
|
||||||
WDT.WTCSR.WRITE = 0x5a00;
|
|
||||||
|
|
||||||
// Enabling back the watchdog timer interrupt.
|
|
||||||
INTC.IPRB.BIT._WDT = GINT_INTP_WDT;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
krow()
|
|
||||||
Reads a keyboard row.
|
|
||||||
|
|
||||||
@arg row Row to check (0 <= row <= 9).
|
|
||||||
|
|
||||||
@return Bit-based representation of pressed keys in the checked row.
|
|
||||||
*/
|
|
||||||
static int krow(int row)
|
|
||||||
{
|
|
||||||
// '11' on the active row, '00' everywhere else.
|
|
||||||
unsigned short smask = 0x0003 << ((row % 8) * 2);
|
|
||||||
// '0' on the active row, '1' everywhere else.
|
|
||||||
unsigned char cmask = ~(1 << (row % 8));
|
|
||||||
// Line results.
|
|
||||||
int result = 0;
|
|
||||||
|
|
||||||
if(row < 0 || row > 9) return 0;
|
|
||||||
|
|
||||||
// Initial configuration.
|
|
||||||
PFC.PBCR.WORD = 0xaaaa;
|
|
||||||
PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x0055;
|
|
||||||
kdelay();
|
|
||||||
|
|
||||||
if(row < 8)
|
|
||||||
{
|
|
||||||
// Configuring port B/M as input except for the row to check,
|
|
||||||
// which has to be an output. This sets '01' (output) on the
|
|
||||||
// active row, '10' (input) everywhere else.
|
|
||||||
PFC.PBCR.WORD = 0xaaaa ^ smask;
|
|
||||||
PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x00aa;
|
|
||||||
kdelay();
|
|
||||||
|
|
||||||
// Every bit set to 1 except the active row bit.
|
|
||||||
PB.DR.BYTE = cmask;
|
|
||||||
PM.DR.BYTE = (PM.DR.BYTE & 0xf0) | 0x0f;
|
|
||||||
kdelay();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The same, but deals with port M.
|
|
||||||
PFC.PBCR.WORD = 0xaaaa;
|
|
||||||
PFC.PMCR.WORD = ((PFC.PMCR.WORD & 0xff00) | 0x00aa) ^ smask;
|
|
||||||
kdelay();
|
|
||||||
|
|
||||||
PB.DR.BYTE = 0xff;
|
|
||||||
PM.DR.BYTE = (PM.DR.BYTE & 0xf0) | cmask;
|
|
||||||
kdelay();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reading the keyboard row.
|
|
||||||
result = ~PA.DR.BYTE;
|
|
||||||
kdelay();
|
|
||||||
|
|
||||||
// Re-initializing the port configuration and data.
|
|
||||||
PFC.PBCR.WORD = 0xaaaa;
|
|
||||||
PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x00aa;
|
|
||||||
kdelay();
|
|
||||||
PFC.PBCR.WORD = 0x5555;
|
|
||||||
PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x0055;
|
|
||||||
kdelay();
|
|
||||||
PB.DR.BYTE = 0x00;
|
|
||||||
PM.DR.BYTE &= 0xf0;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
keyboard_updateState()
|
|
||||||
Updates the keyboard state.
|
|
||||||
*/
|
|
||||||
void keyboard_updateState_7705(volatile unsigned char *keyboard_state)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for(i = 0; i < 10; i++) keyboard_state[i] = krow(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Interrupt handler.
|
|
||||||
//---
|
|
||||||
|
|
||||||
void gint_7705(void)
|
|
||||||
{
|
|
||||||
volatile unsigned int *intevt2 = (unsigned int *)0xa4000000;
|
|
||||||
unsigned int code = *intevt2;
|
|
||||||
|
|
||||||
switch(code)
|
|
||||||
{
|
|
||||||
case IC_RTC_PRI:
|
|
||||||
RTC.RCR2.BIT.PEF = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IC_TMU0_TUNI0:
|
|
||||||
timer_interrupt(TIMER_TMU0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IC_TMU1_TUNI1:
|
|
||||||
timer_interrupt(TIMER_TMU1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IC_TMU2_TUNI2:
|
|
||||||
timer_interrupt(TIMER_TMU2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Setup.
|
|
||||||
//---
|
|
||||||
|
|
||||||
static unsigned short iprs[8];
|
|
||||||
static unsigned char rcr2;
|
|
||||||
|
|
||||||
static void gint_priority_lock_7705(void)
|
|
||||||
{
|
|
||||||
// Saving the interrupt masks from registers IPRA to IPRH.
|
|
||||||
iprs[0] = INTC.IPRA.WORD;
|
|
||||||
iprs[1] = INTC.IPRB.WORD;
|
|
||||||
iprs[2] = INTX.IPRC.WORD;
|
|
||||||
iprs[3] = INTX.IPRD.WORD;
|
|
||||||
iprs[4] = INTX.IPRE.WORD;
|
|
||||||
iprs[5] = INTX.IPRF.WORD;
|
|
||||||
iprs[6] = INTX.IPRG.WORD;
|
|
||||||
iprs[7] = INTX.IPRH.WORD;
|
|
||||||
|
|
||||||
// Disabling everything by default to avoid receiving an interrupt that
|
|
||||||
// the handler doesn't handle, which would cause the user program to
|
|
||||||
// freeze.
|
|
||||||
INTC.IPRA.WORD = 0x0000;
|
|
||||||
INTC.IPRB.WORD = 0x0000;
|
|
||||||
INTX.IPRC.WORD = 0x0000;
|
|
||||||
INTX.IPRD.WORD = 0x0000;
|
|
||||||
INTX.IPRE.WORD = 0x0000;
|
|
||||||
INTX.IPRF.WORD = 0x0000;
|
|
||||||
INTX.IPRG.WORD = 0x0000;
|
|
||||||
INTX.IPRH.WORD = 0x0000;
|
|
||||||
|
|
||||||
// Allowing RTC, which handles keyboard.
|
|
||||||
INTC.IPRA.BIT._RTC = GINT_INTP_RTC;
|
|
||||||
INTC.IPRA.BIT._TMU0 = GINT_INTP_KEY;
|
|
||||||
INTC.IPRA.BIT._TMU1 = GINT_INTP_GRAY;
|
|
||||||
INTC.IPRA.BIT._TMU2 = GINT_INTP_TIMER;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gint_priority_unlock_7705(void)
|
|
||||||
{
|
|
||||||
// Restoring the saved states.
|
|
||||||
INTC.IPRA.WORD = iprs[0];
|
|
||||||
INTC.IPRB.WORD = iprs[1];
|
|
||||||
INTX.IPRC.WORD = iprs[2];
|
|
||||||
INTX.IPRD.WORD = iprs[3];
|
|
||||||
INTX.IPRE.WORD = iprs[4];
|
|
||||||
INTX.IPRF.WORD = iprs[5];
|
|
||||||
INTX.IPRG.WORD = iprs[6];
|
|
||||||
INTX.IPRH.WORD = iprs[7];
|
|
||||||
}
|
|
||||||
|
|
||||||
void gint_setup_7705(void)
|
|
||||||
{
|
|
||||||
gint_priority_lock_7705();
|
|
||||||
|
|
||||||
// Saving the RTC configuration.
|
|
||||||
rcr2 = RTC.RCR2.BYTE;
|
|
||||||
// Disabling RTC interrupts by default.
|
|
||||||
RTC.RCR2.BYTE = 0x09;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gint_stop_7705(void)
|
|
||||||
{
|
|
||||||
gint_priority_unlock_7705();
|
|
||||||
|
|
||||||
// Restoring the RTC configuration.
|
|
||||||
RTC.RCR2.BYTE = rcr2;
|
|
||||||
}
|
|
14
src/gray/gclear.c
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <gray.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
gclear()
|
||||||
|
Clears the video ram.
|
||||||
|
*/
|
||||||
|
void gclear(void)
|
||||||
|
{
|
||||||
|
int *v1 = gray_lightVRAM();
|
||||||
|
int *v2 = gray_darkVRAM();
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < 256; i++) v1[i] = v2[i] = 0;
|
||||||
|
}
|
15
src/gray/gclear_area.c
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#include <gray.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
gclear_area()
|
||||||
|
Clears an area of the video ram. End points (x1, y1) and (x2, y2) are
|
||||||
|
included.
|
||||||
|
*/
|
||||||
|
void gclear_area(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
display_useVRAM(gray_lightVRAM());
|
||||||
|
dclear_area(x1, y1, x2, y2);
|
||||||
|
display_useVRAM(gray_darkVRAM());
|
||||||
|
dclear_area(x1, y1, x2, y2);
|
||||||
|
}
|
20
src/gray/gline.c
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <gray.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
gline()
|
||||||
|
Draws a line in the vram. Automatically optimizes special cases.
|
||||||
|
*/
|
||||||
|
void gline(int x1, int y1, int x2, int y2, enum Color color)
|
||||||
|
{
|
||||||
|
enum Color c1, c2;
|
||||||
|
|
||||||
|
if(color == Color_None) return;
|
||||||
|
else if(color == Color_Invert) c1 = c2 = Color_Invert;
|
||||||
|
else c1 = color & 1, c2 = color >> 1;
|
||||||
|
|
||||||
|
display_useVRAM(gray_lightVRAM());
|
||||||
|
dline(x1, y1, x2, y2, c1);
|
||||||
|
display_useVRAM(gray_darkVRAM());
|
||||||
|
dline(x1, y1, x2, y2, c2);
|
||||||
|
}
|
47
src/gray/gpixel.c
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#include <gray.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
gpixel()
|
||||||
|
Puts a pixel in the vram.
|
||||||
|
*/
|
||||||
|
void gpixel(int x, int y, enum Color color)
|
||||||
|
{
|
||||||
|
if((unsigned int)x > 127 || (unsigned int)y > 63) return;
|
||||||
|
|
||||||
|
int offset = (y << 2) + (x >> 5);
|
||||||
|
int mask = 0x80000000 >> (x & 31);
|
||||||
|
|
||||||
|
int *v1 = gray_lightVRAM();
|
||||||
|
int *v2 = gray_lightVRAM();
|
||||||
|
|
||||||
|
switch(color)
|
||||||
|
{
|
||||||
|
case Color_White:
|
||||||
|
v1[offset] &= ~mask;
|
||||||
|
v2[offset] &= ~mask;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Light:
|
||||||
|
v1[offset] |= mask;
|
||||||
|
v2[offset] &= ~mask;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Dark:
|
||||||
|
v1[offset] &= ~mask;
|
||||||
|
v2[offset] |= mask;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Black:
|
||||||
|
v1[offset] |= mask;
|
||||||
|
v2[offset] |= mask;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Color_Invert:
|
||||||
|
v1[offset] ^= mask;
|
||||||
|
v2[offset] ^= mask;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,6 @@
|
||||||
//
|
//
|
||||||
//---
|
//---
|
||||||
|
|
||||||
#include <display.h>
|
|
||||||
#include <gray.h>
|
#include <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
|
@ -0,0 +1,15 @@
|
||||||
|
#include <gray.h>
|
||||||
|
#include <display.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
greverse_area()
|
||||||
|
Reverses an area of the vram. End points (x1, y1) and (x2, y2) are
|
||||||
|
included.
|
||||||
|
*/
|
||||||
|
void greverse_area(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
display_useVRAM(gray_lightVRAM());
|
||||||
|
dreverse_area(x1, y1, x2, y2);
|
||||||
|
display_useVRAM(gray_darkVRAM());
|
||||||
|
dreverse_area(x1, y1, x2, y2);
|
||||||
|
}
|
502
src/keyboard.c
|
@ -1,502 +0,0 @@
|
||||||
#include <keyboard.h>
|
|
||||||
#include <timer.h>
|
|
||||||
#include <mpu.h>
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Keyboard variables.
|
|
||||||
//---
|
|
||||||
|
|
||||||
// These ones get modified by interrupts.
|
|
||||||
static volatile unsigned char keyboard_state[10] = { 0 };
|
|
||||||
static volatile int interrupt_flag = 0;
|
|
||||||
|
|
||||||
// Key statistics.
|
|
||||||
static int repeat_first = 10, repeat_next = 2;
|
|
||||||
static int last_key = KEY_NONE, last_repeats = 0, last_events = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Auxiliary functions.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
sleep()
|
|
||||||
Puts the CPU to sleep and waits for an interrupt.
|
|
||||||
*/
|
|
||||||
static void sleep(void)
|
|
||||||
{
|
|
||||||
__asm__
|
|
||||||
(
|
|
||||||
"sleep\n\t"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
getPressedKey()
|
|
||||||
Finds a pressed key in the keyboard state and returns it.
|
|
||||||
|
|
||||||
@return A pressed key.
|
|
||||||
*/
|
|
||||||
static int getPressedKey(void)
|
|
||||||
{
|
|
||||||
int row = 1, column = 0;
|
|
||||||
int state;
|
|
||||||
if(keyboard_state[0] & 1) return KEY_AC_ON;
|
|
||||||
|
|
||||||
while(row <= 9 && !keyboard_state[row]) row++;
|
|
||||||
if(row > 9) return KEY_NONE;
|
|
||||||
|
|
||||||
state = keyboard_state[row];
|
|
||||||
while(!(state & 1))
|
|
||||||
{
|
|
||||||
state >>= 1;
|
|
||||||
column++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (column << 4) | row;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
getPressedKeys()
|
|
||||||
Find 'count' pressed keys in the keyboard state.
|
|
||||||
|
|
||||||
@arg keys Will be filled.
|
|
||||||
@arg count Size of array.
|
|
||||||
|
|
||||||
@return Number of actual pressed keys found.
|
|
||||||
*/
|
|
||||||
static int getPressedKeys(int *keys, int count)
|
|
||||||
{
|
|
||||||
int row = 1, column;
|
|
||||||
int found = 0, actually_pressed;
|
|
||||||
int state;
|
|
||||||
if(count <= 0) return 0;
|
|
||||||
|
|
||||||
if(keyboard_state[0] & 1)
|
|
||||||
{
|
|
||||||
keys[found++] = KEY_AC_ON;
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
while(count && row <= 9)
|
|
||||||
{
|
|
||||||
while(row <= 9 && !keyboard_state[row]) row++;
|
|
||||||
if(row > 9) break;
|
|
||||||
|
|
||||||
state = keyboard_state[row];
|
|
||||||
column = 0;
|
|
||||||
|
|
||||||
while(count && column < 8)
|
|
||||||
{
|
|
||||||
if(state & 1)
|
|
||||||
{
|
|
||||||
keys[found++] = (column << 4) | row;
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
state >>= 1;
|
|
||||||
column++;
|
|
||||||
}
|
|
||||||
|
|
||||||
row++;
|
|
||||||
}
|
|
||||||
|
|
||||||
actually_pressed = found;
|
|
||||||
|
|
||||||
while(count)
|
|
||||||
{
|
|
||||||
keys[found++] = KEY_NONE;
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return actually_pressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Interrupt management.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
keyboard_interrupt()
|
|
||||||
Callback for keyboard update. Allows keyboard analysis functions to
|
|
||||||
wake only when keyboard interrupts happen.
|
|
||||||
*/
|
|
||||||
void keyboard_interrupt(void)
|
|
||||||
{
|
|
||||||
if(isSH3())
|
|
||||||
keyboard_updateState_7705(keyboard_state);
|
|
||||||
else
|
|
||||||
keyboard_updateState_7305(keyboard_state);
|
|
||||||
|
|
||||||
interrupt_flag = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
keyboard_init()
|
|
||||||
Starts the keyboard timer.
|
|
||||||
*/
|
|
||||||
void keyboard_init(void)
|
|
||||||
{
|
|
||||||
timer_start(TIMER_KEYBOARD, 1700, TIMER_Po_256, keyboard_interrupt,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
keyboard_quit()
|
|
||||||
Stops the keyboard timer.
|
|
||||||
*/
|
|
||||||
void keyboard_quit(void)
|
|
||||||
{
|
|
||||||
timer_stop(TIMER_KEYBOARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Keyboard configuration.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
keyboard_setFrequency()
|
|
||||||
Sets the keyboard frequency.
|
|
||||||
|
|
||||||
@arg frequency In Hz.
|
|
||||||
*/
|
|
||||||
void keyboard_setFrequency(int frequency)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
keyboard_setRepeatRate()
|
|
||||||
Sets the default repeat rate for key events. The unit for the argument
|
|
||||||
is the keyboard period.
|
|
||||||
For example at 16 Hz, values of (10, 2) will imitate the system
|
|
||||||
default.
|
|
||||||
|
|
||||||
@arg first Delay before first repeat, in keyboard period units.
|
|
||||||
@arg next Delay before following repeats, in keyboard period
|
|
||||||
units.
|
|
||||||
*/
|
|
||||||
void keyboard_setRepeatRate(int first, int next)
|
|
||||||
{
|
|
||||||
if(first < 0) first = 0;
|
|
||||||
if(next < 0) next = 0;
|
|
||||||
|
|
||||||
repeat_first = first;
|
|
||||||
repeat_next = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Keyboard access.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
keylast()
|
|
||||||
Returns the matrix code of the last pressed key. If repeat_count is
|
|
||||||
non-NULL, it is set to the number of repetitions.
|
|
||||||
|
|
||||||
@arg repeat_count
|
|
||||||
|
|
||||||
@return Key matrix code.
|
|
||||||
*/
|
|
||||||
int keylast(int *repeat_count)
|
|
||||||
{
|
|
||||||
if(repeat_count) *repeat_count = last_repeats;
|
|
||||||
return last_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
keystate()
|
|
||||||
Returns the address of the keyboard state array. The returned address
|
|
||||||
is the handler's buffer, therefore it contains volatile data.
|
|
||||||
|
|
||||||
@return 10-byte keyboard state buffer.
|
|
||||||
*/
|
|
||||||
volatile unsigned char *keystate(void)
|
|
||||||
{
|
|
||||||
return keyboard_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
getkey()
|
|
||||||
Blocking function with auto-repeat and SHIFT modifying functionalities.
|
|
||||||
Roughly reproduces the behavior of the system's GetKey().
|
|
||||||
|
|
||||||
@return Pressed key matrix code.
|
|
||||||
*/
|
|
||||||
int getkey(void)
|
|
||||||
{
|
|
||||||
return getkey_opt(
|
|
||||||
Getkey_ShiftModifier |
|
|
||||||
Getkey_AlphaModifier |
|
|
||||||
|
|
||||||
Getkey_RepeatArrowKeys,
|
|
||||||
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
getkey_opt()
|
|
||||||
Enhances getkey() with most general functionalities.
|
|
||||||
If max_cycles is non-zero and positive, getkey_opt() will return
|
|
||||||
KEY_NOEVENT if no event occurs during max_cycle analysis.
|
|
||||||
|
|
||||||
@arg options OR-combination of GetkeyOpt values.
|
|
||||||
@arg max_cycles
|
|
||||||
|
|
||||||
@return Pressed key matrix code.
|
|
||||||
*/
|
|
||||||
int getkey_opt(enum GetkeyOpt options, int max_cycles)
|
|
||||||
{
|
|
||||||
int key;
|
|
||||||
enum KeyType type;
|
|
||||||
int modifier = 0, last_modifier = KEY_NONE;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if(!max_cycles) max_cycles = -1;
|
|
||||||
|
|
||||||
while(max_cycles != 0)
|
|
||||||
{
|
|
||||||
while(!interrupt_flag) sleep();
|
|
||||||
interrupt_flag = 0;
|
|
||||||
if(max_cycles > 0) max_cycles--;
|
|
||||||
|
|
||||||
// Getting key and adding modifiers.
|
|
||||||
key = getPressedKey();
|
|
||||||
|
|
||||||
// Handling "no_key" event;
|
|
||||||
if(key == KEY_NONE)
|
|
||||||
{
|
|
||||||
// Condition for returning.
|
|
||||||
r = (last_key != KEY_NONE &&
|
|
||||||
options & Getkey_ReleaseEvent);
|
|
||||||
|
|
||||||
last_key = KEY_NONE;
|
|
||||||
last_modifier = KEY_NONE;
|
|
||||||
last_repeats = 0;
|
|
||||||
last_events = 0;
|
|
||||||
|
|
||||||
if(r) return KEY_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling "new key" events.
|
|
||||||
else if(key != last_key)
|
|
||||||
{
|
|
||||||
if(options & Getkey_ShiftModifier && key == KEY_SHIFT)
|
|
||||||
{
|
|
||||||
if(last_modifier != KEY_SHIFT)
|
|
||||||
modifier ^= MOD_SHIFT;
|
|
||||||
last_modifier = KEY_SHIFT;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(options & Getkey_AlphaModifier && key == KEY_ALPHA)
|
|
||||||
{
|
|
||||||
if(last_modifier != KEY_ALPHA)
|
|
||||||
modifier ^= MOD_ALPHA;
|
|
||||||
last_modifier = KEY_ALPHA;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_key = key;
|
|
||||||
last_repeats = 0;
|
|
||||||
last_events = 0;
|
|
||||||
|
|
||||||
return key | modifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling key repetitions.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
type = keytype(key);
|
|
||||||
|
|
||||||
// Checking whether this key type is repeated.
|
|
||||||
if(options & (type << 4))
|
|
||||||
{
|
|
||||||
last_events++;
|
|
||||||
r = last_repeats ? repeat_next : repeat_first;
|
|
||||||
|
|
||||||
if(last_events >= r)
|
|
||||||
{
|
|
||||||
last_repeats++;
|
|
||||||
last_events = 0;
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When no key was pressed during the given delay...
|
|
||||||
return KEY_NOEVENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
multigetkey()
|
|
||||||
Listens the keyboard for simultaneous key hits. Uses the same options
|
|
||||||
as getkey_opt().
|
|
||||||
multigetkey() fills the 'keys' array with 'count' key codes, adding
|
|
||||||
KEY_NOKEY if less than 'count' keys are pressed.
|
|
||||||
Be aware that rectangle and column effects can make multigetkey() read
|
|
||||||
unpressed keys as pressed (see documentation for more information).
|
|
||||||
Setting count = 3 is generally safe.
|
|
||||||
|
|
||||||
@arg keys Key code array.
|
|
||||||
@arg count Maximum number of keys that will be read.
|
|
||||||
@arg max_cycles
|
|
||||||
*/
|
|
||||||
void multigetkey(int *keys, int count, int max_cycles)
|
|
||||||
{
|
|
||||||
int number;
|
|
||||||
|
|
||||||
if(!max_cycles) max_cycles = -1;
|
|
||||||
|
|
||||||
while(max_cycles != 0)
|
|
||||||
{
|
|
||||||
while(!interrupt_flag) sleep();
|
|
||||||
interrupt_flag = 0;
|
|
||||||
if(max_cycles > 0) max_cycles--;
|
|
||||||
|
|
||||||
number = getPressedKeys(keys, count);
|
|
||||||
|
|
||||||
// We need to update the last key data, in case multigetkey()
|
|
||||||
// returns a single key, and getkey() is called a short time
|
|
||||||
// after. Otherwise getkey() could re-send an event for this
|
|
||||||
// key.
|
|
||||||
if(number == 1)
|
|
||||||
{
|
|
||||||
last_key = keys[0];
|
|
||||||
last_repeats = 0;
|
|
||||||
last_events = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(number) return;
|
|
||||||
|
|
||||||
// Handle key repetitions.
|
|
||||||
/*
|
|
||||||
else
|
|
||||||
{
|
|
||||||
type = keytype(key);
|
|
||||||
|
|
||||||
// Checking whether this key type is repeated.
|
|
||||||
if(options & (type << 4))
|
|
||||||
{
|
|
||||||
last_events++;
|
|
||||||
r = last_repeats ? repeat_next : repeat_first;
|
|
||||||
|
|
||||||
if(last_events >= r)
|
|
||||||
{
|
|
||||||
last_repeats++;
|
|
||||||
last_events = 0;
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// When no key was pressed during the given delay... (no need to fill
|
|
||||||
// the array, it has already been done by getPressedKeys()).
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Key analysis.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
keyid()
|
|
||||||
Returns a non-matrix key code that can be used for array subscript.
|
|
||||||
Ignores modifiers.
|
|
||||||
|
|
||||||
@arg key
|
|
||||||
|
|
||||||
@return Modified keycode.
|
|
||||||
*/
|
|
||||||
int keyid(int key)
|
|
||||||
{
|
|
||||||
if(key < 0) return -1;
|
|
||||||
key &= MOD_CLEAR;
|
|
||||||
|
|
||||||
int row = 9 - (key & 0x0f);
|
|
||||||
int column = 6 - ((key & 0xf0) >> 4);
|
|
||||||
|
|
||||||
return 6 * row + column;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
keychar()
|
|
||||||
Returns the ASCII character associated with a key, or 0 for control
|
|
||||||
keys.
|
|
||||||
|
|
||||||
@arg key
|
|
||||||
|
|
||||||
@return Associated character.
|
|
||||||
*/
|
|
||||||
int keychar(int key)
|
|
||||||
{
|
|
||||||
char flat[] = {
|
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
||||||
0x0, '2', '^', 0x0, 0x0, 0x0,
|
|
||||||
'X', 'l', 'l', 's', 'c', 't',
|
|
||||||
0x0, 0x0, '(', ')', ',', '>',
|
|
||||||
'7', '8', '9', 0x0, 0x0, 0x0,
|
|
||||||
'4', '5', '6', '*', '/', 0x0,
|
|
||||||
'1', '2', '3', '+', '-', 0x0,
|
|
||||||
'0', '.', 'e', '-', 0x0, 0x0
|
|
||||||
};
|
|
||||||
char alpha[] = {
|
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
||||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
||||||
0x0, 'r', 'o', 0x0, 0x0, 0x0,
|
|
||||||
'A', 'B', 'C', 'D', 'E', 'F',
|
|
||||||
'G', 'H', 'I', 'J', 'K', 'L',
|
|
||||||
'M', 'N', 'O', 0x0, 0x0, 0x0,
|
|
||||||
'P', 'Q', 'R', 'S', 'T', 0x0,
|
|
||||||
'U', 'V', 'W', 'X', 'Y', 0x0,
|
|
||||||
'Z', ' ', '"', 0x0, 0x0, 0x0
|
|
||||||
};
|
|
||||||
|
|
||||||
int id = keyid(key);
|
|
||||||
|
|
||||||
if(key & MOD_ALPHA) return alpha[id];
|
|
||||||
return flat[id];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
keytype()
|
|
||||||
Returns a key's type. Ignores modifiers.
|
|
||||||
|
|
||||||
@arg key
|
|
||||||
|
|
||||||
@return Key type.
|
|
||||||
*/
|
|
||||||
enum KeyType keytype(int key)
|
|
||||||
{
|
|
||||||
key &= MOD_CLEAR;
|
|
||||||
|
|
||||||
if(key == KEY_UP || key == KEY_RIGHT || key == KEY_DOWN ||
|
|
||||||
key == KEY_LEFT) return KeyType_Arrow;
|
|
||||||
|
|
||||||
if((key & 0x0f) == 0x09) return KeyType_Function;
|
|
||||||
|
|
||||||
return keychar(key) ? KeyType_Character : KeyType_Control;
|
|
||||||
}
|
|
24
src/keyboard/getPressedKey.c
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include <keyboard_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
getPressedKey()
|
||||||
|
Finds a pressed key in the keyboard state and returns it.
|
||||||
|
*/
|
||||||
|
int getPressedKey(volatile unsigned char *keyboard_state)
|
||||||
|
{
|
||||||
|
int row = 1, column = 0;
|
||||||
|
int state;
|
||||||
|
if(keyboard_state[0] & 1) return KEY_AC_ON;
|
||||||
|
|
||||||
|
while(row <= 9 && !keyboard_state[row]) row++;
|
||||||
|
if(row > 9) return KEY_NONE;
|
||||||
|
|
||||||
|
state = keyboard_state[row];
|
||||||
|
while(!(state & 1))
|
||||||
|
{
|
||||||
|
state >>= 1;
|
||||||
|
column++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (column << 4) | row;
|
||||||
|
}
|
54
src/keyboard/getPressedKeys.c
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include <keyboard_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
getPressedKeys()
|
||||||
|
Find 'count' pressed keys in the keyboard state and fills the 'keys'
|
||||||
|
array. Returns the number of actually-pressed keys found.
|
||||||
|
*/
|
||||||
|
int getPressedKeys(volatile unsigned char *keyboard_state, int *keys,
|
||||||
|
int count)
|
||||||
|
{
|
||||||
|
int row = 1, column;
|
||||||
|
int found = 0, actually_pressed;
|
||||||
|
int state;
|
||||||
|
if(count <= 0) return 0;
|
||||||
|
|
||||||
|
if(keyboard_state[0] & 1)
|
||||||
|
{
|
||||||
|
keys[found++] = KEY_AC_ON;
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(count && row <= 9)
|
||||||
|
{
|
||||||
|
while(row <= 9 && !keyboard_state[row]) row++;
|
||||||
|
if(row > 9) break;
|
||||||
|
|
||||||
|
state = keyboard_state[row];
|
||||||
|
column = 0;
|
||||||
|
|
||||||
|
while(count && column < 8)
|
||||||
|
{
|
||||||
|
if(state & 1)
|
||||||
|
{
|
||||||
|
keys[found++] = (column << 4) | row;
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
state >>= 1;
|
||||||
|
column++;
|
||||||
|
}
|
||||||
|
|
||||||
|
row++;
|
||||||
|
}
|
||||||
|
|
||||||
|
actually_pressed = found;
|
||||||
|
|
||||||
|
while(count)
|
||||||
|
{
|
||||||
|
keys[found++] = KEY_NONE;
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actually_pressed;
|
||||||
|
}
|
113
src/keyboard/getkey.c
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <keyboard_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
getkey()
|
||||||
|
Blocking function with auto-repeat and SHIFT modifying functionalities.
|
||||||
|
Roughly reproduces the behavior of the system's GetKey().
|
||||||
|
*/
|
||||||
|
int getkey(void)
|
||||||
|
{
|
||||||
|
return getkey_opt(
|
||||||
|
Getkey_ShiftModifier |
|
||||||
|
Getkey_AlphaModifier |
|
||||||
|
|
||||||
|
Getkey_RepeatArrowKeys,
|
||||||
|
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
getkey_opt()
|
||||||
|
Enhances getkey() with most general functionalities.
|
||||||
|
If max_cycles is non-zero and positive, getkey_opt() will return
|
||||||
|
KEY_NOEVENT if no event occurs during max_cycle analysis.
|
||||||
|
*/
|
||||||
|
int getkey_opt(enum GetkeyOpt options, int max_cycles)
|
||||||
|
{
|
||||||
|
int key;
|
||||||
|
enum KeyType type;
|
||||||
|
int modifier = 0, last_modifier = KEY_NONE;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if(!max_cycles) max_cycles = -1;
|
||||||
|
|
||||||
|
while(max_cycles != 0)
|
||||||
|
{
|
||||||
|
while(!interrupt_flag) sleep();
|
||||||
|
interrupt_flag = 0;
|
||||||
|
if(max_cycles > 0) max_cycles--;
|
||||||
|
|
||||||
|
// Getting key and adding modifiers.
|
||||||
|
key = getPressedKey(keyboard_state);
|
||||||
|
|
||||||
|
// Handling "no_key" event;
|
||||||
|
if(key == KEY_NONE)
|
||||||
|
{
|
||||||
|
// Condition for returning.
|
||||||
|
r = (last_key != KEY_NONE &&
|
||||||
|
options & Getkey_ReleaseEvent);
|
||||||
|
|
||||||
|
last_key = KEY_NONE;
|
||||||
|
last_modifier = KEY_NONE;
|
||||||
|
last_repeats = 0;
|
||||||
|
last_events = 0;
|
||||||
|
|
||||||
|
if(r) return KEY_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handling "new key" events.
|
||||||
|
else if(key != last_key)
|
||||||
|
{
|
||||||
|
if(options & Getkey_ShiftModifier && key == KEY_SHIFT)
|
||||||
|
{
|
||||||
|
if(last_modifier != KEY_SHIFT)
|
||||||
|
modifier ^= MOD_SHIFT;
|
||||||
|
last_modifier = KEY_SHIFT;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(options & Getkey_AlphaModifier && key == KEY_ALPHA)
|
||||||
|
{
|
||||||
|
if(last_modifier != KEY_ALPHA)
|
||||||
|
modifier ^= MOD_ALPHA;
|
||||||
|
last_modifier = KEY_ALPHA;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_key = key;
|
||||||
|
last_repeats = 0;
|
||||||
|
last_events = 0;
|
||||||
|
|
||||||
|
return key | modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handling key repetitions.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type = keytype(key);
|
||||||
|
|
||||||
|
// Checking whether this key type is repeated.
|
||||||
|
if(options & (type << 4))
|
||||||
|
{
|
||||||
|
last_events++;
|
||||||
|
r = last_repeats ? repeat_next : repeat_first;
|
||||||
|
|
||||||
|
if(last_events >= r)
|
||||||
|
{
|
||||||
|
last_repeats++;
|
||||||
|
last_events = 0;
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When no key was pressed during the given delay...
|
||||||
|
return KEY_NOEVENT;
|
||||||
|
}
|
26
src/keyboard/keyboard_config.c
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <keyboard_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyboard_setFrequency()
|
||||||
|
Sets the keyboard frequency (in Hz).
|
||||||
|
*/
|
||||||
|
void keyboard_setFrequency(int frequency)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyboard_setRepeatRate()
|
||||||
|
Sets the default repeat rate for key events. The unit for the argument
|
||||||
|
is the keyboard period.
|
||||||
|
For example at 32 Hz, values of (20, 4) will imitate the system
|
||||||
|
default.
|
||||||
|
*/
|
||||||
|
void keyboard_setRepeatRate(int first, int next)
|
||||||
|
{
|
||||||
|
if(first < 0) first = 0;
|
||||||
|
if(next < 0) next = 0;
|
||||||
|
|
||||||
|
repeat_first = first;
|
||||||
|
repeat_next = next;
|
||||||
|
}
|
34
src/keyboard/keyboard_internals.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef _KEYBOARD_INTERNALS_H
|
||||||
|
#define _KEYBOARD_INTERNALS_H 1
|
||||||
|
|
||||||
|
#include <keyboard.h>
|
||||||
|
|
||||||
|
// Keyboard variables.
|
||||||
|
extern volatile unsigned char keyboard_state[10];
|
||||||
|
extern volatile int interrupt_flag = 0;
|
||||||
|
|
||||||
|
// Key statistics.
|
||||||
|
extern int repeat_first, repeat_next;
|
||||||
|
extern int last_key, last_repeats, last_events;
|
||||||
|
|
||||||
|
/*
|
||||||
|
sleep()
|
||||||
|
Puts the CPU into sleep until an interrupt request is accepted.
|
||||||
|
*/
|
||||||
|
void sleep(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
getPressedKey()
|
||||||
|
Finds a pressed key in the keyboard state and returns it.
|
||||||
|
*/
|
||||||
|
int getPressedKey(volatile unsigned char *keyboard_state);
|
||||||
|
|
||||||
|
/*
|
||||||
|
getPressedKeys()
|
||||||
|
Find 'count' pressed keys in the keyboard state and fills the 'keys'
|
||||||
|
array. Returns the number of actually-pressed keys found.
|
||||||
|
*/
|
||||||
|
int getPressedKeys(volatile unsigned char *keyboard_state, int *keys,
|
||||||
|
int count);
|
||||||
|
|
||||||
|
#endif // _KEYBOARD_INTERNALS_H
|
56
src/keyboard/keyboard_interrupt.c
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <timer.h>
|
||||||
|
#include <mpu.h>
|
||||||
|
|
||||||
|
#include <keyboard_internals.h>
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Keyboard variables.
|
||||||
|
//---
|
||||||
|
|
||||||
|
// These ones get modified by interrupts.
|
||||||
|
volatile unsigned char keyboard_state[10] = { 0 };
|
||||||
|
volatile int interrupt_flag = 0;
|
||||||
|
|
||||||
|
// Key statistics.
|
||||||
|
int repeat_first = 10, repeat_next = 2;
|
||||||
|
int last_key = KEY_NONE, last_repeats = 0, last_events = 0;
|
||||||
|
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Interrupt management.
|
||||||
|
//---
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyboard_interrupt()
|
||||||
|
Callback for keyboard update. Allows keyboard analysis functions to
|
||||||
|
wake only when keyboard interrupts happen.
|
||||||
|
*/
|
||||||
|
void keyboard_interrupt(void)
|
||||||
|
{
|
||||||
|
if(isSH3())
|
||||||
|
keyboard_updateState_7705(keyboard_state);
|
||||||
|
else
|
||||||
|
keyboard_updateState_7305(keyboard_state);
|
||||||
|
|
||||||
|
interrupt_flag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyboard_init()
|
||||||
|
Starts the keyboard timer.
|
||||||
|
*/
|
||||||
|
void keyboard_init(void)
|
||||||
|
{
|
||||||
|
timer_start(TIMER_KEYBOARD, 1700, TIMER_Po_256, keyboard_interrupt,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyboard_quit()
|
||||||
|
Stops the keyboard timer.
|
||||||
|
*/
|
||||||
|
void keyboard_quit(void)
|
||||||
|
{
|
||||||
|
timer_stop(TIMER_KEYBOARD);
|
||||||
|
}
|
25
src/keyboard/keyboard_misc.c
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <keyboard_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
keylast()
|
||||||
|
Returns the matrix code of the last pressed key. If repeat_count is
|
||||||
|
non-NULL, it is set to the number of repetitions.
|
||||||
|
*/
|
||||||
|
int keylast(int *repeat_count)
|
||||||
|
{
|
||||||
|
if(repeat_count) *repeat_count = last_repeats;
|
||||||
|
return last_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
keystate()
|
||||||
|
Returns the address of the keyboard state array. The returned address
|
||||||
|
is the handler's buffer, therefore it contains volatile data.
|
||||||
|
*/
|
||||||
|
volatile unsigned char *keystate(void)
|
||||||
|
{
|
||||||
|
return keyboard_state;
|
||||||
|
}
|
146
src/keyboard/keyboard_sh7305.c
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint core module: keyboard analyzer
|
||||||
|
//
|
||||||
|
// Probably the most difficult hardware interaction. There is very few
|
||||||
|
// documentation on how the system actually analyzes the keyboard. While
|
||||||
|
// disassembling syscalls reveals the following procedure (which was
|
||||||
|
// already documented by SimonLothar), there is nothing about the
|
||||||
|
// detection problems of the multi-getkey system.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <7305.h>
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Keyboard management.
|
||||||
|
//---
|
||||||
|
|
||||||
|
/*
|
||||||
|
kdelay()
|
||||||
|
Should sleep during a few milliseconds. Well...
|
||||||
|
This delay has a great influence on key detection. Here are a few
|
||||||
|
observations of what happens with common numbers of "nop" (without
|
||||||
|
overclock). Take care of the effect of overclock !
|
||||||
|
|
||||||
|
Column effects
|
||||||
|
May have something to do with register HIZCRB not being used here. When
|
||||||
|
many keys on the same column are pressed, other keys of the same column
|
||||||
|
may be triggered.
|
||||||
|
|
||||||
|
- Less Bad key detection.
|
||||||
|
- 8 Very few column effects. Most often, three keys may be pressed
|
||||||
|
simultaneously. However, [UP] has latencies and is globally not
|
||||||
|
detected.
|
||||||
|
- 12 Seems best. Every individual key is detected well. No column
|
||||||
|
effect observed before four keys.
|
||||||
|
- 16 Every single key is detected correctly. Pressing two keys on a
|
||||||
|
same column does not usually create a column effect. Three keys
|
||||||
|
almost always.
|
||||||
|
- More Does not improve single key detection, and increase column
|
||||||
|
effects. At 256 every single key press create a whole column
|
||||||
|
effect.
|
||||||
|
*/
|
||||||
|
static void kdelay(void)
|
||||||
|
{
|
||||||
|
#define r4(str) str str str str
|
||||||
|
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
r4("nop\n\t")
|
||||||
|
r4("nop\n\t")
|
||||||
|
r4("nop\n\t")
|
||||||
|
);
|
||||||
|
|
||||||
|
#undef r4
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
krow()
|
||||||
|
Reads a keyboard row. Works like krow() for SH7705. See gint_7705.c for
|
||||||
|
more details.
|
||||||
|
*/
|
||||||
|
static int krow(int row)
|
||||||
|
{
|
||||||
|
volatile unsigned short *injector1 = (unsigned short *)0xa4050116;
|
||||||
|
volatile unsigned char *data1 = (unsigned char *)0xa4050136;
|
||||||
|
|
||||||
|
volatile unsigned short *injector2 = (unsigned short *)0xa4050118;
|
||||||
|
volatile unsigned char *data2 = (unsigned char *)0xa4050138;
|
||||||
|
|
||||||
|
volatile unsigned short *detector = (unsigned short *)0xa405014c;
|
||||||
|
volatile unsigned char *keys = (unsigned char *)0xa405016c;
|
||||||
|
|
||||||
|
volatile unsigned char *key_register = (unsigned char *)0xa40501c6;
|
||||||
|
// volatile unsigned short *hizcrb = (unsigned short *)0xa405015a;
|
||||||
|
|
||||||
|
unsigned short smask;
|
||||||
|
unsigned char cmask;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
if(row < 0 || row > 9) return 0;
|
||||||
|
|
||||||
|
// Additional configuration for SH7305.
|
||||||
|
*detector = 0xaaaa;
|
||||||
|
*key_register = 0xff;
|
||||||
|
*injector1 = (*injector1 & 0xf000) | 0x0555;
|
||||||
|
*injector2 = (*injector2 & 0xf000) | 0x0555;
|
||||||
|
*data1 |= 0x3f;
|
||||||
|
*data2 |= 0x3f;
|
||||||
|
kdelay();
|
||||||
|
|
||||||
|
if(row < 6)
|
||||||
|
{
|
||||||
|
smask = 0x0003 << (row * 2);
|
||||||
|
cmask = ~(1 << row);
|
||||||
|
|
||||||
|
*injector1 = ((*injector1 & 0xf000) | 0x0aaa) ^ smask;
|
||||||
|
*injector2 = (*injector2 & 0xf000) | 0x0aaa;
|
||||||
|
kdelay();
|
||||||
|
|
||||||
|
*data1 = (*data1 & 0xc0) | cmask;
|
||||||
|
*data2 |= 0x3f;
|
||||||
|
kdelay();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
smask = 0x0003 << ((row - 6) * 2);
|
||||||
|
cmask = ~(1 << (row - 6));
|
||||||
|
|
||||||
|
*injector1 = (*injector1 & 0xf000) | 0x0aaa;
|
||||||
|
*injector2 = ((*injector2 & 0xf000) | 0x0aaa) ^ smask;
|
||||||
|
kdelay();
|
||||||
|
|
||||||
|
*data1 |= 0x3f;
|
||||||
|
*data2 = (*data2 & 0xc0) | cmask;
|
||||||
|
kdelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading the keyboard row.
|
||||||
|
result = ~*keys;
|
||||||
|
kdelay();
|
||||||
|
|
||||||
|
// Re-initializing the port configuration and data.
|
||||||
|
*injector1 = (*injector1 & 0xf000) | 0x0aaa;
|
||||||
|
*injector2 = (*injector2 & 0xf000) | 0x0aaa;
|
||||||
|
kdelay();
|
||||||
|
*injector1 = (*injector1 & 0xf000) | 0x0555;
|
||||||
|
*injector2 = (*injector2 & 0xf000) | 0x0555;
|
||||||
|
kdelay();
|
||||||
|
*data1 &= 0xc0;
|
||||||
|
*data2 &= 0xc0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyboard_updateState()
|
||||||
|
Updates the keyboard state.
|
||||||
|
*/
|
||||||
|
void keyboard_updateState_7305(volatile unsigned char *keyboard_state)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < 10; i++) keyboard_state[i] = krow(i);
|
||||||
|
}
|
139
src/keyboard/keyboard_sh7705.c
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint core module: keyboard analyzer
|
||||||
|
//
|
||||||
|
// Probably the most difficult hardware interaction. There is very few
|
||||||
|
// documentation on how the system actually analyzes the keyboard. While
|
||||||
|
// disassembling syscalls reveals the following procedure (which was
|
||||||
|
// already documented by SimonLothar), there is nothing about the
|
||||||
|
// detection problems of the multi-getkey system.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <7705.h>
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Keyboard management.
|
||||||
|
//---
|
||||||
|
|
||||||
|
/*
|
||||||
|
kdelay()
|
||||||
|
Used to be a low-level sleep using the watchdog, as in the system. This
|
||||||
|
way seems OK at least, and it doesn't create column effects as for
|
||||||
|
SH7305.
|
||||||
|
*/
|
||||||
|
static void kdelay(void)
|
||||||
|
{
|
||||||
|
#define r4(str) str str str str
|
||||||
|
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
r4("nop\n\t")
|
||||||
|
r4("nop\n\t")
|
||||||
|
r4("nop\n\t")
|
||||||
|
);
|
||||||
|
|
||||||
|
#undef r4
|
||||||
|
|
||||||
|
/* Watchdog delay version.
|
||||||
|
const int delay = 0xf4;
|
||||||
|
|
||||||
|
// Disabling the watchdog timer interrupt and resetting the
|
||||||
|
// configuration. Setting the delay.
|
||||||
|
INTC.IPRB.BIT._WDT = 0;
|
||||||
|
WDT.WTCSR.WRITE = 0xa500;
|
||||||
|
WDT.WTCNT.WRITE = 0x5a00 | (delay & 0xff);
|
||||||
|
|
||||||
|
// Counting on Po/256.
|
||||||
|
WDT.WTCSR.WRITE = 0xa505;
|
||||||
|
// Starting the timer (sets again to Po/256).
|
||||||
|
WDT.WTCSR.WRITE = 0xa585;
|
||||||
|
|
||||||
|
// Waiting until it overflows (delaying), then clearing the overflow
|
||||||
|
// flag.
|
||||||
|
while((WDT.WTCSR.READ.BYTE & 0x08) == 0);
|
||||||
|
WDT.WTCSR.WRITE = 0xa500 | (WDT.WTCSR.READ.BYTE & 0xf7);
|
||||||
|
|
||||||
|
// Resetting the configuration and the counter.
|
||||||
|
WDT.WTCSR.WRITE = 0xa500;
|
||||||
|
WDT.WTCSR.WRITE = 0x5a00;
|
||||||
|
|
||||||
|
// Enabling back the watchdog timer interrupt.
|
||||||
|
INTC.IPRB.BIT._WDT = GINT_INTP_WDT;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
krow()
|
||||||
|
Reads a keyboard row.
|
||||||
|
*/
|
||||||
|
static int krow(int row)
|
||||||
|
{
|
||||||
|
// '11' on the active row, '00' everywhere else.
|
||||||
|
unsigned short smask = 0x0003 << ((row % 8) * 2);
|
||||||
|
// '0' on the active row, '1' everywhere else.
|
||||||
|
unsigned char cmask = ~(1 << (row % 8));
|
||||||
|
// Line results.
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
if(row < 0 || row > 9) return 0;
|
||||||
|
|
||||||
|
// Initial configuration.
|
||||||
|
PFC.PBCR.WORD = 0xaaaa;
|
||||||
|
PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x0055;
|
||||||
|
kdelay();
|
||||||
|
|
||||||
|
if(row < 8)
|
||||||
|
{
|
||||||
|
// Configuring port B/M as input except for the row to check,
|
||||||
|
// which has to be an output. This sets '01' (output) on the
|
||||||
|
// active row, '10' (input) everywhere else.
|
||||||
|
PFC.PBCR.WORD = 0xaaaa ^ smask;
|
||||||
|
PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x00aa;
|
||||||
|
kdelay();
|
||||||
|
|
||||||
|
// Every bit set to 1 except the active row bit.
|
||||||
|
PB.DR.BYTE = cmask;
|
||||||
|
PM.DR.BYTE = (PM.DR.BYTE & 0xf0) | 0x0f;
|
||||||
|
kdelay();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The same, but deals with port M.
|
||||||
|
PFC.PBCR.WORD = 0xaaaa;
|
||||||
|
PFC.PMCR.WORD = ((PFC.PMCR.WORD & 0xff00) | 0x00aa) ^ smask;
|
||||||
|
kdelay();
|
||||||
|
|
||||||
|
PB.DR.BYTE = 0xff;
|
||||||
|
PM.DR.BYTE = (PM.DR.BYTE & 0xf0) | cmask;
|
||||||
|
kdelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading the keyboard row.
|
||||||
|
result = ~PA.DR.BYTE;
|
||||||
|
kdelay();
|
||||||
|
|
||||||
|
// Re-initializing the port configuration and data.
|
||||||
|
PFC.PBCR.WORD = 0xaaaa;
|
||||||
|
PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x00aa;
|
||||||
|
kdelay();
|
||||||
|
PFC.PBCR.WORD = 0x5555;
|
||||||
|
PFC.PMCR.WORD = (PFC.PMCR.WORD & 0xff00) | 0x0055;
|
||||||
|
kdelay();
|
||||||
|
PB.DR.BYTE = 0x00;
|
||||||
|
PM.DR.BYTE &= 0xf0;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyboard_updateState()
|
||||||
|
Updates the keyboard state.
|
||||||
|
*/
|
||||||
|
void keyboard_updateState_7705(volatile unsigned char *keyboard_state)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < 10; i++) keyboard_state[i] = krow(i);
|
||||||
|
}
|
36
src/keyboard/keychar.c
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#include <keyboard.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
keychar()
|
||||||
|
Returns the ASCII character associated with a key, or 0 for control
|
||||||
|
keys.
|
||||||
|
*/
|
||||||
|
int keychar(int key)
|
||||||
|
{
|
||||||
|
char flat[] = {
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, '2', '^', 0x0, 0x0, 0x0,
|
||||||
|
'X', 'L', 'l', 's', 'c', 't',
|
||||||
|
0x0, 0x0, '(', ')', ',', '>',
|
||||||
|
'7', '8', '9', 0x0, 0x0, 0x0,
|
||||||
|
'4', '5', '6', '*', '/', 0x0,
|
||||||
|
'1', '2', '3', '+', '-', 0x0,
|
||||||
|
'0', '.', 'e', '_', 0x0, 0x0
|
||||||
|
};
|
||||||
|
char alpha[] = {
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 'r', 'o', 0x0, 0x0, 0x0,
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F',
|
||||||
|
'G', 'H', 'I', 'J', 'K', 'L',
|
||||||
|
'M', 'N', 'O', 0x0, 0x0, 0x0,
|
||||||
|
'P', 'Q', 'R', 'S', 'T', 0x0,
|
||||||
|
'U', 'V', 'W', 'X', 'Y', 0x0,
|
||||||
|
'Z', ' ', '"', 0x0, 0x0, 0x0
|
||||||
|
};
|
||||||
|
|
||||||
|
int id = keyid(key);
|
||||||
|
|
||||||
|
return (key & MOD_ALPHA) ? alpha[id] : flat[id];
|
||||||
|
}
|
17
src/keyboard/keyid.c
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include <keyboard.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
keyid()
|
||||||
|
Returns a non-matrix key code that can be used for array subscript.
|
||||||
|
Ignores modifiers.
|
||||||
|
*/
|
||||||
|
int keyid(int key)
|
||||||
|
{
|
||||||
|
if(key < 0) return -1;
|
||||||
|
key &= MOD_CLEAR;
|
||||||
|
|
||||||
|
int row = 9 - (key & 0x0f);
|
||||||
|
int column = 6 - ((key & 0xf0) >> 4);
|
||||||
|
|
||||||
|
return 6 * row + column;
|
||||||
|
}
|
17
src/keyboard/keytype.c
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include <keyboard.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
keytype()
|
||||||
|
Returns a key's type. Ignores modifiers.
|
||||||
|
*/
|
||||||
|
enum KeyType keytype(int key)
|
||||||
|
{
|
||||||
|
key &= MOD_CLEAR;
|
||||||
|
|
||||||
|
if(key == KEY_UP || key == KEY_RIGHT || key == KEY_DOWN ||
|
||||||
|
key == KEY_LEFT) return KeyType_Arrow;
|
||||||
|
|
||||||
|
if((key & 0x0f) == 0x09) return KeyType_Function;
|
||||||
|
|
||||||
|
return keychar(key) ? KeyType_Character : KeyType_Control;
|
||||||
|
}
|
46
src/keyboard/multigetkey.c
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <keyboard_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
multigetkey()
|
||||||
|
Listens the keyboard for simultaneous key hits. Uses the same options
|
||||||
|
as getkey_opt().
|
||||||
|
multigetkey() fills the 'keys' array with 'count' key codes, adding
|
||||||
|
KEY_NOKEY if less than 'count' keys are pressed.
|
||||||
|
Be aware that rectangle and column effects can make multigetkey() read
|
||||||
|
unpressed keys as pressed (see documentation for more information).
|
||||||
|
Setting count = 3 is generally safe.
|
||||||
|
The function returns after 'max_cycles' if no key is pressed.
|
||||||
|
*/
|
||||||
|
void multigetkey(int *keys, int count, int max_cycles)
|
||||||
|
{
|
||||||
|
int number;
|
||||||
|
|
||||||
|
if(!max_cycles) max_cycles = -1;
|
||||||
|
|
||||||
|
while(max_cycles != 0)
|
||||||
|
{
|
||||||
|
while(!interrupt_flag) sleep();
|
||||||
|
interrupt_flag = 0;
|
||||||
|
if(max_cycles > 0) max_cycles--;
|
||||||
|
|
||||||
|
number = getPressedKeys(keyboard_state, keys, count);
|
||||||
|
|
||||||
|
// We need to update the last key data, in case multigetkey()
|
||||||
|
// returns a single key, and getkey() is called a short time
|
||||||
|
// after. Otherwise getkey() could re-send an event for this
|
||||||
|
// key.
|
||||||
|
if(number == 1)
|
||||||
|
{
|
||||||
|
last_key = keys[0];
|
||||||
|
last_repeats = 0;
|
||||||
|
last_events = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(number) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When no key was pressed during the given delay... (no need to fill
|
||||||
|
// the array, it has already been done by getPressedKeys()).
|
||||||
|
return;
|
||||||
|
}
|
13
src/keyboard/sleep.c
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <keyboard_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
sleep()
|
||||||
|
Puts the CPU to sleep and waits for an interrupt.
|
||||||
|
*/
|
||||||
|
void sleep(void)
|
||||||
|
{
|
||||||
|
__asm__
|
||||||
|
(
|
||||||
|
"sleep\n\t"
|
||||||
|
);
|
||||||
|
}
|
159
src/mpu/gint_sh7305.c
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint core module: sh7305 interrupt handler
|
||||||
|
//
|
||||||
|
// Of course all the work related to interrupts is heavily platform-
|
||||||
|
// dependent. This module handles interrupts and configures the MPU to
|
||||||
|
// save and restore the system's configuration when execution ends.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <gint.h>
|
||||||
|
#include <timer.h>
|
||||||
|
#include <7305.h>
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Interrupt codes.
|
||||||
|
//---
|
||||||
|
|
||||||
|
#define IC_RTC_PRI 0xaa0
|
||||||
|
#define IC_KEYSC 0xbe0
|
||||||
|
#define IC_TMU0_TUNI0 0x400
|
||||||
|
#define IC_TMU0_TUNI1 0x420
|
||||||
|
#define IC_TMU0_TUNI2 0x440
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Interrupt handler.
|
||||||
|
//---
|
||||||
|
|
||||||
|
void gint_7305(void)
|
||||||
|
{
|
||||||
|
volatile unsigned int *intevt = (unsigned int *)0xff000028;
|
||||||
|
unsigned int code = *intevt;
|
||||||
|
|
||||||
|
switch(code)
|
||||||
|
{
|
||||||
|
case IC_RTC_PRI:
|
||||||
|
RTC.RCR2.PEF = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IC_TMU0_TUNI0:
|
||||||
|
timer_interrupt(TIMER_TMU0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IC_TMU0_TUNI1:
|
||||||
|
timer_interrupt(TIMER_TMU1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IC_TMU0_TUNI2:
|
||||||
|
timer_interrupt(TIMER_TMU2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Setup.
|
||||||
|
//---
|
||||||
|
|
||||||
|
static unsigned short ipr[12];
|
||||||
|
static unsigned char rcr2;
|
||||||
|
// Saves of the keyboard registers. Could be better.
|
||||||
|
static unsigned short inj1, inj2, det;
|
||||||
|
static unsigned char data1, data2, keys, reg;
|
||||||
|
|
||||||
|
static void gint_priority_lock_7305(void)
|
||||||
|
{
|
||||||
|
// Saving the current interrupt priorities.
|
||||||
|
ipr[0] = INTX.IPRA.WORD;
|
||||||
|
ipr[1] = INTX.IPRB.WORD;
|
||||||
|
ipr[2] = INTX.IPRC.WORD;
|
||||||
|
ipr[3] = INTX.IPRD.WORD;
|
||||||
|
ipr[4] = INTX.IPRE.WORD;
|
||||||
|
ipr[5] = INTX.IPRF.WORD;
|
||||||
|
ipr[6] = INTX.IPRG.WORD;
|
||||||
|
ipr[7] = INTX.IPRH.WORD;
|
||||||
|
ipr[8] = INTX.IPRI.WORD;
|
||||||
|
ipr[9] = INTX.IPRJ.WORD;
|
||||||
|
ipr[10] = INTX.IPRK.WORD;
|
||||||
|
ipr[11] = INTX.IPRL.WORD;
|
||||||
|
|
||||||
|
// Disabling everything by default to avoid freezing on non-handled
|
||||||
|
// interrupts.
|
||||||
|
INTX.IPRA.WORD = 0x0000;
|
||||||
|
INTX.IPRB.WORD = 0x0000;
|
||||||
|
INTX.IPRC.WORD = 0x0000;
|
||||||
|
INTX.IPRD.WORD = 0x0000;
|
||||||
|
INTX.IPRE.WORD = 0x0000;
|
||||||
|
INTX.IPRF.WORD = 0x0000;
|
||||||
|
INTX.IPRG.WORD = 0x0000;
|
||||||
|
INTX.IPRH.WORD = 0x0000;
|
||||||
|
INTX.IPRI.WORD = 0x0000;
|
||||||
|
INTX.IPRJ.WORD = 0x0000;
|
||||||
|
INTX.IPRK.WORD = 0x0000;
|
||||||
|
INTX.IPRL.WORD = 0x0000;
|
||||||
|
|
||||||
|
// Saving keyboard registers.
|
||||||
|
inj1 = *((volatile unsigned short *)0xa4050116);
|
||||||
|
data1 = *((volatile unsigned char *)0xa4050136);
|
||||||
|
inj2 = *((volatile unsigned short *)0xa4050118);
|
||||||
|
data2 = *((volatile unsigned char *)0xa4050138);
|
||||||
|
det = *((volatile unsigned short *)0xa405014c);
|
||||||
|
keys = *((volatile unsigned char *)0xa405016c);
|
||||||
|
reg = *((volatile unsigned char *)0xa40501c6);
|
||||||
|
|
||||||
|
// Allowing RTC. Keyboard analysis is done regularly using a RTC
|
||||||
|
// because SH7305's special KEYSC interface does not allow us to clear
|
||||||
|
// the keyboard interrupt flags.
|
||||||
|
INTX.IPRK._RTC = GINT_INTP_RTC;
|
||||||
|
INTX.IPRA.TMU0_0 = GINT_INTP_KEY;
|
||||||
|
INTX.IPRA.TMU0_1 = GINT_INTP_GRAY;
|
||||||
|
INTX.IPRA.TMU0_2 = GINT_INTP_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gint_priority_unlock_7305(void)
|
||||||
|
{
|
||||||
|
// Restoring the interrupt priorities.
|
||||||
|
INTX.IPRA.WORD = ipr[0];
|
||||||
|
INTX.IPRB.WORD = ipr[1];
|
||||||
|
INTX.IPRC.WORD = ipr[2];
|
||||||
|
INTX.IPRD.WORD = ipr[3];
|
||||||
|
INTX.IPRE.WORD = ipr[4];
|
||||||
|
INTX.IPRF.WORD = ipr[5];
|
||||||
|
INTX.IPRG.WORD = ipr[6];
|
||||||
|
INTX.IPRH.WORD = ipr[7];
|
||||||
|
INTX.IPRI.WORD = ipr[8];
|
||||||
|
INTX.IPRJ.WORD = ipr[9];
|
||||||
|
INTX.IPRK.WORD = ipr[10];
|
||||||
|
INTX.IPRL.WORD = ipr[11];
|
||||||
|
|
||||||
|
// Restoring keyboard registers.
|
||||||
|
*((volatile unsigned short *)0xa4050116) = inj1;
|
||||||
|
*((volatile unsigned char *)0xa4050136) = data1;
|
||||||
|
*((volatile unsigned short *)0xa4050118) = inj2;
|
||||||
|
*((volatile unsigned char *)0xa4050138) = data2;
|
||||||
|
*((volatile unsigned short *)0xa405014c) = det;
|
||||||
|
*((volatile unsigned char *)0xa405016c) = keys;
|
||||||
|
*((volatile unsigned char *)0xa40501c6) = reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gint_setup_7305(void)
|
||||||
|
{
|
||||||
|
gint_priority_lock_7305();
|
||||||
|
|
||||||
|
// Saving the RTC configuration.
|
||||||
|
rcr2 = RTC.RCR2.BYTE;
|
||||||
|
// Disabling RTC interrupts by default.
|
||||||
|
RTC.RCR2.BYTE = 0x09;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gint_stop_7305(void)
|
||||||
|
{
|
||||||
|
gint_priority_unlock_7305();
|
||||||
|
|
||||||
|
// Restoring the RTC configuration.
|
||||||
|
RTC.RCR2.BYTE = rcr2;
|
||||||
|
}
|
125
src/mpu/gint_sh7705.c
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint core module: sh7705 interrupt handler
|
||||||
|
//
|
||||||
|
// Of course all the work related to interrupts is heavily platform-
|
||||||
|
// dependent. This module handles interrupts and configures the MPU to
|
||||||
|
// save and restore the system's configuration when execution ends.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <gint.h>
|
||||||
|
#include <timer.h>
|
||||||
|
#include <7705.h>
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Interrupt codes.
|
||||||
|
//---
|
||||||
|
|
||||||
|
#define IC_RTC_PRI 0x4a0
|
||||||
|
#define IC_PINT07 0x700
|
||||||
|
#define IC_TMU0_TUNI0 0x400
|
||||||
|
#define IC_TMU1_TUNI1 0x420
|
||||||
|
#define IC_TMU2_TUNI2 0x440
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Interrupt handler.
|
||||||
|
//---
|
||||||
|
|
||||||
|
void gint_7705(void)
|
||||||
|
{
|
||||||
|
volatile unsigned int *intevt2 = (unsigned int *)0xa4000000;
|
||||||
|
unsigned int code = *intevt2;
|
||||||
|
|
||||||
|
switch(code)
|
||||||
|
{
|
||||||
|
case IC_RTC_PRI:
|
||||||
|
RTC.RCR2.BIT.PEF = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IC_TMU0_TUNI0:
|
||||||
|
timer_interrupt(TIMER_TMU0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IC_TMU1_TUNI1:
|
||||||
|
timer_interrupt(TIMER_TMU1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IC_TMU2_TUNI2:
|
||||||
|
timer_interrupt(TIMER_TMU2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Setup.
|
||||||
|
//---
|
||||||
|
|
||||||
|
static unsigned short iprs[8];
|
||||||
|
static unsigned char rcr2;
|
||||||
|
|
||||||
|
static void gint_priority_lock_7705(void)
|
||||||
|
{
|
||||||
|
// Saving the interrupt masks from registers IPRA to IPRH.
|
||||||
|
iprs[0] = INTC.IPRA.WORD;
|
||||||
|
iprs[1] = INTC.IPRB.WORD;
|
||||||
|
iprs[2] = INTX.IPRC.WORD;
|
||||||
|
iprs[3] = INTX.IPRD.WORD;
|
||||||
|
iprs[4] = INTX.IPRE.WORD;
|
||||||
|
iprs[5] = INTX.IPRF.WORD;
|
||||||
|
iprs[6] = INTX.IPRG.WORD;
|
||||||
|
iprs[7] = INTX.IPRH.WORD;
|
||||||
|
|
||||||
|
// Disabling everything by default to avoid receiving an interrupt that
|
||||||
|
// the handler doesn't handle, which would cause the user program to
|
||||||
|
// freeze.
|
||||||
|
INTC.IPRA.WORD = 0x0000;
|
||||||
|
INTC.IPRB.WORD = 0x0000;
|
||||||
|
INTX.IPRC.WORD = 0x0000;
|
||||||
|
INTX.IPRD.WORD = 0x0000;
|
||||||
|
INTX.IPRE.WORD = 0x0000;
|
||||||
|
INTX.IPRF.WORD = 0x0000;
|
||||||
|
INTX.IPRG.WORD = 0x0000;
|
||||||
|
INTX.IPRH.WORD = 0x0000;
|
||||||
|
|
||||||
|
// Allowing RTC, which handles keyboard.
|
||||||
|
INTC.IPRA.BIT._RTC = GINT_INTP_RTC;
|
||||||
|
INTC.IPRA.BIT._TMU0 = GINT_INTP_KEY;
|
||||||
|
INTC.IPRA.BIT._TMU1 = GINT_INTP_GRAY;
|
||||||
|
INTC.IPRA.BIT._TMU2 = GINT_INTP_TIMER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gint_priority_unlock_7705(void)
|
||||||
|
{
|
||||||
|
// Restoring the saved states.
|
||||||
|
INTC.IPRA.WORD = iprs[0];
|
||||||
|
INTC.IPRB.WORD = iprs[1];
|
||||||
|
INTX.IPRC.WORD = iprs[2];
|
||||||
|
INTX.IPRD.WORD = iprs[3];
|
||||||
|
INTX.IPRE.WORD = iprs[4];
|
||||||
|
INTX.IPRF.WORD = iprs[5];
|
||||||
|
INTX.IPRG.WORD = iprs[6];
|
||||||
|
INTX.IPRH.WORD = iprs[7];
|
||||||
|
}
|
||||||
|
|
||||||
|
void gint_setup_7705(void)
|
||||||
|
{
|
||||||
|
gint_priority_lock_7705();
|
||||||
|
|
||||||
|
// Saving the RTC configuration.
|
||||||
|
rcr2 = RTC.RCR2.BYTE;
|
||||||
|
// Disabling RTC interrupts by default.
|
||||||
|
RTC.RCR2.BYTE = 0x09;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gint_stop_7705(void)
|
||||||
|
{
|
||||||
|
gint_priority_unlock_7705();
|
||||||
|
|
||||||
|
// Restoring the RTC configuration.
|
||||||
|
RTC.RCR2.BYTE = rcr2;
|
||||||
|
}
|
|
@ -1,3 +1,11 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint core module: mpu
|
||||||
|
//
|
||||||
|
// Determines which kind of MPU is running the program.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
#include <mpu.h>
|
#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
|
@ -0,0 +1,27 @@
|
||||||
|
#include <gint.h>
|
||||||
|
#include <timer.h>
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <7305.h>
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Various MPU-dependent procedures.
|
||||||
|
//---
|
||||||
|
|
||||||
|
/*
|
||||||
|
gint_setRTCFrequency()
|
||||||
|
Sets the RTC interrupt frequency and enables interrupts.
|
||||||
|
*/
|
||||||
|
void gint_setRTCFrequency_7305(enum RTCFrequency frequency)
|
||||||
|
{
|
||||||
|
if(frequency < 1 || frequency > 7) return;
|
||||||
|
RTC.RCR2.BYTE = (frequency << 4) | 0x09;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
gint_getRTCFrequency()
|
||||||
|
Returns the RTC interrupt frequency.
|
||||||
|
*/
|
||||||
|
enum RTCFrequency gint_getRTCFrequency_7305(void)
|
||||||
|
{
|
||||||
|
return (RTC.RCR2.BYTE & 0x70) >> 4;
|
||||||
|
}
|
27
src/mpu/various_7705.c
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include <gint.h>
|
||||||
|
#include <timer.h>
|
||||||
|
#include <keyboard.h>
|
||||||
|
#include <7705.h>
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Various MPU-dependent procedures.
|
||||||
|
//---
|
||||||
|
|
||||||
|
/*
|
||||||
|
gint_setRTCFrequency()
|
||||||
|
Sets the RTC interrupt frequency and enables interrupts.
|
||||||
|
*/
|
||||||
|
void gint_setRTCFrequency_7705(enum RTCFrequency frequency)
|
||||||
|
{
|
||||||
|
if(frequency < 1 || frequency > 7) return;
|
||||||
|
RTC.RCR2.BYTE = (frequency << 4) | 0x09;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
gint_getRTCFrequency()
|
||||||
|
Returns the RTC interrupt frequency.
|
||||||
|
*/
|
||||||
|
enum RTCFrequency gint_getRTCFrequency_7705(void)
|
||||||
|
{
|
||||||
|
return (RTC.RCR2.BYTE & 0x70) >> 4;
|
||||||
|
}
|
|
@ -1,27 +1,11 @@
|
||||||
/*
|
|
||||||
screen.c
|
|
||||||
|
|
||||||
This module is in charge of interaction with the physical screen. See
|
|
||||||
module 'display' for video ram management and drawing.
|
|
||||||
|
|
||||||
The screen basically has two input values, which are a register
|
|
||||||
selector and the selected register's value. What this module does is
|
|
||||||
essentially selecting registers by setting *selector and assigning them
|
|
||||||
values by setting *data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <screen.h>
|
#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)
|
||||||
{
|
{
|
|
@ -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
|
|
@ -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
|
@ -0,0 +1,48 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
memset()
|
||||||
|
Sets the contents of a memory area. A smart copy is performed.
|
||||||
|
*/
|
||||||
|
void *memset(void *d, int byte, size_t byte_number)
|
||||||
|
{
|
||||||
|
uint8_t *dest = (uint8_t *)d;
|
||||||
|
unsigned short word = (byte << 8) | byte;
|
||||||
|
unsigned int longword = (word << 16) | word;
|
||||||
|
|
||||||
|
// When the area is small, simply copying using byte operations. The
|
||||||
|
// minimum length used for long operations must be at least 3.
|
||||||
|
if(byte_number < 8)
|
||||||
|
{
|
||||||
|
while(byte_number)
|
||||||
|
{
|
||||||
|
*dest++ = byte;
|
||||||
|
byte_number--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaching a long offset.
|
||||||
|
while((intptr_t)dest & 3)
|
||||||
|
{
|
||||||
|
*dest++ = byte;
|
||||||
|
byte_number--;
|
||||||
|
}
|
||||||
|
// Copying using long operations.
|
||||||
|
while(byte_number >= 4)
|
||||||
|
{
|
||||||
|
*((uint32_t *)dest) = longword;
|
||||||
|
dest += 4;
|
||||||
|
byte_number -= 4;
|
||||||
|
}
|
||||||
|
// Ending the copy.
|
||||||
|
while(byte_number)
|
||||||
|
{
|
||||||
|
*dest++ = byte;
|
||||||
|
byte_number--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
290
src/tales.c
|
@ -1,290 +0,0 @@
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <tales.h>
|
|
||||||
|
|
||||||
static struct Font *font;
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Local functions.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
getCharacterIndex()
|
|
||||||
Returns the index of a character in a font data area depending on the
|
|
||||||
font format and the size of the characters.
|
|
||||||
|
|
||||||
@arg character
|
|
||||||
|
|
||||||
@return Index in data area (as long array). Returns -1 when the
|
|
||||||
character does not belong to the font format set.
|
|
||||||
*/
|
|
||||||
static int getCharacterIndex(int c)
|
|
||||||
{
|
|
||||||
const char *data = (const char *)&font->glyphs;
|
|
||||||
int index, current;
|
|
||||||
int offset;
|
|
||||||
int width, bits;
|
|
||||||
|
|
||||||
c &= 0x7f;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Getting the character index in the glyph array.
|
|
||||||
|
|
||||||
if(font->format == FontFormat_Ascii) index = c;
|
|
||||||
else if(font->format == FontFormat_Print) index = c - 32;
|
|
||||||
|
|
||||||
else switch(font->format)
|
|
||||||
{
|
|
||||||
case FontFormat_Numeric:
|
|
||||||
if(!isdigit(c)) return -1;
|
|
||||||
index = c - '0';
|
|
||||||
break;
|
|
||||||
case FontFormat_LowerCase:
|
|
||||||
if(!islower(c)) return -1;
|
|
||||||
index = c - 'a';
|
|
||||||
break;
|
|
||||||
case FontFormat_UpperCase:
|
|
||||||
if(!isupper(c)) return -1;
|
|
||||||
index = c - 'A';
|
|
||||||
break;
|
|
||||||
case FontFormat_Letters:
|
|
||||||
if(!isalpha(c)) return -1;
|
|
||||||
index = c - 'A' - ('a' - 'z') * (c >= 'a');
|
|
||||||
break;
|
|
||||||
case FontFormat_Common:
|
|
||||||
if(!isalnum(c)) return -1;
|
|
||||||
index = c - '0' - ('A' - '9') * (c >= 'A') -
|
|
||||||
('a' - 'z') * (c >= 'a');
|
|
||||||
break;
|
|
||||||
case FontFormat_Unknown:
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Reaching the character offset.
|
|
||||||
|
|
||||||
current = index & ~7;
|
|
||||||
offset = font->index[current >> 3];
|
|
||||||
|
|
||||||
while(current < index)
|
|
||||||
{
|
|
||||||
width = data[offset << 2];
|
|
||||||
bits = font->data_height * width + 8;
|
|
||||||
|
|
||||||
offset += (bits + 31) >> 5;
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
operate()
|
|
||||||
Operates on the vram using the given operators. The x-coordinate should
|
|
||||||
be a multiple of 32.
|
|
||||||
|
|
||||||
@arg operators Operator array.
|
|
||||||
@arg height Number of operators (height of text).
|
|
||||||
@arg x
|
|
||||||
@arg y
|
|
||||||
*/
|
|
||||||
static void operate(uint32_t *operators, int height, int x, int y)
|
|
||||||
{
|
|
||||||
int *vram = display_getCurrentVRAM();
|
|
||||||
int vram_offset = (x >> 5) + (y << 2);
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for(i = 0; i < height; i++)
|
|
||||||
{
|
|
||||||
// TODO BLENDING MODES //
|
|
||||||
vram[vram_offset] |= operators[i];
|
|
||||||
|
|
||||||
vram_offset += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
update()
|
|
||||||
Updates the operators using the given glyph. The operation will not be
|
|
||||||
complete if there are not enough bits available in the operator data.
|
|
||||||
In this case the offset will become negative, which means that the
|
|
||||||
calling procedure has to call operate() and re-call update().
|
|
||||||
|
|
||||||
@arg operators Operator array.
|
|
||||||
@arg height Number of operators.
|
|
||||||
@arg available Number of free bits in the operators (lower
|
|
||||||
bits).
|
|
||||||
@arg glyph Glyph data, including meta-data.
|
|
||||||
|
|
||||||
@return Number of bits available after the operation. May be negative:
|
|
||||||
in this case, call operate() and update() again.
|
|
||||||
*/
|
|
||||||
static int update(uint32_t *operators, int height, int available,
|
|
||||||
uint32_t *glyph)
|
|
||||||
{
|
|
||||||
// Glyph width.
|
|
||||||
int width = glyph[0] >> 24;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// The glyph mask extracts 'width' bits at the left. The partial mask
|
|
||||||
// is used when there are less than 'width' bits available in the
|
|
||||||
// current data longword.
|
|
||||||
uint32_t glyph_mask = 0xffffffff << (32 - width);
|
|
||||||
uint32_t partial_mask;
|
|
||||||
|
|
||||||
int shift;
|
|
||||||
uint32_t line;
|
|
||||||
|
|
||||||
// Current data longword, next data array index, and number of bits
|
|
||||||
// still available in 'data'.
|
|
||||||
uint32_t data = glyph[0] << 8;
|
|
||||||
int data_index = 1;
|
|
||||||
int bits_available = 24;
|
|
||||||
|
|
||||||
for(i = 0; i < height; i++)
|
|
||||||
{
|
|
||||||
shift = 32 - available;
|
|
||||||
|
|
||||||
// Getting the next 'width' bits. In some cases these bits will
|
|
||||||
// intersect two different longs.
|
|
||||||
line = data & glyph_mask;
|
|
||||||
line = (shift >= 0) ? (line >> shift) : (line << -shift);
|
|
||||||
operators[i] |= line;
|
|
||||||
|
|
||||||
data <<= width;
|
|
||||||
bits_available -= width;
|
|
||||||
|
|
||||||
// Continue until they do.
|
|
||||||
if(bits_available >= 0) continue;
|
|
||||||
|
|
||||||
// Computing a special mask that extracts just the number of
|
|
||||||
// bits missing, and loading a new data longword.
|
|
||||||
partial_mask = 0xffffffff << (32 + bits_available);
|
|
||||||
data = glyph[data_index++];
|
|
||||||
shift += width + bits_available;
|
|
||||||
|
|
||||||
if(shift <= 31)
|
|
||||||
{
|
|
||||||
line = data & partial_mask;
|
|
||||||
line = (shift >= 0) ? (line >> shift) :
|
|
||||||
(line << -shift);
|
|
||||||
operators[i] |= line;
|
|
||||||
}
|
|
||||||
|
|
||||||
data <<= -bits_available;
|
|
||||||
bits_available += 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
return available - width;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Public API.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
print_configure()
|
|
||||||
Sets the font and mode to use for the following print operations.
|
|
||||||
|
|
||||||
@arg font
|
|
||||||
@arg mode
|
|
||||||
*/
|
|
||||||
void print_configure(struct Font *next_font)
|
|
||||||
{
|
|
||||||
font = next_font;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
print_raw()
|
|
||||||
Prints the given string, without any analysis.
|
|
||||||
|
|
||||||
@arg str
|
|
||||||
@arg x
|
|
||||||
@arg y
|
|
||||||
*/
|
|
||||||
void print_raw(const char *str, int x, int y)
|
|
||||||
{
|
|
||||||
// Operator data, and number of available bits in the operators (which
|
|
||||||
// is the same for all operators, since they are treated equally).
|
|
||||||
uint32_t *operators;
|
|
||||||
int available;
|
|
||||||
|
|
||||||
// Raw glyph data, each glyph being represented by one or several
|
|
||||||
// longwords, and an index in this array.
|
|
||||||
uint32_t *data = (uint32_t *)font->glyphs;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
// Height of each glyph. This value is constant because the storage
|
|
||||||
// format requires it: it allows greater optimization.
|
|
||||||
int height;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(!font) return;
|
|
||||||
|
|
||||||
// Allocating data. There will be one operator for each line.
|
|
||||||
height = font->data_height;
|
|
||||||
if(x > 127 || y > 63 || y <= -height) return;
|
|
||||||
|
|
||||||
operators = calloc(height, sizeof(uint32_t));
|
|
||||||
if(!operators) return;
|
|
||||||
|
|
||||||
// Computing the initial operator offset to have 32-aligned operators.
|
|
||||||
// This allows to write directly video ram longs instead of having to
|
|
||||||
// shift operators, and do all the vram operation twice.
|
|
||||||
available = 32 - (x & 31);
|
|
||||||
x &= ~31;
|
|
||||||
|
|
||||||
// Displaying character after another.
|
|
||||||
while(*str)
|
|
||||||
{
|
|
||||||
index = getCharacterIndex(*str++);
|
|
||||||
if(index < 0) continue;
|
|
||||||
|
|
||||||
// Updating the operators.
|
|
||||||
available = update(operators, height, available, data + index);
|
|
||||||
|
|
||||||
// Continue until operators are full (this includes an
|
|
||||||
// additional bit to add a space between each character).
|
|
||||||
if(available > 1)
|
|
||||||
{
|
|
||||||
available--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When operators are full, updating the video ram and
|
|
||||||
// preparing the operators for another row.
|
|
||||||
|
|
||||||
operate(operators, height, x, y);
|
|
||||||
x += 32;
|
|
||||||
if(x > 96) break;
|
|
||||||
|
|
||||||
memset(operators, 0, height << 2);
|
|
||||||
if(available >= 0)
|
|
||||||
{
|
|
||||||
available = 31 + available;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finishing update, in case it has been only partially done,
|
|
||||||
// because there was not enough bits available to fit all the
|
|
||||||
// information. Also adding a space, assuming that characters
|
|
||||||
// aren't more than 30 bits wide.
|
|
||||||
available += 32 + (data[index] >> 24);
|
|
||||||
available = update(operators, height, available, data + index);
|
|
||||||
available--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final operation.
|
|
||||||
if(x <= 96 && available < 32) operate(operators, height, x, y);
|
|
||||||
|
|
||||||
free(operators);
|
|
||||||
}
|
|
88
src/tales/dtext.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
#include <tales_internals.h>
|
||||||
|
#include <tales.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
dtext()
|
||||||
|
Prints the given string, without any analysis.
|
||||||
|
*/
|
||||||
|
void dtext(const char *str, int x, int y)
|
||||||
|
{
|
||||||
|
// Operator data, and number of available bits in the operators (which
|
||||||
|
// is the same for all operators, since they are treated equally).
|
||||||
|
uint32_t *operators;
|
||||||
|
int available;
|
||||||
|
|
||||||
|
// Raw glyph data, each glyph being represented by one or several
|
||||||
|
// longwords, and an index in this array.
|
||||||
|
uint32_t *data = (uint32_t *)font->glyphs;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
// Height of each glyph. This value is constant because the storage
|
||||||
|
// format requires it: it allows greater optimization.
|
||||||
|
int height;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if(!font) return;
|
||||||
|
|
||||||
|
// Allocating data. There will be one operator for each line.
|
||||||
|
height = font->data_height;
|
||||||
|
if(x > 127 || y > 63 || y <= -height) return;
|
||||||
|
|
||||||
|
operators = calloc(height, sizeof(uint32_t));
|
||||||
|
if(!operators) return;
|
||||||
|
|
||||||
|
// Computing the initial operator offset to have 32-aligned operators.
|
||||||
|
// This allows to write directly video ram longs instead of having to
|
||||||
|
// shift operators, and do all the vram operation twice.
|
||||||
|
available = 32 - (x & 31);
|
||||||
|
x &= ~31;
|
||||||
|
|
||||||
|
// Displaying character after another.
|
||||||
|
while(*str)
|
||||||
|
{
|
||||||
|
index = getCharacterIndex(*str++);
|
||||||
|
if(index < 0) continue;
|
||||||
|
|
||||||
|
// Updating the operators.
|
||||||
|
available = update(operators, height, available, data + index);
|
||||||
|
|
||||||
|
// Continue until operators are full (this includes an
|
||||||
|
// additional bit to add a space between each character).
|
||||||
|
if(available > 1)
|
||||||
|
{
|
||||||
|
available--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When operators are full, updating the video ram and
|
||||||
|
// preparing the operators for another row.
|
||||||
|
|
||||||
|
operate(operators, height, x, y);
|
||||||
|
x += 32;
|
||||||
|
if(x > 96) break;
|
||||||
|
|
||||||
|
memset(operators, 0, height << 2);
|
||||||
|
if(available >= 0)
|
||||||
|
{
|
||||||
|
available = 31 + available;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finishing update, in case it has been only partially done,
|
||||||
|
// because there was not enough bits available to fit all the
|
||||||
|
// information. Also adding a space, assuming that characters
|
||||||
|
// aren't more than 30 bits wide.
|
||||||
|
available += 32 + (data[index] >> 24);
|
||||||
|
available = update(operators, height, available, data + index);
|
||||||
|
available--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final operation.
|
||||||
|
if(x <= 96 && available < 32) operate(operators, height, x, y);
|
||||||
|
|
||||||
|
free(operators);
|
||||||
|
}
|
11
src/tales/tales_configuration.c
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#include <tales_internals.h>
|
||||||
|
#include <tales.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
text_configure()
|
||||||
|
Sets the font and mode to use for the following print operations.
|
||||||
|
*/
|
||||||
|
void text_configure(struct Font *font)
|
||||||
|
{
|
||||||
|
font = next_font;
|
||||||
|
}
|
164
src/tales/tales_internals.c
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
#include <tales_internals.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
struct Font *font;
|
||||||
|
|
||||||
|
/*
|
||||||
|
getCharacterIndex()
|
||||||
|
Returns the index of a character in a font data area depending on the
|
||||||
|
font format and the size of the characters. Returns the index in the
|
||||||
|
data area, as long array, or -1 when the character does not belong to
|
||||||
|
the font format set.
|
||||||
|
*/
|
||||||
|
int getCharacterIndex(int c)
|
||||||
|
{
|
||||||
|
const char *data = (const char *)&font->glyphs;
|
||||||
|
int index, current;
|
||||||
|
int offset;
|
||||||
|
int width, bits;
|
||||||
|
|
||||||
|
c &= 0x7f;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Getting the character index in the glyph array.
|
||||||
|
|
||||||
|
if(font->format == FontFormat_Ascii) index = c;
|
||||||
|
else if(font->format == FontFormat_Print) index = c - 32;
|
||||||
|
|
||||||
|
else switch(font->format)
|
||||||
|
{
|
||||||
|
case FontFormat_Numeric:
|
||||||
|
if(!isdigit(c)) return -1;
|
||||||
|
index = c - '0';
|
||||||
|
break;
|
||||||
|
case FontFormat_LowerCase:
|
||||||
|
if(!islower(c)) return -1;
|
||||||
|
index = c - 'a';
|
||||||
|
break;
|
||||||
|
case FontFormat_UpperCase:
|
||||||
|
if(!isupper(c)) return -1;
|
||||||
|
index = c - 'A';
|
||||||
|
break;
|
||||||
|
case FontFormat_Letters:
|
||||||
|
if(!isalpha(c)) return -1;
|
||||||
|
index = c - 'A' - ('a' - 'z') * (c >= 'a');
|
||||||
|
break;
|
||||||
|
case FontFormat_Common:
|
||||||
|
if(!isalnum(c)) return -1;
|
||||||
|
index = c - '0' - ('A' - '9') * (c >= 'A') -
|
||||||
|
('a' - 'z') * (c >= 'a');
|
||||||
|
break;
|
||||||
|
case FontFormat_Unknown:
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Reaching the character offset.
|
||||||
|
|
||||||
|
current = index & ~7;
|
||||||
|
offset = font->index[current >> 3];
|
||||||
|
|
||||||
|
while(current < index)
|
||||||
|
{
|
||||||
|
width = data[offset << 2];
|
||||||
|
bits = font->data_height * width + 8;
|
||||||
|
|
||||||
|
offset += (bits + 31) >> 5;
|
||||||
|
current++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
operate()
|
||||||
|
Operates on the vram using the given operators. The x-coordinate should
|
||||||
|
be a multiple of 32. There should be `height` operators.
|
||||||
|
*/
|
||||||
|
void operate(uint32_t *operators, int height, int x, int y)
|
||||||
|
{
|
||||||
|
int *vram = display_getCurrentVRAM();
|
||||||
|
int vram_offset = (x >> 5) + (y << 2);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
// TODO BLENDING MODES //
|
||||||
|
vram[vram_offset] |= operators[i];
|
||||||
|
|
||||||
|
vram_offset += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
update()
|
||||||
|
Updates the operators using the given glyph. The operation will not be
|
||||||
|
complete if there are not enough bits available in the operator data.
|
||||||
|
In this case the offset will become negative, which means that the
|
||||||
|
calling procedure has to call operate() and re-call update().
|
||||||
|
`available` represents the number of free bits in the operators (lower
|
||||||
|
bits).
|
||||||
|
Returns the number of bits available after the operation. If it's
|
||||||
|
negative, call operate() and update() again.
|
||||||
|
*/
|
||||||
|
int update(uint32_t *operators, int height, int available,
|
||||||
|
uint32_t *glyph)
|
||||||
|
{
|
||||||
|
// Glyph width.
|
||||||
|
int width = glyph[0] >> 24;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// The glyph mask extracts 'width' bits at the left. The partial mask
|
||||||
|
// is used when there are less than 'width' bits available in the
|
||||||
|
// current data longword.
|
||||||
|
uint32_t glyph_mask = 0xffffffff << (32 - width);
|
||||||
|
uint32_t partial_mask;
|
||||||
|
|
||||||
|
int shift;
|
||||||
|
uint32_t line;
|
||||||
|
|
||||||
|
// Current data longword, next data array index, and number of bits
|
||||||
|
// still available in 'data'.
|
||||||
|
uint32_t data = glyph[0] << 8;
|
||||||
|
int data_index = 1;
|
||||||
|
int bits_available = 24;
|
||||||
|
|
||||||
|
for(i = 0; i < height; i++)
|
||||||
|
{
|
||||||
|
shift = 32 - available;
|
||||||
|
|
||||||
|
// Getting the next 'width' bits. In some cases these bits will
|
||||||
|
// intersect two different longs.
|
||||||
|
line = data & glyph_mask;
|
||||||
|
line = (shift >= 0) ? (line >> shift) : (line << -shift);
|
||||||
|
operators[i] |= line;
|
||||||
|
|
||||||
|
data <<= width;
|
||||||
|
bits_available -= width;
|
||||||
|
|
||||||
|
// Continue until they do.
|
||||||
|
if(bits_available >= 0) continue;
|
||||||
|
|
||||||
|
// Computing a special mask that extracts just the number of
|
||||||
|
// bits missing, and loading a new data longword.
|
||||||
|
partial_mask = 0xffffffff << (32 + bits_available);
|
||||||
|
data = glyph[data_index++];
|
||||||
|
shift += width + bits_available;
|
||||||
|
|
||||||
|
if(shift <= 31)
|
||||||
|
{
|
||||||
|
line = data & partial_mask;
|
||||||
|
line = (shift >= 0) ? (line >> shift) :
|
||||||
|
(line << -shift);
|
||||||
|
operators[i] |= line;
|
||||||
|
}
|
||||||
|
|
||||||
|
data <<= -bits_available;
|
||||||
|
bits_available += 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
return available - width;
|
||||||
|
}
|
47
src/tales/tales_internals.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//---
|
||||||
|
//
|
||||||
|
// gint drawing module: tales
|
||||||
|
//
|
||||||
|
// Text displaying. Does some good optimization, though requires dynamic
|
||||||
|
// allocation.
|
||||||
|
//
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef _TALES_INTERNALS_H
|
||||||
|
#define _TALES_INTERNALS_H 1
|
||||||
|
|
||||||
|
#include <tales.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
extern struct Font *font;
|
||||||
|
|
||||||
|
/*
|
||||||
|
getCharacterIndex()
|
||||||
|
Returns the index of a character in a font data area depending on the
|
||||||
|
font format and the size of the characters. Returns the index in the
|
||||||
|
data area, as long array, or -1 when the character does not belong to
|
||||||
|
the font format set.
|
||||||
|
*/
|
||||||
|
int getCharacterIndex(int c);
|
||||||
|
|
||||||
|
/*
|
||||||
|
operate()
|
||||||
|
Operates on the vram using the given operators. The x-coordinate should
|
||||||
|
be a multiple of 32. There should be `height` operators.
|
||||||
|
*/
|
||||||
|
void operate(uint32_t *operators, int height, int x, int y);
|
||||||
|
|
||||||
|
/*
|
||||||
|
update()
|
||||||
|
Updates the operators using the given glyph. The operation will not be
|
||||||
|
complete if there are not enough bits available in the operator data.
|
||||||
|
In this case the offset will become negative, which means that the
|
||||||
|
calling procedure has to call operate() and re-call update().
|
||||||
|
`available` represents the number of free bits in the operators (lower
|
||||||
|
bits).
|
||||||
|
Returns the number of bits available after the operation. If it's
|
||||||
|
negative, call operate() and update() again.
|
||||||
|
*/
|
||||||
|
int update(uint32_t *operators, int height, int available, uint32_t *glyph);
|
||||||
|
|
||||||
|
#endif // _TALES_INTERNALS_H
|
201
src/timer.c
|
@ -1,201 +0,0 @@
|
||||||
#include <timer.h>
|
|
||||||
#include <mpu.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Internal declarations.
|
|
||||||
// Timer structure and running information (callbacks, repeats etc.)
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
struct Timer
|
|
||||||
This structure holds information for a running timer.
|
|
||||||
*/
|
|
||||||
struct Timer
|
|
||||||
{
|
|
||||||
void (*callback)(void);
|
|
||||||
int repetitions;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Static timers.
|
|
||||||
static struct Timer timers[3] = { { NULL, 0 }, { NULL, 0 }, { NULL, 0 } };
|
|
||||||
|
|
||||||
/*
|
|
||||||
struct mod_tmu
|
|
||||||
This structure holds information about the timer unit (peripheral
|
|
||||||
module) registers.
|
|
||||||
*/
|
|
||||||
struct mod_tmu
|
|
||||||
{
|
|
||||||
// Timer constant register.
|
|
||||||
unsigned int TCOR;
|
|
||||||
// Timer counter.
|
|
||||||
unsigned int TCNT;
|
|
||||||
|
|
||||||
// Timer control register.
|
|
||||||
union
|
|
||||||
{
|
|
||||||
unsigned short WORD;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
unsigned :7;
|
|
||||||
// Underflow flag.
|
|
||||||
unsigned UNF :1;
|
|
||||||
unsigned :2;
|
|
||||||
// Underflow interrupt enable.
|
|
||||||
unsigned UNIE :1;
|
|
||||||
// Clock edge, reserved on SH7305.
|
|
||||||
unsigned CKEG :2;
|
|
||||||
// Timer prescaler.
|
|
||||||
unsigned TPSC :3;
|
|
||||||
};
|
|
||||||
} TCR;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Internal API.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
timer_get()
|
|
||||||
|
|
||||||
Returns the timer and TSTR register addresses.
|
|
||||||
|
|
||||||
@arg timer Timer id.
|
|
||||||
@arg tmu mod_tmu structure pointer address.
|
|
||||||
@arg tstr mod_tstr structure pointer address.
|
|
||||||
*/
|
|
||||||
static void timer_get(int timer, struct mod_tmu **tmu, unsigned char **tstr)
|
|
||||||
{
|
|
||||||
// Using SH7705 information for SH-3-based MPUs.
|
|
||||||
if(MPU_CURRENT == MPU_SH7337 || MPU_CURRENT == MPU_SH7355)
|
|
||||||
{
|
|
||||||
if(tstr) *tstr = (unsigned char *)0xfffffe92;
|
|
||||||
if(tmu) *tmu = (struct mod_tmu *)0xfffffe94;
|
|
||||||
}
|
|
||||||
// Assuming SH7305 by default.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(tstr) *tstr = (unsigned char *)0xa4490004;
|
|
||||||
if(tmu) *tmu = (struct mod_tmu *)0xa4490008;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shifting tmu value to get to the timer-nth timer in the unit.
|
|
||||||
if(tmu) *tmu += timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
timer_interrupt()
|
|
||||||
Handles the interrupt for the given timer.
|
|
||||||
|
|
||||||
@timer Timer that generated the interrupt.
|
|
||||||
*/
|
|
||||||
void timer_interrupt(int timer)
|
|
||||||
{
|
|
||||||
// Getting the timer address.
|
|
||||||
struct mod_tmu *tmu;
|
|
||||||
timer_get(timer, &tmu, NULL);
|
|
||||||
|
|
||||||
// Resetting the interrupt flag.
|
|
||||||
(*tmu).TCR.UNF = 0;
|
|
||||||
|
|
||||||
// Calling the callback function.
|
|
||||||
if(timers[timer].callback) timers[timer].callback();
|
|
||||||
|
|
||||||
// Reducing the number of repetitions left, if not infinite.
|
|
||||||
if(!timers[timer].repetitions) return;
|
|
||||||
// And stopping it if necessary.
|
|
||||||
if(timers[timer].repetitions == 1) timer_stop(timer);
|
|
||||||
else timers[timer].repetitions--;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//---
|
|
||||||
// Public API.
|
|
||||||
//---
|
|
||||||
|
|
||||||
/*
|
|
||||||
timer_start()
|
|
||||||
Configures and starts a timer.
|
|
||||||
|
|
||||||
@arg timer Timer identifier.
|
|
||||||
@arg delay Delay before expiration.
|
|
||||||
@arg prescaler Clock prescaler value.
|
|
||||||
@arg callback Callback function.
|
|
||||||
@arg repetitions Number of repetitions, 0 for infinite.
|
|
||||||
*/
|
|
||||||
void timer_start(int timer, int delay, int prescaler, void (*callback)(void),
|
|
||||||
int repetitions)
|
|
||||||
{
|
|
||||||
// Getting the timer address. Using a byte to alter TSTR.
|
|
||||||
struct mod_tmu *tmu;
|
|
||||||
unsigned char *tstr;
|
|
||||||
int byte = (1 << timer);
|
|
||||||
timer_get(timer, &tmu, &tstr);
|
|
||||||
|
|
||||||
// Setting the constant register.
|
|
||||||
(*tmu).TCOR = delay;
|
|
||||||
// Loading the delay in the counter.
|
|
||||||
(*tmu).TCNT = delay;
|
|
||||||
|
|
||||||
// Resetting underflow flag.
|
|
||||||
(*tmu).TCR.UNF = 0;
|
|
||||||
// Enabling interruptions on underflow.
|
|
||||||
(*tmu).TCR.UNIE = 1;
|
|
||||||
// Counting on rising edge. On SH7305 these two bits are reserved but
|
|
||||||
// writing 0 is ignored.
|
|
||||||
(*tmu).TCR.CKEG = 0;
|
|
||||||
// Setting the prescaler.
|
|
||||||
(*tmu).TCR.TPSC = prescaler;
|
|
||||||
|
|
||||||
// Loading the structure information.
|
|
||||||
timers[timer].callback = callback;
|
|
||||||
timers[timer].repetitions = repetitions;
|
|
||||||
|
|
||||||
// Starting the timer and returning.
|
|
||||||
*tstr |= byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
timer_stop()
|
|
||||||
Stops the given timer. This function may be called even if the timer is
|
|
||||||
not running.
|
|
||||||
|
|
||||||
@arg timer Timer to stop.
|
|
||||||
*/
|
|
||||||
void timer_stop(int timer)
|
|
||||||
{
|
|
||||||
// Getting TSTR address and the corresponding byte.
|
|
||||||
unsigned char *tstr;
|
|
||||||
int byte = (1 << timer);
|
|
||||||
timer_get(timer, NULL, &tstr);
|
|
||||||
|
|
||||||
// Stopping the timer.
|
|
||||||
*tstr &= ~byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
timer_reload()
|
|
||||||
Reloads the given timer with the given constant. Starts the timer if
|
|
||||||
it was stopped.
|
|
||||||
|
|
||||||
@arg timer Timer identifier.
|
|
||||||
@arg new_delay
|
|
||||||
*/
|
|
||||||
void timer_reload(int timer, int new_delay)
|
|
||||||
{
|
|
||||||
struct mod_tmu *tmu;
|
|
||||||
unsigned char *tstr;
|
|
||||||
int byte = (1 << timer);
|
|
||||||
timer_get(timer, &tmu, &tstr);
|
|
||||||
|
|
||||||
// Setting the constant and the delay.
|
|
||||||
(*tmu).TCOR = new_delay;
|
|
||||||
(*tmu).TCNT = new_delay;
|
|
||||||
|
|
||||||
// Starting the timer.
|
|
||||||
*tstr |= byte;
|
|
||||||
}
|
|
25
src/timer/timer_get.c
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include <timer_internals.h>
|
||||||
|
#include <mpu.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
timer_get()
|
||||||
|
Returns the timer and TSTR register addresses.
|
||||||
|
*/
|
||||||
|
void timer_get(int timer, struct mod_tmu **tmu, unsigned char **tstr)
|
||||||
|
{
|
||||||
|
// Using SH7705 information for SH-3-based MPUs.
|
||||||
|
if(isSH3())
|
||||||
|
{
|
||||||
|
if(tstr) *tstr = (unsigned char *)0xfffffe92;
|
||||||
|
if(tmu) *tmu = (struct mod_tmu *)0xfffffe94;
|
||||||
|
}
|
||||||
|
// Assuming SH7305 by default.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(tstr) *tstr = (unsigned char *)0xa4490004;
|
||||||
|
if(tmu) *tmu = (struct mod_tmu *)0xa4490008;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shifting tmu value to get to the timer-nth timer in the unit.
|
||||||
|
if(tmu) *tmu += timer;
|
||||||
|
}
|
54
src/timer/timer_internals.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef _TIMER_INTERNALS_H
|
||||||
|
#define _TIMER_INTERNALS_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct Timer
|
||||||
|
This structure holds information for a running timer.
|
||||||
|
*/
|
||||||
|
struct Timer
|
||||||
|
{
|
||||||
|
void (*callback)(void);
|
||||||
|
int repeats;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct Timer timers[3];
|
||||||
|
|
||||||
|
/*
|
||||||
|
struct mod_tmu
|
||||||
|
This structure holds information about the timer unit (peripheral
|
||||||
|
module) registers.
|
||||||
|
*/
|
||||||
|
struct mod_tmu
|
||||||
|
{
|
||||||
|
// Timer constant register.
|
||||||
|
unsigned int TCOR;
|
||||||
|
// Timer counter.
|
||||||
|
unsigned int TCNT;
|
||||||
|
|
||||||
|
// Timer control register.
|
||||||
|
union
|
||||||
|
{
|
||||||
|
unsigned short WORD;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned :7;
|
||||||
|
// Underflow flag.
|
||||||
|
unsigned UNF :1;
|
||||||
|
unsigned :2;
|
||||||
|
// Underflow interrupt enable.
|
||||||
|
unsigned UNIE :1;
|
||||||
|
// Clock edge, reserved on SH7305.
|
||||||
|
unsigned CKEG :2;
|
||||||
|
// Timer prescaler.
|
||||||
|
unsigned TPSC :3;
|
||||||
|
};
|
||||||
|
} TCR;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
timer_get()
|
||||||
|
Returns the timer and TSTR register addresses.
|
||||||
|
*/
|
||||||
|
void timer_get(int timer, struct mod_tmu **tmu, unsigned char **tstr);
|
||||||
|
|
||||||
|
#endif // _TIMER_INTERNALS_H
|
27
src/timer/timer_interrupt.c
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include <timer.h>
|
||||||
|
#include <timer_internals.h>
|
||||||
|
|
||||||
|
struct Timer timers[3] = { { NULL, 0 }, { NULL, 0 }, { NULL, 0 } };
|
||||||
|
|
||||||
|
/*
|
||||||
|
timer_interrupt()
|
||||||
|
Handles the interrupt for the given timer.
|
||||||
|
*/
|
||||||
|
void timer_interrupt(int timer)
|
||||||
|
{
|
||||||
|
// Getting the timer address.
|
||||||
|
struct mod_tmu *tmu;
|
||||||
|
timer_get(timer, &tmu, NULL);
|
||||||
|
|
||||||
|
// Resetting the interrupt flag.
|
||||||
|
(*tmu).TCR.UNF = 0;
|
||||||
|
|
||||||
|
// Calling the callback function.
|
||||||
|
if(timers[timer].callback) timers[timer].callback();
|
||||||
|
|
||||||
|
// Reducing the number of repetitions left, if not infinite.
|
||||||
|
if(!timers[timer].repeats) return;
|
||||||
|
// And stopping it if necessary.
|
||||||
|
if(timers[timer].repeats == 1) timer_stop(timer);
|
||||||
|
else timers[timer].repeats--;
|
||||||
|
}
|
22
src/timer/timer_reload.c
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include <timer.h>
|
||||||
|
#include <timer_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
timer_reload()
|
||||||
|
Reloads the given timer with the given constant. Starts the timer if
|
||||||
|
it was stopped.
|
||||||
|
*/
|
||||||
|
void timer_reload(int timer, int new_delay)
|
||||||
|
{
|
||||||
|
struct mod_tmu *tmu;
|
||||||
|
unsigned char *tstr;
|
||||||
|
int byte = (1 << timer);
|
||||||
|
timer_get(timer, &tmu, &tstr);
|
||||||
|
|
||||||
|
// Setting the constant and the delay.
|
||||||
|
(*tmu).TCOR = new_delay;
|
||||||
|
(*tmu).TCNT = new_delay;
|
||||||
|
|
||||||
|
// Starting the timer.
|
||||||
|
*tstr |= byte;
|
||||||
|
}
|
38
src/timer/timer_start.c
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#include <timer.h>
|
||||||
|
#include <timer_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
timer_start()
|
||||||
|
Configures and starts a timer.
|
||||||
|
*/
|
||||||
|
void timer_start(int timer, int delay, int prescaler, void (*callback)(void),
|
||||||
|
int repeats)
|
||||||
|
{
|
||||||
|
// Getting the timer address. Using a byte to alter TSTR.
|
||||||
|
struct mod_tmu *tmu;
|
||||||
|
unsigned char *tstr;
|
||||||
|
int byte = (1 << timer);
|
||||||
|
timer_get(timer, &tmu, &tstr);
|
||||||
|
|
||||||
|
// Setting the constant register.
|
||||||
|
(*tmu).TCOR = delay;
|
||||||
|
// Loading the delay in the counter.
|
||||||
|
(*tmu).TCNT = delay;
|
||||||
|
|
||||||
|
// Resetting underflow flag.
|
||||||
|
(*tmu).TCR.UNF = 0;
|
||||||
|
// Enabling interruptions on underflow.
|
||||||
|
(*tmu).TCR.UNIE = 1;
|
||||||
|
// Counting on rising edge. On SH7305 these two bits are reserved but
|
||||||
|
// writing 0 is ignored.
|
||||||
|
(*tmu).TCR.CKEG = 0;
|
||||||
|
// Setting the prescaler.
|
||||||
|
(*tmu).TCR.TPSC = prescaler;
|
||||||
|
|
||||||
|
// Loading the structure information.
|
||||||
|
timers[timer].callback = callback;
|
||||||
|
timers[timer].repeats = repeats;
|
||||||
|
|
||||||
|
// Starting the timer and returning.
|
||||||
|
*tstr |= byte;
|
||||||
|
}
|
18
src/timer/timer_stop.c
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include <timer.h>
|
||||||
|
#include <timer_internals.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
timer_stop()
|
||||||
|
Stops the given timer. This function may be called even if the timer is
|
||||||
|
not running.
|
||||||
|
*/
|
||||||
|
void timer_stop(int timer)
|
||||||
|
{
|
||||||
|
// Getting TSTR address and the corresponding byte.
|
||||||
|
unsigned char *tstr;
|
||||||
|
int byte = (1 << timer);
|
||||||
|
timer_get(timer, NULL, &tstr);
|
||||||
|
|
||||||
|
// Stopping the timer.
|
||||||
|
*tstr &= ~byte;
|
||||||
|
}
|
|
@ -1,190 +0,0 @@
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Syscall information
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Syscall id: 0x24a
|
|
||||||
Syscall address: 0x8003fc48
|
|
||||||
|
|
||||||
r4 {short *matrixcode}
|
|
||||||
|
|
||||||
Syscall 0x24a redirects to 8003 f9d6.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Stack
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
------ Pointer sent at <3f9fe> (also as first parameter) and <3fa12>
|
|
||||||
(12) Data from a44b 0000
|
|
||||||
------ Pointer sent at <3f9de>
|
|
||||||
(4) {short *matrixcode} at <3f9fe>
|
|
||||||
pr
|
|
||||||
r14
|
|
||||||
------ Bottom
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Syscall code
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
# Saves some data.
|
|
||||||
3f9d6: 2fe6 mov.l r14, @-r15
|
|
||||||
3f9d8: 4f22 sts.l pr, @-r15
|
|
||||||
3f9da: 7ff0 add #-16, r15
|
|
||||||
|
|
||||||
# First subroutine call: disables keyboard interrupts.
|
|
||||||
# Also pushes {short *matrixcode} on the stack.
|
|
||||||
3f9dc: d128 mov.l #0x8003e264, r1
|
|
||||||
3f9de: 410b jsr @r1
|
|
||||||
3f9e0: 1f43 mov.l r4, @(12, r15)
|
|
||||||
|
|
||||||
# Loads KEYSC data to the stack.
|
|
||||||
# Second subroutine call, with parameters : stack top and {short *matrixcode}.
|
|
||||||
# Checks whether the key given in the matrixcode was pressed.
|
|
||||||
3f9e2: d528 mov.l #0xa44b0000, r5
|
|
||||||
3f9e4: 6451 mov.w @r5, r4
|
|
||||||
3f9e6: 2f41 mov.w r4, @r15
|
|
||||||
3f9e8: 64f3 mov r15, r4
|
|
||||||
3f9ea: 8551 mov.w @(2, r5), r0
|
|
||||||
3f9ec: 81f1 mov.w r0, @(2, r15)
|
|
||||||
3f9ee: 8552 mov.w @(4, r5), r0
|
|
||||||
3f9f0: 81f2 mov.w r0, @(4, r15)
|
|
||||||
3f9f2: 8553 mov.w @(6, r5), r0
|
|
||||||
3f9f4: 81f3 mov.w r0, @(6, r15)
|
|
||||||
3f9f6: 8554 mov.w @(8, r5), r0
|
|
||||||
3f9f8: 81f4 mov.w r0, @(8, r15)
|
|
||||||
3f9fa: 8555 mov.w @(10, r5), r0
|
|
||||||
3f9fc: 81f5 mov.w r0, @(10, r15)
|
|
||||||
3f9fe: bd30 bsr <3f462>
|
|
||||||
3fa00: 55f3 mov.l @(12, r15), r5
|
|
||||||
|
|
||||||
# Loads IPRF address to r6, sets the keyboard interrupt priority to 13 and
|
|
||||||
# performs a third subroutine call : enables keyboard interrupts (curiously
|
|
||||||
# the interrupt priority is set two times).
|
|
||||||
# Also saves the last subroutine result (key pressed) to r14.
|
|
||||||
3fa02: d621 mov.l <3fa88>(#0xa4080014), r6
|
|
||||||
3fa04: 9113 mov.w <3fa2e>(#0x0fff), r1
|
|
||||||
3fa06: 9213 mov.w <3fa30>(#0xd000), r2
|
|
||||||
3fa08: 6e03 mov r0, r14
|
|
||||||
3fa0a: 6761 mov.w @r6, r7
|
|
||||||
3fa0c: 2719 and r1, r7
|
|
||||||
3fa0e: 272b or r2, r7
|
|
||||||
3fa10: d212 mov.l #0x8003e26c, r2
|
|
||||||
3fa12: 420b jsr @r2
|
|
||||||
3fa14: 2671 mov.w r7, @r6
|
|
||||||
|
|
||||||
# Restores the 'key pressed' result to r0, restores saved data and returns.
|
|
||||||
3fa16: 60e3 mov r14, r0
|
|
||||||
3fa18: 7f10 add #16, r15
|
|
||||||
3fa1a: 4f26 lds.l @r15+, pr
|
|
||||||
3fa1c: 000b rts
|
|
||||||
3fa1e: 6ef6 mov.l @r15+, r14
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Subroutine call at <3f9de>
|
|
||||||
- Denies keyboard interrupt in IMR5.
|
|
||||||
- Disables keyboard interrupt in IPRF.
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Call redirects to 8003 e278.
|
|
||||||
|
|
||||||
# Loads IMR5 address to r4.
|
|
||||||
3e278: d478 mov.l <3e45c>(#0xa4080094), r4
|
|
||||||
3e27a: e180 mov #-128, r1
|
|
||||||
3e27c: 955b mov.w <3e336>(#0x0fff), r5
|
|
||||||
# Writes 0x80 to IMR5.
|
|
||||||
3e27e: 2410 mov.b r1, @r4
|
|
||||||
|
|
||||||
# Loads IPRF address to r4.
|
|
||||||
3e280: 7480 add #-128, r4
|
|
||||||
# Sets the four upper bits of IPRF to 0.
|
|
||||||
3e282: 6241 mov.w @r4, r2
|
|
||||||
3e284: 2259 and r5, r2
|
|
||||||
3e286: 000b rts
|
|
||||||
3e288: 2421 mov.w r2, @r4
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Subroutine call at <3f9fe>
|
|
||||||
- Detects whether a specific key is pressed using the KEYSC data loaded onto
|
|
||||||
the stack.
|
|
||||||
- Returns 1 if the key is pressed, 0 otherwise.
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Stack:
|
|
||||||
r4 (bottom stack address)
|
|
||||||
{short *matrixcode}
|
|
||||||
------ Bottom stack address.
|
|
||||||
|
|
||||||
# Pushes {short *matrixcode} on the stack and on top, pushes r4.
|
|
||||||
# Loads the key column to r2 and replaces r5 by #1 (r5 will then be shifted to
|
|
||||||
# be used as a mask in a KEYSC word).
|
|
||||||
3f462: 7ff8 add #-8, r15
|
|
||||||
3f464: 1f51 mov.l r5, @(4, r15)
|
|
||||||
3f466: 6250 mov.b @r5, r2
|
|
||||||
3f468: e501 mov #1, r5
|
|
||||||
3f46a: 51f1 mov.l @(4, r15), r1
|
|
||||||
3f46c: 622c extu.b r2, r2
|
|
||||||
3f46e: 2f42 mov.l r4, @r15
|
|
||||||
|
|
||||||
# Loads the key row to r0.
|
|
||||||
3f470: 8411 mov.b @(1, r1), r0
|
|
||||||
3f472: 600c extu.b r0, r0
|
|
||||||
# Shifts it right within r6 and tests the row's lower bit.
|
|
||||||
3f474: 6603 mov r0, r6
|
|
||||||
3f476: 4621 shar r6
|
|
||||||
3f478: c801 tst #1, r0
|
|
||||||
|
|
||||||
# Shift r5 by its column number.
|
|
||||||
# If the row's lower bit was 1, then also shift r5 by 8 (because there are two
|
|
||||||
# rows for each word).
|
|
||||||
3f47a: 8d01 bt/s <3f480>
|
|
||||||
3f47c: 452d shld r2, r5
|
|
||||||
3f47e: 4518 shll8 r5
|
|
||||||
|
|
||||||
# Loads the bottom stack address to r1.
|
|
||||||
3f480: 61f2 mov.l @r15, r1
|
|
||||||
# Erases the even/odd bit of the row in r6 (because the KEYSC data is read in
|
|
||||||
# words so we don't want to get on a byte offset, and the lower bit has
|
|
||||||
# already been handled when shifting r5).
|
|
||||||
3f482: 4600 shll r6
|
|
||||||
|
|
||||||
# Loads the final mask into r2.
|
|
||||||
3f484: 625d extu.w r5, r2
|
|
||||||
3f486: 6063 mov r6, r0
|
|
||||||
# Tests whether the key represented by the parameter matrixcode is pressed.
|
|
||||||
3f488: 041d mov.w @(r0, r1), r4
|
|
||||||
3f48a: 2428 tst r2, r4
|
|
||||||
# Returns the KEYSC bit, that is 1 if the key was pressed, 0 otherwise.
|
|
||||||
3f48c: 0029 movt r0
|
|
||||||
3f48e: ca01 xor #1, r0
|
|
||||||
3f490: 000b rts
|
|
||||||
3f492: 7f08 add #8, r15
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Subroutine call at <3fa12>
|
|
||||||
- Sets the keyboard interrupt priority to 13.
|
|
||||||
- Clears the keyboard interrupt mask.
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Calls redirects to 8003 e28a.
|
|
||||||
|
|
||||||
3e28a: d475 mov.l #0xa4080014, r4
|
|
||||||
3e28c: 9753 mov.w #0x0fff, r7
|
|
||||||
3e28e: 9253 mov.w #0xd000, r2
|
|
||||||
3e290: e580 mov #-128, r5
|
|
||||||
3e292: 6141 mov.w @r4, r1
|
|
||||||
3e294: 2179 and r7, r1
|
|
||||||
3e296: 212b or r2, r1
|
|
||||||
3e298: 2411 mov.w r1, @r4
|
|
||||||
3e29a: d472 mov.l <3e464>(#0xa40800d4), r4
|
|
||||||
3e29c: 000b rts
|
|
||||||
3e29e: 2450 mov.b r5, @r4
|
|
|
@ -1,558 +0,0 @@
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Syscall information
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Syscall id: 0x24a
|
|
||||||
Syscall address: 0x8003fa28
|
|
||||||
|
|
||||||
r4 {short *matrixcode}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Stack
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
#1
|
|
||||||
pr
|
|
||||||
r8
|
|
||||||
r9
|
|
||||||
r10
|
|
||||||
r11
|
|
||||||
r12
|
|
||||||
r13
|
|
||||||
r14
|
|
||||||
------ Bottom
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Variables
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
r14 Current row
|
|
||||||
r13
|
|
||||||
r12
|
|
||||||
r11 Delay routine address
|
|
||||||
r10 1 << r14 ? Could be the data register mask.
|
|
||||||
r9 Current column.
|
|
||||||
r8 {short *matrixcode}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Syscall code
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
# Saves r8-r14 to the stack. Loads data:
|
|
||||||
# r13 = 8, r12 = 1, r11 = 8003 e47a, r10 = 1, r9 = 0, r8 = {short *matrixcode}.
|
|
||||||
3fa28: 2fe6 mov.l r14, @-r15
|
|
||||||
3fa2a: 2fd6 mov.l r13, @-r15
|
|
||||||
3fa2c: 2fc6 mov.l r12, @-r15
|
|
||||||
3fa2e: ed08 mov #8, r13
|
|
||||||
3fa30: 2fb6 mov.l r11, @-r15
|
|
||||||
3fa32: ec01 mov #1, r12
|
|
||||||
3fa34: 2fa6 mov.l r10, @-r15
|
|
||||||
3fa36: 6ac3 mov r12, r10
|
|
||||||
3fa38: 2f96 mov.l r9, @-r15
|
|
||||||
3fa3a: e900 mov #0, r9
|
|
||||||
3fa3c: db22 mov.l #0x8003e47a, r11
|
|
||||||
3fa3e: 2f86 mov.l r8, @-r15
|
|
||||||
3fa40: 4f22 sts.l pr, @-r15
|
|
||||||
3fa42: 6843 mov r4, r8
|
|
||||||
|
|
||||||
# r14 = 0, pushes #1 on the stack and branches to <3fb1a>. This branch does
|
|
||||||
# nothing if r14 is lower than 12 (thus here it does nothing).
|
|
||||||
3fa44: 7ffc add #-4, r15
|
|
||||||
3fa46: 2fc2 mov.l r12, @r15
|
|
||||||
3fa48: a067 bra <3fb1a>
|
|
||||||
3fa4a: 6e93 mov r9, r14
|
|
||||||
|
|
||||||
# Sets gbr = a400 0100: area where the port control registers are located.
|
|
||||||
3fa4c: d21f mov.l #0xa4000100, r2
|
|
||||||
3fa4e: 421e ldc r2, gbr
|
|
||||||
|
|
||||||
# B_CTRL = h'aaaa
|
|
||||||
3fa50: d01f mov.l #0x0000aaaa, r0
|
|
||||||
3fa52: c101 mov.w r0, @(2, gbr)
|
|
||||||
|
|
||||||
# M_CTRL = h'__55
|
|
||||||
3fa54: c50c mov.w @(24, gbr), r0
|
|
||||||
3fa56: d31f mov.l #0x0000ff00, r3
|
|
||||||
3fa58: 2039 and r3, r0
|
|
||||||
3fa5a: cbaa or #0x55, r0
|
|
||||||
3fa5c: c10c mov.w r0, @(24, gbr)
|
|
||||||
|
|
||||||
# Delay.
|
|
||||||
3fa5e: 4b0b jsr @r11
|
|
||||||
3fa60: e402 mov #2, r4
|
|
||||||
|
|
||||||
# Loads 801b e65c as a data segment base. It contains words in this order:
|
|
||||||
# aaa9 aaa6 aa9a aa6a ... 9aaa 6aaa 00a9 00a6 009a 006a.
|
|
||||||
# These words are the B_CTRL/M_CTRL values.
|
|
||||||
# Loads the r14-th word into B_CTRL (r14 <= 8) or the lowest byte of M_CTRL
|
|
||||||
# (r14 > 8). The row-to-check pin is configured as output, and all the others
|
|
||||||
# are configured as inputs.
|
|
||||||
3fa62: 3ed3 cmp/ge r13, r14
|
|
||||||
3fa64: d41c mov.l #0x801be65c, r4
|
|
||||||
3fa66: 8904 bt <3fa72>
|
|
||||||
3fa68: 60e3 mov r14, r0
|
|
||||||
3fa6a: 4000 shll r0
|
|
||||||
3fa6c: 004d mov.w @(r0, r4), r0
|
|
||||||
3fa6e: a009 bra <3fa84>
|
|
||||||
3fa70: c101 mov.w r0, @(2, gbr)
|
|
||||||
3fa72: 60e3 mov r14, r0
|
|
||||||
3fa74: 4000 shll r0
|
|
||||||
3fa76: 004d mov.w @(r0, r4), r0
|
|
||||||
3fa78: 6203 mov r0, r2
|
|
||||||
3fa7a: c50c mov.w @(24, gbr), r0
|
|
||||||
3fa7c: d315 mov.l #0x0000ff00, r3
|
|
||||||
3fa7e: 2039 and r3, r0
|
|
||||||
3fa80: 202b or r2, r0
|
|
||||||
3fa82: c10c mov.w r0, @(24, gbr)
|
|
||||||
|
|
||||||
# Delay.
|
|
||||||
3fa84: 4b0b jsr @r11
|
|
||||||
3fa86: e402 mov #2, r4
|
|
||||||
|
|
||||||
# Loads the port data registers section into gbr.
|
|
||||||
3fa88: d314 mov.l #0xa4000120, r3
|
|
||||||
3fa8a: 431e ldc r3, gbr
|
|
||||||
3fa8c: 3ed3 cmp/ge r13, r14
|
|
||||||
3fa8e: 8d27 bt/s <3fae0>
|
|
||||||
|
|
||||||
# When the row-to-check is lower than or equal to 8.
|
|
||||||
# Writes ~r10 to B_DATA. Sets M_DATA to 0x-f.
|
|
||||||
3fa90: 64a7 not r10, r4
|
|
||||||
3fa92: 6043 mov r4, r0
|
|
||||||
3fa94: c002 mov.b r0, @(2, gbr)
|
|
||||||
3fa96: c418 mov.b @(24, gbr), r0
|
|
||||||
3fa98: c9f0 and #0xf0, r0
|
|
||||||
3fa9a: a026 bra <3faea>
|
|
||||||
3fa9c: cb0f or #15, r0
|
|
||||||
|
|
||||||
3fa9e: 0000 .word 0x0000
|
|
||||||
3faa0: 801b mov.b r0, @(11, r1)
|
|
||||||
3faa2: e6dc mov #-36, r6
|
|
||||||
3faa4: 8006 mov.b r0, @(6, r0)
|
|
||||||
3faa6: 9088 mov.w <3fbba>(#0xc10c), r0
|
|
||||||
3faa8: 8002 mov.b r0, @(2, r0)
|
|
||||||
3faaa: 4e4e ldc r14, spc
|
|
||||||
3faac: 8800 cmp/eq #0, r0
|
|
||||||
3faae: 77ed add #-19, r7
|
|
||||||
3fab0: 8003 mov.b r0, @(3, r0)
|
|
||||||
3fab2: d446 mov.l <3fbcc>(#0x004da007), r4
|
|
||||||
3fab4: 8003 mov.b r0, @(3, r0)
|
|
||||||
3fab6: da54 mov.l <3fc08>(#0x0000aaaa), r10
|
|
||||||
3fab8: 8003 mov.b r0, @(3, r0)
|
|
||||||
3faba: de9c mov.l <3fd2c>(#0xee00eb0a), r14
|
|
||||||
3fabc: 8006 mov.b r0, @(6, r0)
|
|
||||||
3fabe: 90d8 mov.w <3fc72>(#0x421e), r0
|
|
||||||
3fac0: 8006 mov.b r0, @(6, r0)
|
|
||||||
3fac2: 2ed8 tst r13, r14
|
|
||||||
3fac4: 8800 cmp/eq #0, r0
|
|
||||||
3fac6: 7054 add #84, r0
|
|
||||||
|
|
||||||
# Data segment.
|
|
||||||
3fac8: 8003 e47a
|
|
||||||
3facc: a400 0100
|
|
||||||
3fad0: 0000 aaaa
|
|
||||||
3fad4: 0000 ff00
|
|
||||||
3fad8: 801b e65c
|
|
||||||
3fadc: a400 0120
|
|
||||||
|
|
||||||
# When the row-to-check is strictly greater than 8.
|
|
||||||
# Writes 0xff to B_DATA, and ~r10 to M_DATA.
|
|
||||||
3fae0: 908e mov.w <3fc00>(#0x00ff), r0
|
|
||||||
3fae2: c002 mov.b r0, @(2, gbr)
|
|
||||||
3fae4: c418 mov.b @(24, gbr), r0
|
|
||||||
3fae6: c9f0 and #0xf0, r0
|
|
||||||
3fae8: 204b or r4, r0
|
|
||||||
|
|
||||||
# Delay.
|
|
||||||
3faea: c018 mov.b r0, @(24, gbr)
|
|
||||||
3faec: 4b0b jsr @r11
|
|
||||||
3faee: e402 mov #2, r4
|
|
||||||
|
|
||||||
# Loads result in r5, as ~A_DATA.
|
|
||||||
3faf0: c400 mov.b @(0, gbr), r0
|
|
||||||
3faf2: 6507 not r0, r5
|
|
||||||
|
|
||||||
# r9 = 0 the first time. Compares r12 with the result. If none of the bits of
|
|
||||||
# r12 are set in checked row, branches to <3fb04>.
|
|
||||||
3faf4: 6493 mov r9, r4
|
|
||||||
3faf6: 635c extu.b r5, r3
|
|
||||||
3faf8: 23c8 tst r12, r3
|
|
||||||
3fafa: 8903 bt <3fb04>
|
|
||||||
|
|
||||||
# r9 seems to be the key column and r14 the row.
|
|
||||||
3fafc: 2840 mov.b r4, @r8
|
|
||||||
3fafe: 60e3 mov r14, r0
|
|
||||||
3fb00: a00f bra <3fb22>
|
|
||||||
3fb02: 8081 mov.b r0, @(1, r8)
|
|
||||||
|
|
||||||
3fb04: 655c extu.b r5, r5
|
|
||||||
3fb06: 4501 shlr r5
|
|
||||||
3fb08: 7401 add #1, r4
|
|
||||||
3fb0a: 34d3 cmp/ge r13, r4
|
|
||||||
3fb0c: 8bf3 bf <3faf6>
|
|
||||||
3fb0e: 4a00 shll r10
|
|
||||||
3fb10: 60e3 mov r14, r0
|
|
||||||
3fb12: 8807 cmp/eq #7, r0
|
|
||||||
3fb14: 8f01 bf/s <3fb1a>
|
|
||||||
3fb16: 7e01 add #1, r14
|
|
||||||
3fb18: 6ac3 mov r12, r10
|
|
||||||
3fb1a: e30c mov #12, r3
|
|
||||||
3fb1c: 3e33 cmp/ge r3, r14
|
|
||||||
3fb1e: 8b95 bf <3fa4c>
|
|
||||||
3fb20: 2f92 mov.l r9, @r15
|
|
||||||
|
|
||||||
# Ends the procedure and restores everything ?
|
|
||||||
|
|
||||||
# B_CTRL = 0xaaaa. M_CTRL = 0x--aa. Delay.
|
|
||||||
3fb22: d338 mov.l <3fc04>(#0xa4000100), r3
|
|
||||||
3fb24: d038 mov.l <3fc08>(#0x0000aaaa), r0
|
|
||||||
3fb26: 431e ldc r3, gbr
|
|
||||||
3fb28: c101 mov.w r0, @(2, gbr)
|
|
||||||
3fb2a: c50c mov.w @(24, gbr), r0
|
|
||||||
3fb2c: d237 mov.l <3fc0c>(#0x0000ff00), r2
|
|
||||||
3fb2e: 2029 and r2, r0
|
|
||||||
3fb30: cbaa or #0xaa, r0
|
|
||||||
3fb32: c10c mov.w r0, @(24, gbr)
|
|
||||||
3fb34: 4b0b jsr @r11
|
|
||||||
3fb36: e402 mov #2, r4
|
|
||||||
|
|
||||||
# B_CTRL = 0x5555. M_CTRL = 0x--55. Delay.
|
|
||||||
3fb38: 9063 mov.w <3fc02>(#0x5555), r0
|
|
||||||
3fb3a: c101 mov.w r0, @(2, gbr)
|
|
||||||
3fb3c: c50c mov.w @(24, gbr), r0
|
|
||||||
3fb3e: d333 mov.l <3fc0c>(#0x0000ff00), r3
|
|
||||||
3fb40: 2039 and r3, r0
|
|
||||||
3fb42: cb55 or #0x55, r0
|
|
||||||
3fb44: c10c mov.w r0, @(24, gbr)
|
|
||||||
3fb46: 4b0b jsr @r11
|
|
||||||
3fb48: e402 mov #2, r4
|
|
||||||
|
|
||||||
# B_DATA = 0x00. M_DATA = 0x-0.
|
|
||||||
3fb4a: e000 mov #0, r0
|
|
||||||
3fb4c: d230 mov.l <3fc10>(#0xa4000120), r2
|
|
||||||
3fb4e: 421e ldc r2, gbr
|
|
||||||
3fb50: c002 mov.b r0, @(2, gbr)
|
|
||||||
3fb52: c418 mov.b @(24, gbr), r0
|
|
||||||
3fb54: c9f0 and #0xf0, r0
|
|
||||||
3fb56: c018 mov.b r0, @(24, gbr)
|
|
||||||
|
|
||||||
# Returns the value at the top of the stack (originally 1).
|
|
||||||
3fb58: 60f2 mov.l @r15, r0
|
|
||||||
3fb5a: 7f04 add #4, r15
|
|
||||||
3fb5c: 4f26 lds.l @r15+, pr
|
|
||||||
3fb5e: 68f6 mov.l @r15+, r8
|
|
||||||
3fb60: 69f6 mov.l @r15+, r9
|
|
||||||
3fb62: 6af6 mov.l @r15+, r10
|
|
||||||
3fb64: 6bf6 mov.l @r15+, r11
|
|
||||||
3fb66: 6cf6 mov.l @r15+, r12
|
|
||||||
3fb68: 6df6 mov.l @r15+, r13
|
|
||||||
3fb6a: 000b rts
|
|
||||||
3fb6c: 6ef6 mov.l @r15+, r14
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
First subroutine
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
# Go back to <3fa4c> (first call to this subroutine) until r14 reaches 12.
|
|
||||||
3fb1a: e30c mov #12, r3
|
|
||||||
3fb1c: 3e33 cmp/ge r3, r14
|
|
||||||
3fb1e: 8b95 bf <3fa4c>
|
|
||||||
|
|
||||||
3fb20: 2f92 mov.l r9, @r15
|
|
||||||
3fb22: d338 mov.l <3fc04>(#0xa4000100), r3
|
|
||||||
3fb24: d038 mov.l <3fc08>(#0x0000aaaa), r0
|
|
||||||
3fb26: 431e ldc r3, gbr
|
|
||||||
3fb28: c101 mov.w r0, @(2, gbr)
|
|
||||||
3fb2a: c50c mov.w @(24, gbr), r0
|
|
||||||
3fb2c: d237 mov.l <3fc0c>(#0x0000ff00), r2
|
|
||||||
3fb2e: 2029 and r2, r0
|
|
||||||
3fb30: cbaa or #-86, r0
|
|
||||||
3fb32: c10c mov.w r0, @(24, gbr)
|
|
||||||
3fb34: 4b0b jsr @r11
|
|
||||||
3fb36: e402 mov #2, r4
|
|
||||||
3fb38: 9063 mov.w <3fc02>(#0x5555), r0
|
|
||||||
3fb3a: c101 mov.w r0, @(2, gbr)
|
|
||||||
3fb3c: c50c mov.w @(24, gbr), r0
|
|
||||||
3fb3e: d333 mov.l <3fc0c>(#0x0000ff00), r3
|
|
||||||
3fb40: 2039 and r3, r0
|
|
||||||
3fb42: cb55 or #85, r0
|
|
||||||
3fb44: c10c mov.w r0, @(24, gbr)
|
|
||||||
3fb46: 4b0b jsr @r11
|
|
||||||
3fb48: e402 mov #2, r4
|
|
||||||
3fb4a: e000 mov #0, r0
|
|
||||||
3fb4c: d230 mov.l <3fc10>(#0xa4000120), r2
|
|
||||||
3fb4e: 421e ldc r2, gbr
|
|
||||||
3fb50: c002 mov.b r0, @(2, gbr)
|
|
||||||
3fb52: c418 mov.b @(24, gbr), r0
|
|
||||||
3fb54: c9f0 and #-16, r0
|
|
||||||
3fb56: c018 mov.b r0, @(24, gbr)
|
|
||||||
3fb58: 60f2 mov.l @r15, r0
|
|
||||||
3fb5a: 7f04 add #4, r15
|
|
||||||
3fb5c: 4f26 lds.l @r15+, pr
|
|
||||||
3fb5e: 68f6 mov.l @r15+, r8
|
|
||||||
3fb60: 69f6 mov.l @r15+, r9
|
|
||||||
3fb62: 6af6 mov.l @r15+, r10
|
|
||||||
3fb64: 6bf6 mov.l @r15+, r11
|
|
||||||
3fb66: 6cf6 mov.l @r15+, r12
|
|
||||||
3fb68: 6df6 mov.l @r15+, r13
|
|
||||||
3fb6a: 000b rts
|
|
||||||
3fb6c: 6ef6 mov.l @r15+, r14
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Second subroutine
|
|
||||||
Just a delay ! If also configures IRQ0.
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
Stack:
|
|
||||||
gbr
|
|
||||||
macl
|
|
||||||
pr
|
|
||||||
r14
|
|
||||||
------ Bottom
|
|
||||||
|
|
||||||
# Saves r14, pr, macl and gbr to the stack. Saves parameter (r4 = 2) to r14.
|
|
||||||
# Loads data: r2 = 8006 31dc, r5 = 1 (parameter 'set' for syscall 0x3ed).
|
|
||||||
3e47a: 0312 stc gbr, r3
|
|
||||||
3e47c: d235 mov.l <3e554>(#0x800631dc), r2
|
|
||||||
3e47e: e501 mov #1, r5
|
|
||||||
3e480: 2fe6 mov.l r14, @-r15
|
|
||||||
3e482: 6e43 mov r4, r14
|
|
||||||
3e484: 4f22 sts.l pr, @-r15
|
|
||||||
3e486: 4f12 sts.l macl, @-r15
|
|
||||||
3e488: 7ffc add #-4, r15
|
|
||||||
3e48a: 2f32 mov.l r3, @r15
|
|
||||||
|
|
||||||
# Sets the "watchdog occupied" status in the RAM interrupt status byte.
|
|
||||||
3e48c: 420b jsr @r2
|
|
||||||
3e48e: e410 mov #16, r4
|
|
||||||
|
|
||||||
# Ensures 1 <= r14 <= 40 by setting r14 = 1 or 40 if needed.
|
|
||||||
# Here r14 is always 2.
|
|
||||||
3e490: 4e15 cmp/pl r14
|
|
||||||
3e492: 8d01 bt/s <3e498>
|
|
||||||
3e494: e428 mov #40, r4
|
|
||||||
3e496: ee01 mov #1, r14
|
|
||||||
3e498: 3e47 cmp/gt r4, r14
|
|
||||||
3e49a: 8b00 bf <3e49e>
|
|
||||||
3e49c: 6e43 mov r4, r14
|
|
||||||
|
|
||||||
# r4 = ~((r14 * 92) >> 4) on a single byte, which is 244 when r14 = 2.
|
|
||||||
# 256 - r4 will be used as a delay for the watchdog timer.
|
|
||||||
# Sets gbr to 0xfffffee0 and disables the watchdog interrupt.
|
|
||||||
3e49e: e45c mov #92, r4
|
|
||||||
3e4a0: 924b mov.w <3e53a>(#0xfee0), r2
|
|
||||||
3e4a2: e3fc mov #-4, r3
|
|
||||||
3e4a4: 0e47 mul.l r4, r14
|
|
||||||
3e4a6: 421e ldc r2, gbr
|
|
||||||
3e4a8: 041a sts macl, r4
|
|
||||||
3e4aa: 443c shad r3, r4
|
|
||||||
3e4ac: 6447 not r4, r4
|
|
||||||
3e4ae: 644c extu.b r4, r4
|
|
||||||
3e4b0: c502 mov.w @(4, gbr), r0
|
|
||||||
3e4b2: 9343 mov.w <3e53c>(#0x0fff), r3
|
|
||||||
3e4b4: 2039 and r3, r0
|
|
||||||
3e4b6: c102 mov.w r0, @(4, gbr)
|
|
||||||
|
|
||||||
# Loads the watchdog module base address into gbr. Resets everything in the
|
|
||||||
# watchdog configuration. Then loads r4 (here 244) to the counter, sets the
|
|
||||||
# frequency at Po/256, starts the timer and waits until it overflows.
|
|
||||||
# This is probably just a way of delaying port usage.
|
|
||||||
3e4b8: d027 mov.l <3e558>(#0x0000a500), r0
|
|
||||||
3e4ba: e180 mov #-128, r1
|
|
||||||
3e4bc: 411e ldc r1, gbr
|
|
||||||
3e4be: c103 mov.w r0, @(6, gbr)
|
|
||||||
3e4c0: 903d mov.w <3e53e>(#0x5a00), r0
|
|
||||||
3e4c2: 204b or r4, r0
|
|
||||||
3e4c4: c102 mov.w r0, @(4, gbr)
|
|
||||||
3e4c6: d025 mov.l <3e55c>(#0x0000a505), r0
|
|
||||||
3e4c8: c103 mov.w r0, @(6, gbr)
|
|
||||||
3e4ca: d025 mov.l <3e560>(#0x0000a585), r0
|
|
||||||
3e4cc: c103 mov.w r0, @(6, gbr)
|
|
||||||
3e4ce: e408 mov #8, r4
|
|
||||||
3e4d0: c406 mov.b @(6, gbr), r0
|
|
||||||
3e4d2: 600c extu.b r0, r0
|
|
||||||
3e4d4: 2048 tst r4, r0
|
|
||||||
3e4d6: 89fb bt <3e4d0>
|
|
||||||
|
|
||||||
# Resets the overflow flag, then resets the whole configuration.
|
|
||||||
3e4d8: c406 mov.b @(6, gbr), r0
|
|
||||||
3e4da: 600c extu.b r0, r0
|
|
||||||
3e4dc: d31e mov.l <3e558>(#0x0000a500), r3
|
|
||||||
3e4de: c9f7 and #0xf7, r0
|
|
||||||
3e4e0: 203b or r3, r0
|
|
||||||
3e4e2: c103 mov.w r0, @(6, gbr)
|
|
||||||
3e4e4: 6033 mov r3, r0
|
|
||||||
3e4e6: c103 mov.w r0, @(6, gbr)
|
|
||||||
|
|
||||||
# Resets the counter.
|
|
||||||
3e4e8: 9029 mov.w <3e53e>(#0x5a00), r0
|
|
||||||
3e4ea: c102 mov.w r0, @(4, gbr)
|
|
||||||
|
|
||||||
# Unsets the "watchdog occupied" bit in the RAM interrupt status byte.
|
|
||||||
3e4ec: d219 mov.l <3e554>(#0x800631dc), r2
|
|
||||||
3e4ee: e500 mov #0, r5
|
|
||||||
3e4f0: 420b jsr @r2
|
|
||||||
3e4f2: e410 mov #16, r4
|
|
||||||
|
|
||||||
# Configures IRQ0 if possible (that is, if both the watchdog and the SD card
|
|
||||||
# are idle).
|
|
||||||
3e4f4: d31b mov.l <3e564>(#0x8003dbec), r3
|
|
||||||
3e4f6: 430b jsr @r3
|
|
||||||
3e4f8: 0009 nop
|
|
||||||
|
|
||||||
# Un-stacks everything and returns.
|
|
||||||
3e4fa: 62f2 mov.l @r15, r2
|
|
||||||
3e4fc: 421e ldc r2, gbr
|
|
||||||
3e4fe: 7f04 add #4, r15
|
|
||||||
3e500: 4f16 lds.l @r15+, macl
|
|
||||||
3e502: 4f26 lds.l @r15+, pr
|
|
||||||
3e504: 000b rts
|
|
||||||
3e506: 6ef6 mov.l @r15+, r14
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------
|
|
||||||
Third subroutine
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
# Erases the lowest bit in an unknown byte (probably an extension of the
|
|
||||||
# interrupt status byte). Returns if this bit was 0.
|
|
||||||
3dbec: 4f22 sts.l pr, @-r15
|
|
||||||
3dbee: e501 mov #1, r5
|
|
||||||
3dbf0: d349 mov.l <3dd18>(#0x80063236), r3
|
|
||||||
3dbf2: 430b jsr @r3
|
|
||||||
3dbf4: 6453 mov r5, r4
|
|
||||||
3dbf6: 8801 cmp/eq #1, r0
|
|
||||||
3dbf8: 8b07 bf <3dc0a>
|
|
||||||
|
|
||||||
# Checks if the watchdog timer is occupied, or if the SD-card is busy (?).
|
|
||||||
# If any of them is in use, aborts. Otherwise controls is transferred to the
|
|
||||||
# extract below, as if called directly.
|
|
||||||
# Probably the SD-card has something to do with generating IRQ0 interrupts.
|
|
||||||
3dbfa: e500 mov #0, r5
|
|
||||||
3dbfc: d347 mov.l <3dd1c>(#0x800631f6), r3
|
|
||||||
3dbfe: 430b jsr @r3
|
|
||||||
3dc00: e418 mov #24, r4
|
|
||||||
3dc02: 2008 tst r0, r0
|
|
||||||
3dc04: 8b01 bf <3dc0a>
|
|
||||||
3dc06: af82 bra <3db0e>
|
|
||||||
3dc08: 4f26 lds.l @r15+, pr
|
|
||||||
|
|
||||||
# Returns.
|
|
||||||
3dc0a: 4f26 lds.l @r15+, pr
|
|
||||||
3dc0c: 000b rts
|
|
||||||
3dc0e: 0009 nop
|
|
||||||
|
|
||||||
Here's the extract of code that takes control when the interrupt conditions
|
|
||||||
required by the main procedure are fulfilled.
|
|
||||||
It configures IRQ0 interrupts.
|
|
||||||
|
|
||||||
# Disables IRQ0 interrupt in IPRC.
|
|
||||||
3db0e: d32d mov.l <3dbc4>(#0xa4000000), r3
|
|
||||||
3db10: 431e ldc r3, gbr
|
|
||||||
3db12: c50b mov.w @(22, gbr), r0
|
|
||||||
3db14: d22e mov.l <3dbd0>(#0x0000fff0), r2
|
|
||||||
3db16: 2029 and r2, r0
|
|
||||||
3db18: c10b mov.w r0, @(22, gbr)
|
|
||||||
|
|
||||||
# Sets PTH0 to 'other functions' mode, which is IRQ0 and IRL0 input for the
|
|
||||||
# interrupt controller.
|
|
||||||
3db1a: d12e mov.l <3dbd4>(#0xa4000100), r1
|
|
||||||
3db1c: 411e ldc r1, gbr
|
|
||||||
3db1e: c507 mov.w @(14, gbr), r0
|
|
||||||
3db20: d32d mov.l <3dbd8>(#0x0000fffc), r3
|
|
||||||
3db22: 2039 and r3, r0
|
|
||||||
3db24: c107 mov.w r0, @(14, gbr)
|
|
||||||
|
|
||||||
# Sets IRQ0 detection mode to low level input in ICR1.
|
|
||||||
3db26: d227 mov.l <3dbc4>(#0xa4000000), r2
|
|
||||||
3db28: 421e ldc r2, gbr
|
|
||||||
3db2a: c508 mov.w @(16, gbr), r0
|
|
||||||
3db2c: 2039 and r3, r0
|
|
||||||
3db2e: cb02 or #2, r0
|
|
||||||
3db30: c108 mov.w r0, @(16, gbr)
|
|
||||||
|
|
||||||
# Clears the IRQ0 interrupt flag.
|
|
||||||
3db32: c404 mov.b @(4, gbr), r0
|
|
||||||
3db34: c9fe and #-2, r0
|
|
||||||
3db36: c004 mov.b r0, @(4, gbr)
|
|
||||||
|
|
||||||
# Enables IRQ0 interrupts with priority 13 in IPRC.
|
|
||||||
3db38: c50b mov.w @(22, gbr), r0
|
|
||||||
3db3a: d125 mov.l <3dbd0>(#0x0000fff0), r1
|
|
||||||
3db3c: 2019 and r1, r0
|
|
||||||
3db3e: cb0d or #13, r0
|
|
||||||
3db40: c10b mov.w r0, @(22, gbr)
|
|
||||||
|
|
||||||
# Returns 1.
|
|
||||||
3db42: 000b rts
|
|
||||||
3db44: e001 mov #1, r0
|
|
||||||
|
|
||||||
Here is the first subroutine, that handles an unknown byte at 0x8800713d.
|
|
||||||
This byte may be an extension of the interrupt status byte.
|
|
||||||
|
|
||||||
Returns 1 if a bit in the unknown byte matches the mask r4.
|
|
||||||
Also, if r5 = 1, erases the bits according to the mask.
|
|
||||||
|
|
||||||
# Sets r6 to 1 if a bit in the mask is set in the unknown byte, 0 otherwise.
|
|
||||||
# Also sets r0 = r5.
|
|
||||||
63236: 624c extu.b r4, r2
|
|
||||||
63238: d72a mov.l <632e4>(#0x8800713d), r7
|
|
||||||
6323a: 6370 mov.b @r7, r3
|
|
||||||
6323c: 633c extu.b r3, r3
|
|
||||||
6323e: 2328 tst r2, r3
|
|
||||||
63240: 8f02 bf/s <63248>
|
|
||||||
63242: 6053 mov r5, r0
|
|
||||||
63244: a001 bra <6324a>
|
|
||||||
63246: e600 mov #0, r6
|
|
||||||
63248: e601 mov #1, r6
|
|
||||||
|
|
||||||
# Uses the mask comparison result as return value. If the operation is 0, then
|
|
||||||
# stop there.
|
|
||||||
6324a: 8801 cmp/eq #1, r0
|
|
||||||
6324c: 8f04 bf/s <63258>
|
|
||||||
6324e: 6063 mov r6, r0
|
|
||||||
|
|
||||||
# Otherwise, erase the mask in the unknown byte.
|
|
||||||
63250: 6370 mov.b @r7, r3
|
|
||||||
63252: 6447 not r4, r4
|
|
||||||
63254: 2349 and r4, r3
|
|
||||||
63256: 2730 mov.b r3, @r7
|
|
||||||
63258: 000b rts
|
|
||||||
6325a: 0009 nop
|
|
||||||
|
|
||||||
Here is the second subroutine.
|
|
||||||
It does exactly the same as the first, except that it uses the common
|
|
||||||
interrupt status byte at 0x8800713c.
|
|
||||||
It's the syscall 0x3ee.
|
|
||||||
|
|
||||||
631f6: 624c extu.b r4, r2
|
|
||||||
631f8: d739 mov.l <632e0>(#0x8800713c), r7
|
|
||||||
631fa: 6370 mov.b @r7, r3
|
|
||||||
631fc: 633c extu.b r3, r3
|
|
||||||
631fe: 2328 tst r2, r3
|
|
||||||
63200: 8f02 bf/s <63208>
|
|
||||||
63202: 6053 mov r5, r0
|
|
||||||
63204: a001 bra <6320a>
|
|
||||||
63206: e600 mov #0, r6
|
|
||||||
63208: e601 mov #1, r6
|
|
||||||
6320a: 8801 cmp/eq #1, r0
|
|
||||||
6320c: 8f04 bf/s <63218>
|
|
||||||
6320e: 6063 mov r6, r0
|
|
||||||
63210: 6370 mov.b @r7, r3
|
|
||||||
63212: 6447 not r4, r4
|
|
||||||
63214: 2349 and r4, r3
|
|
||||||
63216: 2730 mov.b r3, @r7
|
|
||||||
63218: 000b rts
|
|
||||||
6321a: 0009 nop
|
|
|
@ -1,91 +0,0 @@
|
||||||
fxos lephe$ fxos disasm -s 0x24b sh7305.fls -l 200
|
|
||||||
Syscall table: 0x801cdd84
|
|
||||||
Syscall id: 0x24b
|
|
||||||
Syscall address: 0x4cfc0380
|
|
||||||
|
|
||||||
===================================
|
|
||||||
|
|
||||||
Stack state:
|
|
||||||
r4 s1: Parameter of call located at <e>
|
|
||||||
pr
|
|
||||||
r14
|
|
||||||
r13
|
|
||||||
------ Bottom
|
|
||||||
|
|
||||||
===================================
|
|
||||||
|
|
||||||
Missing information:
|
|
||||||
|
|
||||||
- Function of <fffffe44> (negative offset relative to <e>)
|
|
||||||
- Function of <fffffdde> (negative offset relative to <3e>, does not seem to be
|
|
||||||
the same as <fffffe44>, curiously)
|
|
||||||
- Function of <8003e8c8> (probably syscall, but not found)
|
|
||||||
|
|
||||||
===================================
|
|
||||||
|
|
||||||
#
|
|
||||||
# Initialization.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Saves the registers.
|
|
||||||
0: 2fd6 mov.l r13, @-r15
|
|
||||||
2: 2fe6 mov.l r14, @-r15
|
|
||||||
4: 4f22 sts.l pr, @-r15
|
|
||||||
6: 7ffc add #-4, r15
|
|
||||||
8: 2f42 mov.l r4, @r15
|
|
||||||
|
|
||||||
# Loads 1 into r13. If jump at <16> is performed, r13 is changed to 0.
|
|
||||||
a: ed01 mov #1, r13
|
|
||||||
# r14 gets decremented whenever call at <e> is looped (considering the
|
|
||||||
# documentation, it is probably the number of tries before the function
|
|
||||||
# gives up).
|
|
||||||
c: ee05 mov #5, r14
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Main loop, calls <fffffe44>. No more than initial_r14 turns.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Calls <fffffe44>(r4).
|
|
||||||
e: bf19 bsr <fffffe44>
|
|
||||||
10: 64f2 mov.l @r15, r4
|
|
||||||
|
|
||||||
# If result != 0, then <1a>, else <24>.
|
|
||||||
12: 2008 tst r0, r0
|
|
||||||
14: 8b01 bf <1a>
|
|
||||||
16: a005 bra <24>
|
|
||||||
18: ed00 mov #0, r13
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# When <fffffe44> returns non-zero, calls <8003e8c8>, decrements r14 and
|
|
||||||
# loops.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Call <fffffe44> returned non-zero (r13 = 1).
|
|
||||||
# Calls <8003e8c8>(10).
|
|
||||||
1a: d22d mov.l <d0>(0x8003e8c8), r2
|
|
||||||
1c: 420b jsr @r2
|
|
||||||
1e: e40a mov #10, r4
|
|
||||||
# Decrementing the number of tries before returning, and looping to <e> if the
|
|
||||||
# number of tries has not been exceeded.
|
|
||||||
20: 4e10 dt r14
|
|
||||||
22: 8bf4 bf <e>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# When <fffffe44> returns zero or the number of tries has been exceeded,
|
|
||||||
# return from the function with the correct value.
|
|
||||||
#
|
|
||||||
|
|
||||||
# Call <fffffe44> returned zero (r13 = 0). Ends the function and returns 1 if
|
|
||||||
# the key is pressed, 0 otherwise.
|
|
||||||
24: 60d3 mov r13, r0
|
|
||||||
26: 7f04 add #4, r15
|
|
||||||
28: 4f26 lds.l @r15+, pr
|
|
||||||
2a: 6ef6 mov.l @r15+, r14
|
|
||||||
2c: 000b rts
|
|
||||||
2e: 6df6 mov.l @r15+, r13
|
|