Browse Source

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

pull/1/head
lephe 9 months ago
parent
commit
3f7c0a04ad
68 changed files with 2729 additions and 825 deletions
  1. 2
    9
      .gitignore
  2. 33
    146
      Makefile
  3. 94
    80
      README.md
  4. 52
    15
      TODO
  5. 159
    119
      configure
  6. 7
    1
      fx9860g.ld
  7. 7
    1
      fxcg50.ld
  8. 8
    1
      include/core/bootlog.h
  9. 1
    1
      include/core/memory.h
  10. 11
    15
      include/core/mmu.h
  11. 6
    6
      include/core/setup.h
  12. 0
    78
      include/defs/types.h
  13. 30
    0
      include/display/fx.h
  14. 1
    3
      include/gint/clock.h
  15. 5
    16
      include/gint/defs/attributes.h
  16. 48
    0
      include/gint/defs/types.h
  17. 67
    0
      include/gint/defs/util.h
  18. 21
    0
      include/gint/display-cg.h
  19. 110
    19
      include/gint/display-fx.h
  20. 29
    3
      include/gint/display.h
  21. 6
    6
      include/gint/drivers.h
  22. 17
    0
      include/gint/drivers/iokbd.h
  23. 2
    2
      include/gint/drivers/t6k11.h
  24. 12
    14
      include/gint/gint.h
  25. 130
    21
      include/gint/keyboard.h
  26. 86
    0
      include/gint/keycodes.h
  27. 4
    9
      include/gint/mpu.h
  28. 4
    3
      include/gint/mpu/cpg.h
  29. 9
    9
      include/gint/mpu/intc.h
  30. 3
    2
      include/gint/mpu/pfc.h
  31. 4
    2
      include/gint/mpu/rtc.h
  32. 11
    11
      include/gint/rtc.h
  33. 14
    5
      include/gint/timer.h
  34. 176
    0
      make/Makefile
  35. 13
    9
      src/clock/freq.c
  36. 18
    0
      src/clock/sleep.c
  37. 23
    21
      src/core/bootlog.c
  38. 6
    6
      src/core/gint.c
  39. 3
    3
      src/core/inth.S
  40. 4
    3
      src/core/memory.c
  41. 4
    4
      src/core/mmu.c
  42. 6
    5
      src/core/mpu.c
  43. 16
    28
      src/core/setup.c
  44. 20
    31
      src/core/start.c
  45. BIN
      src/font5x6.png
  46. 118
    0
      src/keysc/getkey.c
  47. 3
    2
      src/keysc/iokbd.c
  48. 46
    25
      src/keysc/keysc.c
  49. 6
    6
      src/r61524/r61524.c
  50. 0
    0
      src/render-cg/dclear.c
  51. 44
    0
      src/render-fx/bopti-asm.h
  52. 110
    0
      src/render-fx/bopti-asm.s
  53. 268
    0
      src/render-fx/bopti.c
  54. 39
    0
      src/render-fx/dclear.c
  55. 136
    0
      src/render-fx/dline.c
  56. 28
    0
      src/render-fx/dpixel.c
  57. 61
    0
      src/render-fx/drect.c
  58. 15
    0
      src/render-fx/dupdate.c
  59. BIN
      src/render-fx/font-default.png
  60. 28
    0
      src/render-fx/masks.c
  61. 15
    0
      src/render-fx/topti-asm.h
  62. 170
    0
      src/render-fx/topti-asm.s
  63. 252
    0
      src/render-fx/topti.c
  64. 1
    1
      src/rtc/inth.s
  65. 15
    11
      src/rtc/rtc.c
  66. 15
    9
      src/t6k11/t6k11.c
  67. 8
    8
      src/tmu/inth.s
  68. 69
    56
      src/tmu/tmu.c

+ 2
- 9
.gitignore View File

@@ -1,5 +1,5 @@
# Build directory
build/**
# Build directories
build*/**

# Sublime Text files
*.sublime-project
@@ -7,10 +7,3 @@ build/**

# Lots of unordered project notes
notes/**

# Output files
bin/**

# Configuration files
config/**


+ 33
- 146
Makefile View File

@@ -1,172 +1,59 @@
#! /usr/bin/make -f
#
# gint project Makefile
#
#---

#
# Build configuration
#

# 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)
builds := $(wildcard build*)

# 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 :=
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

#
# File listings
# all targets
#

# 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
all-targets := $(foreach b,$(builds),all-$b)

# Source files
src := $(shell find src -name '*.[csS]')
src_obj := $(foreach s,$(src),$(call src2obj,$s))
all: $(all-targets)

# 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
all-build%: build%
@ echo -e "$B::$W Making into $<$N"
@ $(MAKE) --no-print-directory -C $<

#
# Toolchain
# install targets
#

gcc = $(toolchain)-gcc
as = $(toolchain)-as
ld = $(toolchain)-ld
ar = $(toolchain)-ar
objcopy = $(toolchain)-objcopy
conv = fxconv
install-targets := $(foreach b,$(builds),install-$b)

install: $(install-targets)

#
# 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))
install-build%: build%
@ echo -e "$B::$W Installing from $<$N"
@ $(MAKE) --no-print-directory -C $< install

#
# Build rules
# uninstall targets
#

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)
uninstall-targets := $(foreach b,$(builds),uninstall-$b)

# 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 $@
uninstall: $(uninstall-targets)

# 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 $@
uninstall-build%: build%
@ echo -e "$B::$W Uninstalling from $<$N"
@ $(MAKE) --no-print-directory -C $< uninstall

#
# Cleaning
# Coloring tools
#

clean:
@ rm -rf build/
distclean: clean
@ rm -rf bin/
@ rm -f $(config)

#
# Utilities
#

$(version): $(src) $(wildcard include/**/*)
@ echo '$(v_type)-$(v_major).$(v_minor)-$(v_newbuild)' > $@

