way too much, including bopti/topti, timers, and more.

This commit is contained in:
lephe 2019-02-21 20:58:38 +01:00
parent ae4e5d83b2
commit 3f7c0a04ad
68 changed files with 2729 additions and 825 deletions

11
.gitignore vendored
View file

@ -1,5 +1,5 @@
# Build directory
build/**
# Build directories
build*/**
# Sublime Text files
*.sublime-project
@ -7,10 +7,3 @@ build/**
# Lots of unordered project notes
notes/**
# Output files
bin/**
# Configuration files
config/**

213
Makefile Executable file → Normal file
View file

@ -1,172 +1,59 @@
#! /usr/bin/make -f
#
# gint project Makefile
#
#---
#
# Build configuration
#
builds := $(wildcard build*)
# Some rules don't care if config files are missing (eg. clean, etc)
cfg := config/Makefile.cfg
-include $(cfg)
# Those that do may use this special dependency
cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg))
cf-fx := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
-fstrict-volatile-bitfields -I include $(cfg_macros)
cf-cg := -m4 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
-fstrict-volatile-bitfields -I include $(cfg_macros)
# On fx9860g, use the sh3eb-elf toolchain; on fxcg50, prefer sh4eb-nofpu-elf
toolchain := $(if $(filter $(cfg_target),fx),sh3eb-elf,sh4eb-nofpu-elf)
# Compiler flags, assembler flags, dependency generation, archiving
cflags := $(cf-$(cfg_target))
sflags := $(cfg_macros)
dflags = -MMD -MT $@ -MF $(@:.o=.d) -MP
arflags :=
#
# File listings
#
# Target file (cfg_target is either "fx" or "cg")
target := bin/libgint-$(cfg_target).a
# Automatic names for object and dependency files
src2obj = build/$(1:src/%=%).o
src2dep = build/$(1:src/%=%).d
# Source files
src := $(shell find src -name '*.[csS]')
src_obj := $(foreach s,$(src),$(call src2obj,$s))
# Files with special handling
# spe := build/version.o src/display/font.bmp
spe :=
spe_obj := build/version.o $(foreach s,$(spe),$(call src2obj,$s))
# All object files
obj := $(src_obj) $(spe_obj)
# Version file
version := config/version
#
# Toolchain
#
gcc = $(toolchain)-gcc
as = $(toolchain)-as
ld = $(toolchain)-ld
ar = $(toolchain)-ar
objcopy = $(toolchain)-objcopy
conv = fxconv
#
# Version management
#
# Retrieve version information
v_file = $(shell cat $(version) | sed 's/[-.]/ /g')
v_type = $(word 1,$(v_file))
v_major = $(word 2,$(v_file))
v_minor = $(word 3,$(v_file))
v_build = $(word 4,$(v_file))
# Create the current version symbol and the new version integer
v_newbuild = $(shell echo $$(($(v_build) + 1)))
v_letter = $(shell echo -n $(v_type) | sed -r 's/^(.).*/\1/')
v_symbol = $(shell printf '0x%02x%01x%01x%04x' "'$(v_letter)'" \
$(v_major) $(v_minor) $(v_build))
#
# Build rules
#
all: $(target)
$(target): $(obj) $(version) | $(dir $(target))
$(call cmd_l,ar,$@) $(ar) -crs $(arflags) $@ $(obj)
# Assembler sources
build/%.s.o: src/%.s build/%.s.d
@ mkdir -p $(dir $@)
$(call cmd_b,as,$<) $(gcc) -c $< -o $@ $(sflags)
build/%.S.o: src/%.S build/%.S.d
@ mkdir -p $(dir $@)
$(call cmd_b,as,$<) $(gcc) -c $< -o $@ $(sflags)
# C sources
build/%.c.o: src/%.c build/%.c.d $(cfgdep)
@ mkdir -p $(dir $@)
$(call cmd_b,gcc,$<) $(gcc) -c $< -o $@ $(dflags) $(cflags)
# Special files
$(call src2obj,src/display/font.bmp): src/display/font.bmp
@ mkdir -p $(dir $@)
$(call cmd_m,fxconv,$<) $(conv) -font $< -n gint_font -o $@
# Version symbol. ld generates a .stack section for unknown reasons; I fall
# back to removing it afterwards.
build/version.o:
@ mkdir -p $(dir $@)
@ echo "_GINT_VERSION = $(v_symbol);" > $@.txt
$(call cmd_b,ld,$@) $(ld) -r -R $@.txt -o $@
#
# Cleaning
#
clean:
@ rm -rf build/
distclean: clean
@ rm -rf bin/
@ rm -f $(config)
#
# Utilities
#
$(version): $(src) $(wildcard include/**/*)
@ echo '$(v_type)-$(v_major).$(v_minor)-$(v_newbuild)' > $@
# Evaluated when a rule requires the configuration file but it doesn't exist
CFGDEP:
@ echo "Configuration file $(cfg) is missing. Have you configured?"
@ echo "See \`./configure --help\` for details."
ifeq "$(builds)" ""
nobuild:
@ echo "error: you don't seem to have any build directory (build*)" >&2
@ echo "" >&2
@ echo "You can configure one like this:" >&2
@ echo " mkdir build && cd build && ../configure [options...]" >&2
@ echo "" >&2
@ false
endif
# Make directories: make conveniently leaves a '/' at the end of $(dir ...)
%/:
@ mkdir -p $@
# Don't try to unlink directories once they're built (that wouldn't work =p)
.PRECIOUS: %/
#
# all targets
#
# Dependency information
-include $(shell [ -d build ] && find build -name *.d)
build/%.d: ;
.PRECIOUS: build/%.d
all-targets := $(foreach b,$(builds),all-$b)
# Do not output full commands by default
VERBOSE ?=
all: $(all-targets)
# Simple command output method
# $1 Program name
# $2 Argument string to display
# $3 Command color
define cmd
@ echo -e "\e[""$3"";1m>\e[0;1m $1\e[0m $2"
$(if $(VERBOSE),,@)
endef
all-build%: build%
@ echo -e "$B::$W Making into $<$N"
@ $(MAKE) --no-print-directory -C $<
# Some pre-colored command kinds: misc, build, link, clean, install
cmd_m = $(call cmd,$1,$2,30)
cmd_b = $(call cmd,$1,$2,32)
cmd_l = $(call cmd,$1,$2,36)
cmd_c = $(call cmd,$1,$2,31)
cmd_i = $(call cmd,$1,$2,33)
#
# install targets
#
install-targets := $(foreach b,$(builds),install-$b)
install: $(install-targets)
install-build%: build%
@ echo -e "$B::$W Installing from $<$N"
@ $(MAKE) --no-print-directory -C $< install
#
# uninstall targets
#
uninstall-targets := $(foreach b,$(builds),uninstall-$b)
uninstall: $(uninstall-targets)
uninstall-build%: build%
@ echo -e "$B::$W Uninstalling from $<$N"
@ $(MAKE) --no-print-directory -C $< uninstall
#
# Coloring tools
#
B = \e[34;1m
W = \e[39;1m
N = \e[0m
.PHONY: nobuild all all-build% install install-build%

160
README.md
View file

@ -1,98 +1,112 @@
gint project
============
# gint project
gint (pronounce 'guin') is a low-level library for fx-9860G calculators. It
provides a mostly free-standing runtime support for the platform, and can be
used to develop add-ins under Linux along with the gcc toolchain (`sh3eb-elf`
target) and the [fxSDK](http://git.planet-casio.com/lephe/fxsdk).
gint (pronounce 'guin') is a development system for Casio fx-9860G II and
fx-CG 50 calculators. It provides a mostly free-standing runtime and is used
to develop add-ins under Linux, along with specialized GCC toolchains and the
[fxSDK](http://git.planet-casio.com/lephe/fxsdk).
gint is free software: you may use it for any purpose, share it, modify it and
share your changes. No credit of any kind is required, though appreciated.
gint is a modular kernel that implements its own drivers for the calculator's
hardware, overriding the operating system and its syscalls. It is a drop-in
replacement from fxlib, with which it is mostly incompatible. gint exposes a
new, richer API to manipulate the hardware and take advantage of the full
capabilities of the machine.
This is free software: you may use it for any purpose, share it, and modify it
as long as you share your changes. Credit is not required, but please let me
know!
**TODO: Update this file for everything related to project organization**
Programming interface
---------------------
## Programming interface
Because of its free-standing design, gint's API provides direct and efficient
access to the low-level MPU features, among which:
* Extensive keyboard control, including replication of the system behavior for
office applications and event-driven decisions for games
* Hardware timers running at over 10 MHz, allowing microsecond-level control
* Unlimited mappings of callbacks to Real-Time Clock events (requires build)
* Access to processor registers for debugging information, determination of
processor speed (overclock is on the TODO list), backlight management...
* Multi-key management with event systems suitable for games
* Hardware timers with sub-millisecond and sub-microsecond resolution
* Fast screen drivers with DMAC on fx-CG 50
* Efficient and user-extendable interrupt management
The library also offers powerful higher-level features:
* A gray engine that works by rapidly swapping monochrome images
* Blazingly fast drawing functions when working with the fxSDK (10 times faster
image rendering that MonochromeLib)
* C Standard functions such as the `printf()` family
* An enhanced versio of the system's GetKey() and GetKeyWait()
* A gray engine that works by rapidly swapping monochrome images on fx-9860G II
* Blazingly fast drawing functions when working with the fxSDK (image rendering
is 10 times faster than MonochromeLib)
* Integrated font management with the fxSDK
* Integration with a Newlib port by Memallox (WIP)
Interrupt handler
-----------------
## Building and installing gint
The interrupt handler is the lowest-level part of the library. It directly
accesses the peripheral modules and workarounds the system to perform keyboard
analyzes directly on hardware or timer management.
You can choose to build gint for fx-9860G II (monochrome calculators, aka
Graph 85 family), fx-CG 50 (color calculators, aka Prizm or Graph 90 family),
or both. There are a few dependencies:
gint does not allow user programs to use their own handlers, but it allows them
to complete the original handler if they want to use interrupts that are not
supported by the library. It is also possible to map various interrupt-driven
events to user-provided callbacks using the API, which is not allowed by the
operating system. This is particularly useful for timers and the Real-Time
Clock (the 16 Hz interrupt can be used to run a physical engine, while the 1 Hz
interrupt can be used in a real-time management game).
Building and installing
-----------------------
To build and install gint, you will need the following components:
* The `sh3eb-elf` toolchain linked somewhere in the PATH
* A suitable GCC toolcahin in the `PATH`. You can absolutely *not* build gint
with your system compiler!
* For fx-9860G II, `sh3eb-elf` is strongly advised
* For fx-CG 50, `sh4eb-nofpu-elf` is slightly better but `sh3eb-elf` is
completely fine
* The [fxSDK](http://git.planet-casio.com/lephe/fxsdk) installed and available
in the PATH
in the PATH. You will need `fxsdk` and `fxconv` to build gint, and if you
intend to develop add-ins for fx-9860G II, you probably want `fxg1a` as well.
The classical way to build gint is to enter a terminal and use the usual:
fx-CG 50 developers probably want a g3a wrapper as well; the reference
implementation is tari's [mkg3a](https://www.taricorp.net/projects/mkg3a/).
This is only necessary when creating g3a files, not to use gint.
$ ./configure
$ make
# make install
The build process is detailed below for both platforms, the principle is the
same. You can build both targets at the same time by reading the two sections.
This will build the `all-lib` target and install the following components in
the storage folder of the fxSDK:
* `libgint.a`, the gint library
* `libc.a`, the partial standard library
* The libgint headers for development
By default gint will be installed in the appropriate compiler folder, which
is `$PREFIX/` for libraries and linker scripts, and `$PREFIX/include/gint/` for
headers, where `PREFIX` is obtained by running
`${toolchain}-gcc --print-search-dirs` and reading the line that starts with
`install:`. You can change this with the `--prefix` configure option.
When explicitly running target `all`, the following additional files will be
generated in the working directory:
* `gintdemo.g1a`, a test application
* `gintdbg.g1a`, a non-gint application used for debugging in case gint crashes
### Building for fx-9860G II
The usual `clean`, `mrproper`, and `distclean` rules will clean the directory.
There are configuration options, which can be obtained using
`./configure --help`. Most of them customize size limits, if a project needs to
extend them. The true free-standing program may build the library using the
`--no-syscalls` switch, but some features will be disabled (dynamic
allocation...).
Create a build directory and configure in it:
% mkdir build.fx && cd build.fx
% ../configure --target=fx9860g
Source organization
-------------------
Then build the source and install the library files to the selected directory.
You might need root access if you selected a target directory owned by root
with `--prefix`, or if you built your compiler as root.
gint is made of *modules*. Each module has one or more of the following
component files:
* A header file in `/include`
* An internal header file in `/include/internals`
* Single-function source files in `/src/module`: to avoid linking against the
whole library, some functions have their own object files. Their names are
those of the functions.
* Other source files in `/src/module`: contain multiple functions that always
work together, or are lightweight enough not to be separated. Their names
often begin with `module_`.
* Other files in `/src/module`: the `display` module contains a font.
% make
% make install
The demo application is in the `demo` folder.
### Building for fx-CG 50
Create a build directory and configure in it. The default toolchain is
`sh4eb-nofpu-elf`, if you wish to build with `sh3eb-elf`, you need to add a
command-line option `--toolchain=sh3eb-elf`.
% mkdir build.cg && cd build.cg
% ../configure --target=fxcg50
Then build the source and install the library files to the selected directory.
% make
% make install
## Using gint
To use gint as your runtime environment, the bare minimum is:
* Build with `-ffreestanding`;
* Link with `-T fx9860g.ld` and `-lgint-fx` on fx-9860G;
* Link with `-T fxcg50.ld` and `-lgint-cg` on fx-CG 50.
If you don't have a standard library such as
[Memallox's port of newlib](https://git.planet-casio.com/Memallox/libc), you
also need `-nostdlib`. I typically use `-m3 -mb` or `-m4 -mb` to specify the
platform, but that may not even be necessary.
Typically you might want to do this with the
[fxSDK](http://git.planet-casio.com/lephe/fxsdk), which hides most of the
details and makes it easy to roll add-ins.

67
TODO
View file

@ -1,15 +1,61 @@
# Since compatibility
Another, more recent list of things to do:
- Complete memory functions in [core/memory.c]
- Finish the boot log in <core/bootlog.h>
- Finish the boot log in [core/bootlog.c]
- Document the SH7305 PFC in <gint/mpu/pfc.h>
- Change the description of the version number in <gint/gint.h>
- Define the version number from full Git info
- Do this display in <gint/display-fx.h> and <gint/display-cg.h>
- Remove all mentions of GINT_LAX in the code
- Write exception handlers
- Do overclock in [clock/]
- Test getkey()
* Make keysc/keysc.c/state static and review forgotten global variables
* Find out what happened to the clock frequency on fxcg50
* Implement profiling
* Save and restore interrupt masks
* More keyboard
Strange, (irreproducible?) SysERRORs on SH3?
- Happened when dealing with overclock and after a transfer
- SysERROR when add-in fully returns, nothing during execution
- Affects all add-ins of the calculator!
* Make sure interrupt for extra timers is masked when you ctx_restore to avoid
interrupts
* For modules, use #define TMU ((tmu_t *)0xdeadbeef)) instead of tmu_t *TMU
* Don't hardcode fx-cg50 stack address in fxcg50.ld for Prizm compatibility
* Implement the gint_switch()
* File management
* Drawing functions, bopti and tales
* Gray engine
* Overclock
* Add a driver init function to the r61524 driver.
* Finish the bootlog and use the keyboard to control it.
* Save driver contexts on the stack in gint_pause().
* Write the core memory functions.
- Allow arbitrary code in gint_pause() instead of just main menu?
- Dynamically detect P1 static RAM in TLB for runtime Prizm compatibility.
* Use a solid API for boot-time printing (or just use fxlib)
- Use qdiv10() (there should be no __sdivsi3 in gint)
(bootlog, timer_delay(), getkey_repeat(), int2bcd())
* Load an exception handler and a TLB miss handler.
# Before compatibility
Bugs to fix:
- Alignment of ALL .data / .rodata files is required to ensure converted data
is properly aligned
- Unpreditcable crap happens when more than 10 keys are pressed simultaneously
- Ensure heap data is freed when a task-switch results in leaving the app
is properly aligned -> Ok using ALIGN(4)
- Ensure heap data is freed when a task-switch results in leaving the app (?)
Things to do before 1.0:
- bopti: Test partial transparency
- demo: Try 284x124 at (-60, -28) (all disadvantages)
- perf: Try 284x124 at (-60, -28) (all disadvantages)
- project: Check size of *all* library structures
- project: Clean headers that have some internal definitions
- project: Get rid of 7705.h (keyboard) and 7305.h (keyboard, gint)
- time: Compute CLOCKS_PER_SEC
Things to do later:
@ -17,12 +63,8 @@ Things to do later:
- clock: Only measure if requires as option, otherwise trust {FTune}
- clock: Handle overclock (relaunch clocks when overclocking)
- clock: Split code into several files, change clock_config_t type
- core: Change interrupt priority using the gint API
- core: Register more interrupts (and understand their parameters)
- core: Remove redundant code linked to environment saves
- core: Review interrupt system (again) - this one is too slow
- display: Try to make this module lighter (lots of code in text section)
- errno: Introduce errno and use it more or less everywhere
- esper: Cleaner playback, synthesizing
- events: Allow customization of keyboard event system (option to return
| events with modifiers, etc)
@ -34,14 +76,10 @@ Things to do later:
- stdio: More serious formatted printing functions and headers
- string: Use cmp/str to implement memchr() (assembler examples)
- string: Do some tests for memcmp() and memcpy()
- string: Lighter functions?
- usb: Implement a driver
Things to investigate:
- Registers that may need to be saved within setjmp()
- Optimizing core/gint.c leads to raising of an illegal slot exception when
running the interrupt handler, although it ends on rte; lds.l @r15+, mach,
which is totally not an illegal slot.
Possibly useful modules:
- DMAC
@ -49,4 +87,3 @@ Possibly useful modules:
- TPU
- USB
- CMT on SH7305, WDT on SH7705
- ACD (SH7705 only)

262
configure vendored
View file

@ -4,132 +4,170 @@
# Basic configuration
#
declare -A conf
# Target platform
conf_target=
target=
toolchain=
# Build options
prefix=
cflags=
# Behavior
conf[GINT_BOOT_LOG]=
conf[GINT_NO_SYSCALLS]=
cong[GINT_LAX]=
conf[GINT_EXTENDED_LIBC]=
conf[GINT_STATIC_GRAY]=
boot_log=
no_syscalls=
static_gray=
# Size limits
conf[ATEXIT_MAX]=16
conf[TIMER_SLOTS]=16
conf[EVENTS_QUEUE_SIZE]=64
atexit_max=
# Output files
output="config/Makefile.cfg"
output="Makefile.cfg"
#
# Help screen and output util
# Help screen
#
error="\e[31;1merror:\e[0m"
C_="$(echo -e '\e[30;1m')"
Cr="$(echo -e '\e[31;1m')"
Cg="$(echo -e '\e[32;1m')"
Cp="$(echo -e '\e[34;1m')"
C0="$(echo -e '\e[0m')"
help()
{
cat << EOF
cat << EOF
Configuration script for the gint library.
Usage: $0 [options...]
Platform settings (specify exactly one):
${Cg}-fx9860g$C0
Target platform is fx-9860G II: all monochrome models that support add-ins
or can be flashed to support them.
${Cg}-fxcg50$C0
Target platform is fx-CG 50; there is some compatibility with fx-CG 10/20.
You should build out-of-tree by creating a build directory and configuring from
there.
Options that affect the behavior of the library $C_[${Cp}default$C_]$C0:
$Cr-boot-log $C_[${Cp}disabled$C_]$C0
Enable an on-screen log at startup if a key is kept pressed while launching
the add-in, allowing easy debug and crash diagnoses.
$Cr-no-syscalls $C_[${Cp}disabled$C_]$C0
Never use syscalls. Expect trouble with malloc() and the gray engine. Do
not trigger this switch unless you know what you are doing.
$Cr-lax $C_[${Cp}disabled$C_]$C0
Make more assumptions about functions parameters. This disables coordinate
checks in drawing functions. Be careful!
$Cr-extended-libc $C_[${Cp}disabled$C_]$C0
Enable specific C99 headers/features that are normally not required by
calculator programs. This may allow porting programs from other platforms.
$Cr-static-gray-engine $C_[${Cp}disabled$C_]$C0
Place the gray engine vram in static ram instead of using the heap. Always
use this option when using both the gray engine and -no-syscalls.
Target selection:
--target=fx9860g|fxcg50
Options that customize size limits $C_[${Cp}default$C_]$C0:
$Cr-atexit-max$C0=${Cg}integer$C_ [${Cp}16$C_]$C0
Number of exit handlers that can be registered by atexit().
$Cr-timer-slots$C0=${Cg}integer$C_ [${Cp}16$C_]$C0
Number of virtual timers that may be registered at the same time.
$Cr-events-queue-size$C0=${Cg}integer$C_ [${Cp}64$C_]$C0
Number of events simultaneously stored in the event queue.
fx9860g covers all fx-9860G II-like monochromes models that support add-ins
or can be flashed with an OS that does. This includes SH3 and SH4 machines.
Default toolchain is 'sh3eb-elf'.
fxcg50 covers just the fx-CG 50; there is some unofficial compatibility with
fx-CG 10/20. All of these are SH4-only.
Default toolchain is 'sh4eb-nofpu-elf'.
Build options:
--toolchain=TRIPLET Build with a different toolchain
--prefix=PREFIX Install prefix (PREFIX/lib and PREFIX/include are used)
--cflags=FLAGS Additional compiler flags at end of command
Library options (disabled by default):
--boot-log Display a boot debug log at startup if a key is pressed
Used to investigate unstabilities.
--no-syscalls Cut off all syscalls (this will break things)
Only use this option if you have a good idea of which.
--static-gray Allocate gray VRAMs in static RAM instead of the heap
May help when --no-syscalls is on.
Size limits:
--atexit-max=NUM Number of exit handlers in atexit()'s array [16]
Deprecated options (to be removed):
-extended-libc Provide a few C99 headers (in the future, refer to the
newlib port by Memallox's for the standard library)
-timer-slots=<n> Number of virtual timer slots (this feature will be
moved to an independent library) [16]
-events-queue-size=<n>
Size of event queue (this mechanism is likely to
disappear in future versions) [64]
EOF
exit 0
exit 0
}
if [[ "$@" == "--help" ]]; then
help
exit 1
fi
if [[ -f make/Makefile ]]; then
echo "error: you should configure from a build directory, like this:" >&2
echo " mkdir build && cd build && ../configure [options..]" >&2
exit 1
fi
#
# Parsing arguments
#
fail=false
for arg; do case "$arg" in
-h | -? | --help) help;;
-h | -? | --help)
help;;
-fx9860g)
conf_target="FX9860G";;
-fxcg50)
conf_target="FXCG50";;
--target=*)
case ${arg#*=} in
"fx9860g")
target=fx9860g
toolchain=${toolchain:-sh3eb-elf};;
"fxcg50")
target=fxcg50
toolchain=${toolchain:-sh4eb-nofpu-elf};;
*)
echo "error: invalid target '$target'"
fail=true
esac;;
-boot-log) conf[GINT_BOOT_LOG]=true;;
-no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
-lax) conf[GINT_LAX]=true;;
-extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
-static-gray-engine) conf[GINT_STATIC_GRAY]=true;;
--prefix=*)
prefix=${arg#*=};;
--cflags=*)
cflags=${arg#*=};;
-atexit-max=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
conf[ATEXIT_MAX]=$size
else echo -e "$error -atexit-max expects an integer value"
fail=true; fi;;
-timer-slots=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
conf[TIMER_SLOTS]=$size
else echo -e "$error -timer-slots expects an integer value"
fail=true; fi;;
-events-queue-size=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
conf[EVENTS_QUEUE_SIZE]=$size
else echo -e "$error -events-queue-size expects an integer"\
"value"
fail=true; fi;;
--boot-log)
boot_log=true;;
--no-syscalls)
no_syscalls=true;;
--static-gray)
static_gray=true;;
-atexit-max | -timer-slots | -events-queue-size)
echo -e "$error syntax for $arg is $arg=<integer-value>";;
--atexit-max=*)
n=${arg#*=}
if [[ $n == +([0-9]) ]]; then
atexit_max=$n
else
echo -e "error: -atexit-max expects an integer value"
fail=true
fi;;
*)
echo -e "$error unrecognized argument '$arg'"; fail=true;;
--atexit-max)
echo "error: '$arg' expects a value (see '$0 --help')";
fail=true;;
-extended-libc)
echo "warning: support for '-extended-libc' has been removed";;
-timer-slots=*)
echo "warning: support for '-timer-slots' has been removed";;
-events-queue-size=*)
echo "warning: support for '-events-queue-size' has been removed";;
esac; done
#
# Checking mandatory arguments
#
if [[ ! $conf_target ]]; then
echo -e "$error No target specified. See $0 --help."
fail=true;
if [[ -z "$target" ]]; then
echo "error: no target specified! (see '$0 --help')"
fail=true;
fi
# If no prefix is specified, install to the GCC's build folder
if [[ -z "$prefix" ]]; then
echo "No prefix specified, let's ask the compiler:"
echo " $toolchain-gcc --print-search-dirs | grep install | sed 's/install: //'"
inst=$($toolchain-gcc --print-search-dirs | grep install | sed 's/install: //')
if [[ $? != 0 ]]; then
echo "Call returned $?, giving up."
fi
echo "Got '$inst'".
if [[ ! -d $inst ]]; then
echo "Directory does not exist (or is not a directory), giving up."
fi
echo ""
prefix=$inst
fi
#
@ -138,32 +176,34 @@ fi
output_config()
{
[ ${conf_target} == "FX9860G" ] \
&& echo "cfg_target = fx" \
|| echo "cfg_target = cg"
mod=${target/98/fx}
echo "CONFIG.TARGET = ${mod:2:2}"
echo "CONFIG.TARGET.LONG = $target"
echo -n "cfg_macros ="
echo -n " -D$conf_target"
[[ $prefix ]] && echo "PREFIX = $prefix"
[[ $toolchain ]] && echo "CONFIG.TOOLCHAIN = $toolchain"
[[ $cflags ]] && echo "CONFIG.CFLAGS = $cflags"
[ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG"
[ "${conf[GINT_NO_SYSCALLS]}" ] && echo -n " -DGINT_NO_SYSCALLS"
[ "${conf[GINT_LAX]}" ] && echo -n " -DGINT_LAX"
[ "${conf[GINT_EXTENDED_LIBC]}" ] && echo -n " -DGINT_EXTENDED_LIBC"
[ "${conf[GINT_STATIC_GRAY]}" ] && echo -n " -DGINT_STATIC_GRAY"
echo -n " -DATEXIT_MAX=${conf[ATEXIT_MAX]}"
echo -n " -DTIMER_SLOTS=${conf[TIMER_SLOTS]}"
echo -n " -DEVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}"
echo ""
[ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "cfg_ext = true"
echo -n "CONFIG.MACROS ="
echo -n " -D$(echo $target | tr 'a-z' 'A-Z')"
[[ "$boot_log" ]] && echo -n " -DGINT_BOOT_LOG"
[[ "$no_syscalls" ]] && echo -n " -DGINT_NO_SYSCALLS"
[[ "$static_gray" ]] && echo -n " -DGINT_STATIC_GRAY"
[[ "$atexit_max" ]] && echo -n " -DATEXIT_MAX=$atexit_max"
echo ""
}
if $fail; then
echo "Output file $output has not been modified."
else
mkdir -p config
output_config > $output
echo "Configuration saved in $output, ready to make!"
echo "note: output file $output has not been changed."
exit 1
fi
output_config > $output
src="Makefile"
dst="../make/Makefile"
[[ -L $src && $(readlink $src) == $dst ]] && rm $src
ln -s $dst $src
echo "Configuration saved in $output, ready to make!"

View file

@ -130,7 +130,13 @@ SECTIONS
. = ALIGN(16);
} > ram AT> rom
_sdata = SIZEOF(.data);
/* Read-write data sub-aligned to 4 bytes (mainly from fxconv) */
.data.4 : SUBALIGN(4) {
*(.data.4)
. = ALIGN(16);
} > ram AT> rom
_sdata = SIZEOF(.data) + SIZEOF(.data.4);

View file

@ -118,7 +118,13 @@ SECTIONS
. = ALIGN(16);
} > ram AT> rom
_sdata = SIZEOF(.data);
/* Read-write data sub-aligned to 4 bytes (mainly from fxconv) */
.data.4 : SUBALIGN(4) {
*(.data.4)
. = ALIGN(16);
} > ram AT> rom
_sdata = SIZEOF(.data) + SIZEOF(.data.4);

