diff --git a/.gitignore b/.gitignore index 5b8a11e..39f9724 100644 --- a/.gitignore +++ b/.gitignore @@ -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/** - diff --git a/Makefile b/Makefile old mode 100755 new mode 100644 index ca1ac0b..8feb79c --- a/Makefile +++ b/Makefile @@ -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% diff --git a/README.md b/README.md index d5b21b9..49c5c8b 100644 --- a/README.md +++ b/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. diff --git a/TODO b/TODO index 47e0637..9716f84 100644 --- a/TODO +++ b/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 +- Finish the boot log in [core/bootlog.c] +- Document the SH7305 PFC in +- Change the description of the version number in +- Define the version number from full Git info +- Do this display in and +- 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) diff --git a/configure b/configure index d97157a..cad5b14 100755 --- a/configure +++ b/configure @@ -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= Number of virtual timer slots (this feature will be + moved to an independent library) [16] + -events-queue-size= + 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=";; + --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!" diff --git a/fx9860g.ld b/fx9860g.ld index 3a45257..6b5a3ba 100644 --- a/fx9860g.ld +++ b/fx9860g.ld @@ -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); diff --git a/fxcg50.ld b/fxcg50.ld index 251cff0..78d7410 100644 --- a/fxcg50.ld +++ b/fxcg50.ld @@ -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); diff --git a/include/core/bootlog.h b/include/core/bootlog.h index 8b65247..aa83e4f 100644 --- a/include/core/bootlog.h +++ b/include/core/bootlog.h @@ -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 */ diff --git a/include/core/memory.h b/include/core/memory.h index 49c02aa..522f2a4 100644 --- a/include/core/memory.h +++ b/include/core/memory.h @@ -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. //--- diff --git a/include/core/mmu.h b/include/core/mmu.h index d92d3ee..38eccc2 100644 --- a/include/core/mmu.h +++ b/include/core/mmu.h @@ -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 -#include +#include +#include //--- // 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) diff --git a/include/core/setup.h b/include/core/setup.h index 72c46eb..c692b02 100644 --- a/include/core/setup.h +++ b/include/core/setup.h @@ -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 +#include /* Prototypes for the library management functions are in */ /* 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 diff --git a/include/defs/types.h b/include/defs/types.h deleted file mode 100644 index e7d487f..0000000 --- a/include/defs/types.h +++ /dev/null @@ -1,78 +0,0 @@ -//--- -// gint:defs:types - Type-related macros -//--- - -#ifndef GINT_DEFS_TYPES -#define GINT_DEFS_TYPES - -#include -#include -#include - -//--- -// 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 */ diff --git a/include/display/fx.h b/include/display/fx.h new file mode 100644 index 0000000..876b930 --- /dev/null +++ b/include/display/fx.h @@ -0,0 +1,30 @@ +//--- +// display:fx - Internal definitions for the display module on fx9860g +//--- + +#ifndef DISPLAY_FX +#define DISPLAY_FX + +#include +#include + +/* 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 */ diff --git a/include/gint/clock.h b/include/gint/clock.h index eeb3f00..c35c0ba 100644 --- a/include/gint/clock.h +++ b/include/gint/clock.h @@ -5,8 +5,6 @@ #ifndef GINT_CLOCK #define GINT_CLOCK -#include - //--- // 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 diff --git a/include/defs/attributes.h b/include/gint/defs/attributes.h similarity index 56% rename from include/defs/attributes.h rename to include/gint/defs/attributes.h index 9986936..62e2d4b 100644 --- a/include/defs/attributes.h +++ b/include/gint/defs/attributes.h @@ -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 */ diff --git a/include/gint/defs/types.h b/include/gint/defs/types.h new file mode 100644 index 0000000..eab3781 --- /dev/null +++ b/include/gint/defs/types.h @@ -0,0 +1,48 @@ +//--- +// gint:defs:types - Type definitions +//--- + +#ifndef GINT_DEFS_TYPES +#define GINT_DEFS_TYPES + +#include + +/* For size_t, mainly */ +#include +/* For all fixed-width integer types */ +#include + +/* 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 */ diff --git a/include/gint/defs/util.h b/include/gint/defs/util.h new file mode 100644 index 0000000..6f3452c --- /dev/null +++ b/include/gint/defs/util.h @@ -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)) \ +)) diff --git a/include/gint/display-cg.h b/include/gint/display-cg.h new file mode 100644 index 0000000..36cdd33 --- /dev/null +++ b/include/gint/display-cg.h @@ -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 */ diff --git a/include/gint/display-fx.h b/include/gint/display-fx.h index c7448c1..ae758d9 100644 --- a/include/gint/display-fx.h +++ b/include/gint/display-fx.h @@ -11,16 +11,14 @@ #ifdef FX9860G -/* Screen dimensions on fx9860g */ -#define DWIDTH 128 -#define DHEIGHT 64 +#include /* 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 */ diff --git a/include/gint/display.h b/include/gint/display.h index f3f0465..c69e1d5 100644 --- a/include/gint/display.h +++ b/include/gint/display.h @@ -5,6 +5,8 @@ #ifndef GINT_DISPLAY #define GINT_DISPLAY +#include + /* 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 @@ -29,4 +31,28 @@ extern uint32_t *vram; #include #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 */ diff --git a/include/gint/drivers.h b/include/gint/drivers.h index 475378e..7894753 100644 --- a/include/gint/drivers.h +++ b/include/gint/drivers.h @@ -5,8 +5,8 @@ #ifndef GINT_DRIVERS #define GINT_DRIVERS -#include -#include +#include +#include //--- // 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. */ diff --git a/include/gint/drivers/iokbd.h b/include/gint/drivers/iokbd.h new file mode 100644 index 0000000..455ff3c --- /dev/null +++ b/include/gint/drivers/iokbd.h @@ -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 + +/* 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 */ diff --git a/include/drivers/t6k11.h b/include/gint/drivers/t6k11.h similarity index 96% rename from include/drivers/t6k11.h rename to include/gint/drivers/t6k11.h index a4bc7e1..a177fa6 100644 --- a/include/drivers/t6k11.h +++ b/include/gint/drivers/t6k11.h @@ -7,7 +7,7 @@ #ifndef GINT_DRIVERS_T6K11 #define GINT_DRIVERS_T6K11 -#include +#include /* 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); diff --git a/include/gint/gint.h b/include/gint/gint.h index 14f11e9..4918df0 100644 --- a/include/gint/gint.h +++ b/include/gint/gint.h @@ -5,11 +5,10 @@ #ifndef GINT_GINT #define GINT_GINT -#include -#include +#include -/* 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 diff --git a/include/gint/keyboard.h b/include/gint/keyboard.h index 12696dd..efc3096 100644 --- a/include/gint/keyboard.h +++ b/include/gint/keyboard.h @@ -5,34 +5,55 @@ #ifndef GINT_KEYBOARD #define GINT_KEYBOARD -#include +/* 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 +#include /* 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 . */ +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 */ diff --git a/include/gint/keycodes.h b/include/gint/keycodes.h new file mode 100644 index 0000000..5e46f3d --- /dev/null +++ b/include/gint/keycodes.h @@ -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 */ diff --git a/include/core/mpu.h b/include/gint/mpu.h similarity index 86% rename from include/core/mpu.h rename to include/gint/mpu.h index c963eb6..3b945ba 100644 --- a/include/core/mpu.h +++ b/include/gint/mpu.h @@ -12,8 +12,6 @@ #ifndef GINT_CORE_MPU #define GINT_CORE_MPU -#include - /* 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 diff --git a/include/core/cpg.h b/include/gint/mpu/cpg.h similarity index 93% rename from include/core/cpg.h rename to include/gint/mpu/cpg.h index 07d5244..afb57bf 100644 --- a/include/core/cpg.h +++ b/include/gint/mpu/cpg.h @@ -5,7 +5,8 @@ #ifndef GINT_CORE_CPG #define GINT_CORE_CPG -#include +#include +#include //--- // 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)) diff --git a/include/core/intc.h b/include/gint/mpu/intc.h similarity index 96% rename from include/core/intc.h rename to include/gint/mpu/intc.h index 255f7f8..a1e0c4d 100644 --- a/include/core/intc.h +++ b/include/gint/mpu/intc.h @@ -12,7 +12,7 @@ #ifndef GINT_CORE_INTC #define GINT_CORE_INTC -#include +#include //--- // 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 */ diff --git a/include/core/pfc.h b/include/gint/mpu/pfc.h similarity index 93% rename from include/core/pfc.h rename to include/gint/mpu/pfc.h index bcee78b..8e17e3e 100644 --- a/include/core/pfc.h +++ b/include/gint/mpu/pfc.h @@ -8,7 +8,8 @@ #ifndef GINT_CORE_PFC #define GINT_CORE_PFC -#include +#include +#include //--- // 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)) diff --git a/include/mod/rtc.h b/include/gint/mpu/rtc.h similarity index 95% rename from include/mod/rtc.h rename to include/gint/mpu/rtc.h index b44215b..7d5fd50 100644 --- a/include/mod/rtc.h +++ b/include/gint/mpu/rtc.h @@ -5,6 +5,8 @@ #ifndef GINT_CORE_RTC #define GINT_CORE_RTC +#include + //--- // 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)) diff --git a/include/gint/rtc.h b/include/gint/rtc.h index fff6c5c..5f0dd54 100644 --- a/include/gint/rtc.h +++ b/include/gint/rtc.h @@ -5,7 +5,7 @@ #ifndef GINT_RTC #define GINT_RTC -#include +#include //--- // 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 diff --git a/include/gint/timer.h b/include/gint/timer.h index 7f325e7..064823b 100644 --- a/include/gint/timer.h +++ b/include/gint/timer.h @@ -5,9 +5,8 @@ #ifndef GINT_TIMER #define GINT_TIMER -#include -#include -#include +#include +#include /* 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 */ diff --git a/make/Makefile b/make/Makefile new file mode 100755 index 0000000..280c633 --- /dev/null +++ b/make/Makefile @@ -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) diff --git a/src/clock/freq.c b/src/clock/freq.c index 4ff270f..ca493d5 100644 --- a/src/clock/freq.c +++ b/src/clock/freq.c @@ -1,13 +1,12 @@ //--- -// gint:core:freq - Clock frequency management +// gint:clock:freq - Clock frequency management //--- #include #include -#include -#include -#include +#include +#include //--- // 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, diff --git a/src/clock/sleep.c b/src/clock/sleep.c index e69de29..caf298f 100644 --- a/src/clock/sleep.c +++ b/src/clock/sleep.c @@ -0,0 +1,18 @@ +//--- +// gint:clock:sleep - Various low-level sleep functions +//--- + +#include +#include + +/* 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(); +} diff --git a/src/core/bootlog.c b/src/core/bootlog.c index d0877aa..9683b7d 100644 --- a/src/core/bootlog.c +++ b/src/core/bootlog.c @@ -2,9 +2,11 @@ // gint:core:bootlog - Boot-time on-screen log for extreme debugging //--- -#include -#include -#include +/* TODO: Review, enhance and fix bootlog */ + +#include +#include +#include #include #include @@ -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(); diff --git a/src/core/gint.c b/src/core/gint.c index 8be9115..fba33bc 100644 --- a/src/core/gint.c +++ b/src/core/gint.c @@ -3,13 +3,13 @@ //--- #include -#include -#include #include +#include +#include /* 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); diff --git a/src/core/inth.S b/src/core/inth.S index 102ae52..7411838 100644 --- a/src/core/inth.S +++ b/src/core/inth.S @@ -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 */ diff --git a/src/core/memory.c b/src/core/memory.c index 3322120..41861cf 100644 --- a/src/core/memory.c +++ b/src/core/memory.c @@ -1,4 +1,5 @@ -#include +#include +#include #include #include @@ -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; } diff --git a/src/core/mmu.c b/src/core/mmu.c index c942c6e..cd0c889 100644 --- a/src/core/mmu.c +++ b/src/core/mmu.c @@ -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; diff --git a/src/core/mpu.c b/src/core/mpu.c index f571818..292d84b 100644 --- a/src/core/mpu.c +++ b/src/core/mpu.c @@ -2,15 +2,16 @@ // gint:core:mpu - Runtime MPU detection //--- -#include -#include -#include +#include +#include +#include +#include /* 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 diff --git a/src/core/setup.c b/src/core/setup.c index b830239..4e6d6ff 100644 --- a/src/core/setup.c +++ b/src/core/setup.c @@ -6,8 +6,8 @@ #include #include #include -#include -#include +#include +#include /* 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); diff --git a/src/core/start.c b/src/core/start.c index 13925b5..f445c17 100644 --- a/src/core/start.c +++ b/src/core/start.c @@ -2,13 +2,13 @@ // gint:core:start - Kernel initialization and C runtime //-- -#include -#include +#include +#include #include -#include #include #include #include +#include /* 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 */ diff --git a/src/font5x6.png b/src/font5x6.png new file mode 100644 index 0000000..dbe2b25 Binary files /dev/null and b/src/font5x6.png differ diff --git a/src/keysc/getkey.c b/src/keysc/getkey.c new file mode 100644 index 0000000..c945ed2 --- /dev/null +++ b/src/keysc/getkey.c @@ -0,0 +1,118 @@ +//--- +// gint:keysc:getkey - High-level keyboard monitoring function +//--- + +#include +#include + +#ifdef FX9860G +#include +#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; +} diff --git a/src/keysc/iokbd.c b/src/keysc/iokbd.c index dd0f8fb..3455fa5 100644 --- a/src/keysc/iokbd.c +++ b/src/keysc/iokbd.c @@ -2,8 +2,8 @@ // gint:keysc:iokbd - I/O-based keyboard input //--- -#include -#include +#include +#include /* 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 diff --git a/src/keysc/keysc.c b/src/keysc/keysc.c index f4e8d76..1c0c27d 100644 --- a/src/keysc/keysc.c +++ b/src/keysc/keysc.c @@ -5,10 +5,12 @@ #include #include #include +#include #include -#include -#include +#include +#include +#include //--- // 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); } diff --git a/src/r61524/r61524.c b/src/r61524/r61524.c index cf5960d..8d813ba 100644 --- a/src/r61524/r61524.c +++ b/src/r61524/r61524.c @@ -3,7 +3,7 @@ //--- #include -#include +#include #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, diff --git a/src/render-cg/dclear.c b/src/render-cg/dclear.c new file mode 100644 index 0000000..e69de29 diff --git a/src/render-fx/bopti-asm.h b/src/render-fx/bopti-asm.h new file mode 100644 index 0000000..daa3820 --- /dev/null +++ b/src/render-fx/bopti-asm.h @@ -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 */ diff --git a/src/render-fx/bopti-asm.s b/src/render-fx/bopti-asm.s new file mode 100644 index 0000000..6b5f1aa --- /dev/null +++ b/src/render-fx/bopti-asm.s @@ -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 diff --git a/src/render-fx/bopti.c b/src/render-fx/bopti.c new file mode 100644 index 0000000..3bbc225 --- /dev/null +++ b/src/render-fx/bopti.c @@ -0,0 +1,268 @@ +#define GINT_NEED_VRAM +#include +#include +#include +#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); +} diff --git a/src/render-fx/dclear.c b/src/render-fx/dclear.c new file mode 100644 index 0000000..ea5c4b1 --- /dev/null +++ b/src/render-fx/dclear.c @@ -0,0 +1,39 @@ +#define GINT_NEED_VRAM +#include + +/* 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; + } +} diff --git a/src/render-fx/dline.c b/src/render-fx/dline.c new file mode 100644 index 0000000..0347b41 --- /dev/null +++ b/src/render-fx/dline.c @@ -0,0 +1,136 @@ +#define GINT_NEED_VRAM +#include +#include +#include + +/* 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); +} diff --git a/src/render-fx/dpixel.c b/src/render-fx/dpixel.c new file mode 100644 index 0000000..aba5611 --- /dev/null +++ b/src/render-fx/dpixel.c @@ -0,0 +1,28 @@ +#define GINT_NEED_VRAM +#include +#include + +/* 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; + } +} diff --git a/src/render-fx/drect.c b/src/render-fx/drect.c new file mode 100644 index 0000000..927cf98 --- /dev/null +++ b/src/render-fx/drect.c @@ -0,0 +1,61 @@ +#define GINT_NEED_VRAM +#include +#include +#include + +/* 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; + } +} diff --git a/src/render-fx/dupdate.c b/src/render-fx/dupdate.c new file mode 100644 index 0000000..c6047de --- /dev/null +++ b/src/render-fx/dupdate.c @@ -0,0 +1,15 @@ +#define GINT_NEED_VRAM +#include +#include + +/* 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); +} diff --git a/src/render-fx/font-default.png b/src/render-fx/font-default.png new file mode 100644 index 0000000..91efa86 Binary files /dev/null and b/src/render-fx/font-default.png differ diff --git a/src/render-fx/masks.c b/src/render-fx/masks.c new file mode 100644 index 0000000..fcc228d --- /dev/null +++ b/src/render-fx/masks.c @@ -0,0 +1,28 @@ +#include + +/* 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); +} diff --git a/src/render-fx/topti-asm.h b/src/render-fx/topti-asm.h new file mode 100644 index 0000000..6211ccd --- /dev/null +++ b/src/render-fx/topti-asm.h @@ -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 */ diff --git a/src/render-fx/topti-asm.s b/src/render-fx/topti-asm.s new file mode 100644 index 0000000..28695a8 --- /dev/null +++ b/src/render-fx/topti-asm.s @@ -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 + diff --git a/src/render-fx/topti.c b/src/render-fx/topti.c new file mode 100644 index 0000000..6a9a3d1 --- /dev/null +++ b/src/render-fx/topti.c @@ -0,0 +1,252 @@ +#define GINT_NEED_VRAM +#include +#include +#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]); +} diff --git a/src/rtc/inth.s b/src/rtc/inth.s index 1cbc37a..270bf98 100644 --- a/src/rtc/inth.s +++ b/src/rtc/inth.s @@ -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 diff --git a/src/rtc/rtc.c b/src/rtc/rtc.c index 7b08671..9405e1b 100644 --- a/src/rtc/rtc.c +++ b/src/rtc/rtc.c @@ -5,10 +5,10 @@ #include #include #include -#include -#include -#include +#include +#include +#include //--- // 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), diff --git a/src/t6k11/t6k11.c b/src/t6k11/t6k11.c index f5d0eca..34ca1ea 100644 --- a/src/t6k11/t6k11.c +++ b/src/t6k11/t6k11.c @@ -3,11 +3,11 @@ //--- #include -#include +#include -#include -#include -#include +#include +#include +#include #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, diff --git a/src/tmu/inth.s b/src/tmu/inth.s index 1bc7a40..8e339ec 100644 --- a/src/tmu/inth.s +++ b/src/tmu/inth.s @@ -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 diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c index faa8d4d..279f0b2 100644 --- a/src/tmu/tmu.c +++ b/src/tmu/tmu.c @@ -7,10 +7,10 @@ #include #include -#include +#include -#include -#include +#include +#include //--- // 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),