# Evaluated when a rule requires the configuration file but it doesn't exist
CFGDEP:
@ echo "Configuration file $(cfg) is missing. Have you configured?"
@ echo "See \`./configure --help\` for details."
@ false
B = \e[34;1m
W = \e[39;1m
N = \e[0m

# 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: %/

# Dependency information
-include $(shell [ -d build ] && find build -name *.d)
build/%.d: ;
.PRECIOUS: build/%.d

# 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)
.PHONY: nobuild all all-build% install install-build%

+ 94
- 80
README.md View File

@@ -1,98 +1,112 @@
gint project
============
# gint project

gint (pronounce 'guin') is a low-level library for fx-9860G calculators. It
provides a mostly free-standing runtime support for the platform, and can be
used to develop add-ins under Linux along with the gcc toolchain (`sh3eb-elf`
target) and the [fxSDK](http://git.planet-casio.com/lephe/fxsdk).
gint (pronounce 'guin') is a development system for Casio fx-9860G II and
fx-CG 50 calculators. It provides a mostly free-standing runtime and is used
to develop add-ins under Linux, along with specialized GCC toolchains and the
[fxSDK](http://git.planet-casio.com/lephe/fxsdk).

gint is free software: you may use it for any purpose, share it, modify it and
share your changes. No credit of any kind is required, though appreciated.
gint is a modular kernel that implements its own drivers for the calculator's
hardware, overriding the operating system and its syscalls. It is a drop-in
replacement from fxlib, with which it is mostly incompatible. gint exposes a
new, richer API to manipulate the hardware and take advantage of the full
capabilities of the machine.

This is free software: you may use it for any purpose, share it, and modify it
as long as you share your changes. Credit is not required, but please let me
know!

**TODO: Update this file for everything related to project organization**


Programming interface
---------------------
## Programming interface

Because of its free-standing design, gint's API provides direct and efficient
access to the low-level MPU features, among which:
* Extensive keyboard control, including replication of the system behavior for
office applications and event-driven decisions for games
* Hardware timers running at over 10 MHz, allowing microsecond-level control
* Unlimited mappings of callbacks to Real-Time Clock events (requires build)
* Access to processor registers for debugging information, determination of
processor speed (overclock is on the TODO list), backlight management...

* Multi-key management with event systems suitable for games
* Hardware timers with sub-millisecond and sub-microsecond resolution
* Fast screen drivers with DMAC on fx-CG 50
* Efficient and user-extendable interrupt management

The library also offers powerful higher-level features:
* A gray engine that works by rapidly swapping monochrome images
* Blazingly fast drawing functions when working with the fxSDK (10 times faster
image rendering that MonochromeLib)
* C Standard functions such as the `printf()` family

* An enhanced versio of the system's GetKey() and GetKeyWait()
* A gray engine that works by rapidly swapping monochrome images on fx-9860G II
* Blazingly fast drawing functions when working with the fxSDK (image rendering
is 10 times faster than MonochromeLib)
* Integrated font management with the fxSDK
* Integration with a Newlib port by Memallox (WIP)

Interrupt handler
-----------------

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.
## Building and installing gint

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).
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:

* 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. 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.

Building and installing
-----------------------
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.

To build and install gint, you will need the following components:
* The `sh3eb-elf` toolchain linked somewhere in the PATH
* The [fxSDK](http://git.planet-casio.com/lephe/fxsdk) installed and available
in the PATH

The classical way to build gint is to enter a terminal and use the usual:

$ ./configure
$ make
# make install

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

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

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...).


Source organization
-------------------

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.

The demo application is in the `demo` folder.
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.

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.

### Building for fx-9860G II

Create a build directory and configure in it:

% mkdir build.fx && cd build.fx
% ../configure --target=fx9860g

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.

% make
% make install

### 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.

+ 52
- 15
TODO View File

@@ -1,15 +1,61 @@
# Since compatibility

Another, more recent list of things to do:
- Complete memory functions in [core/memory.c]
- Finish the boot log in <core/bootlog.h>
- Finish the boot log in [core/bootlog.c]
- Document the SH7305 PFC in <gint/mpu/pfc.h>
- Change the description of the version number in <gint/gint.h>
- Define the version number from full Git info
- Do this display in <gint/display-fx.h> and <gint/display-cg.h>
- Remove all mentions of GINT_LAX in the code
- Write exception handlers
- Do overclock in [clock/]
- Test getkey()

* Make keysc/keysc.c/state static and review forgotten global variables
* Find out what happened to the clock frequency on fxcg50
* Implement profiling
* Save and restore interrupt masks
* More keyboard

Strange, (irreproducible?) SysERRORs on SH3?
- Happened when dealing with overclock and after a transfer
- SysERROR when add-in fully returns, nothing during execution
- Affects all add-ins of the calculator!

* Make sure interrupt for extra timers is masked when you ctx_restore to avoid
interrupts

* For modules, use #define TMU ((tmu_t *)0xdeadbeef)) instead of tmu_t *TMU
* Don't hardcode fx-cg50 stack address in fxcg50.ld for Prizm compatibility
* Implement the gint_switch()
* File management
* Drawing functions, bopti and tales
* Gray engine
* Overclock
* Add a driver init function to the r61524 driver.
* Finish the bootlog and use the keyboard to control it.
* Save driver contexts on the stack in gint_pause().
* Write the core memory functions.
- Allow arbitrary code in gint_pause() instead of just main menu?
- Dynamically detect P1 static RAM in TLB for runtime Prizm compatibility.
* Use a solid API for boot-time printing (or just use fxlib)
- Use qdiv10() (there should be no __sdivsi3 in gint)
(bootlog, timer_delay(), getkey_repeat(), int2bcd())
* Load an exception handler and a TLB miss handler.

# Before compatibility

Bugs to fix:
- Alignment of ALL .data / .rodata files is required to ensure converted data
is properly aligned
- Unpreditcable crap happens when more than 10 keys are pressed simultaneously
- Ensure heap data is freed when a task-switch results in leaving the app
is properly aligned -> Ok using ALIGN(4)
- Ensure heap data is freed when a task-switch results in leaving the app (?)

Things to do before 1.0:
- bopti: Test partial transparency
- demo: Try 284x124 at (-60, -28) (all disadvantages)
- perf: Try 284x124 at (-60, -28) (all disadvantages)
- project: Check size of *all* library structures
- project: Clean headers that have some internal definitions
- project: Get rid of 7705.h (keyboard) and 7305.h (keyboard, gint)
- time: Compute CLOCKS_PER_SEC

Things to do later:
@@ -17,12 +63,8 @@ Things to do later:
- clock: Only measure if requires as option, otherwise trust {FTune}
- clock: Handle overclock (relaunch clocks when overclocking)
- clock: Split code into several files, change clock_config_t type
- core: Change interrupt priority using the gint API
- core: Register more interrupts (and understand their parameters)
- core: Remove redundant code linked to environment saves
- core: Review interrupt system (again) - this one is too slow
- display: Try to make this module lighter (lots of code in text section)
- errno: Introduce errno and use it more or less everywhere
- esper: Cleaner playback, synthesizing
- events: Allow customization of keyboard event system (option to return
| events with modifiers, etc)
@@ -34,14 +76,10 @@ Things to do later:
- stdio: More serious formatted printing functions and headers
- string: Use cmp/str to implement memchr() (assembler examples)
- string: Do some tests for memcmp() and memcpy()
- string: Lighter functions?
- usb: Implement a driver

Things to investigate:
- Registers that may need to be saved within setjmp()
- Optimizing core/gint.c leads to raising of an illegal slot exception when
running the interrupt handler, although it ends on rte; lds.l @r15+, mach,
which is totally not an illegal slot.

Possibly useful modules:
- DMAC
@@ -49,4 +87,3 @@ Possibly useful modules:
- TPU
- USB
- CMT on SH7305, WDT on SH7705
- ACD (SH7705 only)

+ 159
- 119
configure View File

@@ -4,132 +4,170 @@
# Basic configuration
#

declare -A conf

# Target platform
conf_target=
target=
toolchain=

# Build options
prefix=
cflags=

# Behavior
conf[GINT_BOOT_LOG]=
conf[GINT_NO_SYSCALLS]=
cong[GINT_LAX]=
conf[GINT_EXTENDED_LIBC]=
conf[GINT_STATIC_GRAY]=
boot_log=
no_syscalls=
static_gray=

# Size limits
conf[ATEXIT_MAX]=16
conf[TIMER_SLOTS]=16
conf[EVENTS_QUEUE_SIZE]=64
atexit_max=

# Output files
output="config/Makefile.cfg"
output="Makefile.cfg"

#
# Help screen and output util
# Help screen
#

error="\e[31;1merror:\e[0m"
C_="$(echo -e '\e[30;1m')"
Cr="$(echo -e '\e[31;1m')"
Cg="$(echo -e '\e[32;1m')"
Cp="$(echo -e '\e[34;1m')"
C0="$(echo -e '\e[0m')"

help()
{
cat << EOF
cat << EOF
Configuration script for the gint library.
Usage: $0 [options...]

Platform settings (specify exactly one):
${Cg}-fx9860g$C0
Target platform is fx-9860G II: all monochrome models that support add-ins
or can be flashed to support them.
${Cg}-fxcg50$C0
Target platform is fx-CG 50; there is some compatibility with fx-CG 10/20.

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.

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.
You should build out-of-tree by creating a build directory and configuring from
there.

Target selection:
--target=fx9860g|fxcg50

fx9860g covers all fx-9860G II-like monochromes models that support add-ins
or can be flashed with an OS that does. This includes SH3 and SH4 machines.
Default toolchain is 'sh3eb-elf'.

fxcg50 covers just the fx-CG 50; there is some unofficial compatibility with
fx-CG 10/20. All of these are SH4-only.
Default toolchain is 'sh4eb-nofpu-elf'.

Build options:
--toolchain=TRIPLET Build with a different toolchain
--prefix=PREFIX Install prefix (PREFIX/lib and PREFIX/include are used)
--cflags=FLAGS Additional compiler flags at end of command

Library options (disabled by default):
--boot-log Display a boot debug log at startup if a key is pressed
Used to investigate unstabilities.
--no-syscalls Cut off all syscalls (this will break things)
Only use this option if you have a good idea of which.
--static-gray Allocate gray VRAMs in static RAM instead of the heap
May help when --no-syscalls is on.

Size limits:
--atexit-max=NUM Number of exit handlers in atexit()'s array [16]

Deprecated options (to be removed):
-extended-libc Provide a few C99 headers (in the future, refer to the
newlib port by Memallox's for the standard library)
-timer-slots=<n> Number of virtual timer slots (this feature will be
moved to an independent library) [16]
-events-queue-size=<n>
Size of event queue (this mechanism is likely to
disappear in future versions) [64]
EOF

exit 0
exit 0
}

if [[ "$@" == "--help" ]]; then
help
exit 1
fi

if [[ -f make/Makefile ]]; then
echo "error: you should configure from a build directory, like this:" >&2
echo " mkdir build && cd build && ../configure [options..]" >&2
exit 1
fi

#
# Parsing arguments
#

fail=false
for arg; do case "$arg" in
-h | -? | --help) help;;

-fx9860g)
conf_target="FX9860G";;
-fxcg50)
conf_target="FXCG50";;

-boot-log) conf[GINT_BOOT_LOG]=true;;
-no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
-lax) conf[GINT_LAX]=true;;
-extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
-static-gray-engine) conf[GINT_STATIC_GRAY]=true;;

