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 directories
build/** build*/**
# Sublime Text files # Sublime Text files
*.sublime-project *.sublime-project
@ -7,10 +7,3 @@ build/**
# Lots of unordered project notes # Lots of unordered project notes
notes/** notes/**
# Output files
bin/**
# Configuration files
config/**

213
Makefile Executable file → Normal file
View file

@ -1,172 +1,59 @@
#! /usr/bin/make -f #! /usr/bin/make -f
#
# gint project Makefile
#
#---
# builds := $(wildcard build*)
# Build configuration
#
# Some rules don't care if config files are missing (eg. clean, etc) ifeq "$(builds)" ""
cfg := config/Makefile.cfg nobuild:
-include $(cfg) @ echo "error: you don't seem to have any build directory (build*)" >&2
@ echo "" >&2
# Those that do may use this special dependency @ echo "You can configure one like this:" >&2
cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg)) @ echo " mkdir build && cd build && ../configure [options...]" >&2
@ echo "" >&2
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."
@ false @ false
endif
# Make directories: make conveniently leaves a '/' at the end of $(dir ...) #
%/: # all targets
@ mkdir -p $@ #
# Don't try to unlink directories once they're built (that wouldn't work =p)
.PRECIOUS: %/
# Dependency information all-targets := $(foreach b,$(builds),all-$b)
-include $(shell [ -d build ] && find build -name *.d)
build/%.d: ;
.PRECIOUS: build/%.d
# Do not output full commands by default all: $(all-targets)
VERBOSE ?=
# Simple command output method all-build%: build%
# $1 Program name @ echo -e "$B::$W Making into $<$N"
# $2 Argument string to display @ $(MAKE) --no-print-directory -C $<
# $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) # install targets
cmd_b = $(call cmd,$1,$2,32) #
cmd_l = $(call cmd,$1,$2,36)
cmd_c = $(call cmd,$1,$2,31) install-targets := $(foreach b,$(builds),install-$b)
cmd_i = $(call cmd,$1,$2,33)
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 gint (pronounce 'guin') is a development system for Casio fx-9860G II and
provides a mostly free-standing runtime support for the platform, and can be fx-CG 50 calculators. It provides a mostly free-standing runtime and is used
used to develop add-ins under Linux along with the gcc toolchain (`sh3eb-elf` to develop add-ins under Linux, along with specialized GCC toolchains and the
target) and the [fxSDK](http://git.planet-casio.com/lephe/fxsdk). [fxSDK](http://git.planet-casio.com/lephe/fxsdk).
gint is free software: you may use it for any purpose, share it, modify it and gint is a modular kernel that implements its own drivers for the calculator's
share your changes. No credit of any kind is required, though appreciated. 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** **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 Because of its free-standing design, gint's API provides direct and efficient
access to the low-level MPU features, among which: 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 * Multi-key management with event systems suitable for games
* Hardware timers running at over 10 MHz, allowing microsecond-level control * Hardware timers with sub-millisecond and sub-microsecond resolution
* Unlimited mappings of callbacks to Real-Time Clock events (requires build) * Fast screen drivers with DMAC on fx-CG 50
* Access to processor registers for debugging information, determination of * Efficient and user-extendable interrupt management
processor speed (overclock is on the TODO list), backlight management...
The library also offers powerful higher-level features: 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 * An enhanced versio of the system's GetKey() and GetKeyWait()
image rendering that MonochromeLib) * A gray engine that works by rapidly swapping monochrome images on fx-9860G II
* C Standard functions such as the `printf()` family * 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 You can choose to build gint for fx-9860G II (monochrome calculators, aka
accesses the peripheral modules and workarounds the system to perform keyboard Graph 85 family), fx-CG 50 (color calculators, aka Prizm or Graph 90 family),
analyzes directly on hardware or timer management. or both. There are a few dependencies:
gint does not allow user programs to use their own handlers, but it allows them * A suitable GCC toolcahin in the `PATH`. You can absolutely *not* build gint
to complete the original handler if they want to use interrupts that are not with your system compiler!
supported by the library. It is also possible to map various interrupt-driven * For fx-9860G II, `sh3eb-elf` is strongly advised
events to user-provided callbacks using the API, which is not allowed by the * For fx-CG 50, `sh4eb-nofpu-elf` is slightly better but `sh3eb-elf` is
operating system. This is particularly useful for timers and the Real-Time completely fine
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
* The [fxSDK](http://git.planet-casio.com/lephe/fxsdk) installed and available * 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 The build process is detailed below for both platforms, the principle is the
$ make same. You can build both targets at the same time by reading the two sections.
# make install
This will build the `all-lib` target and install the following components in By default gint will be installed in the appropriate compiler folder, which
the storage folder of the fxSDK: is `$PREFIX/` for libraries and linker scripts, and `$PREFIX/include/gint/` for
* `libgint.a`, the gint library headers, where `PREFIX` is obtained by running
* `libc.a`, the partial standard library `${toolchain}-gcc --print-search-dirs` and reading the line that starts with
* The libgint headers for development `install:`. You can change this with the `--prefix` configure option.
When explicitly running target `all`, the following additional files will be ### Building for fx-9860G II
generated in the working directory:
* `gintdemo.g1a`, a test application
* `gintdbg.g1a`, a non-gint application used for debugging in case gint crashes
The usual `clean`, `mrproper`, and `distclean` rules will clean the directory. Create a build directory and configure in it:
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...).
% 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 % make
component files: % make install
* 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.
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: Bugs to fix:
- Alignment of ALL .data / .rodata files is required to ensure converted data - Alignment of ALL .data / .rodata files is required to ensure converted data
is properly aligned is properly aligned -> Ok using ALIGN(4)
- 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 (?)
- Ensure heap data is freed when a task-switch results in leaving the app
Things to do before 1.0: Things to do before 1.0:
- bopti: Test partial transparency - 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: 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 - time: Compute CLOCKS_PER_SEC
Things to do later: Things to do later:
@ -17,12 +63,8 @@ Things to do later:
- clock: Only measure if requires as option, otherwise trust {FTune} - clock: Only measure if requires as option, otherwise trust {FTune}
- clock: Handle overclock (relaunch clocks when overclocking) - clock: Handle overclock (relaunch clocks when overclocking)
- clock: Split code into several files, change clock_config_t type - 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: 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) - 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 - esper: Cleaner playback, synthesizing
- events: Allow customization of keyboard event system (option to return - events: Allow customization of keyboard event system (option to return
| events with modifiers, etc) | events with modifiers, etc)
@ -34,14 +76,10 @@ Things to do later:
- stdio: More serious formatted printing functions and headers - stdio: More serious formatted printing functions and headers
- string: Use cmp/str to implement memchr() (assembler examples) - string: Use cmp/str to implement memchr() (assembler examples)
- string: Do some tests for memcmp() and memcpy() - string: Do some tests for memcmp() and memcpy()
- string: Lighter functions?
- usb: Implement a driver - usb: Implement a driver
Things to investigate: Things to investigate:
- Registers that may need to be saved within setjmp() - 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: Possibly useful modules:
- DMAC - DMAC
@ -49,4 +87,3 @@ Possibly useful modules:
- TPU - TPU
- USB - USB
- CMT on SH7305, WDT on SH7705 - CMT on SH7305, WDT on SH7705
- ACD (SH7705 only)

260
configure vendored
View file

@ -4,166 +4,206 @@
# Basic configuration # Basic configuration
# #
declare -A conf
# Target platform # Target platform
conf_target= target=
toolchain=
# Build options
prefix=
cflags=
# Behavior # Behavior
conf[GINT_BOOT_LOG]= boot_log=
conf[GINT_NO_SYSCALLS]= no_syscalls=
cong[GINT_LAX]= static_gray=
conf[GINT_EXTENDED_LIBC]=
conf[GINT_STATIC_GRAY]=
# Size limits # Size limits
conf[ATEXIT_MAX]=16 atexit_max=
conf[TIMER_SLOTS]=16
conf[EVENTS_QUEUE_SIZE]=64
# Output files # 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() help()
{ {
cat << EOF cat << EOF
Configuration script for the gint library. Configuration script for the gint library.
Usage: $0 [options...] Usage: $0 [options...]
Platform settings (specify exactly one): You should build out-of-tree by creating a build directory and configuring from
${Cg}-fx9860g$C0 there.
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.
Options that affect the behavior of the library $C_[${Cp}default$C_]$C0: Target selection:
$Cr-boot-log $C_[${Cp}disabled$C_]$C0 --target=fx9860g|fxcg50
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.
Options that customize size limits $C_[${Cp}default$C_]$C0: fx9860g covers all fx-9860G II-like monochromes models that support add-ins
$Cr-atexit-max$C0=${Cg}integer$C_ [${Cp}16$C_]$C0 or can be flashed with an OS that does. This includes SH3 and SH4 machines.
Number of exit handlers that can be registered by atexit(). Default toolchain is 'sh3eb-elf'.
$Cr-timer-slots$C0=${Cg}integer$C_ [${Cp}16$C_]$C0
Number of virtual timers that may be registered at the same time. fxcg50 covers just the fx-CG 50; there is some unofficial compatibility with
$Cr-events-queue-size$C0=${Cg}integer$C_ [${Cp}64$C_]$C0 fx-CG 10/20. All of these are SH4-only.
Number of events simultaneously stored in the event queue. 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 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 # Parsing arguments
# #
fail=false fail=false
for arg; do case "$arg" in for arg; do case "$arg" in
-h | -? | --help) help;; -h | -? | --help)
help;;
-fx9860g)
conf_target="FX9860G";;
-fxcg50)
conf_target="FXCG50";;
-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;;
-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;;
-atexit-max | -timer-slots | -events-queue-size)
echo -e "$error syntax for $arg is $arg=<integer-value>";;
--target=*)
case ${arg#*=} in
"fx9860g")
target=fx9860g
toolchain=${toolchain:-sh3eb-elf};;
"fxcg50")
target=fxcg50
toolchain=${toolchain:-sh4eb-nofpu-elf};;
*) *)
echo -e "$error unrecognized argument '$arg'"; fail=true;; echo "error: invalid target '$target'"
fail=true
esac;;
--prefix=*)
prefix=${arg#*=};;
--cflags=*)
cflags=${arg#*=};;
--boot-log)
boot_log=true;;
--no-syscalls)
no_syscalls=true;;
--static-gray)
static_gray=true;;
--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;;
--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 esac; done
# #
# Checking mandatory arguments # Checking mandatory arguments
# #
if [[ ! $conf_target ]]; then if [[ -z "$target" ]]; then
echo -e "$error No target specified. See $0 --help." echo "error: no target specified! (see '$0 --help')"
fail=true; fail=true;
fi 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
# #
# Output config # Output config
# #
output_config() output_config()
{ {
[ ${conf_target} == "FX9860G" ] \ mod=${target/98/fx}
&& echo "cfg_target = fx" \ echo "CONFIG.TARGET = ${mod:2:2}"
|| echo "cfg_target = cg" echo "CONFIG.TARGET.LONG = $target"
echo -n "cfg_macros =" [[ $prefix ]] && echo "PREFIX = $prefix"
echo -n " -D$conf_target" [[ $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 -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 "" echo ""
[ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "cfg_ext = true"
} }
if $fail; then if $fail; then
echo "Output file $output has not been modified." echo "note: output file $output has not been changed."
else exit 1
mkdir -p config
output_config > $output
echo "Configuration saved in $output, ready to make!"
fi 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); . = ALIGN(16);
} > ram AT> rom } > 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); . = ALIGN(16);
} > ram AT> rom } > 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 #ifndef GINT_CORE_BOOTLOG
@ -21,4 +21,11 @@ void bootlog_mapped(uint32_t rom, uint32_t ram);
handlers set up. */ handlers set up. */
void bootlog_kernel(void); 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 */ #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. // 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 // gint does not touch the MMU because the risk of permanently wreaking
// system is deemed too high. However, to ensure that the add-in runs // 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. // properly, checks using read-only accesses to the MMU are performed.
//--- //---
#ifndef GINT_CORE_MMU #ifndef GINT_CORE_MMU
#define GINT_CORE_MMU #define GINT_CORE_MMU
#include <defs/attributes.h> #include <gint/defs/attributes.h>
#include <defs/types.h> #include <gint/defs/types.h>
//--- //---
// SH7705 TLB // SH7705 TLB
//--- //---
#ifdef FX9860G
/* tlb_addr_t - address part of a TLB entry */ /* tlb_addr_t - address part of a TLB entry */
typedef struct typedef struct
{ {
@ -26,7 +24,7 @@ typedef struct
uint V :1; uint V :1;
uint ASID :8; uint ASID :8;
} PACKED(4) tlb_addr_t; } GPACKED(4) tlb_addr_t;
/* tlb_data_t - data part of a TLB entry */ /* tlb_data_t - data part of a TLB entry */
typedef struct typedef struct
@ -43,7 +41,7 @@ typedef struct
uint SH :1; uint SH :1;
uint :1; uint :1;
} PACKED(4) tlb_data_t; } GPACKED(4) tlb_data_t;
/* tlb_addr() - get the P4 address of a TLB address entry /* tlb_addr() - get the P4 address of a TLB address entry
@way TLB way (0..3) @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. */ Returns a pointer to the entry. */
const tlb_data_t *tlb_data(uint way, uint E); 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 This function returns the amount of mapped text and data segment memory, in
bytes. The ranges are defined as follows: bytes. The ranges are defined as follows:
ROM 00300000:512k ROM 00300000:512k
@ -68,8 +66,6 @@ const tlb_data_t *tlb_data(uint way, uint E);
@ram Pointer to amount of mapped RAM */ @ram Pointer to amount of mapped RAM */
void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram); void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram);
#endif
//--- //---
// SH7305 Unified TLB // SH7305 Unified TLB
//--- //---
@ -82,7 +78,7 @@ typedef struct
uint V :1; uint V :1;
uint ASID :8; uint ASID :8;
} PACKED(4) utlb_addr_t; } GPACKED(4) utlb_addr_t;
/* utlb_addr() - get the P4 address of a UTLB address entry /* utlb_addr() - get the P4 address of a UTLB address entry
@E Entry number (should be in range 0..63) @E Entry number (should be in range 0..63)
@ -104,7 +100,7 @@ typedef struct
uint SH :1; uint SH :1;
uint WT :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 /* utlb_data() - get the P4 address of a UTLB data entry
@E Entry number (should be in range 0..63) @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 #ifndef GINT_CORE_SETUP
#define 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> */ /* Prototypes for the library management functions are in <gint/gint.h> */
/* gint_setvbr() /* gint_setvbr()
Changes the VBR address and the calls the configuration function while Changes the VBR address and calls the configuration function while
interrupts are disabled. The configuration function must change either the interrupts are disabled. The configuration function must disable all
priority registers or the interrupt masks, and make sure that all the interrupts that the new handlers cannot handle, either by clearing the
interrupts that it leaves enabled are handled by the new VBR handlers. priority registers or by setting the interrupt masks.
@vbr New VBR address @vbr New VBR address
@configure Configuration function @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 #ifndef GINT_CLOCK
#define GINT_CLOCK #define GINT_CLOCK
#include <defs/types.h>
//--- //---
// Clock signals // Clock signals
//--- //---
@ -59,7 +57,7 @@ const clock_frequency_t *clock_freq(void);
The function stops the processor until an interrupt is accepted; the The function stops the processor until an interrupt is accepted; the
duration is not known in advance. This function should be used when 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. */ 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 /* sleep_us() - sleep for a definite duration in microseconds

View file

@ -5,37 +5,26 @@
#ifndef GINT_DEFS_ATTRIBUTES #ifndef GINT_DEFS_ATTRIBUTES
#define GINT_DEFS_ATTRIBUTES #define GINT_DEFS_ATTRIBUTES
/* Generic attribute element */
#define ATTR(...) __attribute__((__VA_ARGS__))
/* Objects from specific sections */ /* 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 */ /* Objects from the .gint.data and .gint.bss sections */
#define GDATA __attribute__((section(".gint.data"))) #define GDATA __attribute__((section(".gint.data")))
#define GBSS __attribute__((section(".gint.bss"))) #define GBSS __attribute__((section(".gint.bss")))
/* Additional sections that are only needed on SH3 */ /* Additional sections that are only needed on SH3 */
#define GDATA3 __attribute__((section(".gint.data.sh3"))) #define GDATA3 __attribute__((section(".gint.data.sh3")))
#define GBSS3 __attribute__((section(".gint.bss.sh3"))) #define GBSS3 __attribute__((section(".gint.bss.sh3")))
/* Initialization functions */
#define PRETEXT __attribute__((section(".pretext")))
/* Unused parameters or variables */ /* Unused parameters or variables */
#define UNUSED __attribute__((unused)) #define GUNUSED __attribute__((unused))
/* Functions that *must* be inlined */ /* Functions that *must* be inlined */
#define INLINE __attribute__((always_inline)) inline #define GINLINE __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)))
/* Aligned variables */ /* 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, /* 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 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 */ 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 */ #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 #ifdef FX9860G
/* Screen dimensions on fx9860g */ #include <gint/defs/types.h>
#define DWIDTH 128
#define DHEIGHT 64
/* color_t - colors available for drawing /* color_t - colors available for drawing
The following colors are defined by the library: The following colors are defined by the library:
OPAQUE COLORS (override existing pixels) OPAQUE COLORS (override existing pixels)
white, black - the usual thing 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) OPERATORS (combine with existing pixels)
none - leaves unchanged 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 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. 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). @x1 @y1 @x2 @y2 Bounding rectangle (drawn area).
@color Allowed colors: white, black, none, reverse */ @color Allowed colors: white, black, none, reverse */
void drect(int x1, int y1, int x2, int y2, color_t color); 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 /* dpixel() - change a pixel's color
If the requested color is an operator, the result will depend on the current If the requested color is an operator, the result will depend on the current
color of the pixel. color of the pixel. Setting single pixels is the slowest method to produce a
graphical result: all other functions for rendering lines, rectangles,
If GINT_LAX is defined, this function makes the following assumptions: images or text use highly-optimized methods, so check them out first if you
0 <= x <= 127 care about performance.
0 <= y <= 63
@x @y Coordinates of the pixel to repaint @x @y Coordinates of the pixel to repaint
@color Allowed colors: white, black, none, reverse */ @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 that the affected pixels may not be exactly the same when using dline() and
Bdisp_DrawLineVRAM(). 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: @x1 @y1 @x2 @y2 End points of the line (both included).
0 <= x1 <= x2 <= 127
0 <= y1, y2 <= 63
@x1 @y1 @x2 @y2 End points of theline (both included).
@color Allowed colors: white, black, none, reverse */ @color Allowed colors: white, black, none, reverse */
void dline(int x1, int y1, int x2, int y2, color_t color); 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 /* FX9860G */
#endif /* GINT_DISPLAY_FX */ #endif /* GINT_DISPLAY_FX */

