mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-29 13:03:36 +01:00
Hybrid build system and runtime (no interrupts). t6k11 driver. Basic r61524 driver.
This commit is contained in:
parent
c50cbf6a9c
commit
3b90b40dd7
26 changed files with 2697 additions and 48 deletions
72
Makefile
72
Makefile
|
@ -15,9 +15,17 @@ cfg := config/Makefile.cfg
|
||||||
# Those that do may use this special dependency
|
# Those that do may use this special dependency
|
||||||
cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg))
|
cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg))
|
||||||
|
|
||||||
|
cf-fx := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
|
||||||
|
-I include $(cfg_macros)
|
||||||
|
cf-cg := -m4 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
|
||||||
|
-I include $(cfg_macros)
|
||||||
|
|
||||||
|
# On fx9860g, use the sh3eb-elf toolchain; on fxcg50, prefer sh4eb-elf
|
||||||
|
toolchain := $(if $(filter $(cfg_target),fx),sh3eb-elf,sh4eb-elf)
|
||||||
|
|
||||||
# Compiler flags, assembler flags, dependency generation, archiving
|
# Compiler flags, assembler flags, dependency generation, archiving
|
||||||
cflags := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -O2 \
|
cflags := $(cf-$(cfg_target))
|
||||||
-I include $(cfg_defs)
|
sflags := $(cfg_macros)
|
||||||
dflags = -MMD -MT $@ -MF $(@:.o=.d) -MP
|
dflags = -MMD -MT $@ -MF $(@:.o=.d) -MP
|
||||||
arflags :=
|
arflags :=
|
||||||
|
|
||||||
|
@ -25,47 +33,73 @@ arflags :=
|
||||||
# File listings
|
# File listings
|
||||||
#
|
#
|
||||||
|
|
||||||
# Target file
|
# Target file (cfg_target is either "fx" or "cg")
|
||||||
target := bin/libgint.a
|
target := bin/libgint-$(cfg_target).a
|
||||||
|
|
||||||
# Automatic names for object and dependency files
|
# Automatic names for object and dependency files
|
||||||
src2obj = build/$(1:src/%=%).o
|
src2obj = build/$(1:src/%=%).o
|
||||||
src2dep = build/$(1:src/%=%).d
|
src2dep = build/$(1:src/%=%).d
|
||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
src := $(shell find src -name '*.[cs]')
|
src := $(shell find src -name '*.[csS]')
|
||||||
src_obj := $(foreach s,$(src),$(call src2obj,$s))
|
src_obj := $(foreach s,$(src),$(call src2obj,$s))
|
||||||
|
|
||||||
# Files with special handling
|
# Files with special handling
|
||||||
spe := src/display/font.bmp
|
# spe := build/version.o src/display/font.bmp
|
||||||
spe_obj := $(foreach s,$(spe),$(call src2obj,$s))
|
spe :=
|
||||||
|
spe_obj := build/version.o $(foreach s,$(spe),$(call src2obj,$s))
|
||||||
|
|
||||||
# All object files
|
# All object files
|
||||||
obj := $(src_obj) $(spe_obj)
|
obj := $(src_obj) $(spe_obj)
|
||||||
|
|
||||||
|
# Version file
|
||||||
|
version := config/version
|
||||||
|
|
||||||
#
|
#
|
||||||
# Toolchain
|
# Toolchain
|
||||||
#
|
#
|
||||||
|
|
||||||
gcc = sh3eb-elf-gcc
|
gcc = $(toolchain)-gcc
|
||||||
as = sh3eb-elf-as
|
as = $(toolchain)-as
|
||||||
ld = sh3eb-elf-ld
|
ld = $(toolchain)-ld
|
||||||
ar = sh3eb-elf-ar
|
ar = $(toolchain)-ar
|
||||||
|
objcopy = $(toolchain)-objcopy
|
||||||
conv = fxconv
|
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
|
# Build rules
|
||||||
#
|
#
|
||||||
|
|
||||||
all: $(target)
|
all: $(target)
|
||||||
|
|
||||||
$(target): $(obj) | $(dir $(target))
|
$(target): $(obj) $(version) | $(dir $(target))
|
||||||
$(call cmd_l,ar,$@) $(ar) -crs $(arflags) $@ $(obj)
|
$(call cmd_l,ar,$@) $(ar) -crs $(arflags) $@ $(obj)
|
||||||
|
|
||||||
# Assembler sources
|
# Assembler sources
|
||||||
build/%.s.o: src/%.s build/%.s.d
|
build/%.s.o: src/%.s build/%.s.d
|
||||||
@ mkdir -p $(dir $@)
|
@ mkdir -p $(dir $@)
|
||||||
$(call cmd_b,as,$<) $(as) -c $< -o $@ $(sflags)
|
$(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
|
# C sources
|
||||||
build/%.c.o: src/%.c build/%.c.d $(cfgdep)
|
build/%.c.o: src/%.c build/%.c.d $(cfgdep)
|
||||||
|
@ -77,6 +111,13 @@ $(call src2obj,src/display/font.bmp): src/display/font.bmp
|
||||||
@ mkdir -p $(dir $@)
|
@ mkdir -p $(dir $@)
|
||||||
$(call cmd_m,fxconv,$<) $(conv) -font $< -n gint_font -o $@
|
$(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
|
# Cleaning
|
||||||
#
|
#
|
||||||
|
@ -85,12 +126,15 @@ clean:
|
||||||
@ rm -rf build/
|
@ rm -rf build/
|
||||||
distclean: clean
|
distclean: clean
|
||||||
@ rm -rf bin/
|
@ rm -rf bin/
|
||||||
@ rm -f $(cfg)
|
@ rm -f $(config)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Utilities
|
# 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
|
# Evaluated when a rule requires the configuration file but it doesn't exist
|
||||||
CFGDEP:
|
CFGDEP:
|
||||||
@ echo "Configuration file $(cfg) is missing. Have you configured?"
|
@ echo "Configuration file $(cfg) is missing. Have you configured?"
|
||||||
|
|
68
configure
vendored
68
configure
vendored
|
@ -40,31 +40,33 @@ help()
|
||||||
Configuration script for the gint library.
|
Configuration script for the gint library.
|
||||||
Usage: $0 [options...]
|
Usage: $0 [options...]
|
||||||
|
|
||||||
Required settings:
|
Platform settings (specify exactly one):
|
||||||
$Cr--target$C0=${Cg}fx9860g$C0,${Cg}fxcg50$C0
|
${Cg}-fx9860g$C0
|
||||||
Select the target platform, either ${Cg}fx9860g$C0 for monochrome
|
Target platform is fx-9860G II: all monochrome models that support add-ins
|
||||||
calculators, or ${Cg}fxcg50$C0 for Prizm calculators.
|
or can be flashed to support them.
|
||||||
|
${Cg}-fxcg50$C0
|
||||||
|
Target platform is fx-CG50.
|
||||||
|
|
||||||
Options that affect the behavior of the library:
|
Options that affect the behavior of the library:
|
||||||
$Cr--boot-log $C_[default:$Cp not specified$C_]$C0
|
$Cr-boot-log $C_[default:$Cp not specified$C_]$C0
|
||||||
Enable an on-screen log at startup if a key is kept pressed while launching
|
Enable an on-screen log at startup if a key is kept pressed while launching
|
||||||
the add-in, allowing easy debug and crash diagnoses.
|
the add-in, allowing easy debug and crash diagnoses.
|
||||||
$Cr--no-syscalls $C_[default:$Cp not specified$C_]$C0
|
$Cr-no-syscalls $C_[default:$Cp not specified$C_]$C0
|
||||||
Never use syscalls. Expect trouble with malloc() and the gray engine. Do
|
Never use syscalls. Expect trouble with malloc() and the gray engine. Do
|
||||||
not trigger this switch unless you know what you are doing.
|
not trigger this switch unless you know what you are doing.
|
||||||
$Cr--extended-libc $C_[default:$Cp not specified$C_]$C0
|
$Cr-extended-libc $C_[default:$Cp not specified$C_]$C0
|
||||||
Enable specific C99 headers/features that are normally not required by
|
Enable specific C99 headers/features that are normally not required by
|
||||||
calculator programs. This may allow porting programs from other platforms.
|
calculator programs. This may allow porting programs from other platforms.
|
||||||
$Cr--static-gray-engine $C_[default:$Cp not specified$C_]$C0
|
$Cr-static-gray-engine $C_[default:$Cp not specified$C_]$C0
|
||||||
Place the gray engine vram in static ram instead of using the heap. Always
|
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.
|
use this option when using both the gray engine and -no-syscalls.
|
||||||
|
|
||||||
Options that customize size limits:
|
Options that customize size limits:
|
||||||
$Cr--atexit-max$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0
|
$Cr-atexit-max$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0
|
||||||
Number of exit handlers that can be registered by atexit().
|
Number of exit handlers that can be registered by atexit().
|
||||||
$Cr--timer-slots$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0
|
$Cr-timer-slots$C0=${Cg}integer$C_ [default:$Cp 16$C_]$C0
|
||||||
Number of virtual timers that may be registered at the same time.
|
Number of virtual timers that may be registered at the same time.
|
||||||
$Cr--events-queue-size$C0=${Cg}integer$C_ [default:$Cp 64$C_]$C0
|
$Cr-events-queue-size$C0=${Cg}integer$C_ [default:$Cp 64$C_]$C0
|
||||||
Number of events simultaneously stored in the event queue.
|
Number of events simultaneously stored in the event queue.
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
@ -79,43 +81,37 @@ fail=false
|
||||||
for arg; do case "$arg" in
|
for arg; do case "$arg" in
|
||||||
-h | -? | --help) help;;
|
-h | -? | --help) help;;
|
||||||
|
|
||||||
--target=*)
|
-fx9860g)
|
||||||
conf_target=${arg#*=}
|
conf_target="FX9860G";;
|
||||||
if [[ $conf_target = "fx9860g" ]]; then
|
-fxcg50)
|
||||||
conf_target="GINT_FX9860G"
|
conf_target="FXCG50";;
|
||||||
else if [[ $conf_target = "fxcg50" ]]; then
|
|
||||||
conf_target="GINT_FXCG50"
|
|
||||||
else
|
|
||||||
echo -e "$error Invalid target. See $0 --help."
|
|
||||||
fail=true;
|
|
||||||
fi; fi;;
|
|
||||||
|
|
||||||
--boot-log) conf[GINT_BOOT_LOG]=true;;
|
-boot-log) conf[GINT_BOOT_LOG]=true;;
|
||||||
--no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
|
-no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
|
||||||
--extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
|
-extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
|
||||||
--static-gray-engine) conf[GINT_STATIC_GRAY]=true;;
|
-static-gray-engine) conf[GINT_STATIC_GRAY]=true;;
|
||||||
|
|
||||||
--atexit-max=*)
|
-atexit-max=*)
|
||||||
size=${arg#*=}
|
size=${arg#*=}
|
||||||
if [[ $size == +([0-9]) ]]; then
|
if [[ $size == +([0-9]) ]]; then
|
||||||
conf[ATEXIT_MAX]=$size
|
conf[ATEXIT_MAX]=$size
|
||||||
else echo -e "$error --atexit-max expects an integer value"
|
else echo -e "$error -atexit-max expects an integer value"
|
||||||
fail=true; fi;;
|
fail=true; fi;;
|
||||||
--timer-slots=*)
|
-timer-slots=*)
|
||||||
size=${arg#*=}
|
size=${arg#*=}
|
||||||
if [[ $size == +([0-9]) ]]; then
|
if [[ $size == +([0-9]) ]]; then
|
||||||
conf[TIMER_SLOTS]=$size
|
conf[TIMER_SLOTS]=$size
|
||||||
else echo -e "$error --timer-slots expects an integer value"
|
else echo -e "$error -timer-slots expects an integer value"
|
||||||
fail=true; fi;;
|
fail=true; fi;;
|
||||||
--events-queue-size=*)
|
-events-queue-size=*)
|
||||||
size=${arg#*=}
|
size=${arg#*=}
|
||||||
if [[ $size == +([0-9]) ]]; then
|
if [[ $size == +([0-9]) ]]; then
|
||||||
conf[EVENTS_QUEUE_SIZE]=$size
|
conf[EVENTS_QUEUE_SIZE]=$size
|
||||||
else echo -e "$error --events-queue-size expects an integer"\
|
else echo -e "$error -events-queue-size expects an integer"\
|
||||||
"value"
|
"value"
|
||||||
fail=true; fi;;
|
fail=true; fi;;
|
||||||
|
|
||||||
--atexit-max | --timer-slots | --events-queue-size)
|
-atexit-max | -timer-slots | -events-queue-size)
|
||||||
echo -e "$error syntax for $arg is $arg=<integer-value>";;
|
echo -e "$error syntax for $arg is $arg=<integer-value>";;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
|
@ -137,7 +133,11 @@ fi
|
||||||
|
|
||||||
output_config()
|
output_config()
|
||||||
{
|
{
|
||||||
echo -n "cfg_defs ="
|
[ ${conf_target} == "FX9860G" ] \
|
||||||
|
&& echo "cfg_target = fx" \
|
||||||
|
|| echo "cfg_target = cg"
|
||||||
|
|
||||||
|
echo -n "cfg_macros ="
|
||||||
echo -n " -D$conf_target"
|
echo -n " -D$conf_target"
|
||||||
|
|
||||||
[ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG"
|
[ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG"
|
||||||
|
|
186
fx9860g.ld
Normal file
186
fx9860g.ld
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
/*
|
||||||
|
Linker script for the fx9860g platform. Most of the symbols defined
|
||||||
|
here are used in the initialization routine in core/start.c; others are
|
||||||
|
used in core/setup.c.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* fx9860g may mean SH3 or SH4 and we want full compatibility */
|
||||||
|
OUTPUT_ARCH(sh3)
|
||||||
|
/* ELF offers a lot of symbol/section/relocation insights */
|
||||||
|
OUTPUT_FORMAT(elf32-sh)
|
||||||
|
/* Located in core/start.c */
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* Userspace mapping of the add-in (0x200 B are for the G1A header).
|
||||||
|
220k is the maximum amount of simultaneously-mappable code */
|
||||||
|
rom (rx): o = 0x00300200, l = 220k
|
||||||
|
/* This is mapped to RAM; 8k on SH3, apparently 32k on SH4 */
|
||||||
|
ram (rw): o = 0x08100000, l = 8k
|
||||||
|
/* gint's VBR space, mentioned here for completeness */
|
||||||
|
vbr (rwx): o = 0x8800e000, l = 4k
|
||||||
|
/* Some RAM region from P1 area; gint's data will reside here */
|
||||||
|
rram (rwx): o = 0x8800f000, l = 4k
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
** ROM sections
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* First address to be mapped to ROM (including G1A header) */
|
||||||
|
_brom = 0x00300000;
|
||||||
|
/* Size of ROM mappings */
|
||||||
|
_srom = 0x200
|
||||||
|
+ SIZEOF(.text) + SIZEOF(.rodata)
|
||||||
|
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
|
||||||
|
|
||||||
|
/* Machine code going to ROM:
|
||||||
|
- Initialization sections (.pretext.entry and .pretext)
|
||||||
|
- Compiler-provided constructors (.ctors) and destructors (.dtors)
|
||||||
|
- All text from .text and .text.* (including user code)
|
||||||
|
- Code sections from fxlib, named "C" and "P" */
|
||||||
|
|
||||||
|
.pretext : {
|
||||||
|
*(.pretext.entry)
|
||||||
|
*(.pretext)
|
||||||
|
|
||||||
|
_btors = . ;
|
||||||
|
*(.ctors .ctors.*)
|
||||||
|
_mtors = . ;
|
||||||
|
*(.dtors .dtors.*)
|
||||||
|
_etors = . ;
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
*(.text .text.*)
|
||||||
|
*(C P)
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
/* Interrupt handlers going to ROM:
|
||||||
|
- gint's interrupt handler blocks (.gint.blocks)
|
||||||
|
|
||||||
|
Although gint's blocks end up in VBR space, they are selected and
|
||||||
|
installed on-the-fly by the library and the drivers, so we can't
|
||||||
|
just put them in the vbr region and wait for the copy */
|
||||||
|
.gint.blocks : {
|
||||||
|
KEEP(*(.gint.blocks));
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
/* Driver data going to ROM:
|
||||||
|
- Exposed driver interfaces (.gint.drivers)
|
||||||
|
|
||||||
|
The driver information is required to start and configure the
|
||||||
|
driver, even if the symbols are not referenced */
|
||||||
|
.gint.drivers : {
|
||||||
|
_bdrv = . ;
|
||||||
|
KEEP(*(.gint.drivers));
|
||||||
|
_edrv = . ;
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
/* Read-only data going to ROM:
|
||||||
|
- Resources or assets from fxconv or similar converters
|
||||||
|
- Data marked read-only by the compiler (.rodata and .rodata.*) */
|
||||||
|
.rodata : SUBALIGN(4) {
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** RAM sections
|
||||||
|
*/
|
||||||
|
|
||||||
|
. = ORIGIN(ram);
|
||||||
|
|
||||||
|
/* BSS stuff going to RAM:
|
||||||
|
- Data marked BSS by the compiler
|
||||||
|
- BSS sections from fxlib, namely "B" and "R"
|
||||||
|
The BSS section is to be stripped from the ELF file later, and wiped
|
||||||
|
at startup. */
|
||||||
|
.bss (NOLOAD) : {
|
||||||
|
_rbss = . ;
|
||||||
|
|
||||||
|
*(.bss COMMON)
|
||||||
|
*(B R)
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
} > ram :NONE
|
||||||
|
|
||||||
|
_sbss = SIZEOF(.bss);
|
||||||
|
|
||||||
|
/* Read-write data going to RAM:
|
||||||
|
- Data sections generated by the compiler (.data and .data.*)
|
||||||
|
- Data sections from fxlib, "D" */
|
||||||
|
.data ALIGN(4) : ALIGN(4) {
|
||||||
|
_ldata = LOADADDR(.data);
|
||||||
|
_rdata = . ;
|
||||||
|
|
||||||
|
*(.data .data.*)
|
||||||
|
*(D)
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
} > ram AT> rom
|
||||||
|
|
||||||
|
_sdata = SIZEOF(.data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** RRAM sections
|
||||||
|
** 8800e000:4k VBR space
|
||||||
|
** 8800f000:4k .gint.data and .gint.bss
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* VBR address: let's just start at the beginning of the RRAM area.
|
||||||
|
There's an unused 0x100-byte gap at the start of the VBR space.
|
||||||
|
The VBR space is already a large block (> 2 kiB), so I'm cutting off
|
||||||
|
the gap to spare some memory */
|
||||||
|
_gint_vbr = 0x8800df00;
|
||||||
|
|
||||||
|
. = ORIGIN(rram);
|
||||||
|
|
||||||
|
/* gint's data section, going to Real RAM. This section contains many
|
||||||
|
small objects from the library (static/global variables, etc) */
|
||||||
|
.gint.data ALIGN(4) : ALIGN(4) {
|
||||||
|
_lgdata = LOADADDR(.gint.data);
|
||||||
|
_rgdata = . ;
|
||||||
|
|
||||||
|
*(.gint.data .gint.data.*)
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
} > rram AT> rom
|
||||||
|
|
||||||
|
_sgdata = SIZEOF(.gint.data);
|
||||||
|
|
||||||
|
/* gint's uninitialized BSS section, going to Real RAM. All the large
|
||||||
|
data arrays will be located here */
|
||||||
|
.gint.bss (NOLOAD) : {
|
||||||
|
/* Since it's uninitialized, the location doesn't matter */
|
||||||
|
*(.gint.bss .gint.bss.*)
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
} > rram :NONE
|
||||||
|
|
||||||
|
_sgbss = SIZEOF(.gint.bss);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Other sections
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Unwanted sections going to meet Dave Null:
|
||||||
|
- Java classes registration (why are there even here?)
|
||||||
|
- Asynchronous unwind tables: no C++ exception handling for now ^^
|
||||||
|
- Comments or anything the compiler might put in its assembler
|
||||||
|
- A stack section sometimes generated for build/version.o */
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.jcr)
|
||||||
|
*(.eh_frame_hdr)
|
||||||
|
*(.eh_frame)
|
||||||
|
*(.comment)
|
||||||
|
}
|
||||||
|
}
|
173
fxcg50.ld
Normal file
173
fxcg50.ld
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
Linker script for fxcg50 add-ins. Most symbols are used in the startup
|
||||||
|
routine in core/start.c; some others in core/setup.c.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* All fxcg50 have SH4 processors (finally rid of compatibility issues) */
|
||||||
|
OUTPUT_ARCH(sh4)
|
||||||
|
/* ELF offers a lot of symbol/section/relocation insights */
|
||||||
|
OUTPUT_FORMAT(elf32-sh)
|
||||||
|
/* Located in core/start.c */
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
/* Userspace mapping of the add-in (without G3A header) */
|
||||||
|
rom (rx): o = 0x00300000, l = 220k
|
||||||
|
/* Static RAM; stack grows down from the end of this region.
|
||||||
|
The first 0x2000 bytes are reserved by gint, see below */
|
||||||
|
ram (rw): o = 0x08102000, l = 512k
|
||||||
|
/* gint's VBR space, mentioned here for completeness */
|
||||||
|
vbr (rwx): o = 0x8c160000, l = 4k
|
||||||
|
/* Some RAM region from P1 area; gint's data will reside here */
|
||||||
|
rram (rwx): o = 0x8c161000, l = 4k
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
** ROM sections
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* First address to be mapped to ROM */
|
||||||
|
_brom = 0x00300000;
|
||||||
|
/* Size of ROM mappings */
|
||||||
|
_srom = SIZEOF(.text) + SIZEOF(.rodata)
|
||||||
|
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
|
||||||
|
|
||||||
|
/* Machine code going to ROM:
|
||||||
|
- Initialization sections (.pretext.entry and .pretext)
|
||||||
|
- Compiler-provided constructors (.ctors) and destructors (.dtors)
|
||||||
|
- All text from .text and .text.* (including user code) */
|
||||||
|
.text : {
|
||||||
|
*(.pretext.entry)
|
||||||
|
*(.pretext)
|
||||||
|
|
||||||
|
_btors = . ;
|
||||||
|
*(.ctors .ctors.*)
|
||||||
|
_mtors = . ;
|
||||||
|
*(.dtors .dtors.*)
|
||||||
|
_etors = . ;
|
||||||
|
|
||||||
|
*(.text .text.*)
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
/* Interrupt handlers going to ROM:
|
||||||
|
- gint's interrupt handler blocks (.gint.blocks)
|
||||||
|
|
||||||
|
Although gint's blocks end up in VBR space, they are installed at
|
||||||
|
startup by the library/drivers, so we store them here for now */
|
||||||
|
.gint.blocks : {
|
||||||
|
KEEP(*(.gint.blocks));
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
/* Driver data going to ROM:
|
||||||
|
- Exposed driver interfaces (.gint.drivers)
|
||||||
|
|
||||||
|
The driver information is required to start and configure the
|
||||||
|
driver, even if the symbols are not referenced */
|
||||||
|
.gint.drivers : {
|
||||||
|
_bdrv = . ;
|
||||||
|
KEEP(*(.gint.drivers));
|
||||||
|
_edrv = . ;
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
/* Read-only data going to ROM:
|
||||||
|
- Resources or assets from fxconv or similar converters
|
||||||
|
- Data marked read-only by the compiler (.rodata and .rodata.*) */
|
||||||
|
.rodata : SUBALIGN(4) {
|
||||||
|
/* Put these first, they need to be 4-aligned */
|
||||||
|
*(.rodata.assets)
|
||||||
|
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
} > rom
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** RAM sections
|
||||||
|
*/
|
||||||
|
|
||||||
|
. = ORIGIN(ram);
|
||||||
|
|
||||||
|
/* BSS data going to RAM. The BSS section is to be stripped from the
|
||||||
|
ELF file later, and wiped at startup */
|
||||||
|
.bss (NOLOAD) : {
|
||||||
|
_rbss = . ;
|
||||||
|
|
||||||
|
*(.bss COMMON)
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
} > ram :NONE
|
||||||
|
|
||||||
|
_sbss = SIZEOF(.bss);
|
||||||
|
|
||||||
|
/* Read-write data sextions going to RAM (.data and .data.*) */
|
||||||
|
.data ALIGN(4) : ALIGN(4) {
|
||||||
|
_ldata = LOADADDR(.data);
|
||||||
|
_rdata = . ;
|
||||||
|
|
||||||
|
*(.data .data.*)
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
} > ram AT> rom
|
||||||
|
|
||||||
|
_sdata = SIZEOF(.data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** gint-related sections
|
||||||
|
** 88160000:4k VBR space
|
||||||
|
** 88161000:4k .gint.data and .gint.bss
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* VBR address: let's just start at the beginning of the RAM area.
|
||||||
|
There's an unused 0x100-byte gap at the start of the VBR space.
|
||||||
|
The VBR space is already a large block (> 2 kiB), so I'm cutting off
|
||||||
|
the gap to spare some memory */
|
||||||
|
_gint_vbr = 0x8815ff00;
|
||||||
|
|
||||||
|
. = ORIGIN(rram);
|
||||||
|
|
||||||
|
/* gint's data section, going to static RAM. This section contains many
|
||||||
|
small objects from the library (static/global variables, etc) */
|
||||||
|
.gint.data ALIGN(4) : ALIGN(4) {
|
||||||
|
_lgdata = LOADADDR(.gint.data);
|
||||||
|
_rgdata = . ;
|
||||||
|
|
||||||
|
*(.gint.data .gint.data.*)
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
} > rram AT> rom
|
||||||
|
|
||||||
|
_sgdata = SIZEOF(.gint.data);
|
||||||
|
|
||||||
|
/* gint's uninitialized BSS section, going to static RAM. All the large
|
||||||
|
data arrays will be located here */
|
||||||
|
.gint.bss (NOLOAD) : {
|
||||||
|
/* Since it's uninitialized, the location doesn't matter */
|
||||||
|
*(.gint.bss .gint.bss.*)
|
||||||
|
|
||||||
|
. = ALIGN(16);
|
||||||
|
} > rram :NONE
|
||||||
|
|
||||||
|
_sgbss = SIZEOF(.gint.bss);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Other sections
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Unwanted sections going to meet Dave Null:
|
||||||
|
- Java classes registration (why are there even here?)
|
||||||
|
- Asynchronous unwind tables: no C++ exception handling for now ^^
|
||||||
|
- Comments or anything the compiler might put in its assembler */
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.jcr)
|
||||||
|
*(.eh_frame_hdr)
|
||||||
|
*(.eh_frame)
|
||||||
|
*(.comment)
|
||||||
|
}
|
||||||
|
}
|
24
include/core/bootlog.h
Normal file
24
include/core/bootlog.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
//---
|
||||||
|
// gint:core:bootlog - Boot-time on-screen log for extreme debugging
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_CORE_BOOTLOG
|
||||||
|
#define GINT_CORE_BOOTLOG
|
||||||
|
|
||||||
|
/* bootlog_loaded() - Section loading stage
|
||||||
|
Called when RAM sections have been wiped and copied */
|
||||||
|
void bootlog_loaded(void);
|
||||||
|
|
||||||
|
/* bootlog_mapped() - ROM mapping stage
|
||||||
|
Called after all ROM pages have been traversed. All of them may not have
|
||||||
|
been mapped.
|
||||||
|
@rom Amount of mapped ROM, in bytes
|
||||||
|
@ram Amount of mapped RAM, in bytes */
|
||||||
|
void bootlog_mapped(uint32_t rom, uint32_t ram);
|
||||||
|
|
||||||
|
/* bootlog_kernel() - Gint loading stage
|
||||||
|
Called when gint has been installed, the VBR switched and the interrupt
|
||||||
|
handlers set up. */
|
||||||
|
void bootlog_kernel(void);
|
||||||
|
|
||||||
|
#endif /* GINT_CORE_BOOTLOG */
|
390
include/core/intc.h
Normal file
390
include/core/intc.h
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
//---
|
||||||
|
// gint:core:intc - Interrupt Controller
|
||||||
|
//
|
||||||
|
// The interrupt controller is unwieldy because SH7705 and SH7305 have a
|
||||||
|
// completely different interface. Everything here is split up and you'll
|
||||||
|
// have to explicitly handle both to be able to use the device.
|
||||||
|
//
|
||||||
|
// gint's API provides higher-level and platform-agnostic interrupt
|
||||||
|
// management. This is probably what you are looking for.
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_CORE_INTC
|
||||||
|
#define GINT_CORE_INTC
|
||||||
|
|
||||||
|
#include <defs/types.h>
|
||||||
|
|
||||||
|
//---
|
||||||
|
// SH7705 Interrupt Controller. Refer to:
|
||||||
|
// "Renesas SH7705 Group Hardware Manual"
|
||||||
|
// Section 6: "Interrupt Controller (INTC)"
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* sh7705_intc_ipc_t - Interrupt Priority Controller
|
||||||
|
A set of 16-bit register that control the interrupt priorities. The SH7705's
|
||||||
|
IPC has its registers scattered everywhere in the memory, so there is a
|
||||||
|
pointer for each register. The SH7305 needs only one pointer for the whole
|
||||||
|
IPC because the registers are in a contiguous area. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
volatile word_union(*IPRA,
|
||||||
|
uint TMU0 :4; /* Timer 0 */
|
||||||
|
uint TMU1 :4; /* Timer 1 */
|
||||||
|
uint TMU2 :4; /* Timer 2 */
|
||||||
|
uint RTC :4; /* Real-Time Clock */
|
||||||
|
);
|
||||||
|
|
||||||
|
volatile word_union(*IPRB,
|
||||||
|
uint WDT :4; /* Watchdog Timer */
|
||||||
|
uint REF :4; /* BSC Refresh Request, SDRAM (?) */
|
||||||
|
uint :4;
|
||||||
|
uint :4;
|
||||||
|
);
|
||||||
|
|
||||||
|
volatile word_union(*IPRC,
|
||||||
|
uint IRQ3 :4; /* Interrupt request 3 */
|
||||||
|
uint IRQ2 :4; /* Interrupt request 2 */
|
||||||
|
uint IRQ1 :4; /* Interrupt request 1 */
|
||||||
|
uint IRQ0 :4; /* Interrupt request 0 */
|
||||||
|
);
|
||||||
|
|
||||||
|
volatile word_union(*IPRD,
|
||||||
|
uint PINT0_7 :4; /* External interrupt pins 0 to 7 */
|
||||||
|
uint PINT8_15 :4; /* External interrupt pins 8 to 15 */
|
||||||
|
uint IRQ5 :4; /* Interrupt request 5 */
|
||||||
|
uint IRQ4 :4; /* Interrupt request 4 */
|
||||||
|
);
|
||||||
|
|
||||||
|
volatile word_union(*IPRE,
|
||||||
|
uint DMAC :4; /* Direct Memory Access Controller */
|
||||||
|
uint SCIF0 :4; /* Serial Communication Interface 0 */
|
||||||
|
uint SCIF2 :4; /* Serial Communication Interface 2 */
|
||||||
|
uint ADC :4; /* Analog/Decimal Converter */
|
||||||
|
);
|
||||||
|
|
||||||
|
volatile word_union(*IPRF,
|
||||||
|
uint :4;
|
||||||
|
uint :4;
|
||||||
|
uint USB :4; /* USB Controller */
|
||||||
|
uint :4;
|
||||||
|
);
|
||||||
|
|
||||||
|
volatile word_union(*IPRG,
|
||||||
|
uint TPU0 :4; /* Timer Pulse Unit 0 */
|
||||||
|
uint TPU1 :4; /* Timer Pulse Unit 1 */
|
||||||
|
uint :4;
|
||||||
|
uint :4;
|
||||||
|
);
|
||||||
|
|
||||||
|
volatile word_union(*IPRH,
|
||||||
|
uint TPU2 :4; /* Timer Pulse Unit 2 */
|
||||||
|
uint TPU3 :4; /* Timer Pulse Unit 3 */
|
||||||
|
uint :4;
|
||||||
|
uint :4;
|
||||||
|
);
|
||||||
|
|
||||||
|
} PACKED(4) sh7705_intc_ipc_t;
|
||||||
|
|
||||||
|
/* sh7705_intc_icr1_t - Interrupt Control Register 1 (general) */
|
||||||
|
typedef word_union(sh7705_intc_icr1_t,
|
||||||
|
uint MAI :1; /* Mask All Interrupts */
|
||||||
|
uint IRQLVL :1; /* Interrupt Request Level Detect */
|
||||||
|
uint BLMSK :1; /* Enable NMI when BL is set */
|
||||||
|
uint :1;
|
||||||
|
uint IRQ5E :2; /* IRQ 5 Edge Detection */
|
||||||
|
uint IRQ4E :2; /* etc. */
|
||||||
|
uint IRQ3E :2;
|
||||||
|
uint IRQ2E :2;
|
||||||
|
uint IRQ1E :2;
|
||||||
|
uint IRQ0E :2;
|
||||||
|
);
|
||||||
|
|
||||||
|
/* sh7705_intc_t - the SH7705 interrupt controller */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* All interrupt priority registers */
|
||||||
|
union {
|
||||||
|
sh7705_intc_ipc_t _;
|
||||||
|
volatile uint16_t *IPRS[8];
|
||||||
|
} PACKED(4);
|
||||||
|
|
||||||
|
/* Control registers */
|
||||||
|
volatile sh7705_intc_icr1_t *ICR1;
|
||||||
|
|
||||||
|
} PACKED(4) sh7705_intc_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//---
|
||||||
|
// SH7305 Interrupt Controller. Refer to:
|
||||||
|
// "Renesas SH7724 User's Manual: Hardware"
|
||||||
|
// Section 13: "Interrupt Controller (INTC)"
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* sh7305_intc_ipc_t - Interrupt Priority Controller
|
||||||
|
Some of the fields have been left unnamed because they correspond to SH7724
|
||||||
|
peripheral modules that are *very* unlikely to even exist in the SH7305, let
|
||||||
|
alone by of any use to us */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
word_union(IPRA,
|
||||||
|
uint TMU0_0 :4; /* TMU0 Channel 0 */
|
||||||
|
uint TMU0_1 :4; /* TMU0 Channel 1 */
|
||||||
|
uint TMU0_2 :4; /* TMU0 Channel 2 */
|
||||||
|
uint IrDA :4; /* Infrared Communication */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRB,
|
||||||
|
uint :4; /* JPEG Processing Unit */
|
||||||
|
uint LCDC :4; /* LCD Controller */
|
||||||
|
uint DMAC1A :4; /* Direct Memory Access Controller 1 */
|
||||||
|
uint :4; /* Blending Engine Unit */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRC,
|
||||||
|
uint TMU1_0 :4; /* TMU1 Channel 0 */
|
||||||
|
uint TMU1_1 :4; /* TMU1 Channel 1 */
|
||||||
|
uint TMU1_2 :4; /* TMU1 Channel 2 */
|
||||||
|
uint :4; /* Sound Processing Unit */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRD,
|
||||||
|
uint :4;
|
||||||
|
uint MMCIF :4; /* MultiMedia Card Interface */
|
||||||
|
uint :4;
|
||||||
|
uint :4; /* ATAPI Interface */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRE,
|
||||||
|
uint DMAC0A :4; /* Direct Memory Access Controller 0 */
|
||||||
|
uint :4; /* Various graphical engines */
|
||||||
|
uint SCIFA3 :4; /* SCIFA channel 3 interrupt */
|
||||||
|
uint :4; /* Video Processing Unit */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRF,
|
||||||
|
uint KEYSC :4; /* Key Scan Interface */
|
||||||
|
uint DMACOB :4; /* DMAC0 transfer/error info */
|
||||||
|
uint USB0_1 :4; /* USB controller */
|
||||||
|
uint CMT :4; /* Compare Match Timer */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRG,
|
||||||
|
uint SCIF0 :4; /* SCIF0 transfer/error info */
|
||||||
|
uint SCIF1 :4; /* SCIF1 transfer/error info */
|
||||||
|
uint SCIF2 :4; /* SCIF2 transfer/error info */
|
||||||
|
uint :4; /* Video Engine Unit */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRH,
|
||||||
|
uint MSIOF0 :4; /* Clock-synchronized SCIF channel 0 */
|
||||||
|
uint MSIOF1 :4; /* Clock-synchronized SCIF channel 1 */
|
||||||
|
uint :4; /* I2C Interface channel 0 */
|
||||||
|
uint :4; /* I2C Interface channel 1 */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRI,
|
||||||
|
uint SCIFA4 :4; /* SCIFA channel 4 interrupt */
|
||||||
|
uint :4; /* MediaRAM InterConnected Buffers */
|
||||||
|
uint :4; /* Transport Stream Interface */
|
||||||
|
uint :4; /* 2D Graphics Accelerator & ICB */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRJ,
|
||||||
|
uint :4; /* Capture Engine Unit */
|
||||||
|
uint :4; /* Ethernet Memory Access Controller */
|
||||||
|
uint FSI :4; /* FIFO-Buffered Serial Interface */
|
||||||
|
uint SDHI1 :4; /* SD Card Host Interface channel 1 */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRK,
|
||||||
|
uint RTC :4; /* Real-Time Clock */
|
||||||
|
uint DMAC1B :4; /* DMAC1 transfer/error info */
|
||||||
|
uint :4; /* MediaRAM InterConnected Buffers */
|
||||||
|
uint SDHI0 :4; /* SD Card Host Interface channel 0 */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
word_union(IPRL,
|
||||||
|
uint SCIFA5 :4; /* SCIFA channel 5 interrupt */
|
||||||
|
uint :4;
|
||||||
|
uint TPU :4; /* Timer-Pulse Unit */
|
||||||
|
uint :4; /* Image Extraction DMAC */
|
||||||
|
);
|
||||||
|
pad(2);
|
||||||
|
|
||||||
|
} PACKED(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.
|
||||||
|
Writing 0 is ignored; reading from IMCRs yields undefined values */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
byte_union(IMR0,
|
||||||
|
uint :1;
|
||||||
|
uint TUNI1_2 :1; /* TMU1 overflow interrupts */
|
||||||
|
uint TUNI1_1 :1;
|
||||||
|
uint TUNI1_0 :1;
|
||||||
|
uint SDHII3 :1; /* SD Card Host 1 interrupts */
|
||||||
|
uint SDHII2 :1;
|
||||||
|
uint SDHII1 :1;
|
||||||
|
uint SDHII0 :1;
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR1,
|
||||||
|
uint :4;
|
||||||
|
uint DEI3 :1; /* DMAC0A interrupts */
|
||||||
|
uint DEI2 :1;
|
||||||
|
uint DEI1 :1;
|
||||||
|
uint DEI0 :1;
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR2,
|
||||||
|
uint :7;
|
||||||
|
uint SCIFA0 :1; /* Asynchronous Serial interrupts */
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR3,
|
||||||
|
uint DEI3 :1; /* DMAC1A interrupts */
|
||||||
|
uint DEI2 :1;
|
||||||
|
uint DEI1 :1;
|
||||||
|
uint DEI0 :1;
|
||||||
|
uint :4;
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR4,
|
||||||
|
uint :1;
|
||||||
|
uint TUNI0_2 :1; /* TMU0 overflow interrupts */
|
||||||
|
uint TUNI0_1 :1;
|
||||||
|
uint TUNI0_0 :1;
|
||||||
|
uint :3;
|
||||||
|
uint LCDCI :1; /* LCD Controller Interrupt */
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR5,
|
||||||
|
uint KEYI :1; /* Key Interface */
|
||||||
|
uint DADERR :1; /* DMAC0B interrupts */
|
||||||
|
uint DEI5 :1;
|
||||||
|
uint DEI4 :1;
|
||||||
|
uint :1;
|
||||||
|
uint SCIF2 :1; /* Serial Communication Interface */
|
||||||
|
uint SCIF1 :1;
|
||||||
|
uint SCIF0 :1;
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR6,
|
||||||
|
uint :2;
|
||||||
|
uint :1;
|
||||||
|
uint SCIFA4 :1; /* SCIFA4 interrupt */
|
||||||
|
uint :1;
|
||||||
|
uint :1;
|
||||||
|
uint MSIOFI0 :1; /* Clock-synchronized SCIF channel 0 */
|
||||||
|
uint MSIOFI1 :1; /* Clock-synchronized SCIF channel 1 */
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
uint8_t IMR7;
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR8,
|
||||||
|
uint SDHII3 :1; /* SD Card Host 0 interrupts */
|
||||||
|
uint SDHII2 :1;
|
||||||
|
uint SDHII1 :1;
|
||||||
|
uint SDHII0 :1;
|
||||||
|
uint :2;
|
||||||
|
uint SCFIA5 :1; /* SCIFA5 interrupt */
|
||||||
|
uint FSI :1; /* FIFO-Buffered Serial Interface */
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR9,
|
||||||
|
uint :3;
|
||||||
|
uint CMTI :1; /* Compare Match Timer Interrupt */
|
||||||
|
uint :1;
|
||||||
|
uint USI1 :1; /* USB1 */
|
||||||
|
uint USI0 :1; /* USB0 */
|
||||||
|
uint :1;
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR10,
|
||||||
|
uint :1;
|
||||||
|
uint DADERR :1; /* DMAC1B interrupts */
|
||||||
|
uint DEI5 :1;
|
||||||
|
uint DEI4 :1;
|
||||||
|
uint :1;
|
||||||
|
uint ATI :1; /* RTC Alarm interrupt */
|
||||||
|
uint PRI :1; /* RTC Periodic interrupt */
|
||||||
|
uint CUI :1; /* RTC Carry interrupt */
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
byte_union(IMR11,
|
||||||
|
uint :5;
|
||||||
|
uint TPUI :1; /* Timer-Pulse Unit */
|
||||||
|
uint :2;
|
||||||
|
);
|
||||||
|
pad(3);
|
||||||
|
|
||||||
|
uint8_t IMR12;
|
||||||
|
|
||||||
|
} PACKED(4) sh7305_intc_masks_t;
|
||||||
|
|
||||||
|
/* sh7305_intc_userimask_t - User Interrupt Mask
|
||||||
|
Sets the minimum required level for interrupts to be accepted.
|
||||||
|
|
||||||
|
WARNING: Writing to this register is only allowed when the upper bits of the
|
||||||
|
operand (ie. the new value of USERIMASK) are 0xa5; otherwise, the write is
|
||||||
|
ignored. To modify the value of this register, do not access the bit field
|
||||||
|
directly, backup the variable and modify it:
|
||||||
|
|
||||||
|
void set_user_imask(int new_level)
|
||||||
|
{
|
||||||
|
sh7305_intc_userimask_t mask = *(INTC._7305.USERIMASK);
|
||||||
|
mask._0xa5 = 0xa5;
|
||||||
|
mask.UIMASK = new_level & 0x0f;
|
||||||
|
*(INTC._7305.USERIMASK) = mask;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
typedef lword_union(sh7305_intc_userimask_t,
|
||||||
|
uint _0xa5 :8; /* Always set to 0xa5 before writing */
|
||||||
|
uint :16;
|
||||||
|
uint UIMASK :4; /* User Interrupt Mask Level */
|
||||||
|
uint :4;
|
||||||
|
);
|
||||||
|
|
||||||
|
/* sh7305_intc_t - the SH7305 interrupt controller */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* Interrupt priority registers */
|
||||||
|
union {
|
||||||
|
volatile sh7305_intc_ipc_t *_;
|
||||||
|
volatile uint16_t *IPRS;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interrupt mask & mask clear registers */
|
||||||
|
volatile sh7305_intc_masks_t *MSK;
|
||||||
|
volatile sh7305_intc_masks_t *MSKCLR;
|
||||||
|
|
||||||
|
/* Other registers */
|
||||||
|
volatile sh7305_intc_userimask_t *USERIMASK;
|
||||||
|
|
||||||
|
} PACKED(4) sh7305_intc_t;
|
||||||
|
|
||||||
|
#endif /* GINT_CORE_INTC */
|
63
include/core/mmu.h
Normal file
63
include/core/mmu.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
//---
|
||||||
|
// gint: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.
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_CORE_MMU
|
||||||
|
#define GINT_CORE_MMU
|
||||||
|
|
||||||
|
#include <defs/attributes.h>
|
||||||
|
#include <defs/types.h>
|
||||||
|
|
||||||
|
/* utlb_addr_t - address part of a UTLB entry */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint VPN :22;
|
||||||
|
uint D :1;
|
||||||
|
uint V :1;
|
||||||
|
uint ASID :8;
|
||||||
|
|
||||||
|
} PACKED(4) utlb_addr_t;
|
||||||
|
|
||||||
|
/* utlb_addr() - get the P4 address of a UTLB address entry
|
||||||
|
@E Entry number (should be in range 0..63)
|
||||||
|
Returns a pointer to the entry. */
|
||||||
|
const utlb_addr_t *utlb_addr(uint E);
|
||||||
|
|
||||||
|
/* utlb_data_t - data part of a UTLB entry */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint :3;
|
||||||
|
uint PPN :19;
|
||||||
|
uint :1;
|
||||||
|
uint V :1;
|
||||||
|
uint SZ1 :1;
|
||||||
|
uint PR :2;
|
||||||
|
uint SZ2 :1;
|
||||||
|
uint C :1;
|
||||||
|
uint D :1;
|
||||||
|
uint SH :1;
|
||||||
|
uint WT :1;
|
||||||
|
|
||||||
|
} PACKED(4) utlb_data_t;
|
||||||
|
|
||||||
|
/* utlb_data() - get the P4 address of a UTLB data entry
|
||||||
|
@E Entry number (should be in range 0..63)
|
||||||
|
Returns a pointer to the entry. */
|
||||||
|
const utlb_data_t *utlb_data(uint E);
|
||||||
|
|
||||||
|
/* utlb_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
|
||||||
|
RAM 08100000:512k
|
||||||
|
Other mappings are ignored. Both pointers may be NULL.
|
||||||
|
|
||||||
|
@rom Pointer to amount of mapped ROM
|
||||||
|
@ram Pointer to amount of mapped RAM */
|
||||||
|
void utlb_mapped_memory(uint32_t *rom, uint32_t *ram);
|
||||||
|
|
||||||
|
#endif /* GINT_CORE_MMU */
|
52
include/core/mpu.h
Normal file
52
include/core/mpu.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
//---
|
||||||
|
// gint:core:mpu - Runtime MPU detection
|
||||||
|
//
|
||||||
|
// This component detects the architecture and MPU type of the underlying
|
||||||
|
// hardware by relying on version registers and/or side-information. It
|
||||||
|
// provides macros isSH3() and isSH4() to perform MPU-dependent jobs:
|
||||||
|
//
|
||||||
|
// if(isSH3()) { ... } else { ... }
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_CORE_MPU
|
||||||
|
#define GINT_CORE_MPU
|
||||||
|
|
||||||
|
#include <defs/attributes.h>
|
||||||
|
|
||||||
|
/* mpu_t - Supported MPUs */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
mpu_unknown = 0,
|
||||||
|
mpu_sh7337 = 1, /* fx9860g, SH-3 based */
|
||||||
|
mpu_sh7305 = 2, /* fx9860g II, fxcg50, SH-4A based */
|
||||||
|
mpu_sh7355 = 3, /* fx9860g II, SH-3 based */
|
||||||
|
mpu_sh7724 = 4, /* For reference */
|
||||||
|
} mpu_t;
|
||||||
|
|
||||||
|
#ifdef FX9860G
|
||||||
|
|
||||||
|
/* mpu_id() - get the name of the underlying MPU */
|
||||||
|
HDRFUNC mpu_t mpu_id(void)
|
||||||
|
{
|
||||||
|
extern const mpu_t mpu;
|
||||||
|
return mpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quick SH-3/SH-4 tests. Unknown models are assumed to be SH-4A */
|
||||||
|
#define isSH3() (mpu_id() & 1)
|
||||||
|
#define isSH4() (!isSH3())
|
||||||
|
|
||||||
|
/* mpu_init() - probe the MPU type
|
||||||
|
This function must be executed before mpu_id() can be used. */
|
||||||
|
void mpu_init(void);
|
||||||
|
|
||||||
|
#else /* FXCG50 */
|
||||||
|
|
||||||
|
/* All fxcg50 machines have an SH7305, which makes things simpler. */
|
||||||
|
#define mpu_id() mpu_sh7305
|
||||||
|
#define isSH3() 0
|
||||||
|
#define isSH4() 1
|
||||||
|
|
||||||
|
#endif /* FX9860G */
|
||||||
|
|
||||||
|
#endif /* GINT_CORE_MPU */
|
23
include/core/setup.h
Normal file
23
include/core/setup.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
//---
|
||||||
|
// gint:core:setup - Installing and unloading the library
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_CORE_SETUP
|
||||||
|
#define GINT_CORE_SETUP
|
||||||
|
|
||||||
|
#include <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.
|
||||||
|
|
||||||
|
@vbr New VBR address
|
||||||
|
@configure Configuration function
|
||||||
|
Returns the previous VBR address. */
|
||||||
|
uint32_t gint_setvbr(uint32_t vbr, void (*configure)(void));
|
||||||
|
|
||||||
|
#endif /* GINT_CORE_SETUP */
|
35
include/defs/attributes.h
Normal file
35
include/defs/attributes.h
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
//---
|
||||||
|
// gint:defs:attributes - Macros for compiler-specific attributes
|
||||||
|
//---
|
||||||
|
|
||||||
|
#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)))
|
||||||
|
/* Objects from the .gint.data and .gint.bss sections */
|
||||||
|
#define GDATA __attribute__((section(".gint.data")))
|
||||||
|
#define GBSS __attribute__((section(".gint.bss")))
|
||||||
|
/* Initialization functions */
|
||||||
|
#define PRETEXT __attribute__((section(".pretext")))
|
||||||
|
|
||||||
|
/* Unused parameters or variables */
|
||||||
|
#define UNUSED __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)))
|
||||||
|
|
||||||
|
/* 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)))
|
||||||
|
|
||||||
|
#endif /* GINT_DEFS_ATTRIBUTES */
|
78
include/defs/types.h
Normal file
78
include/defs/types.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
//---
|
||||||
|
// 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 */
|
53
include/drivers/t6k11.h
Normal file
53
include/drivers/t6k11.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
//---
|
||||||
|
// gint:drivers:t6k11 - Toshiba T6K11 driver
|
||||||
|
//
|
||||||
|
// This is the screen driver used by fx9860g (monochrome) models.
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_DRIVERS_T6K11
|
||||||
|
#define GINT_DRIVERS_T6K11
|
||||||
|
|
||||||
|
#include <defs/types.h>
|
||||||
|
|
||||||
|
/* t6k11_display() - send vram data to the LCD device
|
||||||
|
|
||||||
|
A typical 128*64 VRAM area would use y1 = 0, y2 = 64, stride = 16. It is
|
||||||
|
possible to send only a section of the video RAM by specifying y1 > 0 or
|
||||||
|
y2 < 64 and moving the vram pointer accordingly.
|
||||||
|
|
||||||
|
@vram Video RAM address
|
||||||
|
@y1 First row to send
|
||||||
|
@y2 Last row to send + 1
|
||||||
|
@stride Number of bytes between each row */
|
||||||
|
void t6k11_display(const void *vram, int y1, int y2, size_t stride);
|
||||||
|
|
||||||
|
/* t6k11_contrast() - change the contrast setting
|
||||||
|
|
||||||
|
Adjusts the screen contrast. The parameter takes value in range 0 .. 32 and
|
||||||
|
is adjusted when not in range.
|
||||||
|
|
||||||
|
0 (bright) <-------- 14 (OS default) --------> 32 (dark)
|
||||||
|
|
||||||
|
It is not possible to read the contrast value from the display driver, but
|
||||||
|
the system stores its contrast setting in RAM. The location is OS-dependent.
|
||||||
|
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
|
||||||
|
|
||||||
|
@contrast Requested contrast value */
|
||||||
|
void t6k11_contrast(int contrast);
|
||||||
|
|
||||||
|
/* t6k11_backlight() - manage the screen backlight
|
||||||
|
|
||||||
|
Changes the backlight setting depending on the value of "setting":
|
||||||
|
- If setting = 0, turns the backlight off.
|
||||||
|
- If setting > 0, turns the backlight on.
|
||||||
|
- If setting < 0, toggles backlight.
|
||||||
|
|
||||||
|
This function has no effect on models that do not support the backlight.
|
||||||
|
|
||||||
|
@setting Requested backlight setting */
|
||||||
|
void t6k11_backlight(int setting);
|
||||||
|
|
||||||
|
#endif /* GINT_DRIVERS_T6K11 */
|
8
include/gint/display-fx.h
Normal file
8
include/gint/display-fx.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
//---
|
||||||
|
// gint:display-fx - fx9860g drawing functions
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_DISPLAY_FX
|
||||||
|
#define GINT_DISPLAY_FX
|
||||||
|
|
||||||
|
#endif /* GINT_DISPLAY_FX */
|
32
include/gint/display.h
Normal file
32
include/gint/display.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
//---
|
||||||
|
// gint:display - Drawing functions
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_DISPLAY
|
||||||
|
#define GINT_DISPLAY
|
||||||
|
|
||||||
|
/* 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:
|
||||||
|
[fx9860g] A 4-aligned buffer of size 1024.
|
||||||
|
[fxcg50] A 4-aligned buffer of size 177408.
|
||||||
|
|
||||||
|
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 ;
|
||||||
|
- 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 ^^ */
|
||||||
|
|
||||||
|
#ifdef FX9860G
|
||||||
|
#include <gint/display-fx.h>
|
||||||
|
#else
|
||||||
|
#include <gint/display-cg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* GINT_DISPLAY */
|
47
include/gint/drivers.h
Normal file
47
include/gint/drivers.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
//---
|
||||||
|
// gint:drivers - General tools for drivers
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_DRIVERS
|
||||||
|
#define GINT_DRIVERS
|
||||||
|
|
||||||
|
#include <defs/attributes.h>
|
||||||
|
#include <defs/types.h>
|
||||||
|
|
||||||
|
/* gint_driver_t - driver meta-information used by gint */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* Driver name */
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
/* Size of a context object for the driver */
|
||||||
|
uint ctx_size;
|
||||||
|
|
||||||
|
/* System context. The driver has to allocate a buffer of size at least
|
||||||
|
ctx_size, where gint stores the system's configuration. It is
|
||||||
|
advised to place this buffer in the .gint_bss section using the GBSS
|
||||||
|
macro of <defs/attributes.h> if it doesn't need to be initialized */
|
||||||
|
void *sys_ctx;
|
||||||
|
|
||||||
|
/* ctx_save() - save the driver's hardware support
|
||||||
|
This function is provided by the driver to save the state of its
|
||||||
|
hardware support (memory-mapped MPU registers, port state, etc).
|
||||||
|
@ctx A buffer of size ctx_size */
|
||||||
|
void (*ctx_save)(void *ctx);
|
||||||
|
|
||||||
|
/* ctx_restore() - restore a saved context
|
||||||
|
This function is provided by the driver to restore the state saved
|
||||||
|
by ctx_save(). It can alter the contents of the buffer freely.
|
||||||
|
@ctx A context buffer filled by ctx_save() */
|
||||||
|
void (*ctx_restore)(void *ctx);
|
||||||
|
|
||||||
|
} PACKED(4) gint_driver_t;
|
||||||
|
|
||||||
|
/* GINT_DECLARE_DRIVER() - make a driver visible to gint
|
||||||
|
Use this macro to expose a driver by passing it the name of a gint_driver_t
|
||||||
|
structure. This macro moves the structure to the .gint.drivers section,
|
||||||
|
which is automatically traversed at startup */
|
||||||
|
#define GINT_DECLARE_DRIVER(name) \
|
||||||
|
SECTION(".gint.drivers") extern gint_driver_t name;
|
||||||
|
|
||||||
|
#endif /* GINT_DRIVERS */
|
59
include/gint/gint.h
Normal file
59
include/gint/gint.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
//---
|
||||||
|
// gint - An alternative runtime environment for fx9860g and fxcg50
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifndef GINT_GINT
|
||||||
|
#define GINT_GINT
|
||||||
|
|
||||||
|
#include <defs/attributes.h>
|
||||||
|
#include <defs/types.h>
|
||||||
|
|
||||||
|
/* gint_version() - get the library version number
|
||||||
|
Returns gint's running version number, which is made of four fields:
|
||||||
|
|
||||||
|
31 24 23 20 19 16 15 0
|
||||||
|
+---------------+---------------+---------------+---------------+
|
||||||
|
| channel | major | minor | build |
|
||||||
|
+---------------+---------------+---------------+---------------+
|
||||||
|
|
||||||
|
The first field is a letter indicating the type of version ('a'lpha, 'b'eta,
|
||||||
|
'r'elease, 'd'ev, etc). The second and third field are the version number on
|
||||||
|
the form "major.minor". The last field is the build number for this version.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Library management
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* gint_install() - install and start gint
|
||||||
|
This function installs event handlers, masks interrupts and switches VBR.
|
||||||
|
Unless you are doing experimental runtime switching and you know how this
|
||||||
|
function is implemented, you should not call it. */
|
||||||
|
void gint_install(void);
|
||||||
|
|
||||||
|
/* gint_unload() - unload gint and give back control to the system
|
||||||
|
This function restores the runtime environment saved by gint_install(). It
|
||||||
|
is only called when the add-in terminates. To temporarily leave gint during
|
||||||
|
execution, use gint_pause(). When possible, use syscalls without leaving
|
||||||
|
gint for better performance. */
|
||||||
|
void gint_unload(void);
|
||||||
|
|
||||||
|
/* gint_pause() - return to main menu, with possibility of coming back
|
||||||
|
|
||||||
|
This function safely invokes the calculator's main menu by unloading gint.
|
||||||
|
If the user selects the gint application again in the menu, this function
|
||||||
|
reloads gint and returns. Otherwise, the add-in is fully unloaded by the
|
||||||
|
system and the application terminates.
|
||||||
|
|
||||||
|
This function is typically called when the [MENU] key is pressed during a
|
||||||
|
getkey() call. */
|
||||||
|
void gint_pause(void);
|
||||||
|
|
||||||
|
#endif /* GINT_GINT */
|
117
src/core/bootlog.c
Normal file
117
src/core/bootlog.c
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
//---
|
||||||
|
// gint:core:bootlog - Boot-time on-screen log for extreme debugging
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <defs/types.h>
|
||||||
|
#include <core/intc.h>
|
||||||
|
#include <core/mpu.h>
|
||||||
|
#include <core/mmu.h>
|
||||||
|
#include <gint/gint.h>
|
||||||
|
|
||||||
|
/* Linker script symbols - see core/start.c for details */
|
||||||
|
extern char
|
||||||
|
brom, srom,
|
||||||
|
sgdata, sgbss, sdata, sbss,
|
||||||
|
btors, mtors, etors;
|
||||||
|
|
||||||
|
/* bootlog_loaded() - Section loading stage */
|
||||||
|
void bootlog_loaded(void)
|
||||||
|
{
|
||||||
|
/* Version string - the string constant resides in ROM */
|
||||||
|
uint32_t v = gint_version();
|
||||||
|
const char *model = "gint #0.0-000";
|
||||||
|
char str[14];
|
||||||
|
|
||||||
|
for(int i = 0; i < 14; i++) str[i] = model[i];
|
||||||
|
|
||||||
|
/* Quickly get the three digits of the build number */
|
||||||
|
int x_q = (v & 0xffff) / 10;
|
||||||
|
int x_r = (v & 0xffff) - 10 * x_q;
|
||||||
|
int y_q = x_q / 10;
|
||||||
|
int y_r = x_q - 10 * y_q;
|
||||||
|
|
||||||
|
str[5] = (v & 0xff000000) >> 24;
|
||||||
|
str[6] += (v & 0x00f00000) >> 20;
|
||||||
|
str[8] += (v & 0x000f0000) >> 16;
|
||||||
|
str[10] += y_q;
|
||||||
|
str[11] += y_r;
|
||||||
|
str[12] += x_r;
|
||||||
|
|
||||||
|
/* Size of memory sections */
|
||||||
|
uint32_t rom_size = (uint32_t)&srom;
|
||||||
|
uint32_t ram_size = (uint32_t)&sdata + (uint32_t)&sbss;
|
||||||
|
uint32_t gint_size = (uint32_t)&sgdata + (uint32_t)&sgbss;
|
||||||
|
|
||||||
|
/* MPU type */
|
||||||
|
mpu_t mpu = mpu_id();
|
||||||
|
const char *names = "SH7337\0 SH7305\0 SH7355\0 SH7724";
|
||||||
|
|
||||||
|
/* TODO: Use a solid API for boot-time printing */
|
||||||
|
Bdisp_AllClr_VRAM();
|
||||||
|
print(1, 1, str);
|
||||||
|
print(15, 1, " Loaded");
|
||||||
|
|
||||||
|
if((uint)mpu < 4) print(16, 2, names + 8 * (mpu - 1));
|
||||||
|
else print_dec(16, 2, mpu, 6);
|
||||||
|
|
||||||
|
print(1, 2, "ROM RAM GINT");
|
||||||
|
print(4, 3, "k c d");
|
||||||
|
print_dec(1, 3, (rom_size + 0x3ff) >> 10, 3);
|
||||||
|
print_dec(6, 3, ram_size, 4);
|
||||||
|
print_dec(11, 3, gint_size, 4);
|
||||||
|
print_dec(17, 3, &mtors - &btors, 2);
|
||||||
|
print_dec(20, 3, &etors - &mtors, 2);
|
||||||
|
Bdisp_PutDisp_DD();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bootlog_mapped() - ROM mapping stage */
|
||||||
|
void bootlog_mapped(int rom, int ram)
|
||||||
|
{
|
||||||
|
rom = (rom + 0x3ff) >> 10;
|
||||||
|
ram = (ram + 0x3ff) >> 10;
|
||||||
|
|
||||||
|
print(15, 1, " Mapped");
|
||||||
|
print(1, 4, "MMU ROM: k RAM: k");
|
||||||
|
(rom < 0) ? print(9, 4, "???") : print_dec(9, 4, rom, 3);
|
||||||
|
(ram < 0) ? print(18, 4, "???") : print_dec(18, 4, ram, 3);
|
||||||
|
Bdisp_PutDisp_DD();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bootlog_kernel() - Gint loading stage */
|
||||||
|
void bootlog_kernel(void)
|
||||||
|
{
|
||||||
|
if(isSH3())
|
||||||
|
{
|
||||||
|
#ifdef FX9860G
|
||||||
|
extern sh7705_intc_t INTC3;
|
||||||
|
|
||||||
|
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(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);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
extern sh7305_intc_t INTC4;
|
||||||
|
|
||||||
|
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(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bdisp_PutDisp_DD();
|
||||||
|
}
|
110
src/core/inth.S
Normal file
110
src/core/inth.S
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
** gint:core:inth - Interrupt handlers
|
||||||
|
** This file only contains the entry points because the gates are managed
|
||||||
|
** by device drivers. Each driver will supply its own interrupt handler
|
||||||
|
** blocks depending on its configuration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
.global _inth_entry_7305
|
||||||
|
|
||||||
|
#ifdef FX9860G
|
||||||
|
.global _inth_entry_7705
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.section .gint.blocks, "ax"
|
||||||
|
.align 4
|
||||||
|
|
||||||
|
/* Interrupt handlers
|
||||||
|
|
||||||
|
The .gint.blocks section consists of blocks of 32 bytes intended for mapping
|
||||||
|
into the VBR space (exception, TLB miss, and interrupt handlers). Each event
|
||||||
|
gate is linearly associated with a block number:
|
||||||
|
|
||||||
|
block_id = (event_code - 0x380) / 0x20
|
||||||
|
|
||||||
|
This file provides entry points; drivers may provide their own interrupt
|
||||||
|
handlers, and store them in the .gint.blocks section for consistency. They
|
||||||
|
should be aware of the consequences of reordering the blocks into the VBR
|
||||||
|
space:
|
||||||
|
|
||||||
|
- 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. */
|
||||||
|
|
||||||
|
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */
|
||||||
|
|
||||||
|
_inth_entry_7305:
|
||||||
|
/* Get the event code from the INTEVT register */
|
||||||
|
mov.l 1f, r0
|
||||||
|
/* TODO: mov.l @r0, r0 */
|
||||||
|
mov.l @r0, r4
|
||||||
|
|
||||||
|
/* Interrupt codes start at 0x400 */
|
||||||
|
mov #4, r1
|
||||||
|
shll8 r1
|
||||||
|
sub r1, r0
|
||||||
|
|
||||||
|
/* Jump to a C routine (TODO: Remove this) */
|
||||||
|
sts.l pr, @-r15
|
||||||
|
mov.l 2f, r0
|
||||||
|
jsr @r0
|
||||||
|
nop
|
||||||
|
lds.l @r15+, pr
|
||||||
|
rte
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* Add the distance between nop and the first entry, and jump
|
||||||
|
add #16, r0
|
||||||
|
braf r0
|
||||||
|
nop */
|
||||||
|
|
||||||
|
2: .long _debug
|
||||||
|
1: .long 0xff000028
|
||||||
|
|
||||||
|
#ifdef FX9860G
|
||||||
|
|
||||||
|
/* SH7705-TYPE INTERRUT HANDLER ENTRY - 32 BYTES */
|
||||||
|
|
||||||
|
_inth_entry_7705:
|
||||||
|
/* Get the event code from the INTEVT2 register */
|
||||||
|
mov.l 1f, r0
|
||||||
|
mov.l @r0, r0 /* r0 = old_code */
|
||||||
|
|
||||||
|
/* Translate the event code to SH4 format */
|
||||||
|
mov.l 2f, r2
|
||||||
|
mov #-5, r3
|
||||||
|
shld r3, r0 /* r0 = old_code >> 5 */
|
||||||
|
add #-32, r0 /* r0 = (old_code - 0x400) >> 5 */
|
||||||
|
mov.b @(r0, r2), r0 /* r0 = (new_code - 0x400) >> 5 */
|
||||||
|
mov #5, r3
|
||||||
|
shld r3, r0 /* r0 = new_code - 0x400 */
|
||||||
|
|
||||||
|
/* Add the distance between nop and the first entry, and jump */
|
||||||
|
add #8, r0
|
||||||
|
braf r0
|
||||||
|
nop
|
||||||
|
|
||||||
|
1: .long 0xa4000000 /* INTEVT2 register */
|
||||||
|
2: .long _inth_remap
|
||||||
|
|
||||||
|
|
||||||
|
.section .gint.data
|
||||||
|
.align 4
|
||||||
|
|
||||||
|
/* EVENT CODE TRANSLATION TABLE - 72 BYTES */
|
||||||
|
|
||||||
|
_inth_remap:
|
||||||
|
.byte 0x00, 0x01, 0x02, 0xfe, 0x34, 0x35, 0x36, 0xff
|
||||||
|
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f
|
||||||
|
.byte 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0xff, 0xff
|
||||||
|
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
|
.byte 0x20, 0x21, 0x22, 0x23, 0x28, 0x28, 0xff, 0x28
|
||||||
|
.byte 0x48, 0x48, 0xff, 0x48, 0xff, 0xff, 0xff, 0xff
|
||||||
|
.byte 0xff, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff
|
||||||
|
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||||
|
.byte 0x2d, 0x2d, 0xff, 0xff, 0x2d, 0x2d, 0xff, 0xff
|
||||||
|
|
||||||
|
#endif
|
107
src/core/memory.c
Normal file
107
src/core/memory.c
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#include <core/mpu.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static void memcpy4(uint32_t * restrict d, const void * restrict src, size_t n)
|
||||||
|
{
|
||||||
|
int modulo = (uintptr_t)src & 3;
|
||||||
|
|
||||||
|
/* Best case: perform 32-bit accesses only */
|
||||||
|
if(!modulo)
|
||||||
|
{
|
||||||
|
const uint32_t *s = src;
|
||||||
|
while(n--) *d++ = *s++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* Here's where SH-3 and SH-4A start working differently. SH-4A has a
|
||||||
|
2-cycle 'movua' instruction to perform unaligned reads */
|
||||||
|
else if(isSH4())
|
||||||
|
{
|
||||||
|
uint32_t longword;
|
||||||
|
const uint32_t *s = src;
|
||||||
|
|
||||||
|
while(n--)
|
||||||
|
{
|
||||||
|
__asm__(
|
||||||
|
"movua.l %1, %0"
|
||||||
|
: "=z"(longword)
|
||||||
|
: "m>"(*s)
|
||||||
|
);
|
||||||
|
s++;
|
||||||
|
*d++ = longword;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* On SH-3, we can only hope that there is 2-alignment */
|
||||||
|
else if(!(modulo & 1))
|
||||||
|
{
|
||||||
|
const uint16_t *s = src;
|
||||||
|
uint16_t * restrict dst = (void *)d;
|
||||||
|
|
||||||
|
while(n--)
|
||||||
|
{
|
||||||
|
*dst++ = *s++;
|
||||||
|
*dst++ = *s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Or just perform the raw copy */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const uint8_t *s = src;
|
||||||
|
uint8_t * restrict dst = (void *)d;
|
||||||
|
|
||||||
|
while(n--) *dst++ = *s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memcpy(void * restrict dst, const void * restrict src, size_t n)
|
||||||
|
{
|
||||||
|
uint8_t *d = dst;
|
||||||
|
const uint8_t *s = src;
|
||||||
|
|
||||||
|
/* Small areas: don't bother with complex methods */
|
||||||
|
if(n < 32)
|
||||||
|
{
|
||||||
|
while(n--) *d++ = *s++;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a longword offset to perform word or longword operations */
|
||||||
|
while((uintptr_t)d & 3) *d++ = *s++, n--;
|
||||||
|
|
||||||
|
/* Perform the big, efficient copy */
|
||||||
|
memcpy4((void *)d, s, (n >> 2));
|
||||||
|
|
||||||
|
size_t m = n & 3;
|
||||||
|
d += (n - m);
|
||||||
|
s += (n - m);
|
||||||
|
n = m;
|
||||||
|
|
||||||
|
/* Copy around the last bytes */
|
||||||
|
while(n--) *d++ = *s++;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *_memmove(void *dst, const void *src, size_t n)
|
||||||
|
{
|
||||||
|
// (same as memcpy, but heed for direction if areas overlap)
|
||||||
|
|
||||||
|
// more complicated
|
||||||
|
// allocate a buffer aligned with destination (source would be ok too)
|
||||||
|
// read unaligned from source to buffer
|
||||||
|
// copy aligned from buffer to destination
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _memcmp(const void *s1, const void *s2, size_t n)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *_memset(void *s, int byte, size_t n)
|
||||||
|
{
|
||||||
|
return s;
|
||||||
|
}
|
42
src/core/mmu.c
Normal file
42
src/core/mmu.c
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
//---
|
||||||
|
// gint:core:mmu - MMU-related definitions
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <core/mmu.h>
|
||||||
|
|
||||||
|
/* utlb_addr() - get the P4 address of a UTLB address entry */
|
||||||
|
INLINE 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)
|
||||||
|
{
|
||||||
|
uint32_t addr = 0xf7000000 | ((E & 0x3f) << 8);
|
||||||
|
return (void *)addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* utlb_mapped_memory() - count amount of mapped memory */
|
||||||
|
void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
|
||||||
|
{
|
||||||
|
uint32_t rom = 0, ram = 0;
|
||||||
|
|
||||||
|
for(int E = 0; E < 64; E++)
|
||||||
|
{
|
||||||
|
const utlb_addr_t *addr = utlb_addr(E);
|
||||||
|
const utlb_data_t *data = utlb_data(E);
|
||||||
|
if(!addr->V || !data->V) continue;
|
||||||
|
|
||||||
|
int sz = ((data->SZ1 << 1) | data->SZ2) << 3;
|
||||||
|
int size = 1 << ((0x14100c0a >> sz) & 0xff);
|
||||||
|
|
||||||
|
uint32_t src = addr->VPN << 10;
|
||||||
|
if(src >= 0x00300000 && src < 0x00380000) rom += size;
|
||||||
|
if(src >= 0x08100000 && src < 0x08180000) ram += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(p_rom) *p_rom = rom;
|
||||||
|
if(p_ram) *p_ram = ram;
|
||||||
|
}
|
60
src/core/mpu.c
Normal file
60
src/core/mpu.c
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
//---
|
||||||
|
// gint:core:mpu - Runtime MPU detection
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <core/mpu.h>
|
||||||
|
#include <defs/attributes.h>
|
||||||
|
#include <defs/types.h>
|
||||||
|
|
||||||
|
/* This file is only useful on fx9860g machines because all fxcg50 are SH4 */
|
||||||
|
#ifdef FX9860G
|
||||||
|
|
||||||
|
/* Holds the name of the current MPU; initialized at startup by mpu_init() */
|
||||||
|
GBSS const mpu_t mpu;
|
||||||
|
|
||||||
|
/* mpu_detect() - detect the underlying MPU
|
||||||
|
Many thanks to Simon Lothar for relevant documentation.
|
||||||
|
|
||||||
|
Processor Version Register (PVR) and Product Version Register (PRR) provide
|
||||||
|
info for SH-4-based MPUS; SH-3 based boards are detected and distinguished
|
||||||
|
by testing writable bits in the Port L Control Register (PLCR).
|
||||||
|
|
||||||
|
Returns the detected MPU type, falling back on mpu_unknown */
|
||||||
|
static mpu_t mpu_detect(void)
|
||||||
|
{
|
||||||
|
/* Processor Version Register */
|
||||||
|
volatile uint32_t *pvr = (void *)0xff000030;
|
||||||
|
/* Product Version Register */
|
||||||
|
volatile uint32_t *prr = (void *)0xff000044;
|
||||||
|
/* Port L Control Register */
|
||||||
|
volatile uint16_t *plcr = (void *)0xa4000114;
|
||||||
|
|
||||||
|
/* Detecting SH-3-based MPUs by testing writable bits in PLCR */
|
||||||
|
|
||||||
|
uint16_t old = *plcr;
|
||||||
|
*plcr = 0xffff;
|
||||||
|
uint16_t tested = *plcr;
|
||||||
|
*plcr = old;
|
||||||
|
|
||||||
|
if(tested == 0x00ff) return mpu_sh7337;
|
||||||
|
if(tested == 0x0fff) return mpu_sh7355;
|
||||||
|
|
||||||
|
/* Check that we're dealing with an SH-4-based MPU */
|
||||||
|
if((*pvr & 0xffffff00) != 0x10300b00) return mpu_unknown;
|
||||||
|
|
||||||
|
/* Tell SH-4 MPUs by testing the product version register */
|
||||||
|
|
||||||
|
uint32_t ver = *prr & 0xfffffff0;
|
||||||
|
if(ver == 0x00002c00) return mpu_sh7305;
|
||||||
|
if(ver == 0x00002200) return mpu_sh7724;
|
||||||
|
|
||||||
|
return mpu_unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mpu_init() - detect and save information about the underlying MPU */
|
||||||
|
void mpu_init(void)
|
||||||
|
{
|
||||||
|
const_cast(mpu, mpu_t) = mpu_detect();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
135
src/core/setup.c
Normal file
135
src/core/setup.c
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
//---
|
||||||
|
// gint:core:setup - Installing and unloading the library
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <gint/gint.h>
|
||||||
|
#include <core/setup.h>
|
||||||
|
#include <core/intc.h>
|
||||||
|
#include <core/mpu.h>
|
||||||
|
|
||||||
|
/* Interrupt controllers */
|
||||||
|
|
||||||
|
#ifdef FX9860G
|
||||||
|
GDATA sh7705_intc_t INTC3 = {
|
||||||
|
.IPRS = {
|
||||||
|
(void *)0xfffffee2, (void *)0xfffffee4,
|
||||||
|
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
|
||||||
|
(void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004,
|
||||||
|
},
|
||||||
|
.ICR1 = (void *)0xa4000010,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
GDATA sh7305_intc_t INTC4 = {
|
||||||
|
.IPRS = (void *)0xa4080000,
|
||||||
|
.MSK = (void *)0xa4080080,
|
||||||
|
.MSKCLR = (void *)0xa40800c0,
|
||||||
|
.USERIMASK = (void *)0xa4700000,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* VBR address, from the linker script */
|
||||||
|
extern char gint_vbr;
|
||||||
|
/* System's VBR address */
|
||||||
|
GBSS static uint32_t system_vbr;
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Context system for gint's core
|
||||||
|
//---
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint16_t iprs[12];
|
||||||
|
|
||||||
|
} PACKED(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())
|
||||||
|
{
|
||||||
|
#ifdef FX9860G
|
||||||
|
for(int i = 0; i < 8; i++) ctx->iprs[i] = *(INTC3.IPRS[i]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 12; i++) ctx->iprs[i] = INTC4.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())
|
||||||
|
{
|
||||||
|
#ifdef FX9860G
|
||||||
|
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = ctx->iprs[i];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = ctx->iprs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Initialization and unloading
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* System context */
|
||||||
|
GBSS static gint_core_ctx sys_ctx;
|
||||||
|
|
||||||
|
/* lock() - lock interrupts to avoid receiving unsupported signals */
|
||||||
|
static void lock(void)
|
||||||
|
{
|
||||||
|
/* Just disable everything, drivers will enable what they support */
|
||||||
|
if(isSH3())
|
||||||
|
{
|
||||||
|
#ifdef FX9860G
|
||||||
|
for(int i = 0; i < 8; i++) *(INTC3.IPRS[i]) = 0x0000;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gint_install() - install and start gint */
|
||||||
|
void gint_install(void)
|
||||||
|
{
|
||||||
|
/* VBR address, provided by the linker script */
|
||||||
|
uint32_t vbr = (uint32_t)&gint_vbr;
|
||||||
|
|
||||||
|
/* Event handler entry points */
|
||||||
|
extern void inth_entry_7705(void);
|
||||||
|
extern void inth_entry_7305(void);
|
||||||
|
|
||||||
|
/* First save the hardware configuration. This step is crucial because
|
||||||
|
we don't want the system to find out about us directly manipulating
|
||||||
|
the peripheral modules */
|
||||||
|
gint_ctx_save(&sys_ctx);
|
||||||
|
|
||||||
|
/* Load the event handler entry points into memory */
|
||||||
|
/* TODO: Load an exception handler and a TLB miss handler */
|
||||||
|
|
||||||
|
void *inth_entry = isSH3() ? inth_entry_7705 : inth_entry_7305;
|
||||||
|
memcpy((void *)(vbr + 0x600), inth_entry, 32);
|
||||||
|
|
||||||
|
/* Time to switch VBR and roll! */
|
||||||
|
system_vbr = gint_setvbr(vbr, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unlock() - unlock interrupts, restoring system settings */
|
||||||
|
static void unlock(void)
|
||||||
|
{
|
||||||
|
gint_ctx_restore(&sys_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* gint_unload() - unload gint and give back control to the system */
|
||||||
|
void gint_unload(void)
|
||||||
|
{
|
||||||
|
gint_setvbr(system_vbr, unlock);
|
||||||
|
}
|
211
src/core/start.c
Normal file
211
src/core/start.c
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
//---
|
||||||
|
// gint:core:start - Kernel initialization and C runtime
|
||||||
|
//--
|
||||||
|
|
||||||
|
#include <defs/attributes.h>
|
||||||
|
#include <defs/types.h>
|
||||||
|
#include <core/bootlog.h>
|
||||||
|
#include <core/mpu.h>
|
||||||
|
#include <core/mmu.h>
|
||||||
|
#include <gint/drivers.h>
|
||||||
|
#include <gint/gint.h>
|
||||||
|
|
||||||
|
/* Symbols provided by the linker script. For sections:
|
||||||
|
- l* represents the load address (source address in ROM)
|
||||||
|
- s* represents the size of the section
|
||||||
|
- r* represents the relocation address (destination address in RAM)
|
||||||
|
gint's BSS section is not mentioned here because it's never initialized */
|
||||||
|
extern uint32_t
|
||||||
|
brom, srom, /* Limits of ROM mappings */
|
||||||
|
lgdata, sgdata, rgdata, /* gint's data section */
|
||||||
|
ldata, sdata, rdata, /* User's data section */
|
||||||
|
sbss, rbss, /* User's BSS section */
|
||||||
|
btors, mtors, etors; /* Constructor/destructor arrays */
|
||||||
|
extern gint_driver_t
|
||||||
|
bdrv, edrv; /* Driver table */
|
||||||
|
|
||||||
|
/* User-provided main() function */
|
||||||
|
int main(int isappli, int optnum);
|
||||||
|
|
||||||
|
/* regcpy() - copy a memory region using symbol information
|
||||||
|
@l Source pointer (load address)
|
||||||
|
@s Size of area (should be a multiple of 16)
|
||||||
|
@r Destination pointer (relocation address) */
|
||||||
|
SECTION(".pretext")
|
||||||
|
static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
|
||||||
|
{
|
||||||
|
while(s > 0)
|
||||||
|
{
|
||||||
|
*r++ = *l++;
|
||||||
|
*r++ = *l++;
|
||||||
|
*r++ = *l++;
|
||||||
|
*r++ = *l++;
|
||||||
|
s -= 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define regcpy(l, s, r) regcpy(&l, (int32_t)&s, &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")
|
||||||
|
static void regclr(uint32_t *r, int32_t s)
|
||||||
|
{
|
||||||
|
while(s > 0)
|
||||||
|
{
|
||||||
|
*r++ = 0;
|
||||||
|
*r++ = 0;
|
||||||
|
*r++ = 0;
|
||||||
|
*r++ = 0;
|
||||||
|
s -= 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define regclr(r, s) regclr(&r, (int32_t)&s)
|
||||||
|
|
||||||
|
/* explore() - read data from a memory region to get it mapped by the system
|
||||||
|
This function accesses all the 1k-blocks between b and b + s. This
|
||||||
|
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")
|
||||||
|
static void explore(volatile void *b, int32_t s)
|
||||||
|
{
|
||||||
|
ATTR(unused) uint8_t x;
|
||||||
|
|
||||||
|
while(s > 0)
|
||||||
|
{
|
||||||
|
x = *(volatile uint8_t *)b;
|
||||||
|
b += 1024;
|
||||||
|
s -= 1024;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#define explore(b, s) explore(&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")
|
||||||
|
static void acall(void (**f)(void), void (**l)(void))
|
||||||
|
{
|
||||||
|
while(f < l) (*(*f++))();
|
||||||
|
}
|
||||||
|
#define acall(f, l) acall((void (**)(void))&f, (void (**)(void))&l)
|
||||||
|
|
||||||
|
|
||||||
|
/* start() - this is where it all starts
|
||||||
|
Returns a status code. Invoking main menu is better than returning! */
|
||||||
|
SECTION(".pretext.entry")
|
||||||
|
int start(int isappli, int optnum)
|
||||||
|
{
|
||||||
|
/* We are currently in a dynamic userspace mapping of an add-in run
|
||||||
|
from the storage memory. We are running in privileged mode with one
|
||||||
|
golden rule:
|
||||||
|
|
||||||
|
Do not disturb the operating system.
|
||||||
|
|
||||||
|
gint will silently run in an isolated part of the memory (fx9860g)
|
||||||
|
or at the start of the RAM (fxcg50). It acts as a kernel,
|
||||||
|
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
|
||||||
|
fully mapped, but we need to initialize it. We also need to do some
|
||||||
|
hardware detection because old fx9860g models have an older
|
||||||
|
processor with some incompatible features */
|
||||||
|
|
||||||
|
/* Detect architecture - this will tell SH3 from SH4 on fx9860g */
|
||||||
|
#ifdef FX9860G
|
||||||
|
mpu_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Load data sections and wipe the bss section. This has to be done
|
||||||
|
first for static and global variables to be initialized */
|
||||||
|
regcpy(lgdata, sgdata, rgdata);
|
||||||
|
regcpy(ldata, sdata, rdata);
|
||||||
|
regclr(rbss, sbss);
|
||||||
|
|
||||||
|
#ifdef GINT_BOOT_LOG
|
||||||
|
bootlog_loaded();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* TODO: Do the TLB investigation on SH3 (UTLB things are SH4 only) */
|
||||||
|
|
||||||
|
/* Traverse all ROM pages */
|
||||||
|
explore(brom, srom);
|
||||||
|
/* Count how much memory got mapped from this process */
|
||||||
|
uint32_t rom, ram;
|
||||||
|
utlb_mapped_memory(&rom, &ram);
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
#ifdef GINT_BOOT_LOG
|
||||||
|
isSH3() ? bootlog_mapped(-1, -1)
|
||||||
|
: bootlog_mapped(rom, ram);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Cancel add-in execution if not all pages are mapped
|
||||||
|
TODO: Resort to better graphical display */
|
||||||
|
if(rom < (uint32_t)&srom)
|
||||||
|
{
|
||||||
|
Bdisp_AllClr_VRAM();
|
||||||
|
print(1, 1, "Missing memory!");
|
||||||
|
print_hex(1, 2, rom, 8);
|
||||||
|
print(1, 3, "<");
|
||||||
|
print_hex(1, 4, (uint32_t)&srom, 8);
|
||||||
|
Bdisp_PutDisp_DD();
|
||||||
|
delay(20);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
|
gint_driver_t *drv;
|
||||||
|
|
||||||
|
/* Initialize all drivers by saving the system settings */
|
||||||
|
for(drv = &bdrv; drv < &edrv; drv++)
|
||||||
|
{
|
||||||
|
drv->ctx_save(drv->sys_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* With gint fully initialized, we are ready to start the hosted user
|
||||||
|
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
|
||||||
|
modules. The OS is bound to be confused (and crash) if we don't
|
||||||
|
resoture them. */
|
||||||
|
|
||||||
|
/* Restore all driver settings */
|
||||||
|
for(drv = &drv; drv < &edrv; drv++)
|
||||||
|
{
|
||||||
|
drv->ctx_restore(drv->sys_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finally, unload gint and give back control to the system */
|
||||||
|
gint_unload();
|
||||||
|
|
||||||
|
/* TODO: Invoke main menu instead of returning? */
|
||||||
|
return ret;
|
||||||
|
}
|
50
src/core/vbr.s
Normal file
50
src/core/vbr.s
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
** gint:core:vbr - Assembler-level VBR management
|
||||||
|
*/
|
||||||
|
|
||||||
|
.global _gint_setvbr
|
||||||
|
.text
|
||||||
|
.align 2
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
|
||||||
|
@arg vbr New VBR address (uint32_t)
|
||||||
|
@arg configure Configuration function (void -> void)
|
||||||
|
Returns the previous VBR address. */
|
||||||
|
_gint_setvbr:
|
||||||
|
mov.l r9, @-r15
|
||||||
|
mov.l r8, @-r15
|
||||||
|
sts.l pr, @-r15
|
||||||
|
|
||||||
|
mov #1, r8
|
||||||
|
mov #28, r0
|
||||||
|
shld r0, r8
|
||||||
|
|
||||||
|
/* Block all interrupts */
|
||||||
|
stc sr, r0
|
||||||
|
or r8, r0
|
||||||
|
ldc r0, sr
|
||||||
|
|
||||||
|
/* Set the new VBR address */
|
||||||
|
stc vbr, r9
|
||||||
|
ldc r4, vbr
|
||||||
|
|
||||||
|
/* Call the configuration function */
|
||||||
|
jsr @r5
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* Enable interrupts again */
|
||||||
|
stc sr, r0
|
||||||
|
not r8, r8
|
||||||
|
and r8, r0
|
||||||
|
ldc r0, sr
|
||||||
|
|
||||||
|
lds.l @r15+, pr
|
||||||
|
mov.l @r15+, r8
|
||||||
|
mov r9, r0
|
||||||
|
rts
|
||||||
|
mov.l @r15+, r9
|
341
src/r61524/r61524.c
Normal file
341
src/r61524/r61524.c
Normal file
|
@ -0,0 +1,341 @@
|
||||||
|
//---
|
||||||
|
// gint:r61524 - Renesas R61524 driver
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <gint/drivers.h>
|
||||||
|
#include <defs/types.h>
|
||||||
|
|
||||||
|
#ifdef FXCG50
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Device specification sheet
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* Registers and operations */
|
||||||
|
enum {
|
||||||
|
device_code_read = 0x000,
|
||||||
|
driver_output_control = 0x001,
|
||||||
|
entry_mode = 0x003,
|
||||||
|
display_control_2 = 0x008,
|
||||||
|
low_power_control = 0x00b,
|
||||||
|
|
||||||
|
ram_address_horizontal = 0x200,
|
||||||
|
ram_address_vertical = 0x201,
|
||||||
|
write_data = 0x202,
|
||||||
|
|
||||||
|
horizontal_ram_start = 0x210,
|
||||||
|
horizontal_ram_end = 0x211,
|
||||||
|
vertical_ram_start = 0x212,
|
||||||
|
vertical_ram_end = 0x213,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef word_union(entry_mode_t,
|
||||||
|
uint TRI :1;
|
||||||
|
uint DFM :1;
|
||||||
|
uint :1;
|
||||||
|
uint BGR :1;
|
||||||
|
uint :2;
|
||||||
|
uint HWM :1;
|
||||||
|
uint :1;
|
||||||
|
uint ORG :1;
|
||||||
|
uint :1;
|
||||||
|
uint ID :2;
|
||||||
|
uint AM :1;
|
||||||
|
uint :1;
|
||||||
|
uint EPF :2;
|
||||||
|
);
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Device communication primitives
|
||||||
|
//---
|
||||||
|
|
||||||
|
#define synco() __asm__ volatile ("synco":::"memory")
|
||||||
|
|
||||||
|
/* Interface with the controller */
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
/* Clear RS and write the register number */
|
||||||
|
*PRDR &= ~0x10;
|
||||||
|
synco();
|
||||||
|
*intf = reg;
|
||||||
|
synco();
|
||||||
|
|
||||||
|
/* Set RS back. We don't do this in read()/write() because the display
|
||||||
|
driver is optimized for consecutive GRAM access. LCD-transfers will
|
||||||
|
be faster when executing select() followed by many write().
|
||||||
|
(Most applications should use DMA though.) */
|
||||||
|
*PRDR |= 0x10;
|
||||||
|
synco();
|
||||||
|
}
|
||||||
|
|
||||||
|
INLINE static uint16_t read(void)
|
||||||
|
{
|
||||||
|
return *intf;
|
||||||
|
}
|
||||||
|
|
||||||
|
INLINE static void write(uint16_t data)
|
||||||
|
{
|
||||||
|
*intf = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Driver functions
|
||||||
|
//---
|
||||||
|
|
||||||
|
void pixel(int ha, int va, int color)
|
||||||
|
{
|
||||||
|
select(ram_address_horizontal);
|
||||||
|
write(ha);
|
||||||
|
|
||||||
|
select(ram_address_vertical);
|
||||||
|
write(va);
|
||||||
|
|
||||||
|
select(write_data);
|
||||||
|
write(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void r61524_test(void)
|
||||||
|
{
|
||||||
|
uint16_t device_name;
|
||||||
|
uint16_t doc;
|
||||||
|
int SM, SS;
|
||||||
|
entry_mode_t em;
|
||||||
|
uint16_t dc2;
|
||||||
|
int FP, BP;
|
||||||
|
uint16_t lpc;
|
||||||
|
int VEM, COL;
|
||||||
|
|
||||||
|
uint32_t AD;
|
||||||
|
uint16_t HSA, HEA;
|
||||||
|
uint16_t VSA, VEA;
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
select(device_code_read);
|
||||||
|
device_name = read();
|
||||||
|
|
||||||
|
Bdisp_AllClr_VRAM();
|
||||||
|
print(1, 1, "Name=????");
|
||||||
|
print_hex(6, 1, device_name, 4);
|
||||||
|
|
||||||
|
if(device_name != 0x1524)
|
||||||
|
{
|
||||||
|
print(1, 2, "Aborting.");
|
||||||
|
Bdisp_PutDisp_DD();
|
||||||
|
delay(20);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
select(driver_output_control);
|
||||||
|
doc = read();
|
||||||
|
SM = (doc >> 10) & 1;
|
||||||
|
SS = (doc >> 8) & 1;
|
||||||
|
|
||||||
|
select(entry_mode);
|
||||||
|
em.word = read();
|
||||||
|
|
||||||
|
select(display_control_2);
|
||||||
|
dc2 = read();
|
||||||
|
FP = (dc2 >> 8) & 0xf;
|
||||||
|
BP = dc2 & 0xf;
|
||||||
|
|
||||||
|
select(low_power_control);
|
||||||
|
lpc = read();
|
||||||
|
VEM = (lpc >> 4) & 1;
|
||||||
|
COL = lpc & 1;
|
||||||
|
|
||||||
|
/* Shift by 9, not 8, because of horizontal/vertical range inversion */
|
||||||
|
select(ram_address_horizontal);
|
||||||
|
AD = read();
|
||||||
|
select(ram_address_vertical);
|
||||||
|
AD |= read() << 9;
|
||||||
|
|
||||||
|
select(horizontal_ram_start);
|
||||||
|
HSA = read();
|
||||||
|
select(horizontal_ram_end);
|
||||||
|
HEA = read();
|
||||||
|
|
||||||
|
select(vertical_ram_start);
|
||||||
|
VSA = read();
|
||||||
|
select(vertical_ram_end);
|
||||||
|
VEA = read();
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
print(15, 4, " SM=?");
|
||||||
|
print_hex(19, 4, SM, 1);
|
||||||
|
print(15, 5, " SS=?");
|
||||||
|
print_hex(19, 5, SS, 1);
|
||||||
|
|
||||||
|
print(1, 2, "TRI=? DFM=? BGR=?");
|
||||||
|
print_hex(5, 2, em.TRI, 1);
|
||||||
|
print_hex(12, 2, em.DFM, 1);
|
||||||
|
print_hex(19, 2, em.BGR, 1);
|
||||||
|
print(1, 3, "HWM=? ORG=? ID=?");
|
||||||
|
print_hex(5, 3, em.HWM, 1);
|
||||||
|
print_hex(12, 3, em.DFM, 1);
|
||||||
|
print_hex(19, 3, em.ID, 1);
|
||||||
|
print(1, 4, " AM=? EPF=?");
|
||||||
|
print_hex(5, 4, em.AM, 1);
|
||||||
|
print_hex(12, 4, em.EPF, 1);
|
||||||
|
|
||||||
|
print(1, 5, " FP=? BP=?");
|
||||||
|
print_hex(5, 5, FP, 1);
|
||||||
|
print_hex(12, 5, BP, 1);
|
||||||
|
|
||||||
|
print(1, 6, "VEM=? COL=?");
|
||||||
|
print_hex(5, 6, VEM, 1);
|
||||||
|
print_hex(12, 6, COL, 1);
|
||||||
|
|
||||||
|
Bdisp_PutDisp_DD();
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
Bdisp_AllClr_VRAM();
|
||||||
|
|
||||||
|
print(1, 1, "Address=?????");
|
||||||
|
print_hex(9, 1, AD, 5);
|
||||||
|
|
||||||
|
print(1, 2, "HSA=??? HEA=???");
|
||||||
|
print_hex(5, 2, HSA, 3);
|
||||||
|
print_hex(14, 2, HEA, 3);
|
||||||
|
print(1, 3, "VSA=??? VEA=???");
|
||||||
|
print_hex(5, 3, VSA, 3);
|
||||||
|
print_hex(14, 3, VEA, 3);
|
||||||
|
|
||||||
|
Bdisp_PutDisp_DD();
|
||||||
|
delay(20);
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
select(horizontal_ram_start);
|
||||||
|
write(0);
|
||||||
|
select(horizontal_ram_end);
|
||||||
|
write(395);
|
||||||
|
|
||||||
|
select(vertical_ram_start);
|
||||||
|
write(0);
|
||||||
|
select(vertical_ram_end);
|
||||||
|
write(223);
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
// Bdisp_AllClr_VRAM();
|
||||||
|
// Bdisp_PutDisp_DD();
|
||||||
|
|
||||||
|
select(ram_address_horizontal);
|
||||||
|
write(HEA);
|
||||||
|
select(ram_address_vertical);
|
||||||
|
write(VSA);
|
||||||
|
select(write_data);
|
||||||
|
|
||||||
|
uint16_t color = 0xf3e4;
|
||||||
|
for(int v = 0; v < 396; v++)
|
||||||
|
for(int h = 0; h < 224; h++)
|
||||||
|
{
|
||||||
|
color = (v << 7) ^ h ^ 0x3754;
|
||||||
|
write(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(60);
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* Shift by 9, not 8, because of horizontal/vertical range inversion */
|
||||||
|
select(ram_address_horizontal);
|
||||||
|
AD = read();
|
||||||
|
select(ram_address_vertical);
|
||||||
|
AD |= read() << 9;
|
||||||
|
|
||||||
|
select(horizontal_ram_start);
|
||||||
|
HSA = read();
|
||||||
|
select(horizontal_ram_end);
|
||||||
|
HEA = read();
|
||||||
|
|
||||||
|
select(vertical_ram_start);
|
||||||
|
VSA = read();
|
||||||
|
select(vertical_ram_end);
|
||||||
|
VEA = read();
|
||||||
|
|
||||||
|
Bdisp_AllClr_VRAM();
|
||||||
|
|
||||||
|
print(1, 1, "Address=?????");
|
||||||
|
print_hex(9, 1, AD, 5);
|
||||||
|
|
||||||
|
print(1, 2, "HSA=??? HEA=???");
|
||||||
|
print_hex(5, 2, HSA, 3);
|
||||||
|
print_hex(14, 2, HEA, 3);
|
||||||
|
print(1, 3, "VSA=??? VEA=???");
|
||||||
|
print_hex(5, 3, VSA, 3);
|
||||||
|
print_hex(14, 3, VEA, 3);
|
||||||
|
|
||||||
|
Bdisp_PutDisp_DD();
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Context system for this driver
|
||||||
|
//---
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* Graphics RAM range */
|
||||||
|
uint16_t HSA, HEA, VSA, VEA;
|
||||||
|
|
||||||
|
} PACKED(2) ctx_t;
|
||||||
|
|
||||||
|
/* Allocate one buffer in gint's storage section */
|
||||||
|
GBSS static ctx_t sys_ctx;
|
||||||
|
|
||||||
|
static void ctx_save(void *buf)
|
||||||
|
{
|
||||||
|
ctx_t *ctx = buf;
|
||||||
|
|
||||||
|
select(horizontal_ram_start);
|
||||||
|
ctx->HSA = read();
|
||||||
|
select(horizontal_ram_end);
|
||||||
|
ctx->HEA = read();
|
||||||
|
|
||||||
|
select(vertical_ram_start);
|
||||||
|
ctx->VSA = read();
|
||||||
|
select(vertical_ram_end);
|
||||||
|
ctx->VEA = read();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ctx_restore(void *buf)
|
||||||
|
{
|
||||||
|
ctx_t *ctx = buf;
|
||||||
|
|
||||||
|
select(horizontal_ram_start);
|
||||||
|
write(ctx->HSA);
|
||||||
|
select(horizontal_ram_end);
|
||||||
|
write(ctx->HEA);
|
||||||
|
|
||||||
|
select(vertical_ram_start);
|
||||||
|
write(ctx->VSA);
|
||||||
|
select(vertical_ram_end);
|
||||||
|
write(ctx->VEA);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Driver structure definition
|
||||||
|
//---
|
||||||
|
|
||||||
|
gint_driver_t drv_r61524 = {
|
||||||
|
.name = "Renesas R61524",
|
||||||
|
.ctx_size = sizeof(ctx_t),
|
||||||
|
.sys_ctx = &sys_ctx,
|
||||||
|
.ctx_save = ctx_save,
|
||||||
|
.ctx_restore = ctx_restore,
|
||||||
|
};
|
||||||
|
|
||||||
|
GINT_DECLARE_DRIVER(drv_r61524);
|
||||||
|
|
||||||
|
#endif /* FXCG50 */
|
209
src/t6k11/t6k11.c
Normal file
209
src/t6k11/t6k11.c
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
//---
|
||||||
|
// gint:t6k11 - Toshiba T6K11 driver
|
||||||
|
//---
|
||||||
|
|
||||||
|
#include <gint/drivers.h>
|
||||||
|
#include <drivers/t6k11.h>
|
||||||
|
|
||||||
|
#include <defs/attributes.h>
|
||||||
|
#include <defs/types.h>
|
||||||
|
#include <core/mpu.h>
|
||||||
|
|
||||||
|
#ifdef FX9860G
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Device specification sheet
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* Screen registers. Registers 8..11 and 13..15 must not be used! */
|
||||||
|
enum {
|
||||||
|
reg_display = 0,
|
||||||
|
reg_counter = 1,
|
||||||
|
reg_analog = 2,
|
||||||
|
reg_alternate = 3,
|
||||||
|
|
||||||
|
reg_yaddr = 4, /* These two use the same register */
|
||||||
|
reg_xaddr = 4, /* (interpretation depends on count mode) */
|
||||||
|
|
||||||
|
reg_zaddr = 5,
|
||||||
|
reg_contrast = 6,
|
||||||
|
reg_data = 7,
|
||||||
|
reg_daconv = 12,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Automatic counter increment during read/write */
|
||||||
|
enum {
|
||||||
|
cnt_ydown = 0,
|
||||||
|
cnt_yup = 1,
|
||||||
|
cnt_xdown = 2,
|
||||||
|
cnt_xup = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Device communication primitives
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* I/O may be performed either with RS = 0 or RS = 1. The memory-mapping of the
|
||||||
|
device I/O maps bit 16 of the address to pin RS. There may be other mapped
|
||||||
|
pins in the address. (?) */
|
||||||
|
|
||||||
|
/* RS = 0: Register selection */
|
||||||
|
GDATA static volatile uint8_t *sel = (void *)0xb4000000;
|
||||||
|
/* RS = 1: Command data or vram data */
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
*sel = reg;
|
||||||
|
*cmd = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* status() - read the status byte from the display driver */
|
||||||
|
INLINE 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)
|
||||||
|
{
|
||||||
|
*sel = reg_data;
|
||||||
|
|
||||||
|
/* Unroll the loop for more speed */
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
*cmd = *buf++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Driver functions
|
||||||
|
//---
|
||||||
|
|
||||||
|
/* t6k11_display() - send vram data to the LCD device */
|
||||||
|
void t6k11_display(const void *vram, int y1, int y2, size_t stride)
|
||||||
|
{
|
||||||
|
for(int y = y1; y < y2; y++)
|
||||||
|
{
|
||||||
|
/* Set the X-address register for this row */
|
||||||
|
command(reg_xaddr, y | 0xc0);
|
||||||
|
|
||||||
|
/* Use Y-Up mode */
|
||||||
|
command(reg_counter, cnt_yup);
|
||||||
|
|
||||||
|
/* Start counting Y from 0 */
|
||||||
|
command(reg_yaddr, 0);
|
||||||
|
|
||||||
|
/* Send the row's data to the device */
|
||||||
|
write_row(vram);
|
||||||
|
vram += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* t6k11_contrast() - change the contrast setting */
|
||||||
|
void t6k11_contrast(int contrast)
|
||||||
|
{
|
||||||
|
if(contrast < 0) contrast = 0;
|
||||||
|
if(contrast > 32) contrast = 32;
|
||||||
|
|
||||||
|
/* Reasonable values for contrast, taken from the diagnostic mode of an
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* t6k11_backlight() - manage the screen backlight */
|
||||||
|
void t6k11_backlight(int setting)
|
||||||
|
{
|
||||||
|
volatile uint8_t *port;
|
||||||
|
uint8_t mask;
|
||||||
|
|
||||||
|
/* This setting is mapped to an I/O port:
|
||||||
|
- On SH3, bit 7 of port G
|
||||||
|
- On SH4, bit 4 of port N */
|
||||||
|
if(isSH3())
|
||||||
|
{
|
||||||
|
port = (void *)0xa400012c;
|
||||||
|
mask = 0x80;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
port = (void *)0xa4050138;
|
||||||
|
mask = 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!setting) *port &= ~mask;
|
||||||
|
if(setting > 0) *port |= mask;
|
||||||
|
if(setting < 0) *port ^= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Context system for this driver
|
||||||
|
//---
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* Some status bits, obtained by using the STRD command */
|
||||||
|
uint8_t strd;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
/* Pre-allocate a context in gint's uninitialized section */
|
||||||
|
GBSS static ctx_t sys_ctx;
|
||||||
|
|
||||||
|
static void ctx_save(void *buf)
|
||||||
|
{
|
||||||
|
ctx_t *ctx = buf;
|
||||||
|
ctx->strd = status();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ctx_restore(void *buf)
|
||||||
|
{
|
||||||
|
ctx_t *ctx = buf;
|
||||||
|
|
||||||
|
/* Set an X-address of 0 with the original display mode */
|
||||||
|
uint8_t nf = (ctx->strd & 0x04) >> 2;
|
||||||
|
command(reg_xaddr, 0x80 | (nf << 6));
|
||||||
|
|
||||||
|
/* Restore the counter mode */
|
||||||
|
uint8_t cnt = (ctx->strd & 0x03);
|
||||||
|
command(reg_counter, cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
// Driver structure definition
|
||||||
|
//---
|
||||||
|
|
||||||
|
gint_driver_t drv_t6k11 = {
|
||||||
|
.name = "Toshiba T6K11",
|
||||||
|
.ctx_size = sizeof(ctx_t),
|
||||||
|
.sys_ctx = &sys_ctx,
|
||||||
|
.ctx_save = ctx_save,
|
||||||
|
.ctx_restore = ctx_restore,
|
||||||
|
};
|
||||||
|
|
||||||
|
GINT_DECLARE_DRIVER(drv_t6k11);
|
||||||
|
|
||||||
|
#endif /* FX9860G */
|
Loading…
Reference in a new issue