-atexit-max=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
conf[ATEXIT_MAX]=$size
else echo -e "$error -atexit-max expects an integer value"
fail=true; fi;;
-timer-slots=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
conf[TIMER_SLOTS]=$size
else echo -e "$error -timer-slots expects an integer value"
fail=true; fi;;
-events-queue-size=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
conf[EVENTS_QUEUE_SIZE]=$size
else echo -e "$error -events-queue-size expects an integer"\
"value"
fail=true; fi;;

-atexit-max | -timer-slots | -events-queue-size)
echo -e "$error syntax for $arg is $arg=<integer-value>";;

*)
echo -e "$error unrecognized argument '$arg'"; fail=true;;
-h | -? | --help)
help;;

--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;;

--prefix=*)
prefix=${arg#*=};;
--cflags=*)
cflags=${arg#*=};;

--boot-log)
boot_log=true;;
--no-syscalls)
no_syscalls=true;;
--static-gray)
static_gray=true;;

--atexit-max=*)
n=${arg#*=}
if [[ $n == +([0-9]) ]]; then
atexit_max=$n
else
echo -e "error: -atexit-max expects an integer value"
fail=true
fi;;

--atexit-max)
echo "error: '$arg' expects a value (see '$0 --help')";
fail=true;;

-extended-libc)
echo "warning: support for '-extended-libc' has been removed";;
-timer-slots=*)
echo "warning: support for '-timer-slots' has been removed";;
-events-queue-size=*)
echo "warning: support for '-events-queue-size' has been removed";;
esac; done

#
# 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"

echo -n "cfg_macros ="
echo -n " -D$conf_target"
mod=${target/98/fx}
echo "CONFIG.TARGET = ${mod:2:2}"
echo "CONFIG.TARGET.LONG = $target"

[[ $prefix ]] && echo "PREFIX = $prefix"
[[ $toolchain ]] && echo "CONFIG.TOOLCHAIN = $toolchain"
[[ $cflags ]] && echo "CONFIG.CFLAGS = $cflags"

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 ""
}

[ "${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"
if $fail; then
echo "note: output file $output has not been changed."
exit 1
fi

echo -n " -DATEXIT_MAX=${conf[ATEXIT_MAX]}"
echo -n " -DTIMER_SLOTS=${conf[TIMER_SLOTS]}"
echo -n " -DEVENTS_QUEUE_SIZE=${conf[EVENTS_QUEUE_SIZE]}"
output_config > $output

echo ""
src="Makefile"
dst="../make/Makefile"

[ "${conf[GINT_EXTENDED_LIBC]}" != "" ] && echo "cfg_ext = true"
}
[[ -L $src && $(readlink $src) == $dst ]] && rm $src
ln -s $dst $src

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!"
fi
echo "Configuration saved in $output, ready to make!"

+ 7
- 1
fx9860g.ld View File

@@ -130,7 +130,13 @@ SECTIONS
. = ALIGN(16);
} > ram AT> rom

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

_sdata = SIZEOF(.data) + SIZEOF(.data.4);




+ 7
- 1
fxcg50.ld View File

@@ -118,7 +118,13 @@ SECTIONS
. = ALIGN(16);
} > ram AT> rom

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

_sdata = SIZEOF(.data) + SIZEOF(.data.4);




+ 8
- 1
include/core/bootlog.h View File

@@ -1,5 +1,5 @@
//---
// gint:core:bootlog - Boot-time on-screen log for extreme debugging
// core:bootlog - Boot-time on-screen log for extreme debugging
//---

#ifndef GINT_CORE_BOOTLOG
@@ -21,4 +21,11 @@ void bootlog_mapped(uint32_t rom, uint32_t ram);
handlers set up. */
void bootlog_kernel(void);

/* All these functions are enabled only if GINT_BOOT_LOG is defined */
#ifndef GINT_BOOT_LOG
#define bootlog_loaded(...)
#define bootlog_mapped(...)
#define bootlog_kernel(...)
#endif

#endif /* GINT_CORE_BOOTLOG */

+ 1
- 1
include/core/memory.h View File

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

+ 11
- 15
include/core/mmu.h View File

@@ -1,23 +1,21 @@
//---
// gint:core:mmu - MMU-related definitions
// core:mmu - MMU-related definitions
//
// gint does not touch the MMU because the risk of interfering with the
// system is deemed too high. However, to ensure that the add-in runs
// properly, checks using read-only access to the MMU are performed.
// gint does not touch the MMU because the risk of permanently wreaking
// the system is deemed too high. However, to ensure that the add-in runs
// properly, checks using read-only accesses to the MMU are performed.
//---

#ifndef GINT_CORE_MMU
#define GINT_CORE_MMU

#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>

//---
// SH7705 TLB
//---

#ifdef FX9860G