View file

@ -5,6 +5,8 @@
#ifndef GINT_DISPLAY #ifndef GINT_DISPLAY
#define GINT_DISPLAY #define GINT_DISPLAY
#include <gint/defs/types.h>
/* Expose the VRAM variable if GINT_NEED_VRAM is defined. This address is used /* 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 as the VRAM basis by all of gint's drawing functions, and must point to a
suitable buffer: suitable buffer:
@ -13,15 +15,15 @@
This variable is primarily meant to be exposed to gint functions, but This variable is primarily meant to be exposed to gint functions, but
add-ins may use it or change it freely: add-ins may use it or change it freely:
- To use another video ram area (triple buffering or more, gray engine) ; - To use another video ram area (triple buffering or more, gray engine);
- To implement additional drawing functions ; - To implement additional drawing functions;
- When not drawing, as additional RAM (especially on fxcg50). */ - When not drawing, as additional RAM (especially on fxcg50). */
#ifdef GINT_NEED_VRAM #ifdef GINT_NEED_VRAM
extern uint32_t *vram; extern uint32_t *vram;
#endif #endif
/* As you would expect, display on fx9860g and display on fxcg50 are completely /* 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 #ifdef FX9860G
#include <gint/display-fx.h> #include <gint/display-fx.h>
@ -29,4 +31,28 @@ extern uint32_t *vram;
#include <gint/display-cg.h> #include <gint/display-cg.h>
#endif #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 */ #endif /* GINT_DISPLAY */