View file

@ -1,5 +1,5 @@
//---
// gint:core:bootlog - Boot-time on-screen log for extreme debugging
// core:bootlog - Boot-time on-screen log for extreme debugging
//---
#ifndef GINT_CORE_BOOTLOG
@ -21,4 +21,11 @@ void bootlog_mapped(uint32_t rom, uint32_t ram);
handlers set up. */
void bootlog_kernel(void);
/* All these functions are enabled only if GINT_BOOT_LOG is defined */
#ifndef GINT_BOOT_LOG
#define bootlog_loaded(...)
#define bootlog_mapped(...)
#define bootlog_kernel(...)
#endif
#endif /* GINT_CORE_BOOTLOG */

View file

@ -1,5 +1,5 @@
//---
// gint:core:memory - Core memory functions
// core:memory - Core memory functions
//
// These are the basic standard memory functions required by GCC.
//---

View file

@ -1,23 +1,21 @@
//---
// gint:core:mmu - MMU-related definitions
// core:mmu - MMU-related definitions
//
// gint does not touch the MMU because the risk of interfering with the
// system is deemed too high. However, to ensure that the add-in runs
// properly, checks using read-only access to the MMU are performed.
// gint does not touch the MMU because the risk of permanently wreaking
// the system is deemed too high. However, to ensure that the add-in runs
// properly, checks using read-only accesses to the MMU are performed.
//---
#ifndef GINT_CORE_MMU
#define GINT_CORE_MMU
#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 TLB
//---
#ifdef FX9860G
/* tlb_addr_t - address part of a TLB entry */
typedef struct
{
@ -26,7 +24,7 @@ typedef struct
uint V :1;
uint ASID :8;
} PACKED(4) tlb_addr_t;
} GPACKED(4) tlb_addr_t;
/* tlb_data_t - data part of a TLB entry */
typedef struct
@ -43,7 +41,7 @@ typedef struct
uint SH :1;
uint :1;
} PACKED(4) tlb_data_t;
} GPACKED(4) tlb_data_t;
/* tlb_addr() - get the P4 address of a TLB address entry
@way TLB way (0..3)
@ -57,7 +55,7 @@ const tlb_addr_t *tlb_addr(uint way, uint E);
Returns a pointer to the entry. */
const tlb_data_t *tlb_data(uint way, uint E);
/* tlb_mapped_memort() - count amount of mapped memory
/* tlb_mapped_memory() - count amount of mapped memory
This function returns the amount of mapped text and data segment memory, in
bytes. The ranges are defined as follows:
ROM 00300000:512k
@ -68,8 +66,6 @@ const tlb_data_t *tlb_data(uint way, uint E);
@ram Pointer to amount of mapped RAM */
void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram);
#endif
//---
// SH7305 Unified TLB
//---
@ -82,7 +78,7 @@ typedef struct
uint V :1;
uint ASID :8;
} PACKED(4) utlb_addr_t;
} GPACKED(4) utlb_addr_t;
/* utlb_addr() - get the P4 address of a UTLB address entry
@E Entry number (should be in range 0..63)
@ -104,7 +100,7 @@ typedef struct
uint SH :1;
uint WT :1;
} PACKED(4) utlb_data_t;
} GPACKED(4) utlb_data_t;
/* utlb_data() - get the P4 address of a UTLB data entry
@E Entry number (should be in range 0..63)

View file

@ -1,19 +1,19 @@
//---
// gint:core:setup - Installing and unloading the library
// core:setup - Installing and unloading the library
//---
#ifndef GINT_CORE_SETUP
#define GINT_CORE_SETUP
#include <defs/types.h>
#include <gint/defs/types.h>
/* Prototypes for the library management functions are in <gint/gint.h> */
/* gint_setvbr()
Changes the VBR address and the calls the configuration function while
interrupts are disabled. The configuration function must change either the
priority registers or the interrupt masks, and make sure that all the
interrupts that it leaves enabled are handled by the new VBR handlers.
Changes the VBR address and calls the configuration function while
interrupts are disabled. The configuration function must disable all
interrupts that the new handlers cannot handle, either by clearing the
priority registers or by setting the interrupt masks.
@vbr New VBR address
@configure Configuration function

View file

@ -1,78 +0,0 @@
//---
// gint:defs:types - Type-related macros
//---
#ifndef GINT_DEFS_TYPES
#define GINT_DEFS_TYPES
#include <defs/attributes.h>
#include <stddef.h>
#include <stdint.h>
//---
// Const casts
//---
/* const_cast() - perform const casts
This is intended for initialization purposes only, like "final" in Java.
This macro is the most generic form without any type inference; using
const_cint() or const_cptr() may be more convenient for simple types */
#define const_cast(x, T) (*((T *)(&(x))))
/* const_cptr() - perform const casts on pointers */
#define const_cptr(x) (*((void **)(&(x))))
/* const_cint() - perform const casts on integers
This macro saves you from specifying the integral type that you're
manipulating, which may avoid errors if the type changes. It will only work
with the primitive types that are either mentioned in the following list or
aliased as one of the listed types.
This is to prevent unforeseen effects of tricks such as typeof(x + 0) that
promotes various small integers to ints */
#define const_cint(x) (*_Generic((x), \
char: (char *) (&(x)), \
unsigned char: (unsigned char *) (&(x)), \
short: (short *) (&(x)), \
unsigned short: (unsigned short *) (&(x)), \
int: (int *) (&(x)), \
unsigned int: (unsigned int *) (&(x)), \
long: (long *) (&(x)), \
unsigned long: (unsigned long *) (&(x)), \
long long: (long long *) (&(x)), \
unsigned long long: (unsigned long long *) (&(x)) \
))
//---
// Structure elements
//----
/* Fixed-width types for bit fields are entirely meaningless */
typedef unsigned int uint;
/* Giving a type to padding bytes is misguiding */
#define pad_nam2(c) _ ## c
#define pad_name(c) pad_nam2(c)
#define pad(bytes) uint8_t pad_name(__COUNTER__)[bytes]
/* byte_union() - union between an uint8_t 'byte' element and a bit field */
#define byte_union(name, fields) \
union { \
uint8_t byte; \
struct { fields } PACKED(1); \
} PACKED(1) name
/* word_union() - union between an uint16_t 'word' element and a bit field */
#define word_union(name, fields) \
union { \
uint16_t word; \
struct { fields } PACKED(2); \
} PACKED(2) name
/* lword_union() - union between an uint32_t 'lword' element and a bit field */
#define lword_union(name, fields) \
union { \
uint32_t lword; \
struct { fields } PACKED(4); \
} PACKED(4) name
#endif /* GINT_DEFS_TYPES */

30
include/display/fx.h Normal file
View file

@ -0,0 +1,30 @@
//---
// display:fx - Internal definitions for the display module on fx9860g
//---
#ifndef DISPLAY_FX
#define DISPLAY_FX
#include <gint/defs/types.h>
#include <gint/display.h>
/* masks() - compute the vram masks for a given rectangle
Since the VRAM is line-based with four uin32_t elements per row, we can
execute any operation on a rectangle by running it on each set of four
uint32_t elements.
This function calculates four uint32_t values and stores them in @mask. Each
of the 128 bits in this array represents a column of the screen, and the bit
of column c is 1 iff x1 <= c <= x2.
These masks can then be and-ed/or-ed/anything on the VRAM to draw.
@x1 @x2 Targeted screen range, horizontally (both included)
@masks Stores the result of the function (four uint32_t values) */
void masks(int x1, int x2, uint32_t *masks);
/* Font currently configured for text rendering */
extern font_t const * topti_font;
#endif /* DISPLAY_FX */

View file

@ -5,8 +5,6 @@
#ifndef GINT_CLOCK
#define GINT_CLOCK
#include <defs/types.h>
//---
// Clock signals
//---
@ -59,7 +57,7 @@ const clock_frequency_t *clock_freq(void);
The function stops the processor until an interrupt is accepted; the
duration is not known in advance. This function should be used when the
add-in is idle, for instance while waiting for keyboard input. */
void sleep(void);
#define sleep() __asm__("sleep")
/* sleep_us() - sleep for a definite duration in microseconds

View file

@ -5,37 +5,26 @@
#ifndef GINT_DEFS_ATTRIBUTES
#define GINT_DEFS_ATTRIBUTES
/* Generic attribute element */
#define ATTR(...) __attribute__((__VA_ARGS__))
/* Objects from specific sections */
#define SECTION(x) __attribute__((section(x)))
#define GSECTION(x) __attribute__((section(x)))
/* Objects from the .gint.data and .gint.bss sections */
#define GDATA __attribute__((section(".gint.data")))
#define GBSS __attribute__((section(".gint.bss")))
/* Additional sections that are only needed on SH3 */
#define GDATA3 __attribute__((section(".gint.data.sh3")))
#define GBSS3 __attribute__((section(".gint.bss.sh3")))
/* Initialization functions */
#define PRETEXT __attribute__((section(".pretext")))
/* Unused parameters or variables */
#define UNUSED __attribute__((unused))
#define GUNUSED __attribute__((unused))
/* Functions that *must* be inlined */
#define INLINE __attribute__((always_inline)) inline
/* Short static functions defined in header files */
#define HDRFUNC __attribute__((always_inline)) static inline
/* Constructors and destructors */
#define CTOR(x) __attribute__((constructor ((x) + 101))) PRETEXT
#define DTOR(x) __attribute__((destructor ((x) + 101)))
#define GINLINE __attribute__((always_inline)) inline
/* Aligned variables */
#define ALIGNED(x) __attribute__((aligned(x)))
#define GALIGNED(x) __attribute__((aligned(x)))
/* Packed structures. I require explicit alignment because if it's unspecified,
GCC cannot optimize access size, and reads to memory-mapped I/O with invalid
access sizes silently fail - honestly you don't want this to happen */
#define PACKED(x) __attribute__((packed, aligned(x)))
#define GPACKED(x) __attribute__((packed, aligned(x)))
#endif /* GINT_DEFS_ATTRIBUTES */