/* tlb_addr_t - address part of a TLB entry */
typedef struct
{
@@ -26,7 +24,7 @@ typedef struct
uint V :1;
uint ASID :8;

} PACKED(4) tlb_addr_t;
} GPACKED(4) tlb_addr_t;

/* tlb_data_t - data part of a TLB entry */
typedef struct
@@ -43,7 +41,7 @@ typedef struct
uint SH :1;
uint :1;

} PACKED(4) tlb_data_t;
} GPACKED(4) tlb_data_t;

/* tlb_addr() - get the P4 address of a TLB address entry
@way TLB way (0..3)
@@ -57,7 +55,7 @@ const tlb_addr_t *tlb_addr(uint way, uint E);
Returns a pointer to the entry. */
const tlb_data_t *tlb_data(uint way, uint E);

/* tlb_mapped_memort() - count amount of mapped memory
/* tlb_mapped_memory() - count amount of mapped memory
This function returns the amount of mapped text and data segment memory, in
bytes. The ranges are defined as follows:
ROM 00300000:512k
@@ -68,8 +66,6 @@ const tlb_data_t *tlb_data(uint way, uint E);
@ram Pointer to amount of mapped RAM */
void tlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram);

#endif

//---
// SH7305 Unified TLB
//---
@@ -82,7 +78,7 @@ typedef struct
uint V :1;
uint ASID :8;

} PACKED(4) utlb_addr_t;
} GPACKED(4) utlb_addr_t;

/* utlb_addr() - get the P4 address of a UTLB address entry
@E Entry number (should be in range 0..63)
@@ -104,7 +100,7 @@ typedef struct
uint SH :1;
uint WT :1;

} PACKED(4) utlb_data_t;
} GPACKED(4) utlb_data_t;

/* utlb_data() - get the P4 address of a UTLB data entry
@E Entry number (should be in range 0..63)

+ 6
- 6
include/core/setup.h View File

@@ -1,19 +1,19 @@
//---
// gint:core:setup - Installing and unloading the library
// core:setup - Installing and unloading the library
//---

#ifndef GINT_CORE_SETUP
#define GINT_CORE_SETUP

#include <defs/types.h>
#include <gint/defs/types.h>

/* Prototypes for the library management functions are in <gint/gint.h> */

/* gint_setvbr()
Changes the VBR address and the calls the configuration function while
interrupts are disabled. The configuration function must change either the
priority registers or the interrupt masks, and make sure that all the
interrupts that it leaves enabled are handled by the new VBR handlers.
Changes the VBR address and calls the configuration function while
interrupts are disabled. The configuration function must disable all
interrupts that the new handlers cannot handle, either by clearing the
priority registers or by setting the interrupt masks.

@vbr New VBR address
@configure Configuration function

+ 0
- 78
include/defs/types.h View File

@@ -1,78 +0,0 @@
//---
// gint:defs:types - Type-related macros
//---

#ifndef GINT_DEFS_TYPES
#define GINT_DEFS_TYPES

#include <defs/attributes.h>
#include <stddef.h>
#include <stdint.h>

//---
// Const casts
//---

/* const_cast() - perform const casts
This is intended for initialization purposes only, like "final" in Java.
This macro is the most generic form without any type inference; using
const_cint() or const_cptr() may be more convenient for simple types */
#define const_cast(x, T) (*((T *)(&(x))))

/* const_cptr() - perform const casts on pointers */
#define const_cptr(x) (*((void **)(&(x))))

/* const_cint() - perform const casts on integers
This macro saves you from specifying the integral type that you're
manipulating, which may avoid errors if the type changes. It will only work
with the primitive types that are either mentioned in the following list or
aliased as one of the listed types.
This is to prevent unforeseen effects of tricks such as typeof(x + 0) that
promotes various small integers to ints */
#define const_cint(x) (*_Generic((x), \
char: (char *) (&(x)), \
unsigned char: (unsigned char *) (&(x)), \
short: (short *) (&(x)), \
unsigned short: (unsigned short *) (&(x)), \
int: (int *) (&(x)), \
unsigned int: (unsigned int *) (&(x)), \
long: (long *) (&(x)), \
unsigned long: (unsigned long *) (&(x)), \
long long: (long long *) (&(x)), \
unsigned long long: (unsigned long long *) (&(x)) \
))

//---
// Structure elements
//----

/* Fixed-width types for bit fields are entirely meaningless */
typedef unsigned int uint;

/* Giving a type to padding bytes is misguiding */
#define pad_nam2(c) _ ## c
#define pad_name(c) pad_nam2(c)
#define pad(bytes) uint8_t pad_name(__COUNTER__)[bytes]

/* byte_union() - union between an uint8_t 'byte' element and a bit field */
#define byte_union(name, fields) \
union { \
uint8_t byte; \
struct { fields } PACKED(1); \
} PACKED(1) name

/* word_union() - union between an uint16_t 'word' element and a bit field */
#define word_union(name, fields) \
union { \
uint16_t word; \
struct { fields } PACKED(2); \
} PACKED(2) name

/* lword_union() - union between an uint32_t 'lword' element and a bit field */
#define lword_union(name, fields) \
union { \
uint32_t lword; \
struct { fields } PACKED(4); \
} PACKED(4) name

#endif /* GINT_DEFS_TYPES */

+ 30
- 0
include/display/fx.h View File

@@ -0,0 +1,30 @@
//---
// display:fx - Internal definitions for the display module on fx9860g
//---

#ifndef DISPLAY_FX
#define DISPLAY_FX

#include <gint/defs/types.h>
#include <gint/display.h>

/* masks() - compute the vram masks for a given rectangle

Since the VRAM is line-based with four uin32_t elements per row, we can
execute any operation on a rectangle by running it on each set of four
uint32_t elements.

This function calculates four uint32_t values and stores them in @mask. Each
of the 128 bits in this array represents a column of the screen, and the bit
of column c is 1 iff x1 <= c <= x2.

These masks can then be and-ed/or-ed/anything on the VRAM to draw.

@x1 @x2 Targeted screen range, horizontally (both included)
@masks Stores the result of the function (four uint32_t values) */
void masks(int x1, int x2, uint32_t *masks);

/* Font currently configured for text rendering */
extern font_t const * topti_font;

#endif /* DISPLAY_FX */

+ 1
- 3
include/gint/clock.h View File

@@ -5,8 +5,6 @@
#ifndef GINT_CLOCK
#define GINT_CLOCK

#include <defs/types.h>

//---
// Clock signals
//---
@@ -59,7 +57,7 @@ const clock_frequency_t *clock_freq(void);
The function stops the processor until an interrupt is accepted; the
duration is not known in advance. This function should be used when the
add-in is idle, for instance while waiting for keyboard input. */
void sleep(void);
#define sleep() __asm__("sleep")

/* sleep_us() - sleep for a definite duration in microseconds


include/defs/attributes.h → include/gint/defs/attributes.h View File

@@ -5,37 +5,26 @@
#ifndef GINT_DEFS_ATTRIBUTES
#define GINT_DEFS_ATTRIBUTES

/* Generic attribute element */
#define ATTR(...) __attribute__((__VA_ARGS__))

