mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 04:23:36 +01:00
way too much, including bopti/topti, timers, and more.
This commit is contained in:
parent
ae4e5d83b2
commit
3f7c0a04ad
68 changed files with 2729 additions and 825 deletions
11
.gitignore
vendored
11
.gitignore
vendored
|
@ -1,5 +1,5 @@
|
|||
# Build directory
|
||||
build/**
|
||||
# Build directories
|
||||
build*/**
|
||||
|
||||
# Sublime Text files
|
||||
*.sublime-project
|
||||
|
@ -7,10 +7,3 @@ build/**
|
|||
|
||||
# Lots of unordered project notes
|
||||
notes/**
|
||||
|
||||
# Output files
|
||||
bin/**
|
||||
|
||||
# Configuration files
|
||||
config/**
|
||||
|
||||
|
|
213
Makefile
Executable file → Normal file
213
Makefile
Executable file → Normal file
|
@ -1,172 +1,59 @@
|
|||
#! /usr/bin/make -f
|
||||
#
|
||||
# gint project Makefile
|
||||
#
|
||||
#---
|
||||
|
||||
#
|
||||
# Build configuration
|
||||
#
|
||||
builds := $(wildcard build*)
|
||||
|
||||
# Some rules don't care if config files are missing (eg. clean, etc)
|
||||
cfg := config/Makefile.cfg
|
||||
-include $(cfg)
|
||||
|
||||
# Those that do may use this special dependency
|
||||
cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg))
|
||||
|
||||
cf-fx := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
|
||||
-fstrict-volatile-bitfields -I include $(cfg_macros)
|
||||
cf-cg := -m4 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
|
||||
-fstrict-volatile-bitfields -I include $(cfg_macros)
|
||||
|
||||
# On fx9860g, use the sh3eb-elf toolchain; on fxcg50, prefer sh4eb-nofpu-elf
|
||||
toolchain := $(if $(filter $(cfg_target),fx),sh3eb-elf,sh4eb-nofpu-elf)
|
||||
|
||||
# Compiler flags, assembler flags, dependency generation, archiving
|
||||
cflags := $(cf-$(cfg_target))
|
||||
sflags := $(cfg_macros)
|
||||
dflags = -MMD -MT $@ -MF $(@:.o=.d) -MP
|
||||
arflags :=
|
||||
|
||||
#
|
||||
# File listings
|
||||
#
|
||||
|
||||
# Target file (cfg_target is either "fx" or "cg")
|
||||
target := bin/libgint-$(cfg_target).a
|
||||
|
||||
# Automatic names for object and dependency files
|
||||
src2obj = build/$(1:src/%=%).o
|
||||
src2dep = build/$(1:src/%=%).d
|
||||
|
||||
# Source files
|
||||
src := $(shell find src -name '*.[csS]')
|
||||
src_obj := $(foreach s,$(src),$(call src2obj,$s))
|
||||
|
||||
# Files with special handling
|
||||
# spe := build/version.o src/display/font.bmp
|
||||
spe :=
|
||||
spe_obj := build/version.o $(foreach s,$(spe),$(call src2obj,$s))
|
||||
|
||||
# All object files
|
||||
obj := $(src_obj) $(spe_obj)
|
||||
|
||||
# Version file
|
||||
version := config/version
|
||||
|
||||
#
|
||||
# Toolchain
|
||||
#
|
||||
|
||||
gcc = $(toolchain)-gcc
|
||||
as = $(toolchain)-as
|
||||
ld = $(toolchain)-ld
|
||||
ar = $(toolchain)-ar
|
||||
objcopy = $(toolchain)-objcopy
|
||||
conv = fxconv
|
||||
|
||||
|
||||
#
|
||||
# Version management
|
||||
#
|
||||
|
||||
# Retrieve version information
|
||||
v_file = $(shell cat $(version) | sed 's/[-.]/ /g')
|
||||
v_type = $(word 1,$(v_file))
|
||||
v_major = $(word 2,$(v_file))
|
||||
v_minor = $(word 3,$(v_file))
|
||||
v_build = $(word 4,$(v_file))
|
||||
|
||||
# Create the current version symbol and the new version integer
|
||||
v_newbuild = $(shell echo $$(($(v_build) + 1)))
|
||||
v_letter = $(shell echo -n $(v_type) | sed -r 's/^(.).*/\1/')
|
||||
v_symbol = $(shell printf '0x%02x%01x%01x%04x' "'$(v_letter)'" \
|
||||
$(v_major) $(v_minor) $(v_build))
|
||||
|
||||
#
|
||||
# Build rules
|
||||
#
|
||||
|
||||
all: $(target)
|
||||
|
||||
$(target): $(obj) $(version) | $(dir $(target))
|
||||
$(call cmd_l,ar,$@) $(ar) -crs $(arflags) $@ $(obj)
|
||||
|
||||
# Assembler sources
|
||||
build/%.s.o: src/%.s build/%.s.d
|
||||
@ mkdir -p $(dir $@)
|
||||
$(call cmd_b,as,$<) $(gcc) -c $< -o $@ $(sflags)
|
||||
build/%.S.o: src/%.S build/%.S.d
|
||||
@ mkdir -p $(dir $@)
|
||||
$(call cmd_b,as,$<) $(gcc) -c $< -o $@ $(sflags)
|
||||
|
||||
# C sources
|
||||
build/%.c.o: src/%.c build/%.c.d $(cfgdep)
|
||||
@ mkdir -p $(dir $@)
|
||||
$(call cmd_b,gcc,$<) $(gcc) -c $< -o $@ $(dflags) $(cflags)
|
||||
|
||||
# Special files
|
||||
$(call src2obj,src/display/font.bmp): src/display/font.bmp
|
||||
@ mkdir -p $(dir $@)
|
||||
$(call cmd_m,fxconv,$<) $(conv) -font $< -n gint_font -o $@
|
||||
|
||||
# Version symbol. ld generates a .stack section for unknown reasons; I fall
|
||||
# back to removing it afterwards.
|
||||
build/version.o:
|
||||
@ mkdir -p $(dir $@)
|
||||
@ echo "_GINT_VERSION = $(v_symbol);" > $@.txt
|
||||
$(call cmd_b,ld,$@) $(ld) -r -R $@.txt -o $@
|
||||
|
||||
#
|
||||
# Cleaning
|
||||
#
|
||||
|
||||
clean:
|
||||
@ rm -rf build/
|
||||
distclean: clean
|
||||
@ rm -rf bin/
|
||||
@ rm -f $(config)
|
||||
|
||||
#
|
||||
# Utilities
|
||||
#
|
||||
|
||||
$(version): $(src) $(wildcard include/**/*)
|
||||
@ echo '$(v_type)-$(v_major).$(v_minor)-$(v_newbuild)' > $@
|
||||
|
||||
# Evaluated when a rule requires the configuration file but it doesn't exist
|
||||
CFGDEP:
|
||||
@ echo "Configuration file $(cfg) is missing. Have you configured?"
|
||||
@ echo "See \`./configure --help\` for details."
|
||||
ifeq "$(builds)" ""
|
||||
nobuild:
|
||||
@ echo "error: you don't seem to have any build directory (build*)" >&2
|
||||
@ echo "" >&2
|
||||
@ echo "You can configure one like this:" >&2
|
||||
@ echo " mkdir build && cd build && ../configure [options...]" >&2
|
||||
@ echo "" >&2
|
||||
@ false
|
||||
endif
|
||||
|
||||
# Make directories: make conveniently leaves a '/' at the end of $(dir ...)
|
||||
%/:
|
||||
@ mkdir -p $@
|
||||
# Don't try to unlink directories once they're built (that wouldn't work =p)
|
||||
.PRECIOUS: %/
|
||||
#
|
||||
# all targets
|
||||
#
|
||||
|
||||
# Dependency information
|
||||
-include $(shell [ -d build ] && find build -name *.d)
|
||||
build/%.d: ;
|
||||
.PRECIOUS: build/%.d
|
||||
all-targets := $(foreach b,$(builds),all-$b)
|
||||
|
||||
# Do not output full commands by default
|
||||
VERBOSE ?=
|
||||
all: $(all-targets)
|
||||
|
||||
# Simple command output method
|
||||
# $1 Program name
|
||||
# $2 Argument string to display
|
||||
# $3 Command color
|
||||
define cmd
|
||||
@ echo -e "\e[""$3"";1m>\e[0;1m $1\e[0m $2"
|
||||
$(if $(VERBOSE),,@)
|
||||
endef
|
||||
all-build%: build%
|
||||
@ echo -e "$B::$W Making into $<$N"
|
||||
@ $(MAKE) --no-print-directory -C $<
|
||||
|
||||
# Some pre-colored command kinds: misc, build, link, clean, install
|
||||
cmd_m = $(call cmd,$1,$2,30)
|
||||
cmd_b = $(call cmd,$1,$2,32)
|
||||
cmd_l = $(call cmd,$1,$2,36)
|
||||
cmd_c = $(call cmd,$1,$2,31)
|
||||
cmd_i = $(call cmd,$1,$2,33)
|
||||
#
|
||||
# install targets
|
||||
#
|
||||
|
||||
install-targets := $(foreach b,$(builds),install-$b)
|
||||
|
||||
install: $(install-targets)
|
||||
|
||||
install-build%: build%
|
||||
@ echo -e "$B::$W Installing from $<$N"
|
||||
@ $(MAKE) --no-print-directory -C $< install
|
||||
|
||||
#
|
||||
# uninstall targets
|
||||
#
|
||||
|
||||
uninstall-targets := $(foreach b,$(builds),uninstall-$b)
|
||||
|
||||
uninstall: $(uninstall-targets)
|
||||
|
||||
uninstall-build%: build%
|
||||
@ echo -e "$B::$W Uninstalling from $<$N"
|
||||
@ $(MAKE) --no-print-directory -C $< uninstall
|
||||
|
||||
#
|
||||
# Coloring tools
|
||||
#
|
||||
|
||||
B = \e[34;1m
|
||||
W = \e[39;1m
|
||||
N = \e[0m
|
||||
|
||||
.PHONY: nobuild all all-build% install install-build%
|
||||
|
|
160
README.md
160
README.md
|
@ -1,98 +1,112 @@
|
|||
gint project
|
||||
============
|
||||
# gint project
|
||||
|
||||
gint (pronounce 'guin') is a low-level library for fx-9860G calculators. It
|
||||
provides a mostly free-standing runtime support for the platform, and can be
|
||||
used to develop add-ins under Linux along with the gcc toolchain (`sh3eb-elf`
|
||||
target) and the [fxSDK](http://git.planet-casio.com/lephe/fxsdk).
|
||||
gint (pronounce 'guin') is a development system for Casio fx-9860G II and
|
||||
fx-CG 50 calculators. It provides a mostly free-standing runtime and is used
|
||||
to develop add-ins under Linux, along with specialized GCC toolchains and the
|
||||
[fxSDK](http://git.planet-casio.com/lephe/fxsdk).
|
||||
|
||||
gint is free software: you may use it for any purpose, share it, modify it and
|
||||
share your changes. No credit of any kind is required, though appreciated.
|
||||
gint is a modular kernel that implements its own drivers for the calculator's
|
||||
hardware, overriding the operating system and its syscalls. It is a drop-in
|
||||
replacement from fxlib, with which it is mostly incompatible. gint exposes a
|
||||
new, richer API to manipulate the hardware and take advantage of the full
|
||||
capabilities of the machine.
|
||||
|
||||
This is free software: you may use it for any purpose, share it, and modify it
|
||||
as long as you share your changes. Credit is not required, but please let me
|
||||
know!
|
||||
|
||||
**TODO: Update this file for everything related to project organization**
|
||||
|
||||
|
||||
Programming interface
|
||||
---------------------
|
||||
## Programming interface
|
||||
|
||||
Because of its free-standing design, gint's API provides direct and efficient
|
||||
access to the low-level MPU features, among which:
|
||||
* Extensive keyboard control, including replication of the system behavior for
|
||||
office applications and event-driven decisions for games
|
||||
* Hardware timers running at over 10 MHz, allowing microsecond-level control
|
||||
* Unlimited mappings of callbacks to Real-Time Clock events (requires build)
|
||||
* Access to processor registers for debugging information, determination of
|
||||
processor speed (overclock is on the TODO list), backlight management...
|
||||
|
||||
* Multi-key management with event systems suitable for games
|
||||
* Hardware timers with sub-millisecond and sub-microsecond resolution
|
||||
* Fast screen drivers with DMAC on fx-CG 50
|
||||
* Efficient and user-extendable interrupt management
|
||||
|
||||
The library also offers powerful higher-level features:
|
||||
* A gray engine that works by rapidly swapping monochrome images
|
||||
* Blazingly fast drawing functions when working with the fxSDK (10 times faster
|
||||
image rendering that MonochromeLib)
|
||||
* C Standard functions such as the `printf()` family
|
||||
|
||||
* An enhanced versio of the system's GetKey() and GetKeyWait()
|
||||
* A gray engine that works by rapidly swapping monochrome images on fx-9860G II
|
||||
* Blazingly fast drawing functions when working with the fxSDK (image rendering
|
||||
is 10 times faster than MonochromeLib)
|
||||
* Integrated font management with the fxSDK
|
||||
* Integration with a Newlib port by Memallox (WIP)
|
||||
|
||||
|
||||
Interrupt handler
|
||||
-----------------
|
||||
## Building and installing gint
|
||||
|
||||
The interrupt handler is the lowest-level part of the library. It directly
|
||||
accesses the peripheral modules and workarounds the system to perform keyboard
|
||||
analyzes directly on hardware or timer management.
|
||||
You can choose to build gint for fx-9860G II (monochrome calculators, aka
|
||||
Graph 85 family), fx-CG 50 (color calculators, aka Prizm or Graph 90 family),
|
||||
or both. There are a few dependencies:
|
||||
|
||||
gint does not allow user programs to use their own handlers, but it allows them
|
||||
to complete the original handler if they want to use interrupts that are not
|
||||
supported by the library. It is also possible to map various interrupt-driven
|
||||
events to user-provided callbacks using the API, which is not allowed by the
|
||||
operating system. This is particularly useful for timers and the Real-Time
|
||||
Clock (the 16 Hz interrupt can be used to run a physical engine, while the 1 Hz
|
||||
interrupt can be used in a real-time management game).
|
||||
|
||||
|
||||
Building and installing
|
||||
-----------------------
|
||||
|
||||
To build and install gint, you will need the following components:
|
||||
* The `sh3eb-elf` toolchain linked somewhere in the PATH
|
||||
* A suitable GCC toolcahin in the `PATH`. You can absolutely *not* build gint
|
||||
with your system compiler!
|
||||
* For fx-9860G II, `sh3eb-elf` is strongly advised
|
||||
* For fx-CG 50, `sh4eb-nofpu-elf` is slightly better but `sh3eb-elf` is
|
||||
completely fine
|
||||
* The [fxSDK](http://git.planet-casio.com/lephe/fxsdk) installed and available
|
||||
in the PATH
|
||||
in the PATH. You will need `fxsdk` and `fxconv` to build gint, and if you
|
||||
intend to develop add-ins for fx-9860G II, you probably want `fxg1a` as well.
|
||||
|
||||
The classical way to build gint is to enter a terminal and use the usual:
|
||||
fx-CG 50 developers probably want a g3a wrapper as well; the reference
|
||||
implementation is tari's [mkg3a](https://www.taricorp.net/projects/mkg3a/).
|
||||
This is only necessary when creating g3a files, not to use gint.
|
||||
|
||||
$ ./configure
|
||||
$ make
|
||||
# make install
|
||||
The build process is detailed below for both platforms, the principle is the
|
||||
same. You can build both targets at the same time by reading the two sections.
|
||||
|
||||
This will build the `all-lib` target and install the following components in
|
||||
the storage folder of the fxSDK:
|
||||
* `libgint.a`, the gint library
|
||||
* `libc.a`, the partial standard library
|
||||
* The libgint headers for development
|
||||
By default gint will be installed in the appropriate compiler folder, which
|
||||
is `$PREFIX/` for libraries and linker scripts, and `$PREFIX/include/gint/` for
|
||||
headers, where `PREFIX` is obtained by running
|
||||
`${toolchain}-gcc --print-search-dirs` and reading the line that starts with
|
||||
`install:`. You can change this with the `--prefix` configure option.
|
||||
|
||||
When explicitly running target `all`, the following additional files will be
|
||||
generated in the working directory:
|
||||
* `gintdemo.g1a`, a test application
|
||||
* `gintdbg.g1a`, a non-gint application used for debugging in case gint crashes
|
||||
### Building for fx-9860G II
|
||||
|
||||
The usual `clean`, `mrproper`, and `distclean` rules will clean the directory.
|
||||
There are configuration options, which can be obtained using
|
||||
`./configure --help`. Most of them customize size limits, if a project needs to
|
||||
extend them. The true free-standing program may build the library using the
|
||||
`--no-syscalls` switch, but some features will be disabled (dynamic
|
||||
allocation...).
|
||||
Create a build directory and configure in it:
|
||||
|
||||
% mkdir build.fx && cd build.fx
|
||||
% ../configure --target=fx9860g
|
||||
|
||||
Source organization
|
||||
-------------------
|
||||
Then build the source and install the library files to the selected directory.
|
||||
You might need root access if you selected a target directory owned by root
|
||||
with `--prefix`, or if you built your compiler as root.
|
||||
|
||||
gint is made of *modules*. Each module has one or more of the following
|
||||
component files:
|
||||
* A header file in `/include`
|
||||
* An internal header file in `/include/internals`
|
||||
* Single-function source files in `/src/module`: to avoid linking against the
|
||||
whole library, some functions have their own object files. Their names are
|
||||
those of the functions.
|
||||
* Other source files in `/src/module`: contain multiple functions that always
|
||||
work together, or are lightweight enough not to be separated. Their names
|
||||
often begin with `module_`.
|
||||
* Other files in `/src/module`: the `display` module contains a font.
|
||||
% make
|
||||
% make install
|
||||
|
||||
The demo application is in the `demo` folder.
|
||||
### Building for fx-CG 50
|
||||
|
||||
Create a build directory and configure in it. The default toolchain is
|
||||
`sh4eb-nofpu-elf`, if you wish to build with `sh3eb-elf`, you need to add a
|
||||
command-line option `--toolchain=sh3eb-elf`.
|
||||
|
||||
% mkdir build.cg && cd build.cg
|
||||
% ../configure --target=fxcg50
|
||||
|
||||
Then build the source and install the library files to the selected directory.
|
||||
|
||||
% make
|
||||
% make install
|
||||
|
||||
## Using gint
|
||||
|
||||
To use gint as your runtime environment, the bare minimum is:
|
||||
|
||||
* Build with `-ffreestanding`;
|
||||
* Link with `-T fx9860g.ld` and `-lgint-fx` on fx-9860G;
|
||||
* Link with `-T fxcg50.ld` and `-lgint-cg` on fx-CG 50.
|
||||
|
||||
If you don't have a standard library such as
|
||||
[Memallox's port of newlib](https://git.planet-casio.com/Memallox/libc), you
|
||||
also need `-nostdlib`. I typically use `-m3 -mb` or `-m4 -mb` to specify the
|
||||
platform, but that may not even be necessary.
|
||||
|
||||
Typically you might want to do this with the
|
||||
[fxSDK](http://git.planet-casio.com/lephe/fxsdk), which hides most of the
|
||||
details and makes it easy to roll add-ins.
|
||||
|
|
67
TODO
67
TODO
|
@ -1,15 +1,61 @@
|
|||
# Since compatibility
|
||||
|
||||
Another, more recent list of things to do:
|
||||
- Complete memory functions in [core/memory.c]
|
||||
- Finish the boot log in <core/bootlog.h>
|
||||
- Finish the boot log in [core/bootlog.c]
|
||||
- Document the SH7305 PFC in <gint/mpu/pfc.h>
|
||||
- Change the description of the version number in <gint/gint.h>
|
||||
- Define the version number from full Git info
|
||||
- Do this display in <gint/display-fx.h> and <gint/display-cg.h>
|
||||
- Remove all mentions of GINT_LAX in the code
|
||||
- Write exception handlers
|
||||
- Do overclock in [clock/]
|
||||
- Test getkey()
|
||||
|
||||
* Make keysc/keysc.c/state static and review forgotten global variables
|
||||
* Find out what happened to the clock frequency on fxcg50
|
||||
* Implement profiling
|
||||
* Save and restore interrupt masks
|
||||
* More keyboard
|
||||
|
||||
Strange, (irreproducible?) SysERRORs on SH3?
|
||||
- Happened when dealing with overclock and after a transfer
|
||||
- SysERROR when add-in fully returns, nothing during execution
|
||||
- Affects all add-ins of the calculator!
|
||||
|
||||
* Make sure interrupt for extra timers is masked when you ctx_restore to avoid
|
||||
interrupts
|
||||
|
||||
* For modules, use #define TMU ((tmu_t *)0xdeadbeef)) instead of tmu_t *TMU
|
||||
* Don't hardcode fx-cg50 stack address in fxcg50.ld for Prizm compatibility
|
||||
* Implement the gint_switch()
|
||||
* File management
|
||||
* Drawing functions, bopti and tales
|
||||
* Gray engine
|
||||
* Overclock
|
||||
* Add a driver init function to the r61524 driver.
|
||||
* Finish the bootlog and use the keyboard to control it.
|
||||
* Save driver contexts on the stack in gint_pause().
|
||||
* Write the core memory functions.
|
||||
- Allow arbitrary code in gint_pause() instead of just main menu?
|
||||
- Dynamically detect P1 static RAM in TLB for runtime Prizm compatibility.
|
||||
* Use a solid API for boot-time printing (or just use fxlib)
|
||||
- Use qdiv10() (there should be no __sdivsi3 in gint)
|
||||
(bootlog, timer_delay(), getkey_repeat(), int2bcd())
|
||||
* Load an exception handler and a TLB miss handler.
|
||||
|
||||
# Before compatibility
|
||||
|
||||
Bugs to fix:
|
||||
- Alignment of ALL .data / .rodata files is required to ensure converted data
|
||||
is properly aligned
|
||||
- Unpreditcable crap happens when more than 10 keys are pressed simultaneously
|
||||
- Ensure heap data is freed when a task-switch results in leaving the app
|
||||
is properly aligned -> Ok using ALIGN(4)
|
||||
- Ensure heap data is freed when a task-switch results in leaving the app (?)
|
||||
|
||||
Things to do before 1.0:
|
||||
- bopti: Test partial transparency
|
||||
- demo: Try 284x124 at (-60, -28) (all disadvantages)
|
||||
- perf: Try 284x124 at (-60, -28) (all disadvantages)
|
||||
- project: Check size of *all* library structures
|
||||
- project: Clean headers that have some internal definitions
|
||||
- project: Get rid of 7705.h (keyboard) and 7305.h (keyboard, gint)
|
||||
- time: Compute CLOCKS_PER_SEC
|
||||
|
||||
Things to do later:
|
||||
|
@ -17,12 +63,8 @@ Things to do later:
|
|||
- clock: Only measure if requires as option, otherwise trust {FTune}
|
||||
- clock: Handle overclock (relaunch clocks when overclocking)
|
||||
- clock: Split code into several files, change clock_config_t type
|
||||
- core: Change interrupt priority using the gint API
|
||||
- core: Register more interrupts (and understand their parameters)
|
||||
- core: Remove redundant code linked to environment saves
|
||||
- core: Review interrupt system (again) - this one is too slow
|
||||
- display: Try to make this module lighter (lots of code in text section)
|
||||
- errno: Introduce errno and use it more or less everywhere
|
||||
- esper: Cleaner playback, synthesizing
|
||||
- events: Allow customization of keyboard event system (option to return
|
||||
| events with modifiers, etc)
|
||||
|
@ -34,14 +76,10 @@ Things to do later:
|
|||
- stdio: More serious formatted printing functions and headers
|
||||
- string: Use cmp/str to implement memchr() (assembler examples)
|
||||
- string: Do some tests for memcmp() and memcpy()
|
||||
- string: Lighter functions?
|
||||
- usb: Implement a driver
|
||||
|
||||
Things to investigate:
|
||||
- Registers that may need to be saved within setjmp()
|
||||
- Optimizing core/gint.c leads to raising of an illegal slot exception when
|
||||
running the interrupt handler, although it ends on rte; lds.l @r15+, mach,
|
||||
which is totally not an illegal slot.
|
||||
|
||||
Possibly useful modules:
|
||||
- DMAC
|
||||
|
@ -49,4 +87,3 @@ Possibly useful modules:
|
|||
- TPU
|
||||
- USB
|
||||
- CMT on SH7305, WDT on SH7705
|
||||
- ACD (SH7705 only)
|
||||
|
|
262
configure
vendored
262
configure
vendored
|
@ -4,132 +4,170 @@
|
|||
# Basic configuration
|
||||
#
|
||||
|
||||
declare -A conf
|
||||
|
||||
# Target platform
|
||||
conf_target=
|
||||
target=
|
||||
toolchain=
|
||||
|
||||
# Build options
|
||||
prefix=
|
||||
cflags=
|
||||
|
||||
# Behavior
|
||||
conf[GINT_BOOT_LOG]=
|
||||
conf[GINT_NO_SYSCALLS]=
|
||||
cong[GINT_LAX]=
|
||||
conf[GINT_EXTENDED_LIBC]=
|
||||
conf[GINT_STATIC_GRAY]=
|
||||
boot_log=
|
||||
no_syscalls=
|
||||
static_gray=
|
||||
|
||||
# Size limits
|
||||
conf[ATEXIT_MAX]=16
|
||||
conf[TIMER_SLOTS]=16
|
||||
conf[EVENTS_QUEUE_SIZE]=64
|
||||
atexit_max=
|
||||
|
||||
# Output files
|
||||
output="config/Makefile.cfg"
|
||||
output="Makefile.cfg"
|
||||
|
||||
#
|
||||
# Help screen and output util
|
||||
# Help screen
|
||||
#
|
||||
|
||||
error="\e[31;1merror:\e[0m"
|
||||
C_="$(echo -e '\e[30;1m')"
|
||||
Cr="$(echo -e '\e[31;1m')"
|
||||
Cg="$(echo -e '\e[32;1m')"
|
||||
Cp="$(echo -e '\e[34;1m')"
|
||||
C0="$(echo -e '\e[0m')"
|
||||
|
||||
help()
|
||||
{
|
||||
cat << EOF
|
||||
cat << EOF
|
||||
Configuration script for the gint library.
|
||||
Usage: $0 [options...]
|
||||
|
||||
Platform settings (specify exactly one):
|
||||
${Cg}-fx9860g$C0
|
||||
Target platform is fx-9860G II: all monochrome models that support add-ins
|
||||
or can be flashed to support them.
|
||||
${Cg}-fxcg50$C0
|
||||
Target platform is fx-CG 50; there is some compatibility with fx-CG 10/20.
|
||||
You should build out-of-tree by creating a build directory and configuring from
|
||||
there.
|
||||
|
||||
Options that affect the behavior of the library $C_[${Cp}default$C_]$C0:
|
||||
$Cr-boot-log $C_[${Cp}disabled$C_]$C0
|
||||
Enable an on-screen log at startup if a key is kept pressed while launching
|
||||
the add-in, allowing easy debug and crash diagnoses.
|
||||
$Cr-no-syscalls $C_[${Cp}disabled$C_]$C0
|
||||
Never use syscalls. Expect trouble with malloc() and the gray engine. Do
|
||||
not trigger this switch unless you know what you are doing.
|
||||
$Cr-lax $C_[${Cp}disabled$C_]$C0
|
||||
Make more assumptions about functions parameters. This disables coordinate
|
||||
checks in drawing functions. Be careful!
|
||||
$Cr-extended-libc $C_[${Cp}disabled$C_]$C0
|
||||
Enable specific C99 headers/features that are normally not required by
|
||||
calculator programs. This may allow porting programs from other platforms.
|
||||
$Cr-static-gray-engine $C_[${Cp}disabled$C_]$C0
|
||||
Place the gray engine vram in static ram instead of using the heap. Always
|
||||
use this option when using both the gray engine and -no-syscalls.
|
||||
Target selection:
|
||||
--target=fx9860g|fxcg50
|
||||
|
||||
Options that customize size limits $C_[${Cp}default$C_]$C0:
|
||||
$Cr-atexit-max$C0=${Cg}integer$C_ [${Cp}16$C_]$C0
|
||||
Number of exit handlers that can be registered by atexit().
|
||||
$Cr-timer-slots$C0=${Cg}integer$C_ [${Cp}16$C_]$C0
|
||||
Number of virtual timers that may be registered at the same time.
|
||||
$Cr-events-queue-size$C0=${Cg}integer$C_ [${Cp}64$C_]$C0
|
||||
Number of events simultaneously stored in the event queue.
|
||||
fx9860g covers all fx-9860G II-like monochromes models that support add-ins
|
||||
or can be flashed with an OS that does. This includes SH3 and SH4 machines.
|
||||
Default toolchain is 'sh3eb-elf'.
|
||||
|
||||
fxcg50 covers just the fx-CG 50; there is some unofficial compatibility with
|
||||
fx-CG 10/20. All of these are SH4-only.
|
||||
Default toolchain is 'sh4eb-nofpu-elf'.
|
||||
|
||||
Build options:
|
||||
--toolchain=TRIPLET Build with a different toolchain
|
||||
--prefix=PREFIX Install prefix (PREFIX/lib and PREFIX/include are used)
|
||||
--cflags=FLAGS Additional compiler flags at end of command
|
||||
|
||||
Library options (disabled by default):
|
||||
--boot-log Display a boot debug log at startup if a key is pressed
|
||||
Used to investigate unstabilities.
|
||||
--no-syscalls Cut off all syscalls (this will break things)
|
||||
Only use this option if you have a good idea of which.
|
||||
--static-gray Allocate gray VRAMs in static RAM instead of the heap
|
||||
May help when --no-syscalls is on.
|
||||
|
||||
Size limits:
|
||||
--atexit-max=NUM Number of exit handlers in atexit()'s array [16]
|
||||
|
||||
Deprecated options (to be removed):
|
||||
-extended-libc Provide a few C99 headers (in the future, refer to the
|
||||
newlib port by Memallox's for the standard library)
|
||||
-timer-slots=<n> Number of virtual timer slots (this feature will be
|
||||
moved to an independent library) [16]
|
||||
-events-queue-size=<n>
|
||||
Size of event queue (this mechanism is likely to
|
||||
disappear in future versions) [64]
|
||||
EOF
|
||||
|
||||
exit 0
|
||||
exit 0
|
||||
}
|
||||
|
||||
if [[ "$@" == "--help" ]]; then
|
||||
help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f make/Makefile ]]; then
|
||||
echo "error: you should configure from a build directory, like this:" >&2
|
||||
echo " mkdir build && cd build && ../configure [options..]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
#
|
||||
# Parsing arguments
|
||||
#
|
||||
|
||||
fail=false
|
||||
for arg; do case "$arg" in
|
||||
-h | -? | --help) help;;
|
||||
-h | -? | --help)
|
||||
help;;
|
||||
|
||||
-fx9860g)
|
||||
conf_target="FX9860G";;
|
||||
-fxcg50)
|
||||
conf_target="FXCG50";;
|
||||
--target=*)
|
||||
case ${arg#*=} in
|
||||
"fx9860g")
|
||||
target=fx9860g
|
||||
toolchain=${toolchain:-sh3eb-elf};;
|
||||
"fxcg50")
|
||||
target=fxcg50
|
||||
toolchain=${toolchain:-sh4eb-nofpu-elf};;
|
||||
*)
|
||||
echo "error: invalid target '$target'"
|
||||
fail=true
|
||||
esac;;
|
||||
|
||||
-boot-log) conf[GINT_BOOT_LOG]=true;;
|
||||
-no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
|
||||
-lax) conf[GINT_LAX]=true;;
|
||||
-extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
|
||||
-static-gray-engine) conf[GINT_STATIC_GRAY]=true;;
|
||||
--prefix=*)
|
||||
prefix=${arg#*=};;
|
||||
--cflags=*)
|
||||
cflags=${arg#*=};;
|
||||
|
||||
-atexit-max=*)
|
||||
size=${arg#*=}
|
||||
if [[ $size == +([0-9]) ]]; then
|
||||
conf[ATEXIT_MAX]=$size
|
||||
else echo -e "$error -atexit-max expects an integer value"
|
||||
fail=true; fi;;
|
||||
-timer-slots=*)
|
||||
size=${arg#*=}
|
||||
if [[ $size == +([0-9]) ]]; then
|
||||
conf[TIMER_SLOTS]=$size
|
||||
else echo -e "$error -timer-slots expects an integer value"
|
||||
fail=true; fi;;
|
||||
-events-queue-size=*)
|
||||
size=${arg#*=}
|
||||
if [[ $size == +([0-9]) ]]; then
|
||||
conf[EVENTS_QUEUE_SIZE]=$size
|
||||
else echo -e "$error -events-queue-size expects an integer"\
|
||||
"value"
|
||||
fail=true; fi;;
|
||||
--boot-log)
|
||||
boot_log=true;;
|
||||
--no-syscalls)
|
||||
no_syscalls=true;;
|
||||
--static-gray)
|
||||
static_gray=true;;
|
||||
|
||||
-atexit-max | -timer-slots | -events-queue-size)
|
||||
echo -e "$error syntax for $arg is $arg=<integer-value>";;
|
||||
--atexit-max=*)
|
||||
n=${arg#*=}
|
||||
if [[ $n == +([0-9]) ]]; then
|
||||
atexit_max=$n
|
||||
else
|
||||
echo -e "error: -atexit-max expects an integer value"
|
||||
fail=true
|
||||
fi;;
|
||||
|
||||
*)
|
||||
echo -e "$error unrecognized argument '$arg'"; fail=true;;
|
||||
--atexit-max)
|
||||
echo "error: '$arg' expects a value (see '$0 --help')";
|
||||
fail=true;;
|
||||
|
||||
-extended-libc)
|
||||
echo "warning: support for '-extended-libc' has been removed";;
|
||||
-timer-slots=*)
|
||||
echo "warning: support for '-timer-slots' has been removed";;
|
||||
-events-queue-size=*)
|
||||
echo "warning: support for '-events-queue-size' has been removed";;
|
||||
esac; done
|
||||
|
||||
#
|
||||
# Checking mandatory arguments
|
||||
#
|
||||
|
||||
if [[ ! $conf_target ]]; then
|
||||
echo -e "$error No target specified. See $0 --help."
|
||||
fail=true;
|
||||
if [[ -z "$target" ]]; then
|
||||
echo "error: no target specified! (see '$0 --help')"
|
||||
fail=true;
|
||||
fi
|
||||
|
||||
# If no prefix is specified, install to the GCC's build folder
|
||||
if [[ -z "$prefix" ]]; then
|
||||
echo "No prefix specified, let's ask the compiler:"
|
||||
echo " $toolchain-gcc --print-search-dirs | grep install | sed 's/install: //'"
|
||||
inst=$($toolchain-gcc --print-search-dirs | grep install | sed 's/install: //')
|
||||
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "Call returned $?, giving up."
|
||||
fi
|
||||
|
||||
echo "Got '$inst'".
|
||||
|
||||
if [[ ! -d $inst ]]; then
|
||||
echo "Directory does not exist (or is not a directory), giving up."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
prefix=$inst
|
||||
fi
|
||||
|
||||
#
|
||||
|
@ -138,32 +176,34 @@ fi
|
|||
|
||||
output_config()
|
||||
{
|
||||
[ ${conf_target} == "FX9860G" ] \
|
||||
&& echo "cfg_target = fx" \
|
||||
|| echo "cfg_target = cg"
|
||||
mod=${target/98/fx}
|
||||
echo "CONFIG.TARGET = ${mod:2:2}"
|
||||
echo "CONFIG.TARGET.LONG = $target"
|
||||
|
||||
echo -n "cfg_macros ="
|
||||
echo -n " -D$conf_target"
|
||||
[[ $prefix ]] && echo "PREFIX = $prefix"
|
||||
[[ $toolchain ]] && echo "CONFIG.TOOLCHAIN = $toolchain"
|
||||
[[ $cflags ]] && echo "CONFIG.CFLAGS = $cflags"
|
||||
|
||||
[ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG"
|
||||
[ "${conf[GINT_NO_SYSCALLS]}" ] && echo -n " -DGINT_NO_SYSCALLS"
|
||||
[ "${conf[GINT_LAX]}" ] && echo -n " -DGINT_LAX"
|
||||
[ "${conf[GINT_EXTENDED_LIBC]}" ] && echo -n " -DGINT_EXTENDED_LIBC"
|
||||
[ "${conf[GINT_STATIC_GRAY]}" ] && echo -n " -DGINT_STATIC_GRAY"
|
||||
|
||||
echo -n " -DATEXIT_MAX=${conf[ATEXIT_MAX]}"
|
||||
echo -n " -DTIMER_SLOTS=${conf[TIMER_SLOTS]}"
|
||||
echo -n " -DEVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}"
|
||||
|
||||
echo ""
|
||||
|
||||
[ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "cfg_ext = true"
|
||||
echo -n "CONFIG.MACROS ="
|
||||
echo -n " -D$(echo $target | tr 'a-z' 'A-Z')"
|
||||
[[ "$boot_log" ]] && echo -n " -DGINT_BOOT_LOG"
|
||||
[[ "$no_syscalls" ]] && echo -n " -DGINT_NO_SYSCALLS"
|
||||
[[ "$static_gray" ]] && echo -n " -DGINT_STATIC_GRAY"
|
||||
[[ "$atexit_max" ]] && echo -n " -DATEXIT_MAX=$atexit_max"
|
||||
echo ""
|
||||
}
|
||||
|
||||
if $fail; then
|
||||
echo "Output file $output has not been modified."
|
||||
else
|
||||
mkdir -p config
|
||||
output_config > $output
|
||||
echo "Configuration saved in $output, ready to make!"
|
||||
echo "note: output file $output has not been changed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
output_config > $output
|
||||
|
||||
src="Makefile"
|
||||
dst="../make/Makefile"
|
||||
|
||||
[[ -L $src && $(readlink $src) == $dst ]] && rm $src
|
||||
ln -s $dst $src
|
||||
|
||||
echo "Configuration saved in $output, ready to make!"
|
||||
|
|
|
@ -130,7 +130,13 @@ SECTIONS
|
|||
. = ALIGN(16);
|
||||
} > ram AT> rom
|
||||
|
||||
_sdata = SIZEOF(.data);
|
||||
/* Read-write data sub-aligned to 4 bytes (mainly from fxconv) */
|
||||
.data.4 : SUBALIGN(4) {
|
||||
*(.data.4)
|
||||
. = ALIGN(16);
|
||||
} > ram AT> rom
|
||||
|
||||
_sdata = SIZEOF(.data) + SIZEOF(.data.4);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -118,7 +118,13 @@ SECTIONS
|
|||
. = ALIGN(16);
|
||||
} > ram AT> rom
|
||||
|
||||
_sdata = SIZEOF(.data);
|
||||
/* Read-write data sub-aligned to 4 bytes (mainly from fxconv) */
|
||||
.data.4 : SUBALIGN(4) {
|
||||
*(.data.4)
|
||||
. = ALIGN(16);
|
||||
} > ram AT> rom
|
||||
|
||||
_sdata = SIZEOF(.data) + SIZEOF(.data.4);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//---
|
||||
// gint:core:bootlog - Boot-time on-screen log for extreme debugging
|
||||
// core:bootlog - Boot-time on-screen log for extreme debugging
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_BOOTLOG
|
||||
|
@ -21,4 +21,11 @@ void bootlog_mapped(uint32_t rom, uint32_t ram);
|
|||
handlers set up. */
|
||||
void bootlog_kernel(void);
|
||||
|
||||
/* All these functions are enabled only if GINT_BOOT_LOG is defined */
|
||||
#ifndef GINT_BOOT_LOG
|
||||
#define bootlog_loaded(...)
|
||||
#define bootlog_mapped(...)
|
||||
#define bootlog_kernel(...)
|
||||
#endif
|
||||
|
||||
#endif /* GINT_CORE_BOOTLOG */
|
||||
|
|
|
@ -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.
|
||||
//---
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
//---
|
||||
// gint:core:mmu - MMU-related definitions
|
||||
// core:mmu - MMU-related definitions
|
||||
//
|
||||
// gint does not touch the MMU because the risk of interfering with the
|
||||
// system is deemed too high. However, to ensure that the add-in runs
|
||||
// properly, checks using read-only access to the MMU are performed.
|
||||
// gint does not touch the MMU because the risk of permanently wreaking
|
||||
// the system is deemed too high. However, to ensure that the add-in runs
|
||||
// properly, checks using read-only accesses to the MMU are performed.
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_MMU
|
||||
#define GINT_CORE_MMU
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 TLB
|
||||
//---
|
||||
|
||||
#ifdef FX9860G
|
||||
|
||||
/* tlb_addr_t - address part of a TLB entry */
|
||||
typedef struct
|
||||
{
|
||||
|
@ -26,7 +24,7 @@ typedef struct
|
|||
uint V :1;
|
||||
uint ASID :8;
|
||||
|
||||
} PACKED(4) tlb_addr_t;
|
||||
} GPACKED(4) tlb_addr_t;
|
||||
|
||||
/* tlb_data_t - data part of a TLB entry */
|
||||
typedef struct
|
||||
|
@ -43,7 +41,7 @@ typedef struct
|
|||
uint SH :1;
|
||||
uint :1;
|
||||
|
||||
} PACKED(4) tlb_data_t;
|
||||
} GPACKED(4) tlb_data_t;
|
||||
|
||||
/* tlb_addr() - get the P4 address of a TLB address entry
|
||||
@way TLB way (0..3)
|
||||
|
@ -57,7 +55,7 @@ const tlb_addr_t *tlb_addr(uint way, uint E);
|
|||
Returns a pointer to the entry. */
|
||||
const tlb_data_t *tlb_data(uint way, uint E);
|
||||
|
||||
/* tlb_mapped_memort() - count amount of mapped memory
|
||||
/* tlb_mapped_memory() - count amount of mapped memory
|
||||
This function returns the amount of mapped text and data segment memory, in
|
||||
bytes. The ranges are defined as follows:
|
||||
ROM 00300000:512k
|
||||
|
@ -68,8 +66,6 @@ const tlb_data_t *tlb_data(uint way, uint E);
|
|||
@ram Pointer to amount of mapped RAM */
|
||||
void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram);
|
||||
|
||||
#endif
|
||||
|
||||
//---
|
||||
// SH7305 Unified TLB
|
||||
//---
|
||||
|
@ -82,7 +78,7 @@ typedef struct
|
|||
uint V :1;
|
||||
uint ASID :8;
|
||||
|
||||
} PACKED(4) utlb_addr_t;
|
||||
} GPACKED(4) utlb_addr_t;
|
||||
|
||||
/* utlb_addr() - get the P4 address of a UTLB address entry
|
||||
@E Entry number (should be in range 0..63)
|
||||
|
@ -104,7 +100,7 @@ typedef struct
|
|||
uint SH :1;
|
||||
uint WT :1;
|
||||
|
||||
} PACKED(4) utlb_data_t;
|
||||
} GPACKED(4) utlb_data_t;
|
||||
|
||||
/* utlb_data() - get the P4 address of a UTLB data entry
|
||||
@E Entry number (should be in range 0..63)
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
//---
|
||||
// gint:core:setup - Installing and unloading the library
|
||||
// core:setup - Installing and unloading the library
|
||||
//---
|
||||
|
||||
#ifndef GINT_CORE_SETUP
|
||||
#define GINT_CORE_SETUP
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* Prototypes for the library management functions are in <gint/gint.h> */
|
||||
|
||||
/* gint_setvbr()
|
||||
Changes the VBR address and the calls the configuration function while
|
||||
interrupts are disabled. The configuration function must change either the
|
||||
priority registers or the interrupt masks, and make sure that all the
|
||||
interrupts that it leaves enabled are handled by the new VBR handlers.
|
||||
Changes the VBR address and calls the configuration function while
|
||||
interrupts are disabled. The configuration function must disable all
|
||||
interrupts that the new handlers cannot handle, either by clearing the
|
||||
priority registers or by setting the interrupt masks.
|
||||
|
||||
@vbr New VBR address
|
||||
@configure Configuration function
|
||||
|
|
|
@ -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
30
include/display/fx.h
Normal 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 */
|
|
@ -5,8 +5,6 @@
|
|||
#ifndef GINT_CLOCK
|
||||
#define GINT_CLOCK
|
||||
|
||||
#include <defs/types.h>
|
||||
|
||||
//---
|
||||
// Clock signals
|
||||
//---
|
||||
|
@ -59,7 +57,7 @@ const clock_frequency_t *clock_freq(void);
|
|||
The function stops the processor until an interrupt is accepted; the
|
||||
duration is not known in advance. This function should be used when the
|
||||
add-in is idle, for instance while waiting for keyboard input. */
|
||||
void sleep(void);
|
||||
#define sleep() __asm__("sleep")
|
||||
|
||||
/* sleep_us() - sleep for a definite duration in microseconds
|
||||
|
||||
|
|
|
@ -5,37 +5,26 @@
|
|||
#ifndef GINT_DEFS_ATTRIBUTES
|
||||
#define GINT_DEFS_ATTRIBUTES
|
||||
|
||||
/* Generic attribute element */
|
||||
#define ATTR(...) __attribute__((__VA_ARGS__))
|
||||
|
||||
/* Objects from specific sections */
|
||||
#define SECTION(x) __attribute__((section(x)))
|
||||
#define GSECTION(x) __attribute__((section(x)))
|
||||
/* Objects from the .gint.data and .gint.bss sections */
|
||||
#define GDATA __attribute__((section(".gint.data")))
|
||||
#define GBSS __attribute__((section(".gint.bss")))
|
||||
/* Additional sections that are only needed on SH3 */
|
||||
#define GDATA3 __attribute__((section(".gint.data.sh3")))
|
||||
#define GBSS3 __attribute__((section(".gint.bss.sh3")))
|
||||
/* Initialization functions */
|
||||
#define PRETEXT __attribute__((section(".pretext")))
|
||||
|
||||
/* Unused parameters or variables */
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define GUNUSED __attribute__((unused))
|
||||
/* Functions that *must* be inlined */
|
||||
#define INLINE __attribute__((always_inline)) inline
|
||||
/* Short static functions defined in header files */
|
||||
#define HDRFUNC __attribute__((always_inline)) static inline
|
||||
|
||||
/* Constructors and destructors */
|
||||
#define CTOR(x) __attribute__((constructor ((x) + 101))) PRETEXT
|
||||
#define DTOR(x) __attribute__((destructor ((x) + 101)))
|
||||
#define GINLINE __attribute__((always_inline)) inline
|
||||
|
||||
/* Aligned variables */
|
||||
#define ALIGNED(x) __attribute__((aligned(x)))
|
||||
#define GALIGNED(x) __attribute__((aligned(x)))
|
||||
|
||||
/* Packed structures. I require explicit alignment because if it's unspecified,
|
||||
GCC cannot optimize access size, and reads to memory-mapped I/O with invalid
|
||||
access sizes silently fail - honestly you don't want this to happen */
|
||||
#define PACKED(x) __attribute__((packed, aligned(x)))
|
||||
#define GPACKED(x) __attribute__((packed, aligned(x)))
|
||||
|
||||
#endif /* GINT_DEFS_ATTRIBUTES */
|
48
include/gint/defs/types.h
Normal file
48
include/gint/defs/types.h
Normal 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
67
include/gint/defs/util.h
Normal 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
21
include/gint/display-cg.h
Normal 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 */
|
|
@ -11,16 +11,14 @@
|
|||
|
||||
#ifdef FX9860G
|
||||
|
||||
/* Screen dimensions on fx9860g */
|
||||
#define DWIDTH 128
|
||||
#define DHEIGHT 64
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* color_t - colors available for drawing
|
||||
The following colors are defined by the library:
|
||||
|
||||
OPAQUE COLORS (override existing pixels)
|
||||
white, black - the usual thing
|
||||
light, dark - additional colors used by the gray engine
|
||||
light, dark - intermediate colors used with the gray engine
|
||||
|
||||
OPERATORS (combine with existing pixels)
|
||||
none - leaves unchanged
|
||||
|
@ -69,10 +67,6 @@ void dclear(color_t color);
|
|||
This functions applies a color or an operator to a rectangle defined by two
|
||||
points (x1 y1) and (x2 y2). Both are included in the rectangle.
|
||||
|
||||
If GINT_LAX is defined, this function makes the following assumptions:
|
||||
0 <= x1 < x2 <= 127
|
||||
0 <= y1 < y2 <= 63
|
||||
|
||||
@x1 @y1 @x2 @y2 Bounding rectangle (drawn area).
|
||||
@color Allowed colors: white, black, none, reverse */
|
||||
void drect(int x1, int y1, int x2, int y2, color_t color);
|
||||
|
@ -83,11 +77,10 @@ void drect(int x1, int y1, int x2, int y2, color_t color);
|
|||
|
||||
/* dpixel() - change a pixel's color
|
||||
If the requested color is an operator, the result will depend on the current
|
||||
color of the pixel.
|
||||
|
||||
If GINT_LAX is defined, this function makes the following assumptions:
|
||||
0 <= x <= 127
|
||||
0 <= y <= 63
|
||||
color of the pixel. Setting single pixels is the slowest method to produce a
|
||||
graphical result: all other functions for rendering lines, rectangles,
|
||||
images or text use highly-optimized methods, so check them out first if you
|
||||
care about performance.
|
||||
|
||||
@x @y Coordinates of the pixel to repaint
|
||||
@color Allowed colors: white, black, none, reverse */
|
||||
|
@ -98,16 +91,114 @@ void dpixel(int x, int y, color_t color);
|
|||
that the affected pixels may not be exactly the same when using dline() and
|
||||
Bdisp_DrawLineVRAM().
|
||||
|
||||
dline() has optimization facilities for horizontal and vertical lines.
|
||||
dline() has optimization facilities for horizontal and vertical lines, but
|
||||
it does not detect if your line doesn't fit in the screen. So drawing from
|
||||
(-1e6,0) to (1e6,63) will work, but will be veeery slow.
|
||||
|
||||
If GINT_LAX is defined, this function makes the following assumptions:
|
||||
0 <= x1 <= x2 <= 127
|
||||
0 <= y1, y2 <= 63
|
||||
|
||||
@x1 @y1 @x2 @y2 End points of theline (both included).
|
||||
@x1 @y1 @x2 @y2 End points of the line (both included).
|
||||
@color Allowed colors: white, black, none, reverse */
|
||||
void dline(int x1, int y1, int x2, int y2, color_t color);
|
||||
|
||||
//---
|
||||
// Image rendering (bopti)
|
||||
//---
|
||||
|
||||
/* image_t - image files encoded for bopti
|
||||
This format is the result of encoding images for bopti with the fxSDK's
|
||||
[fxconv] tool. The bopti routines can render it extremely fast, which makes
|
||||
it preferable over plain bitmaps if the images are never edited. */
|
||||
typedef struct
|
||||
{
|
||||
/* Image can only be rendered with the gray engine */
|
||||
uint gray :1;
|
||||
/* Left for future use */
|
||||
uint :3;
|
||||
/* Image profile (uniquely identifies a rendering function) */
|
||||
uint profile :4;
|
||||
/* Full width, in pixels */
|
||||
uint width :12;
|
||||
/* Full height, in pixels */
|
||||
uint height :12;
|
||||
|
||||
/* Raw layer data */
|
||||
uint8_t data[];
|
||||
|
||||
} GPACKED(4) image_t;
|
||||
|
||||
//---
|
||||
// Text rendering (topti)
|
||||
//---
|
||||
|
||||
/* font_t - font data encoded for topti */
|
||||
typedef struct
|
||||
{
|
||||
/* Length of font name (not NUL-terminated) */
|
||||
uint title :5;
|
||||
/* Font shape flags */
|
||||
uint bold :1;
|
||||
uint italic :1;
|
||||
uint serif :1;
|
||||
uint mono :1;
|
||||
/* Whether data is variable-length (proportional font) */
|
||||
uint prop :1;
|
||||
/* Reserved for future use */
|
||||
uint :2;
|
||||
/* Implemented charcter set */
|
||||
uint charset :4;
|
||||
/* Line height */
|
||||
uint8_t line_height;
|
||||
/* Storage height */
|
||||
uint8_t data_height;
|
||||
|
||||
/* The rest of the data depends on whether the font is proportional */
|
||||
union {
|
||||
/* For monospaced fonts */
|
||||
struct {
|
||||
/* Width of glyphs */
|
||||
uint16_t width;
|
||||
/* Storage size, in longwords, of each glyph */
|
||||
uint16_t storage_size;
|
||||
/* Raw glyph data */
|
||||
uint32_t data[];
|
||||
};
|
||||
/* For proportional fonts */
|
||||
struct {
|
||||
/* Storage index to find glyphs quickly */
|
||||
uint16_t index[16];
|
||||
/* Size array (padded to 4 bytes), 1 byte per entry,
|
||||
followed by glyph data */
|
||||
uint8_t sized_data[];
|
||||
};
|
||||
};
|
||||
|
||||
/* The font name is stored after the data. The size is the length set
|
||||
in the [title] field, padded to 4 bytes with NULs. There might not
|
||||
be a NUL at the end. */
|
||||
|
||||
} GPACKED(4) font_t;
|
||||
|
||||
/* dfont() - set the default font for text rendering
|
||||
This font will be used by dtext() and sister functions. If font=NULL, gint's
|
||||
default 5*6 font is used.
|
||||
|
||||
@font Font to use for subsequent text rendering calls */
|
||||
void dfont(font_t const * font);
|
||||
|
||||
/* dtext() - display a string of text
|
||||
|
||||
Draws some text in the video RAM using the font set with dfont() (or gint's
|
||||
default if no such font was set).
|
||||
|
||||
Due to the particular design of topti, this function takes advantage of the
|
||||
line structure of the VRAM to rendeer several characters at once. This is
|
||||
not a printf()-family function so [str] cannot contain formats like "%d"
|
||||
(they will be rendered directly) and cannot receive additional arguments.
|
||||
|
||||
@x @y Coordinates of top-left corner of the rendered string
|
||||
@str String to display
|
||||
@color Which color to use */
|
||||
void dtext(int x, int y, const char *str, color_t color);
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
||||
#endif /* GINT_DISPLAY_FX */
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
#ifndef GINT_DISPLAY
|
||||
#define GINT_DISPLAY
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* Expose the VRAM variable if GINT_NEED_VRAM is defined. This address is used
|
||||
as the VRAM basis by all of gint's drawing functions, and must point to a
|
||||
suitable buffer:
|
||||
|
@ -13,15 +15,15 @@
|
|||
|
||||
This variable is primarily meant to be exposed to gint functions, but
|
||||
add-ins may use it or change it freely:
|
||||
- To use another video ram area (triple buffering or more, gray engine) ;
|
||||
- To implement additional drawing functions ;
|
||||
- To use another video ram area (triple buffering or more, gray engine);
|
||||
- To implement additional drawing functions;
|
||||
- When not drawing, as additional RAM (especially on fxcg50). */
|
||||
#ifdef GINT_NEED_VRAM
|
||||
extern uint32_t *vram;
|
||||
#endif
|
||||
|
||||
/* As you would expect, display on fx9860g and display on fxcg50 are completely
|
||||
different worlds. As a consequence, so are the headers ^^ */
|
||||
different worlds. As a consequence, so are the rendering functions ^^ */
|
||||
|
||||
#ifdef FX9860G
|
||||
#include <gint/display-fx.h>
|
||||
|
@ -29,4 +31,28 @@ extern uint32_t *vram;
|
|||
#include <gint/display-cg.h>
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Parameter access
|
||||
//---
|
||||
|
||||
/* dinfo_t - summary of information provided by this module */
|
||||
typedef struct
|
||||
{
|
||||
/* Screen width, in pixels */
|
||||
int width;
|
||||
/* Screen height, in pixels */
|
||||
int height;
|
||||
/* Color depth (is 1 on fx9860g regardless of the gray engine) */
|
||||
int bpp;
|
||||
/* Current rendering font */
|
||||
font_t const * font;
|
||||
|
||||
} dinfo_t;
|
||||
|
||||
/* dinfo() - retrieve information from the display module
|
||||
This function returns the value of most parameters of the display module.
|
||||
|
||||
@info Pointer to allocated dinfo_t structure, will be filled. */
|
||||
void dinfo(dinfo_t *info);
|
||||
|
||||
#endif /* GINT_DISPLAY */
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
#ifndef GINT_DRIVERS
|
||||
#define GINT_DRIVERS
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// Driver initialization
|
||||
|
@ -39,7 +39,7 @@ typedef struct
|
|||
may be set to NULL */
|
||||
void (*init)(void);
|
||||
|
||||
/* unload() - unitialize the driver
|
||||
/* unload() - deinitialize the driver
|
||||
This function is called before ctx_restore() when gint is unloaded.
|
||||
If there is no unload function, the field may be set to NULL */
|
||||
void (*unload)(void);
|
||||
|
@ -65,7 +65,7 @@ typedef struct
|
|||
@ctx A context buffer filled by ctx_save() */
|
||||
void (*ctx_restore)(void *ctx);
|
||||
|
||||
} PACKED(4) gint_driver_t;
|
||||
} GPACKED(4) gint_driver_t;
|
||||
|
||||
/* GINT_DECLARE_DRIVER() - make a driver visible to gint
|
||||
|
||||
|
@ -75,10 +75,10 @@ typedef struct
|
|||
|
||||
The @level argument represents the priority level: lower numbers mean that
|
||||
drivers will be loaded sooner. This numbering allows a primitive form of
|
||||
dependency for drivers. You need to specifiy a level which is strictly
|
||||
dependency for drivers. You need to specify a level which is strictly
|
||||
higher than the level of all the drivers you depend on. */
|
||||
#define GINT_DECLARE_DRIVER(level, name) \
|
||||
SECTION(".gint.drivers." #level) extern gint_driver_t name;
|
||||
GSECTION(".gint.drivers." #level) extern gint_driver_t name;
|
||||
|
||||
/* GINT_DRIVER_SH3() - declare a function for SH3-rectification
|
||||
This macros allows the argument function to not exist on fxcg50. */
|
||||
|
|
17
include/gint/drivers/iokbd.h
Normal file
17
include/gint/drivers/iokbd.h
Normal 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 */
|
|
@ -7,7 +7,7 @@
|
|||
#ifndef GINT_DRIVERS_T6K11
|
||||
#define GINT_DRIVERS_T6K11
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* t6k11_display() - send vram data to the LCD device
|
||||
|
||||
|
@ -33,7 +33,7 @@ void t6k11_display(const void *vram, int y1, int y2, size_t stride);
|
|||
It would be possible to restore contrast, or update the system value on
|
||||
change, if the address was known for all OS versions.
|
||||
|
||||
OS 02.05.2201 8800b93c
|
||||
OS 02.05.2201 8800'b93c
|
||||
|
||||
@contrast Requested contrast value */
|
||||
void t6k11_contrast(int contrast);
|
|
@ -5,11 +5,10 @@
|
|||
#ifndef GINT_GINT
|
||||
#define GINT_GINT
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* gint_version() - get the library version number
|
||||
Returns gint's running version number, which is made of four fields:
|
||||
/* GINT_VERSION - the library version number
|
||||
Provides gint's running version number, which is made of four fields:
|
||||
|
||||
31 24 23 20 19 16 15 0
|
||||
+---------------+---------------+---------------+---------------+
|
||||
|
@ -22,11 +21,9 @@
|
|||
The build number uniquely identifies a binary version of the library.
|
||||
|
||||
For instance, 0x72100053 translates as "release 1.0 build 83". */
|
||||
HDRFUNC uint32_t gint_version(void)
|
||||
{
|
||||
extern char GINT_VERSION;
|
||||
return (uint32_t)&GINT_VERSION;
|
||||
}
|
||||
/* TODO: Fix version number */
|
||||
extern char GINT_VERSION;
|
||||
#define GINT_VERSION ((uint32_t)&GINT_VERSION)
|
||||
|
||||
//---
|
||||
// Library management
|
||||
|
@ -103,16 +100,17 @@ int gint_intlevel(int intid, int level);
|
|||
you're not interfering with usual interrupt assignments.
|
||||
|
||||
The first parameter 'event_code' represents the event_code associated with
|
||||
the interrupt. The codes are normally platform-dependent, but gint always
|
||||
uses SH7305 codes: SH3 platforms have a translation table. See the
|
||||
documentation for a list of event codes and their associated interrupts.
|
||||
the interrupt. If it's not a multiple of 0x20 then you're doing something
|
||||
wrong. The codes are normally platform-dependent, but gint always uses
|
||||
SH7305 codes: SH3 platforms have a translation table. See the documentation
|
||||
for a list of event codes and their associated interrupts.
|
||||
|
||||
The handler function must be an interrupt handler: it must not raise
|
||||
exceptions, must end with 'rte', and it will use the kernel register bank.
|
||||
For convenience I allow any block size to be loaded as an interrupt handler,
|
||||
but it should really be a multiple of 32 bytes and not override other
|
||||
but it should really be a multiple of 0x20 bytes and not override other
|
||||
handlers. If it's not written in assembler, then you're likely doing
|
||||
something wrong.
|
||||
something wrong, even with __attribute__((interrupt_handler)).
|
||||
|
||||
It is common for interrupt handlers to have a few bytes of data, such as the
|
||||
address of a callback function. gint often stores this data in the last
|
||||
|
|
|
@ -5,34 +5,55 @@
|
|||
#ifndef GINT_KEYBOARD
|
||||
#define GINT_KEYBOARD
|
||||
|
||||
#include <defs/types.h>
|
||||
/* Keyboard, key events and getkey()
|
||||
|
||||
gint's keyboard driver regularly scans the keyboard matrix and produces *key
|
||||
events*, the most primitive type of event exposed to the user. A key event
|
||||
basically says that a key has been pressed, held, or released at some point
|
||||
in time. They are suited for real-time applications like games.
|
||||
|
||||
- pollevent() fetches the next queued keyboard event, and returns an event
|
||||
of type KEYEV_NONE if none is available.
|
||||
- waitevent() fetches the next queued keyboard event and waits if none is
|
||||
available. The timeout can be configured.
|
||||
|
||||
GUI programs like the system applications will prefer using a GetKey()-like
|
||||
functions that return a single key press at a time, heeds for releases, for
|
||||
SHIFT and ALPHA modifiers, handles backlight and return-to-menu.
|
||||
|
||||
- getkey_opt() is gint's enhanced GetKey()-like function, with support for
|
||||
custom repetition and many fine-tunable options.
|
||||
- getkey() is a specific call to getkey_opt(), that imitates GetKey(). */
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/keycodes.h>
|
||||
|
||||
/* key_event_t - any keyboard event
|
||||
This structure represents an event that occurs on the keyboard. This is a
|
||||
low-level structure that is produced by the keyboard scanner. It reports key
|
||||
presses, key releases, and key repetitions.
|
||||
presses, key releases, and key repeats.
|
||||
|
||||
These events are detected and reported each time the keyboard is scanned,
|
||||
which is 16 Hz by default, so you'll get 16 repeat events by second if a key
|
||||
is kept pressed. We can filter the events to emit one only every second, for
|
||||
example, but it's difficult to do it for all keys at the same time. Thus the
|
||||
control of repetition delays is restricted to getkey().
|
||||
which is 128 Hz by default, so you'll get 128 repeat events by second if a
|
||||
key is kept pressed. We could filter the events to emit one only every
|
||||
second, for example, but it's difficult to do it for all keys at the same
|
||||
time. Thus the control of repeat delays is left to getkey().
|
||||
|
||||
When mod = 1, shift and alpha indicate whether the key has been modified.
|
||||
This is only possible for key press events returned by getkey(). Note that
|
||||
you can't have key = shift and mod = 1 at the same time.
|
||||
When [mod = 1], attributes [shift] and [alpha] indicate whether the key has
|
||||
been modified. Only key press events returned by getkey() have [mod = 1].
|
||||
Note that you can't have, e.g. [key=KEY_SHIFT] and [mod=1] at the same time.
|
||||
|
||||
The time attribute indicates when the event occurred. This value increases
|
||||
at each keyboard scan and *it wraps around every 4 minutes* (at 16 Hz).
|
||||
I expect this attribute to be useful to analyze combo sequences in games.
|
||||
Make sure you are aware of the two nitpicks:
|
||||
- Don't keep the time values for too long because the wrap-around effect.
|
||||
- 0xfff is just before 0x000, not long after. */
|
||||
The [time] attribute indicates when the event occurred. It is a snapshot of
|
||||
a time counter that increases at each keyboard scan and *wraps around every
|
||||
8 minutes* (at 128 Hz). I expect this attribute to be useful to analyze
|
||||
combo sequences in games. Make sure you are aware of the two nitpicks:
|
||||
* Don't keep the time values for too long because of the wrap-around effect.
|
||||
* 0xffff is "just before" 0x0000, not "long after". */
|
||||
typedef struct
|
||||
{
|
||||
uint time :12; /* Time of event, unique over short periods */
|
||||
uint time :16; /* Time of event, unique over short periods */
|
||||
|
||||
uint :7; /* Reserved for future use */
|
||||
uint :3; /* Reserved for future use */
|
||||
|
||||
uint mod :1; /* Whether modifiers are used */
|
||||
uint shift :1; /* If mod=1, whether SHIFT was pressed */
|
||||
|
@ -41,7 +62,7 @@ typedef struct
|
|||
uint type :2; /* Type of key event */
|
||||
uint key :8; /* Hit key */
|
||||
|
||||
} PACKED(4) key_event_t;
|
||||
} GPACKED(4) key_event_t;
|
||||
|
||||
/* Keyboard event types, as in the type field of key_event_t */
|
||||
enum
|
||||
|
@ -58,10 +79,98 @@ enum
|
|||
#define KEYBOARD_QUEUE_SIZE 64
|
||||
#endif
|
||||
|
||||
/* Keyboard frequency analysis, must be at least 64 for the keyboard to work,
|
||||
and at most 32768 for the extra timer to support it. Better if a power of 2.
|
||||
TODO: Add a configure or runtime setting for KEYBOARD_SCAN_FREQUENCY */
|
||||
#ifndef KEYBOARD_SCAN_FREQUENCY
|
||||
#define KEYBOARD_SCAN_FREQUENCY 128
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Keyboard functions
|
||||
//---
|
||||
|
||||
key_event_t key_poll(void);
|
||||
/* pollevent() - poll the next keyboard event
|
||||
This function returns the next yet-unpolled event from the keyboard buffer.
|
||||
If no event is available, it returns a dummy event with type = KEYEV_NONE.
|
||||
This event has always mod = 0, shift = 0, alpha = 0. */
|
||||
key_event_t pollevent(void);
|
||||
|
||||
/* waitevent() - wait for the next keyboard event
|
||||
This function works as pollevent() but waits if no event is available. When
|
||||
timeout = NULL, it waits indefinitely. Otherwise, it waits until *timeout
|
||||
becomes non-zero. It is particularly suitable to set *timeout to 1 using a
|
||||
timer with [timer_timeout] as callback. See <gint/timer.h>. */
|
||||
key_event_t waitevent(volatile int *timeout);
|
||||
|
||||
/* getkey() - wait for a pressed key
|
||||
|
||||
This function mimics the behavior of the fxlib GetKey(). It returns a
|
||||
key_event_t object where [mod=1], and where [shift] and [alpha] indicate
|
||||
whether SHIFT or ALPHA was pressed before the key was hit. [event] is
|
||||
KEYEV_DOWN when a new key is pressed and KEYEV_HOLD in case of repeats.
|
||||
|
||||
Similarities with GetKey() include:
|
||||
- Wait for a key to be pressed *after* the call (held keys don't count)
|
||||
- Supports SHIFT and ALPHA modifiers
|
||||
- Repeats arrows keys
|
||||
- Allows return to main menu if the MENU key is pressed
|
||||
- Controls backlight on models that have a back-lit screen
|
||||
|
||||
getkey() is equivalent to getkey_opt(GETKEY_DEFAULT, NULL). */
|
||||
key_event_t getkey(void);
|
||||
|
||||
/* The following are the option bits for getkey_opt(). */
|
||||
enum {
|
||||
/* Enable modifiers keys */
|
||||
GETKEY_MOD_SHIFT = 0x01,
|
||||
GETKEY_MOD_ALPHA = 0x02,
|
||||
/* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
|
||||
GETKEY_BACKLIGHT = 0x04,
|
||||
/* MENU triggers a task switch and displays the main menu */
|
||||
GETKEY_MENU = 0x08,
|
||||
/* Repeat arrow keys, or even all keys */
|
||||
GETKEY_REP_ARROWS = 0x10,
|
||||
GETKEY_REP_ALL = 0x20,
|
||||
|
||||
/* Default settings of getkey() */
|
||||
GETKEY_DEFAULT = 0x1f,
|
||||
};
|
||||
|
||||
/* getkey_opt() - enhanced getkey()
|
||||
|
||||
This function enhances getkey() with more general features. An
|
||||
or-combination of option flags (see above) must be supplied as first
|
||||
argument; 0 stands for no option. getkey_opt() returns the same kind of
|
||||
values as getkey().
|
||||
|
||||
getkey_opt() supports a generic timeout function in the form of a volatile
|
||||
pointer [timeout]. If it's NULL, getkey_opt() waits indefinitely. Otherwise,
|
||||
it waits until *timeout becomes non-zero. It's up to you to change the
|
||||
value whenever you want to interrupt the call; using a timer with
|
||||
[timer_timeout] as callback is suitable.
|
||||
|
||||
@options An or-combination of values from the GETKEY_* enumeration
|
||||
@timeout Optional pointer to a timeout value
|
||||
Returns a key event of type KEYEV_DOWN or KEYEV_HOLD with [mod=1]. */
|
||||
key_event_t getkey_opt(int options, volatile int *timeout);
|
||||
|
||||
/* getkey_repeat() - set repeat delays for getkey()
|
||||
|
||||
This function updates the repeat delays of getkey() and getkey_opt(). The
|
||||
unit of the argument is in milliseconds, but the granularity of the delay is
|
||||
dependent on the keyboard scan frequency.
|
||||
|
||||
In the default setting (128 Hz scans), the possible repeat delays are
|
||||
approximately 8 ms, 16 ms, 23 ms, 31 ms... the provided arguments will be
|
||||
rounded to the closest feasible delays to ensure that repetitions are
|
||||
perfectly regular, rather than approximating the requested frequency.
|
||||
|
||||
The system default is (625 ms, 25 ms). With the 128 Hz setting, this default
|
||||
will be approximated at (625 ms, 23.4375 ms).
|
||||
|
||||
@first Delay between key press and first repeat (no more than one hour)
|
||||
@next Delay between subsequent repeats (no more than one hour) */
|
||||
void getkey_repeat(int first, int next);
|
||||
|
||||
#endif /* GINT_KEYBOARD */
|
||||
|
|
86
include/gint/keycodes.h
Normal file
86
include/gint/keycodes.h
Normal 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 */
|
|
@ -12,8 +12,6 @@
|
|||
#ifndef GINT_CORE_MPU
|
||||
#define GINT_CORE_MPU
|
||||
|
||||
#include <defs/attributes.h>
|
||||
|
||||
/* mpu_t - supported MPUs */
|
||||
typedef enum
|
||||
{
|
||||
|
@ -27,14 +25,11 @@ typedef enum
|
|||
#ifdef FX9860G
|
||||
|
||||
/* mpu_id() - get the name of the underlying MPU */
|
||||
HDRFUNC mpu_t mpu_id(void)
|
||||
{
|
||||
extern const mpu_t mpu;
|
||||
return mpu;
|
||||
}
|
||||
extern const mpu_t gint_mpu_id;
|
||||
#define gint_mpu() (gint_mpu_id)
|
||||
|
||||
/* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */
|
||||
#define isSH3() (mpu_id() & 1)
|
||||
#define isSH3() (gint_mpu() & 1)
|
||||
#define isSH4() (!isSH3())
|
||||
|
||||
/* mpu_init() - probe the MPU type
|
||||
|
@ -44,7 +39,7 @@ typedef enum
|
|||
#else /* FXCG50 */
|
||||
|
||||
/* All fxcg50 machines have an SH7305, which makes things simpler. */
|
||||
#define mpu_id() mpu_sh7305
|
||||
#define gint_mpu() mpu_sh7305
|
||||
#define isSH3() 0
|
||||
#define isSH4() 1
|
||||
|
|
@ -5,7 +5,8 @@
|
|||
#ifndef GINT_CORE_CPG
|
||||
#define GINT_CORE_CPG
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 Clock Pulse Generator. Refer to:
|
||||
|
@ -27,7 +28,7 @@ typedef volatile struct
|
|||
uint16_t PFC :2; /* Peripheral clock divider */
|
||||
);
|
||||
|
||||
} PACKED(4) sh7705_cpg_t;
|
||||
} GPACKED(4) sh7705_cpg_t;
|
||||
|
||||
#define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80))
|
||||
|
||||
|
@ -73,7 +74,7 @@ typedef volatile struct
|
|||
uint32_t FLF :11; /* FLL Multiplication Ratio */
|
||||
);
|
||||
|
||||
} PACKED(4) sh7305_cpg_t;
|
||||
} GPACKED(4) sh7305_cpg_t;
|
||||
|
||||
#define SH7305_CPG (*((sh7305_cpg_t *)0xa4150000))
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
#ifndef GINT_CORE_INTC
|
||||
#define GINT_CORE_INTC
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 Interrupt Controller. Refer to:
|
||||
|
@ -83,7 +83,7 @@ typedef struct
|
|||
uint16_t :4;
|
||||
);
|
||||
|
||||
} PACKED(4) sh7705_intc_ipc_t;
|
||||
} GPACKED(4) sh7705_intc_ipc_t;
|
||||
|
||||
/* sh7705_intc_icr1_t - Interrupt Control Register 1 (general) */
|
||||
typedef volatile word_union(sh7705_intc_icr1_t,
|
||||
|
@ -106,12 +106,12 @@ typedef struct
|
|||
union {
|
||||
sh7705_intc_ipc_t _;
|
||||
volatile uint16_t *IPRS[8];
|
||||
} PACKED(4);
|
||||
} GPACKED(4);
|
||||
|
||||
/* Control registers */
|
||||
sh7705_intc_icr1_t *ICR1;
|
||||
|
||||
} PACKED(4) sh7705_intc_t;
|
||||
} GPACKED(4) sh7705_intc_t;
|
||||
|
||||
|
||||
|
||||
|
@ -223,7 +223,7 @@ typedef volatile struct
|
|||
);
|
||||
pad(2);
|
||||
|
||||
} PACKED(4) sh7305_intc_ipc_t;
|
||||
} GPACKED(4) sh7305_intc_ipc_t;
|
||||
|
||||
/* sh7305_intc_masks_t - Interrupt mask management
|
||||
Writing 1 to IMR masks interrupts; writing 1 to IMCRs clears the masks.
|
||||
|
@ -244,7 +244,7 @@ typedef volatile struct
|
|||
uint8_t IMR11; pad(3);
|
||||
uint8_t IMR12;
|
||||
|
||||
} PACKED(4) sh7305_intc_masks_t;
|
||||
} GPACKED(4) sh7305_intc_masks_t;
|
||||
|
||||
|
||||
/* sh7305_intc_userimask_t - User Interrupt Mask
|
||||
|
@ -286,14 +286,14 @@ typedef struct
|
|||
/* Other registers */
|
||||
sh7305_intc_userimask_t *USERIMASK;
|
||||
|
||||
} PACKED(4) sh7305_intc_t;
|
||||
} GPACKED(4) sh7305_intc_t;
|
||||
|
||||
//---
|
||||
// Forward definitions
|
||||
//---
|
||||
|
||||
/* Provided by core/gint.c */
|
||||
extern sh7705_intc_t INTC3;
|
||||
extern sh7305_intc_t INTC4;
|
||||
extern sh7705_intc_t SH7705_INTC;
|
||||
extern sh7305_intc_t SH7305_INTC;
|
||||
|
||||
#endif /* GINT_CORE_INTC */
|
|
@ -8,7 +8,8 @@
|
|||
#ifndef GINT_CORE_PFC
|
||||
#define GINT_CORE_PFC
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 Pin Function Controller. Refer to:
|
||||
|
@ -66,7 +67,7 @@ typedef volatile struct
|
|||
uint8_t PNDR;
|
||||
pad(1);
|
||||
|
||||
} PACKED(4) sh7705_pfc_t;
|
||||
} GPACKED(4) sh7705_pfc_t;
|
||||
|
||||
#define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100))
|
||||
|
|
@ -5,6 +5,8 @@
|
|||
#ifndef GINT_CORE_RTC
|
||||
#define GINT_CORE_RTC
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
|
||||
//---
|
||||
// Hybrid SH7705-SH7305 Real-Time Clock. Refer to:
|
||||
// "Renesas SH7705 Group Hardware Manual"
|
||||
|
@ -22,7 +24,7 @@ typedef struct
|
|||
);
|
||||
pad(1);
|
||||
|
||||
} PACKED(2) rtc_BCD2_t;
|
||||
} GPACKED(2) rtc_BCD2_t;
|
||||
|
||||
/* sh7705_rtc_t, sh7305_rtc_t - Date and time access, RTC control */
|
||||
typedef volatile struct
|
||||
|
@ -68,7 +70,7 @@ typedef volatile struct
|
|||
);
|
||||
pad(1);
|
||||
|
||||
} PACKED(4) rtc_t;
|
||||
} GPACKED(4) rtc_t;
|
||||
|
||||
#define SH7705_RTC (*((rtc_t *)0xfffffec0))
|
||||
#define SH7305_RTC (*((rtc_t *)0xa413fec0))
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef GINT_RTC
|
||||
#define GINT_RTC
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// Time management
|
||||
|
@ -45,14 +45,14 @@ void rtc_set_time(const rtc_time_t *time);
|
|||
/* rtc_frequency_t - possible frequency settings for the RTC's interrupt */
|
||||
typedef enum
|
||||
{
|
||||
rtc_500mHz = 7,
|
||||
rtc_1Hz = 6,
|
||||
rtc_2Hz = 5,
|
||||
rtc_4Hz = 4,
|
||||
rtc_16Hz = 3,
|
||||
rtc_64Hz = 2,
|
||||
rtc_256Hz = 1,
|
||||
rtc_none = 0,
|
||||
RTC_500mHz = 7,
|
||||
RTC_1Hz = 6,
|
||||
RTC_2Hz = 5,
|
||||
RTC_4Hz = 4,
|
||||
RTC_16Hz = 3,
|
||||
RTC_64Hz = 2,
|
||||
RTC_256Hz = 1,
|
||||
RTC_NONE = 0,
|
||||
|
||||
} rtc_frequency_t;
|
||||
|
||||
|
@ -70,8 +70,8 @@ typedef enum
|
|||
up a 1 Hz-callback when the current time ends in 950 ms will result in the
|
||||
callback being called after 50 ms, then every second. This is not a problem
|
||||
for most uses. */
|
||||
void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void *arg),
|
||||
void *arg);
|
||||
void rtc_start_timer(rtc_frequency_t freq,
|
||||
int (*callback)(volatile void *arg), volatile void *arg);
|
||||
|
||||
/* rtc_stop_timer() - stop the RTC timer
|
||||
This function stops the RTC timer that was set up with rtc_start_timer(). If
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
#ifndef GINT_TIMER
|
||||
#define GINT_TIMER
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <core/mpu.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/mpu.h>
|
||||
|
||||
/* Timer identifiers
|
||||
|
||||
|
@ -88,7 +87,7 @@ typedef enum
|
|||
@callback Callback function (called when the timer fires)
|
||||
@arg Passed as argument to the callback function */
|
||||
int timer_setup(int timer, uint32_t delay, timer_input_t clock,
|
||||
int (*callback)(void *arg), void *arg);
|
||||
int (*callback)(volatile void *arg), volatile void *arg);
|
||||
|
||||
/* timer_delay() - compute a delay constant from a duration in seconds
|
||||
|
||||
|
@ -108,7 +107,7 @@ int timer_setup(int timer, uint32_t delay, timer_input_t clock,
|
|||
|
||||
@timer The timer you are planning to use
|
||||
@delay_us Requested delay in microseconds */
|
||||
uint32_t timer_delay(int timer, int delay_us);
|
||||
uint32_t timer_delay(int timer, uint64_t delay_us);
|
||||
|
||||
/* timer_start() - start a configured timer
|
||||
The specified timer will start counting down and fire callbacks at regular
|
||||
|
@ -142,4 +141,14 @@ void timer_pause(int timer);
|
|||
@timer Timer id, as returned by timer_setup() */
|
||||
void timer_stop(int timer);
|
||||
|
||||
//---
|
||||
// Predefined timer callbacks
|
||||
//---
|
||||
|
||||
/* timer_timeout() - callback that sets a flag and halts the timer
|
||||
This predefined callback may be used when a timeout is required. It sets its
|
||||
argument pointer to 1 and halts the timer. The pointer must be of type
|
||||
int * and you must declare the variable as volatile int. */
|
||||
int timer_timeout(volatile void *arg);
|
||||
|
||||
#endif /* GINT_TIMER */
|
||||
|
|
176
make/Makefile
Executable file
176
make/Makefile
Executable 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)
|
|
@ -1,13 +1,12 @@
|
|||
//---
|
||||
// gint:core:freq - Clock frequency management
|
||||
// gint:clock:freq - Clock frequency management
|
||||
//---
|
||||
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/clock.h>
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <core/mpu.h>
|
||||
#include <core/cpg.h>
|
||||
#include <gint/mpu.h>
|
||||
#include <gint/mpu/cpg.h>
|
||||
|
||||
//---
|
||||
// Driver storage
|
||||
|
@ -69,6 +68,11 @@ static void sh7705_probe(void)
|
|||
}
|
||||
|
||||
#undef CPG
|
||||
#else
|
||||
|
||||
/* This prototype will silence warnings on fxcg50 */
|
||||
void sh7705_probe(void);
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
||||
//---
|
||||
|
@ -80,17 +84,17 @@ static void sh7705_probe(void)
|
|||
static void sh7305_probe(void)
|
||||
{
|
||||
/* The meaning of the PLL setting on SH7305 differs from the
|
||||
documentation of SH7224; the setting must not be doubled. */
|
||||
documentation of SH7224; the value must not be doubled. */
|
||||
int pll = CPG.FRQCRA.STC + 1;
|
||||
freq.PLL = pll;
|
||||
|
||||
/* The FLL ratio is the value of the setting, possibly halved */
|
||||
/* The FLL ratio is the value of the setting, halved if SELXM=1 */
|
||||
int fll = CPG.FLLFRQ.FLF;
|
||||
if(CPG.FLLFRQ.SELXM == 1) fll >>= 1;
|
||||
freq.FLL = fll;
|
||||
|
||||
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but here
|
||||
it's actually 1 / (2^setting + 1). */
|
||||
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but
|
||||
SH7305 behaves as 1 / (2^setting + 1). */
|
||||
|
||||
int divb = CPG.FRQCRA.BFC;
|
||||
int divi = CPG.FRQCRA.IFC;
|
||||
|
@ -125,7 +129,7 @@ static void init(void)
|
|||
}
|
||||
|
||||
gint_driver_t drv_cpg = {
|
||||
.name = "Clock Pulse Generator",
|
||||
.name = "CPG",
|
||||
.init = init,
|
||||
.ctx_size = 0,
|
||||
.sys_ctx = NULL,
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -2,9 +2,11 @@
|
|||
// gint:core:bootlog - Boot-time on-screen log for extreme debugging
|
||||
//---
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <core/intc.h>
|
||||
#include <core/mpu.h>
|
||||
/* TODO: Review, enhance and fix bootlog */
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/mpu.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
#include <core/mmu.h>
|
||||
#include <gint/gint.h>
|
||||
|
||||
|
@ -18,7 +20,7 @@ extern char
|
|||
void bootlog_loaded(void)
|
||||
{
|
||||
/* Version string - the string constant resides in ROM */
|
||||
uint32_t v = gint_version();
|
||||
uint32_t v = GINT_VERSION;
|
||||
const char *model = "gint #0.0-000";
|
||||
char str[14];
|
||||
|
||||
|
@ -43,7 +45,7 @@ void bootlog_loaded(void)
|
|||
uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss;
|
||||
|
||||
/* MPU type */
|
||||
mpu_t mpu = mpu_id();
|
||||
mpu_t mpu = gint_mpu();
|
||||
const char *names = "SH7337\0 SH7305\0 SH7355\0 SH7724";
|
||||
|
||||
/* TODO: Use a solid API for boot-time printing */
|
||||
|
@ -87,28 +89,28 @@ void bootlog_kernel(void)
|
|||
if(isSH3())
|
||||
{
|
||||
print(1, 5, "ABCD");
|
||||
print_hex( 6, 5, INTC3._.IPRA->word, 4);
|
||||
print_hex(10, 5, INTC3._.IPRB->word, 4);
|
||||
print_hex(14, 5, INTC3._.IPRC->word, 4);
|
||||
print_hex(18, 5, INTC3._.IPRD->word, 4);
|
||||
print_hex( 6, 5, SH7705_INTC._.IPRA->word, 4);
|
||||
print_hex(10, 5, SH7705_INTC._.IPRB->word, 4);
|
||||
print_hex(14, 5, SH7705_INTC._.IPRC->word, 4);
|
||||
print_hex(18, 5, SH7705_INTC._.IPRD->word, 4);
|
||||
print(1, 6, "EFGH");
|
||||
print_hex( 6, 6, INTC3._.IPRE->word, 4);
|
||||
print_hex(10, 6, INTC3._.IPRF->word, 4);
|
||||
print_hex(14, 6, INTC3._.IPRG->word, 4);
|
||||
print_hex(18, 6, INTC3._.IPRH->word, 4);
|
||||
print_hex( 6, 6, SH7705_INTC._.IPRE->word, 4);
|
||||
print_hex(10, 6, SH7705_INTC._.IPRF->word, 4);
|
||||
print_hex(14, 6, SH7705_INTC._.IPRG->word, 4);
|
||||
print_hex(18, 6, SH7705_INTC._.IPRH->word, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
print(1, 5, "ACFG");
|
||||
print_hex( 6, 5, INTC4._->IPRA.word, 4);
|
||||
print_hex(10, 5, INTC4._->IPRC.word, 4);
|
||||
print_hex(14, 5, INTC4._->IPRF.word, 4);
|
||||
print_hex(18, 5, INTC4._->IPRG.word, 4);
|
||||
print_hex( 6, 5, SH7305_INTC._->IPRA.word, 4);
|
||||
print_hex(10, 5, SH7305_INTC._->IPRC.word, 4);
|
||||
print_hex(14, 5, SH7305_INTC._->IPRF.word, 4);
|
||||
print_hex(18, 5, SH7305_INTC._->IPRG.word, 4);
|
||||
print(1, 6, "HJKL");
|
||||
print_hex( 6, 6, INTC4._->IPRH.word, 4);
|
||||
print_hex(10, 6, INTC4._->IPRJ.word, 4);
|
||||
print_hex(14, 6, INTC4._->IPRK.word, 4);
|
||||
print_hex(18, 6, INTC4._->IPRL.word, 4);
|
||||
print_hex( 6, 6, SH7305_INTC._->IPRH.word, 4);
|
||||
print_hex(10, 6, SH7305_INTC._->IPRJ.word, 4);
|
||||
print_hex(14, 6, SH7305_INTC._->IPRK.word, 4);
|
||||
print_hex(18, 6, SH7305_INTC._->IPRL.word, 4);
|
||||
}
|
||||
|
||||
Bdisp_PutDisp_DD();
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
//---
|
||||
|
||||
#include <gint/gint.h>
|
||||
#include <core/mpu.h>
|
||||
#include <core/intc.h>
|
||||
#include <core/memory.h>
|
||||
#include <gint/mpu.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
|
||||
/* Interrupt controllers */
|
||||
|
||||
GDATA3 sh7705_intc_t INTC3 = {
|
||||
GDATA3 sh7705_intc_t SH7705_INTC = {
|
||||
.IPRS = {
|
||||
(void *)0xfffffee2, (void *)0xfffffee4,
|
||||
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
|
||||
|
@ -18,7 +18,7 @@ GDATA3 sh7705_intc_t INTC3 = {
|
|||
.ICR1 = (void *)0xa4000010,
|
||||
};
|
||||
|
||||
GDATA sh7305_intc_t INTC4 = {
|
||||
GDATA sh7305_intc_t SH7305_INTC = {
|
||||
.IPRS = (void *)0xa4080000,
|
||||
.MSK = (void *)0xa4080080,
|
||||
.MSKCLR = (void *)0xa40800c0,
|
||||
|
@ -37,8 +37,8 @@ int gint_intlevel(int intid, int level)
|
|||
level &= 0xf;
|
||||
|
||||
ipr = isSH3()
|
||||
? INTC3.IPRS[intid >> 2] /* SH3-based */
|
||||
: &INTC4.IPRS[2 * (intid >> 2)]; /* SH4-based */
|
||||
? SH7705_INTC.IPRS[intid >> 2] /* SH3-based */
|
||||
: &SH7305_INTC.IPRS[2 * (intid >> 2)]; /* SH4-based */
|
||||
|
||||
int oldlevel = (*ipr >> shift) & 0xf;
|
||||
*ipr = (*ipr & ~(0xf << shift)) | (level << shift);
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
|
||||
- It is possible to map MPU-specific blocks at runtime, to avoid checking
|
||||
the MPU each time an interrupt is handled;
|
||||
- However, the blocks cannot rely on relative displacements or cross-
|
||||
references unless their relative order is fully known. This happens with
|
||||
the timer driver, and it works swimmingly; just be careful. */
|
||||
- However, the blocks can only rely on relative displacements or cross-
|
||||
references if their relative order is known and heeded for. A good example
|
||||
of this is the timer driver. Please be careful. */
|
||||
|
||||
/* SH7305-TYPE DEBUG INTERRUPT HANDLER - 26 BYTES */
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <core/mpu.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/mpu.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -85,7 +86,7 @@ void *memcpy(void * restrict dst, const void * restrict src, size_t n)
|
|||
return dst;
|
||||
}
|
||||
|
||||
void *_memmove(UNUSED void *dst, UNUSED const void *src, UNUSED size_t n)
|
||||
void *_memmove(GUNUSED void *dst, GUNUSED const void *src, GUNUSED size_t n)
|
||||
{
|
||||
// (same as memcpy, but heed for direction if areas overlap)
|
||||
|
||||
|
@ -94,7 +95,7 @@ void *_memmove(UNUSED void *dst, UNUSED const void *src, UNUSED size_t n)
|
|||
return dst;
|
||||
}
|
||||
|
||||
int _memcmp(UNUSED const void *s1, UNUSED const void *s2, UNUSED size_t n)
|
||||
int _memcmp(GUNUSED const void *s1, GUNUSED const void *s2, GUNUSED size_t n)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
#ifdef FX9860G
|
||||
|
||||
/* tlb_addr() - get the P4 address of a TLB address entry */
|
||||
INLINE const tlb_addr_t *tlb_addr(uint way, uint E)
|
||||
GINLINE const tlb_addr_t *tlb_addr(uint way, uint E)
|
||||
{
|
||||
uint32_t addr = 0xf2000000 | (E << 12) | (way << 8);
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
/* tlb_data() - get the P4 address of a TLB data entry */
|
||||
INLINE const tlb_data_t *tlb_data(uint way, uint E)
|
||||
GINLINE const tlb_data_t *tlb_data(uint way, uint E)
|
||||
{
|
||||
uint32_t addr = 0xf3000000 | (E << 12) | (way << 8);
|
||||
return (void *)addr;
|
||||
|
@ -57,14 +57,14 @@ void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
|
|||
//---
|
||||
|
||||
/* utlb_addr() - get the P4 address of a UTLB address entry */
|
||||
INLINE const utlb_addr_t *utlb_addr(uint E)
|
||||
GINLINE const utlb_addr_t *utlb_addr(uint E)
|
||||
{
|
||||
uint32_t addr = 0xf6000000 | ((E & 0x3f) << 8);
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
/* utlb_data() - get the P4 address of a UTLB data entry */
|
||||
INLINE const utlb_data_t *utlb_data(uint E)
|
||||
GINLINE const utlb_data_t *utlb_data(uint E)
|
||||
{
|
||||
uint32_t addr = 0xf7000000 | ((E & 0x3f) << 8);
|
||||
return (void *)addr;
|
||||
|
|
|
@ -2,15 +2,16 @@
|
|||
// gint:core:mpu - Runtime MPU detection
|
||||
//---
|
||||
|
||||
#include <core/mpu.h>
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <gint/mpu.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/util.h>
|
||||
|
||||
/* This file is only useful on fx9860g machines because all fxcg50 are SH4 */
|
||||
#ifdef FX9860G
|
||||
|
||||
/* Holds the name of the current MPU; initialized at startup by mpu_init() */
|
||||
GBSS const mpu_t mpu;
|
||||
GBSS const mpu_t gint_mpu_id;
|
||||
|
||||
/* mpu_detect() - detect the underlying MPU
|
||||
Many thanks to Simon Lothar for relevant documentation.
|
||||
|
@ -54,7 +55,7 @@ static mpu_t mpu_detect(void)
|
|||
/* mpu_init() - detect and save information about the underlying MPU */
|
||||
void mpu_init(void)
|
||||
{
|
||||
const_cast(mpu, mpu_t) = mpu_detect();
|
||||
const_cast(gint_mpu_id, mpu_t) = mpu_detect();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
#include <gint/drivers.h>
|
||||
#include <core/memory.h>
|
||||
#include <core/setup.h>
|
||||
#include <core/intc.h>
|
||||
#include <core/mpu.h>
|
||||
#include <gint/mpu.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
|
||||
/* VBR address, from the linker script */
|
||||
extern char gint_vbr;
|
||||
|
@ -24,34 +24,26 @@ typedef struct
|
|||
{
|
||||
uint16_t iprs[12];
|
||||
|
||||
} PACKED(2) gint_core_ctx;
|
||||
} GPACKED(2) gint_core_ctx;
|
||||
|
||||
/* gint_ctx_save() - save interrupt controller configuration
|
||||
@arg ctx gint core context object */
|
||||
static void gint_ctx_save(gint_core_ctx *ctx)
|
||||
{
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.IPRS[2 * i];
|
||||
}
|
||||
if(isSH3()) for(int i = 0; i < 8; i++)
|
||||
ctx->iprs[i] = *(SH7705_INTC.IPRS[i]);
|
||||
else for(int i = 0; i < 12; i++)
|
||||
ctx->iprs[i] = SH7305_INTC.IPRS[2 * i];
|
||||
}
|
||||
|
||||
/* gint_ctx_restore() - restore interrupt controller configuration
|
||||
@arg ctx gint core context object */
|
||||
static void gint_ctx_restore(gint_core_ctx *ctx)
|
||||
{
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i];
|
||||
}
|
||||
if(isSH3()) for(int i = 0; i < 8; i++)
|
||||
*(SH7705_INTC.IPRS[i]) = ctx->iprs[i];
|
||||
else for(int i = 0; i < 12; i++)
|
||||
SH7305_INTC.IPRS[2 * i] = ctx->iprs[i];
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -65,14 +57,10 @@ GBSS static gint_core_ctx sys_ctx;
|
|||
static void lock(void)
|
||||
{
|
||||
/* Just disable everything, drivers will enable what they support */
|
||||
if(isSH3())
|
||||
{
|
||||
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000;
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000;
|
||||
}
|
||||
if(isSH3()) for(int i = 0; i < 8; i++)
|
||||
*(SH7705_INTC.IPRS[i]) = 0x0000;
|
||||
else for(int i = 0; i < 12; i++)
|
||||
SH7305_INTC.IPRS[2 * i] = 0x0000;
|
||||
}
|
||||
|
||||
/* gint_install() - install and start gint */
|
||||
|
@ -122,7 +110,7 @@ static void unlock(void)
|
|||
|
||||
/* Restore all driver settings, but do it in reverse order of loading
|
||||
to honor the dependency system */
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &edrv;)
|
||||
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
|
||||
{
|
||||
if(drv->unload) drv->unload();
|
||||
if(drv->ctx_restore) drv->ctx_restore(drv->sys_ctx);
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
// gint:core:start - Kernel initialization and C runtime
|
||||
//--
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <core/bootlog.h>
|
||||
#include <core/mpu.h>
|
||||
#include <core/mmu.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/mpu.h>
|
||||
|
||||
/* Symbols provided by the linker script. For sections:
|
||||
- l* represents the load address (source address in ROM)
|
||||
|
@ -31,7 +31,7 @@ int main(int isappli, int optnum);
|
|||
@l Source pointer (load address)
|
||||
@s Size of area (should be a multiple of 16)
|
||||
@r Destination pointer (relocation address) */
|
||||
SECTION(".pretext")
|
||||
GSECTION(".pretext")
|
||||
static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
|
||||
{
|
||||
while(s > 0)
|
||||
|
@ -48,7 +48,7 @@ static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
|
|||
/* regclr() - clear a memory region using symbol information
|
||||
@r Source pointer (base address)
|
||||
@s Size of area (should be a multiple of 16) */
|
||||
SECTION(".pretext")
|
||||
GSECTION(".pretext")
|
||||
static void regclr(uint32_t *r, int32_t s)
|
||||
{
|
||||
while(s > 0)
|
||||
|
@ -67,10 +67,10 @@ static void regclr(uint32_t *r, int32_t s)
|
|||
corresponds to region [b; b+s) when b and s are 1k-aligned.
|
||||
@b Base pointer: first address checked
|
||||
@s Size of region */
|
||||
SECTION(".pretext")
|
||||
GSECTION(".pretext")
|
||||
static void explore(volatile void *b, int32_t s)
|
||||
{
|
||||
ATTR(unused) uint8_t x;
|
||||
GUNUSED uint8_t x;
|
||||
|
||||
while(s > 0)
|
||||
{
|
||||
|
@ -84,7 +84,7 @@ static void explore(volatile void *b, int32_t s)
|
|||
/* acall() - call an array of functions (constructors or destructors)
|
||||
@f First element of array
|
||||
@l First element outside of the array */
|
||||
SECTION(".pretext")
|
||||
GSECTION(".pretext")
|
||||
static void acall(void (**f)(void), void (**l)(void))
|
||||
{
|
||||
while(f < l) (*(*f++))();
|
||||
|
@ -94,7 +94,7 @@ static void acall(void (**f)(void), void (**l)(void))
|
|||
|
||||
/* start() - this is where it all starts
|
||||
Returns a status code. Invoking main menu is better than returning! */
|
||||
SECTION(".pretext.entry")
|
||||
GSECTION(".pretext.entry")
|
||||
int start(int isappli, int optnum)
|
||||
{
|
||||
/* We are currently in a dynamic userspace mapping of an add-in run
|
||||
|
@ -108,9 +108,9 @@ int start(int isappli, int optnum)
|
|||
redirecting interrupts and reimplementing drivers, so we can't rely
|
||||
too much on the system. Ladies and gentlemen, let's have fun! ;D */
|
||||
|
||||
/* For now, we rely on the system to map the ROM pages. RAM is always
|
||||
/* For now, we rely on the system to map ROM pages. RAM is always
|
||||
fully mapped, but we need to initialize it. We also need to do some
|
||||
hardware detection because old fx9860g models have an older
|
||||
hardware detection because old fx9860g models have an different
|
||||
processor with some incompatible features */
|
||||
|
||||
/* Detect architecture - this will tell SH3 from SH4 on fx9860g */
|
||||
|
@ -123,10 +123,7 @@ int start(int isappli, int optnum)
|
|||
regcpy(lgdata, sgdata, rgdata);
|
||||
regcpy(ldata, sdata, rdata);
|
||||
regclr(rbss, sbss);
|
||||
|
||||
#ifdef GINT_BOOT_LOG
|
||||
bootlog_loaded();
|
||||
#endif
|
||||
|
||||
/* Traverse all ROM pages */
|
||||
explore(brom, srom);
|
||||
|
@ -135,15 +132,12 @@ int start(int isappli, int optnum)
|
|||
uint32_t rom, ram;
|
||||
isSH3() ? tlb_mapped_memory(&rom, &ram)
|
||||
: utlb_mapped_memory(&rom, &ram);
|
||||
|
||||
#ifdef GINT_BOOT_LOG
|
||||
bootlog_mapped(rom, ram);
|
||||
#endif
|
||||
|
||||
//---
|
||||
|
||||
/* Cancel add-in execution if not all pages are mapped
|
||||
TODO: Resort to better graphical display, but still fxlib since
|
||||
TODO: Resort to better graphical display, although still fxlib since
|
||||
add-in is not mapped yet */
|
||||
if(rom < (uint32_t)&srom)
|
||||
{
|
||||
|
@ -162,21 +156,21 @@ int start(int isappli, int optnum)
|
|||
|
||||
/* Install gint and switch VBR */
|
||||
gint_install();
|
||||
|
||||
#ifdef GINT_BOOT_LOG
|
||||
bootlog_kernel();
|
||||
#endif
|
||||
|
||||
/* We are now running on our own in kernel mode. To avoid breaking the
|
||||
system, we want to limit our use of syscalls, so we'll have to do
|
||||
device driving ourselves. */
|
||||
/* We are now running on our own in kernel mode. Since we have taken
|
||||
control of interrupts, pretty much any interaction with the sytem
|
||||
will break it. We'll limit our use of syscalls and do device driving
|
||||
ourselves. (Hopefully we can add cool features in the process.) */
|
||||
|
||||
gint_driver_t *drv;
|
||||
|
||||
/* Initialize all drivers by saving the system settings */
|
||||
for(drv = &bdrv; drv < &edrv; drv++)
|
||||
{
|
||||
/* Hook for old SH3 fx9860g machines */
|
||||
if(isSH3() && drv->driver_sh3) drv->driver_sh3();
|
||||
|
||||
if(drv->ctx_save) drv->ctx_save(drv->sys_ctx);
|
||||
if(drv->init) drv->init();
|
||||
}
|
||||
|
@ -185,19 +179,14 @@ int start(int isappli, int optnum)
|
|||
application. We have already loaded the RAM sections earlier; all
|
||||
that's left is calling the constructors and rolling main(). */
|
||||
|
||||
/* Call the constructors */
|
||||
acall(btors, mtors);
|
||||
|
||||
/* Execute the user application */
|
||||
int ret = main(isappli, optnum);
|
||||
|
||||
/* Call the destructors*/
|
||||
acall(mtors, etors);
|
||||
|
||||
/* Before leaving the application, we need to clean up our mess. We
|
||||
have changed many OS settings while accessing the peripheral
|
||||
have changed many hardware settings while accessing the peripheral
|
||||
modules. The OS is bound to be confused (and hang, or crash, or any
|
||||
other kind of giving up) if we don't restore them */
|
||||
other kind of giving up) if we don't restore them. */
|
||||
|
||||
/* Unload gint and give back control to the system. Driver settings
|
||||
will be restored while interrupts are disabled */
|
||||
|
|
BIN
src/font5x6.png
Normal file
BIN
src/font5x6.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 754 B |
118
src/keysc/getkey.c
Normal file
118
src/keysc/getkey.c
Normal 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;
|
||||
}
|
|
@ -2,8 +2,8 @@
|
|||
// gint:keysc:iokbd - I/O-based keyboard input
|
||||
//---
|
||||
|
||||
#include <defs/types.h>
|
||||
#include <core/pfc.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/mpu/pfc.h>
|
||||
|
||||
/* This file is SH7705-only. */
|
||||
#ifdef FX9860G
|
||||
|
@ -104,6 +104,7 @@ uint8_t iokbd_row(int row)
|
|||
return input;
|
||||
}
|
||||
|
||||
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data */
|
||||
void iokbd_scan(uint8_t *scan)
|
||||
{
|
||||
/* Scan each row independently; the gain from scanning them altogether
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/timer.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/keyboard.h>
|
||||
|
||||
#include <core/mpu.h>
|
||||
#include <defs/attributes.h>
|
||||
#include <gint/drivers/iokbd.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/mpu.h>
|
||||
|
||||
//---
|
||||
// Keyboard buffer
|
||||
|
@ -16,13 +18,15 @@
|
|||
|
||||
/* The driver's internal state. At each step of time, this file compares the
|
||||
internal state with the hardware state and generates events accordingly.
|
||||
Events can be seen as a delta-encoding of the state of the keyboard over
|
||||
time, and this buffer must be the sum of all events. This means that if an
|
||||
event cannot be generated, f.i. because the buffer is full, this state must
|
||||
*not* be updated. */
|
||||
Events can be seen as a delta-encoding of the keyboard state over time.
|
||||
|
||||
The user which sums up these events to maintain a full keyboard state must
|
||||
get a correct result. As a consequence, if an event cannot be generated
|
||||
(whatever the reason), the driver's internal copy of the keyboard state must
|
||||
not be updated (probably the event will be re-emitted at the next scan). */
|
||||
GDATA volatile uint8_t state[12] = { 0 };
|
||||
|
||||
/* A driver event, which is a change in a full row, not a key. */
|
||||
/* A driver event, which is a change in a full row instead of a single key. */
|
||||
typedef struct
|
||||
{
|
||||
uint time :12; /* Locally unique time identifier */
|
||||
|
@ -42,7 +46,6 @@ GDATA static int buffer_end = 0;
|
|||
|
||||
/* Current time, in keyboard-scanning ticks */
|
||||
GDATA int time = 0;
|
||||
GDATA int full_release = 2;
|
||||
|
||||
/* buffer_push() - add an event in the keyboard buffer
|
||||
Returns non-zero if the event cannot be pushed. */
|
||||
|
@ -67,15 +70,13 @@ static int buffer_poll(driver_event_t *ev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* keysc_frame() - generate a round of interrupts for the current frame */
|
||||
/* keysc_frame() - generate a round of events for the current frame */
|
||||
void keysc_frame(void)
|
||||
{
|
||||
ALIGNED(2) uint8_t scan[12] = { 0 };
|
||||
time++;
|
||||
GALIGNED(2) uint8_t scan[12] = { 0 };
|
||||
|
||||
/* First scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */
|
||||
if(isSH3()) iokbd_scan(&scan);
|
||||
if(isSH3()) iokbd_scan(scan);
|
||||
else
|
||||
{
|
||||
volatile uint16_t *KEYSC = (void *)0xa44b0000;
|
||||
|
@ -103,8 +104,8 @@ void keysc_frame(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* key_poll() - poll the next keyboard event from the buffer */
|
||||
key_event_t key_poll(void)
|
||||
/* pollevent() - poll the next keyboard event */
|
||||
key_event_t pollevent(void)
|
||||
{
|
||||
/* Every time a driver event is unqueued, its key events are generated
|
||||
and put in this small buffer */
|
||||
|
@ -112,14 +113,14 @@ key_event_t key_poll(void)
|
|||
/* Number of pending events in the previous buffer */
|
||||
static int events_pending = 0;
|
||||
|
||||
/* If there are pending events, use them */
|
||||
/* Use pending events first, then poll the driver buffer */
|
||||
|
||||
if(events_pending > 0) return events[--events_pending];
|
||||
|
||||
/* Unqueue something from the driver buffer, if possible */
|
||||
driver_event_t ev;
|
||||
if(buffer_poll(&ev)) return (key_event_t){ .type = KEYEV_NONE };
|
||||
|
||||
/* Generate new key events */
|
||||
/* Generate new key events and return the first of them*/
|
||||
int old = ev.old << 1;
|
||||
int new = ev.new;
|
||||
|
||||
|
@ -139,35 +140,55 @@ key_event_t key_poll(void)
|
|||
events[events_pending++] = keyev;
|
||||
}
|
||||
|
||||
/* Return the first of these generated events */
|
||||
return events[--events_pending];
|
||||
}
|
||||
|
||||
/* waitevent() - wait for the next keyboard event */
|
||||
key_event_t waitevent(volatile int *timeout)
|
||||
{
|
||||
key_event_t none = { .type = KEYEV_NONE };
|
||||
|
||||
do {
|
||||
key_event_t ev = pollevent();
|
||||
if(ev.type != KEYEV_NONE) return ev;
|
||||
sleep();
|
||||
|
||||
} while(!(timeout && *timeout));
|
||||
|
||||
return none;
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver initialization
|
||||
//---
|
||||
|
||||
static int callback(UNUSED void *arg)
|
||||
static int callback(GUNUSED volatile void *arg)
|
||||
{
|
||||
keysc_frame();
|
||||
time++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init() - setup the support timer */
|
||||
static void init(void)
|
||||
{
|
||||
int tid = isSH3() ? 4 : 7;
|
||||
int tid = isSH3() ? 3 : 8;
|
||||
|
||||
/* 32768 Hz divided by 16 is 2048 */
|
||||
/* TODO: Use I/O port scanning on SH3 */
|
||||
timer_setup(tid, 2048, 0, callback, NULL);
|
||||
/* Configure the timer to do 128 keyboard scans per second. This
|
||||
frequency *must* be high for the KEYSC interface to work! */
|
||||
/* Note: the supporting timer always runs at 32768 Hz. */
|
||||
/* TODO: The SH3 does not need to run fast, adjust user settings? */
|
||||
int delay = 32768 / KEYBOARD_SCAN_FREQUENCY;
|
||||
if(!delay) delay = 1;
|
||||
|
||||
timer_setup(tid, delay, 0, callback, NULL);
|
||||
timer_start(tid);
|
||||
}
|
||||
|
||||
/* unload() - stop the support timer */
|
||||
static void unload(void)
|
||||
{
|
||||
int tid = isSH3() ? 4 : 7;
|
||||
int tid = isSH3() ? 3 : 8;
|
||||
timer_stop(tid);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
//---
|
||||
|
||||
#include <gint/drivers.h>
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
#ifdef FXCG50
|
||||
|
||||
|
@ -56,7 +56,7 @@ GDATA static volatile uint16_t *intf = (void *)0xb4000000;
|
|||
/* Bit 4 of Port R controls the RS bit of the display driver */
|
||||
GDATA static volatile uint8_t *PRDR = (void *)0xa405013c;
|
||||
|
||||
INLINE static void select(uint16_t reg)
|
||||
GINLINE static void select(uint16_t reg)
|
||||
{
|
||||
/* Clear RS and write the register number */
|
||||
*PRDR &= ~0x10;
|
||||
|
@ -72,12 +72,12 @@ INLINE static void select(uint16_t reg)
|
|||
synco();
|
||||
}
|
||||
|
||||
INLINE static uint16_t read(void)
|
||||
GINLINE static uint16_t read(void)
|
||||
{
|
||||
return *intf;
|
||||
}
|
||||
|
||||
INLINE static void write(uint16_t data)
|
||||
GINLINE static void write(uint16_t data)
|
||||
{
|
||||
*intf = data;
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ typedef struct
|
|||
/* Graphics RAM range */
|
||||
uint16_t HSA, HEA, VSA, VEA;
|
||||
|
||||
} PACKED(2) ctx_t;
|
||||
} GPACKED(2) ctx_t;
|
||||
|
||||
/* Allocate one buffer in gint's storage section */
|
||||
GBSS static ctx_t sys_ctx;
|
||||
|
@ -301,7 +301,7 @@ static void ctx_restore(void *buf)
|
|||
//---
|
||||
|
||||
gint_driver_t drv_r61524 = {
|
||||
.name = "Renesas R61524",
|
||||
.name = "R61524",
|
||||
.init = NULL,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
.sys_ctx = &sys_ctx,
|
||||
|
|
0
src/render-cg/dclear.c
Normal file
0
src/render-cg/dclear.c
Normal file
44
src/render-fx/bopti-asm.h
Normal file
44
src/render-fx/bopti-asm.h
Normal 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
110
src/render-fx/bopti-asm.s
Normal 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
268
src/render-fx/bopti.c
Normal 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
39
src/render-fx/dclear.c
Normal 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
136
src/render-fx/dline.c
Normal 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
28
src/render-fx/dpixel.c
Normal 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
61
src/render-fx/drect.c
Normal 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
15
src/render-fx/dupdate.c
Normal 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);
|
||||
}
|
BIN
src/render-fx/font-default.png
Normal file
BIN
src/render-fx/font-default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
28
src/render-fx/masks.c
Normal file
28
src/render-fx/masks.c
Normal 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
15
src/render-fx/topti-asm.h
Normal 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
170
src/render-fx/topti-asm.s
Normal 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
252
src/render-fx/topti.c
Normal 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]);
|
||||
}
|
|
@ -27,7 +27,7 @@ _inth_rtc_pri:
|
|||
|
||||
/* Jump to another gate to finish the work:
|
||||
- 0xc is the size of storage below
|
||||
- 0x20 is the size of the next gate (alarm interrupt) */
|
||||
- 0x20 is the size of the gap before next gate (alarm interrupt) */
|
||||
mov #0x2c, r2
|
||||
braf r2
|
||||
nop
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
#include <gint/rtc.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/gint.h>
|
||||
#include <mod/rtc.h>
|
||||
|
||||
#include <core/mpu.h>
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/mpu.h>
|
||||
#include <gint/mpu/rtc.h>
|
||||
|
||||
//---
|
||||
// Real-Time Clock peripheral registers
|
||||
|
@ -18,10 +18,10 @@
|
|||
GDATA static rtc_t *RTC = &SH7305_RTC;
|
||||
/* Address of interrupt handler parameters */
|
||||
GBSS struct {
|
||||
int (*callback)(void *arg);
|
||||
void *arg;
|
||||
int (*callback)(volatile void *arg);
|
||||
volatile void *arg;
|
||||
volatile uint8_t *RCR2;
|
||||
} PACKED(4) *params;
|
||||
} GPACKED(4) *params;
|
||||
|
||||
//---
|
||||
// Time management
|
||||
|
@ -97,8 +97,12 @@ void rtc_set_time(const rtc_time_t *time)
|
|||
//---
|
||||
|
||||
/* rtc_start_timer() - configure the RTC timer */
|
||||
void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void*arg), void*arg)
|
||||
void rtc_start_timer(rtc_frequency_t freq,
|
||||
int (*callback)(volatile void *arg), volatile void *arg)
|
||||
{
|
||||
/* Temporarily disable the interrupt */
|
||||
RTC->RCR2.PES = RTC_NONE;
|
||||
|
||||
/* Set up the callback */
|
||||
params->callback = callback;
|
||||
params->arg = arg;
|
||||
|
@ -114,7 +118,7 @@ void rtc_start_timer(rtc_frequency_t freq, int (*callback)(void*arg), void*arg)
|
|||
/* rtc_stop_timer() - stop the RTC timer */
|
||||
void rtc_stop_timer(void)
|
||||
{
|
||||
RTC->RCR2.PES = rtc_none;
|
||||
RTC->RCR2.PES = RTC_NONE;
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -136,7 +140,7 @@ static void init(void)
|
|||
extern void inth_rtc_pri_helper(void);
|
||||
|
||||
/* Install the RTC interrupt handler */
|
||||
UNUSED void *h0, *h1;
|
||||
GUNUSED void *h0, *h1;
|
||||
h0 = gint_inthandler(0xaa0, inth_rtc_pri, 32);
|
||||
h1 = gint_inthandler(0xae0, inth_rtc_pri_helper, 32);
|
||||
|
||||
|
@ -144,7 +148,7 @@ static void init(void)
|
|||
params->RCR2 = &RTC->RCR2.byte;
|
||||
|
||||
/* Disable the periodic interrupt for now, but give it priority 5 */
|
||||
RTC->RCR2.PES = rtc_none;
|
||||
RTC->RCR2.PES = RTC_NONE;
|
||||
gint_intlevel(isSH3() ? 3 : 40, 5);
|
||||
}
|
||||
|
||||
|
@ -179,7 +183,7 @@ static void ctx_restore(void *buf)
|
|||
//---
|
||||
|
||||
gint_driver_t drv_rtc = {
|
||||
.name = "Real-Time Clock",
|
||||
.name = "RTC",
|
||||
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
|
||||
.init = init,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
//---
|
||||
|
||||
#include <gint/drivers.h>
|
||||
#include <drivers/t6k11.h>
|
||||
#include <gint/drivers/t6k11.h>
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <core/mpu.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/mpu.h>
|
||||
|
||||
#ifdef FX9860G
|
||||
|
||||
|
@ -55,21 +55,21 @@ GDATA static volatile uint8_t *cmd = (void *)0xb4010000;
|
|||
/* command() - send a command to set the value of a register
|
||||
@reg Register number
|
||||
@data Value to set in reg */
|
||||
INLINE static void command(uint8_t reg, uint8_t data)
|
||||
GINLINE static void command(uint8_t reg, uint8_t data)
|
||||
{
|
||||
*sel = reg;
|
||||
*cmd = data;
|
||||
}
|
||||
|
||||
/* status() - read the status byte from the display driver */
|
||||
INLINE static uint8_t status(void)
|
||||
GINLINE static uint8_t status(void)
|
||||
{
|
||||
return *sel;
|
||||
}
|
||||
|
||||
/* write_row() - send 16 bytes to the display driver
|
||||
@buf Buffer to take data from */
|
||||
INLINE static void write_row(const uint8_t *buf)
|
||||
GINLINE static void write_row(const uint8_t *buf)
|
||||
{
|
||||
*sel = reg_data;
|
||||
|
||||
|
@ -129,6 +129,12 @@ void t6k11_contrast(int contrast)
|
|||
fx9860G II with OS 02.05.2201, are in range 0x97 .. 0xb7.
|
||||
This means VLC0 = 2 and CONTRAST in [23..55] */
|
||||
command(reg_contrast, 0x97 + contrast);
|
||||
|
||||
/* TODO: Turns out that different models, notably screens without
|
||||
TODO: backlight, will have different ranges. Plus we might want to
|
||||
TODO: use transitions to extreme contrast settings for visual
|
||||
TODO: effects.
|
||||
TODO: Extend the available range of contrast settings. */
|
||||
}
|
||||
|
||||
/* t6k11_backlight() - manage the screen backlight */
|
||||
|
@ -168,7 +174,7 @@ typedef struct
|
|||
/* There *are* other parameters that are affected by the driver, but
|
||||
they cannot be read, so I can't determine the system's setting */
|
||||
|
||||
} PACKED(1) ctx_t;
|
||||
} GPACKED(1) ctx_t;
|
||||
|
||||
/* Pre-allocate a context in gint's uninitialized section */
|
||||
GBSS static ctx_t sys_ctx;
|
||||
|
@ -197,7 +203,7 @@ static void ctx_restore(void *buf)
|
|||
//---
|
||||
|
||||
gint_driver_t drv_t6k11 = {
|
||||
.name = "Toshiba T6K11",
|
||||
.name = "T6K11",
|
||||
.init = NULL,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
.sys_ctx = &sys_ctx,
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
- Clear the interrupt flag
|
||||
- Invoke a callback function and pass it a user-provided argument
|
||||
- Stop the timer if the callback returns non-zero
|
||||
- Host their own callback and arguments
|
||||
- Host their own callback pointers and arguments
|
||||
|
||||
It is important to notice that the code of the following gates looks like
|
||||
they are contiguous in memory. The assembler will make that assumption, and
|
||||
|
@ -60,7 +60,7 @@ _inth_tmu_0:
|
|||
mov.l @(4, r0), r4
|
||||
lds.l @r15+, pr
|
||||
|
||||
/* Prepare stopping the timer */
|
||||
/* Prepare stopping the timer and jump to second section */
|
||||
mov.l .tstr, r5
|
||||
bra .stoptimer
|
||||
mov.l @r15+, r1
|
||||
|
@ -122,11 +122,11 @@ _inth_tmu_storage:
|
|||
To implement the same functionalities as the standard timers, several blocks
|
||||
are once again needed. But the handlers for the extra timers are not located
|
||||
in adjacent gates, except for ETMU1 and ETMU2 which have event codes 0xc20
|
||||
and 0xc40. Since handler 0xc60 is free on both SH3 and SH4, I'm using it to
|
||||
build a three-handler block and achieve the same result as above.
|
||||
and 0xc40. Since handler 0xc60 is free on SH4 and not redirected to on SH3,
|
||||
I use it to build a three-handler block similar to that of the TMU above.
|
||||
|
||||
On SH4 this means that an extra gate has to be installed, but no interrupt
|
||||
pointed here. On SH3 this means that four gates are used for the only extra
|
||||
point here. On SH3 this means that four gates are used for the only extra
|
||||
timer, but the incurred cost is minimal (96 bytes on the binary file)
|
||||
because the size of the VBR area can hardly be shrunk anyway.
|
||||
|
||||
|
@ -141,8 +141,8 @@ _inth_tmu_storage:
|
|||
|
||||
/* FIRST GATE - ETMU2 entry, clear flag and prepare callback */
|
||||
_inth_tmu_extra2:
|
||||
/* Warning: the size of this section (2 bytes) is hardcoded in another
|
||||
interrupt handler, _inth_tmu_extra_others */
|
||||
/* Warning: the size of the following instruction (2 bytes) is
|
||||
hardcoded in another interrupt handler, _inth_tmu_extra_others */
|
||||
mova .storage_extra_1, r0
|
||||
|
||||
.extra_callback:
|
||||
|
@ -214,7 +214,7 @@ _inth_tmu_extra_others:
|
|||
- 0x600 to reach the interrupt handlers
|
||||
- 0x020 to jump over the entry gate
|
||||
- 0x840 to reach the handler of extra timer 2
|
||||
- 0x002 to jump over the first part of its code */
|
||||
- 0x002 to skip its first instruction (the size is hardcoded) */
|
||||
1: .long 0xe62
|
||||
|
||||
.zero 4
|
||||
|
|
125
src/tmu/tmu.c
125
src/tmu/tmu.c
|
@ -7,10 +7,10 @@
|
|||
#include <gint/gint.h>
|
||||
#include <gint/clock.h>
|
||||
|
||||
#include <core/intc.h>
|
||||
#include <gint/mpu/intc.h>
|
||||
|
||||
#include <defs/attributes.h>
|
||||
#include <defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// Timer structures
|
||||
|
@ -31,7 +31,7 @@ typedef volatile struct
|
|||
uint16_t TPSC :3; /* Timer prescaler (input clock) */
|
||||
);
|
||||
|
||||
} PACKED(4) tmu_t;
|
||||
} GPACKED(4) tmu_t;
|
||||
|
||||
/* tmu_extra_t - extra timers on sh7337, sh7355 and sh7305 */
|
||||
typedef volatile struct
|
||||
|
@ -48,16 +48,16 @@ typedef volatile struct
|
|||
uint8_t UNIE :1; /* Underflow interrupt enable */
|
||||
);
|
||||
|
||||
} PACKED(4) tmu_extra_t;
|
||||
} GPACKED(4) tmu_extra_t;
|
||||
|
||||
/* inth_data_t - data storage inside interrupt handlers */
|
||||
typedef struct
|
||||
{
|
||||
int (*callback)(void *arg); /* User-provided callback function */
|
||||
void *arg; /* Argument for [callback] */
|
||||
int (*cb)(volatile void *arg); /* User-provided callback */
|
||||
volatile void *arg; /* Argument for [callback] */
|
||||
volatile void *structure; /* Either TCR or timer address */
|
||||
|
||||
} PACKED(4) inth_data_t;
|
||||
} GPACKED(4) inth_data_t;
|
||||
|
||||
/* timer_t - all data required to run a single timer */
|
||||
typedef struct
|
||||
|
@ -94,7 +94,7 @@ GDATA static volatile uint8_t *TSTR = (void *)0xa4490004;
|
|||
|
||||
/* timer_setup() - set up a timer */
|
||||
int timer_setup(int tid, uint32_t delay, timer_input_t clock,
|
||||
int (*callback)(void *arg), void *arg)
|
||||
int (*callback)(volatile void *arg), volatile void *arg)
|
||||
{
|
||||
/* We need to distinguish normal and extra timers */
|
||||
if(tid < 3)
|
||||
|
@ -122,21 +122,24 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock,
|
|||
tmu_extra_t *t = timers[tid].tmu;
|
||||
if(t->TCR.UNIE) return -1;
|
||||
|
||||
/* There is no clock input and no clock edge settings */
|
||||
t->TCOR = delay;
|
||||
t->TCNT = delay;
|
||||
|
||||
/* Clear the interrupt flag */
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
|
||||
/* There is no clock input and no clock edge settings */
|
||||
t->TCOR = delay;
|
||||
|
||||
/* TODO: FXCG50: does not always work on first try */
|
||||
do t->TCNT = delay;
|
||||
while(t->TCNT != delay);
|
||||
|
||||
t->TCR.UNIE = 1;
|
||||
}
|
||||
|
||||
/* Register the callback and its argument (TMU-owned timers only) */
|
||||
if(timers[tid].data)
|
||||
{
|
||||
timers[tid].data->callback = callback;
|
||||
timers[tid].data->cb = callback;
|
||||
timers[tid].data->arg = arg;
|
||||
}
|
||||
|
||||
|
@ -145,20 +148,19 @@ int timer_setup(int tid, uint32_t delay, timer_input_t clock,
|
|||
}
|
||||
|
||||
/* timer_delay() - compute a delay constant from a duration in seconds */
|
||||
uint32_t timer_delay(int tid, int delay_us)
|
||||
uint32_t timer_delay(int tid, uint64_t delay_us)
|
||||
{
|
||||
/* TODO: Proper timer_delay() */
|
||||
const clock_frequency_t *clock = clock_freq();
|
||||
uint64_t freq;
|
||||
|
||||
freq = isSH3() ? 14750000 >> 2 : clock->Pphi_f >> 2;
|
||||
uint64_t freq = clock->Pphi_f >> 2;
|
||||
|
||||
/* fxcg50: Calculated = 29491200 but it's too low */
|
||||
// uint64_t freq = 29020000 >> 2;
|
||||
|
||||
if(tid >= 3) freq = 32768; /* 32768 Hz */
|
||||
/* Extra timers all run at 32768 Hz */
|
||||
if(tid >= 3) freq = 32768;
|
||||
|
||||
uint64_t product = freq * (uint64_t)delay_us;
|
||||
uint64_t product = freq * delay_us;
|
||||
return product / 1000000;
|
||||
}
|
||||
|
||||
|
@ -167,17 +169,10 @@ uint32_t timer_delay(int tid, int delay_us)
|
|||
@state 0 to start the timer, 1 to stop it (nothing else!) */
|
||||
static void timer_control(int tid, int state)
|
||||
{
|
||||
if(tid < 3)
|
||||
{
|
||||
/* For standard timers, use the MPU's TSTR register */
|
||||
*TSTR = (*TSTR | (1 << tid)) ^ (state << tid);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Extra timers all have their own TSTR register */
|
||||
tmu_extra_t *t = timers[tid].tmu;
|
||||
t->TSTR = (t->TSTR | 1) ^ state;
|
||||
}
|
||||
/* For standard timers, use the MPU's TSTR register */
|
||||
if(tid < 3) *TSTR = (*TSTR | (1 << tid)) ^ (state << tid);
|
||||
/* Extra timers all have their own TSTR register */
|
||||
else ((tmu_extra_t *)timers[tid].tmu)->TSTR = state ^ 1;
|
||||
}
|
||||
|
||||
/* timer_start() - start a configured timer */
|
||||
|
@ -202,8 +197,6 @@ void timer_pause(int tid)
|
|||
/* timer_stp() - stop and free a timer */
|
||||
void timer_stop(int tid)
|
||||
{
|
||||
return;
|
||||
|
||||
/* Stop the timer and disable UNIE to indicate that it's free */
|
||||
timer_pause(tid);
|
||||
|
||||
|
@ -219,13 +212,28 @@ void timer_stop(int tid)
|
|||
|
||||
/* Also clear TCOR and TCNT to avoid spurious interrupts */
|
||||
t->TCOR = 0xffffffff;
|
||||
t->TCNT = 0xffffffff;
|
||||
|
||||
/* TODO: FXCG50: Again */
|
||||
do t->TCNT = 0xffffffff;
|
||||
while(t->TCNT + 1);
|
||||
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Predefined timer callbacks
|
||||
//---
|
||||
|
||||
/* timer_timeout() - callback that sets a flag and halts the timer */
|
||||
int timer_timeout(volatile void *arg)
|
||||
{
|
||||
volatile int *x = arg;
|
||||
(*x)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver initialization
|
||||
//---
|
||||
|
@ -258,11 +266,10 @@ static void driver_sh3(void)
|
|||
|
||||
static void init(void)
|
||||
{
|
||||
/* Install the standard's TMU interrupt handlers. By chance TMU gates
|
||||
use the same event codes on SH7705 and SH7305 */
|
||||
UNUSED void *h0, *h1, *h2, *hs;
|
||||
h0 = gint_inthandler(0x400, inth_tmu_0, 32);
|
||||
h1 = gint_inthandler(0x420, inth_tmu_1, 32);
|
||||
/* Install the standard's TMU interrupt handlers */
|
||||
void *h2, *hs;
|
||||
gint_inthandler(0x400, inth_tmu_0, 32);
|
||||
gint_inthandler(0x420, inth_tmu_1, 32);
|
||||
h2 = gint_inthandler(0x440, inth_tmu_2, 32);
|
||||
hs = gint_inthandler(0x460, inth_tmu_storage, 32);
|
||||
|
||||
|
@ -270,7 +277,6 @@ static void init(void)
|
|||
timers[0].data = h2 + 20;
|
||||
timers[1].data = hs + 8;
|
||||
timers[2].data = hs + 20;
|
||||
|
||||
/* SH3: Override the address of TSTR in the interrupt handler helper */
|
||||
if(isSH3()) *(volatile uint8_t **)(hs + 4) = TSTR;
|
||||
|
||||
|
@ -282,10 +288,12 @@ static void init(void)
|
|||
for(int i = 0; i < 3; i++)
|
||||
{
|
||||
tmu_t *t = timers[i].tmu;
|
||||
t->TCR.UNIE = 0;
|
||||
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
t->TCOR = 0xffffffff;
|
||||
t->TCNT = 0xffffffff;
|
||||
|
||||
do t->TCR.word = 0;
|
||||
while(t->TCR.word);
|
||||
|
||||
/* Standard timers: TCR is provided to the interrupt handler */
|
||||
timers[i].data->structure = &t->TCR;
|
||||
|
@ -296,16 +304,21 @@ static void init(void)
|
|||
{
|
||||
tmu_extra_t *t = timers[i].tmu;
|
||||
|
||||
/* This is *extremely important*: extra timers will generate
|
||||
interrupts as long as TCNT = 0 *regardless of TSTR*! */
|
||||
t->TCOR = 0xffffffff;
|
||||
t->TCNT = 0xffffffff;
|
||||
/* Extra timers seem to generate interrupts as long as TCNT=0,
|
||||
regardless of TSTR. I'm not entirely sure about this weird
|
||||
behaviour, but for safety I'll set TCOR/TCNT to non-zero.
|
||||
This may be related to difficulties when setting TCNT. */
|
||||
|
||||
t->TCR.UNIE = 0;
|
||||
t->TSTR = 0;
|
||||
t->TCOR = 0xffffffff;
|
||||
|
||||
do t->TCR.UNF = 0;
|
||||
while(t->TCR.UNF);
|
||||
/* TODO: FXCG50: Safety */
|
||||
do t->TCNT = 0xffffffff;
|
||||
while(t->TCNT + 1);
|
||||
|
||||
/* Clear interrupts */
|
||||
do t->TCR.byte = 0;
|
||||
while(t->TCR.byte);
|
||||
}
|
||||
|
||||
/* Install the extra timers. We need three extra timers for the
|
||||
|
@ -347,10 +360,10 @@ static void init(void)
|
|||
gint_intlevel(44, 7);
|
||||
|
||||
/* Unmask the extra timers' interrupts */
|
||||
INTC4.MSKCLR->IMR2 = 0x01;
|
||||
INTC4.MSKCLR->IMR5 = 0x06;
|
||||
INTC4.MSKCLR->IMR6 = 0x18;
|
||||
INTC4.MSKCLR->IMR8 = 0x02;
|
||||
SH7305_INTC.MSKCLR->IMR2 = 0x01;
|
||||
SH7305_INTC.MSKCLR->IMR5 = 0x06;
|
||||
SH7305_INTC.MSKCLR->IMR6 = 0x18;
|
||||
SH7305_INTC.MSKCLR->IMR8 = 0x02;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,7 +377,7 @@ typedef struct
|
|||
tmu_extra_t extra[6];
|
||||
uint8_t TSTR;
|
||||
|
||||
} PACKED(4) ctx_t;
|
||||
} GPACKED(4) ctx_t;
|
||||
|
||||
/* Allocate a system buffer in gint's BSS area */
|
||||
GBSS static ctx_t sys_ctx;
|
||||
|
@ -428,7 +441,7 @@ static void ctx_restore(void *buf)
|
|||
//---
|
||||
|
||||
gint_driver_t drv_tmu = {
|
||||
.name = "Timer Unit",
|
||||
.name = "TMU",
|
||||
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
|
||||
.init = init,
|
||||
.ctx_size = sizeof(ctx_t),
|
||||
|
|
Loading…
Reference in a new issue