48
include/gint/defs/types.h Normal file
View file

@ -0,0 +1,48 @@
//---
// gint:defs:types - Type definitions
//---
#ifndef GINT_DEFS_TYPES
#define GINT_DEFS_TYPES
#include <gint/defs/attributes.h>
/* For size_t, mainly */
#include <stddef.h>
/* For all fixed-width integer types */
#include <stdint.h>
/* Fixed-width types for bit fields are quite meaningless */
typedef unsigned int uint;
//---
// Structure elements
//----
/* Giving a type to padding bytes is misguiding, let's hide it in a macro */
#define pad_nam2(c) _ ## c
#define pad_name(c) pad_nam2(c)
#define pad(bytes) uint8_t pad_name(__COUNTER__)[bytes]
/* byte_union() - union between an uint8_t 'byte' element and a bit field */
#define byte_union(name, fields) \
union { \
uint8_t byte; \
struct { fields } GPACKED(1); \
} GPACKED(1) name
/* word_union() - union between an uint16_t 'word' element and a bit field */
#define word_union(name, fields) \
union { \
uint16_t word; \
struct { fields } GPACKED(2); \
} GPACKED(2) name
/* lword_union() - union between an uint32_t 'lword' element and a bit field */
#define lword_union(name, fields) \
union { \
uint32_t lword; \
struct { fields } GPACKED(4); \
} GPACKED(4) name
#endif /* GINT_DEFS_TYPES */

67
include/gint/defs/util.h Normal file
View file

@ -0,0 +1,67 @@
//---
// gint:defs:util - Various utility macros
//---
/* min(), max() (without double evaluation) */
#define min(a, b) ({ \
__auto_type _a = (a); \
__auto_type _b = (b); \
_a < _b ? _a : _b; \
})
#define max(a, b) ({ \
__auto_type _a = (a); \
__auto_type _b = (b); \
_a > _b ? _a : _b; \
})
/* sgn() (without double evaluation) */
#define sgn(s) ({ \
__auto_type _s = (s); \
_s < 0 ? -1 : \
_s > 0 ? +1 : \
0; \
})
/* abs() (without double evaluation) */
#define abs(s) ({ \
__auto_type _s = (s); \
_s < 0 ? -s : s; \
})
/* swap() - exchange two variables of the same type */
#define swap(a, b) ({ \
__auto_type _tmp = (a); \
(a) = (b); \
(b) = _tmp; \
})
//---
// Const casts
//---
/* const_cast() - perform const casts
This is intended for initialization purposes only, like "final" in Java.
This macro is the most generic form without any type inference; using
const_cint() or const_cptr() may be more convenient for simple types */
#define const_cast(x, T) (*((T *)(&(x))))
/* const_cptr() - perform const casts on pointers */
#define const_cptr(x) (*((void **)(&(x))))
/* const_cint() - perform const casts on integers
This macro infers the integer type it's using, which may avoid errors when
types change during code maintenance. It only works on primitive types and
their aliases, this is on purpose to avoid integer coercion with tricks such
as typeof(x + 0). */
#define const_cint(x) (*_Generic((x), \
char: (char *) (&(x)), \
unsigned char: (unsigned char *) (&(x)), \
short: (short *) (&(x)), \
unsigned short: (unsigned short *) (&(x)), \
int: (int *) (&(x)), \
unsigned int: (unsigned int *) (&(x)), \
long: (long *) (&(x)), \
unsigned long: (unsigned long *) (&(x)), \
long long: (long long *) (&(x)), \
unsigned long long: (unsigned long long *) (&(x)) \
))

21
include/gint/display-cg.h Normal file
View file

@ -0,0 +1,21 @@
//---
// gint:display-cg - fxcg50 rendering functions
//
// This module covers all 16-bit opaque rendering functions. For
// gamma-related functions, color composition, check out a color library.
//
//---
#ifndef GINT_DISPLAY_CG
#define GINT_DISPLAY_CG
#ifdef FXCG50
/* Screen dimensions on fxcg50 - never mind the borders, gint lets you use the
full surface! */
#define DWIDTH 396
#define DHEIGHT 224
#endif /* FXCG50 */
#endif /* GINT_DISPLAY_CG */

View file

@ -11,16 +11,14 @@
#ifdef FX9860G
/* Screen dimensions on fx9860g */
#define DWIDTH 128
#define DHEIGHT 64
#include <gint/defs/types.h>
/* color_t - colors available for drawing
The following colors are defined by the library:
OPAQUE COLORS (override existing pixels)
white, black - the usual thing
light, dark - additional colors used by the gray engine
light, dark - intermediate colors used with the gray engine
OPERATORS (combine with existing pixels)
none - leaves unchanged
@ -69,10 +67,6 @@ void dclear(color_t color);
This functions applies a color or an operator to a rectangle defined by two
points (x1 y1) and (x2 y2). Both are included in the rectangle.
If GINT_LAX is defined, this function makes the following assumptions:
0 <= x1 < x2 <= 127
0 <= y1 < y2 <= 63
@x1 @y1 @x2 @y2 Bounding rectangle (drawn area).
@color Allowed colors: white, black, none, reverse */
void drect(int x1, int y1, int x2, int y2, color_t color);
@ -83,11 +77,10 @@ void drect(int x1, int y1, int x2, int y2, color_t color);
/* dpixel() - change a pixel's color
If the requested color is an operator, the result will depend on the current
color of the pixel.
If GINT_LAX is defined, this function makes the following assumptions:
0 <= x <= 127
0 <= y <= 63
color of the pixel. Setting single pixels is the slowest method to produce a
graphical result: all other functions for rendering lines, rectangles,
images or text use highly-optimized methods, so check them out first if you
care about performance.
@x @y Coordinates of the pixel to repaint
@color Allowed colors: white, black, none, reverse */
@ -98,16 +91,114 @@ void dpixel(int x, int y, color_t color);
that the affected pixels may not be exactly the same when using dline() and
Bdisp_DrawLineVRAM().
dline() has optimization facilities for horizontal and vertical lines.
dline() has optimization facilities for horizontal and vertical lines, but
it does not detect if your line doesn't fit in the screen. So drawing from
(-1e6,0) to (1e6,63) will work, but will be veeery slow.
If GINT_LAX is defined, this function makes the following assumptions:
0 <= x1 <= x2 <= 127
0 <= y1, y2 <= 63
@x1 @y1 @x2 @y2 End points of theline (both included).
@x1 @y1 @x2 @y2 End points of the line (both included).
@color Allowed colors: white, black, none, reverse */
void dline(int x1, int y1, int x2, int y2, color_t color);
//---
// Image rendering (bopti)
//---
/* image_t - image files encoded for bopti
This format is the result of encoding images for bopti with the fxSDK's
[fxconv] tool. The bopti routines can render it extremely fast, which makes
it preferable over plain bitmaps if the images are never edited. */
typedef struct
{
/* Image can only be rendered with the gray engine */
uint gray :1;
/* Left for future use */
uint :3;
/* Image profile (uniquely identifies a rendering function) */
uint profile :4;
/* Full width, in pixels */
uint width :12;
/* Full height, in pixels */
uint height :12;
/* Raw layer data */
uint8_t data[];
} GPACKED(4) image_t;
//---
// Text rendering (topti)
//---
/* font_t - font data encoded for topti */
typedef struct
{
/* Length of font name (not NUL-terminated) */
uint title :5;
/* Font shape flags */
uint bold :1;
uint italic :1;
uint serif :1;
uint mono :1;
/* Whether data is variable-length (proportional font) */
uint prop :1;
/* Reserved for future use */
uint :2;
/* Implemented charcter set */
uint charset :4;
/* Line height */
uint8_t line_height;
/* Storage height */
uint8_t data_height;
/* The rest of the data depends on whether the font is proportional */
union {
/* For monospaced fonts */
struct {
/* Width of glyphs */
uint16_t width;
/* Storage size, in longwords, of each glyph */
uint16_t storage_size;
/* Raw glyph data */
uint32_t data[];
};
/* For proportional fonts */
struct {
/* Storage index to find glyphs quickly */
uint16_t index[16];
/* Size array (padded to 4 bytes), 1 byte per entry,
followed by glyph data */
uint8_t sized_data[];
};
};
/* The font name is stored after the data. The size is the length set
in the [title] field, padded to 4 bytes with NULs. There might not
be a NUL at the end. */
} GPACKED(4) font_t;
/* dfont() - set the default font for text rendering
This font will be used by dtext() and sister functions. If font=NULL, gint's
default 5*6 font is used.
@font Font to use for subsequent text rendering calls */
void dfont(font_t const * font);
/* dtext() - display a string of text
Draws some text in the video RAM using the font set with dfont() (or gint's
default if no such font was set).
Due to the particular design of topti, this function takes advantage of the
line structure of the VRAM to rendeer several characters at once. This is
not a printf()-family function so [str] cannot contain formats like "%d"
(they will be rendered directly) and cannot receive additional arguments.
@x @y Coordinates of top-left corner of the rendered string
@str String to display
@color Which color to use */
void dtext(int x, int y, const char *str, color_t color);
#endif /* FX9860G */
#endif /* GINT_DISPLAY_FX */

View file

@ -5,6 +5,8 @@
#ifndef GINT_DISPLAY
#define GINT_DISPLAY
#include <gint/defs/types.h>
/* Expose the VRAM variable if GINT_NEED_VRAM is defined. This address is used
as the VRAM basis by all of gint's drawing functions, and must point to a
suitable buffer:
@ -13,15 +15,15 @@
This variable is primarily meant to be exposed to gint functions, but
add-ins may use it or change it freely:
- To use another video ram area (triple buffering or more, gray engine) ;
- To implement additional drawing functions ;
- To use another video ram area (triple buffering or more, gray engine);
- To implement additional drawing functions;
- When not drawing, as additional RAM (especially on fxcg50). */
#ifdef GINT_NEED_VRAM
extern uint32_t *vram;
#endif
/* As you would expect, display on fx9860g and display on fxcg50 are completely
different worlds. As a consequence, so are the headers ^^ */
different worlds. As a consequence, so are the rendering functions ^^ */
#ifdef FX9860G
#include <gint/display-fx.h>
@ -29,4 +31,28 @@ extern uint32_t *vram;
#include <gint/display-cg.h>
#endif
//---
// Parameter access
//---
/* dinfo_t - summary of information provided by this module */
typedef struct
{
/* Screen width, in pixels */
int width;
/* Screen height, in pixels */
int height;
/* Color depth (is 1 on fx9860g regardless of the gray engine) */
int bpp;
/* Current rendering font */
font_t const * font;
} dinfo_t;
/* dinfo() - retrieve information from the display module
This function returns the value of most parameters of the display module.
@info Pointer to allocated dinfo_t structure, will be filled. */
void dinfo(dinfo_t *info);
#endif /* GINT_DISPLAY */

View file