/* Objects from specific sections */
#define SECTION(x) __attribute__((section(x)))
#define GSECTION(x) __attribute__((section(x)))
/* Objects from the .gint.data and .gint.bss sections */
#define GDATA __attribute__((section(".gint.data")))
#define GBSS __attribute__((section(".gint.bss")))
/* Additional sections that are only needed on SH3 */
#define GDATA3 __attribute__((section(".gint.data.sh3")))
#define GBSS3 __attribute__((section(".gint.bss.sh3")))
/* Initialization functions */
#define PRETEXT __attribute__((section(".pretext")))

/* Unused parameters or variables */
#define UNUSED __attribute__((unused))
#define GUNUSED __attribute__((unused))
/* Functions that *must* be inlined */
#define INLINE __attribute__((always_inline)) inline
/* Short static functions defined in header files */
#define HDRFUNC __attribute__((always_inline)) static inline

/* Constructors and destructors */
#define CTOR(x) __attribute__((constructor ((x) + 101))) PRETEXT
#define DTOR(x) __attribute__((destructor ((x) + 101)))
#define GINLINE __attribute__((always_inline)) inline

/* Aligned variables */
#define ALIGNED(x) __attribute__((aligned(x)))
#define GALIGNED(x) __attribute__((aligned(x)))

/* Packed structures. I require explicit alignment because if it's unspecified,
GCC cannot optimize access size, and reads to memory-mapped I/O with invalid
access sizes silently fail - honestly you don't want this to happen */
#define PACKED(x) __attribute__((packed, aligned(x)))
#define GPACKED(x) __attribute__((packed, aligned(x)))

#endif /* GINT_DEFS_ATTRIBUTES */

+ 48
- 0
include/gint/defs/types.h View File

@@ -0,0 +1,48 @@
//---
// gint:defs:types - Type definitions
//---

#ifndef GINT_DEFS_TYPES
#define GINT_DEFS_TYPES

#include <gint/defs/attributes.h>

/* For size_t, mainly */
#include <stddef.h>
/* For all fixed-width integer types */
#include <stdint.h>

/* Fixed-width types for bit fields are quite meaningless */
typedef unsigned int uint;

//---
// Structure elements
//----

/* Giving a type to padding bytes is misguiding, let's hide it in a macro */
#define pad_nam2(c) _ ## c
#define pad_name(c) pad_nam2(c)
#define pad(bytes) uint8_t pad_name(__COUNTER__)[bytes]

/* byte_union() - union between an uint8_t 'byte' element and a bit field */
#define byte_union(name, fields) \
union { \
uint8_t byte; \
struct { fields } GPACKED(1); \
} GPACKED(1) name

/* word_union() - union between an uint16_t 'word' element and a bit field */
#define word_union(name, fields) \
union { \
uint16_t word; \
struct { fields } GPACKED(2); \
} GPACKED(2) name

/* lword_union() - union between an uint32_t 'lword' element and a bit field */
#define lword_union(name, fields) \
union { \
uint32_t lword; \
struct { fields } GPACKED(4); \
} GPACKED(4) name

#endif /* GINT_DEFS_TYPES */

+ 67
- 0
include/gint/defs/util.h View File

@@ -0,0 +1,67 @@
//---
// gint:defs:util - Various utility macros
//---

/* min(), max() (without double evaluation) */
#define min(a, b) ({ \
__auto_type _a = (a); \
__auto_type _b = (b); \
_a < _b ? _a : _b; \
})
#define max(a, b) ({ \
__auto_type _a = (a); \
__auto_type _b = (b); \
_a > _b ? _a : _b; \
})

/* sgn() (without double evaluation) */
#define sgn(s) ({ \
__auto_type _s = (s); \
_s < 0 ? -1 : \
_s > 0 ? +1 : \
0; \
})

/* abs() (without double evaluation) */
#define abs(s) ({ \
__auto_type _s = (s); \
_s < 0 ? -s : s; \
})

/* swap() - exchange two variables of the same type */
#define swap(a, b) ({ \
__auto_type _tmp = (a); \
(a) = (b); \
(b) = _tmp; \
})

//---
// Const casts
//---

/* const_cast() - perform const casts
This is intended for initialization purposes only, like "final" in Java.
This macro is the most generic form without any type inference; using
const_cint() or const_cptr() may be more convenient for simple types */
#define const_cast(x, T) (*((T *)(&(x))))

/* const_cptr() - perform const casts on pointers */
#define const_cptr(x) (*((void **)(&(x))))

/* const_cint() - perform const casts on integers
This macro infers the integer type it's using, which may avoid errors when
types change during code maintenance. It only works on primitive types and
their aliases, this is on purpose to avoid integer coercion with tricks such
as typeof(x + 0). */
#define const_cint(x) (*_Generic((x), \
char: (char *) (&(x)), \
unsigned char: (unsigned char *) (&(x)), \
short: (short *) (&(x)), \
unsigned short: (unsigned short *) (&(x)), \
int: (int *) (&(x)), \
unsigned int: (unsigned int *) (&(x)), \
long: (long *) (&(x)), \
unsigned long: (unsigned long *) (&(x)), \
long long: (long long *) (&(x)), \
unsigned long long: (unsigned long long *) (&(x)) \
))

+ 21
- 0
include/gint/display-cg.h View File

@@ -0,0 +1,21 @@
//---
// gint:display-cg - fxcg50 rendering functions
//
// This module covers all 16-bit opaque rendering functions. For
// gamma-related functions, color composition, check out a color library.
//
//---

#ifndef GINT_DISPLAY_CG
#define GINT_DISPLAY_CG

#ifdef FXCG50

/* Screen dimensions on fxcg50 - never mind the borders, gint lets you use the
full surface! */
#define DWIDTH 396
#define DHEIGHT 224

#endif /* FXCG50 */

#endif /* GINT_DISPLAY_CG */

+ 110
- 19
include/gint/display-fx.h View File

@@ -11,16 +11,14 @@

#ifdef FX9860G

/* Screen dimensions on fx9860g */
#define DWIDTH 128
#define DHEIGHT 64
#include <gint/defs/types.h>

/* color_t - colors available for drawing
The following colors are defined by the library:

OPAQUE COLORS (override existing pixels)
white, black - the usual thing
light, dark - additional colors used by the gray engine
light, dark - intermediate colors used with the gray engine

OPERATORS (combine with existing pixels)
none - leaves unchanged
@@ -69,10 +67,6 @@ void dclear(color_t color);
This functions applies a color or an operator to a rectangle defined by two
points (x1 y1) and (x2 y2). Both are included in the rectangle.

If GINT_LAX is defined, this function makes the following assumptions:
0 <= x1 < x2 <= 127
0 <= y1 < y2 <= 63

@x1 @y1 @x2 @y2 Bounding rectangle (drawn area).
@color Allowed colors: white, black, none, reverse */
void drect(int x1, int y1, int x2, int y2, color_t color);
@@ -83,11 +77,10 @@ void drect(int x1, int y1, int x2, int y2, color_t color);

/* dpixel() - change a pixel's color
If the requested color is an operator, the result will depend on the current
color of the pixel.

If GINT_LAX is defined, this function makes the following assumptions:
0 <= x <= 127
0 <= y <= 63
color of the pixel. Setting single pixels is the slowest method to produce a
graphical result: all other functions for rendering lines, rectangles,
images or text use highly-optimized methods, so check them out first if you
care about performance.

@x @y Coordinates of the pixel to repaint
@color Allowed colors: white, black, none, reverse */
@@ -98,16 +91,114 @@ void dpixel(int x, int y, color_t color);
that the affected pixels may not be exactly the same when using dline() and
Bdisp_DrawLineVRAM().