View file

@ -5,8 +5,8 @@
#ifndef GINT_DRIVERS #ifndef GINT_DRIVERS
#define GINT_DRIVERS #define GINT_DRIVERS
#include <defs/attributes.h> #include <gint/defs/attributes.h>
#include <defs/types.h> #include <gint/defs/types.h>
//--- //---
// Driver initialization // Driver initialization
@ -39,7 +39,7 @@ typedef struct
may be set to NULL */ may be set to NULL */
void (*init)(void); void (*init)(void);
/* unload() - unitialize the driver /* unload() - deinitialize the driver
This function is called before ctx_restore() when gint is unloaded. This function is called before ctx_restore() when gint is unloaded.
If there is no unload function, the field may be set to NULL */ If there is no unload function, the field may be set to NULL */
void (*unload)(void); void (*unload)(void);
@ -65,7 +65,7 @@ typedef struct
@ctx A context buffer filled by ctx_save() */ @ctx A context buffer filled by ctx_save() */
void (*ctx_restore)(void *ctx); void (*ctx_restore)(void *ctx);
} PACKED(4) gint_driver_t; } GPACKED(4) gint_driver_t;
/* GINT_DECLARE_DRIVER() - make a driver visible to gint /* 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 The @level argument represents the priority level: lower numbers mean that
drivers will be loaded sooner. This numbering allows a primitive form of 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. */ higher than the level of all the drivers you depend on. */
#define GINT_DECLARE_DRIVER(level, name) \ #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 /* GINT_DRIVER_SH3() - declare a function for SH3-rectification
This macros allows the argument function to not exist on fxcg50. */ 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 #ifndef GINT_DRIVERS_T6K11
#define 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 /* 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 It would be possible to restore contrast, or update the system value on
change, if the address was known for all OS versions. 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 */ @contrast Requested contrast value */
void t6k11_contrast(int contrast); void t6k11_contrast(int contrast);