@ -5,8 +5,8 @@
#ifndef GINT_DRIVERS
#define GINT_DRIVERS
#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// Driver initialization
@ -39,7 +39,7 @@ typedef struct
may be set to NULL */
void (*init)(void);
/* unload() - unitialize the driver
/* unload() - deinitialize the driver
This function is called before ctx_restore() when gint is unloaded.
If there is no unload function, the field may be set to NULL */
void (*unload)(void);
@ -65,7 +65,7 @@ typedef struct
@ctx A context buffer filled by ctx_save() */
void (*ctx_restore)(void *ctx);
} PACKED(4) gint_driver_t;
} GPACKED(4) gint_driver_t;
/* GINT_DECLARE_DRIVER() - make a driver visible to gint
@ -75,10 +75,10 @@ typedef struct
The @level argument represents the priority level: lower numbers mean that
drivers will be loaded sooner. This numbering allows a primitive form of
dependency for drivers. You need to specifiy a level which is strictly
dependency for drivers. You need to specify a level which is strictly
higher than the level of all the drivers you depend on. */
#define GINT_DECLARE_DRIVER(level, name) \
SECTION(".gint.drivers." #level) extern gint_driver_t name;
GSECTION(".gint.drivers." #level) extern gint_driver_t name;
/* GINT_DRIVER_SH3() - declare a function for SH3-rectification
This macros allows the argument function to not exist on fxcg50. */

View file

@ -0,0 +1,17 @@
//---
// gint:drivers:iokbd - I/O ports-driven keyboard scanner
//
// This is for SH3 only. It reads key pressed from ports A/B/M.
//---
#ifndef GINT_DRIVERS_IOKBD
#define GINT_DRIVERS_IOKBD
#include <gint/defs/types.h>
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data
Numbering of rows is consistent with the keyboard.
@scan 12-byte buffer filled with row data */
void iokbd_scan(uint8_t *scan);
#endif /* GINT_DRIVERS_IOKBD */

View file

@ -7,7 +7,7 @@
#ifndef GINT_DRIVERS_T6K11
#define GINT_DRIVERS_T6K11
#include <defs/types.h>
#include <gint/defs/types.h>
/* t6k11_display() - send vram data to the LCD device
@ -33,7 +33,7 @@ void t6k11_display(const void *vram, int y1, int y2, size_t stride);
It would be possible to restore contrast, or update the system value on
change, if the address was known for all OS versions.
OS 02.05.2201 8800b93c
OS 02.05.2201 8800'b93c
@contrast Requested contrast value */
void t6k11_contrast(int contrast);

View file

@ -5,11 +5,10 @@
#ifndef GINT_GINT
#define GINT_GINT
#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/defs/types.h>
/* gint_version() - get the library version number
Returns gint's running version number, which is made of four fields:
/* GINT_VERSION - the library version number
Provides gint's running version number, which is made of four fields:
31 24 23 20 19 16 15 0
+---------------+---------------+---------------+---------------+
@ -22,11 +21,9 @@
The build number uniquely identifies a binary version of the library.
For instance, 0x72100053 translates as "release 1.0 build 83". */
HDRFUNC uint32_t gint_version(void)
{
extern char GINT_VERSION;
return (uint32_t)&GINT_VERSION;
}
/* TODO: Fix version number */
extern char GINT_VERSION;
#define GINT_VERSION ((uint32_t)&GINT_VERSION)
//---
// Library management
@ -103,16 +100,17 @@ int gint_intlevel(int intid, int level);
you're not interfering with usual interrupt assignments.
The first parameter 'event_code' represents the event_code associated with
the interrupt. The codes are normally platform-dependent, but gint always
uses SH7305 codes: SH3 platforms have a translation table. See the
documentation for a list of event codes and their associated interrupts.
the interrupt. If it's not a multiple of 0x20 then you're doing something
wrong. The codes are normally platform-dependent, but gint always uses
SH7305 codes: SH3 platforms have a translation table. See the documentation
for a list of event codes and their associated interrupts.
The handler function must be an interrupt handler: it must not raise
exceptions, must end with 'rte', and it will use the kernel register bank.
For convenience I allow any block size to be loaded as an interrupt handler,
but it should really be a multiple of 32 bytes and not override other
but it should really be a multiple of 0x20 bytes and not override other
handlers. If it's not written in assembler, then you're likely doing
something wrong.
something wrong, even with __attribute__((interrupt_handler)).
It is common for interrupt handlers to have a few bytes of data, such as the
address of a callback function. gint often stores this data in the last

View file

@ -5,34 +5,55 @@
#ifndef GINT_KEYBOARD
#define GINT_KEYBOARD
#include <defs/types.h>
/* Keyboard, key events and getkey()
gint's keyboard driver regularly scans the keyboard matrix and produces *key
events*, the most primitive type of event exposed to the user. A key event
basically says that a key has been pressed, held, or released at some point
in time. They are suited for real-time applications like games.
- pollevent() fetches the next queued keyboard event, and returns an event
of type KEYEV_NONE if none is available.
- waitevent() fetches the next queued keyboard event and waits if none is
available. The timeout can be configured.
GUI programs like the system applications will prefer using a GetKey()-like
functions that return a single key press at a time, heeds for releases, for
SHIFT and ALPHA modifiers, handles backlight and return-to-menu.
- getkey_opt() is gint's enhanced GetKey()-like function, with support for
custom repetition and many fine-tunable options.
- getkey() is a specific call to getkey_opt(), that imitates GetKey(). */
#include <gint/defs/types.h>
#include <gint/keycodes.h>
/* key_event_t - any keyboard event
This structure represents an event that occurs on the keyboard. This is a
low-level structure that is produced by the keyboard scanner. It reports key
presses, key releases, and key repetitions.
presses, key releases, and key repeats.
These events are detected and reported each time the keyboard is scanned,
which is 16 Hz by default, so you'll get 16 repeat events by second if a key
is kept pressed. We can filter the events to emit one only every second, for
example, but it's difficult to do it for all keys at the same time. Thus the
control of repetition delays is restricted to getkey().
which is 128 Hz by default, so you'll get 128 repeat events by second if a
key is kept pressed. We could filter the events to emit one only every
second, for example, but it's difficult to do it for all keys at the same
time. Thus the control of repeat delays is left to getkey().
When mod = 1, shift and alpha indicate whether the key has been modified.
This is only possible for key press events returned by getkey(). Note that
you can't have key = shift and mod = 1 at the same time.
When [mod = 1], attributes [shift] and [alpha] indicate whether the key has
been modified. Only key press events returned by getkey() have [mod = 1].
Note that you can't have, e.g. [key=KEY_SHIFT] and [mod=1] at the same time.
The time attribute indicates when the event occurred. This value increases
at each keyboard scan and *it wraps around every 4 minutes* (at 16 Hz).
I expect this attribute to be useful to analyze combo sequences in games.
Make sure you are aware of the two nitpicks:
- Don't keep the time values for too long because the wrap-around effect.
- 0xfff is just before 0x000, not long after. */
The [time] attribute indicates when the event occurred. It is a snapshot of
a time counter that increases at each keyboard scan and *wraps around every
8 minutes* (at 128 Hz). I expect this attribute to be useful to analyze
combo sequences in games. Make sure you are aware of the two nitpicks:
* Don't keep the time values for too long because of the wrap-around effect.
* 0xffff is "just before" 0x0000, not "long after". */
typedef struct
{
uint time :12; /* Time of event, unique over short periods */
uint time :16; /* Time of event, unique over short periods */
uint :7; /* Reserved for future use */
uint :3; /* Reserved for future use */
uint mod :1; /* Whether modifiers are used */
uint shift :1; /* If mod=1, whether SHIFT was pressed */
@ -41,7 +62,7 @@ typedef struct
uint type :2; /* Type of key event */
uint key :8; /* Hit key */
} PACKED(4) key_event_t;
} GPACKED(4) key_event_t;
/* Keyboard event types, as in the type field of key_event_t */
enum
@ -58,10 +79,98 @@ enum
#define KEYBOARD_QUEUE_SIZE 64
#endif
/* Keyboard frequency analysis, must be at least 64 for the keyboard to work,
and at most 32768 for the extra timer to support it. Better if a power of 2.
TODO: Add a configure or runtime setting for KEYBOARD_SCAN_FREQUENCY */
#ifndef KEYBOARD_SCAN_FREQUENCY
#define KEYBOARD_SCAN_FREQUENCY 128
#endif
//---
// Keyboard functions
//---
key_event_t key_poll(void);
/* pollevent() - poll the next keyboard event
This function returns the next yet-unpolled event from the keyboard buffer.
If no event is available, it returns a dummy event with type = KEYEV_NONE.
This event has always mod = 0, shift = 0, alpha = 0. */
key_event_t pollevent(void);
/* waitevent() - wait for the next keyboard event
This function works as pollevent() but waits if no event is available. When
timeout = NULL, it waits indefinitely. Otherwise, it waits until *timeout
becomes non-zero. It is particularly suitable to set *timeout to 1 using a
timer with [timer_timeout] as callback. See <gint/timer.h>. */
key_event_t waitevent(volatile int *timeout);
/* getkey() - wait for a pressed key
This function mimics the behavior of the fxlib GetKey(). It returns a
key_event_t object where [mod=1], and where [shift] and [alpha] indicate
whether SHIFT or ALPHA was pressed before the key was hit. [event] is
KEYEV_DOWN when a new key is pressed and KEYEV_HOLD in case of repeats.
Similarities with GetKey() include:
- Wait for a key to be pressed *after* the call (held keys don't count)
- Supports SHIFT and ALPHA modifiers
- Repeats arrows keys
- Allows return to main menu if the MENU key is pressed
- Controls backlight on models that have a back-lit screen
getkey() is equivalent to getkey_opt(GETKEY_DEFAULT, NULL). */
key_event_t getkey(void);
/* The following are the option bits for getkey_opt(). */
enum {
/* Enable modifiers keys */
GETKEY_MOD_SHIFT = 0x01,
GETKEY_MOD_ALPHA = 0x02,
/* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
GETKEY_BACKLIGHT = 0x04,
/* MENU triggers a task switch and displays the main menu */
GETKEY_MENU = 0x08,
/* Repeat arrow keys, or even all keys */
GETKEY_REP_ARROWS = 0x10,
GETKEY_REP_ALL = 0x20,
/* Default settings of getkey() */
GETKEY_DEFAULT = 0x1f,
};
/* getkey_opt() - enhanced getkey()
This function enhances getkey() with more general features. An
or-combination of option flags (see above) must be supplied as first
argument; 0 stands for no option. getkey_opt() returns the same kind of
values as getkey().
getkey_opt() supports a generic timeout function in the form of a volatile
pointer [timeout]. If it's NULL, getkey_opt() waits indefinitely. Otherwise,
it waits until *timeout becomes non-zero. It's up to you to change the
value whenever you want to interrupt the call; using a timer with
[timer_timeout] as callback is suitable.
@options An or-combination of values from the GETKEY_* enumeration
@timeout Optional pointer to a timeout value
Returns a key event of type KEYEV_DOWN or KEYEV_HOLD with [mod=1]. */
key_event_t getkey_opt(int options, volatile int *timeout);
/* getkey_repeat() - set repeat delays for getkey()
This function updates the repeat delays of getkey() and getkey_opt(). The
unit of the argument is in milliseconds, but the granularity of the delay is
dependent on the keyboard scan frequency.
In the default setting (128 Hz scans), the possible repeat delays are
approximately 8 ms, 16 ms, 23 ms, 31 ms... the provided arguments will be
rounded to the closest feasible delays to ensure that repetitions are
perfectly regular, rather than approximating the requested frequency.
The system default is (625 ms, 25 ms). With the 128 Hz setting, this default
will be approximated at (625 ms, 23.4375 ms).
@first Delay between key press and first repeat (no more than one hour)
@next Delay between subsequent repeats (no more than one hour) */
void getkey_repeat(int first, int next);
#endif /* GINT_KEYBOARD */

86
include/gint/keycodes.h Normal file
View file

@ -0,0 +1,86 @@
//---
// gint:keycodes - Matrix code for all keyboard keys
//---
#ifndef GINT_KEYCODES
#define GINT_KEYCODES
/* Raw matrix codes */
enum {
KEY_F1 = 0x91,
KEY_F2 = 0x92,
KEY_F3 = 0x93,
KEY_F4 = 0x94,
KEY_F5 = 0x95,
KEY_F6 = 0x96,
KEY_SHIFT = 0x81,
KEY_OPTN = 0x82,
KEY_VARS = 0x83,
KEY_MENU = 0x84,
KEY_LEFT = 0x85,
KEY_UP = 0x86,
KEY_ALPHA = 0x71,
KEY_SQUARE = 0x72,
KEY_POWER = 0x73,
KEY_EXIT = 0x74,
KEY_DOWN = 0x75,
KEY_RIGHT = 0x76,
KEY_XOT = 0x61,
KEY_LOG = 0x62,
KEY_LN = 0x63,
KEY_SIN = 0x64,
KEY_COS = 0x65,
KEY_TAN = 0x66,
KEY_FRAC = 0x51,
KEY_FD = 0x52,
KEY_LEFTP = 0x53,
KEY_RIGHTP = 0x54,
KEY_COMMA = 0x55,
KEY_ARROW = 0x56,
KEY_7 = 0x41,
KEY_8 = 0x42,
KEY_9 = 0x43,
KEY_DEL = 0x44,
/* AC/ON has keycode 0x07 instead of 0x45 */
KEY_4 = 0x31,
KEY_5 = 0x32,
KEY_6 = 0x33,
KEY_MUL = 0x34,
KEY_DIV = 0x35,
KEY_1 = 0x21,
KEY_2 = 0x22,
KEY_3 = 0x23,
KEY_ADD = 0x24,
KEY_SUB = 0x25,
KEY_0 = 0x11,
KEY_DOT = 0x12,
KEY_EXP = 0x13,
KEY_NEG = 0x14,
KEY_EXE = 0x15,
/* Why is AC/ON not 0x45? Because it must be on a row/column of its
own. It's used to power up the calculator; if it were in the middle
of the matrix one could use a ghosting effect to boot the calc. */
KEY_ACON = 0x07,
/* Key aliases (handle with care =D) */
KEY_X2 = KEY_SQUARE,
KEY_CARET = KEY_POWER,
KEY_SWITCH = KEY_FD,
KEY_LEFTPAR = KEY_LEFTP,
KEY_RIGHTPAR = KEY_RIGHTP,
KEY_STORE = KEY_ARROW,
KEY_TIMES = KEY_MUL,
KEY_PLUS = KEY_ADD,
KEY_MINUS = KEY_SUB,
};
#endif /* GINT_KEYCODES */

View file

@ -12,8 +12,6 @@
#ifndef GINT_CORE_MPU
#define GINT_CORE_MPU
#include <defs/attributes.h>
/* mpu_t - supported MPUs */
typedef enum
{
@ -27,14 +25,11 @@ typedef enum
#ifdef FX9860G
/* mpu_id() - get the name of the underlying MPU */
HDRFUNC mpu_t mpu_id(void)
{
extern const mpu_t mpu;
return mpu;
}
extern const mpu_t gint_mpu_id;
#define gint_mpu() (gint_mpu_id)
/* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */
#define isSH3() (mpu_id() & 1)
#define isSH3() (gint_mpu() & 1)
#define isSH4() (!isSH3())
/* mpu_init() - probe the MPU type
@ -44,7 +39,7 @@ typedef enum
#else /* FXCG50 */
/* All fxcg50 machines have an SH7305, which makes things simpler. */
#define mpu_id() mpu_sh7305
#define gint_mpu() mpu_sh7305
#define isSH3() 0
#define isSH4() 1

View file

@ -5,7 +5,8 @@
#ifndef GINT_CORE_CPG
#define GINT_CORE_CPG
#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 Clock Pulse Generator. Refer to:
@ -27,7 +28,7 @@ typedef volatile struct
uint16_t PFC :2; /* Peripheral clock divider */
);
} PACKED(4) sh7705_cpg_t;
} GPACKED(4) sh7705_cpg_t;
#define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80))
@ -73,7 +74,7 @@ typedef volatile struct
uint32_t FLF :11; /* FLL Multiplication Ratio */
);
} PACKED(4) sh7305_cpg_t;
} GPACKED(4) sh7305_cpg_t;
#define SH7305_CPG (*((sh7305_cpg_t *)0xa4150000))

View file

@ -12,7 +12,7 @@
#ifndef GINT_CORE_INTC
#define GINT_CORE_INTC
#include <defs/types.h>
#include <gint/defs/types.h>
//---
// SH7705 Interrupt Controller. Refer to:
@ -83,7 +83,7 @@ typedef struct
uint16_t :4;
);
} PACKED(4) sh7705_intc_ipc_t;
} GPACKED(4) sh7705_intc_ipc_t;
/* sh7705_intc_icr1_t - Interrupt Control Register 1 (general) */
typedef volatile word_union(sh7705_intc_icr1_t,
@ -106,12 +106,12 @@ typedef struct
union {
sh7705_intc_ipc_t _;
volatile uint16_t *IPRS[8];
} PACKED(4);
} GPACKED(4);
/* Control registers */
sh7705_intc_icr1_t *ICR1;
} PACKED(4) sh7705_intc_t;
} GPACKED(4) sh7705_intc_t;
@ -223,7 +223,7 @@ typedef volatile struct
);
pad(2);
} PACKED(4) sh7305_intc_ipc_t;
} GPACKED(4) sh7305_intc_ipc_t;
/* sh7305_intc_masks_t - Interrupt mask management
Writing 1 to IMR masks interrupts; writing 1 to IMCRs clears the masks.
@ -244,7 +244,7 @@ typedef volatile struct
uint8_t IMR11; pad(3);
uint8_t IMR12;
} PACKED(4) sh7305_intc_masks_t;
} GPACKED(4) sh7305_intc_masks_t;
/* sh7305_intc_userimask_t - User Interrupt Mask
@ -286,14 +286,14 @@ typedef struct
/* Other registers */
sh7305_intc_userimask_t *USERIMASK;
} PACKED(4) sh7305_intc_t;
} GPACKED(4) sh7305_intc_t;
//---
// Forward definitions
//---
/* Provided by core/gint.c */
extern sh7705_intc_t INTC3;
extern sh7305_intc_t INTC4;
extern sh7705_intc_t SH7705_INTC;
extern sh7305_intc_t SH7305_INTC;
#endif /* GINT_CORE_INTC */

View file

@ -8,7 +8,8 @@
#ifndef GINT_CORE_PFC
#define GINT_CORE_PFC
#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 Pin Function Controller. Refer to:
@ -66,7 +67,7 @@ typedef volatile struct
uint8_t PNDR;
pad(1);
} PACKED(4) sh7705_pfc_t;
} GPACKED(4) sh7705_pfc_t;
#define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100))

View file

@ -5,6 +5,8 @@
#ifndef GINT_CORE_RTC
#define GINT_CORE_RTC
#include <gint/defs/attributes.h>
//---
// Hybrid SH7705-SH7305 Real-Time Clock. Refer to:
// "Renesas SH7705 Group Hardware Manual"
@ -22,7 +24,7 @@ typedef struct
);
pad(1);
} PACKED(2) rtc_BCD2_t;
} GPACKED(2) rtc_BCD2_t;
/* sh7705_rtc_t, sh7305_rtc_t - Date and time access, RTC control */
typedef volatile struct
@ -68,7 +70,7 @@ typedef volatile struct
);
pad(1);
} PACKED(4) rtc_t;
} GPACKED(4) rtc_t;
#define SH7705_RTC (*((rtc_t *)0xfffffec0))
#define SH7305_RTC (*((rtc_t *)0xa413fec0))

View file

@ -5,7 +5,7 @@
#ifndef GINT_RTC
#define GINT_RTC
#include <defs/types.h>
#include <gint/defs/types.h>
//---
// Time management
@ -45,14 +45,14 @@ void rtc_set_time(const rtc_time_t *time);
/* rtc_frequency_t - possible frequency settings for the RTC's interrupt */
typedef enum
{
rtc_500mHz = 7,
rtc_1Hz = 6,
rtc_2Hz = 5,
rtc_4Hz = 4,
rtc_16Hz = 3,
rtc_64Hz = 2,
rtc_256Hz = 1,
rtc_none = 0,
RTC_500mHz = 7,
RTC_1Hz = 6,
RTC_2Hz = 5,
RTC_4Hz = 4,
RTC_16Hz = 3,
RTC_64Hz = 2,
RTC_256Hz = 1,
RTC_NONE = 0,
} rtc_frequency_t;
@ -70,8 +70,8 @@ typedef enum
up a 1 Hz-callback when the current time ends in 950 ms will result in the
callback being called after 50 ms, then every second. This is not a problem
for most uses. */
void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void *arg),
void *arg);
void rtc_start_timer(rtc_frequency_t freq,
int (*callback)(volatile void *arg), volatile void *arg);
/* rtc_stop_timer() - stop the RTC timer
This function stops the RTC timer that was set up with rtc_start_timer(). If

View file

@ -5,9 +5,8 @@
#ifndef GINT_TIMER
#define GINT_TIMER
#include <defs/attributes.h>
#include <defs/types.h>
#include <core/mpu.h>
#include <gint/defs/types.h>
#include <gint/mpu.h>
/* Timer identifiers
@ -88,7 +87,7 @@ typedef enum
@callback Callback function (called when the timer fires)
@arg Passed as argument to the callback function */
int timer_setup(int timer, uint32_t delay, timer_input_t clock,
int (*callback)(void *arg), void *arg);
int (*callback)(volatile void *arg), volatile void *arg);
/* timer_delay() - compute a delay constant from a duration in seconds
@ -108,7 +107,7 @@ int timer_setup(int timer, uint32_t delay, timer_input_t clock,
@timer The timer you are planning to use
@delay_us Requested delay in microseconds */
uint32_t timer_delay(int timer, int delay_us);
uint32_t timer_delay(int timer, uint64_t delay_us);
/* timer_start() - start a configured timer
The specified timer will start counting down and fire callbacks at regular
@ -142,4 +141,14 @@ void timer_pause(int timer);
@timer Timer id, as returned by timer_setup() */
void timer_stop(int timer);
//---
// Predefined timer callbacks
//---
/* timer_timeout() - callback that sets a flag and halts the timer
This predefined callback may be used when a timeout is required. It sets its
argument pointer to 1 and halts the timer. The pointer must be of type
int * and you must declare the variable as volatile int. */
int timer_timeout(volatile void *arg);
#endif /* GINT_TIMER */

176
make/Makefile Executable file
View file

@ -0,0 +1,176 @@
#! /usr/bin/make -f
#
# gint project Makefile
#
#---
#
# Build configuration
#
# Require configuration file (if you want to clean up and lost the file, you
# can either reconfigure or just delete the build directory)
CONFIG := Makefile.cfg
ifeq "$(wildcard $(CONFIG))" ""
$(error "config file $(CONFIG) does not exist (reconfigure or wipe directory)")
endif
include $(CONFIG)
# Machine flags, defaults are provided for common toolchains
ifeq "$(CONFIG.TOOLCHAIN)" "sh3eb-elf"
machine := -m3 -mb
endif
ifeq "$(CONFIG.TOOLCHAIN)" "sh4eb-nofpu-elf"
machine := -m4 -mb
endif
# Compiler flags, assembler flags, dependency generation, archiving
cflags := $(machine) -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
-fstrict-volatile-bitfields -I ../include $(CONFIG.MACROS) \
$(CONFIG.CFLAGS)
sflags := $(CONFIG.MACROS)
dflags = -MMD -MT $@ -MF $(@:.o=.d) -MP
arflags :=
#
# File listings
#
# Target file (CONFIG.TARGET is either "fx" or "cg")
target := libgint-$(CONFIG.TARGET).a
# Automatic names for object and dependency files
src2obj = $(1:../src/%=src/%).o
src2dep = $(1:../src/%=src/%).d
# Source files
prune-fx := "render-cg"
prune-cg := "render-fx"
src := $(shell find ../src \
-name $(prune-$(CONFIG.TARGET)) -prune \
-o -name '*.[csS]' -print)
src_obj := $(foreach s,$(src),$(call src2obj,$s))
# Files with special handling
spe := ../src/font5x6.png
spe_obj := version.o $(foreach s,$(spe),$(call src2obj,$s))
# All object files
obj := $(src_obj) $(spe_obj)
#
# Toolchain
#
gcc = $(CONFIG.TOOLCHAIN)-gcc
as = $(CONFIG.TOOLCHAIN)-as
ld = $(CONFIG.TOOLCHAIN)-ld
ar = $(CONFIG.TOOLCHAIN)-ar
objcopy = $(CONFIG.TOOLCHAIN)-objcopy
conv = fxconv
#
# Version management
#
# Version symbol is obtained by using the last commit hash on 7 nibbles
version_hash = 0x0$(shell git show --format='%h' HEAD | tr -d "\n")
#
# Build rules
#
all: $(target)
$(target): $(obj)
$(call cmd_l,ar,$@) $(ar) rcs $(arflags) $@ $^
# Assembler sources
src/%.s.o: ../src/%.s src/%.s.d
@ mkdir -p $(dir $@)
$(call cmd_b,as,$*.s) $(gcc) -c $< -o $@ $(sflags)
src/%.S.o: ../src/%.S src/%.S.d
@ mkdir -p $(dir $@)
$(call cmd_b,as,$*.S) $(gcc) -c $< -o $@ $(sflags)
# C sources
src/%.c.o: ../src/%.c src/%.c.d
@ mkdir -p $(dir $@)
$(call cmd_b,gcc,$*.c) $(gcc) -c $< -o $@ $(dflags) $(cflags)
# Special files
$(call src2obj,../src/font5x6.png): ../src/font5x6.png
@ mkdir -p $(dir $@)
$(call cmd_m,fxconv,font5x6.png)$(conv) -f $< -o $@ name:gint_font5x6 \
charset:ascii grid.size:5x6 grid.padding:1 grid.border:0
# Version symbol. ld generates a .stack section for unknown reasons; I remove
# it in the linker script.
version.o:
@ mkdir -p $(dir $@)
@ echo "_GINT_VERSION = $(version_hash);" > $@.txt
$(call cmd_b,ld,$@) $(ld) -r -R $@.txt -o $@
#
# Cleaning
#
clean:
@ rm -rf src version.o{,txt}
distclean: clean
@ rm -rf Makefile $(CONFIG) $(target)
#
# Installing
#
install: $(target)
install -d $(PREFIX)
install $(target) -m 755 $(PREFIX)
install ../$(CONFIG.TARGET.LONG).ld -m 644 $(PREFIX)
cp -r ../include/gint $(PREFIX)/include
uninstall:
rm -f $(PREFIX)/$(target)
rm -f $(PREFIX)/$(CONFIG.TARGET.LONG).ld
rm -rf $(PREFIX)/include/gint
#
# Utilities
#
# Directories: make conveniently leaves a '/' at the end of $(dir ...)
%/:
@ mkdir -p $@
# Don't try to unlink directories once they're built (that wouldn't work =p)
.PRECIOUS: %/
# Dependency information
-include $(shell [ -d src ] && find src -name *.d)
src/%.d: ;
.PRECIOUS: src/%.d
.PHONY: all clean distclean
# Do not output full commands by default
VERBOSE ?=
# Simple command output method
# $1 Program name
# $2 Argument string to display
# $3 Command color
define cmd
@ echo -e "\e[""$3"";1m>\e[0;1m $1\e[0m $2"
$(if $(VERBOSE),,@)
endef
# Some pre-colored command kinds: misc, build, link, clean, install
cmd_m = $(call cmd,$1,$2,30)
cmd_b = $(call cmd,$1,$2,32)
cmd_l = $(call cmd,$1,$2,36)
cmd_c = $(call cmd,$1,$2,31)
cmd_i = $(call cmd,$1,$2,33)