dline() has optimization facilities for horizontal and vertical lines.
dline() has optimization facilities for horizontal and vertical lines, but
it does not detect if your line doesn't fit in the screen. So drawing from
(-1e6,0) to (1e6,63) will work, but will be veeery slow.

If GINT_LAX is defined, this function makes the following assumptions:
0 <= x1 <= x2 <= 127
0 <= y1, y2 <= 63

@x1 @y1 @x2 @y2 End points of theline (both included).
@x1 @y1 @x2 @y2 End points of the line (both included).
@color Allowed colors: white, black, none, reverse */
void dline(int x1, int y1, int x2, int y2, color_t color);

//---
// Image rendering (bopti)
//---

/* image_t - image files encoded for bopti
This format is the result of encoding images for bopti with the fxSDK's
[fxconv] tool. The bopti routines can render it extremely fast, which makes
it preferable over plain bitmaps if the images are never edited. */
typedef struct
{
/* Image can only be rendered with the gray engine */
uint gray :1;
/* Left for future use */
uint :3;
/* Image profile (uniquely identifies a rendering function) */
uint profile :4;
/* Full width, in pixels */
uint width :12;
/* Full height, in pixels */
uint height :12;

/* Raw layer data */
uint8_t data[];

} GPACKED(4) image_t;

//---
// Text rendering (topti)
//---

/* font_t - font data encoded for topti */
typedef struct
{
/* Length of font name (not NUL-terminated) */
uint title :5;
/* Font shape flags */
uint bold :1;
uint italic :1;
uint serif :1;
uint mono :1;
/* Whether data is variable-length (proportional font) */
uint prop :1;
/* Reserved for future use */
uint :2;
/* Implemented charcter set */
uint charset :4;
/* Line height */
uint8_t line_height;
/* Storage height */
uint8_t data_height;

/* The rest of the data depends on whether the font is proportional */
union {
/* For monospaced fonts */
struct {
/* Width of glyphs */
uint16_t width;
/* Storage size, in longwords, of each glyph */
uint16_t storage_size;
/* Raw glyph data */
uint32_t data[];
};
/* For proportional fonts */
struct {
/* Storage index to find glyphs quickly */
uint16_t index[16];
/* Size array (padded to 4 bytes), 1 byte per entry,
followed by glyph data */
uint8_t sized_data[];
};
};

/* The font name is stored after the data. The size is the length set
in the [title] field, padded to 4 bytes with NULs. There might not
be a NUL at the end. */

} GPACKED(4) font_t;

/* dfont() - set the default font for text rendering
This font will be used by dtext() and sister functions. If font=NULL, gint's
default 5*6 font is used.

@font Font to use for subsequent text rendering calls */
void dfont(font_t const * font);

/* dtext() - display a string of text

Draws some text in the video RAM using the font set with dfont() (or gint's
default if no such font was set).

Due to the particular design of topti, this function takes advantage of the
line structure of the VRAM to rendeer several characters at once. This is
not a printf()-family function so [str] cannot contain formats like "%d"
(they will be rendered directly) and cannot receive additional arguments.

@x @y Coordinates of top-left corner of the rendered string
@str String to display
@color Which color to use */
void dtext(int x, int y, const char *str, color_t color);

#endif /* FX9860G */

#endif /* GINT_DISPLAY_FX */

+ 29
- 3
include/gint/display.h View File

@@ -5,6 +5,8 @@
#ifndef GINT_DISPLAY
#define GINT_DISPLAY

#include <gint/defs/types.h>

/* Expose the VRAM variable if GINT_NEED_VRAM is defined. This address is used
as the VRAM basis by all of gint's drawing functions, and must point to a
suitable buffer:
@@ -13,15 +15,15 @@

This variable is primarily meant to be exposed to gint functions, but
add-ins may use it or change it freely:
- To use another video ram area (triple buffering or more, gray engine) ;
- To implement additional drawing functions ;
- To use another video ram area (triple buffering or more, gray engine);
- To implement additional drawing functions;
- When not drawing, as additional RAM (especially on fxcg50). */
#ifdef GINT_NEED_VRAM
extern uint32_t *vram;
#endif

/* As you would expect, display on fx9860g and display on fxcg50 are completely
different worlds. As a consequence, so are the headers ^^ */
different worlds. As a consequence, so are the rendering functions ^^ */

#ifdef FX9860G
#include <gint/display-fx.h>
@@ -29,4 +31,28 @@ extern uint32_t *vram;
#include <gint/display-cg.h>
#endif

//---
// Parameter access
//---

/* dinfo_t - summary of information provided by this module */
typedef struct
{
/* Screen width, in pixels */
int width;
/* Screen height, in pixels */
int height;
/* Color depth (is 1 on fx9860g regardless of the gray engine) */
int bpp;
/* Current rendering font */
font_t const * font;

} dinfo_t;

/* dinfo() - retrieve information from the display module
This function returns the value of most parameters of the display module.

@info Pointer to allocated dinfo_t structure, will be filled. */
void dinfo(dinfo_t *info);

#endif /* GINT_DISPLAY */

+ 6
- 6
include/gint/drivers.h View File

@@ -5,8 +5,8 @@
#ifndef GINT_DRIVERS
#define GINT_DRIVERS

#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>

//---
// Driver initialization
@@ -39,7 +39,7 @@ typedef struct
may be set to NULL */
void (*init)(void);

/* unload() - unitialize the driver
/* unload() - deinitialize the driver
This function is called before ctx_restore() when gint is unloaded.
If there is no unload function, the field may be set to NULL */
void (*unload)(void);
@@ -65,7 +65,7 @@ typedef struct
@ctx A context buffer filled by ctx_save() */
void (*ctx_restore)(void *ctx);

} PACKED(4) gint_driver_t;
} GPACKED(4) gint_driver_t;