View file

@ -5,11 +5,10 @@
#ifndef GINT_GINT #ifndef GINT_GINT
#define GINT_GINT #define GINT_GINT
#include <defs/attributes.h> #include <gint/defs/types.h>
#include <defs/types.h>
/* gint_version() - get the library version number /* GINT_VERSION - the library version number
Returns gint's running version number, which is made of four fields: Provides gint's running version number, which is made of four fields:
31 24 23 20 19 16 15 0 31 24 23 20 19 16 15 0
+---------------+---------------+---------------+---------------+ +---------------+---------------+---------------+---------------+
@ -22,11 +21,9 @@
The build number uniquely identifies a binary version of the library. The build number uniquely identifies a binary version of the library.
For instance, 0x72100053 translates as "release 1.0 build 83". */ For instance, 0x72100053 translates as "release 1.0 build 83". */
HDRFUNC uint32_t gint_version(void) /* TODO: Fix version number */
{ extern char GINT_VERSION;
extern char GINT_VERSION; #define GINT_VERSION ((uint32_t)&GINT_VERSION)
return (uint32_t)&GINT_VERSION;
}
//--- //---
// Library management // Library management
@ -103,16 +100,17 @@ int gint_intlevel(int intid, int level);
you're not interfering with usual interrupt assignments. you're not interfering with usual interrupt assignments.
The first parameter 'event_code' represents the event_code associated with The first parameter 'event_code' represents the event_code associated with
the interrupt. The codes are normally platform-dependent, but gint always the interrupt. If it's not a multiple of 0x20 then you're doing something
uses SH7305 codes: SH3 platforms have a translation table. See the wrong. The codes are normally platform-dependent, but gint always uses
documentation for a list of event codes and their associated interrupts. 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 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. 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, 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 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 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 address of a callback function. gint often stores this data in the last

View file

