From 3b90b40dd77d06e213bcb9b73ae4cd4d1dfd3174 Mon Sep 17 00:00:00 2001 From: lephe Date: Thu, 19 Apr 2018 13:24:26 +0200 Subject: [PATCH] Hybrid build system and runtime (no interrupts). t6k11 driver. Basic r61524 driver. --- Makefile | 72 +++++-- configure | 68 +++---- fx9860g.ld | 186 ++++++++++++++++++ fxcg50.ld | 173 +++++++++++++++++ include/core/bootlog.h | 24 +++ include/core/intc.h | 390 ++++++++++++++++++++++++++++++++++++++ include/core/mmu.h | 63 ++++++ include/core/mpu.h | 52 +++++ include/core/setup.h | 23 +++ include/defs/attributes.h | 35 ++++ include/defs/types.h | 78 ++++++++ include/drivers/t6k11.h | 53 ++++++ include/gint/display-fx.h | 8 + include/gint/display.h | 32 ++++ include/gint/drivers.h | 47 +++++ include/gint/gint.h | 59 ++++++ src/core/bootlog.c | 117 ++++++++++++ src/core/inth.S | 110 +++++++++++ src/core/memory.c | 107 +++++++++++ src/core/mmu.c | 42 ++++ src/core/mpu.c | 60 ++++++ src/core/setup.c | 135 +++++++++++++ src/core/start.c | 211 +++++++++++++++++++++ src/core/vbr.s | 50 +++++ src/r61524/r61524.c | 341 +++++++++++++++++++++++++++++++++ src/t6k11/t6k11.c | 209 ++++++++++++++++++++ 26 files changed, 2697 insertions(+), 48 deletions(-) create mode 100644 fx9860g.ld create mode 100644 fxcg50.ld create mode 100644 include/core/bootlog.h create mode 100644 include/core/intc.h create mode 100644 include/core/mmu.h create mode 100644 include/core/mpu.h create mode 100644 include/core/setup.h create mode 100644 include/defs/attributes.h create mode 100644 include/defs/types.h create mode 100644 include/drivers/t6k11.h create mode 100644 include/gint/display-fx.h create mode 100644 include/gint/display.h create mode 100644 include/gint/drivers.h create mode 100644 include/gint/gint.h create mode 100644 src/core/bootlog.c create mode 100644 src/core/inth.S create mode 100644 src/core/memory.c create mode 100644 src/core/mmu.c create mode 100644 src/core/mpu.c create mode 100644 src/core/setup.c create mode 100644 src/core/start.c create mode 100644 src/core/vbr.s create mode 100644 src/r61524/r61524.c create mode 100644 src/t6k11/t6k11.c diff --git a/Makefile b/Makefile index f577bc5..ff5264b 100755 --- a/Makefile +++ b/Makefile @@ -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?" diff --git a/configure b/configure index e99fe35..b1be97f 100755 --- a/configure +++ b/configure @@ -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=";; *) @@ -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" diff --git a/fx9860g.ld b/fx9860g.ld new file mode 100644 index 0000000..dce9c53 --- /dev/null +++ b/fx9860g.ld @@ -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) + } +} diff --git a/fxcg50.ld b/fxcg50.ld new file mode 100644 index 0000000..9b13ad8 --- /dev/null +++ b/fxcg50.ld @@ -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) + } +} diff --git a/include/core/bootlog.h b/include/core/bootlog.h new file mode 100644 index 0000000..152bdea --- /dev/null +++ b/include/core/bootlog.h @@ -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 */ diff --git a/include/core/intc.h b/include/core/intc.h new file mode 100644 index 0000000..f660619 --- /dev/null +++ b/include/core/intc.h @@ -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 + +//--- +// 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 */ diff --git a/include/core/mmu.h b/include/core/mmu.h new file mode 100644 index 0000000..140ebfa --- /dev/null +++ b/include/core/mmu.h @@ -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 +#include + +/* 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 */ diff --git a/include/core/mpu.h b/include/core/mpu.h new file mode 100644 index 0000000..91164d9 --- /dev/null +++ b/include/core/mpu.h @@ -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 + +/* 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 */ diff --git a/include/core/setup.h b/include/core/setup.h new file mode 100644 index 0000000..72c46eb --- /dev/null +++ b/include/core/setup.h @@ -0,0 +1,23 @@ +//--- +// gint:core:setup - Installing and unloading the library +//--- + +#ifndef GINT_CORE_SETUP +#define GINT_CORE_SETUP + +#include + +/* Prototypes for the library management functions are in */ + +/* gint_setvbr() + Changes the VBR address and the calls the configuration function while + interrupts are disabled. The configuration function must change either the + priority registers or the interrupt masks, and make sure that all the + interrupts that it leaves enabled are handled by the new VBR handlers. + + @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 */ diff --git a/include/defs/attributes.h b/include/defs/attributes.h new file mode 100644 index 0000000..64a38a7 --- /dev/null +++ b/include/defs/attributes.h @@ -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 */ diff --git a/include/defs/types.h b/include/defs/types.h new file mode 100644 index 0000000..e7d487f --- /dev/null +++ b/include/defs/types.h @@ -0,0 +1,78 @@ +//--- +// gint:defs:types - Type-related macros +//--- + +#ifndef GINT_DEFS_TYPES +#define GINT_DEFS_TYPES + +#include +#include +#include + +//--- +// Const casts +//--- + +/* const_cast() - perform const casts + This is intended for initialization purposes only, like "final" in Java. + This macro is the most generic form without any type inference; using + const_cint() or const_cptr() may be more convenient for simple types */ +#define const_cast(x, T) (*((T *)(&(x)))) + +/* const_cptr() - perform const casts on pointers */ +#define const_cptr(x) (*((void **)(&(x)))) + +/* const_cint() - perform const casts on integers + This macro saves you from specifying the integral type that you're + manipulating, which may avoid errors if the type changes. It will only work + with the primitive types that are either mentioned in the following list or + aliased as one of the listed types. + This is to prevent unforeseen effects of tricks such as typeof(x + 0) that + promotes various small integers to ints */ +#define const_cint(x) (*_Generic((x), \ + char: (char *) (&(x)), \ + unsigned char: (unsigned char *) (&(x)), \ + short: (short *) (&(x)), \ + unsigned short: (unsigned short *) (&(x)), \ + int: (int *) (&(x)), \ + unsigned int: (unsigned int *) (&(x)), \ + long: (long *) (&(x)), \ + unsigned long: (unsigned long *) (&(x)), \ + long long: (long long *) (&(x)), \ + unsigned long long: (unsigned long long *) (&(x)) \ +)) + +//--- +// Structure elements +//---- + +/* Fixed-width types for bit fields are entirely meaningless */ +typedef unsigned int uint; + +/* Giving a type to padding bytes is misguiding */ +#define pad_nam2(c) _ ## c +#define pad_name(c) pad_nam2(c) +#define pad(bytes) uint8_t pad_name(__COUNTER__)[bytes] + +/* byte_union() - union between an uint8_t 'byte' element and a bit field */ +#define byte_union(name, fields) \ + union { \ + uint8_t byte; \ + struct { fields } PACKED(1); \ + } PACKED(1) name + +/* word_union() - union between an uint16_t 'word' element and a bit field */ +#define word_union(name, fields) \ + union { \ + uint16_t word; \ + struct { fields } PACKED(2); \ + } PACKED(2) name + +/* lword_union() - union between an uint32_t 'lword' element and a bit field */ +#define lword_union(name, fields) \ + union { \ + uint32_t lword; \ + struct { fields } PACKED(4); \ + } PACKED(4) name + +#endif /* GINT_DEFS_TYPES */ diff --git a/include/drivers/t6k11.h b/include/drivers/t6k11.h new file mode 100644 index 0000000..5b89f40 --- /dev/null +++ b/include/drivers/t6k11.h @@ -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 + +/* 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 */ diff --git a/include/gint/display-fx.h b/include/gint/display-fx.h new file mode 100644 index 0000000..fb54785 --- /dev/null +++ b/include/gint/display-fx.h @@ -0,0 +1,8 @@ +//--- +// gint:display-fx - fx9860g drawing functions +//--- + +#ifndef GINT_DISPLAY_FX +#define GINT_DISPLAY_FX + +#endif /* GINT_DISPLAY_FX */ diff --git a/include/gint/display.h b/include/gint/display.h new file mode 100644 index 0000000..f3f0465 --- /dev/null +++ b/include/gint/display.h @@ -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 +#else +#include +#endif + +#endif /* GINT_DISPLAY */ diff --git a/include/gint/drivers.h b/include/gint/drivers.h new file mode 100644 index 0000000..b1ebf95 --- /dev/null +++ b/include/gint/drivers.h @@ -0,0 +1,47 @@ +//--- +// gint:drivers - General tools for drivers +//--- + +#ifndef GINT_DRIVERS +#define GINT_DRIVERS + +#include +#include + +/* 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 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 */ diff --git a/include/gint/gint.h b/include/gint/gint.h new file mode 100644 index 0000000..f081a5d --- /dev/null +++ b/include/gint/gint.h @@ -0,0 +1,59 @@ +//--- +// gint - An alternative runtime environment for fx9860g and fxcg50 +//--- + +#ifndef GINT_GINT +#define GINT_GINT + +#include +#include + +/* 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 */ diff --git a/src/core/bootlog.c b/src/core/bootlog.c new file mode 100644 index 0000000..ed73906 --- /dev/null +++ b/src/core/bootlog.c @@ -0,0 +1,117 @@ +//--- +// gint:core:bootlog - Boot-time on-screen log for extreme debugging +//--- + +#include +#include +#include +#include +#include + +/* 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(); +} diff --git a/src/core/inth.S b/src/core/inth.S new file mode 100644 index 0000000..ef79caf --- /dev/null +++ b/src/core/inth.S @@ -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 diff --git a/src/core/memory.c b/src/core/memory.c new file mode 100644 index 0000000..029dd68 --- /dev/null +++ b/src/core/memory.c @@ -0,0 +1,107 @@ +#include +#include +#include + +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; +} diff --git a/src/core/mmu.c b/src/core/mmu.c new file mode 100644 index 0000000..291b831 --- /dev/null +++ b/src/core/mmu.c @@ -0,0 +1,42 @@ +//--- +// gint:core:mmu - MMU-related definitions +//--- + +#include + +/* 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; +} diff --git a/src/core/mpu.c b/src/core/mpu.c new file mode 100644 index 0000000..f571818 --- /dev/null +++ b/src/core/mpu.c @@ -0,0 +1,60 @@ +//--- +// gint:core:mpu - Runtime MPU detection +//--- + +#include +#include +#include + +/* This file is only useful on fx9860g machines because all fxcg50 are SH4 */ +#ifdef FX9860G + +/* Holds the name of the current MPU; initialized at startup by mpu_init() */ +GBSS const mpu_t mpu; + +/* 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 diff --git a/src/core/setup.c b/src/core/setup.c new file mode 100644 index 0000000..1335a63 --- /dev/null +++ b/src/core/setup.c @@ -0,0 +1,135 @@ +//--- +// gint:core:setup - Installing and unloading the library +//--- + +#include +#include +#include +#include + +/* 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); +} diff --git a/src/core/start.c b/src/core/start.c new file mode 100644 index 0000000..48c0445 --- /dev/null +++ b/src/core/start.c @@ -0,0 +1,211 @@ +//--- +// gint:core:start - Kernel initialization and C runtime +//-- + +#include +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/src/core/vbr.s b/src/core/vbr.s new file mode 100644 index 0000000..463db39 --- /dev/null +++ b/src/core/vbr.s @@ -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 diff --git a/src/r61524/r61524.c b/src/r61524/r61524.c new file mode 100644 index 0000000..461e66e --- /dev/null +++ b/src/r61524/r61524.c @@ -0,0 +1,341 @@ +//--- +// gint:r61524 - Renesas R61524 driver +//--- + +#include +#include + +#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 */ diff --git a/src/t6k11/t6k11.c b/src/t6k11/t6k11.c new file mode 100644 index 0000000..97207bc --- /dev/null +++ b/src/t6k11/t6k11.c @@ -0,0 +1,209 @@ +//--- +// gint:t6k11 - Toshiba T6K11 driver +//--- + +#include +#include + +#include +#include +#include + +#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 */