/* GINT_DECLARE_DRIVER() - make a driver visible to gint

@@ -75,10 +75,10 @@ typedef struct

The @level argument represents the priority level: lower numbers mean that
drivers will be loaded sooner. This numbering allows a primitive form of
dependency for drivers. You need to specifiy a level which is strictly
dependency for drivers. You need to specify a level which is strictly
higher than the level of all the drivers you depend on. */
#define GINT_DECLARE_DRIVER(level, name) \
SECTION(".gint.drivers." #level) extern gint_driver_t name;
GSECTION(".gint.drivers." #level) extern gint_driver_t name;

/* GINT_DRIVER_SH3() - declare a function for SH3-rectification
This macros allows the argument function to not exist on fxcg50. */

+ 17
- 0
include/gint/drivers/iokbd.h View File

@@ -0,0 +1,17 @@
//---
// gint:drivers:iokbd - I/O ports-driven keyboard scanner
//
// This is for SH3 only. It reads key pressed from ports A/B/M.
//---

#ifndef GINT_DRIVERS_IOKBD
#define GINT_DRIVERS_IOKBD

#include <gint/defs/types.h>

/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data
Numbering of rows is consistent with the keyboard.
@scan 12-byte buffer filled with row data */
void iokbd_scan(uint8_t *scan);

#endif /* GINT_DRIVERS_IOKBD */

include/drivers/t6k11.h → include/gint/drivers/t6k11.h View File

@@ -7,7 +7,7 @@
#ifndef GINT_DRIVERS_T6K11
#define GINT_DRIVERS_T6K11

#include <defs/types.h>
#include <gint/defs/types.h>

/* t6k11_display() - send vram data to the LCD device

@@ -33,7 +33,7 @@ void t6k11_display(const void *vram, int y1, int y2, size_t stride);
It would be possible to restore contrast, or update the system value on
change, if the address was known for all OS versions.

OS 02.05.2201 8800b93c
OS 02.05.2201 8800'b93c

@contrast Requested contrast value */
void t6k11_contrast(int contrast);

+ 12
- 14
include/gint/gint.h View File

@@ -5,11 +5,10 @@
#ifndef GINT_GINT
#define GINT_GINT

#include <defs/attributes.h>
#include <defs/types.h>
#include <gint/defs/types.h>

/* gint_version() - get the library version number
Returns gint's running version number, which is made of four fields:
/* GINT_VERSION - the library version number
Provides gint's running version number, which is made of four fields:

31 24 23 20 19 16 15 0
+---------------+---------------+---------------+---------------+
@@ -22,11 +21,9 @@
The build number uniquely identifies a binary version of the library.

For instance, 0x72100053 translates as "release 1.0 build 83". */
HDRFUNC uint32_t gint_version(void)
{
extern char GINT_VERSION;
return (uint32_t)&GINT_VERSION;
}
/* TODO: Fix version number */
extern char GINT_VERSION;
#define GINT_VERSION ((uint32_t)&GINT_VERSION)

//---
// Library management
@@ -103,16 +100,17 @@ int gint_intlevel(int intid, int level);
you're not interfering with usual interrupt assignments.

The first parameter 'event_code' represents the event_code associated with
the interrupt. The codes are normally platform-dependent, but gint always
uses SH7305 codes: SH3 platforms have a translation table. See the
documentation for a list of event codes and their associated interrupts.
the interrupt. If it's not a multiple of 0x20 then you're doing something
wrong. The codes are normally platform-dependent, but gint always uses
SH7305 codes: SH3 platforms have a translation table. See the documentation
for a list of event codes and their associated interrupts.

The handler function must be an interrupt handler: it must not raise
exceptions, must end with 'rte', and it will use the kernel register bank.
For convenience I allow any block size to be loaded as an interrupt handler,
but it should really be a multiple of 32 bytes and not override other
but it should really be a multiple of 0x20 bytes and not override other
handlers. If it's not written in assembler, then you're likely doing
something wrong.
something wrong, even with __attribute__((interrupt_handler)).

It is common for interrupt handlers to have a few bytes of data, such as the
address of a callback function. gint often stores this data in the last

+ 130
- 21
include/gint/keyboard.h View File

@@ -5,34 +5,55 @@
#ifndef GINT_KEYBOARD
#define GINT_KEYBOARD

#include <defs/types.h>
/* Keyboard, key events and getkey()

gint's keyboard driver regularly scans the keyboard matrix and produces *key
events*, the most primitive type of event exposed to the user. A key event
basically says that a key has been pressed, held, or released at some point
in time. They are suited for real-time applications like games.

- pollevent() fetches the next queued keyboard event, and returns an event
of type KEYEV_NONE if none is available.
- waitevent() fetches the next queued keyboard event and waits if none is
available. The timeout can be configured.

GUI programs like the system applications will prefer using a GetKey()-like
functions that return a single key press at a time, heeds for releases, for
SHIFT and ALPHA modifiers, handles backlight and return-to-menu.

- getkey_opt() is gint's enhanced GetKey()-like function, with support for
custom repetition and many fine-tunable options.
- getkey() is a specific call to getkey_opt(), that imitates GetKey(). */

#include <gint/defs/types.h>
#include <gint/keycodes.h>

/* key_event_t - any keyboard event
This structure represents an event that occurs on the keyboard. This is a
low-level structure that is produced by the keyboard scanner. It reports key
presses, key releases, and key repetitions.
presses, key releases, and key repeats.

These events are detected and reported each time the keyboard is scanned,
which is 16 Hz by default, so you'll get 16 repeat events by second if a key
is kept pressed. We can filter the events to emit one only every second, for
example, but it's difficult to do it for all keys at the same time. Thus the
control of repetition delays is restricted to getkey().

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.

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. */
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], 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. It is a snapshot of
a time counter that increases at each keyboard scan and *wraps around every
8 minutes* (at 128 Hz). I expect this attribute to be useful to analyze
combo sequences in games. Make sure you are aware of the two nitpicks:
* Don't keep the time values for too long because of the wrap-around effect.
* 0xffff is "just before" 0x0000, not "long after". */
typedef struct
{
uint time :12; /* Time of event, unique over short periods */
uint time :16; /* Time of event, unique over short periods */

uint :7; /* Reserved for future use */
uint :3; /* Reserved for future use */

uint mod :1; /* Whether modifiers are used */
uint shift :1; /* If mod=1, whether SHIFT was pressed */
@@ -41,7 +62,7 @@ typedef struct
uint type :2; /* Type of key event */
uint key :8; /* Hit key */

} PACKED(4) key_event_t;
} GPACKED(4) key_event_t;

/* Keyboard event types, as in the type field of key_event_t */
enum
@@ -58,10 +79,98 @@ enum
#define KEYBOARD_QUEUE_SIZE 64
#endif

/* Keyboard frequency analysis, must be at least 64 for the keyboard to work,
and at most 32768 for the extra timer to support it. Better if a power of 2.
TODO: Add a configure or runtime setting for KEYBOARD_SCAN_FREQUENCY */
#ifndef KEYBOARD_SCAN_FREQUENCY
#define KEYBOARD_SCAN_FREQUENCY 128
#endif

//---
// Keyboard functions
//---

key_event_t key_poll(void);
/* pollevent() - poll the next keyboard event
This function returns the next yet-unpolled event from the keyboard buffer.
If no event is available, it returns a dummy event with type = KEYEV_NONE.
This event has always mod = 0, shift = 0, alpha = 0. */
key_event_t pollevent(void);

/* waitevent() - wait for the next keyboard event
This function works as pollevent() but waits if no event is available. When
timeout = NULL, it waits indefinitely. Otherwise, it waits until *timeout
becomes non-zero. It is particularly suitable to set *timeout to 1 using a
timer with [timer_timeout] as callback. See <gint/timer.h>. */
key_event_t waitevent(volatile int *timeout);

/* getkey() - wait for a pressed key

This function mimics the behavior of the fxlib GetKey(). It returns a
key_event_t object where [mod=1], and where [shift] and [alpha] indicate
whether SHIFT or ALPHA was pressed before the key was hit. [event] is
KEYEV_DOWN when a new key is pressed and KEYEV_HOLD in case of repeats.

Similarities with GetKey() include:
- Wait for a key to be pressed *after* the call (held keys don't count)
- Supports SHIFT and ALPHA modifiers
- Repeats arrows keys
- Allows return to main menu if the MENU key is pressed
- Controls backlight on models that have a back-lit screen

getkey() is equivalent to getkey_opt(GETKEY_DEFAULT, NULL). */
key_event_t getkey(void);

/* The following are the option bits for getkey_opt(). */
enum {
/* Enable modifiers keys */
GETKEY_MOD_SHIFT = 0x01,
GETKEY_MOD_ALPHA = 0x02,
/* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
GETKEY_BACKLIGHT = 0x04,
/* MENU triggers a task switch and displays the main menu */
GETKEY_MENU = 0x08,
/* Repeat arrow keys, or even all keys */
GETKEY_REP_ARROWS = 0x10,
GETKEY_REP_ALL = 0x20,

/* Default settings of getkey() */
GETKEY_DEFAULT = 0x1f,
};