View file

@ -1,13 +1,12 @@
//---
// gint:core:freq - Clock frequency management
// gint:clock:freq - Clock frequency management
//---
#include <gint/drivers.h>
#include <gint/clock.h>
#include <defs/types.h>
#include <core/mpu.h>
#include <core/cpg.h>
#include <gint/mpu.h>
#include <gint/mpu/cpg.h>
//---
// Driver storage
@ -69,6 +68,11 @@ static void sh7705_probe(void)
}
#undef CPG
#else
/* This prototype will silence warnings on fxcg50 */
void sh7705_probe(void);
#endif /* FX9860G */
//---
@ -80,17 +84,17 @@ static void sh7705_probe(void)
static void sh7305_probe(void)
{
/* The meaning of the PLL setting on SH7305 differs from the
documentation of SH7224; the setting must not be doubled. */
documentation of SH7224; the value must not be doubled. */
int pll = CPG.FRQCRA.STC + 1;
freq.PLL = pll;
/* The FLL ratio is the value of the setting, possibly halved */
/* The FLL ratio is the value of the setting, halved if SELXM=1 */
int fll = CPG.FLLFRQ.FLF;
if(CPG.FLLFRQ.SELXM == 1) fll >>= 1;
freq.FLL = fll;
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but here
it's actually 1 / (2^setting + 1). */
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but
SH7305 behaves as 1 / (2^setting + 1). */
int divb = CPG.FRQCRA.BFC;
int divi = CPG.FRQCRA.IFC;
@ -125,7 +129,7 @@ static void init(void)
}
gint_driver_t drv_cpg = {
.name = "Clock Pulse Generator",
.name = "CPG",
.init = init,
.ctx_size = 0,
.sys_ctx = NULL,

View file

@ -0,0 +1,18 @@
//---
// gint:clock:sleep - Various low-level sleep functions
//---
#include <gint/clock.h>
#include <gint/timer.h>
/* sleep_us() - sleep for a definite duration in microseconds */
void sleep_us(int tid, int us_delay)
{
volatile int flag = 0;
timer_setup(tid, timer_delay(tid, us_delay), 0, timer_timeout,
(void *)&flag);
timer_start(tid);
while(!flag) sleep();
}

View file

@ -2,9 +2,11 @@
// gint:core:bootlog - Boot-time on-screen log for extreme debugging
//---
#include <defs/types.h>
#include <core/intc.h>
#include <core/mpu.h>
/* TODO: Review, enhance and fix bootlog */
#include <gint/defs/types.h>
#include <gint/mpu.h>
#include <gint/mpu/intc.h>
#include <core/mmu.h>
#include <gint/gint.h>
@ -18,7 +20,7 @@ extern char
void bootlog_loaded(void)
{
/* Version string - the string constant resides in ROM */
uint32_t v = gint_version();
uint32_t v = GINT_VERSION;
const char *model = "gint #0.0-000";
char str[14];
@ -43,7 +45,7 @@ void bootlog_loaded(void)
uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss;
/* MPU type */
mpu_t mpu = mpu_id();
mpu_t mpu = gint_mpu();
const char *names = "SH7337\0 SH7305\0 SH7355\0 SH7724";
/* TODO: Use a solid API for boot-time printing */
@ -87,28 +89,28 @@ void bootlog_kernel(void)
if(isSH3())
{
print(1, 5, "ABCD");
print_hex( 6, 5, INTC3._.IPRA->word, 4);
print_hex(10, 5, INTC3._.IPRB->word, 4);
print_hex(14, 5, INTC3._.IPRC->word, 4);
print_hex(18, 5, INTC3._.IPRD->word, 4);
print_hex( 6, 5, SH7705_INTC._.IPRA->word, 4);
print_hex(10, 5, SH7705_INTC._.IPRB->word, 4);
print_hex(14, 5, SH7705_INTC._.IPRC->word, 4);
print_hex(18, 5, SH7705_INTC._.IPRD->word, 4);
print(1, 6, "EFGH");
print_hex( 6, 6, INTC3._.IPRE->word, 4);
print_hex(10, 6, INTC3._.IPRF->word, 4);
print_hex(14, 6, INTC3._.IPRG->word, 4);
print_hex(18, 6, INTC3._.IPRH->word, 4);
print_hex( 6, 6, SH7705_INTC._.IPRE->word, 4);
print_hex(10, 6, SH7705_INTC._.IPRF->word, 4);
print_hex(14, 6, SH7705_INTC._.IPRG->word, 4);
print_hex(18, 6, SH7705_INTC._.IPRH->word, 4);
}
else
{
print(1, 5, "ACFG");
print_hex( 6, 5, INTC4._->IPRA.word, 4);
print_hex(10, 5, INTC4._->IPRC.word, 4);
print_hex(14, 5, INTC4._->IPRF.word, 4);
print_hex(18, 5, INTC4._->IPRG.word, 4);
print_hex( 6, 5, SH7305_INTC._->IPRA.word, 4);
print_hex(10, 5, SH7305_INTC._->IPRC.word, 4);
print_hex(14, 5, SH7305_INTC._->IPRF.word, 4);
print_hex(18, 5, SH7305_INTC._->IPRG.word, 4);
print(1, 6, "HJKL");
print_hex( 6, 6, INTC4._->IPRH.word, 4);
print_hex(10, 6, INTC4._->IPRJ.word, 4);
print_hex(14, 6, INTC4._->IPRK.word, 4);
print_hex(18, 6, INTC4._->IPRL.word, 4);
print_hex( 6, 6, SH7305_INTC._->IPRH.word, 4);
print_hex(10, 6, SH7305_INTC._->IPRJ.word, 4);
print_hex(14, 6, SH7305_INTC._->IPRK.word, 4);
print_hex(18, 6, SH7305_INTC._->IPRL.word, 4);
}
Bdisp_PutDisp_DD();

View file

@ -3,13 +3,13 @@
//---
#include <gint/gint.h>
#include <core/mpu.h>
#include <core/intc.h>
#include <core/memory.h>
#include <gint/mpu.h>
#include <gint/mpu/intc.h>
/* Interrupt controllers */
GDATA3 sh7705_intc_t INTC3 = {
GDATA3 sh7705_intc_t SH7705_INTC = {
.IPRS = {
(void *)0xfffffee2, (void *)0xfffffee4,
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
@ -18,7 +18,7 @@ GDATA3 sh7705_intc_t INTC3 = {
.ICR1 = (void *)0xa4000010,
};
GDATA sh7305_intc_t INTC4 = {
GDATA sh7305_intc_t SH7305_INTC = {
.IPRS = (void *)0xa4080000,
.MSK = (void *)0xa4080080,
.MSKCLR = (void *)0xa40800c0,
@ -37,8 +37,8 @@ int gint_intlevel(int intid, int level)
level &= 0xf;
ipr = isSH3()
? INTC3.IPRS[intid >> 2] /* SH3-based */
: &INTC4.IPRS[2 * (intid >> 2)]; /* SH4-based */
? SH7705_INTC.IPRS[intid >> 2] /* SH3-based */
: &SH7305_INTC.IPRS[2 * (intid >> 2)]; /* SH4-based */
int oldlevel = (*ipr >> shift) & 0xf;
*ipr = (*ipr & ~(0xf << shift)) | (level << shift);

View file

@ -29,9 +29,9 @@
- It is possible to map MPU-specific blocks at runtime, to avoid checking
the MPU each time an interrupt is handled;
- However, the blocks cannot rely on relative displacements or cross-
references unless their relative order is fully known. This happens with
the timer driver, and it works swimmingly; just be careful. */
- However, the blocks can only rely on relative displacements or cross-
references if their relative order is known and heeded for. A good example
of this is the timer driver. Please be careful. */
/* SH7305-TYPE DEBUG INTERRUPT HANDLER - 26 BYTES */

View file

@ -1,4 +1,5 @@
#include <core/mpu.h>
#include <gint/defs/attributes.h>
#include <gint/mpu.h>
#include <stddef.h>
#include <stdint.h>
@ -85,7 +86,7 @@ void *memcpy(void * restrict dst, const void * restrict src, size_t n)
return dst;
}
void *_memmove(UNUSED void *dst, UNUSED const void *src, UNUSED size_t n)
void *_memmove(GUNUSED void *dst, GUNUSED const void *src, GUNUSED size_t n)
{
// (same as memcpy, but heed for direction if areas overlap)
@ -94,7 +95,7 @@ void *_memmove(UNUSED void *dst, UNUSED const void *src, UNUSED size_t n)
return dst;
}
int _memcmp(UNUSED const void *s1, UNUSED const void *s2, UNUSED size_t n)
int _memcmp(GUNUSED const void *s1, GUNUSED const void *s2, GUNUSED size_t n)
{
return 0;
}

View file

@ -11,14 +11,14 @@
#ifdef FX9860G
/* tlb_addr() - get the P4 address of a TLB address entry */
INLINE const tlb_addr_t *tlb_addr(uint way, uint E)
GINLINE const tlb_addr_t *tlb_addr(uint way, uint E)
{
uint32_t addr = 0xf2000000 | (E << 12) | (way << 8);
return (void *)addr;
}
/* tlb_data() - get the P4 address of a TLB data entry */
INLINE const tlb_data_t *tlb_data(uint way, uint E)
GINLINE const tlb_data_t *tlb_data(uint way, uint E)
{
uint32_t addr = 0xf3000000 | (E << 12) | (way << 8);
return (void *)addr;
@ -57,14 +57,14 @@ void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
//---
/* utlb_addr() - get the P4 address of a UTLB address entry */
INLINE const utlb_addr_t *utlb_addr(uint E)
GINLINE const utlb_addr_t *utlb_addr(uint E)
{
uint32_t addr = 0xf6000000 | ((E & 0x3f) << 8);
return (void *)addr;
}
/* utlb_data() - get the P4 address of a UTLB data entry */
INLINE const utlb_data_t *utlb_data(uint E)
GINLINE const utlb_data_t *utlb_data(uint E)
{
uint32_t addr = 0xf7000000 | ((E & 0x3f) << 8);
return (void *)addr;

View file

@ -2,15 +2,16 @@
// gint:core:mpu - Runtime MPU detection
//---
#include <core/mpu.h>
#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/mpu.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
#include <gint/defs/util.h>
/* This file is only useful on fx9860g machines because all fxcg50 are SH4 */
#ifdef FX9860G
/* Holds the name of the current MPU; initialized at startup by mpu_init() */
GBSS const mpu_t mpu;
GBSS const mpu_t gint_mpu_id;
/* mpu_detect() - detect the underlying MPU
Many thanks to Simon Lothar for relevant documentation.
@ -54,7 +55,7 @@ static mpu_t mpu_detect(void)
/* mpu_init() - detect and save information about the underlying MPU */
void mpu_init(void)
{
const_cast(mpu, mpu_t) = mpu_detect();
const_cast(gint_mpu_id, mpu_t) = mpu_detect();
}
#endif

View file

@ -6,8 +6,8 @@
#include <gint/drivers.h>
#include <core/memory.h>
#include <core/setup.h>
#include <core/intc.h>
#include <core/mpu.h>
#include <gint/mpu.h>
#include <gint/mpu/intc.h>
/* VBR address, from the linker script */
extern char gint_vbr;
@ -24,34 +24,26 @@ typedef struct
{
uint16_t iprs[12];
} PACKED(2) gint_core_ctx;
} GPACKED(2) gint_core_ctx;
/* gint_ctx_save() - save interrupt controller configuration
@arg ctx gint core context object */
static void gint_ctx_save(gint_core_ctx *ctx)
{
if(isSH3())
{
for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]);
}
else
{
for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i];
}
if(isSH3()) for(int i = 0; i < 8; i++)
ctx->iprs[i] = *(SH7705_INTC.IPRS[i]);
else for(int i = 0; i < 12; i++)
ctx->iprs[i] = SH7305_INTC.IPRS[2 * i];
}
/* gint_ctx_restore() - restore interrupt controller configuration
@arg ctx gint core context object */
static void gint_ctx_restore(gint_core_ctx *ctx)
{
if(isSH3())
{
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i];
}
else
{
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i];
}
if(isSH3()) for(int i = 0; i < 8; i++)
*(SH7705_INTC.IPRS[i]) = ctx->iprs[i];
else for(int i = 0; i < 12; i++)
SH7305_INTC.IPRS[2 * i] = ctx->iprs[i];
}
//---
@ -65,14 +57,10 @@ GBSS static gint_core_ctx sys_ctx;
static void lock(void)
{
/* Just disable everything, drivers will enable what they support */
if(isSH3())
{
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000;
}
else
{
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000;
}
if(isSH3()) for(int i = 0; i < 8; i++)
*(SH7705_INTC.IPRS[i]) = 0x0000;
else for(int i = 0; i < 12; i++)
SH7305_INTC.IPRS[2 * i] = 0x0000;
}
/* gint_install() - install and start gint */
@ -122,7 +110,7 @@ static void unlock(void)
/* Restore all driver settings, but do it in reverse order of loading
to honor the dependency system */
for(gint_driver_t *drv = &edrv; (--drv) >= &edrv;)
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
{
if(drv->unload) drv->unload();
if(drv->ctx_restore) drv->ctx_restore(drv->sys_ctx);

View file

@ -2,13 +2,13 @@
// gint:core:start - Kernel initialization and C runtime
//--
#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
#include <core/bootlog.h>
#include <core/mpu.h>
#include <core/mmu.h>
#include <gint/drivers.h>
#include <gint/gint.h>
#include <gint/mpu.h>
/* Symbols provided by the linker script. For sections:
- l* represents the load address (source address in ROM)
@ -31,7 +31,7 @@ int main(int isappli, int optnum);
@l Source pointer (load address)
@s Size of area (should be a multiple of 16)
@r Destination pointer (relocation address) */
SECTION(".pretext")
GSECTION(".pretext")
static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
{
while(s > 0)
@ -48,7 +48,7 @@ static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
/* regclr() - clear a memory region using symbol information
@r Source pointer (base address)
@s Size of area (should be a multiple of 16) */
SECTION(".pretext")
GSECTION(".pretext")
static void regclr(uint32_t *r, int32_t s)
{
while(s > 0)
@ -67,10 +67,10 @@ static void regclr(uint32_t *r, int32_t s)
corresponds to region [b; b+s) when b and s are 1k-aligned.
@b Base pointer: first address checked
@s Size of region */
SECTION(".pretext")
GSECTION(".pretext")
static void explore(volatile void *b, int32_t s)
{
ATTR(unused) uint8_t x;
GUNUSED uint8_t x;
while(s > 0)
{
@ -84,7 +84,7 @@ static void explore(volatile void *b, int32_t s)
/* acall() - call an array of functions (constructors or destructors)
@f First element of array
@l First element outside of the array */
SECTION(".pretext")
GSECTION(".pretext")
static void acall(void (**f)(void), void (**l)(void))
{
while(f < l) (*(*f++))();
@ -94,7 +94,7 @@ static void acall(void (**f)(void), void (**l)(void))
/* start() - this is where it all starts
Returns a status code. Invoking main menu is better than returning! */
SECTION(".pretext.entry")
GSECTION(".pretext.entry")
int start(int isappli, int optnum)
{
/* We are currently in a dynamic userspace mapping of an add-in run
@ -108,9 +108,9 @@ int start(int isappli, int optnum)
redirecting interrupts and reimplementing drivers, so we can't rely
too much on the system. Ladies and gentlemen, let's have fun! ;D */
/* For now, we rely on the system to map the ROM pages. RAM is always
/* For now, we rely on the system to map ROM pages. RAM is always
fully mapped, but we need to initialize it. We also need to do some
hardware detection because old fx9860g models have an older
hardware detection because old fx9860g models have an different
processor with some incompatible features */
/* Detect architecture - this will tell SH3 from SH4 on fx9860g */
@ -123,10 +123,7 @@ int start(int isappli, int optnum)
regcpy(lgdata, sgdata, rgdata);
regcpy(ldata, sdata, rdata);
regclr(rbss, sbss);
#ifdef GINT_BOOT_LOG
bootlog_loaded();
#endif
/* Traverse all ROM pages */
explore(brom, srom);
@ -135,15 +132,12 @@ int start(int isappli, int optnum)
uint32_t rom, ram;
isSH3() ? tlb_mapped_memory(&rom, &ram)
: utlb_mapped_memory(&rom, &ram);
#ifdef GINT_BOOT_LOG
bootlog_mapped(rom, ram);
#endif
//---
/* Cancel add-in execution if not all pages are mapped
TODO: Resort to better graphical display, but still fxlib since
TODO: Resort to better graphical display, although still fxlib since
add-in is not mapped yet */
if(rom < (uint32_t)&srom)
{
@ -162,21 +156,21 @@ int start(int isappli, int optnum)
/* Install gint and switch VBR */
gint_install();
#ifdef GINT_BOOT_LOG
bootlog_kernel();
#endif
/* We are now running on our own in kernel mode. To avoid breaking the
system, we want to limit our use of syscalls, so we'll have to do
device driving ourselves. */
/* We are now running on our own in kernel mode. Since we have taken
control of interrupts, pretty much any interaction with the sytem
will break it. We'll limit our use of syscalls and do device driving
ourselves. (Hopefully we can add cool features in the process.) */
gint_driver_t *drv;
/* Initialize all drivers by saving the system settings */
for(drv = &bdrv; drv < &edrv; drv++)
{
/* Hook for old SH3 fx9860g machines */
if(isSH3() && drv->driver_sh3) drv->driver_sh3();
if(drv->ctx_save) drv->ctx_save(drv->sys_ctx);
if(drv->init) drv->init();
}
@ -185,19 +179,14 @@ int start(int isappli, int optnum)
application. We have already loaded the RAM sections earlier; all
that's left is calling the constructors and rolling main(). */
/* Call the constructors */
acall(btors, mtors);
/* Execute the user application */
int ret = main(isappli, optnum);
/* Call the destructors*/
acall(mtors, etors);
/* Before leaving the application, we need to clean up our mess. We
have changed many OS settings while accessing the peripheral
have changed many hardware settings while accessing the peripheral
modules. The OS is bound to be confused (and hang, or crash, or any
other kind of giving up) if we don't restore them */
other kind of giving up) if we don't restore them. */
/* Unload gint and give back control to the system. Driver settings
will be restored while interrupts are disabled */

BIN
src/font5x6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 754 B

118
src/keysc/getkey.c Normal file
View file

@ -0,0 +1,118 @@
//---
// gint:keysc:getkey - High-level keyboard monitoring function
//---
#include <gint/keyboard.h>
#include <gint/defs/types.h>
#ifdef FX9860G
#include <gint/drivers/t6k11.h>
#endif
/* Atom for counting time in getkey() is 32768 Hz, 1ull << ATOM is a second */
#define ATOM 15
/* Delay between a key press and the first repeat, in 1/32768 seconds */
static uint64_t rep_first;
/* Delay between subsequent repeats, in 1/32768 seconds */
static uint64_t rep_next;
/* getkey() - wait for a pressed key */
key_event_t getkey(void)
{
return getkey_opt(GETKEY_DEFAULT, NULL);
}
/* getkey_opt() - enhanced getkey() */
key_event_t getkey_opt(int opt, volatile int *timeout)
{
key_event_t ev;
int shift = 0, alpha = 0, key = 0;
/* Last pressed key (only this key may be repeated) */
int rep_key = 0;
/* Number of repeats already emitted */
int rep_count = 0;
/* Time elapsed since last repeat emission (in atoms) */
uint64_t rep_time = 0;
while(1) switch((ev = waitevent(timeout)).type)
{
/* Timeout has expired, return KEYEV_NONE */
case KEYEV_NONE:
return ev;
/* Key press: handle modifiers or return an event */
case KEYEV_DOWN:
key = ev.key;
/* Handle backlight on fx9860g */
#ifdef FX9860G
if(opt & GETKEY_BACKLIGHT && key == KEY_OPTN && shift)
{
t6k11_backlight(-1);
shift = 0;
continue;
}
#endif
/* Return to menu. TODO: use gint_switch() in getkey_opt() */
if(opt & GETKEY_MENU && key == KEY_MENU && !(alpha || shift))
{
continue;
}
/* Update modifiers */
if(opt & GETKEY_MOD_SHIFT && key == KEY_SHIFT)
{
shift ^= 1;
rep_key = 0;
continue;
}
if(opt & GETKEY_MOD_ALPHA && key == KEY_ALPHA)
{
alpha ^= 1;
rep_key = 0;
continue;
}
/* Return current event */
rep_key = key;
rep_count = 0;
rep_time = 0;
ev.mod = 1;
ev.shift = shift;
ev.alpha = alpha;
return ev;
/* Return new events when a key is held (maybe) */
case KEYEV_HOLD:
if(ev.key != rep_key) break;
rep_time += (1ull << ATOM) / KEYBOARD_SCAN_FREQUENCY;
/* If the key is key pressed long enough, create a new event */
if(rep_time < (rep_count ? rep_next : rep_first)) break;
ev.mod = 1;
ev.shift = shift;
ev.alpha = alpha;
return ev;
/* Reset repeating information if the repeated key is released */
case KEYEV_UP:
if(ev.key != rep_key) break;
rep_key = 0;
rep_count = 0;
rep_time = 0;
break;
}
}
/* getkey_repeat() - set repeat delays for getkey() */
void getkey_repeat(int first, int next)
{
rep_first = ((uint64_t)first << ATOM) / 1000;
rep_next = ((uint64_t)next << ATOM) / 1000;
}

View file

@ -2,8 +2,8 @@
// gint:keysc:iokbd - I/O-based keyboard input
//---
#include <defs/types.h>
#include <core/pfc.h>
#include <gint/defs/types.h>
#include <gint/mpu/pfc.h>
/* This file is SH7705-only. */
#ifdef FX9860G
@ -104,6 +104,7 @@ uint8_t iokbd_row(int row)
return input;
}
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data */
void iokbd_scan(uint8_t *scan)
{
/* Scan each row independently; the gain from scanning them altogether

View file

@ -5,10 +5,12 @@
#include <gint/drivers.h>
#include <gint/gint.h>
#include <gint/timer.h>
#include <gint/clock.h>
#include <gint/keyboard.h>
#include <core/mpu.h>
#include <defs/attributes.h>
#include <gint/drivers/iokbd.h>
#include <gint/defs/attributes.h>
#include <gint/mpu.h>
//---
// Keyboard buffer
@ -16,13 +18,15 @@
/* The driver's internal state. At each step of time, this file compares the
internal state with the hardware state and generates events accordingly.
Events can be seen as a delta-encoding of the state of the keyboard over
time, and this buffer must be the sum of all events. This means that if an
event cannot be generated, f.i. because the buffer is full, this state must
*not* be updated. */
Events can be seen as a delta-encoding of the keyboard state over time.
The user which sums up these events to maintain a full keyboard state must
get a correct result. As a consequence, if an event cannot be generated
(whatever the reason), the driver's internal copy of the keyboard state must
not be updated (probably the event will be re-emitted at the next scan). */
GDATA volatile uint8_t state[12] = { 0 };
/* A driver event, which is a change in a full row, not a key. */
/* A driver event, which is a change in a full row instead of a single key. */
typedef struct
{
uint time :12; /* Locally unique time identifier */
@ -42,7 +46,6 @@ GDATA static int buffer_end = 0;
/* Current time, in keyboard-scanning ticks */
GDATA int time = 0;
GDATA int full_release = 2;
/* buffer_push() - add an event in the keyboard buffer
Returns non-zero if the event cannot be pushed. */
@ -67,15 +70,13 @@ static int buffer_poll(driver_event_t *ev)
return 0;
}
/* keysc_frame() - generate a round of interrupts for the current frame */
/* keysc_frame() - generate a round of events for the current frame */
void keysc_frame(void)
{
ALIGNED(2) uint8_t scan[12] = { 0 };
time++;
GALIGNED(2) uint8_t scan[12] = { 0 };
/* First scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */
if(isSH3()) iokbd_scan(&scan);
if(isSH3()) iokbd_scan(scan);
else
{
volatile uint16_t *KEYSC = (void *)0xa44b0000;
@ -103,8 +104,8 @@ void keysc_frame(void)
}
}
/* key_poll() - poll the next keyboard event from the buffer */
key_event_t key_poll(void)
/* pollevent() - poll the next keyboard event */
key_event_t pollevent(void)
{
/* Every time a driver event is unqueued, its key events are generated
and put in this small buffer */
@ -112,14 +113,14 @@ key_event_t key_poll(void)
/* Number of pending events in the previous buffer */
static int events_pending = 0;
/* If there are pending events, use them */
/* Use pending events first, then poll the driver buffer */
if(events_pending > 0) return events[--events_pending];
/* Unqueue something from the driver buffer, if possible */
driver_event_t ev;
if(buffer_poll(&ev)) return (key_event_t){ .type = KEYEV_NONE };
/* Generate new key events */
/* Generate new key events and return the first of them*/
int old = ev.old << 1;
int new = ev.new;
@ -139,35 +140,55 @@ key_event_t key_poll(void)
events[events_pending++] = keyev;
}
/* Return the first of these generated events */
return events[--events_pending];
}
/* waitevent() - wait for the next keyboard event */
key_event_t waitevent(volatile int *timeout)
{
key_event_t none = { .type = KEYEV_NONE };
do {
key_event_t ev = pollevent();
if(ev.type != KEYEV_NONE) return ev;
sleep();
} while(!(timeout && *timeout));
return none;
}
//---
// Driver initialization
//---
static int callback(UNUSED void *arg)
static int callback(GUNUSED volatile void *arg)
{
keysc_frame();
time++;
return 0;
}
/* init() - setup the support timer */
static void init(void)
{
int tid = isSH3() ? 4 : 7;
int tid = isSH3() ? 3 : 8;
/* 32768 Hz divided by 16 is 2048 */
/* TODO: Use I/O port scanning on SH3 */
timer_setup(tid, 2048, 0, callback, NULL);
/* Configure the timer to do 128 keyboard scans per second. This
frequency *must* be high for the KEYSC interface to work! */
/* Note: the supporting timer always runs at 32768 Hz. */
/* TODO: The SH3 does not need to run fast, adjust user settings? */
int delay = 32768 / KEYBOARD_SCAN_FREQUENCY;
if(!delay) delay = 1;
timer_setup(tid, delay, 0, callback, NULL);
timer_start(tid);
}
/* unload() - stop the support timer */
static void unload(void)
{
int tid = isSH3() ? 4 : 7;
int tid = isSH3() ? 3 : 8;
timer_stop(tid);
}

View file

@ -3,7 +3,7 @@
//---
#include <gint/drivers.h>
#include <defs/types.h>
#include <gint/defs/types.h>
#ifdef FXCG50
@ -56,7 +56,7 @@ GDATA static volatile uint16_t *intf = (void *)0xb4000000;
/* Bit 4 of Port R controls the RS bit of the display driver */
GDATA static volatile uint8_t *PRDR = (void *)0xa405013c;
INLINE static void select(uint16_t reg)
GINLINE static void select(uint16_t reg)
{
/* Clear RS and write the register number */
*PRDR &= ~0x10;
@ -72,12 +72,12 @@ INLINE static void select(uint16_t reg)
synco();
}
INLINE static uint16_t read(void)
GINLINE static uint16_t read(void)
{
return *intf;
}
INLINE static void write(uint16_t data)
GINLINE static void write(uint16_t data)
{
*intf = data;
}
@ -261,7 +261,7 @@ typedef struct
/* Graphics RAM range */
uint16_t HSA, HEA, VSA, VEA;
} PACKED(2) ctx_t;
} GPACKED(2) ctx_t;
/* Allocate one buffer in gint's storage section */
GBSS static ctx_t sys_ctx;
@ -301,7 +301,7 @@ static void ctx_restore(void *buf)
//---
gint_driver_t drv_r61524 = {
.name = "Renesas R61524",
.name = "R61524",
.init = NULL,
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx,

0
src/render-cg/dclear.c Normal file
View file

44
src/render-fx/bopti-asm.h Normal file
View file

@ -0,0 +1,44 @@
//---
// gint:render-fx:bopti-asm - Assembler drawing routines for bopti
//---
#ifndef GINT_RENDERFX_BOPTIASM
#define GINT_RENDERFX_BOPTIASM
/* pair_t: A pair of consecutive VRAM longwords */
typedef struct {
uint32_t l;
uint32_t r;
} pair_t;
/* quadr_t: Two pairs for light and gray VRAMs */
typedef struct {
uint32_t l1;
uint32_t r1;
uint32_t l2;
uint32_t r2;
} quadr_t;
/* Signature of mono rendering functions */
typedef pair_t asm_mono_t(pair_t p, void **layer, uint32_t *masks, int x);
/* Signature of gray rendering functions */
typedef quadr_t asm_gray_t(quadr_t q, void **layer, uint32_t *masks, int x);
/* Each of the following rendering functions:
1. Takes VRAM data for two longword positions of the screen.
2. Reads data for one longword position of the image from *layer. This
consists in n longwords where n is the number of layers in the image.
3. Increments *layer by 4*n.
4. Shifts the image data and apply it to the VRAM positions in accordance
with the two masks given in the masks argument. */
/* bopti_asm_mono(): Rendering function for the "mono" profile */
extern asm_mono_t bopti_asm_mono;
/* bopti_asm_mono_alpha(): Rendering function for the "mono alpha" profile */
extern asm_mono_t bopti_asm_mono_alpha;
/* bopti_asm_gray(): Rendering function for the "gray" profile */
extern asm_gray_t bopti_asm_gray;
/* bpoti_asm_gray_alpha(): Rendering function for the "gray alpha" profile */
extern asm_gray_t bopti_asm_gray_alpha;
#endif /* GINT_RENDERFX_BOPTIASM */

110
src/render-fx/bopti-asm.s Normal file
View file

@ -0,0 +1,110 @@
.global _bopti_asm_mono
.global _bopti_asm_mono_alpha
.global _bopti_asm_gray
.global _bopti_asm_gray_alpha
# REGISTER ALLOCATION:
# r0: layer (left)
# r1: layer (right)
# r2: (temp)
# r3: -
# --
# r4: vram (left)
# r5: vram (right)
# r6: layer pointer; f(x&31)
# r7: mask pointer
# --
# @r15: -(x&31)
_bopti_asm_mono:
# Read data longword and update the layer address pointer
mov.l @r6, r2
mov.l @r2+, r0
mov.l r2, @r6
mov r0, r1
# Shift the layer data
mov.l @r15, r6
shld r6, r0
add #32, r6
shld r6, r1
# Get the masks and clear the target VRAM area and unwanted image data
mov.l @r7, r2
and r2, r0
not r2, r2
and r2, r4
mov.l @(4, r7), r2
and r2, r1
not r2, r2
and r2, r5
# Do the drawing by joining the two
or r4, r0
rts
or r5, r1
# REGISTER ALLOCATION
# r0: layer_1 (left)
# r1: layer_1 (right)
# r2: layer_2 (left)
# r3: layer_2 (right)
# --
# r4: vram (left)
# r5: vram (right)
# r6: layer pointer; f(x&31); mask (left); mask (right)
# r7: masks pointer
# --
# @r15: -(x&31)
_bopti_asm_mono_alpha:
# Read data longwords and update the layer address pointer
mov.l @r6, r2
mov.l @r2+, r0
mov.l @r2+, r3
mov.l r2, @r6
mov r0, r1
mov r3, r2
# Shift all layer data
mov.l @r15, r6
shld r6, r0
shld r6, r2
add #32, r6
shld r6, r1
shld r6, r3
# Apply the masks on the layer data
mov.l @r7, r6
and r6, r0
and r6, r2
mov.l @(4, r7), r6
and r6, r1
and r6, r3
# Blit the clear on the VRAM
not r0, r0
and r4, r0
not r1, r1
and r5, r1
# Blit the write on the VRAM
or r2, r0
rts
or r3, r1
# REGISTER ALLOCATION
# TODO: _bopti_asm_gray
_bopti_asm_gray:
rts
nop
# REGISTER ALLOCATION
# TODO: _bopti_asm_gray_alpha
_bopti_asm_gray_alpha:
rts
nop

268
src/render-fx/bopti.c Normal file
View file

@ -0,0 +1,268 @@
#define GINT_NEED_VRAM
#include <gint/defs/types.h>
#include <gint/display.h>
#include <display/fx.h>
#include "bopti-asm.h"
/* struct rbox: A rendering box (target coordinates and source rectangle)
Some of the data here is redundant, but makes things easier. */
struct rbox
{
/* Left pixel of the first column to be drawn, even if this column is
not drawn entirely */
int x;
/* On-screen location of top-left corner */
int visual_x, y;
/* Width of rendered sub-image */
int width;
/* Horizontal bounds of the box in the image (included, in columns) */
int left, right;
/* Vertical bounds of the box in the image (inc-excluded, in pixels) */
int top, bottom;
};
/* List of rendering functions */
void *bopti_asm[4] = {
bopti_asm_mono, /* asm_mono_t */
bopti_asm_mono_alpha, /* asm_mono_t */
bopti_asm_gray, /* asm_gray_t */
bopti_asm_gray_alpha, /* asm_gray_t */
};
/* struct command: A rendering command
Includes many computed parameters and handy information. Read-only. */
struct command
{
/* x-coordinate of rendering box & 31, used for shifts */
int x;
/* VRAM pointers */
uint32_t *v1;
uint32_t *v2;
/* Initial offset into VRAM */
int offset;
/* Number of VRAM columns affected by the bounding box; this is the
same as the number of rendered image columns if x=0, and this number
plus 1 otherwise. */
int columns;
/* A certain set of rendering masks (see bopti_render()) */
uint32_t *masks;
/* Whether the first column is real (ie. x>=0) or not */
int real_start;
/* Ignored elements between two rendered grid rows */
int vram_stride;
/* Ignored elements between two columns rendered grid columns */
int data_stride;
/* Assembly function, prototype depends on image type */
union {
void *asm_void;
asm_mono_t *asm_mono;
asm_gray_t *asm_gray;
};
};
void bopti_grid(void **layer, int rows, int gray, struct command *c)
{
/* Pointers to vram data */
uint32_t *v1 = c->v1, *v2 = c->v2;
/* Current offset into video RAM */
uint offset = c->offset;
/* Pairs of VRAM operands. A function that returns such a pair will be
optimized by GCC into a function returning into r0,r1 which will
avoid some memory accesses. */
pair_t p, pret = { 0 };
/* Same with two pairs for the gray version */
quadr_t q, qret = { 0 };
/* Monochrome version */
if(!gray) while(rows--)
{
p.r = pret.r = v1[offset & 0xff];
for(int col = 0; col < c->columns; col++)
{
/* Shift the pair to the left. When x=0, we should have
pret.r = p.r but due to some intentional UB with
32-bit shifts, pret.r != p.r so we reload p.r. */
p.l = (c->x) ? pret.r : p.r;
/* Load new second element, if offset+1 overflows from
the VRAM we load from offset 0. It doesn't matter
because the result will not be written back, I just
want to avoid reading from outside the VRAM. */
p.r = v1[(offset + 1) & 0xff];
/* The assembly routine blends a longword of data onto
the pair and returns the resulting pair. */
pret = c->asm_mono(p, layer, c->masks+col+col, -c->x);
/* Write back the result into VRAM, except for column
-1 (occurs once every row, iff visual_x < 0) */
if(c->real_start + col) v1[offset] = pret.l;
offset++;
}
if(c->x) v1[offset] = pret.r;
*layer += c->data_stride;
offset += c->vram_stride;
}
/* Gray version */
else while(rows--)
{
if(c->real_start)
{
q.r1 = qret.r1 = v1[offset & 0xff];
q.r2 = qret.r2 = v2[offset & 0xff];
}
/* Same as before, but 2 buffers at the same time */
for(int col = 0; col < c->columns; col++)
{
q.l1 = (c->x) ? qret.r1 : q.r1;
q.r1 = v1[(offset + 1) & 0xff];
q.l2 = (c->x) ? qret.r2 : q.r2;
q.r2 = v2[(offset + 1) & 0xff];
qret = c->asm_gray(q, layer, c->masks+col+col, -c->x);
if(c->real_start + col)
{
v1[offset] = qret.l1;
v2[offset] = qret.l2;
}
offset++;
}
if(c->x)
{
v1[offset] = qret.r1;
v2[offset] = qret.r2;
}
*layer += c->data_stride;
offset += c->vram_stride;
}
}
void bopti_render(image_t const *img, struct rbox *rbox)
{
/* Compute rendering masks */
uint32_t vm[4];
masks(rbox->visual_x, rbox->x + rbox->width - 1, vm);
/* For each pair of consecutive VRAM elements involved, create a mask
from the intersection of the standard vram mask with the shift-mask
related to x not being a multiple of 32 */
uint32_t masks[10] = {
0, vm[0],
vm[0], vm[1],
vm[1], vm[2],
vm[2], vm[3],
vm[3], 0,
};
uint32_t mx = 0xffffffff >> (rbox->x & 31);
for(int i = 0; i < 5; i++)
{
masks[2*i] &= mx;
masks[2*i+1] &= ~mx;
}
/* Position, in masks[], of the first column being rendered */
int left_origin = (rbox->x >> 5) + 1;
/* Number of columns in [img] */
int img_columns = (img->width + 31) >> 5;
/* Interwoven layer data. Skip left columns that are not rendered */
const uint32_t *layer = (void *)img->data;
layer += rbox->top * img_columns;
layer += rbox->left;
/* Number of grid columns */
int columns = rbox->right - rbox->left + 1;
/* Compute and execute the command for this parameters */
struct command c = {
.x = rbox->x & 31,
/* TODO: bopti: Support gray rendering */
.v1 = vram,
.v2 = vram,
.offset = (rbox->y << 2) + (rbox->x >> 5),
.columns = columns,
.masks = masks + 2 * left_origin,
.real_start = (left_origin > 0),
.vram_stride = 4 - columns,
.data_stride = (img_columns - columns) << 2,
.asm_void = bopti_asm[img->profile],
};
bopti_grid((void **)&layer, rbox->bottom - rbox->top, img->gray, &c);
}
void bopti_render_clip(int visual_x, int y, image_t const *img, int left,
int top, int width, int height)
{
/* Left pixel of leftmost column */
int x = visual_x - (left & 31);
width += (left & 31);
left &= ~31;
/* Adjust the bounding box of the input image */
if(left < 0) width += left, x -= left, left = 0;
if(top < 0) height += top, y -= top, top = 0;
if(left + width > img->width) width = img->width - left;
if(top + height > img->height) height = img->height - top;
/* Check whether the box intersects the screen */
if(width <= 0 || height <= 0) return;
if(x + width <= 0 || x > 127 || y + height <= 0 || y > 63) return;
/* Intersect with the bounding box on-screen. We only need to make sure
that x>=-31, not x>=0. Setting x=0 would discard the horizontal
alignment information (x & 31). */
if(y < 0) top -= y, height += y, y = 0;
if(y + height > 64) height = (64 - y);
int bottom = top + height;
if(x < -32)
{
int overflow = (x + 32) >> 5;
overflow = -overflow << 5;
left += overflow;
width -= overflow;
x += overflow;
}
if(x + width > 128) width = (128 - x);
int right = (left + width - 1) >> 5;
left >>= 5;
/* Finish with the standard bopti renderer */
struct rbox rbox = { x, visual_x, y, width, left, right, top, bottom };
bopti_render(img, &rbox);
}
void bopti_render_noclip(int visual_x, int y, image_t const *img, int left,
int top, int width, int height)
{
/* End row (excluded) */
int bottom = top + height;
/* Left pixel of leftmost column */
int x = visual_x - (left & 31);
width += (left & 31);
/* Start column and end column (included) */
left >>= 5;
int right = (left + width - 1) >> 5;
/* Finish with the standard bopti renderer */
struct rbox rbox = { x, visual_x, y, width, left, right, top, bottom };
bopti_render(img, &rbox);
}

39
src/render-fx/dclear.c Normal file
View file

@ -0,0 +1,39 @@
#define GINT_NEED_VRAM
#include <gint/display.h>
/* dclear() - fill the screen with a single color */
void dclear(color_t color)
{
/* SuperH only supports a single write-move addressing mode, which is
pre-decrement write; the other similar mode is post-increment
read. So we'll use pre-decrement writes to improve performance. */
if(color != color_white && color != color_black) return;
uint32_t fill = -(color >> 1);
uint32_t *index = vram + 256;
while(index > vram)
{
/* Do it by batches to avoid losing cycles on loop tests */
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
*--index = fill;
}
}

136
src/render-fx/dline.c Normal file
View file

@ -0,0 +1,136 @@
#define GINT_NEED_VRAM
#include <gint/defs/util.h>
#include <gint/display.h>
#include <display/fx.h>
/* dhline() - optimized drawing of a horizontal line using a rectangle mask
@x1 @x2 @y Coordinates of endpoints of line (both included)
@color Allowed colors: white, black, none, reverse */
static void dhline(int x1, int x2, int y, color_t color)
{
if((uint)y >= 64) return;
if(x1 > x2) swap(x1, x2);
uint32_t *lword = vram + (y << 2) + 4;
uint32_t m[4];
/* Get the masks for the [x1, x2] range */
masks(x1, x2, m);
switch(color)
{
case color_white:
*--lword &= ~m[3];
*--lword &= ~m[2];
*--lword &= ~m[1];
*--lword &= ~m[0];
break;
case color_black:
*--lword |= m[3];
*--lword |= m[2];
*--lword |= m[1];
*--lword |= m[0];
break;
case color_reverse:
*--lword ^= m[3];
*--lword ^= m[2];
*--lword ^= m[1];
*--lword ^= m[0];
break;
default:
return;
}
}
/* dvline() - optimized drawing of a vertical line
This variant is less powerful than dhline() because the line-based structure
of the vram cannot be used here.
@y1 @y2 @x Coordinates of endpoints of line (both included)
@color Allowed colors: black, white, none, reverse */
static void dvline(int y1, int y2, int x, color_t operator)
{
if((uint)x >= 128) return;
if(y1 > y2) swap(y1, y2);
uint32_t *base = vram + (y1 << 2) + (x >> 5);
uint32_t *lword = base + ((y2 - y1 + 1) << 4);
uint32_t mask = 1 << (~x & 31);
switch(operator)
{
case color_white:
while(lword > base) lword -= 4, *lword &= ~mask;
break;
case color_black:
while(lword > base) lword -= 4, *lword |= mask;
break;
case color_reverse:
while(lword > base) lword -= 4, *lword ^= mask;
break;
default:
break;
}
}
/* dline() - Bresenham line drawing algorithm
Remotely adapted from MonochromeLib code by Pierre "PerriotLL" Le Gall.
Relies on dhline() and dvline() for optimized situations.
@x1 @y1 @x2 @y2 Coordinates of endpoints of line (included)
@color Allowed colors: black, white, none, reverse */
void dline(int x1, int y1, int x2, int y2, color_t color)
{
/* Possible optimizations */
if(y1 == y2)
{
dhline(x1, x2, y1, color);
return;
}
if(x1 == x2)
{
dvline(y1, y2, x1, color);
return;
}
/* Brensenham line drawing algorithm */
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)
{
/* Start with a non-zero cumul to even the overdue between the
two ends of the line (for more regularity) */
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);
}

28
src/render-fx/dpixel.c Normal file
View file

@ -0,0 +1,28 @@
#define GINT_NEED_VRAM
#include <gint/display.h>
#include <gint/defs/types.h>
/* dpixel() - change a pixel's color */
void dpixel(int x, int y, color_t color)
{
/* Sanity checks */
if((uint)x >= 64 || (uint)y >= 128) return;
uint32_t *lword = vram + (y << 2) + (x >> 5);
uint32_t mask = 1 << (~x & 31);
switch(color)
{
case color_white:
*lword &= ~mask;
break;
case color_black:
*lword |= mask;
break;
case color_reverse:
*lword ^= mask;
break;
default:
return;
}
}

61
src/render-fx/drect.c Normal file
View file

@ -0,0 +1,61 @@
#define GINT_NEED_VRAM
#include <gint/defs/util.h>
#include <gint/display.h>
#include <display/fx.h>
/* drect() - fill a rectangle of the screen */
void drect(int x1, int y1, int x2, int y2, color_t color)
{
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
/* Argument checking */
if(x1 >= 128 || x2 < 0 || y1 >= 64 || y2 < 0) return;
if(x1 < 0) x1 = 0;
if(x2 >= 128) x2 = 127;
if(y1 < 0) y1 = 0;
if(y2 >= 64) y2 = 63;
/* Use masks to get the work done fast! */
uint32_t m[4];
masks(x1, x2, m);
uint32_t *base = vram + (y1 << 2);
uint32_t *lword = vram + (y2 << 2) + 4;
switch(color)
{
case color_white:
while(lword > base)
{
*--lword &= ~m[3];
*--lword &= ~m[2];
*--lword &= ~m[1];
*--lword &= ~m[0];
}
break;
case color_black:
while(lword > base)
{
*--lword |= m[3];
*--lword |= m[2];
*--lword |= m[1];
*--lword |= m[0];
}
break;
case color_reverse:
while(lword > base)
{
*--lword ^= m[3];
*--lword ^= m[2];
*--lword ^= m[1];
*--lword ^= m[0];
}
break;
/* Other colors are unsupported */
default: return;
}
}

15
src/render-fx/dupdate.c Normal file
View file

@ -0,0 +1,15 @@
#define GINT_NEED_VRAM
#include <gint/display.h>
#include <gint/drivers/t6k11.h>
/* Standard video RAM for fx9860g is 1 bit per pixel */
GSECTION(".bss") static uint32_t fx_vram[256];
/* Here is the definition of the VRAM pointer */
GDATA uint32_t *vram = fx_vram;
/* dupdate() - pushes the video RAM to the display driver */
void dupdate(void)
{
t6k11_display(vram, 0, 64, 16);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

28
src/render-fx/masks.c Normal file
View file

@ -0,0 +1,28 @@
#include <display/fx.h>
/* masks() - compute the vram masks for a given rectangle */
void masks(int x1, int x2, uint32_t *masks)
{
if(x1 < 0) x1 = 0;
if(x2 < 0) x2 = 0;
/* Indexes of the first and last non-empty longs */
size_t l1 = x1 >> 5;
size_t l2 = x2 >> 5;
size_t i = 0;
/* Base masks (0's are final, 0xffffffff will be adjusted later) */
while(i < l1) masks[i++] = 0x00000000;
while(i <= l2) masks[i++] = 0xffffffff;
while(i < 4) masks[i++] = 0x00000000;
/* Remove the index information in x1 and x2 (it's now in l1 and l2)
and keep only the offsets */
x1 &= 31;
/* For x2 we also want the complement to 31 to invert the shift */
x2 = ~x2 & 31;
/* Now roll! */
masks[l1] &= (0xffffffffu >> x1);
masks[l2] &= (0xffffffffu << x2);
}

15
src/render-fx/topti-asm.h Normal file
View file

@ -0,0 +1,15 @@
//---
// gint:render-fx:topti-asm - Assembler drawing routines for topti
//---
#ifndef GINT_RENDERFX_TOPTIASM
#define GINT_RENDERFX_TOPTIASM
/* Signature of text rendering functions (which do not render text but really
just blend a column of operators onto the VRAM */
typedef void asm_text_t(uint32_t *v1, uint32_t *v2, uint32_t *op, int height);
/* One rendering function per color */
extern asm_text_t *topti_asm_text[8];
#endif /* GINT_RENDERFX_TOPTIASM */

170
src/render-fx/topti-asm.s Normal file
View file

@ -0,0 +1,170 @@
.global _topti_asm_text
# REGISTER ALLOCATION:
# r0: x or ~x
# r1: light (except lighten/darken: swapped at some point with dark)
# r2: dark (except lighten/darken: swapped at some point with light)
# r3: (tmp)
# r4: vram (mono or light)
# r5: vram (nothing or dark)
# r6: operators
# r7: number of rows (r7>0, otherwise the font is clearly ill-formed)
# Mind that there are pipeline optimisation efforts in this file:
# * Doing memory accesses on 4-aligned instructions to avoid contention between
# IF and MA (1 cycle);
# * Avoid using an operand just after it is fetched from memory because of the
# RAW dependency on the destination register (1 cycle);
# * Using delayed slot jumps bf.s rather than bf (1 cycle);
# * Executing the dt early to avoid waiting for its WB slot. Best if >=2
# instructions are between dt and bt.s. (not sure if it's needed).
.align 4
_topti_asm_white:
add #-16, r4
nop
1: mov.l @r6+, r0
add #16, r4
mov.l @r4, r1
dt r7
not r1, r1
and r1, r0
bf.s 1b
mov.l r0, @r4
rts
nop
.align 4
_topti_asm_light:
1: mov.l @r6+, r0
dt r7
mov.l @r4, r1
/* (bubble) */
or r0, r1
mov.l @r5, r2
not r0, r0
mov.l r1, @r4
and r0, r2
/* (bubble) */
mov.l r2, @r5
add #16, r4
bf.s 1b
add #16, r5
rts
nop
.align 4
_topti_asm_dark:
1: mov.l @r6+, r0
dt r7
mov.l @r5, r2
/* (bubble) */
or r0, r2
mov.l @r4, r1
not r0, r0
mov.l r2, @r5
and r0, r1
/* (bubble) */
mov.l r1, @r5
add #16, r4
bf.s 1b
add #16, r5
.align 4
_topti_asm_black:
1: mov.l @r6+, r0
dt r7
mov.l @r4, r1
or r1, r0
mov.l r0, @r4
bf.s 1b
add #16, r4
rts
nop
.align 4
_topti_asm_none:
rts
nop
.align 4
_topti_asm_reverse:
1: mov.l @r6+, r0
dt r7
mov.l @r4, r1
xor r1, r0
mov.l r0, @r4
bf.s 1b
add #16, r4
rts
nop
.align 4
_topti_asm_lighten:
add #-16, r4
add #-16, r5
1: mov.l @r6+, r0
add #16, r5
mov.l @r5, r2
add #16, r4
mov.l @r4, r1
mov r2, r3
xor r0, r3
not r0, r0
or r0, r2
and r1, r2
mov.l r2, @r1
dt r7
or r0, r1
and r3, r1
mov.l r1, @r5
bf.s 1b
rts
nop
.align 4
_topti_asm_darken:
add #-16, r4
add #-16, r5
1: mov.l @r6+, r0
add #16, r5
mov.l @r5, r2
add #16, r4
mov.l @r4, r1
mov r2, r3
xor r0, r3
and r0, r2
or r1, r2
dt r7
mov.l r2, @r4
and r0, r1
or r3, r1
/* (bubble) */
mov.l r1, @r5
bf.s 1b
rts
nop
# Export a table with these functions
.align 4
_topti_asm_text:
.long _topti_asm_white
.long _topti_asm_light
.long _topti_asm_dark
.long _topti_asm_black
.long _topti_asm_none
.long _topti_asm_reverse
.long _topti_asm_lighten
.long _topti_asm_darken

252
src/render-fx/topti.c Normal file
View file

@ -0,0 +1,252 @@
#define GINT_NEED_VRAM
#include <gint/defs/types.h>
#include <gint/display.h>
#include "topti-asm.h"
/* Default font */
extern font_t gint_font5x6;
font_t const * topti_font = &gint_font5x6;
/* dfont() - set the default font for text rendering */
void dfont(font_t const * font)
{
topti_font = font ? font : &gint_font5x6;
}
/* enum charset: Available character set decoders
Each charset is associated with a reduced character table. */
enum charset
{
charset_numeric = 0, /* 10 elements: 0..9 */
charset_upper = 1, /* 26 elements: A..Z */
charset_alpha = 2, /* 52 elements: A..Z, a..z */
charset_alnum = 3, /* 62 elements: A..Z, a..z, 0..9 */
charset_print = 4, /* 95 elements: 0x20..0x7e */
charset_ascii = 5, /* 128 elements: 0x00..0x7f */
};
/* charset_size(): Number of elements in each character set
@set Character set ID
Returns the expected number of glyphs, -1 if charset ID is invalid. */
int charset_size(enum charset set)
{
int size[] = { 10, 26, 52, 62, 95, 128 };
return (uint)set < 6 ? size[set] : -1;
}
/* charset_decode(): Translate ASCII into reduced character sets
Returns the position of [c] in the character table of the given charset, or
-1 if [c] is not part of that set.
@set Any character set
@c Character to decode */
int charset_decode(enum charset set, uint c)
{
int x, y;
switch(set)
{
case charset_numeric:
x = c - '0';
return (x < 10) ? x : -1;
case charset_upper:
x = (c - 'A') & ~0x20;
return (x < 26) ? x : -1;
case charset_alnum:
x = c - '0';
if(x < 10) return x;
/* Intentional fallthrough */
case charset_alpha:
y = c & 0x20;
x = (c ^ y) - 'A';
/* Turn 32 into 26 and leave 0 as 0 */
y = y - (y >> 3) - (y >> 4);
return (x < 26) ? (x + y) : -1;
case charset_print:
x = c - 0x20;
return (x < 0x5f) ? x : -1;
case charset_ascii:
return c;
}
return -1;
}
/* topti_offset(): Use a font index to find the location of a glyph
@f Font object
@glyph Glyph number obtained by charset_decode(), must be nonnegative.
Returns the offset the this glyph's data in the font's data array. When
using a proportional font, the size array is not heeded for. */
int topti_offset(font_t const *f, uint glyph)
{
/* Non-proportional fonts don't need an index */
if(!f->prop) return glyph * f->storage_size;
uint8_t const *width = f->sized_data;
/* The index gives us the position of all glyphs whose IDs are mutiples
of 8. Start with a close one and iterate from there. */
uint g = glyph & ~0x7;
int offset = f->index[g >> 3];
/* Traverse the width array (which is in bits) while converting to
longword size */
while(g < glyph) offset += (width[g++] * f->data_height + 31) >> 5;
return offset;
}
/* topti_split(): Split glyph data into lines
This function splits the data from [glyph] inyo lines and writes a bit of
each line in [operators]. This operation is meant to be used multiple times
in a row, so [free] represents the number of free low bits in [operators].
@glyph Raw glyph data from the font
@width Width of glyph (1 <= width <= 32)
@height Storage height
@free Number of free low bits in [operators]
@operators VRAM operands
Returns the number of free bits in [operators] after the operation. If it's
0, call topti_draw() and reset the operators. If it's neative, call
topti_draw() then do another pass of topti_split() to recover the missing
information. */
int topti_split(uint32_t const * glyph, int width, int height, int free,
uint32_t *operators)
{
/* Extracts [width] bits on the left of [*glyph] */
uint32_t glyph_mask = 0xffffffff << (32 - width);
/* Shifts from the left of [*glyph] to the free bits of [operators] */
int shift;
uint32_t data = *glyph++;
/* Number of bits remaining in [data] */
int source = 32;
for(int i = 0; i < height; i++)
{
shift = 32 - free;
/* Read [width] data bits and put them in the operator.
* There may not be [width] bits left in [data]; this
situation is detected and cared for later. (*1)
* There may not be enough space to store [width] bits; this
is detected by topti_render(). (*2)
* We may have available > 32 as a result of the previous
case, so shift carefully. */
uint32_t line = data & glyph_mask;
line = (shift >= 0) ? (line >> shift) : (line << -shift);
operators[i] |= line;
data <<= width;
source -= width;
/* Continue iterating as long as no information is lost */
if(source >= 0) continue;
/* (*1) Now load a new [data] */
uint32_t partial_mask = 0xffffffff << (source + 32);
data = *glyph++;
shift += source + width;
/* shift>=32 means the the information we lost in (*1) does not
fit in the operators, making this a case of (*2). */
if(shift < 32)
{
/* Recover lost bits */
uint32_t line = data & partial_mask;
line = (shift>=0) ? (line >> shift) : (line << -shift);
operators[i] |= line;
}
data <<= -source;
source += 32;
}
return free - width;
}
/* topti_render(): Render a string on the VRAM
Combines glyph data onto VRAM operands and blits them efficiently onto the
VRAM. To write a single character, use a 2-byte string with a NUL.
@x @y Target position on VRAM
@str Text source
@f Font
@asm_text Assembler function for the rendering proper */
void topti_render(int x, int y, const char *str, font_t const *f,
asm_text_t *asm_text)
{
/* Storage height and number of free bits in operators[] */
int height = f->data_height, free;
/* Raw glyph data */
uint32_t const * data = f->prop
? (void *)(f->sized_data + charset_size(f->charset))
: f->data;
/* Basic clipping */
if(x > 127 || y > 63 || y + height <= 0) return;
if(y + height > 64) height = 64 - y;
/* How much we need to skip vertically if we render text at y < 0 */
int vdisp = 0;
if(y < 0) vdisp = -y, y = 0;
/* Operator data */
uint32_t operators[height];
for(int i = 0; i < height; i++) operators[i] = 0;
/* Put an initial offset to the operators to honor the x coordinate */
free = 32 - (x & 31);
x >>= 5;
/* VRAM pointers */
/* TODO: topti: Support gray vrams */
uint32_t *v1 = vram + (y << 2) + x;
uint32_t *v2 = vram + (y << 2) + x;
/* Pull each character into the operator buffer */
while(*str)
{
int glyph = charset_decode(f->charset, *str++);
if(glyph < 0) continue;
int index = topti_offset(f, glyph);
/* Put glyph data into the operators */
int width = f->prop ? f->sized_data[glyph] : f->width;
free = topti_split(data+index, width, height, free, operators);
/* Potential space after the glyph */
int space = (*str != 0);
free -= space;
if(free > 0) continue;
/* Once operators are full, update VRAM and start again */
if(x >= 0) asm_text(v1, v2, operators + vdisp, height - vdisp);
v1++, v2++;
if(++x >= 4) break;
for(int i = 0; i < height; i++) operators[i] = 0;
free += 32;
/* If information was lost in the split, finish it */
if(free + space >= 32) continue;
free += width + space;
free = topti_split(data+index, width, height, free, operators);
free -= space;
}
/* Put the final longwords */
if(x >= 0 && x < 4 && free < 32)
asm_text(v1, v2, operators + vdisp, height - vdisp);
}
/* dtext() - display a string of text */
void dtext(int x, int y, const char *str, color_t color)
{
if((uint)color >= 8) return;
topti_render(x, y, str, topti_font, topti_asm_text[color]);
}

View file

@ -27,7 +27,7 @@ _inth_rtc_pri:
/* Jump to another gate to finish the work:
- 0xc is the size of storage below
- 0x20 is the size of the next gate (alarm interrupt) */
- 0x20 is the size of the gap before next gate (alarm interrupt) */
mov #0x2c, r2
braf r2
nop

View file

@ -5,10 +5,10 @@
#include <gint/rtc.h>
#include <gint/drivers.h>
#include <gint/gint.h>
#include <mod/rtc.h>
#include <core/mpu.h>
#include <defs/types.h>
#include <gint/defs/types.h>
#include <gint/mpu.h>
#include <gint/mpu/rtc.h>
//---
// Real-Time Clock peripheral registers
@ -18,10 +18,10 @@
GDATA static rtc_t *RTC = &SH7305_RTC;
/* Address of interrupt handler parameters */
GBSS struct {
int (*callback)(void *arg);
void *arg;
int (*callback)(volatile void *arg);
volatile void *arg;
volatile uint8_t *RCR2;
} PACKED(4) *params;
} GPACKED(4) *params;
//---
// Time management
@ -97,8 +97,12 @@ void rtc_set_time(const rtc_time_t *time)
//---
/* rtc_start_timer() - configure the RTC timer */
void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void*arg), void*arg)
void rtc_start_timer(rtc_frequency_t freq,
int (*callback)(volatile void *arg), volatile void *arg)
{
/* Temporarily disable the interrupt */
RTC->RCR2.PES = RTC_NONE;
/* Set up the callback */
params->callback = callback;
params->arg = arg;
@ -114,7 +118,7 @@ void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void*arg), void*arg)
/* rtc_stop_timer() - stop the RTC timer */
void rtc_stop_timer(void)
{
RTC->RCR2.PES = rtc_none;
RTC->RCR2.PES = RTC_NONE;
}
//---
@ -136,7 +140,7 @@ static void init(void)
extern void inth_rtc_pri_helper(void);
/* Install the RTC interrupt handler */
UNUSED void *h0, *h1;
GUNUSED void *h0, *h1;
h0 = gint_inthandler(0xaa0, inth_rtc_pri, 32);
h1 = gint_inthandler(0xae0, inth_rtc_pri_helper, 32);
@ -144,7 +148,7 @@ static void init(void)
params->RCR2 = &RTC->RCR2.byte;
/* Disable the periodic interrupt for now, but give it priority 5 */
RTC->RCR2.PES = rtc_none;
RTC->RCR2.PES = RTC_NONE;
gint_intlevel(isSH3() ? 3 : 40, 5);
}
@ -179,7 +183,7 @@ static void ctx_restore(void *buf)
//---
gint_driver_t drv_rtc = {
.name = "Real-Time Clock",
.name = "RTC",
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
.init = init,
.ctx_size = sizeof(ctx_t),

View file

@ -3,11 +3,11 @@
//---
#include <gint/drivers.h>
#include <drivers/t6k11.h>
#include <gint/drivers/t6k11.h>
#include <defs/attributes.h>
#include <defs/types.h>
#include <core/mpu.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
#include <gint/mpu.h>
#ifdef FX9860G
@ -55,21 +55,21 @@ GDATA static volatile uint8_t *cmd = (void *)0xb4010000;
/* command() - send a command to set the value of a register
@reg Register number
@data Value to set in reg */
INLINE static void command(uint8_t reg, uint8_t data)
GINLINE static void command(uint8_t reg, uint8_t data)
{
*sel = reg;
*cmd = data;
}
/* status() - read the status byte from the display driver */
INLINE static uint8_t status(void)
GINLINE static uint8_t status(void)
{
return *sel;
}
/* write_row() - send 16 bytes to the display driver
@buf Buffer to take data from */
INLINE static void write_row(const uint8_t *buf)
GINLINE static void write_row(const uint8_t *buf)
{
*sel = reg_data;
@ -129,6 +129,12 @@ void t6k11_contrast(int contrast)
fx9860G II with OS 02.05.2201, are in range 0x97 .. 0xb7.
This means VLC0 = 2 and CONTRAST in [23..55] */
command(reg_contrast, 0x97 + contrast);
/* TODO: Turns out that different models, notably screens without
TODO: backlight, will have different ranges. Plus we might want to
TODO: use transitions to extreme contrast settings for visual
TODO: effects.
TODO: Extend the available range of contrast settings. */
}
/* t6k11_backlight() - manage the screen backlight */
@ -168,7 +174,7 @@ typedef struct
/* There *are* other parameters that are affected by the driver, but
they cannot be read, so I can't determine the system's setting */
} PACKED(1) ctx_t;
} GPACKED(1) ctx_t;
/* Pre-allocate a context in gint's uninitialized section */
GBSS static ctx_t sys_ctx;
@ -197,7 +203,7 @@ static void ctx_restore(void *buf)
//---
gint_driver_t drv_t6k11 = {
.name = "Toshiba T6K11",
.name = "T6K11",
.init = NULL,
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx,

View file

@ -27,7 +27,7 @@
- Clear the interrupt flag
- Invoke a callback function and pass it a user-provided argument
- Stop the timer if the callback returns non-zero
- Host their own callback and arguments
- Host their own callback pointers and arguments
It is important to notice that the code of the following gates looks like
they are contiguous in memory. The assembler will make that assumption, and
@ -60,7 +60,7 @@ _inth_tmu_0:
mov.l @(4, r0), r4
lds.l @r15+, pr
/* Prepare stopping the timer */
/* Prepare stopping the timer and jump to second section */
mov.l .tstr, r5
bra .stoptimer
mov.l @r15+, r1
@ -122,11 +122,11 @@ _inth_tmu_storage:
To implement the same functionalities as the standard timers, several blocks
are once again needed. But the handlers for the extra timers are not located
in adjacent gates, except for ETMU1 and ETMU2 which have event codes 0xc20
and 0xc40. Since handler 0xc60 is free on both SH3 and SH4, I'm using it to
build a three-handler block and achieve the same result as above.
and 0xc40. Since handler 0xc60 is free on SH4 and not redirected to on SH3,
I use it to build a three-handler block similar to that of the TMU above.
On SH4 this means that an extra gate has to be installed, but no interrupt
pointed here. On SH3 this means that four gates are used for the only extra
point here. On SH3 this means that four gates are used for the only extra
timer, but the incurred cost is minimal (96 bytes on the binary file)
because the size of the VBR area can hardly be shrunk anyway.
@ -141,8 +141,8 @@ _inth_tmu_storage:
/* FIRST GATE - ETMU2 entry, clear flag and prepare callback */
_inth_tmu_extra2:
/* Warning: the size of this section (2 bytes) is hardcoded in another
interrupt handler, _inth_tmu_extra_others */
/* Warning: the size of the following instruction (2 bytes) is
hardcoded in another interrupt handler, _inth_tmu_extra_others */
mova .storage_extra_1, r0
.extra_callback:
@ -214,7 +214,7 @@ _inth_tmu_extra_others:
- 0x600 to reach the interrupt handlers
- 0x020 to jump over the entry gate
- 0x840 to reach the handler of extra timer 2
- 0x002 to jump over the first part of its code */
- 0x002 to skip its first instruction (the size is hardcoded) */
1: .long 0xe62
.zero 4

View file

@ -7,10 +7,10 @@
#include <gint/gint.h>
#include <gint/clock.h>
#include <core/intc.h>
#include <gint/mpu/intc.h>
#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// Timer structures
@ -31,7 +31,7 @@ typedef volatile struct
uint16_t TPSC :3; /* Timer prescaler (input clock) */
);
} PACKED(4) tmu_t;
} GPACKED(4) tmu_t;
/* tmu_extra_t - extra timers on sh7337, sh7355 and sh7305 */
typedef volatile struct
@ -48,16 +48,16 @@ typedef volatile struct
uint8_t UNIE :1; /* Underflow interrupt enable */
);
} PACKED(4) tmu_extra_t;
} GPACKED(4) tmu_extra_t;
/* inth_data_t - data storage inside interrupt handlers */
typedef struct
{
int (*callback)(void *arg); /* User-provided callback function */
void *arg; /* Argument for [callback] */
int (*cb)(volatile void *arg); /* User-provided callback */
volatile void *arg; /* Argument for [callback] */
volatile void *structure; /* Either TCR or timer address */
} PACKED(4) inth_data_t;
} GPACKED(4) inth_data_t;
/* timer_t - all data required to run a single timer */
typedef struct
@ -94,7 +94,7 @@ GDATA static volatile uint8_t *TSTR = (void *)0xa4490004;
/* timer_setup() - set up a timer */
int timer_setup(int tid, uint32_t delay, timer_input_t clock,
int (*callback)(void *arg), void *arg)
int (*callback)(volatile void *arg), volatile void *arg)
{
/* We need to distinguish normal and extra timers */
if(tid < 3)
@ -122,21 +122,24 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock,
tmu_extra_t *t = timers[tid].tmu;
if(t->TCR.UNIE) return -1;
/* There is no clock input and no clock edge settings */
t->TCOR = delay;
t->TCNT = delay;
/* Clear the interrupt flag */
do t->TCR.UNF = 0;
while(t->TCR.UNF);
/* There is no clock input and no clock edge settings */
t->TCOR = delay;
/* TODO: FXCG50: does not always work on first try */
do t->TCNT = delay;
while(t->TCNT != delay);
t->TCR.UNIE = 1;
}
/* Register the callback and its argument (TMU-owned timers only) */
if(timers[tid].data)
{
timers[tid].data->callback = callback;
timers[tid].data->cb = callback;
timers[tid].data->arg = arg;
}
@ -145,20 +148,19 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock,
}
/* timer_delay() - compute a delay constant from a duration in seconds */
uint32_t timer_delay(int tid, int delay_us)
uint32_t timer_delay(int tid, uint64_t delay_us)
{
/* TODO: Proper timer_delay() */
const clock_frequency_t *clock = clock_freq();
uint64_t freq;
freq = isSH3() ? 14750000 >> 2 : clock->Pphi_f >> 2;
uint64_t freq = clock->Pphi_f >> 2;
/* fxcg50: Calculated = 29491200 but it's too low */
// uint64_t freq = 29020000 >> 2;
if(tid >= 3) freq = 32768; /* 32768 Hz */
/* Extra timers all run at 32768 Hz */
if(tid >= 3) freq = 32768;
uint64_t product = freq * (uint64_t)delay_us;
uint64_t product = freq * delay_us;
return product / 1000000;
}
@ -167,17 +169,10 @@ uint32_t timer_delay(int tid, int delay_us)
@state 0 to start the timer, 1 to stop it (nothing else!) */
static void timer_control(int tid, int state)
{
if(tid < 3)
{
/* For standard timers, use the MPU's TSTR register */
*TSTR = (*TSTR | (1 << tid)) ^ (state << tid);
}
else
{
/* Extra timers all have their own TSTR register */
tmu_extra_t *t = timers[tid].tmu;
t->TSTR = (t->TSTR | 1) ^ state;
}
/* For standard timers, use the MPU's TSTR register */
if(tid < 3) *TSTR = (*TSTR | (1 << tid)) ^ (state << tid);
/* Extra timers all have their own TSTR register */
else ((tmu_extra_t *)timers[tid].tmu)->TSTR = state ^ 1;
}
/* timer_start() - start a configured timer */
@ -202,8 +197,6 @@ void timer_pause(int tid)
/* timer_stp() - stop and free a timer */
void timer_stop(int tid)
{
return;
/* Stop the timer and disable UNIE to indicate that it's free */
timer_pause(tid);
@ -219,13 +212,28 @@ void timer_stop(int tid)
/* Also clear TCOR and TCNT to avoid spurious interrupts */
t->TCOR = 0xffffffff;
t->TCNT = 0xffffffff;
/* TODO: FXCG50: Again */
do t->TCNT = 0xffffffff;
while(t->TCNT + 1);
do t->TCR.UNF = 0;
while(t->TCR.UNF);
}
}
//---
// Predefined timer callbacks
//---
/* timer_timeout() - callback that sets a flag and halts the timer */
int timer_timeout(volatile void *arg)
{
volatile int *x = arg;
(*x)++;
return 0;
}
//---
// Driver initialization
//---
@ -258,11 +266,10 @@ static void driver_sh3(void)
static void init(void)
{
/* Install the standard's TMU interrupt handlers. By chance TMU gates
use the same event codes on SH7705 and SH7305 */
UNUSED void *h0, *h1, *h2, *hs;
h0 = gint_inthandler(0x400, inth_tmu_0, 32);
h1 = gint_inthandler(0x420, inth_tmu_1, 32);
/* Install the standard's TMU interrupt handlers */
void *h2, *hs;
gint_inthandler(0x400, inth_tmu_0, 32);
gint_inthandler(0x420, inth_tmu_1, 32);
h2 = gint_inthandler(0x440, inth_tmu_2, 32);
hs = gint_inthandler(0x460, inth_tmu_storage, 32);
@ -270,7 +277,6 @@ static void init(void)
timers[0].data = h2 + 20;
timers[1].data = hs + 8;
timers[2].data = hs + 20;
/* SH3: Override the address of TSTR in the interrupt handler helper */
if(isSH3()) *(volatile uint8_t **)(hs + 4) = TSTR;
@ -282,10 +288,12 @@ static void init(void)
for(int i = 0; i < 3; i++)
{
tmu_t *t = timers[i].tmu;
t->TCR.UNIE = 0;
do t->TCR.UNF = 0;
while(t->TCR.UNF);
t->TCOR = 0xffffffff;
t->TCNT = 0xffffffff;
do t->TCR.word = 0;
while(t->TCR.word);
/* Standard timers: TCR is provided to the interrupt handler */
timers[i].data->structure = &t->TCR;
@ -296,16 +304,21 @@ static void init(void)
{
tmu_extra_t *t = timers[i].tmu;
/* This is *extremely important*: extra timers will generate
interrupts as long as TCNT = 0 *regardless of TSTR*! */
t->TCOR = 0xffffffff;
t->TCNT = 0xffffffff;
/* Extra timers seem to generate interrupts as long as TCNT=0,
regardless of TSTR. I'm not entirely sure about this weird
behaviour, but for safety I'll set TCOR/TCNT to non-zero.
This may be related to difficulties when setting TCNT. */
t->TCR.UNIE = 0;
t->TSTR = 0;
t->TCOR = 0xffffffff;
do t->TCR.UNF = 0;
while(t->TCR.UNF);
/* TODO: FXCG50: Safety */
do t->TCNT = 0xffffffff;
while(t->TCNT + 1);
/* Clear interrupts */
do t->TCR.byte = 0;
while(t->TCR.byte);
}
/* Install the extra timers. We need three extra timers for the
@ -347,10 +360,10 @@ static void init(void)
gint_intlevel(44, 7);
/* Unmask the extra timers' interrupts */
INTC4.MSKCLR->IMR2 = 0x01;
INTC4.MSKCLR->IMR5 = 0x06;
INTC4.MSKCLR->IMR6 = 0x18;
INTC4.MSKCLR->IMR8 = 0x02;
SH7305_INTC.MSKCLR->IMR2 = 0x01;
SH7305_INTC.MSKCLR->IMR5 = 0x06;
SH7305_INTC.MSKCLR->IMR6 = 0x18;
SH7305_INTC.MSKCLR->IMR8 = 0x02;
}
}
@ -364,7 +377,7 @@ typedef struct
tmu_extra_t extra[6];
uint8_t TSTR;
} PACKED(4) ctx_t;
} GPACKED(4) ctx_t;
/* Allocate a system buffer in gint's BSS area */
GBSS static ctx_t sys_ctx;
@ -428,7 +441,7 @@ static void ctx_restore(void *buf)
//---
gint_driver_t drv_tmu = {
.name = "Timer Unit",
.name = "TMU",
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
.init = init,
.ctx_size = sizeof(ctx_t),