@ -5,34 +5,55 @@
#ifndef GINT_KEYBOARD #ifndef GINT_KEYBOARD
#define 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 /* key_event_t - any keyboard event
This structure represents an event that occurs on the keyboard. This is a 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 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, 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 which is 128 Hz by default, so you'll get 128 repeat events by second if a
is kept pressed. We can filter the events to emit one only every second, for key is kept pressed. We could filter the events to emit one only every
example, but it's difficult to do it for all keys at the same time. Thus the second, for example, but it's difficult to do it for all keys at the same
control of repetition delays is restricted to getkey(). time. Thus the control of repeat delays is left to getkey().
When mod = 1, shift and alpha indicate whether the key has been modified. When [mod = 1], attributes [shift] and [alpha] indicate whether the key has
This is only possible for key press events returned by getkey(). Note that been modified. Only key press events returned by getkey() have [mod = 1].
you can't have key = shift and mod = 1 at the same time. 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 The [time] attribute indicates when the event occurred. It is a snapshot of
at each keyboard scan and *it wraps around every 4 minutes* (at 16 Hz). a time counter that increases at each keyboard scan and *wraps around every
I expect this attribute to be useful to analyze combo sequences in games. 8 minutes* (at 128 Hz). I expect this attribute to be useful to analyze
Make sure you are aware of the two nitpicks: 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. * Don't keep the time values for too long because of the wrap-around effect.
- 0xfff is just before 0x000, not long after. */ * 0xffff is "just before" 0x0000, not "long after". */
typedef struct 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 mod :1; /* Whether modifiers are used */
uint shift :1; /* If mod=1, whether SHIFT was pressed */ uint shift :1; /* If mod=1, whether SHIFT was pressed */
@ -41,7 +62,7 @@ typedef struct
uint type :2; /* Type of key event */ uint type :2; /* Type of key event */
uint key :8; /* Hit key */ 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 */ /* Keyboard event types, as in the type field of key_event_t */
enum enum
@ -58,10 +79,98 @@ enum
#define KEYBOARD_QUEUE_SIZE 64 #define KEYBOARD_QUEUE_SIZE 64
#endif #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 // 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 */ #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 #ifndef GINT_CORE_MPU
#define GINT_CORE_MPU #define GINT_CORE_MPU
#include <defs/attributes.h>
/* mpu_t - supported MPUs */ /* mpu_t - supported MPUs */
typedef enum typedef enum
{ {
@ -27,14 +25,11 @@ typedef enum
#ifdef FX9860G #ifdef FX9860G
/* mpu_id() - get the name of the underlying MPU */ /* mpu_id() - get the name of the underlying MPU */
HDRFUNC mpu_t mpu_id(void) extern const mpu_t gint_mpu_id;
{ #define gint_mpu() (gint_mpu_id)
extern const mpu_t mpu;
return mpu;
}
/* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */ /* 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()) #define isSH4() (!isSH3())
/* mpu_init() - probe the MPU type /* mpu_init() - probe the MPU type
@ -44,7 +39,7 @@ typedef enum
#else /* FXCG50 */ #else /* FXCG50 */
/* All fxcg50 machines have an SH7305, which makes things simpler. */ /* All fxcg50 machines have an SH7305, which makes things simpler. */
#define mpu_id() mpu_sh7305 #define gint_mpu() mpu_sh7305
#define isSH3() 0 #define isSH3() 0
#define isSH4() 1 #define isSH4() 1

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -5,9 +5,8 @@
#ifndef GINT_TIMER #ifndef GINT_TIMER
#define GINT_TIMER #define GINT_TIMER
#include <defs/attributes.h> #include <gint/defs/types.h>
#include <defs/types.h> #include <gint/mpu.h>
#include <core/mpu.h>
/* Timer identifiers /* Timer identifiers
@ -88,7 +87,7 @@ typedef enum
@callback Callback function (called when the timer fires) @callback Callback function (called when the timer fires)
@arg Passed as argument to the callback function */ @arg Passed as argument to the callback function */
int timer_setup(int timer, uint32_t delay, timer_input_t clock, 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 /* 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 @timer The timer you are planning to use
@delay_us Requested delay in microseconds */ @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 /* timer_start() - start a configured timer
The specified timer will start counting down and fire callbacks at regular 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() */ @timer Timer id, as returned by timer_setup() */
void timer_stop(int timer); 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 */ #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/drivers.h>
#include <gint/clock.h> #include <gint/clock.h>
#include <defs/types.h> #include <gint/mpu.h>
#include <core/mpu.h> #include <gint/mpu/cpg.h>
#include <core/cpg.h>
//--- //---
// Driver storage // Driver storage
@ -69,6 +68,11 @@ static void sh7705_probe(void)
} }
#undef CPG #undef CPG
#else
/* This prototype will silence warnings on fxcg50 */
void sh7705_probe(void);
#endif /* FX9860G */ #endif /* FX9860G */
//--- //---
@ -80,17 +84,17 @@ static void sh7705_probe(void)
static void sh7305_probe(void) static void sh7305_probe(void)
{ {
/* The meaning of the PLL setting on SH7305 differs from the /* 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; int pll = CPG.FRQCRA.STC + 1;
freq.PLL = pll; 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; int fll = CPG.FLLFRQ.FLF;
if(CPG.FLLFRQ.SELXM == 1) fll >>= 1; if(CPG.FLLFRQ.SELXM == 1) fll >>= 1;
freq.FLL = fll; freq.FLL = fll;
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but here /* On SH7724, the divider ratio is given by 1 / (setting + 1), but
it's actually 1 / (2^setting + 1). */ SH7305 behaves as 1 / (2^setting + 1). */
int divb = CPG.FRQCRA.BFC; int divb = CPG.FRQCRA.BFC;
int divi = CPG.FRQCRA.IFC; int divi = CPG.FRQCRA.IFC;
@ -125,7 +129,7 @@ static void init(void)
} }
gint_driver_t drv_cpg = { gint_driver_t drv_cpg = {
.name = "Clock Pulse Generator", .name = "CPG",
.init = init, .init = init,
.ctx_size = 0, .ctx_size = 0,
.sys_ctx = NULL, .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 // gint:core:bootlog - Boot-time on-screen log for extreme debugging
//--- //---
#include <defs/types.h> /* TODO: Review, enhance and fix bootlog */
#include <core/intc.h>
#include <core/mpu.h> #include <gint/defs/types.h>
#include <gint/mpu.h>
#include <gint/mpu/intc.h>
#include <core/mmu.h> #include <core/mmu.h>
#include <gint/gint.h> #include <gint/gint.h>
@ -18,7 +20,7 @@ extern char
void bootlog_loaded(void) void bootlog_loaded(void)
{ {
/* Version string - the string constant resides in ROM */ /* 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"; const char *model = "gint #0.0-000";
char str[14]; char str[14];
@ -43,7 +45,7 @@ void bootlog_loaded(void)
uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss; uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss;
/* MPU type */ /* MPU type */
mpu_t mpu = mpu_id(); mpu_t mpu = gint_mpu();
const char *names = "SH7337\0 SH7305\0 SH7355\0 SH7724"; const char *names = "SH7337\0 SH7305\0 SH7355\0 SH7724";
/* TODO: Use a solid API for boot-time printing */ /* TODO: Use a solid API for boot-time printing */
@ -87,28 +89,28 @@ void bootlog_kernel(void)
if(isSH3()) if(isSH3())
{ {
print(1, 5, "ABCD"); print(1, 5, "ABCD");
print_hex( 6, 5, INTC3._.IPRA->word, 4); print_hex( 6, 5, SH7705_INTC._.IPRA->word, 4);
print_hex(10, 5, INTC3._.IPRB->word, 4); print_hex(10, 5, SH7705_INTC._.IPRB->word, 4);
print_hex(14, 5, INTC3._.IPRC->word, 4); print_hex(14, 5, SH7705_INTC._.IPRC->word, 4);
print_hex(18, 5, INTC3._.IPRD->word, 4); print_hex(18, 5, SH7705_INTC._.IPRD->word, 4);
print(1, 6, "EFGH"); print(1, 6, "EFGH");
print_hex( 6, 6, INTC3._.IPRE->word, 4); print_hex( 6, 6, SH7705_INTC._.IPRE->word, 4);
print_hex(10, 6, INTC3._.IPRF->word, 4); print_hex(10, 6, SH7705_INTC._.IPRF->word, 4);
print_hex(14, 6, INTC3._.IPRG->word, 4); print_hex(14, 6, SH7705_INTC._.IPRG->word, 4);
print_hex(18, 6, INTC3._.IPRH->word, 4); print_hex(18, 6, SH7705_INTC._.IPRH->word, 4);
} }
else else
{ {
print(1, 5, "ACFG"); print(1, 5, "ACFG");
print_hex( 6, 5, INTC4._->IPRA.word, 4); print_hex( 6, 5, SH7305_INTC._->IPRA.word, 4);
print_hex(10, 5, INTC4._->IPRC.word, 4); print_hex(10, 5, SH7305_INTC._->IPRC.word, 4);
print_hex(14, 5, INTC4._->IPRF.word, 4); print_hex(14, 5, SH7305_INTC._->IPRF.word, 4);
print_hex(18, 5, INTC4._->IPRG.word, 4); print_hex(18, 5, SH7305_INTC._->IPRG.word, 4);
print(1, 6, "HJKL"); print(1, 6, "HJKL");
print_hex( 6, 6, INTC4._->IPRH.word, 4); print_hex( 6, 6, SH7305_INTC._->IPRH.word, 4);
print_hex(10, 6, INTC4._->IPRJ.word, 4); print_hex(10, 6, SH7305_INTC._->IPRJ.word, 4);
print_hex(14, 6, INTC4._->IPRK.word, 4); print_hex(14, 6, SH7305_INTC._->IPRK.word, 4);
print_hex(18, 6, INTC4._->IPRL.word, 4); print_hex(18, 6, SH7305_INTC._->IPRL.word, 4);
} }
Bdisp_PutDisp_DD(); Bdisp_PutDisp_DD();

View file

@ -3,13 +3,13 @@
//--- //---
#include <gint/gint.h> #include <gint/gint.h>
#include <core/mpu.h>
#include <core/intc.h>
#include <core/memory.h> #include <core/memory.h>
#include <gint/mpu.h>
#include <gint/mpu/intc.h>
/* Interrupt controllers */ /* Interrupt controllers */
GDATA3 sh7705_intc_t INTC3 = { GDATA3 sh7705_intc_t SH7705_INTC = {
.IPRS = { .IPRS = {
(void *)0xfffffee2, (void *)0xfffffee4, (void *)0xfffffee2, (void *)0xfffffee4,
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a, (void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
@ -18,7 +18,7 @@ GDATA3 sh7705_intc_t INTC3 = {
.ICR1 = (void *)0xa4000010, .ICR1 = (void *)0xa4000010,
}; };
GDATA sh7305_intc_t INTC4 = { GDATA sh7305_intc_t SH7305_INTC = {
.IPRS = (void *)0xa4080000, .IPRS = (void *)0xa4080000,
.MSK = (void *)0xa4080080, .MSK = (void *)0xa4080080,
.MSKCLR = (void *)0xa40800c0, .MSKCLR = (void *)0xa40800c0,
@ -37,8 +37,8 @@ int gint_intlevel(int intid, int level)
level &= 0xf; level &= 0xf;
ipr = isSH3() ipr = isSH3()
? INTC3.IPRS[intid >> 2] /* SH3-based */ ? SH7705_INTC.IPRS[intid >> 2] /* SH3-based */
: &INTC4.IPRS[2 * (intid >> 2)]; /* SH4-based */ : &SH7305_INTC.IPRS[2 * (intid >> 2)]; /* SH4-based */
int oldlevel = (*ipr >> shift) & 0xf; int oldlevel = (*ipr >> shift) & 0xf;
*ipr = (*ipr & ~(0xf << shift)) | (level << shift); *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 - It is possible to map MPU-specific blocks at runtime, to avoid checking
the MPU each time an interrupt is handled; the MPU each time an interrupt is handled;
- However, the blocks cannot rely on relative displacements or cross- - However, the blocks can only rely on relative displacements or cross-
references unless their relative order is fully known. This happens with references if their relative order is known and heeded for. A good example
the timer driver, and it works swimmingly; just be careful. */ of this is the timer driver. Please be careful. */
/* SH7305-TYPE DEBUG INTERRUPT HANDLER - 26 BYTES */ /* 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 <stddef.h>
#include <stdint.h> #include <stdint.h>
@ -85,7 +86,7 @@ void *memcpy(void * restrict dst, const void * restrict src, size_t n)
return dst; 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) // (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; 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; return 0;
} }

View file

@ -11,14 +11,14 @@
#ifdef FX9860G #ifdef FX9860G
/* tlb_addr() - get the P4 address of a TLB address entry */ /* 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); uint32_t addr = 0xf2000000 | (E << 12) | (way << 8);
return (void *)addr; return (void *)addr;
} }
/* tlb_data() - get the P4 address of a TLB data entry */ /* 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); uint32_t addr = 0xf3000000 | (E << 12) | (way << 8);
return (void *)addr; 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 */ /* 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); uint32_t addr = 0xf6000000 | ((E & 0x3f) << 8);
return (void *)addr; return (void *)addr;
} }
/* utlb_data() - get the P4 address of a UTLB data entry */ /* 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); uint32_t addr = 0xf7000000 | ((E & 0x3f) << 8);
return (void *)addr; return (void *)addr;

View file

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

View file

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

View file

@ -2,13 +2,13 @@
// gint:core:start - Kernel initialization and C runtime // gint:core:start - Kernel initialization and C runtime
//-- //--
#include <defs/attributes.h> #include <gint/defs/attributes.h>
#include <defs/types.h> #include <gint/defs/types.h>
#include <core/bootlog.h> #include <core/bootlog.h>
#include <core/mpu.h>
#include <core/mmu.h> #include <core/mmu.h>
#include <gint/drivers.h> #include <gint/drivers.h>
#include <gint/gint.h> #include <gint/gint.h>
#include <gint/mpu.h>
/* Symbols provided by the linker script. For sections: /* Symbols provided by the linker script. For sections:
- l* represents the load address (source address in ROM) - l* represents the load address (source address in ROM)
@ -31,7 +31,7 @@ int main(int isappli, int optnum);
@l Source pointer (load address) @l Source pointer (load address)
@s Size of area (should be a multiple of 16) @s Size of area (should be a multiple of 16)
@r Destination pointer (relocation address) */ @r Destination pointer (relocation address) */
SECTION(".pretext") GSECTION(".pretext")
static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r) static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
{ {
while(s > 0) 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 /* regclr() - clear a memory region using symbol information
@r Source pointer (base address) @r Source pointer (base address)
@s Size of area (should be a multiple of 16) */ @s Size of area (should be a multiple of 16) */
SECTION(".pretext") GSECTION(".pretext")
static void regclr(uint32_t *r, int32_t s) static void regclr(uint32_t *r, int32_t s)
{ {
while(s > 0) 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. corresponds to region [b; b+s) when b and s are 1k-aligned.
@b Base pointer: first address checked @b Base pointer: first address checked
@s Size of region */ @s Size of region */
SECTION(".pretext") GSECTION(".pretext")
static void explore(volatile void *b, int32_t s) static void explore(volatile void *b, int32_t s)
{ {
ATTR(unused) uint8_t x; GUNUSED uint8_t x;
while(s > 0) 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) /* acall() - call an array of functions (constructors or destructors)
@f First element of array @f First element of array
@l First element outside of the array */ @l First element outside of the array */
SECTION(".pretext") GSECTION(".pretext")
static void acall(void (**f)(void), void (**l)(void)) static void acall(void (**f)(void), void (**l)(void))
{ {
while(f < l) (*(*f++))(); while(f < l) (*(*f++))();
@ -94,7 +94,7 @@ static void acall(void (**f)(void), void (**l)(void))
/* start() - this is where it all starts /* start() - this is where it all starts
Returns a status code. Invoking main menu is better than returning! */ Returns a status code. Invoking main menu is better than returning! */
SECTION(".pretext.entry") GSECTION(".pretext.entry")
int start(int isappli, int optnum) int start(int isappli, int optnum)
{ {
/* We are currently in a dynamic userspace mapping of an add-in run /* 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 redirecting interrupts and reimplementing drivers, so we can't rely
too much on the system. Ladies and gentlemen, let's have fun! ;D */ 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 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 */ processor with some incompatible features */
/* Detect architecture - this will tell SH3 from SH4 on fx9860g */ /* 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(lgdata, sgdata, rgdata);
regcpy(ldata, sdata, rdata); regcpy(ldata, sdata, rdata);
regclr(rbss, sbss); regclr(rbss, sbss);
#ifdef GINT_BOOT_LOG
bootlog_loaded(); bootlog_loaded();
#endif
/* Traverse all ROM pages */ /* Traverse all ROM pages */
explore(brom, srom); explore(brom, srom);
@ -135,15 +132,12 @@ int start(int isappli, int optnum)
uint32_t rom, ram; uint32_t rom, ram;
isSH3() ? tlb_mapped_memory(&rom, &ram) isSH3() ? tlb_mapped_memory(&rom, &ram)
: utlb_mapped_memory(&rom, &ram); : utlb_mapped_memory(&rom, &ram);
#ifdef GINT_BOOT_LOG
bootlog_mapped(rom, ram); bootlog_mapped(rom, ram);
#endif
//--- //---
/* Cancel add-in execution if not all pages are mapped /* 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 */ add-in is not mapped yet */
if(rom < (uint32_t)&srom) if(rom < (uint32_t)&srom)
{ {
@ -162,21 +156,21 @@ int start(int isappli, int optnum)
/* Install gint and switch VBR */ /* Install gint and switch VBR */
gint_install(); gint_install();
#ifdef GINT_BOOT_LOG
bootlog_kernel(); bootlog_kernel();
#endif
/* We are now running on our own in kernel mode. To avoid breaking the /* We are now running on our own in kernel mode. Since we have taken
system, we want to limit our use of syscalls, so we'll have to do control of interrupts, pretty much any interaction with the sytem
device driving ourselves. */ 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; gint_driver_t *drv;
/* Initialize all drivers by saving the system settings */ /* Initialize all drivers by saving the system settings */
for(drv = &bdrv; drv < &edrv; drv++) for(drv = &bdrv; drv < &edrv; drv++)
{ {
/* Hook for old SH3 fx9860g machines */
if(isSH3() && drv->driver_sh3) drv->driver_sh3(); if(isSH3() && drv->driver_sh3) drv->driver_sh3();
if(drv->ctx_save) drv->ctx_save(drv->sys_ctx); if(drv->ctx_save) drv->ctx_save(drv->sys_ctx);
if(drv->init) drv->init(); 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 application. We have already loaded the RAM sections earlier; all
that's left is calling the constructors and rolling main(). */ that's left is calling the constructors and rolling main(). */
/* Call the constructors */
acall(btors, mtors); acall(btors, mtors);
/* Execute the user application */
int ret = main(isappli, optnum); int ret = main(isappli, optnum);
/* Call the destructors*/
acall(mtors, etors); acall(mtors, etors);
/* Before leaving the application, we need to clean up our mess. We /* 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 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 /* Unload gint and give back control to the system. Driver settings
will be restored while interrupts are disabled */ 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 // gint:keysc:iokbd - I/O-based keyboard input
//--- //---
#include <defs/types.h> #include <gint/defs/types.h>
#include <core/pfc.h> #include <gint/mpu/pfc.h>
/* This file is SH7705-only. */ /* This file is SH7705-only. */
#ifdef FX9860G #ifdef FX9860G
@ -104,6 +104,7 @@ uint8_t iokbd_row(int row)
return input; return input;
} }
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data */
void iokbd_scan(uint8_t *scan) void iokbd_scan(uint8_t *scan)
{ {
/* Scan each row independently; the gain from scanning them altogether /* Scan each row independently; the gain from scanning them altogether

View file

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

View file

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

View file

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

View file

@ -3,11 +3,11 @@
//--- //---
#include <gint/drivers.h> #include <gint/drivers.h>
#include <drivers/t6k11.h> #include <gint/drivers/t6k11.h>
#include <defs/attributes.h> #include <gint/defs/attributes.h>
#include <defs/types.h> #include <gint/defs/types.h>
#include <core/mpu.h> #include <gint/mpu.h>
#ifdef FX9860G #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 /* command() - send a command to set the value of a register
@reg Register number @reg Register number
@data Value to set in reg */ @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; *sel = reg;
*cmd = data; *cmd = data;
} }
/* status() - read the status byte from the display driver */ /* status() - read the status byte from the display driver */
INLINE static uint8_t status(void) GINLINE static uint8_t status(void)
{ {
return *sel; return *sel;
} }
/* write_row() - send 16 bytes to the display driver /* write_row() - send 16 bytes to the display driver
@buf Buffer to take data from */ @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; *sel = reg_data;
@ -129,6 +129,12 @@ void t6k11_contrast(int contrast)
fx9860G II with OS 02.05.2201, are in range 0x97 .. 0xb7. fx9860G II with OS 02.05.2201, are in range 0x97 .. 0xb7.
This means VLC0 = 2 and CONTRAST in [23..55] */ This means VLC0 = 2 and CONTRAST in [23..55] */
command(reg_contrast, 0x97 + contrast); 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 */ /* t6k11_backlight() - manage the screen backlight */
@ -168,7 +174,7 @@ typedef struct
/* There *are* other parameters that are affected by the driver, but /* There *are* other parameters that are affected by the driver, but
they cannot be read, so I can't determine the system's setting */ 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 */ /* Pre-allocate a context in gint's uninitialized section */
GBSS static ctx_t sys_ctx; GBSS static ctx_t sys_ctx;
@ -197,7 +203,7 @@ static void ctx_restore(void *buf)
//--- //---
gint_driver_t drv_t6k11 = { gint_driver_t drv_t6k11 = {
.name = "Toshiba T6K11", .name = "T6K11",
.init = NULL, .init = NULL,
.ctx_size = sizeof(ctx_t), .ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx, .sys_ctx = &sys_ctx,

View file

@ -27,7 +27,7 @@
- Clear the interrupt flag - Clear the interrupt flag
- Invoke a callback function and pass it a user-provided argument - Invoke a callback function and pass it a user-provided argument
- Stop the timer if the callback returns non-zero - 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 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 they are contiguous in memory. The assembler will make that assumption, and
@ -60,7 +60,7 @@ _inth_tmu_0:
mov.l @(4, r0), r4 mov.l @(4, r0), r4
lds.l @r15+, pr lds.l @r15+, pr
/* Prepare stopping the timer */ /* Prepare stopping the timer and jump to second section */
mov.l .tstr, r5 mov.l .tstr, r5
bra .stoptimer bra .stoptimer
mov.l @r15+, r1 mov.l @r15+, r1
@ -122,11 +122,11 @@ _inth_tmu_storage:
To implement the same functionalities as the standard timers, several blocks 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 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 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 and 0xc40. Since handler 0xc60 is free on SH4 and not redirected to on SH3,
build a three-handler block and achieve the same result as above. 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 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) 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. 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 */ /* FIRST GATE - ETMU2 entry, clear flag and prepare callback */
_inth_tmu_extra2: _inth_tmu_extra2:
/* Warning: the size of this section (2 bytes) is hardcoded in another /* Warning: the size of the following instruction (2 bytes) is
interrupt handler, _inth_tmu_extra_others */ hardcoded in another interrupt handler, _inth_tmu_extra_others */
mova .storage_extra_1, r0 mova .storage_extra_1, r0
.extra_callback: .extra_callback:
@ -214,7 +214,7 @@ _inth_tmu_extra_others:
- 0x600 to reach the interrupt handlers - 0x600 to reach the interrupt handlers
- 0x020 to jump over the entry gate - 0x020 to jump over the entry gate
- 0x840 to reach the handler of extra timer 2 - 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 1: .long 0xe62
.zero 4 .zero 4

View file

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