/* getkey_opt() - enhanced getkey()

This function enhances getkey() with more general features. An
or-combination of option flags (see above) must be supplied as first
argument; 0 stands for no option. getkey_opt() returns the same kind of
values as getkey().

getkey_opt() supports a generic timeout function in the form of a volatile
pointer [timeout]. If it's NULL, getkey_opt() waits indefinitely. Otherwise,
it waits until *timeout becomes non-zero. It's up to you to change the
value whenever you want to interrupt the call; using a timer with
[timer_timeout] as callback is suitable.

@options An or-combination of values from the GETKEY_* enumeration
@timeout Optional pointer to a timeout value
Returns a key event of type KEYEV_DOWN or KEYEV_HOLD with [mod=1]. */
key_event_t getkey_opt(int options, volatile int *timeout);

/* getkey_repeat() - set repeat delays for getkey()

This function updates the repeat delays of getkey() and getkey_opt(). The
unit of the argument is in milliseconds, but the granularity of the delay is
dependent on the keyboard scan frequency.

In the default setting (128 Hz scans), the possible repeat delays are
approximately 8 ms, 16 ms, 23 ms, 31 ms... the provided arguments will be
rounded to the closest feasible delays to ensure that repetitions are
perfectly regular, rather than approximating the requested frequency.

The system default is (625 ms, 25 ms). With the 128 Hz setting, this default
will be approximated at (625 ms, 23.4375 ms).

@first Delay between key press and first repeat (no more than one hour)
@next Delay between subsequent repeats (no more than one hour) */
void getkey_repeat(int first, int next);

#endif /* GINT_KEYBOARD */

+ 86
- 0
include/gint/keycodes.h View File

@@ -0,0 +1,86 @@
//---
// gint:keycodes - Matrix code for all keyboard keys
//---

#ifndef GINT_KEYCODES
#define GINT_KEYCODES

/* Raw matrix codes */
enum {
KEY_F1 = 0x91,
KEY_F2 = 0x92,
KEY_F3 = 0x93,
KEY_F4 = 0x94,
KEY_F5 = 0x95,
KEY_F6 = 0x96,

KEY_SHIFT = 0x81,
KEY_OPTN = 0x82,
KEY_VARS = 0x83,
KEY_MENU = 0x84,
KEY_LEFT = 0x85,
KEY_UP = 0x86,

KEY_ALPHA = 0x71,
KEY_SQUARE = 0x72,
KEY_POWER = 0x73,
KEY_EXIT = 0x74,
KEY_DOWN = 0x75,
KEY_RIGHT = 0x76,

KEY_XOT = 0x61,
KEY_LOG = 0x62,
KEY_LN = 0x63,
KEY_SIN = 0x64,
KEY_COS = 0x65,
KEY_TAN = 0x66,

KEY_FRAC = 0x51,
KEY_FD = 0x52,
KEY_LEFTP = 0x53,
KEY_RIGHTP = 0x54,
KEY_COMMA = 0x55,
KEY_ARROW = 0x56,

KEY_7 = 0x41,
KEY_8 = 0x42,
KEY_9 = 0x43,
KEY_DEL = 0x44,
/* AC/ON has keycode 0x07 instead of 0x45 */

KEY_4 = 0x31,
KEY_5 = 0x32,
KEY_6 = 0x33,
KEY_MUL = 0x34,
KEY_DIV = 0x35,

KEY_1 = 0x21,
KEY_2 = 0x22,
KEY_3 = 0x23,
KEY_ADD = 0x24,
KEY_SUB = 0x25,

KEY_0 = 0x11,
KEY_DOT = 0x12,
KEY_EXP = 0x13,
KEY_NEG = 0x14,
KEY_EXE = 0x15,

/* Why is AC/ON not 0x45? Because it must be on a row/column of its
own. It's used to power up the calculator; if it were in the middle
of the matrix one could use a ghosting effect to boot the calc. */
KEY_ACON = 0x07,

/* Key aliases (handle with care =D) */
KEY_X2 = KEY_SQUARE,
KEY_CARET = KEY_POWER,
KEY_SWITCH = KEY_FD,
KEY_LEFTPAR = KEY_LEFTP,
KEY_RIGHTPAR = KEY_RIGHTP,
KEY_STORE = KEY_ARROW,
KEY_TIMES = KEY_MUL,
KEY_PLUS = KEY_ADD,
KEY_MINUS = KEY_SUB,
};

#endif /* GINT_KEYCODES */

include/core/mpu.h → include/gint/mpu.h View File

@@ -12,8 +12,6 @@
#ifndef GINT_CORE_MPU
#define GINT_CORE_MPU

#include <defs/attributes.h>

/* mpu_t - supported MPUs */
typedef enum
{
@@ -27,14 +25,11 @@ typedef enum
#ifdef FX9860G

/* mpu_id() - get the name of the underlying MPU */
HDRFUNC mpu_t mpu_id(void)
{
extern const mpu_t mpu;
return mpu;
}
extern const mpu_t gint_mpu_id;
#define gint_mpu() (gint_mpu_id)

/* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */
#define isSH3() (mpu_id() & 1)
#define isSH3() (gint_mpu() & 1)
#define isSH4() (!isSH3())

/* mpu_init() - probe the MPU type
@@ -44,7 +39,7 @@ typedef enum
#else /* FXCG50 */

/* All fxcg50 machines have an SH7305, which makes things simpler. */
#define mpu_id() mpu_sh7305
#define gint_mpu() mpu_sh7305
#define isSH3() 0
#define isSH4() 1


include/core/cpg.h → include/gint/mpu/cpg.h View File

@@ -5,7 +5,8 @@
#ifndef GINT_CORE_CPG
#define GINT_CORE_CPG

#include <defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>

//---
// SH7705 Clock Pulse Generator. Refer to:
@@ -27,7 +28,7 @@ typedef volatile struct
uint16_t PFC :2; /* Peripheral clock divider */
);

} PACKED(4) sh7705_cpg_t;
} GPACKED(4) sh7705_cpg_t;

#define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80))

@@ -73,7 +74,7 @@ typedef volatile struct
uint32_t FLF :11; /* FLL Multiplication Ratio */
);

} PACKED(4) sh7305_cpg_t;
} GPACKED(4) sh7305_cpg_t;

#define SH7305_CPG (*((sh7305_cpg_t *)0xa4150000))


include/core/intc.h → include/gint/mpu/intc.h View File

@@ -12,7 +12,7 @@
#ifndef GINT_CORE_INTC
#define GINT_CORE_INTC

#include <defs/types.h>
#include <gint/defs/types.h>

//---
// SH7705 Interrupt Controller. Refer to:
@@ -83,7 +83,7 @@ typedef struct
uint16_t :4;
);

} PACKED(4) sh7705_intc_ipc_t;
} GPACKED(4) sh7705_intc_ipc_t;

/* sh7705_intc_icr1_t - Interrupt Control Register 1 (general) */
typedef volatile word_union(sh7705_intc_icr1_t,
@@ -106,12 +106,12 @@ typedef struct
union {
sh7705_intc_ipc_t _;
volatile uint16_t *IPRS[8];
} PACKED