Hybrid build system and runtime (no interrupts). t6k11 driver. Basic r61524 driver.

This commit is contained in:
lephe 2018-04-19 13:24:26 +02:00
parent 1d107a1d64
commit 5c20997c9a
26 changed files with 2697 additions and 48 deletions

View file

@ -15,9 +15,17 @@ cfg := config/Makefile.cfg
# Those that do may use this special dependency
cfgdep := $(if $(shell [ -f $(cfg) ] || echo n),CFGDEP,$(cfg))
cf-fx := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -Os \
-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
cflags := -m3 -mb -ffreestanding -nostdlib -Wall -Wextra -std=c11 -O2 \
-I include $(cfg_defs)
cflags := $(cf-$(cfg_target))
sflags := $(cfg_macros)
dflags = -MMD -MT $@ -MF $(@:.o=.d) -MP
arflags :=
@ -25,47 +33,73 @@ arflags :=
# File listings
#
# Target file
target := bin/libgint.a
# Target file (cfg_target is either "fx" or "cg")
target := bin/libgint-$(cfg_target).a
# Automatic names for object and dependency files
src2obj = build/$(1:src/%=%).o
src2dep = build/$(1:src/%=%).d
# Source files
src := $(shell find src -name '*.[cs]')
src := $(shell find src -name '*.[csS]')
src_obj := $(foreach s,$(src),$(call src2obj,$s))
# Files with special handling
spe := src/display/font.bmp
spe_obj := $(foreach s,$(spe),$(call src2obj,$s))
# spe := build/version.o src/display/font.bmp
spe :=
spe_obj := build/version.o $(foreach s,$(spe),$(call src2obj,$s))
# All object files
obj := $(src_obj) $(spe_obj)
# Version file
version := config/version
#
# Toolchain
#
gcc = sh3eb-elf-gcc
as = sh3eb-elf-as
ld = sh3eb-elf-ld
ar = sh3eb-elf-ar
gcc = $(toolchain)-gcc
as = $(toolchain)-as
ld = $(toolchain)-ld
ar = $(toolchain)-ar
objcopy = $(toolchain)-objcopy
conv = fxconv
#
# Version management
#
# Retrieve version information
v_file = $(shell cat $(version) | sed 's/[-.]/ /g')
v_type = $(word 1,$(v_file))
v_major = $(word 2,$(v_file))
v_minor = $(word 3,$(v_file))
v_build = $(word 4,$(v_file))
# Create the current version symbol and the new version integer
v_newbuild = $(shell echo $$(($(v_build) + 1)))
v_letter = $(shell echo -n $(v_type) | sed -r 's/^(.).*/\1/')
v_symbol = $(shell printf '0x%02x%01x%01x%04x' "'$(v_letter)'" \
$(v_major) $(v_minor) $(v_build))
#
# Build rules
#
all: $(target)
$(target): $(obj) | $(dir $(target))
$(target): $(obj) $(version) | $(dir $(target))
$(call cmd_l,ar,$@) $(ar) -crs $(arflags) $@ $(obj)
# Assembler sources
build/%.s.o: src/%.s build/%.s.d
@ mkdir -p $(dir $@)
$(call cmd_b,as,$<) $(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
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 $@)
$(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
#
@ -85,12 +126,15 @@ clean:
@ rm -rf build/
distclean: clean
@ rm -rf bin/
@ rm -f $(cfg)
@ rm -f $(config)
#
# Utilities
#
$(version): $(src) $(wildcard include/**/*)
@ echo '$(v_type)-$(v_major).$(v_minor)-$(v_newbuild)' > $@
# Evaluated when a rule requires the configuration file but it doesn't exist
CFGDEP:
@ echo "Configuration file $(cfg) is missing. Have you configured?"

68
configure vendored
View file

@ -40,31 +40,33 @@ help()
Configuration script for the gint library.
Usage: $0 [options...]
Required settings:
$Cr--target$C0=${Cg}fx9860g$C0,${Cg}fxcg50$C0
Select the target platform, either ${Cg}fx9860g$C0 for monochrome
calculators, or ${Cg}fxcg50$C0 for Prizm calculators.
Platform settings (specify exactly one):
${Cg}-fx9860g$C0
Target platform is fx-9860G II: all monochrome models that support add-ins
or can be flashed to support them.
${Cg}-fxcg50$C0
Target platform is fx-CG50.
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
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
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
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
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:
$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().
$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.
$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.
EOF
@ -79,43 +81,37 @@ fail=false
for arg; do case "$arg" in
-h | -? | --help) help;;
--target=*)
conf_target=${arg#*=}
if [[ $conf_target = "fx9860g" ]]; then
conf_target="GINT_FX9860G"
else if [[ $conf_target = "fxcg50" ]]; then
conf_target="GINT_FXCG50"
else
echo -e "$error Invalid target. See $0 --help."
fail=true;
fi; fi;;
-fx9860g)
conf_target="FX9860G";;
-fxcg50)
conf_target="FXCG50";;
--boot-log) conf[GINT_BOOT_LOG]=true;;
--no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
--extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
--static-gray-engine) conf[GINT_STATIC_GRAY]=true;;
-boot-log) conf[GINT_BOOT_LOG]=true;;
-no-syscalls) conf[GINT_NO_SYSCALLS]=true;;
-extended-libc) conf[GINT_EXTENDED_LIBC]=true;;
-static-gray-engine) conf[GINT_STATIC_GRAY]=true;;
--atexit-max=*)
-atexit-max=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
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;;
--timer-slots=*)
-timer-slots=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
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;;
--events-queue-size=*)
-events-queue-size=*)
size=${arg#*=}
if [[ $size == +([0-9]) ]]; then
conf[EVENTS_QUEUE_SIZE]=$size
else echo -e "$error --events-queue-size expects an integer"\
else echo -e "$error -events-queue-size expects an integer"\
"value"
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>";;
*)
@ -137,7 +133,11 @@ fi
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"
[ "${conf[GINT_BOOT_LOG]}" ] && echo -n " -DGINT_BOOT_LOG"

186
fx9860g.ld Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 */