Compare commits

..

No commits in common. "master" and "2.6.0" have entirely different histories.

251 changed files with 3170 additions and 16511 deletions

View file

@ -1,15 +1,14 @@
# Build system for the gint unikernel # Build system for the gint unikernel
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(Gint VERSION 2.11.0 LANGUAGES C ASM) project(Gint VERSION 2.6.0 LANGUAGES C ASM)
include(GitVersionNumber) include(GitVersionNumber)
include(Fxconv) include(Fxconv)
option(GINT_NO_OS_STACK "Do not use the OS stack as a memory pool (fx-CG only)") option(GINT_USER_VRAM "Put all VRAMs into the user stack (fx-CG 50 only)")
option(GINT_STATIC_GRAY "Use static memory instead of malloc for gray buffers (fx-9860G only)") option(GINT_STATIC_GRAY "Use static memory instead of malloc for gray buffers (fx-9860G only)")
option(GINT_KMALLOC_DEBUG "Enable debug functions for kmalloc") option(GINT_KMALLOC_DEBUG "Enable debug functions for kmalloc")
option(GINT_USB_DEBUG "Enable debug functions for the USB driver")
set(CMAKE_INSTALL_MESSAGE LAZY) set(CMAKE_INSTALL_MESSAGE LAZY)
@ -22,101 +21,19 @@ else()
endif() endif()
configure_file(include/gint/config.h.in include/gint/config.h) configure_file(include/gint/config.h.in include/gint/config.h)
set(SOURCES set(SOURCES_COMMON
# Clock Pulse Generator driver
src/cpg/cpg.c src/cpg/cpg.c
src/cpg/overclock.c
# CPU driver
src/cpu/atomic.c src/cpu/atomic.c
src/cpu/cpu.c src/cpu/cpu.c
src/cpu/ics.s src/cpu/ics.s
src/cpu/registers.s src/cpu/registers.s
src/cpu/sleep.c src/cpu/sleep.c
# Direct Memory Access driver
src/dma/dma.c src/dma/dma.c
src/dma/inth.s src/dma/inth.s
src/dma/memcpy.c src/dma/memcpy.c
src/dma/memset.c src/dma/memset.c
# Filesystem interface
src/fs/close.c
src/fs/closedir.c
src/fs/creat.c
src/fs/fdopendir.c
src/fs/fs.c
src/fs/lseek.c
src/fs/mkdir.c
src/fs/open.c
src/fs/opendir.c
src/fs/pread.c
src/fs/pwrite.c
src/fs/read.c
src/fs/readdir.c
src/fs/rename.c
src/fs/rewinddir.c
src/fs/rmdir.c
src/fs/seekdir.c
src/fs/stat.c
src/fs/telldir.c
src/fs/unlink.c
src/fs/write.c
# Filesystem interface to Fugue
src/fs/fugue/BFile_Ext_Stat.c
src/fs/fugue/fugue.c
src/fs/fugue/fugue_dir.c
src/fs/fugue/fugue_open.c
src/fs/fugue/fugue_mkdir.c
src/fs/fugue/fugue_stat.c
src/fs/fugue/fugue_rename.c
src/fs/fugue/fugue_rmdir.c
src/fs/fugue/fugue_unlink.c
src/fs/fugue/util.c
# GDB remote serial protocol
src/gdb/gdb.c
src/gdb/gdb.S
# Gray engine
src/gray/engine.c
src/gray/gclear.c
src/gray/ggetpixel.c
src/gray/gint_gline.c
src/gray/gpixel.c
src/gray/grect.c
src/gray/gsubimage.c
src/gray/gtext.c
# Image library
src/image/image_alloc.c
src/image/image_alloc_palette.c
src/image/image_alpha.c
src/image/image_clear.c
src/image/image_copy.c
src/image/image_copy_alloc.c
src/image/image_copy_palette.c
src/image/image_create.c
src/image/image_create_vram.c
src/image/image_data_size.c
src/image/image_decode_pixel.c
src/image/image_fill.c
src/image/image_free.c
src/image/image_get_pixel.c
src/image/image_hflip.c
src/image/image_hflip_alloc.c
src/image/image_linear.c
src/image/image_linear.S
src/image/image_linear_alloc.c
src/image/image_rotate.c
src/image/image_rotate_around.c
src/image/image_rotate_around_scale.c
src/image/image_scale.c
src/image/image_set_palette.c
src/image/image_set_pixel.c
src/image/image_sub.c
src/image/image_target.c
src/image/image_valid.c
src/image/image_vflip.c
src/image/image_vflip_alloc.c
# Interrupt Controller driver
src/intc/intc.c src/intc/intc.c
src/intc/inth.s src/intc/inth.s
# Kernel
src/kernel/exch.c src/kernel/exch.c
src/kernel/exch.s src/kernel/exch.s
src/kernel/hardware.c src/kernel/hardware.c
@ -124,209 +41,118 @@ set(SOURCES
src/kernel/kernel.c src/kernel/kernel.c
src/kernel/osmenu.c src/kernel/osmenu.c
src/kernel/start.c src/kernel/start.c
src/kernel/start.S
src/kernel/syscalls.S src/kernel/syscalls.S
src/kernel/tlbh.S src/kernel/tlbh.S
src/kernel/world.c src/kernel/world.c
# Key Scan Interface driver
src/keysc/getkey.c src/keysc/getkey.c
src/keysc/iokbd.c src/keysc/iokbd.c
src/keysc/keycodes.c src/keysc/keycodes.c
src/keysc/keydev.c src/keysc/keydev.c
src/keysc/keydev_idle.c
src/keysc/keydev_process_key.c
src/keysc/keydown_all.c
src/keysc/keydown_any.c
src/keysc/keysc.c src/keysc/keysc.c
src/keysc/scan_frequency.c
# Memory allocator
src/kmalloc/arena_gint.c src/kmalloc/arena_gint.c
src/kmalloc/arena_osheap.c src/kmalloc/arena_osheap.c
src/kmalloc/kmalloc.c src/kmalloc/kmalloc.c
# MMU driver
src/mmu/mmu.c src/mmu/mmu.c
# R61523 display driver
src/r61523/r61523.c
# R61524 display driver
src/r61524/r61524.c
# Format-agnostic rendering
src/render/dcircle.c
src/render/dellipse.c
src/render/dhline.c src/render/dhline.c
src/render/dimage.c src/render/dimage.c
src/render/dline.c src/render/dline.c
src/render/dpoly.c
src/render/dprint.c src/render/dprint.c
src/render/drect_border.c src/render/drect_border.c
src/render/dtext.c src/render/dtext.c
src/render/dupdate_hook.c src/render/dupdate_hook.c
src/render/dvline.c src/render/dvline.c
src/render/dwindow.c
src/render/topti.c src/render/topti.c
# RGB Rendering src/rtc/rtc.c
src/render-cg/dclear.c src/rtc/rtc_ticks.c
src/render-cg/dgetpixel.c src/spu/spu.c
src/render-cg/dpixel.c src/tmu/inth-etmu.s
src/render-cg/drect.c src/tmu/inth-tmu.s
src/render-cg/dsubimage.c src/tmu/sleep.c
src/render-cg/dupdate.c src/tmu/tmu.c
src/render-cg/dvram.c src/usb/classes/ff-bulk.c
src/render-cg/dvram.S src/usb/configure.c
src/render-cg/gint_dline.c src/usb/pipes.c
src/render-cg/topti-asm.S src/usb/setup.c
src/render-cg/topti.c src/usb/string.c
# Fast RGB image renderer src/usb/usb.c
src/render-cg/image/image.c )
src/render-cg/image/image_rgb16.S set(SOURCES_FX
src/render-cg/image/image_rgb16_normal.S src/gray/engine.c
src/render-cg/image/image_rgb16_clearbg_dye.S src/gray/gclear.c
src/render-cg/image/image_rgb16_swapcolor.S src/gray/gint_gline.c
src/render-cg/image/image_p8.S src/gray/gpixel.c
src/render-cg/image/image_p8_normal.S src/gray/grect.c
src/render-cg/image/image_p8_clearbg.S src/gray/gsubimage.c
src/render-cg/image/image_p8_swapcolor.S src/gray/gtext.c
src/render-cg/image/image_p8_dye.S src/render-fx/bopti-asm-gray-scsp.s
src/render-cg/image/image_p4.S src/render-fx/bopti-asm-gray.s
src/render-cg/image/image_p4_normal.S src/render-fx/bopti-asm-mono-scsp.s
src/render-cg/image/image_p4_clearbg.S src/render-fx/bopti-asm.s
src/render-cg/image/image_p4_clearbg_alt.S
src/render-cg/image/image_p4_swapcolor.S
src/render-cg/image/image_p4_dye.S
# Interface to the fast RGB image renderer
src/render-cg/image/image_rgb16.c
src/render-cg/image/image_rgb16_effect.c
src/render-cg/image/image_rgb16_swapcolor.c
src/render-cg/image/image_rgb16_dye.c
src/render-cg/image/image_p8.c
src/render-cg/image/image_p8_effect.c
src/render-cg/image/image_p8_swapcolor.c
src/render-cg/image/image_p8_dye.c
src/render-cg/image/image_p4.c
src/render-cg/image/image_p4_clearbg_alt.c
src/render-cg/image/image_p4_effect.c
src/render-cg/image/image_p4_swapcolor.c
src/render-cg/image/image_p4_dye.c
# Mono rendering
src/render-fx/bopti-asm-gray-scsp.S
src/render-fx/bopti-asm-gray.S
src/render-fx/bopti-asm-mono-scsp.S
src/render-fx/bopti-asm.S
src/render-fx/bopti.c src/render-fx/bopti.c
src/render-fx/dclear.c src/render-fx/dclear.c
src/render-fx/dgetpixel.c
src/render-fx/dpixel.c src/render-fx/dpixel.c
src/render-fx/drect.c src/render-fx/drect.c
src/render-fx/dsubimage.c src/render-fx/dsubimage.c
src/render-fx/dupdate.c src/render-fx/dupdate.c
src/render-fx/gint_dline.c src/render-fx/gint_dline.c
src/render-fx/masks.c src/render-fx/masks.c
src/render-fx/topti-asm.S src/render-fx/topti-asm.s
src/render-fx/topti.c src/render-fx/topti.c
# RTC driver
src/rtc/rtc.c
src/rtc/rtc_ticks.c
# Sound Processing Unit driver
src/spu/spu.c
# T6K11 display driver
src/t6k11/t6k11.c src/t6k11/t6k11.c
# Timer Unit driver
src/tmu/inth-etmu.s
src/tmu/inth-tmu.s
src/tmu/sleep.c
src/tmu/tmu.c
# UBC driver
src/ubc/ubc.c
src/ubc/ubc.S
# USB driver
src/usb/asyncio.c
src/usb/classes/ff-bulk.c
src/usb/classes/ff-bulk-gray.c src/usb/classes/ff-bulk-gray.c
src/usb/configure.c )
src/usb/pipes.c set(SOURCES_CG
src/usb/read4.S src/r61524/r61524.c
src/usb/setup.c src/render-cg/bopti-asm.s
src/usb/string.c src/render-cg/bopti.c
src/usb/usb.c src/render-cg/dclear.c
src/usb/write4.S src/render-cg/dpixel.c
# Video driver interface src/render-cg/drect.c
src/video/video.c src/render-cg/dsubimage.c
src/render-cg/dupdate.c
src/render-cg/dvram.c
src/render-cg/gint_dline.c
src/render-cg/topti-asm.s
src/render-cg/topti.c
) )
set(ASSETS_FX src/font5x7.png src/gdb/icons-i1msb.png) set(ASSETS_FX src/font5x7.png)
set(ASSETS_CG src/font8x9.png src/gdb/icons-rgb565.png) set(ASSETS_CG src/font8x9.png)
set(ASSETS_CP ${ASSETS_CG})
fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG}) fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG})
include_directories( include_directories(
"${PROJECT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}/include"
"${PROJECT_BINARY_DIR}/include") "${PROJECT_BINARY_DIR}/include"
add_compile_options(-Wall -Wextra -std=c11 -Os -g -fstrict-volatile-bitfields -mtas -flto) "${FXSDK_COMPILER_INSTALL}/include/openlibm")
add_compile_options(-Wall -Wextra -std=c11 -Os -fstrict-volatile-bitfields -mtas)
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G) if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
add_compile_definitions(FX9860G) add_compile_definitions(FX9860G)
add_library(gint-fx STATIC ${SOURCES_COMMON} ${SOURCES_FX} ${ASSETS_FX})
set(NAME "gint-fx") set(NAME "gint-fx")
set(LINKER_SCRIPTS set(LINKER_SCRIPT "fx9860g.ld")
"${CMAKE_CURRENT_BINARY_DIR}/fx9860g.ld")
add_library(${NAME} STATIC ${SOURCES} ${ASSETS_FX} ${LINKER_SCRIPTS})
endif() endif()
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50) if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
add_compile_definitions(FXCG50) add_compile_definitions(FXCG50)
add_library(gint-cg STATIC ${SOURCES_COMMON} ${SOURCES_CG} ${ASSETS_CG})
set(NAME "gint-cg") set(NAME "gint-cg")
set(LINKER_SCRIPTS set(LINKER_SCRIPT "fxcg50.ld")
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50.ld"
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50_fastload.ld")
add_library(${NAME} STATIC ${SOURCES} ${ASSETS_CG} ${LINKER_SCRIPTS})
endif()
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_G3A)
add_compile_definitions(FX9860G_G3A)
set(NAME "gint-fxg3a")
set(LINKER_SCRIPTS
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50.ld"
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50_fastload.ld")
add_library(${NAME} STATIC ${SOURCES} ${ASSETS_FX} ${LINKER_SCRIPTS})
endif()
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCP)
add_compile_definitions(FXCP)
set(NAME "gint-cp")
set(LINKER_SCRIPTS
"${CMAKE_CURRENT_BINARY_DIR}/fxcp_hh2.ld")
add_library(${NAME} STATIC ${SOURCES} ${ASSETS_CP} ${LINKER_SCRIPTS})
endif() endif()
set_target_properties("${NAME}" PROPERTIES OUTPUT_NAME "${NAME}") set_target_properties("${NAME}" PROPERTIES OUTPUT_NAME "${NAME}")
# Generate the archive with gcc-ar instead of ar as it will load the LTO plugin
# which is required to generate a usable archive.
set(CMAKE_C_ARCHIVE_CREATE "${CMAKE_C_COMPILER_AR} qcs <TARGET> <OBJECTS>")
# Also the ranlib rule (useless because ar is passed the s flag anyway)
set(CMAKE_C_ARCHIVE_FINISH "${CMAKE_C_COMPILER_RANLIB} <TARGET>")
# Generate linker scripts
macro(generate_linker_script OUTPUT INPUT OPTIONS)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT}"
COMMAND ${FXSDK_TOOLCHAIN}cpp "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}"
-P -C -traditional-cpp ${OPTIONS} -o "${OUTPUT}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}")
endmacro()
generate_linker_script("fx9860g.ld" "fx9860g.ld.c" "")
generate_linker_script("fxcg50.ld" "fxcg50.ld.c" "")
generate_linker_script("fxcg50_fastload.ld" "fxcg50.ld.c" "-DFXCG50_FASTLOAD")
generate_linker_script("fxcp_hh2.ld" "fxcp_hh2.ld.c" "")
# Library file # Library file
install(TARGETS "${NAME}" DESTINATION "${FXSDK_LIB}") install(TARGETS "${NAME}" DESTINATION "${FXSDK_COMPILER_INSTALL}")
# Linker scripts # Linker script
install(FILES ${LINKER_SCRIPTS} DESTINATION "${FXSDK_LIB}") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_SCRIPT}"
DESTINATION "${FXSDK_COMPILER_INSTALL}")
# Headers # Headers
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/" install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include"
DESTINATION "${FXSDK_INCLUDE}" DESTINATION "${FXSDK_COMPILER_INSTALL}"
FILES_MATCHING PATTERN "*.h") FILES_MATCHING PATTERN "*.h")
# Auto-generated config header # Auto-generated config header
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/gint/config.h" install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/gint/config.h"
DESTINATION "${FXSDK_INCLUDE}/gint") DESTINATION "${FXSDK_COMPILER_INSTALL}/include/gint")
# CMake module to find gint # CMake module to find gint
install(FILES cmake/FindGint.cmake DESTINATION "${FXSDK_CMAKE_MODULE_PATH}") install(FILES cmake/FindGint.cmake DESTINATION "${FXSDK_CMAKE_MODULE_PATH}")

View file

@ -32,7 +32,7 @@ The library also offers powerful higher-level features:
* An enhanced version of the system's GetKey() and GetKeyWait() * An enhanced version of the system's GetKey() and GetKeyWait()
* A gray engine that works by rapidly swapping monochrome images on fx-9860G II * A gray engine that works by rapidly swapping monochrome images on fx-9860G II
* Blazingly fast rendering functions (image rendering is 10 times faster than * Blazingly fast rendering functions (image rendering is 10 times faster tha
MonochromeLib) MonochromeLib)
* Integrated font management * Integrated font management
@ -66,6 +66,12 @@ CMake environment for the calculator. gint is always installed in the
compiler's install path (as given by `sh-elf-gcc --print-search-dirs`) which is compiler's install path (as given by `sh-elf-gcc --print-search-dirs`) which is
detected automatically, so normally you don't need to set the install prefix. detected automatically, so normally you don't need to set the install prefix.
fx-CG 50 developers probably want a g3a wrapper as well; the reference
implementation is Tari's [mkg3a](https://gitlab.com/taricorp/mkg3a). This is
needed at the very last compilation step to create the g3a file. On Arch Linux,
you can use the [AUR/mkg3a](https://aur.archlinux.org/packages/mkg3a) package
maintained directly by Tari.
gint depends on OpenLibm and fxlibc (linked above). Both can be installed gint depends on OpenLibm and fxlibc (linked above). Both can be installed
easily (essentially copy-paste the command from the respective READMEs). You easily (essentially copy-paste the command from the respective READMEs). You
can technically use another libc but there be dragons. can technically use another libc but there be dragons.
@ -105,17 +111,6 @@ The available options are:
% fxsdk build-cg install % fxsdk build-cg install
``` ```
**"Cross-Building" a fx-9860G II project for fx-CG 50**
Programs written for fx-9860G can also be built to run on the fx-CG series,
provided no low-level function or hardware-specific behavior (like syscalls) is
used by the program.
```
% fxsdk build-fxg3a
% fxsdk build-fxg3a install
```
## Using in CMake-based add-ins ## Using in CMake-based add-ins
Find the `Gint` module and link against `Gint::Gint`. gint declares the include Find the `Gint` module and link against `Gint::Gint`. gint declares the include
@ -132,16 +127,10 @@ target_link_libraries(<target_name> Gint::Gint)
Projects created with the fxSDK link with gint out-of-the-box. If you're not Projects created with the fxSDK link with gint out-of-the-box. If you're not
using the fxSDK, you will need to: using the fxSDK, you will need to:
* Build with `-ffreestanding -fstrict-volatile-bitfields` and either * Build with `-ffreestanding -fstrict-volatile-bitfields`;
`-DFX9860G` or `-DFXGC50`;
* Link with `-T fx9860g.ld -lgint-fx -lopenlibm -lc` on fx-9860G; * Link with `-T fx9860g.ld -lgint-fx -lopenlibm -lc` on fx-9860G;
* Link with `-T fxcg50.ld -lgint-cg -lopenlibm -lc` on fx-CG 50. * Link with `-T fxcg50.ld -lgint-cg -lopenlibm -lc` on fx-CG 50.
To manually build the fx-CG executable for an fx-9860G program, mix as follows:
* Use platform flags `-DFXCG50 -DFX9860G_G3A`;
* Link with `-T fxcg50.ld -lgint-fxg3a -lopenlibm -lc`.
If you don't have a standard library such as If you don't have a standard library such as
[Memallox's port of newlib](/PlaneteCasio/libc), you also need `-nostdlib`. I [Memallox's port of newlib](/PlaneteCasio/libc), you also need `-nostdlib`. I
typically use `-m3 -mb` or `-m4-nofpu -mb` to specify the platform, but that typically use `-m3 -mb` or `-m4-nofpu -mb` to specify the platform, but that

78
TODO
View file

@ -1,82 +1,22 @@
Bugs to fix:
* render: figure out why fx-CG dclear() now takes 4.1 ms instead of 2.6 ms
Extensions on existing code: Extensions on existing code:
* clock: mono support
* usb: and interrupt pipes
* fs: support RAM files
* fs: support USB streams as generic file descriptors
* fugue: support glob() to abstract away BFile entirely
* fugue/fs: offer a primitive to remove recursively, which is native in Fugue
* bfile: implement the optimization-restart as realized by Kbd2 * bfile: implement the optimization-restart as realized by Kbd2
* kernel: better restore to userspace before panic (ensure BL=0 IMASK=0) * kernel: better restore to userspace before panic (ensure BL=0 IMASK=0)
* project: add license file * project: add license file
* kernel: group linker script symbols in a single header file * kernel: group linker script symbols in a single header file
* bopti: try to display fullscreen images with TLB access + DMA on fxcg50
* core: try to leave add-in without reset in case of panic
* core: use cmp/str for memchr()
* r61524: brightness control and clean the file * r61524: brightness control and clean the file
* core: review forgotten globals and MPU addresses not in <gint/mpu/*.h> * core: review forgotten globals and MPU addresses not in <gint/mpu/*.h>
* core: run destructors when a task-switch results in leaving the app * core: run destructors when a task-switch results in leaving the app
* fs: support read-only files backed with GetBlockAddress() on fx-CG * core rtc: use qdiv10 to massively improve division performance
* kernel: SH4- or G-III-specific linker scripts?
* render: Properly document bopti fx. Also make it faster maybe?
Future directions: Future directions.
* A complete file system abstraction
* Integrate overclock management
* Audio playback using TSWilliamson's libsnd method * Audio playback using TSWilliamson's libsnd method
* Serial communication * Serial communication
* USB communication, using Yatis' reverse-engineering of the module
* Make fx9860g projects work out of the box on fxcg50 * Make fx9860g projects work out of the box on fxcg50
* Use the DSP to enhance parallel computation
* Base for Yatis' threads library * Base for Yatis' threads library
Bits that need to abstract out into APIs:
- display: Toggle backlight (or set level)
-> Have a backlight property with min/max values
@ getkey
@ justui jscene
- os: OS interface to access OS info, like in fxos
@ usb/setup.c set_strings
@ usb/classes/ff-bulk.c identify command
- render: current video mode
@ usb/classes/ff-bulk.c capture_vram
- gray: make gray engine a subset of t6k11 which then supports two modes
Video formats:
- i1msb
- 2i1msb
- rgb565
T6K11:
mode 0 : i1msb 128x64 @64 Hz
mode 1 : 2i1msb 128x64 @64 Hz
R61524:
mode 0 : rgb565 396x224 @? Hz
mode 1 : i3??? 396x224 @? Hz
mode 2 : i1msb 128x64 @? Hz # <- for g1a emulation :)
mode 3 : 2i1msb 128x64 @? Hz # <- for g1a emulation :)
enum {
/* Indexed 1-bit, 0 is white, 1 is black. Row-major, left-to-right, each
row a set of 4-byte values with leftmost pixel on the MSB. */
IMAGE_FORMAT_I1MSB,
/* Indexed 2-bit: white, light gray, dark gray, black. Represented as a
pair of I1MSB buffers, the first being bit #0, the second bit #1. */
IMAGE_FORMAT_2I1MSB,
};
fx-CP port progress.
DONE:
- kernel can start add-ins, take over interrupts (but no interrupt sources yet)
- panic handler works (no interactive choices, only RESET)
- no OS menu (likely never) and poweroff (maybe later)
- memory: 64 kB total + OS heap (no gint arena, no MMU)
- r61523: prototype display driver
- can use the rendering pipeline with no DMA acceleration
IN-PROGRESS:
- unification of the image/video interface
TODO:
- hardware info: rom and fs type
- quit handler
- DMA acceleration for dclear and dupdate
- keyboard and touch screen input
- drivers: cpg, dma, intc, rtc, tmu
- find more memory, 64 kB is not enough
- benchmark memory and display performance
- improve API and performance of display driver
- way off: filesystem, remote debugging, usb

View file

@ -1,30 +1,12 @@
macro(get_linker_script_path OUTVAR NAME)
execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=${NAME}
OUTPUT_VARIABLE ${OUTVAR}
OUTPUT_STRIP_TRAILING_WHITESPACE)
endmacro()
# Determine platform Code # Determine platform Code
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50) if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
set(PC cg) set(PC cg)
set(INTF_DEFN FXCG50) set(INTF_DEFN FXCG50)
get_linker_script_path(LS1 fxcg50_fastload.ld) set(INTF_LINK "-T;fxcg50.ld")
get_linker_script_path(LS2 fxcg50.ld)
set(INTF_LINKER_SCRIPT
"$<IF:$<CONFIG:FastLoad>,${LS1},${LS2}>")
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G) elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
set(PC fx) set(PC fx)
set(INTF_DEFN FX9860G) set(INTF_DEFN FX9860G)
get_linker_script_path(INTF_LINKER_SCRIPT "fx9860g.ld") set(INTF_LINK "-T;fx9860g.ld")
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_G3A)
set(PC fxg3a)
set(INTF_DEFN FX9860G_G3A)
get_linker_script_path(INTF_LINKER_SCRIPT "fxcg50.ld")
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fxCP)
set(PC cp)
set(INTF_DEFN FXCP)
get_linker_script_path(INTF_LINKER_SCRIPT "fxcp_hh2.ld")
else() else()
message(FATAL_ERROR "gint: unknown fxSDK platform '${FXSDK_PLATFORM}'") message(FATAL_ERROR "gint: unknown fxSDK platform '${FXSDK_PLATFORM}'")
endif() endif()
@ -32,16 +14,13 @@ endif()
execute_process( execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libgint-${PC}.a COMMAND ${CMAKE_C_COMPILER} -print-file-name=libgint-${PC}.a
OUTPUT_VARIABLE GINT_PATH OUTPUT_VARIABLE GINT_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process( execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.a COMMAND ${CMAKE_C_COMPILER} -print-file-name=include/gint/config.h
OUTPUT_VARIABLE FXLIBC_PATH OUTPUT_VARIABLE GINT_CONFIG_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE) OUTPUT_STRIP_TRAILING_WHITESPACE
execute_process( )
COMMAND fxsdk path include
OUTPUT_VARIABLE FXSDK_INCLUDE
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(GINT_CONFIG_PATH "${FXSDK_INCLUDE}/gint/config.h")
if("${GINT_PATH}" STREQUAL "libgint-${PC}.a") if("${GINT_PATH}" STREQUAL "libgint-${PC}.a")
unset(PATH_TO_LIBGINT) unset(PATH_TO_LIBGINT)
@ -56,25 +35,13 @@ else()
endif() endif()
endif() endif()
if("${FXLIBC_PATH}" STREQUAL "libc.a")
unset(PATH_TO_FXLIBC)
else()
set(PATH_TO_FXLIBC TRUE)
endif()
include(FindPackageHandleStandardArgs) include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Gint find_package_handle_standard_args(Gint
REQUIRED_VARS PATH_TO_FXLIBC PATH_TO_LIBGINT REQUIRED_VARS PATH_TO_LIBGINT
VERSION_VAR GINT_VERSION) VERSION_VAR GINT_VERSION
)
if(Gint_FOUND) if(Gint_FOUND)
if(NOT TARGET Gint::Fxlibc)
add_library(Gint::Fxlibc UNKNOWN IMPORTED)
endif()
set_target_properties(Gint::Fxlibc PROPERTIES
IMPORTED_LOCATION "${FXLIBC_PATH}"
INTERFACE_LINK_OPTIONS "-lgcc")
if(NOT TARGET Gint::Gint) if(NOT TARGET Gint::Gint)
add_library(Gint::Gint UNKNOWN IMPORTED) add_library(Gint::Gint UNKNOWN IMPORTED)
endif() endif()
@ -82,10 +49,8 @@ if(Gint_FOUND)
IMPORTED_LOCATION "${GINT_PATH}" IMPORTED_LOCATION "${GINT_PATH}"
INTERFACE_COMPILE_OPTIONS -fstrict-volatile-bitfields INTERFACE_COMPILE_OPTIONS -fstrict-volatile-bitfields
INTERFACE_COMPILE_DEFINITIONS "${INTF_DEFN}" INTERFACE_COMPILE_DEFINITIONS "${INTF_DEFN}"
INTERFACE_LINK_LIBRARIES "-lopenlibm;-lgcc" INTERFACE_INCLUDE_DIRECTORIES "${FXSDK_COMPILER_INSTALL}/include/openlibm"
INTERFACE_LINK_OPTIONS "-T;${INTF_LINKER_SCRIPT}" INTERFACE_LINK_LIBRARIES "-lc;-lopenlibm;-lgcc"
INTERFACE_LINK_DEPENDS "${INTF_LINKER_SCRIPT}") INTERFACE_LINK_OPTIONS "${INTF_LINK}"
)
target_link_libraries(Gint::Gint INTERFACE Gint::Fxlibc)
target_link_libraries(Gint::Fxlibc INTERFACE Gint::Gint)
endif() endif()

View file

@ -35,7 +35,8 @@ MEMORY
/* On-chip IL memory */ /* On-chip IL memory */
ilram (rwx): o = 0xe5200000, l = 4k ilram (rwx): o = 0xe5200000, l = 4k
/* On-chip X and Y memory */ /* On-chip X and Y memory */
xyram (rwx): o = 0xe500e000, l = 16k xram (rwx): o = 0xe5007000, l = 8k
yram (rwx): o = 0xe5017000, l = 8k
} }
SECTIONS SECTIONS
@ -61,19 +62,19 @@ SECTIONS
*(.text.entry) *(.text.entry)
_bctors = . ; _bctors = . ;
KEEP(*(.ctors .ctors.*)) *(.ctors .ctors.*)
_ectors = . ; _ectors = . ;
_bdtors = . ; _bdtors = . ;
KEEP(*(.dtors .dtors.*)) *(.dtors .dtors.*)
_edtors = . ; _edtors = . ;
_gint_exch_start = . ; _gint_exch_start = . ;
KEEP(*(.gint.exch)) *(.gint.exch)
_gint_exch_size = ABSOLUTE(. - _gint_exch_start); _gint_exch_size = ABSOLUTE(. - _gint_exch_start);
_gint_tlbh_start = . ; _gint_tlbh_start = . ;
KEEP(*(.gint.tlbh)) *(.gint.tlbh)
_gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start); _gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start);
*(.text .text.*) *(.text .text.*)
@ -109,7 +110,6 @@ SECTIONS
*(.rodata.4) *(.rodata.4)
*(.rodata .rodata.*) *(.rodata .rodata.*)
*(.gint.rodata.sh3)
} > rom } > rom
@ -128,7 +128,7 @@ SECTIONS
.bss (NOLOAD) : { .bss (NOLOAD) : {
_rbss = . ; _rbss = . ;
*(.bss .bss.* COMMON) *(.bss COMMON)
*(B R) *(B R)
. = ALIGN(16); . = ALIGN(16);
@ -188,18 +188,29 @@ SECTIONS
. = ALIGN(16); . = ALIGN(16);
} > ilram AT> rom } > ilram AT> rom
. = ORIGIN(xyram); . = ORIGIN(xram);
.xyram ALIGN(4) : ALIGN(4) { .xram ALIGN(4) : ALIGN(4) {
_lxyram = LOADADDR(.xyram); _lxram = LOADADDR(.xram);
_rxyram = . ; _rxram = . ;
*(.xram .yram .xyram) *(.xram)
. = ALIGN(16); . = ALIGN(16);
} > xyram AT> rom } > xram AT> rom
. = ORIGIN(yram);
.yram ALIGN(4) : ALIGN(4) {
_lyram = LOADADDR(.yram);
_ryram = . ;
*(.yram)
. = ALIGN(16);
} > yram AT> rom
_silram = SIZEOF(.ilram); _silram = SIZEOF(.ilram);
_sxyram = SIZEOF(.xyram); _sxram = SIZEOF(.xram);
_syram = SIZEOF(.yram);
@ -226,6 +237,9 @@ SECTIONS
*/ */
/DISCARD/ : { /DISCARD/ : {
/* Debug sections (often from libgcc) */
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
.debug_ranges .debug_line .debug_str)
/* Java class registration (why are they even here?!) */ /* Java class registration (why are they even here?!) */
*(.jcr) *(.jcr)
/* Asynchronous unwind tables: no C++ exception handling */ /* Asynchronous unwind tables: no C++ exception handling */

View file

@ -10,24 +10,19 @@ OUTPUT_FORMAT(elf32-sh)
/* Located in core/start.c */ /* Located in core/start.c */
ENTRY(_start) ENTRY(_start)
#ifdef FXCG50_FASTLOAD
# define _ROM_REGION 0x8c200000
#else
# define _ROM_REGION 0x00300000
#endif
MEMORY MEMORY
{ {
/* Userspace mapping of the add-in (without G3A header) */ /* Userspace mapping of the add-in (without G3A header) */
rom (rx): o = _ROM_REGION, l = 2M rom (rx): o = 0x00300000, l = 2M
/* Static RAM; stack grows down from the end of this region. /* Static RAM; stack grows down from the end of this region.
The first 5k (0x1400 bytes) are reserved by gint for the VBR space, The first 5k (0x1400 bytes) are reserved by gint for the VBR space,
which is loaded dynamically and accessed through P1 */ which is loaded dynamically and accessed through P1 */
ram (rw): o = 0x08101400, l = 491k ram (rw): o = 0x08101400, l = 507k
/* On-chip IL memory */ /* On-chip IL memory */
ilram (rwx): o = 0xe5200000, l = 4k ilram (rwx): o = 0xe5200000, l = 4k
/* On-chip X and Y memory */ /* On-chip X and Y memory */
xyram (rwx): o = 0xe500e000, l = 16k xram (rwx): o = 0xe5007000, l = 8k
yram (rwx): o = 0xe5017000, l = 8k
} }
SECTIONS SECTIONS
@ -37,7 +32,7 @@ SECTIONS
*/ */
/* First address to be mapped to ROM */ /* First address to be mapped to ROM */
_brom = ORIGIN(rom); _brom = 0x00300000;
/* Size of ROM mappings */ /* Size of ROM mappings */
_srom = SIZEOF(.text) + SIZEOF(.rodata) _srom = SIZEOF(.text) + SIZEOF(.rodata)
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks); + SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
@ -50,19 +45,19 @@ SECTIONS
*(.text.entry) *(.text.entry)
_bctors = . ; _bctors = . ;
KEEP(*(.ctors .ctors.*)) *(.ctors .ctors.*)
_ectors = . ; _ectors = . ;
_bdtors = . ; _bdtors = . ;
KEEP(*(.dtors .dtors.*)) *(.dtors .dtors.*)
_edtors = . ; _edtors = . ;
_gint_exch_start = . ; _gint_exch_start = . ;
KEEP(*(.gint.exch)) *(.gint.exch)
_gint_exch_size = ABSOLUTE(. - _gint_exch_start); _gint_exch_size = ABSOLUTE(. - _gint_exch_start);
_gint_tlbh_start = . ; _gint_tlbh_start = . ;
KEEP(*(.gint.tlbh)) *(.gint.tlbh)
_gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start); _gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start);
*(.text .text.*) *(.text .text.*)
@ -107,7 +102,8 @@ SECTIONS
.bss (NOLOAD) : { .bss (NOLOAD) : {
_rbss = . ; _rbss = . ;
*(.bss .bss.* COMMON) *(.bss.vram)
*(.bss COMMON)
. = ALIGN(16); . = ALIGN(16);
} > ram :NONE } > ram :NONE
@ -148,23 +144,29 @@ SECTIONS
. = ALIGN(16); . = ALIGN(16);
} > ilram AT> rom } > ilram AT> rom
. = ORIGIN(xyram); . = ORIGIN(xram);
.xyram ALIGN(4) : ALIGN(4) { .xram ALIGN(4) : ALIGN(4) {
_lxyram = LOADADDR(.xyram); _lxram = LOADADDR(.xram);
_rxyram = . ; _rxram = . ;
*(.xram .yram .xyram) *(.xram)
. = ALIGN(16); . = ALIGN(16);
} > xyram AT> rom } > xram AT> rom
. = ORIGIN(yram);
.yram ALIGN(4) : ALIGN(4) {
_lyram = LOADADDR(.yram);
_ryram = . ;
*(.yram)
. = ALIGN(16);
} > yram AT> rom
_silram = SIZEOF(.ilram); _silram = SIZEOF(.ilram);
_sxyram = SIZEOF(.xyram); _sxram = SIZEOF(.xram);
_syram = SIZEOF(.yram);
_lgmapped = ABSOLUTE(0);
_sgmapped = ABSOLUTE(0);
_lreloc = ABSOLUTE(0);
_sreloc = ABSOLUTE(0);
/* gint's uninitialized BSS section, going to static RAM. All the large /* gint's uninitialized BSS section, going to static RAM. All the large
data arrays will be located here */ data arrays will be located here */
@ -186,7 +188,10 @@ SECTIONS
/DISCARD/ : { /DISCARD/ : {
/* SH3-only data sections */ /* SH3-only data sections */
*(.gint.rodata.sh3 .gint.data.sh3 .gint.bss.sh3) *(.gint.data.sh3 .gint.bss.sh3)
/* Debug sections (often from libgcc) */
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
.debug_ranges .debug_line .debug_str)
/* Java class registration (why are they even here?!) */ /* Java class registration (why are they even here?!) */
*(.jcr) *(.jcr)
/* Asynchronous unwind tables: no C++ exception handling */ /* Asynchronous unwind tables: no C++ exception handling */

View file

@ -1,206 +0,0 @@
/*
Linker script for fxCP add-ins linking with HollyHock2.
*/
OUTPUT_ARCH(sh4)
OUTPUT_FORMAT(elf32-sh)
ENTRY(_start_header)
MEMORY
{
/* VRAM backup - our main fixed area to load code, size 320x528x2 */
ram (rwx): o = 0x8c052800, l = 337920
/* End-of-RAM area, 104 kB are safe to use according to experience. What
happens is HollyHock itself is loaded at 0x8cfe0000 (128 kB before the
end) so we have to fit after it. CPBoy uses 0x8cfe6000, follow that. */
eram (rwx): o = 0x8cfe6000, l = 102144 /* 104k - 0x1100 */
/* Space for the vbr */
vbr (rwx): o = 0x8cffef00, l = 0x1100
/* On-chip IL memory */
ilram (rwx): o = 0xe5200000, l = 4k
/* On-chip X and Y memory */
xyram (rwx): o = 0xe500e000, l = 16k
/* Flat binary space, used to have everything in the correct order in the
binary. This is just for the bin file layout. */
bin (rwx): o = 0x00000000, l = 1M
}
SECTIONS
{
/*
** ROM sections
*/
/* First address to be mapped to ROM */
_brom = ORIGIN(ram);
/* Size of ROM mappings */
_srom = SIZEOF(.text) + SIZEOF(.rodata)
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
/* Machine code going to ROM:
- Entry function (.text.entry)
- Compiler-provided constructors (.ctors) and destructors (.dtors)
- All text from .text and .text.* (including user code) */
.hh2 : {
KEEP(*(.hh2.header))
KEEP(*(.hh2.info))
KEEP(*(.hh2.stage2))
. = ALIGN(16);
} > eram AT> bin
_gint_hh2_stage2_offset = SIZEOF(.hh2);
_gint_hh2_stage2_load = ORIGIN(ram);
.text : {
*(.text.entry)
_bctors = . ;
*(.ctors .ctors.*)
_ectors = . ;
_bdtors = . ;
*(.dtors .dtors.*)
_edtors = . ;
. = ALIGN(0x10);
_gint_exch_start = . ;
*(.gint.exch)
_gint_exch_size = ABSOLUTE(. - _gint_exch_start);
. = ALIGN(0x10);
_gint_tlbh_start = . ;
*(.gint.tlbh)
_gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start);
*(.text .text.*)
} > ram AT> bin
/* gint's interrupt handler blocks (.gint.blocks)
Although gint's blocks end up in VBR space, they are relocated at
startup by the library/drivers, so we store them here for now */
.gint.blocks ALIGN(4) : ALIGN(4) {
KEEP(*(.gint.blocks));
} > ram AT> bin
/* 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 ALIGN(4) : ALIGN(4) {
_gint_drivers = . ;
KEEP(*(SORT_BY_NAME(.gint.drivers.*)));
_gint_drivers_end = . ;
} > ram AT> bin
/* 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 ALIGN(4) : ALIGN(4) SUBALIGN(4) {
/* Put these first, they need to be 4-aligned */
*(.rodata.4)
*(.rodata .rodata.*)
} > ram AT> bin
/*
** RAM sections
*/
/* Read-write data sections going to RAM (.data and .data.*) */
.data ALIGN(4) : ALIGN(4) {
_ldata = LOADADDR(.data);
_rdata = . ;
*(.data .data.*)
/* Code that must remain mapped; no MMU on HH2, so fine */
*(.gint.mapped)
/* References to mapped code - no relocation needed */
*(.gint.mappedrel)
. = ALIGN(16);
} > ram AT> bin
/* Read-write data sub-aligned to 4 bytes (mainly from fxconv) */
.data.4 ALIGN(4) : ALIGN(4) SUBALIGN(4) {
*(.data.4)
. = ALIGN(16);
} > ram AT> bin
_sdata = SIZEOF(.data) + SIZEOF(.data.4);
/* On-chip memory sections: IL, X and Y memory */
. = ORIGIN(ilram);
.ilram ALIGN(4) : ALIGN(4) {
_lilram = ABSOLUTE(LOADADDR(.ilram) + ORIGIN(ram));
_rilram = . ;
*(.ilram)
. = ALIGN(16);
} > ilram AT> bin
. = ORIGIN(xyram);
.xyram ALIGN(4) : ALIGN(4) {
_lxyram = ABSOLUTE(LOADADDR(.xyram) + ORIGIN(ram));
_rxyram = . ;
*(.xram .yram .xyram)
. = ALIGN(16);
} > xyram AT> bin
/* .text and .xyram are the first and last section in the stage-2 load. */
_gint_hh2_stage2_size = LOADADDR(.xyram) + SIZEOF(.xyram) - LOADADDR(.text);
_silram = SIZEOF(.ilram);
_sxyram = SIZEOF(.xyram);
_lgmapped = ABSOLUTE(0);
_sgmapped = ABSOLUTE(0);
_lreloc = ABSOLUTE(0);
_sreloc = ABSOLUTE(0);
_gint_region_vbr = ORIGIN(vbr);
/* 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 .bss.* COMMON)
. = ALIGN(16);
} > ram :NONE
_sbss = SIZEOF(.bss);
/* gint's uninitialized BSS section, going to static RAM. All the large
data arrays will be located here */
.gint.bss (NOLOAD) : {
*(.gint.bss)
. = ALIGN(16);
/* End of user RAM */
_euram = . ;
} > ram :NONE
_sgbss = SIZEOF(.gint.bss);
/*
** Unused sections
*/
/DISCARD/ : {
/* SH3-only data sections */
*(.gint.rodata.sh3 .gint.data.sh3 .gint.bss.sh3)
/* Java class registration (why are they even here?!) */
*(.jcr)
/* Asynchronous unwind tables: no C++ exception handling */
*(.eh_frame_hdr)
*(.eh_frame)
/* Comments or anything the compiler might generate */
*(.comment)
}
}

View file

@ -5,17 +5,14 @@
configure: configure:
@ fxsdk build-fx -c $(GINT_CMAKE_OPTIONS) @ fxsdk build-fx -c $(GINT_CMAKE_OPTIONS)
@ fxsdk build-cg -c $(GINT_CMAKE_OPTIONS) @ fxsdk build-cg -c $(GINT_CMAKE_OPTIONS)
@ fxsdk build-fxg3a -c $(GINT_CMAKE_OPTIONS)
build: build:
@ fxsdk build-fx @ fxsdk build-fx
@ fxsdk build-cg @ fxsdk build-cg
@ fxsdk build-fxg3a
install: install:
@ fxsdk build-fx install @ fxsdk build-fx install
@ fxsdk build-cg install @ fxsdk build-cg install
@ fxsdk build-fxg3a install
uninstall: uninstall:
@ if [ -e build-fx/install_manifest.txt ]; then \ @ if [ -e build-fx/install_manifest.txt ]; then \
@ -24,8 +21,5 @@ uninstall:
@ if [ -e build-cg/install_manifest.txt ]; then \ @ if [ -e build-cg/install_manifest.txt ]; then \
xargs rm -f < build-cg/install_manifest.txt; \ xargs rm -f < build-cg/install_manifest.txt; \
fi fi
@ if [ -e build-fxg3a/install_manifest.txt ]; then \
xargs rm -f < build-fxg3a/install_manifest.txt; \
fi
.PHONY: configure build install uninstall .PHONY: configure build install uninstall

View file

@ -1,60 +1,9 @@
//--- //---
// gint:bfile - BFile interface // gint:bfile - BFile interface
// //
// BFile is the OS's native interface to the filesystem. It has quite a bit of // The system's file system is a nightmare and the risk of corrupting it
// legacy, so it's not very easy to use. Please note that BFile cannot be used // or the flash by driving it manually is too great to risk. This module
// from within gint, so do a gint_world_switch() (<gint/gint.h>) to call BFile // interfaces with the BFile syscalls for file management.
// functions; otherwise the add-in is likely to crash.
//
// There are two different filesystems:
// * An older, in-house one made by CASIO and informally called CASIOWIN (which
// is the name of the OS itself), which is incomplete, hard to use, and very
// limiting, but also very fast.
// * A newer one, designed by Kyoto Software Research and called Fugue, which
// is mostly complete and behaves as a regular filesystem, though very slow.
//
// You can detect which one is used by querying gint[HWFS] (<gint/hardware.h>):
// * HWFS_FUGUE is the newer one; it is found in:
// - The fx-CG series (in France: Prizm and Graph 90+E)
// - The fx G-III series (in France: Graph 35+E II)
// * HWFS_CASIOWIN is the older one; it is found in all older black-and-white
// model that have a storage memory.
//
// Wherever Fugue is used, gint supports the Unix file API (open, read, write,
// etc) and the C99 standard file API (fopen, fread, fwrite, etc), so there is
// no need to call into BFile directly (you should still do a world switch
// before using these functions).
//
// You should only have to use BFile if you're dealing with the CASIOWIN
// filesystem. With Fugue, not only are the Unix/C99 APIs easier to use, but
// the meanings of arguments and return values in BFile calls also change
// compared to the CASIOWIN filesystem, so there's little to gain anyway.
//
// If you're here for the CASIOWIN filesystem, be aware of its limitations. In
// general reading files will work fine; expect no issues with that. Modifying
// files is where things get difficult. Keep the following in mind:
//
// * Non-standard meanings of arguments and return values (eg. BFile_Read()
// returns the number of readable bytes between the new position and EOF).
// * Files must be created with a fixed size indicated in BFile_Create() and
// are initialized with 0xff.
// * A write to a file can only replace 1's with 0's, meaning in practice a
// file can only be written to once. (That's why it's created with 0xff.)
// * All writes must be of even size; writing an odd number of bytes can mess
// up the file and confuse the filesystem.
// * Only one level of folders is supported; attempting to create second-level
// subfolders results in weird recursive directories (don't do that).
//
// File paths in the OS use the non-standard FONTCHARACTER encoding, which is a
// 16-bit fixed-width encoding. Most users don't care about special characters;
// in GCC, you can get a 16-bit literal string with the u"" prefix, eg.
// u"\\\\fls0\\data.bin"
// which is what you usually supply as the [uint16_t const *path] argument.
//
// All functions return nonnegative values on success. If no return value is
// described then a successful call returns 0. Unless specified otherwise, all
// functions can also return a negative error code as documented near the end
// of this header.
//--- //---
#ifndef GINT_BFILE #ifndef GINT_BFILE
@ -66,170 +15,88 @@ extern "C" {
#include <stdint.h> #include <stdint.h>
//--- /* BFile_Remove(): Remove a file
// Common file access functions Also works if the file does not exist.
//---
/* Remove a file or folder (also works if the entry does not exist). */ @file FONTCHARACTER file path
int BFile_Remove(uint16_t const *path); Returns a BFile error code. */
int BFile_Remove(uint16_t const *file);
/* Rename a file (can move folders; Fugue only). */
int BFile_Rename(uint16_t const *oldpath, uint16_t const *newpath);
#define BFile_File 1
#define BFile_Folder 5
/* BFile_Create(): Create a new file or folder
/* BFile_Create(): Create a new entry
The file or directory must not exist. For a file the size pointer must point The file or directory must not exist. For a file the size pointer must point
to the desired file size (which is fixed), for a folder it is ignored. With to the desired file size (which is fixed), for a folder it is ignored.
CASIOWIN this is the final size of the file. With Fugue the file can be
resized dynamically and is usually created with initial size 0.
@path FONTCHARACTER file path @file FONTCHARACTER file path
@type Entry type (BFile_File or BFile_Folder) @type Entry type
@size Pointer to file size if type is BFile_File, use NULL otherwise */ @size Pointer to file size if [type = BFile_File], use NULL otherwise
int BFile_Create(uint16_t const *path, int type, int *size); Returns a BFile error code. */
enum BFile_EntryType
{
BFile_File = 1,
BFile_Folder = 5,
};
int BFile_Create(uint16_t const *file, enum BFile_EntryType type, int *size);
#define BFile_ReadOnly 0x01 /* BFile_Open(): Open an existing file
#define BFile_WriteOnly 0x02 The file must exist.
#define BFile_ReadWrite (BFile_ReadOnly | BFile_WriteOnly)
#define BFile_Share 0x80
#define BFile_ReadShare (BFile_ReadOnly | BFile_Share)
#define BFile_ReadWriteShare (BFile_ReadWrite | BFile_Share)
/* BFile_Open(): Open a file for reading or writing @file FONTCHARACTER file path
The file must exist, even when opening in writing mode. The meaning of the
[Share] flag isn't clear; I believe it's simply ignored in the CASIOWIN
filesystem.
@path FONTCHARACTER file path
@mode Desired access mode @mode Desired access mode
Returns a file descriptor (or a negative error code). */ Returns a file descriptor on success, or a negative BFile error code. */
int BFile_Open(uint16_t const *path, int mode); enum BFile_OpenMode
{
BFile_ReadOnly = 0x01,
BFile_WriteOnly = 0x02,
BFile_ReadWrite = BFile_ReadOnly | BFile_WriteOnly,
};
int BFile_Open(uint16_t const *file, enum BFile_OpenMode mode);
/* Close an open file descriptor. */ /* BFile_Close(): Close a file descriptor
@fd Open file descriptor
Returns a BFile error code. */
int BFile_Close(int fd); int BFile_Close(int fd);
/* Get the size of an open file. */ /* BFile_Size(): Retrieve size of an open file
@fd Open file descriptor
Returns the file size in bytes, or a negative BFile error code*/
int BFile_Size(int fd); int BFile_Size(int fd);
/* BFile_Write(): Write data to an open file /* BFile_Write(): Write data to an open file
Second and third argument specify the data and length to write.
WARNING: The CASIOWIN fs is known to become inconsistent if an odd number of WARNING: The file systems has shown to become inconsistent if an odd number
bytes is written with BFile_Write(). Always keep it even! of bytes is written with BFile_Write(). Keep it even!
With CASIOWIN, returns the new file offset after writing (or an error code). @fd File descriptor open for writing
With Fugue, returns the amount of data written (or an error code). */ @data Data to write
@even_size Size to write (must be even, yes)
Returns a BFile error code. */
int BFile_Write(int fd, void const *data, int even_size); int BFile_Write(int fd, void const *data, int even_size);
/* BFile_Read(): Read data from an open file /* BFile_Read(): Read data from an open file
The second and third argument specify where to store and how much to read.
The extra argument [whence] specifies where data is read from in the style The location from where the data is read depends on [whence]:
of pread(2), and is supported with both filesystems.
* If [whence >= 0], it is taken as an absolute location within the file; * If [whence >= 0], it is taken as an absolute location within the file;
* If [whence == -1], BFile_Read() reads from the current position. * If [whence == -1], BFile_Read() reads from the current position.
With Fugue this function can read past end of file and return the requested @fd File descriptor open for reading
amount of bytes even when the file does not have enough data to read that @data Buffer of at least [size] bytes to store data to
much. It seems that extra bytes read as zeros. Reading past the end does @size Number of bytes to read
*not* extend the file size. @whence Starting position of the data to read in the file
Returns a BFile error code. */
With CASIOWIN, returns how much data can be read from the updated file
offset (ie. how many bytes until end of file), or an error code.
With Fugue, returns the amount of data read (or an error code). */
int BFile_Read(int handle, void *data, int size, int whence); int BFile_Read(int handle, void *data, int size, int whence);
/* BFile_Seek(): Seek to a relative or absolute position within an open file /* BFile_FindFirst(): Search a directory for file with matching name
Doesn't work on Main memory. Only four search handle can be opened, you need
With CASIOWIN, moves [offset] bytes relative to the current position, and to close them to be able to reuse them. Search is NOT case sensitive and *
returns how much data can be read from the new position. BFile_Seek(fd, 0) can be used as a wildcard.
combined with BFile_Size(fd) can be used to determine the current position.
With Fugue, moves to the absolute position [offset] within the file, and
returns the amount of allocated space following the new position (usually
larger than the amount of data until end-of-file because files are allocated
in units of 4096 bytes). There is no way to seek relative to the current
position unless the target is precomputed with BFile_GetPos(). */
int BFile_Seek(int fd, int offset);
/* BFile_GetPos(): Get the current position in an open file
This call does not exist in the CASIOWIN interface, so this function always
returns -1 on models with a CASIOWIN filesystem.
This call exists in Fugue, however some Fugue models have their syscall list
inherited from the CASIOWIN era and don't have an entry point for it (or if
there's one it's escape scrutiny so far).
* Prizm / Graph 90+E / fx-CG series: this function works as intended.
* Graph 35+E II / G-III series: the call is missing, returns -1.
For the latter models there is no easily reliable way of knowing the current
position within an open file! */
int BFile_GetPos(int fd);
//---
// Error codes
//---
#define BFile_EntryNotFound -1
#define BFile_IllegalParam -2
#define BFile_IllegalPath -3
#define BFile_DeviceFull -4
#define BFile_IllegalDevice -5
#define BFile_IllegalFilesystem -6
#define BFile_IllegalSystem -7
#define BFile_AccessDenied -8
#define BFile_AlreadyLocked -9
#define BFile_IllegalTaskID -10
#define BFile_PermissionError -11
#define BFile_EntryFull -12
#define BFile_AlreadyExists -13
#define BFile_ReadOnlyFile -14
#define BFile_IllegalFilter -15
#define BFile_EnumerateEnd -16
#define BFile_DeviceChanged -17
//#define BFile_NotRecordFile -18 // Not used
#define BFile_IllegalSeekPos -19
#define BFile_IllegalBlockFile -20
//#define BFile_NoSuchDevice -21 // Not used
//#define BFile_EndOfFile -22 // Not used
#define BFile_NotMountDevice -23
#define BFile_NotUnmountDevice -24
#define BFile_CannotLockSystem -25
#define BFile_RecordNotFound -26
//#define BFile_NotDualRecordFile -27 // Not used
#define BFile_NoAlarmSupport -28
#define BFile_CannotAddAlarm -29
#define BFile_FileFindUsed -30
#define BFile_DeviceError -31
#define BFile_SystemNotLocked -32
#define BFile_DeviceNotFound -33
#define BFile_FileTypeMismatch -34
#define BFile_NotEmpty -35
#define BFile_BrokenSystemData -36
#define BFile_MediaNotReady -37
#define BFile_TooManyAlarms -38
#define BFile_SameAlarmExists -39
#define BFile_AccessSwapArea -40
#define BFile_MultimediaCard -41
#define BFile_CopyProtection -42
#define BFile_IllegalFileData -43
//---
// Search API
//
// The search API is described below. It's somewhat unreliable, with unclear
// semantics and a history of breaking in seemingly reasonable programs. One
// day probably we'll know how to use it properly and reliably.
//
// Note: always close search handles or trouble will ensue (eg. add-in
// discovery failing).
//---
@search FONTCHARACTER file path to match
@shandle Search handle to pass to BFile_FindNext or BFile_FindClose
@founfile FONTCHARACTER found file path
@fileinfo Structure containing a lot of information on the found file
Returns 0 on success, -1 if no file found, or negative value on error. */
struct BFile_FileInfo struct BFile_FileInfo
{ {
uint16_t index; uint16_t index;
@ -242,72 +109,38 @@ struct BFile_FileInfo
/* Address of first fragment (do not use directly) */ /* Address of first fragment (do not use directly) */
void *address; void *address;
}; };
enum BFile_FileType
#define BFile_Type_Directory 0x0000 {
#define BFile_Type_File 0x0001 BFile_Type_Directory = 0x0000,
#define BFile_Type_Addin 0x0002 BFile_Type_File = 0x0001,
#define BFile_Type_Eact 0x0003 BFile_Type_Addin = 0x0002,
#define BFile_Type_Language 0x0004 BFile_Type_Eact = 0x0003,
#define BFile_Type_Bitmap 0x0005 BFile_Type_Language = 0x0004,
#define BFile_Type_MainMem 0x0006 BFile_Type_Bitmap = 0x0005,
#define BFile_Type_Temp 0x0007 BFile_Type_MainMem = 0x0006,
#define BFile_Type_Dot 0x0008 BFile_Type_Temp = 0x0007,
#define BFile_Type_DotDot 0x0009 BFile_Type_Dot = 0x0008,
#define BFile_Type_Volume 0x000a BFile_Type_DotDot = 0x0009,
#define BFile_Type_Archived 0x0041 BFile_Type_Volume = 0x000a,
BFile_Type_Archived = 0x0041,
/* BFile_FindFirst(): Search the storage memory for paths matching a pattern };
int BFile_FindFirst(uint16_t const *search, int *shandle, uint16_t *foundfile,
This if for the storage memory only. There are only four search handles;
make sure to close them, there is no automatic device to close them for you
even after the add-in exists.
Search is NOT case sensitive. '*' can be used as a wildcard, although it's
unclear whether more than one '*' is supported, whether '*' can match in a
directory name, whether multiple folders can be searched simultaneously, and
whether directories can be matched.
@pattern FONTCHARACTER glob pattern
@shandle Will receive search handle (to use in BFile_FindNext/FindClose)
@foundfile Will receive FONTCHARACTER path of matching entry
@fileinfo Will receive metadata of matching entry
On success, returns 0, stores the search handle in *shandle, and stores
information about the first match in foundfile and fileinfo. The negative
error code BFile_EntryNotFound should be interpreted as an empty result (ie.
no entry matched) rather than an error.
Returns 0 on success or a negative error code. BFile_EntryNotFound should be
interpreted as an empty result (ie. no file matched). */
int BFile_FindFirst(uint16_t const *pattern, int *shandle, uint16_t *foundfile,
struct BFile_FileInfo *fileinfo); struct BFile_FileInfo *fileinfo);
/* BFile_FindNext(): Continue a search /* BFile_FindNext(): Continue a search a directory for file with matching name
Continues the search for matches. The search handle is the value set in @shandle Search handle from BFile_FindFirst
*shandle in BFile_FindFirst(). As before, *foundfile receives the matching @founfile FONTCHARACTER found file path
entry's path and *fileinfo its metadata. @fileinfo Structure containing a lot of information on the found file
Returns 0 on success, -1 if end is reached, or negative value on error. */
Returns 0 on success. The negative error code BFile_EnumerateEnd should be
interpreted as the end of the search (ie. all matching files have been
returned) rather than an error. */
int BFile_FindNext(int shandle, uint16_t *foundfile, int BFile_FindNext(int shandle, uint16_t *foundfile,
struct BFile_FileInfo *fileinfo); struct BFile_FileInfo *fileinfo);
/* Close a search handle (with or without exhausting the matches). */ /* BFile_FindClose(): Close the specified search handle
@shandle Search handle from BFile_FindFirst
Return 0 on success or negative value on error. */
int BFile_FindClose(int shandle); int BFile_FindClose(int shandle);
//---
// Additional functions
//
// The following functions are implemented in gint using lower-level BFile
// functions. They add abstraction/convenience to the API, but can be pretty
// slow as they result in more BFile calls being made.
//---
/* BFile_Ext_Stat(): Stat an entry for type and size */
int BFile_Ext_Stat(uint16_t const *path, int *type, int *size);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -52,108 +52,11 @@ typedef struct
module; this address never changes. */ module; this address never changes. */
const clock_frequency_t *clock_freq(void); const clock_frequency_t *clock_freq(void);
/* cpg_compute_freq(): Compute the current clock frequency
This function updates the data structure returned by clock_freq() by
determining the current clock frequencies from the CPG. */
void cpg_compute_freq(void);
//--- //---
// Overclock // Overclock
//--- //---
/* The following enumerations define the clock speed settings supported by /* TODO: All overclock */
gint. These are always the settings from Ftune/Ptune, which are the most
widely tested and gint treats as the standard. */
enum {
/* Combinations of hardware settings that are none of Ftune's levels */
CLOCK_SPEED_UNKNOWN = 0,
/* Ftune's 5 default overclock levels. The main settings are listed
below, though many more are involved.
On SH3 fx-9860G-like:
F1: CPU @ 29 MHz [Default speed]
F2: CPU @ 58 MHz [Similar to G-III default]
F3: CPU @ 88 MHz
F4: CPU @ 118 MHz [Fastest CPU option]
F5: CPU @ 118 MHz [Reduced memory wait times]
On SH4 fx-9860G-like:
F1: CPU @ 29 MHz, BFC @ 29 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [Similar to G-III default]
F3: CPU @ 29 MHz, BFC @ 29 MHz [SH3 default]
F4: CPU @ 118 MHz, BFC @ 59 MHz
F5: CPU @ 236 MHz, BFC @ 118 MHz [Fastest option]
On G-III / Graph 35+E II:
F1: CPU @ 58 MHz, BFC @ 29 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [fx-CG 10/20 default]
F3: CPU @ 29 MHz, BFC @ 29 MHz [SH3 default]
F4: CPU @ 118 MHz, BFC @ 58 MHz
F5: CPU @ 235 MHz, BFC @ 58 MHz [Fastest option]
On fx-CG 10/20:
F1: CPU @ 58 MHz, BFC @ 29 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [Improved memory speed]
F3: CPU @ 118 MHz, BFC @ 58 MHz [Faster than F2]
F4: CPU @ 118 MHz, BFC @ 118 MHz [Fastest bus option]
F5: CPU @ 191 MHz, BFC @ 94 MHz [Fastest CPU option]
On fx-CG 50:
F1: CPU @ 116 MHz, BFC @ 58 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [fx-CG 10/20 default]
F3: CPU @ 94 MHz, BFC @ 47 MHz [Clearly slow: F2 < F3 < F1]
F4: CPU @ 232 MHz, BFC @ 58 MHz [Fastest CPU option]
F5: CPU @ 189 MHz, BFC @ 94 MHz [Fastest bus option] */
CLOCK_SPEED_F1 = 1,
CLOCK_SPEED_F2 = 2,
CLOCK_SPEED_F3 = 3,
CLOCK_SPEED_F4 = 4,
CLOCK_SPEED_F5 = 5,
/* The default clock speed is always Ftune's F1 */
CLOCK_SPEED_DEFAULT = CLOCK_SPEED_F1,
};
/* clock_get_speed(): Determine the current clock speed
This function compares the current hardware state with the settings for each
speed level and returns the current one. If the hardware state does not
correspond to any of Ftune's settings, CLOCK_SPEED_UNKNOWN is returned. */
int clock_get_speed(void);
/* clock_set_speed(): Set the current clock speed
This function sets the clock speed to the desired level. This is "the
overclock function", although depending on the model or settings it is also
the downclocking function.
The process of changing clock speeds is non-trivial, requires waiting for
the DMA to finish its work and slightly affects running timers. You should
avoid changing the clock speed constantly if not necessary. If this function
detects that the desired clock speed is already in use, it returns without
performing any change.
Currently the clock speed is not reset during a world switch nor when
leaving the add-in. */
void clock_set_speed(int speed);
/* If you want to faithfully save and restore the clock state while properly
handling clock speeds that are not Ftune/PTune's defaults, you can get a
full copy of the settings.
WARNING: Applying random settings with cpg_set_overclock_setting() might
damage your calculator! */
struct cpg_overclock_setting
{
uint32_t FLLFRQ, FRQCR;
uint32_t CS0BCR, CS2BCR, CS3BCR, CS5aBCR;
uint32_t CS0WCR, CS2WCR, CS3WCR, CS5aWCR;
};
/* Queries the clock setting from the hardware. */
void cpg_get_overclock_setting(struct cpg_overclock_setting *s);
/* Applies the specified overclock setting. */
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s);
//--- //---
// Sleep functions // Sleep functions

View file

@ -5,6 +5,8 @@
#ifndef GINT_CONFIG #ifndef GINT_CONFIG
#define GINT_CONFIG #define GINT_CONFIG
#include <gint/defs/types.h>
/* GINT_VERSION: Latest tag and number of additional commits /* GINT_VERSION: Latest tag and number of additional commits
"2.1.0" = Release 2.1.0 "2.1.0" = Release 2.1.0
"2.1.1-5" = 5 commits after release 2.1.1 */ "2.1.1-5" = 5 commits after release 2.1.1 */
@ -14,50 +16,10 @@
0x03f7c0a0 = Commit 3f7c0a0 */ 0x03f7c0a0 = Commit 3f7c0a0 */
#define GINT_HASH 0x@GINT_GIT_HASH@ #define GINT_HASH 0x@GINT_GIT_HASH@
/* GINT_HW_{FX,CG}: Identifies the type of hardware running the program. */ /* GINT_USER_VRAM: Selects whether to store VRAMs in the user stack (occupying
#if defined(FX9860G) 350k/512k of the area) or in the system stack (the default). (fx-CG 50) */
# define GINT_HW_FX 1
# define GINT_HW_CG 0
# define GINT_HW_CP 0
# define GINT_HW_SWITCH(FX,CG,CP) (FX)
#elif defined(FXCG50)
# define GINT_HW_FX 0
# define GINT_HW_CG 1
# define GINT_HW_CP 0
# define GINT_HW_SWITCH(FX,CG,CP) (CG)
#elif defined(FXCP)
# define GINT_HW_FX 0
# define GINT_HW_CG 0
# define GINT_HW_CP 1
# define GINT_HW_SWITCH(FX,CG,CP) (CP)
#endif
/* Shorthand to simplify definitions below. Won't be needed for long. */
#if defined(FX9860G_G3A)
# define GINT_FX9860G_G3A 1
#else
# define GINT_FX9860G_G3A 0
#endif
/* GINT_OS_{FX,CG}: Identifies the type of OS API we're assuming. Currently I
see no reason this would be different from hardware, but who knows. */
#define GINT_OS_FX GINT_HW_FX
#define GINT_OS_CG GINT_HW_CG
#define GINT_OS_CP GINT_HW_CP
#define GINT_OS_SWITCH GINT_HW_SWITCH
/* GINT_NO_OS_STACK: Disables using a chunk of the OS stack as a heap. The top
section covering 355/512 ko is otherwise used. (fx-CG 50) */
#cmakedefine GINT_NO_OS_STACK
/* GINT_USER_VRAM: Selects whether to store VRAMs in the user stack or in the
OS stack. Deprecated, now controlled by GINT_NO_OS_STACK. (fx-CG 50) */
#cmakedefine GINT_USER_VRAM #cmakedefine GINT_USER_VRAM
#ifdef GINT_USER_VRAM
# define GINT_NO_OS_STACK
#endif
/* GINT_STATIC_GRAY: Selects whether additional gray VRAMs are allocated /* GINT_STATIC_GRAY: Selects whether additional gray VRAMs are allocated
statically or in the system heap (fx-9860G) */ statically or in the system heap (fx-9860G) */
#cmakedefine GINT_STATIC_GRAY #cmakedefine GINT_STATIC_GRAY
@ -68,16 +30,4 @@
enabled or disabled at runtime. */ enabled or disabled at runtime. */
#cmakedefine GINT_KMALLOC_DEBUG #cmakedefine GINT_KMALLOC_DEBUG
/* GINT_USB_DEBUG: Selects whether USB debug functions are enabled */
#cmakedefine GINT_USB_DEBUG
/* GINT_RENDER_DMODE: Selects whether the dmode override is available on
rendering functions. */
#define GINT_RENDER_DMODE (GINT_HW_FX || GINT_FX9860G_G3A)
/* GINT_RENDER_{MONO,RGB}: Enable the mono/rgb rendering API.
Currently these are exclusive. */
#define GINT_RENDER_MONO (GINT_HW_FX || GINT_FX9860G_G3A)
#define GINT_RENDER_RGB ((GINT_HW_CG || GINT_HW_CP) && !GINT_FX9860G_G3A)
#endif /* GINT_CONFIG */ #endif /* GINT_CONFIG */

View file

@ -10,13 +10,12 @@
/* Objects from the gint's uninitialized BSS section */ /* Objects from the gint's uninitialized BSS section */
#define GBSS __attribute__((section(".gint.bss"))) #define GBSS __attribute__((section(".gint.bss")))
/* Additional sections that are only needed on SH3 */ /* Additional sections that are only needed on SH3 */
#define GRODATA3 __attribute__((section(".gint.rodata.sh3")))
#define GDATA3 __attribute__((section(".gint.data.sh3"))) #define GDATA3 __attribute__((section(".gint.data.sh3")))
#define GBSS3 __attribute__((section(".gint.bss.sh3"))) #define GBSS3 __attribute__((section(".gint.bss.sh3")))
/* Objects for the ILRAM, XRAM and YRAM regions */ /* Objects for the ILRAM, XRAM and YRAM regions */
#define GILRAM __attribute__((section(".ilram"))) #define GILRAM __attribute__((section(".ilram")))
#define GXRAM __attribute__((section(".xyram"))) #define GXRAM __attribute__((section(".xram")))
#define GYRAM __attribute__((section(".xyram"))) #define GYRAM __attribute__((section(".yram")))
/* Unused parameters or variables */ /* Unused parameters or variables */
#define GUNUSED __attribute__((unused)) #define GUNUSED __attribute__((unused))
@ -34,9 +33,6 @@
/* Transparent unions */ /* Transparent unions */
#define GTRANSPARENT __attribute__((transparent_union)) #define GTRANSPARENT __attribute__((transparent_union))
/* Functions and globals that are visible through whole-program optimization */
#define GVISIBLE __attribute__((externally_visible))
/* Weak symbols */ /* Weak symbols */
#define GWEAK __attribute__((weak)) #define GWEAK __attribute__((weak))

View file

@ -144,19 +144,6 @@ typedef struct {
/* GINT_CALL_NULL: Empty function call */ /* GINT_CALL_NULL: Empty function call */
#define GINT_CALL_NULL ((gint_call_t){ .function = NULL, .args = {} }) #define GINT_CALL_NULL ((gint_call_t){ .function = NULL, .args = {} })
/* GINT_CALL_FLAG(): Build an indirect call to an odd address
This is used as a workaround to have an extra flag in gint_call_t structures
without altering their size and exploits the fact that SuperH code has to be
aligned on even addresses.
It is up to the caller to notice the presence of the flag and realign the
address properly.
This flag is currently only checked by gint_inth_callback to specify that the
called function will take the interrupt context as its first argument. */
#define GINT_CALL_FLAG(func, ...) \
GINT_CALL((void*)((uint32_t)(func) | 1), __VA_ARGS__)
/* gint_call(): Perform an indirect call */ /* gint_call(): Perform an indirect call */
static GINLINE int gint_call(gint_call_t cb) static GINLINE int gint_call(gint_call_t cb)
{ {
@ -166,8 +153,7 @@ static GINLINE int gint_call(gint_call_t cb)
int (*f)(int r4, int r5, int r6, int r7) = cb.function; int (*f)(int r4, int r5, int r6, int r7) = cb.function;
#endif #endif
gint_call_arg_t *args = cb.args; return f(cb.args[0].i, cb.args[1].i, cb.args[2].i, cb.args[3].i);
return f ? f(args[0].i, args[1].i, args[2].i, args[3].i) : -1;
} }
//--- //---

View file

@ -1,36 +0,0 @@
//---
// gint:defs:timeout - RTC-based timeouts
//
// This header provides an interface for simplistic timers used for timeout
// waiting. Currently they are based on the RTC with a resolution of 1/128 s.
//---
#ifndef GINT_DEFS_TIMEOUT
#define GINT_DEFS_TIMEOUT
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
#include <stdbool.h>
/* Object holding information about a timeout (specifically, when it expires).
TODO: timeout: consider using a struct timespec with clock_gettime()? */
typedef clock_t timeout_t;
static inline timeout_t timeout_make_ms(int ms)
{
return clock() + (int64_t)ms * CLOCKS_PER_SEC / 1000;
}
static inline bool timeout_elapsed(timeout_t const *t)
{
return t && clock() >= *t;
}
#ifdef __cplusplus
}
#endif
#endif /* GINT_DEFS_TIMEOUT */

View file

@ -15,26 +15,10 @@
#include <stdbool.h> #include <stdbool.h>
/* Common system types: ssize_t, off_t, etc. */ /* Common system types: ssize_t, off_t, etc. */
#include <sys/types.h> #include <sys/types.h>
/* For va_list */
#include <stdarg.h>
/* Fixed-width types for bit fields are quite meaningless */ /* Fixed-width types for bit fields are quite meaningless */
typedef unsigned int uint; typedef unsigned int uint;
//---
// Short types
//---
typedef unsigned int uint;
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
//--- //---
// Structure elements // Structure elements
//---- //----

View file

@ -14,11 +14,6 @@
#define GAUTOTYPE __auto_type #define GAUTOTYPE __auto_type
#endif #endif
#ifdef __cplusplus
#include <algorithm>
using std::min;
using std::max;
#else
/* min(), max() (without double evaluation) */ /* min(), max() (without double evaluation) */
#define min(a, b) ({ \ #define min(a, b) ({ \
GAUTOTYPE _a = (a); \ GAUTOTYPE _a = (a); \
@ -30,7 +25,6 @@ using std::max;
GAUTOTYPE _b = (b); \ GAUTOTYPE _b = (b); \
_a > _b ? _a : _b; \ _a > _b ? _a : _b; \
}) })
#endif
/* sgn() (without double evaluation) */ /* sgn() (without double evaluation) */
#define sgn(s) ({ \ #define sgn(s) ({ \

View file

@ -1,37 +1,27 @@
//--- //---
// gint:display-cg - fx-CG 50 rendering functions // gint:display-cg - fxcg50 rendering functions
// //
// This module covers rendering functions specific to the fx-CG 50. In addition // This module covers all 16-bit opaque rendering functions. For
// to triple-buffering management, this mainly includes image manipulation // gamma-related functions, color composition, check out a color library.
// tools as well as the very versatile dimage_effect() and dsubimage_effect()
// functions that support high-performance image rendering with a number of
// geometric and color effects.
// //
// The fx-CG OS restricts the display to a 384x216 rectangle rougly around the // All the functions in this module work on a 396x224 resolution - gint
// center, leaving margins on three sides. However, gint configures the display // lets you use the full surface!
// to use the full 396x224 surface!
//--- //---
#ifndef GINT_DISPLAY_CG #ifndef GINT_DISPLAY_CG
#define GINT_DISPLAY_CG #define GINT_DISPLAY_CG
#if GINT_RENDER_RGB #ifdef FXCG50
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#include <gint/defs/types.h> #include <gint/defs/types.h>
#include <gint/image.h>
/* Dimensions of the VRAM */ /* Dimensions of the VRAM */
#if GINT_HW_CG
#define DWIDTH 396 #define DWIDTH 396
#define DHEIGHT 224 #define DHEIGHT 224
#elif GINT_HW_CP
#define DWIDTH 320
#define DHEIGHT 528
#endif
/* gint VRAM address. This value must always point to a 32-aligned buffer of /* gint VRAM address. This value must always point to a 32-aligned buffer of
size 177408. Any function can use it freely to perform rendering or store size 177408. Any function can use it freely to perform rendering or store
@ -67,81 +57,80 @@ enum {
green is not used). */ green is not used). */
#define C_RGB(r,g,b) (((r) << 11) | ((g) << 6) | (b)) #define C_RGB(r,g,b) (((r) << 11) | ((g) << 6) | (b))
/* See <gint/image.h> for the details on image manipulation. */ //---
typedef image_t bopti_image_t; // Image rendering (bopti)
//---
/* bopti_image_t: Image files encoded for bopti
This format is created by the fxSDK's [fxconv] tool from standard images. */
typedef struct
{
/* Color profile (type of palette), could be extended into a bit field
later on */
uint16_t profile;
/* Color code assigned to transparent pixels (unused in 16-bit) */
uint16_t alpha;
/* Full width and height, in pixels */
uint16_t width;
uint16_t height;
/* Color palette:
* 16-bit and 16-bit alpha: none
* 8-bit: 256 colors (total 512 bytes)
* 4-bit: 16 colors (total 32 bytes)
Then raw pixel data in row-major order. */
uint16_t data[];
} GPACKED(4) bopti_image_t;
/* Old alias to image_t, now deprecated because of libimg */
typedef bopti_image_t image_t __attribute__((deprecated(
"image_t has been renamed to bopti_image_t")));
//--- //---
// Video RAM management // Video RAM management
//--- //---
/* dsetvram(): Control video RAM address and triple buffering /* dsetvram() - Control video RAM address and triple buffering
Normal rendering under gint uses double-buffering: there is one image Normal rendering under gint uses double-buffering: there is one image
displayed on the screen and one in memory, in a region called the video RAM displayed on the screen and one in memory, in a region called the video RAM
(VRAM). The application draws frames in the VRAM then sends them to the (VRAM). The application draws frames in the VRAM then sends them to the
screen only when they are finished, using dupdate(). screen only when they are finished, using dupdate().
On fx-CG, sending frames with dupdate() is a common bottleneck because it On fxcg50, the performance bottleneck is almost always the graphical
takes about 11 ms. Fortunately, while the DMA is sending the frame to the rendering (especially in games) because the high amount of data, 173 kB per
display, the CPU is available to do work in parallel. This function sets up frame in full-resolution, makes graphics manipulation computationally
triple buffering (ie. a second VRAM) so that the CPU can start working on expensive. The transfer also takes about 10 ms in itself.
the next frame while the DMA is sending the current one.
However, experience shows minimal performance improvements, because writing Since gint transfers data to the screen using the DMA, it is possible to run
to main RAM does not parallelize with DMA transfers. Since gint 2.8, this the application while the finished frame is being transferred. However,
is no longer the default, and the memory for the extra VRAM is instead writing to the VRAM during this period will cause display artifacts since
available via malloc(). the VRAM it is still being read by the DMA.
VRAMs must be contiguous, 32-aligned, (2*396*224)-byte buffers with 32 bytes The solution to this is to use triple-buffering with the display and two
of extra data on each side (ie. 32 bytes into a 32-aligned buffer of size VRAMs that are alternately being written to while the other is being
177472). transferred. The VRAM switching is handled by dupdate() and is activated
whenever two VRAMs are configured.
By default gint uses triple buffering with two VRAMs in the system stack.
VRAMs must be contiguous, 32-aligned, (2*396*224)-byte buffers.
@main Main VRAM area, used alone if [secondary] is NULL @main Main VRAM area, used alone if [secondary] is NULL
@secondary Additional VRAM area, enables triple buffering if non-NULL */ @secondary Additional VRAM area, enables triple buffering if non-NULL */
// TODO: In gint 3 the triple buffering mechanism will be removed. Applications
// that want to change VRAMs in-between every frame will be able to do so by
// talking directly to the video interface to set VRAM, and wrapping dupdate.
// Basically it will just no longer be handled by gint itself.
void dsetvram(uint16_t *main, uint16_t *secondary); void dsetvram(uint16_t *main, uint16_t *secondary);
/* dgetvram() - Get VRAM addresses /* dgetvram() - Get VRAM addresses
Returns the VRAM buffer addresses used to render on fx-CG 50. */ Returns the VRAM buffer addresses used to render on fx-CG 50. */
void dgetvram(uint16_t **main, uint16_t **secondary); void dgetvram(uint16_t **main, uint16_t **secondary);
//---
// VRAM backup
// On the fx-CP gint backs up the VRAM when loading and restores it when
// leaving. While this is a transparent mechanism, the following parts of the
// implementation are exposed at the internal API level.
//---
/* Encode the VRAM contents between [in_start] and [in_end] at [output]. While
[*in_end] is excluded from the encoding, it will be modified temporarily to
serve as bounds check, so it must not be accessed asynchronously during the
encoding. The size of the output is not known but is at most the distance
between in_end and in_start in bytes. Setting output = in_start to compress
in-place is supported. Returns the end pointer after encoding. */
uint8_t *gint_vrambackup_encode(
uint8_t *output, uint16_t *in_start, uint16_t *in_end);
/* Predefine palette based on the GUI at the Hollyhock loading screen. Contains
109 entries plus a 110th dummy entry used internally as bounds check. */
extern uint16_t gint_vrambackup_palette[110];
/* Get the pointer to the encoded VRAM backup created at load time. If [size]
is not NULL, sets the size in [*size]. The pointer is heap allocated and
remains owned by gint. */
void *gint_vrambackup_get(int *size);
/* Decode the load-time VRAM backup back to VRAM. */
void gint_vrambackup_show(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* GINT_RENDER_RGB */ #endif /* FXCG50 */
#endif /* GINT_DISPLAY_CG */ #endif /* GINT_DISPLAY_CG */

View file

@ -9,7 +9,7 @@
#ifndef GINT_DISPLAY_FX #ifndef GINT_DISPLAY_FX
#define GINT_DISPLAY_FX #define GINT_DISPLAY_FX
#if GINT_RENDER_MONO #ifdef FX9860G
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -83,33 +83,14 @@ typedef struct
uint height :12; uint height :12;
/* Raw layer data */ /* Raw layer data */
uint8_t *data; uint8_t data[];
} GPACKED(4) bopti_image_t; } GPACKED(4) bopti_image_t;
/* Image formats ("profiles") */
enum {
/* MONO: black/white, 1 layer (bw) */
IMAGE_MONO = 0,
/* MONO_ALPHA: black/white/transparent, 2 layers (alpha+bw) */
IMAGE_MONO_ALPHA = 1,
/* GRAY: black/dark/light/white, 2 layers (light+dark) */
IMAGE_GRAY = 2,
/* GRAY_ALPHA: black/dark/light/white/transparent, 3 layres
(alpha+light+dark) */
IMAGE_GRAY_ALPHA = 3,
};
/* Number of layers in the image. */
GINLINE static int image_layer_count(int profile)
{
return profile + (profile <= 1);
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* GINT_RENDER_MONO */ #endif /* FX9860G */
#endif /* GINT_DISPLAY_FX */ #endif /* GINT_DISPLAY_FX */

View file

@ -15,19 +15,20 @@ extern "C" {
#include <gint/defs/types.h> #include <gint/defs/types.h>
#include <gint/defs/call.h> #include <gint/defs/call.h>
#include <gint/config.h>
/* Platform-specific functions include VRAM management and the definition of /* Platform-specific functions include VRAM management and the definition of
the color_t type. */ the color_t type. */
#if GINT_RENDER_MONO #ifdef FX9860G
#include <gint/display-fx.h> #include <gint/display-fx.h>
#endif #endif
#if GINT_RENDER_RGB #ifdef FXCG50
#include <gint/display-cg.h> #include <gint/display-cg.h>
#endif #endif
/* TODO: dinfo() or similar */
//--- //---
// Video RAM management // Video RAM management
//--- //---
@ -81,31 +82,6 @@ void dupdate_set_hook(gint_call_t function);
/* dupdate_get_hook(): Get a copy of the dupdate() hook */ /* dupdate_get_hook(): Get a copy of the dupdate() hook */
gint_call_t dupdate_get_hook(void); gint_call_t dupdate_get_hook(void);
//---
// Rendering mode control
//---
/* dmode: Rendering mode settings
This structure indicates the current window settings. Rendering is limited
to the rectangle spanning from (left,top) included, to (right,bottom)
excluded. The default is from (0,0) to (DWIDTH,DHEIGHT). */
struct dwindow {
int left, top;
int right, bottom;
};
extern struct dwindow dwindow;
/* dwindow_set(): Set the rendering window
This function changes [dwindow] settings to limit rendering to a sub-surface
of the VRAM. Note that this doesn't change the coordinate system; the pixel
at (DWIDTH/2, DHEIGHT/2) is always in the middle of the screen regardless of
the window setting. However, it might be masked out by the window.
Returns the old dwindow settings (if it needs to be restored later). */
struct dwindow dwindow_set(struct dwindow new_mode);
//--- //---
// Area rendering functions // Area rendering functions
//--- //---
@ -175,11 +151,6 @@ void drect_border(int x1, int y1, int x2, int y2,
*: When the gray engine is on, see dgray(). */ *: When the gray engine is on, see dgray(). */
void dpixel(int x, int y, int color); void dpixel(int x, int y, int color);
/* dgetpixel(): Get a pixel's color
Returns the current color of any pixel in the VRAM. This function ignores
the rendering window. Returns -1 if (x,y) is out of bounds. */
int dgetpixel(int x, int y);
/* dline(): Render a straight line /* dline(): Render a straight line
This function draws a line using a Bresenham-style algorithm. Please note This function draws a line using a Bresenham-style algorithm. Please note
@ -213,53 +184,6 @@ void dhline(int y, int color);
@color Line color (same values as dline() are allowed) */ @color Line color (same values as dline() are allowed) */
void dvline(int x, int color); void dvline(int x, int color);
//---
// Other geometric shapes
//---
/* dcircle(): Circle with border
This function renders a circle defined by its middle point (xm, ym) and a
radius r. The border and fill can be set separately. This function uses
Bresenham's algorithm and only renders circles of odd diameter; if you want
an even diameter, use dellipse() with a bounding box.
@xm @ym Coordinates of center
@r Radius (integer, >= 0)
@fill_color Color of the disc inside the circle, C_NONE to disable
@border_color Color of the circle itself, C_NONE to disable */
void dcircle(int xm, int ym, int r, int fill_color, int border_color);
/* dellipse(): Ellipse with border
This function renders a non-rotated ellipse, defined by its bounding box.
The border and fill can be set separately. The border is as 1-pixel thin
border given by Bresenham's algorithm.
To render an ellipse from its center coordinates (x,y) and semi-major/minor
axes a/b, use dellipse(x-a, y-b, x+a, y+b, fill_color, border_color).
@x1 @y1 @x2 @y2 Ellipse's bounding box (both bounds included, like drect())
@fill_color Color of the surface inside the ellipse, C_NONE to disable
@border_color Color of the ellipse itself, C_NONE to disable */
void dellipse(int x1, int y1, int x2, int y2, int fill_color,
int border_color);
/* dpoly(): Render an arbitrary polygon
Renders the polygon defined by vertices (x[i], y[i]) for 0 < i < N. A fill
color and border color can be specified separately. For filling, N must be
at least 3. For border, N must be at least 2.
Note: this is a fairly slow function, not designed for rendering triangles
in 3D games. There are faster methods for that.
@x @y Arrays of vertex coordinates
@N Number of vertices (length of x and y)
@fill_color Color of the polygon's interior, C_NONE to disable
@border_color Color of the polygon's border, C_NONE to disable */
void dpoly(int const *x, int const *y, int N, int fill_color, int border_color);
//--- //---
// Text rendering (topti) // Text rendering (topti)
//--- //---
@ -461,28 +385,6 @@ void dprint_opt(int x, int y, int fg, int bg, int halign, int valign,
Calls dprint_opt() with bg=C_NONE, halign=DTEXT_LEFT and valign=DTEXT_TOP */ Calls dprint_opt() with bg=C_NONE, halign=DTEXT_LEFT and valign=DTEXT_TOP */
void dprint(int x, int y, int fg, char const *format, ...); void dprint(int x, int y, int fg, char const *format, ...);
//---
// Text rendering utilities
//---
/* dfont_glyph_index(): Obtain the glyph index of a Unicode code point
Returns the position of code_point in the character table of the given font,
or -1 if code_point is not part of that set. */
int dfont_glyph_index(font_t const *font, uint32_t code_point);
/* topti_glyph_offset(): Use a font index to find the location of a glyph
The provided glyph value (usuall obtained by dfont_glyph_index()) must be
nonnegative. Returns the offset the this glyph's data in the font's data
array. When using a proportional font, the size array is ignored. */
int dfont_glyph_offset(font_t const *font, uint glyph_number);
/* dtext_utf8_next(): Read the next UTF-8 code point of a string
Returns the next code point and advances the string. Returns 0 (NUL) at the
end of the string. */
uint32_t dtext_utf8_next(uint8_t const **str_pointer);
//--- //---
// Image rendering (bopti) // Image rendering (bopti)
//--- //---

View file

@ -267,7 +267,7 @@ typedef void **gint_world_t;
the section name and the linker then sorts by name. If your driver has a the section name and the linker then sorts by name. If your driver has a
level lower than 10, you must add a leading 0. */ level lower than 10, you must add a leading 0. */
#define GINT_DECLARE_DRIVER(level, name) \ #define GINT_DECLARE_DRIVER(level, name) \
GSECTION(".gint.drivers." #level) GVISIBLE extern gint_driver_t name; GSECTION(".gint.drivers." #level) extern gint_driver_t name;
//--- //---
// Internal driver control // Internal driver control

View file

@ -1,293 +0,0 @@
//---
// gint:usb:asyncio - Asynchronous I/O common definitions
//---
#ifndef GINT_USB_ASYNCIO
#define GINT_USB_ASYNCIO
#include <gint/defs/types.h>
#include <gint/defs/call.h>
/* Data tracking the progress of a multi-part multi-round async I/O operation.
* Writes are multi-part because they are constructed over several calls to
write(2) followed by a "commit" with fynsc(2). They are additionally
multi-round because each call to write(2) requires mutiple rounds of
hardware communication when the hardware buffer is smaller than the data.
* Reads are multi-part because each transaction from the host requires
several calls to read(2) ("user segments") if the user's buffer is shorter
than the full transaction. In addition, reads are multi-round because a
single read(2) to a user buffer takes multiple rounds of hardware
communication ("hardware segments") whenever the user buffer is larger
than the hardware buffer.
The process of performing such an I/O operation, as tracked by this
structure and use throughout gint, is as follows.
## Writes
WRITING ---------------------.
^ | | HW buffer
Start writing | | Not full | full: start
| | | transmission
write(2) | v v
--> IDLE ------------------> PENDING <------------- FLYING-WRITE
^ ^ | DONE interrupt
| DONE write(2) | |
| interrupt | |
| | | Data exhausted
| fsync(2): start | v
FLYING-SYNC <------------ IN-PROGRESS
transmission
Initially the operation is in the IDLE state. When a write(2) is issued, it
interacts with hardware then transitions to the IN-PROGRESS state, where it
remains for any subsequent write(2). A fsync(2) will properly commit data to
the hardware, finish the operation and return to the IDLE state.
The FLYING-WRITE and FLYING-SYNC states refer to waiting periods, after
issuing hardware commands, during which hardware communicates. Usually an
interrupt signals when hardware is ready to resume work.
Note that in a series of write(2), hardware is only instructed to send data
once the hardware buffer is full. Therefore, a write(2) might transition
directly from IDLE or IN-PROGRESS, to PENDING, to IN-PROGRESS, without
actually communicating with the outside world.
An asynchronous write(2) might return to the caller as soon as writing is
finished even if the operation is left in the FLYING-WRITE state, and it may
even return while the operation is in the WRITING state if the DMA is used.
The invariants and meaning for each state are as follow:
State(s) Characterization Description
============================================================================
IDLE type == ASYNCIO_NONE No I/O operation
----------------------------------------------------------------------------
PENDING data_w && round_size == 0 Ready to write pending data
----------------------------------------------------------------------------
WRITING, round_size > 0 CPU/DMA write to HW in progress
FLYING-WRITE HW transmission in progress
----------------------------------------------------------------------------
IN-PROGRESS !data_w && type == WRITE Waiting for write(2) or fsync(2)
----------------------------------------------------------------------------
FLYING-SYNC type == ASYNCIO_SYNC HW commit in progress
============================================================================
The series of asyncio_op function calls for a write is as follows:
transaction ::= write* fsync
write ::= asyncio_op_start_write round+ asyncio_op_finish_write
round ::= asyncio_op_start_write_round asyncio_op_finish_write_round
fsync ::= asyncio_op_start_sync asyncio_op_finish_sync
Each write(2) (with a single user-provided buffer) is split into multiple
rounds that each fill the (small) hardware buffer. More writes can follow
until an fynsc(2) commits the pipe.
## Reads
IN interrupt
--> IDLE-EMPTY --------------> IDLE-READY
| \ read(2) | ^
read(2) | \ Transaction | | User buffer
| \ exhausted* | | filled
| '----<----------. | |
| \ | |
| IN interrupt \ | |
v .--------->--------. \ v | .---. Read from
WAITING READING v hardware
'---------<--------' '---'
HW buffer exhausted with
user buffer not full
On this diagram, the right side indicates the presence of data to read from
hardware while the bottom side indicates a read(2) request by the user.
Notice the diagonal arrow back to IDLE-EMPTY insteaf of WAITING, which
highlights that read(2) will always return at the end of a transaction even
if the user-provided buffer is not full (to avoid waiting).
A read(2) request (a "user segment") might consume several full hardware
buffers ("hardware segments") if the user buffer is large, thus looping
repeatedly between WAITING and READING. Conversely, each hardware segment
might fulfill many read(2) requests if the user buffer is small, thus
looping between IDLE-READY and READING.
* Note that if the transaction finishes right as the user buffer fills up,
we return to IDLE-READY and the next call to read(2) will successfully read
0 bytes and transition back to IDLE-EMPTY. This allows the user to read the
entire transaction by reading data until they get fewer bytes than requested
without running the risk of blocking on the next transaction.
The invariants and meaning for each state are as follow. The right side
(presence of a hardware segment) is indicated by `buffer_used >= 0`, while
the bottom side (presence of a read(2) request) is indicated by `size > 0`.
As an exception, IDLE-EMPTY uses `buffer_used == 0` so that the default
zero-initialization of transfer data is sufficient.
State Invariant characterization Description
============================================================================
IDLE-EMPTY type == ASYNCIO_NONE No I/O operation, the pipe is
&& buffer_used == 0 idle with no request and no
&& size == 0 hardware segment (but we might
&& round_size == 0 still be mid-transaction)
----------------------------------------------------------------------------
IDLE-READY type == ASYNCIO_READ There is a hardware segment not
&& buffer_used >= 0 marked as complete, but no
&& size == 0 read(2) request to consume it
&& round_size == 0
----------------------------------------------------------------------------
WAITING type == ASYNCIO_READ There is a read(2) request but
&& buffer_used < 0 no hardware segment, and either
&& size > 0 the request is new or the
&& round_size == 0 transaction isn't exhausted
----------------------------------------------------------------------------
READING type == ASYNCIO_READ A read round in progress and
&& buffer_used >= 0 either the read(2) request or
&& size > 0 hardware segment will be
&& round_size > 0 exhausted when it ends
============================================================================
The series of asyncio_op function calls for a read is a bit more complicated
because transactions are divided into two non-comparable sequences of
segments: one for packets received by the hardware buffer (on BRDY), one for
the data being copied to user buffers (on read(2)).
|<------ Transaction from the host (no size limit) ------>|
v BRDY v BRDY v BRDY v BRDY v BRDY
+-----------+-----------+-----------+-----------+---------+
| HW buffer | HW buffer | HW buffer | HW buffer | (short) | HW segments
+-----------+------+----+-----------+-----------+---------+
| R1 | R2 | R3 | R4 | R5 | R6 | Read rounds
+-----------+------+----+-----------+-----------+---------+
| User buffer #1 | User buffer #2 | (short) | User segments
+------------------+----------------------------+---------+
^ read(2) ^ read(2) ^ read(2)
Reads rounds are exactly the intersections between hardware segments and
read(2) user segments.
States can be checked and transitioned with the API functions below. */
enum { ASYNCIO_NONE, ASYNCIO_READ, ASYNCIO_WRITE, ASYNCIO_SYNC };
typedef volatile struct
{
/* Type of I/O operation (NONE/WRITE/SYNC/READ) */
uint8_t type;
/* Whether the DMA should be used for hardware access */
bool dma :1;
/* For reading pipes, whether the transaction is expected to continue with
another hardware segment after the current one */
bool cont_r :1;
/* For reading pipes, interrupt flag signaling an incoming hardware segment
not yet added to the operation */
bool interrupt_r :1;
/* For reading pipes, whether the current read call should close the
current hardware segment if all the data is read even if the read call
is not partial */
bool autoclose_r :1;
/* Hardware resource being used for access (meaning depends on hardware).
Usually, this is assigned for the duration of hardware transaction.
This value is user-managed and not modified by asyncio_op functions. */
uint8_t controller;
/* Number of bytes in short buffer (0..3) */
uint8_t shbuf_size;
/* Short buffer */
uint32_t shbuf;
/* Size of data currently in the hardware buffer */
int16_t buffer_used;
/* Size of data being read/written in the current round (which may itself
be asynchronous if it's using the DMA) */
uint16_t round_size;
union {
/* Address of data to transfer, incremented gradually [write] */
void const *data_w;
/* Address of buffer to store data to, incremented gradually [read] */
void *data_r;
};
/* Size of data left to transfer to satisfy the complete request */
int size;
/* For reading operations, pointer to total amount of transferred data */
int *realized_size_r;
/* Callback at the end of the current write, final commit, or read */
gint_call_t callback;
} asyncio_op_t;
//---
// Initialization and query functions
//---
/* asyncio_op_clear(): Initialize/clear the storage for an I/O operation */
void asyncio_op_clear(asyncio_op_t *op);
/* asyncio_op_busy(): Check whether the transfer is busy for syscalls
This function checks whether the transfer is in a state where the CPU is
busy wrt. starting a new syscall, ie. read(2), write(2) or fsync(2). Returns
true if the CPU is busy and the call has to wait, false if the call can
proceed immediately. */
bool asyncio_op_busy(asyncio_op_t const *op);
//---
// I/O functions
//---
/* Start/finish a write(2) call. */
void asyncio_op_start_write(asyncio_op_t *op,
void const *data, size_t size, bool use_dma, gint_call_t const *callback);
void asyncio_op_finish_write(asyncio_op_t *op);
/* Start/finish a single-block write to hardware. */
void asyncio_op_start_write_round(asyncio_op_t *op, size_t size);
void asyncio_op_finish_write_round(asyncio_op_t *op);
/* Start an fsync(2) operation (after one or more writes) and finish it. */
void asyncio_op_start_sync(asyncio_op_t *op, gint_call_t const *callback);
void asyncio_op_finish_sync(asyncio_op_t *op);
/* Start a read(2) call. The call will finish automatically when the final
round finishes. If `autoclose` is set, the current hardware segment will
be marked as completed if the round reads it entirely, even if the request
is fulfilled. */
void asyncio_op_start_read(asyncio_op_t *op, void *data, size_t size,
bool use_dma, int *realized_size, bool autoclose,
gint_call_t const *callback);
/* Start a hardware segment. `cont` should be true if there will be another
segment in the same transaction. The segment will finish automatically when
it is completely consumed by a read round. */
void asyncio_op_start_read_hwseg(asyncio_op_t *op, size_t size, bool cont);
bool asyncio_op_has_read_call(asyncio_op_t const *op);
bool asyncio_op_has_read_hwseg(asyncio_op_t const *op);
/* Start a single-block read from hardware. The requested size is automatically
t->size, however the round may of course be smaller depending on how much
data is available. Returns the round size. */
int asyncio_op_start_read_round(asyncio_op_t *op);
enum {
ASYNCIO_HWSEG_EXHAUSTED = 0x01,
ASYNCIO_REQUEST_FINISHED = 0x02,
ASYNCIO_TRANSACTION_EXHAUSTED = 0x04,
};
/* Finish a single-block read from hardware. This function also finishes the
current hardware segment and read call if appropriate, *except* that it
doesn't invoke the read(2) callback. You should make a copy of it before
calling and invoke it manually after. Returns a combination of the above
flags indicating what finished along with the round. */
int asyncio_op_finish_read_round(asyncio_op_t *op);
/* Cancel a read call. This keeps the hardware segment part intact. */
void asyncio_op_cancel_read(asyncio_op_t *op);
#endif /* GINT_USB_ASYNCIO */

View file

@ -10,11 +10,23 @@ extern "C" {
#endif #endif
#include <gint/keyboard.h> #include <gint/keyboard.h>
#include <stdbool.h>
/* Size of the buffer event queue */ /* Size of the buffer event queue */
#define KEYBOARD_QUEUE_SIZE 32 #define KEYBOARD_QUEUE_SIZE 32
/* keydev_event_t: Change of state in a full row */
typedef struct {
/* Device time for the event */
uint16_t time;
/* Keys that changed state */
uint8_t changed;
/* Row number */
uint8_t row :4;
/* Type of change, either KEYEV_DOWN or KEYEV_UP */
uint8_t kind :4;
} keydev_event_t;
/* Event transforms /* Event transforms
Every keyboard input device has a built-in event stream editor that can Every keyboard input device has a built-in event stream editor that can
@ -72,26 +84,14 @@ extern "C" {
enum { enum {
/* Delayed SHIFT: Pressing then immediately releasing SHIFT when the /* Delayed SHIFT: Pressing then immediately releasing SHIFT when the
keyboard is idle applies SHIFT to the next repeat streak. */ keyboard is idle applies SHIFT to the next repeat streak. */
KEYDEV_TR_DELAYED_SHIFT = 0x01, /* = GETKEY_MOD_SHIFT */ KEYDEV_TR_DELAYED_SHIFT = 0x01,
/* Delayed ALPHA: Idem with the ALPHA key */ /* Delayed ALPHA: Idem with the ALPHA key */
KEYDEV_TR_DELAYED_ALPHA = 0x02, /* = GETKEY_MOD_ALPHA */ KEYDEV_TR_DELAYED_ALPHA = 0x02,
/* Combination of the delayed modifiers */
KEYDEV_TR_DELAYED_MODS = KEYDEV_TR_DELAYED_SHIFT
| KEYDEV_TR_DELAYED_ALPHA,
/* Instant SHIFT: Each individual event of every repeat streak gets /* Instant SHIFT: Each individual event of every repeat streak gets
SHIFT applied if SHIFT is pressed at the time of the repeat. */ SHIFT applied if SHIFT is pressed at the time of the repeat. */
KEYDEV_TR_INSTANT_SHIFT = 0x04, KEYDEV_TR_INSTANT_SHIFT = 0x04,
/* Instant ALPHA: Idem with the ALPHA key */ /* Instant ALPHA: Idem with the ALPHA key */
KEYDEV_TR_INSTANT_ALPHA = 0x08, KEYDEV_TR_INSTANT_ALPHA = 0x08,
/* Combination of the instance modifiers */
KEYDEV_TR_INSTANT_MODS = KEYDEV_TR_INSTANT_SHIFT
| KEYDEV_TR_INSTANT_ALPHA,
/* Combination of all modifiers */
KEYDEV_TR_ALL_MODS = KEYDEV_TR_DELAYED_MODS
| KEYDEV_TR_INSTANT_MODS,
/* Repeats: Keys are repeated according to a repeat filter function */ /* Repeats: Keys are repeated according to a repeat filter function */
KEYDEV_TR_REPEATS = 0x10, KEYDEV_TR_REPEATS = 0x10,
/* Delete Modifiers: Remove modifier keys from generated events, which /* Delete Modifiers: Remove modifier keys from generated events, which
@ -101,9 +101,6 @@ enum {
KEYDEV_TR_DELETE_RELEASES = 0x40, KEYDEV_TR_DELETE_RELEASES = 0x40,
}; };
/* keydev_repeat_profile_t: Function deciding when and how to repeat keys. */
typedef int (*keydev_repeat_profile_t)(int key, int duration, int count);
/* keydev_transform_t: Full specification for transforms on the event stream */ /* keydev_transform_t: Full specification for transforms on the event stream */
typedef struct { typedef struct {
/* List of enabled transforms. The order is cannot be changed because /* List of enabled transforms. The order is cannot be changed because
@ -125,13 +122,10 @@ typedef struct {
that the precision of the delay is limited by the speed at which the that the precision of the delay is limited by the speed at which the
keyboard is scanned, which is nowhere near microsecond-level. By keyboard is scanned, which is nowhere near microsecond-level. By
default (128 Hz) the precision is about 7.8 ms. */ default (128 Hz) the precision is about 7.8 ms. */
keydev_repeat_profile_t repeater; int (*repeater)(int key, int duration, int count);
} GPACKEDENUM keydev_transform_t; } GPACKEDENUM keydev_transform_t;
/* keydev_async_filter_t: Low-level asynchronous event filter. */
typedef bool (*keydev_async_filter_t)(key_event_t event);
/* keydev_t: Keyboard device /* keydev_t: Keyboard device
This structure represents the state and settings of a keyboard device that This structure represents the state and settings of a keyboard device that
@ -139,9 +133,9 @@ typedef bool (*keydev_async_filter_t)(key_event_t event);
is useful for demo/replays that input events without the physical keyboard, is useful for demo/replays that input events without the physical keyboard,
and a couple of corner uses like control over USB. and a couple of corner uses like control over USB.
The keyboard device has built-in event transformations, which modify the The keyboard device has built-in event transformations, which modifty the
stream of events by adding information, combining modifiers, and removing stream of events by adding information, combining modifiers, and removing
undesired events. Because the event transformation rely on the current state undesired events. Because the event transformation reky on the current state
of the keyboard, they must be run by the driver whenever events are read, so of the keyboard, they must be run by the driver whenever events are read, so
they are tied to the device. they are tied to the device.
@ -149,27 +143,35 @@ typedef bool (*keydev_async_filter_t)(key_event_t event);
keydown() are shortcuts for keydev functions using the physical keyboard as keydown() are shortcuts for keydev functions using the physical keyboard as
their input. */ their input. */
typedef struct { typedef struct {
/* Latest state of keys we are aware of. At every processing step, the
different between this and the fresh information is queued and this
is updated. state_now is identical to the real state obtained from
the device unless earlier events failed to be queued, in which case
a difference is maintained so they will be reconsidered later. */
GALIGNED(4) uint8_t state_now[12];
/* State of keys based on produced events. (state_queue + queue) is
always identical to (state_now). When the queue is empty both states
are the same. This is the user's view of the keyboard. */
GALIGNED(4) uint8_t state_queue[12];
/* Current device time in scanning-ticks */ /* Current device time in scanning-ticks */
uint time; uint time;
/* Last time when repeats were considered */ /* Last time when repeats were considered */
uint time_repeats; uint time_repeats;
/* Event queue (circular buffer) */
keydev_event_t queue[KEYBOARD_QUEUE_SIZE];
/* Next event in queue, position after last event in queue */ /* Next event in queue, position after last event in queue */
int8_t queue_next; int8_t queue_next;
int8_t queue_end; int8_t queue_end;
/* Number of events lost because of missing queue space */ /* Number of events lost because of missing queue space */
uint16_t events_lost; uint events_lost;
/* Crafted events waiting to be picked up */
key_event_t out[8];
int out_size;
/* Event transforms */ /* Event transforms */
keydev_transform_t tr; keydev_transform_t tr;
/* Asynchronous event filter. This is a low-level filter which is
called when events are generated to process and filter them. It
provides the unique ability to run keyboard-triggered code even if
the program's main thread is busy, for instance running some sort of
infinite loop. */
keydev_async_filter_t async_filter;
// <Delayed Modifiers> // <Delayed Modifiers>
/* delayed_* is set when the delayed modifier is active (after a press/ /* delayed_* is set when the delayed modifier is active (after a press/
@ -183,33 +185,14 @@ typedef struct {
// <Repeats> // <Repeats>
/* Candidate key for repeats (or 0 if no key is candidate yet) */ /* Candidate key for repeats (or 0 if no key is candidate yet) */
int16_t rep_key; int rep_key;
/* Number of repeats already sent */ /* Number of repeats alreay sent */
int16_t rep_count; int rep_count;
/* Time since key was first pressed (us) */ /* Time since key was first pressed (us) */
int rep_time; int rep_time;
/* Delay until next repeat, set by the repeat planner (us) */ /* Delay until next repeat, set by the repeat planner (us) */
int rep_delay; int rep_delay;
/* Latest state of keys we are aware of. At every processing step, the
difference between this and the fresh information is queued and this
is updated. state_now is identical to the real state obtained from
the device unless earlier events failed to be queued, in which case
a difference is maintained so they will be reconsidered later. */
GALIGNED(4) uint8_t state_now[12];
/* State of keys based on produced events. (state_queue + queue) is
always identical to (state_now). When the queue is empty both states
are the same. This is the user's view of the keyboard. */
GALIGNED(4) uint8_t state_queue[12];
/* Event queue (circular buffer) */
key_event_t queue[KEYBOARD_QUEUE_SIZE];
/* Parameters for the standard repeat function */
int rep_standard_first, rep_standard_next;
/* State of keys that have changes since the last flip monitoring reset. */
GALIGNED(4) uint8_t state_flips[12];
} keydev_t; } keydev_t;
/* keydev_std(): Standard keyboard input device /* keydev_std(): Standard keyboard input device
@ -235,12 +218,6 @@ void keydev_process_state(keydev_t *d, uint8_t state[12]);
devices (such as demo replays) to feed in new events. */ devices (such as demo replays) to feed in new events. */
void keydev_process_key(keydev_t *d, int keycode, bool state); void keydev_process_key(keydev_t *d, int keycode, bool state);
/* keydev_repeat_event(): Generate a repeat event if applicable
At the end of every scan tick, this source will generate a repeat event if
the repeat transform is enabled and the conditions for a repeat are
satisfied. */
key_event_t keydev_repeat_event(keydev_t *d);
/* keydev_tick(): Prepare the next tick /* keydev_tick(): Prepare the next tick
This function maintains time trackers in the device and should be called in This function maintains time trackers in the device and should be called in
each frame after the scanning is finished and the keydev_process_*() each frame after the scanning is finished and the keydev_process_*()
@ -254,22 +231,24 @@ void keydev_tick(keydev_t *d, uint us);
/* keydev_unqueue_event(): Retrieve the next keyboard event in queue /* keydev_unqueue_event(): Retrieve the next keyboard event in queue
This source provides the queued KEYEV_UP, KEYEV_DOWN and KEYEV_HOLD events This source provides the queued KEYEV_UP and KEYEV_DOWN events generated
generated from the regular scans in chronological order. It does not apply from the regular scans in chronological order. It does not generate the
transforms; to do this, use keydev_read(). */ KEYEV_HOLD events though, keyev_repeat_event() must be used to obtain these
at the end of every tick. */
key_event_t keydev_unqueue_event(keydev_t *d); key_event_t keydev_unqueue_event(keydev_t *d);
/* keydev_repeat_event(): Generate a repeat event if applicable
At the end of every scan tick (or later if the application is unable to keep
up), this source will generate a repeat event if the repeat transform is
enabled and the conditions for a repeat are satisfied. */
key_event_t keydev_repeat_event(keydev_t *d);
/* keydev_idle(): Check if all keys are released /* keydev_idle(): Check if all keys are released
A list of keys to ignore can be specified as variable arguments. The list A list of keys to ignore can be specified as variable arguments. The list
must be terminated by a 0 keycode. */ must be terminated by a 0 keycode. */
bool keydev_idle(keydev_t *d, ...); bool keydev_idle(keydev_t *d, ...);
/* keydev_async_filter(): Obtain current async filter */
keydev_async_filter_t keydev_async_filter(keydev_t const *d);
/* keydev_set_async_filter(): Set low-level async filter */
void keydev_set_async_filter(keydev_t *d, keydev_async_filter_t filter);
//--- //---
// High-level API to read from the device // High-level API to read from the device
//--- //---
@ -277,49 +256,14 @@ void keydev_set_async_filter(keydev_t *d, keydev_async_filter_t filter);
/* keydev_keydown(): Check if a key is down according to generated events */ /* keydev_keydown(): Check if a key is down according to generated events */
bool keydev_keydown(keydev_t *d, int key); bool keydev_keydown(keydev_t *d, int key);
/* keydev_keypressed(): Check if a key was pressed
This compares to the state at the time of the last keydev_clear_flips(). */
bool keydev_keypressed(keydev_t *d, int key);
/* keydev_keyreleased(): Check if a key was released
This compares to the state at the time of the last keydev_clear_flips(). */
bool keydev_keyreleased(keydev_t *d, int key);
/* keydev_clear_flips(): Reset flip info used by keypressed()/keyreleased() */
void keydev_clear_flips(keydev_t *d);
/* keydev_transform(): Obtain current transform parameters */ /* keydev_transform(): Obtain current transform parameters */
keydev_transform_t keydev_transform(keydev_t *d); keydev_transform_t keydev_transform(keydev_t *d);
/* keydev_set_transform(): Set transform parameters */ /* keydev_set_transform(): Set transform parameters */
void keydev_set_transform(keydev_t *d, keydev_transform_t tr); void keydev_set_transform(keydev_t *d, keydev_transform_t tr);
/* keydev_set_standard_repeats(): Enable a simple repeater /* keydev_read(): Retrieve the next transformed event */
key_event_t keydev_read(keydev_t *d);
This function changes the [repeater] member of the devices's transform. It
loads the default repeat profile which applies one delay (in us) before the
first repeat, and then a second, usually shorter delay, between subsequent
repeats.
The unit of the argument is in microseconds, but the granularity of the
delay is dependent on the keyboard scan frequency. In the default setting
(128 Hz scans), the possible repeat delays are approximately 8 ms, 16 ms,
23 ms, 31 ms. The system default is (500 ms, 125 ms). With the 128 Hz
setting, this default is reached exactly without approximation. gint's
default is (400 ms, 40 ms) for more reactivity.
Note: Due to a current API limitation, every input device uses the delays
for the physical keyboard.
@first_us Delay between key press and first repeat (microseconds)
@next_us Delay between subsequent repeats (microseconds) */
void keydev_set_standard_repeats(keydev_t *d, int first_us, int next_us);
/* keydev_read(): Retrieve the next transformed event
If there is no event, returns an event with type KEYEV_NONE, unless
[wait=true], in which case waits for an event to occur or *timeout to
become non-zero (if timeout is not NULL), whichever comes first. */
key_event_t keydev_read(keydev_t *d, bool wait, volatile int *timeout);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -1,26 +0,0 @@
//---
// gint:drivers:r61523 - Reneses R61523 driver
//
// This driver is used to control the 16-bit color LCD of the fx-CP 400.
//---
#ifndef GINT_DRIVERS_R61523
#define GINT_DRIVERS_R61523
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
/* r61523_display(): Update the entire display (320x528) */
void r61523_display(uint16_t *vram);
/* r61523_win_set(): Set the display window */
void r61523_win_set(int x1, int x2, int y1, int y2);
#ifdef __cplusplus
}
#endif
#endif /* GINT_DRIVERS_R61523 */

View file

@ -35,45 +35,16 @@ enum {
@method Transfer method, see above */ @method Transfer method, see above */
void r61524_display(uint16_t *vram, int start, int height, int method); void r61524_display(uint16_t *vram, int start, int height, int method);
/* r61524_display_rect(): Send a rectangular section of VRAM to the display
This function updates the rectangle going from (xmin,ymin) to (xmax,ymax) in
the VRAM to the display (both ends included). This can be faster than a full
dupdate() or a striped r61524_display() depending on the situation. However,
because the source VRAM is not contiguous, the transfer is only possible by
CPU, so this is only interesting for small regions.
@vram Source VRAM with a stride of 396*2 bytes
@xmin @xmax Horizontal range to be updated (both included)
@ymin @ymax Vertical range to be updated (both included) */
void r61524_display_rect(uint16_t *vram, int xmin, int xmax, int ymin,
int ymax);
/* r61524_display_mono_128x64(): Display a mono-style VRAM
This experimental function updates the display with the contents of a 128x64
VRAM, used with the fxg3a compilation target.
TODO: Make that a video mode. */
void r61524_display_mono_128x64(uint32_t *vram);
/* r61524_display_mono_128x64(): Display a gray-style VRAM
This experimental function updates the display with the contents of a 128x64
gray VRAM pair, used with the fxg3a compilation target.
TODO: Make that a video mode. */
void r61524_display_gray_128x64(uint32_t *light, uint32_t *dark);
/* r61524_start_frame(): Prepare the display for a region update /* r61524_start_frame(): Prepare the display for a region update
This function sets up the display driver to receive graphics data to update This function sets up the display driver to receive graphics data to update
the rectangle going from (xmin,ymin) to (xmax,ymax) (both included). This is liens [start] to [start+height-1] (both included) of the current window.
the initial step of r61524_display(), which is normally followed by writing This is the initial step of r61524_display(), which is normally followed by
all the data to 0xb4000000. writing all the data to 0xb4000000.
In order to write with the DMA, it is necessary to write the full horizontal
range and select ymin and ymax to be congruent to 0 and 3 modulo 4.
This function can be used to implement additional display driver update This function can be used to implement additional display driver update
methods or alternate rendering pipelines. */ methods or alternate rendering pipelines. */
void r61524_start_frame(int xmin, int xmax, int ymin, int ymax); void r61524_start_frame(int start, int height);
/* r162524_win_get() and r61524_win_set(): Manipulate the display window /* r162524_win_get() and r61524_win_set(): Manipulate the display window

View file

@ -13,14 +13,11 @@
extern "C" { extern "C" {
#endif #endif
#include <gint/config.h>
#include <gint/mpu/dma.h> #include <gint/mpu/dma.h>
#include <gint/clock.h>
/* Clock Pulse Generator (see cpg/cpg.c) */ /* Clock Pulse Generator (see cpg/cpg.c) */
typedef struct { typedef struct {
uint32_t SSCGCR; uint32_t SSCGCR;
struct cpg_overclock_setting speed;
} cpg_state_t; } cpg_state_t;
/* CPU (see cpu/cpu.c) */ /* CPU (see cpu/cpu.c) */
@ -28,7 +25,6 @@ typedef struct {
uint32_t SR; uint32_t SR;
uint32_t VBR; uint32_t VBR;
uint32_t CPUOPM; uint32_t CPUOPM;
uint32_t rN_bank[8];
} cpu_state_t; } cpu_state_t;
/* Direct Memory Access controller (see dma/dma.c) */ /* Direct Memory Access controller (see dma/dma.c) */
@ -90,23 +86,13 @@ typedef struct {
typedef struct { typedef struct {
/* Control and power-up. We don't save power-related registers from other /* Control and power-up. We don't save power-related registers from other
modules nor UPONCR, because they must be changed to use the module */ modules nor UPONCR, because they must be changed to use the module */
uint16_t SYSCFG, BUSWAIT, DVSTCTR, SOFCFG, TESTMODE, REG_C2; uint16_t SYSCFG, DVSTCTR, TESTMODE, REG_C2;
/* FIFO configuration */
uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL;
/* Interrupt configuration */ /* Interrupt configuration */
uint16_t INTENB0, INTENB1, BRDYENB, NRDYENB, BEMPENB; uint16_t INTENB0, BRDYENB, NRDYENB, BEMPENB, SOFCFG;
/* Default Control Pipe */ /* Default Control Pipe (maybe not needed) */
uint16_t DCPMAXP; uint16_t DCPCFG, DCPMAXP, DCPCTR;
#ifdef GINT_USB_DEBUG
/* Registers tracked read-only for state analysis and debugging */
uint16_t SYSSTS, FRMNUM, UFRMNUM;
uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL, CFIFOCTR, D0FIFOCTR, D1FIFOCTR;
uint16_t INTSTS0, INTSTS1, BRDYSTS, NRDYSTS, BEMPSTS;
uint16_t DCPCFG, DCPCTR;
uint16_t USBADDR, USBREQ, USBVAL, USBINDX, USBLENG;
uint16_t PIPESEL, PIPECFG[9], PIPEnCTR[9], PIPEBUF[9];
/* Ignored: UPONCR, PIPEnMAXP, PIPEnPERI, PIPEnTRN, PIPEnTRE, DEVADDn */
#endif
} usb_state_t; } usb_state_t;
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -1,107 +0,0 @@
//---
// gint:fs - Filesystem abstraction
//---
#ifndef GINT_FS
#define GINT_FS
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
#include <stddef.h>
/* Maximum number of file descriptors */
#define FS_FD_MAX 16
/* fs_descriptor_type_t: Overloaded file descriptor functions
This structure provides the base functions for every type of file
descriptor. The prototypes are standard, except that the first argument
(int fd) is replaced by (void *data) which points to the custom data
allocator for the descriptor. */
typedef struct {
/* See <unistd.h> for a description of these functions */
ssize_t (*read)(void *data, void *buf, size_t size);
ssize_t (*write)(void *data, void const *buf, size_t size);
off_t (*lseek)(void *data, off_t offset, int whence);
int (*close)(void *data);
} fs_descriptor_type_t;
/* fs_descriptor_t: File descriptor information
This internal type describes the entries of the descriptor table. */
typedef struct {
/* Interface functions */
fs_descriptor_type_t const *type;
/* Custom data (can also be an integer cast to (void *)) */
void *data;
} fs_descriptor_t;
/* fs_get_descriptor(): Get a file descriptor's data
This function is used internally in order to implement read(), write(), and
other standard functions functions. It could be useful for other APIs that
want to present their resources as file descriptors but still implement
extra non-standard functions; this allows them to use the file descriptor as
input and still access the custom data.
Returns NULL if there is no file descriptor with this number, in which case
the caller probably needs to set errno = EBADF. */
fs_descriptor_t const *fs_get_descriptor(int fd);
/* fs_create_descriptor(): Create a new file descriptor
This function is used in open() and its variants to allocate new file
descriptors. The descriptor's data is created with a copy of the provided
structure, which must include a non-NULL type attribute.
This function always returns the smallest file descriptor that is unused by
the application and is not 0, 1 or 2; unless it runs out of file
descriptors, in which case it returns -1 and the caller might want to set
errno = ENFILE. */
int fs_create_descriptor(fs_descriptor_t const *data);
/* fs_free_descriptor(): Close a file descriptor's slot
This function frees the specified file descriptor. It is automatically
called by close() after the descriptor type's close() function, so there is
normally no need to call this function directly. */
void fs_free_descriptor(int fd);
/* open_generic(): Open a file descriptor using custom file functions
Opens a new file descriptor of the specified type with the provided user
data. If reuse_fd < 0, a new file descriptor is allocated, otherwise the
exact file descriptor reuse_fd is used. (This is useful to reopen standard
streams.) In this case, the only possible return values are -1 and reuse_fd
itself. */
int open_generic(fs_descriptor_type_t const *type, void *data, int reuse_fd);
/* fs_path_normalize(): Normalize a path to eliminate ., .. and redundant /
This function creates a copy of the specified path which is an absolute path
with redundant / removed and . and .. components resolved. For instance:
"" -> "/"
"/subfolder/./" -> "/subfolder"
"/../subfolder/../subfolder" -> "/subfolder"
"/../../x/y/../t" -> "/x/t"
The new string is created with malloc() and should be free()'d after use. */
char *fs_path_normalize(char const *path);
/* fs_path_normalize_fc(): Normalize a path and translate it to FONTCHARACTER
This function is similar to fs_path_normalize(), but it creates a 16-bit
string that starts with \\fls0\ rather than /, and can be used in direct
calls to BFile. */
uint16_t *fs_path_normalize_fc(char const *path);
#ifdef __cplusplus
}
#endif
#endif /* GINT_FS */

View file

@ -1,105 +0,0 @@
//---
// gint:gdb - GDB remote serial protocol
//---
#ifndef GINT_GDB
#define GINT_GDB
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* gdb_cpu_state_t: State of the CPU when breaking
This struct keep the same register indices as those declared by GDB to allow
easy R/W without needing a "translation" table.
See : https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/sh-tdep.c;
h=c402961b80a0b4589243023ea5362d43f644a9ec;hb=4f3e26ac6ee31f7bc4b04abd
8bdb944e7f1fc5d2#l361
*/
// TODO : Should we expose r*b*, ssr, spc ? are they double-saved when breaking
// inside an interrupt handler ?
typedef struct {
union {
struct {
uint32_t r0;
uint32_t r1;
uint32_t r2;
uint32_t r3;
uint32_t r4;
uint32_t r5;
uint32_t r6;
uint32_t r7;
uint32_t r8;
uint32_t r9;
uint32_t r10;
uint32_t r11;
uint32_t r12;
uint32_t r13;
uint32_t r14;
uint32_t r15;
uint32_t pc;
uint32_t pr;
uint32_t gbr;
uint32_t vbr;
uint32_t mach;
uint32_t macl;
uint32_t sr;
} reg;
uint32_t regs[23];
};
} gdb_cpu_state_t;
/* gdb_start(): Start the GDB remote serial protocol server
This function starts the GDB stub (program that communicates with GDB). It
takes over USB, and returns once communication is established with a GDB
instance on the computer. Normally this is called by the panic handler after
a crash. It can also be called manually, in which case it should be followed
by a call to gdb_main() with the current state (NULL if you don't have any).
If the GDB stub is already started, this is a no-op. Returns 0 on success
and a negative value if the USB setup fails. */
int gdb_start(void);
/* gdb_main(): Main GDB loop
Main loop for when the program is in a paused state. Communicates with GDB
over USB and mutates the cpu_state struct, memory and the UBC configuration
accordingly. Returns once the program should resume. */
void gdb_main(gdb_cpu_state_t *cpu_state);
/* gdb_start_on_exception(): Set the GDB stub to autostart on crashes
This function sets a crash handler that starts the GDB stub whenever a
System ERROR occurs. */
// TODO: Get some visible signal on-screen when that happens
void gdb_start_on_exception(void);
/* gdb_redirect_streams(): Select whether to redirect stdout/stderr
This function specifies whether stdout and stderr shall be redirected when
debugging. If redirected, stdout and stderr will change from their default
implementation (or the one supplied by the user via open_generic()) to a
stubcall that prints text in the GDB console. This setting must be set
before GDB starts, so usually just after gdb_start_on_exception().
This is intended to be used with debug methods that print text. For example,
if the program has a status() function that prints information useful when
debugging, one might want to invoke it from GDB with "call status()". With
default stdout/stderr this would be at best impractical to read the output
on the calculator, at worst buggy due to the program being interrupted.
Redirecting stdout/stderr allows the result to be printed in GDB.
The default is to not redirect stdout/stderr. */
void gdb_redirect_streams(bool redirect_stdout, bool redirect_stderr);
/** Stubcalls **/
/* Write to a file descriptor on the remote debugger. */
void gdb_stubcall_write(int fd, void const *buf, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* GINT_GDB */

View file

@ -41,13 +41,6 @@ int gint_world_switch(gint_call_t function);
__attribute__((deprecated("Use gint_world_switch() instead"))) __attribute__((deprecated("Use gint_world_switch() instead")))
void gint_switch(void (*function)(void)); void gint_switch(void (*function)(void));
/* gint_world_sync(): Synchronize asynchronous drivers
This function waits for asynchronous tasks to complete by unbinding all
drivers. This is useful in certain hardware operations while remaining in
gint. */
void gint_world_sync(void);
/* gint_osmenu(): Call the calculator's main menu /* gint_osmenu(): Call the calculator's main menu
This function safely invokes the calculator's main menu with gint_switch(). This function safely invokes the calculator's main menu with gint_switch().
@ -59,77 +52,18 @@ void gint_world_sync(void);
call to getkey(), but can also be called manually. */ call to getkey(), but can also be called manually. */
void gint_osmenu(void); void gint_osmenu(void);
/* gint_osmenu_native(): Like gint_osmenu() without the world switch
This is a replacement for gint_osmenu() which can be used when the current
kernel is already the native OS kernel. */
void gint_osmenu_native(void);
/* gint_setrestart(): Set whether to restart the add-in after exiting /* gint_setrestart(): Set whether to restart the add-in after exiting
An add-in that returns from its main() function automatically exits to the An add-in that reaches the end of its code exits. On the calculator, except
OS' main menu. However, when this happens the OS does not allow the add-in using OS-dependent settings, it cannot be started again unless another
to be restarted unless another add-in is launched first. (This is because application is launched first.
the OS tries to *resume* the current add-in, which then proceeds to exit
again immediately.)
This function enables a gint trick where after main() returns the add-in This setting allows the add-in to restart by calling gint_osmenu() instead
will invoke the main menu with gint_osmenu() rather than exiting. If the of exiting. This can give a proper illusion of restarting if used correctly.
add-in is selected again, gint will jump back to the entry point, creating
the illusion that the add-in exited and was then restarted.
@restart 0 to exit, 1 to restart by using gint_osmenu() */ @restart 0 to exit, 1 to restart by using gint_osmenu() */
void gint_setrestart(int restart); void gint_setrestart(int restart);
/* gint_poweroff(): World switch and power off the calculator
Shows the CASIO logo / poweroff screen if show_logo is true. The program
will resume execution normally after powering on again. Don't call this
function with show_logo=false as a result of pressing AC/ON because the
calculator will restart immediately unless the user releases the AC/ON key
extremely quickly. */
void gint_poweroff(bool show_logo);
/* gint_set_onchip_save_mode(): Specify memory save policy for world switches
World switches can cause corruption in on-chip memory in two ways. First, if
the calculator powers off while in the OS world, on-chip memory will be
wiped because it's not powered when the calculator is off. Second, the OS
may run code that overwrites on-chip memory (we don't have any examples of
that but it's not part of the add-in interface).
As a result, the best option is to backup on-chip memory and restore it when
loading back into the add-in. The issue is that there's 20 kB of on-chip
memory (4 kB ILRAM + 16 kB XYRAM) and on some machines (fx-9860G-like) or in
some applications we don't have that much memory lying around.
This function selects between three modes for handling this save:
* [Reinitialization mode]
Don't save on-chip memory at all, instead just reinitialize the globals
and leave the rest corrupted. This is useful if on-chip memory is used
only for temporaries (e.g. frames in Azur), for which we don't care if
they're corrupted, or code (e.g gint interrupt handling code), which will
be reloaded and is otherwise a constant.
* [Backup mode]
Save on-chip memory to a user-provided buffer of size GINT_ONCHIP_BUFSIZE.
* [Nothing mode]
Don't do anything, and let the world switch function choose its preferred
saving method. This allows application-specific compromises.
This is not a problem on SH3 because on-chip memory is only used on SH4. */
enum {
GINT_ONCHIP_REINITIALIZE = 0,
GINT_ONCHIP_BACKUP = 1,
GINT_ONCHIP_NOTHING = 2,
};
#define GINT_ONCHIP_BUFSIZE (20 << 10)
void gint_set_onchip_save_mode(int mode, void *ptr);
/* gint_get_onchip_save_mode(): Get the current on-chip memory save policy */
int gint_get_onchip_save_mode(void **ptr);
/* This function has been moved to the INTC driver */ /* This function has been moved to the INTC driver */
__attribute__((deprecated("Use intc_handler() instead"))) __attribute__((deprecated("Use intc_handler() instead")))
static GINLINE void *gint_inthandler(int code, void const *h, size_t size) { static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
@ -155,49 +89,10 @@ static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
.callback: .callback:
.long _gint_inth_callback .long _gint_inth_callback
The gint_call_t object can be built with GINT_CALL_FLAG to specify that the
called function should get a pointer to a gint_inth_callback_context_t
strcture as its first argument.
@call Address of a gint_call_t object @call Address of a gint_call_t object
Returns the return value of the callback. */ Returns the return value of the callback. */
extern int (*gint_inth_callback)(gint_call_t const *call); extern int (*gint_inth_callback)(gint_call_t const *call);
/* gint_inth_callback_context_t: Context of the interrupted function when an
interrupt is handled by gint_inth_callback. */
typedef struct {
uint32_t ssr;
uint32_t spc;
uint32_t r7;
uint32_t r6;
uint32_t r5;
uint32_t r4;
uint32_t r3;
uint32_t r2;
uint32_t r1;
uint32_t r0;
} gint_inth_callback_context_t;
/* gint_set_quit_handler(): Setup a call to be invoked when leaving the add-in
This function sets up the provided GINT_CALL() to be invoked when the
add-in is unloaded, which is either when we exit from main() or when
starting another application from the main menu. Crucially, this is only
*after* selecting an application, not before opening the main menu. The
quit handler is not invoked if the user re-enters the add-in.
This is based on the SetQuitHandler() syscall, and therefore the callback
runs in the OS world by default. If [run_in_os_world] is set to false, a
world switch will be performed to run the callback in the gint world.
TODO: Currently the quit handler is not called when exiting from main().
TODO: Detail how this interacts with destructor functions!
TODO: [run_in_os_world == false] is not honored yet (because unstable)
@call Callback to be performed when leaving add-in
@run_in_os_world true to stay in OS world, false to use gint world */
void gint_set_quit_handler(gint_call_t gcall, bool run_in_os_world);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -18,8 +18,6 @@
extern "C" { extern "C" {
#endif #endif
#include <gint/config.h>
/* For compatibility with ASM, include the following bits only in C code */ /* For compatibility with ASM, include the following bits only in C code */
#ifndef CPP_ASM #ifndef CPP_ASM
@ -42,14 +40,14 @@ void hw_detect(void);
dual-platform version for libraries. dual-platform version for libraries.
Warning: this macro is also used hardcoded in exch.s. */ Warning: this macro is also used hardcoded in exch.s. */
#if GINT_HW_FX #if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
# define isSH3() (gint[HWMPU] & 1) #define isSH3() (gint[HWMPU] & 1)
# define isSH4() (!isSH3()) #define isSH4() (!isSH3())
# define isSlim() (gint[HWCALC] == HWCALC_FX9860G_SLIM) #endif
#elif GINT_HW_CG || GINT_HW_CP
# define isSH3() 0 #ifdef FXCG50
# define isSH4() 1 #define isSH3() 0
# define isSlim() 0 #define isSH4() 1
#endif #endif
/* This bit should be set in all data longwords except HWMPU, HWCPUVR, HWCPUPR /* This bit should be set in all data longwords except HWMPU, HWCPUVR, HWCPUPR
@ -72,7 +70,6 @@ void hw_detect(void);
#define HWKBD 8 /* Keyboard */ #define HWKBD 8 /* Keyboard */
#define HWKBDSF /* Deprecated: use keysc_scan_frequency() */ #define HWKBDSF /* Deprecated: use keysc_scan_frequency() */
#define HWDD /* Deprecated: use the T6K11/R61524 API */ #define HWDD /* Deprecated: use the T6K11/R61524 API */
#define HWFS 11 /* Filesystem type */
/* /*
** MPU type ** MPU type
@ -107,10 +104,6 @@ void hw_detect(void);
#define HWCALC_FXCG50 5 #define HWCALC_FXCG50 5
/* fx-CG 50 emulator, hardcoded in kernel/inth.S */ /* fx-CG 50 emulator, hardcoded in kernel/inth.S */
#define HWCALC_FXCG_MANAGER 6 #define HWCALC_FXCG_MANAGER 6
/* fx-9860G Slim, SH-3-based fx-9860G with hardware differences */
#define HWCALC_FX9860G_SLIM 7
/* fx-CP 400 */
#define HWCALC_FXCP400 8
/* /*
** Keyboard ** Keyboard
@ -125,17 +118,6 @@ void hw_detect(void);
/* The keyboard uses a KEYSC-based scan method. This is only possible on SH4 */ /* The keyboard uses a KEYSC-based scan method. This is only possible on SH4 */
#define HWKBD_KSI 0x04 #define HWKBD_KSI 0x04
/*
** Filesystem type
*/
/* Unknown or no filesystem. */
#define HWFS_NONE 0
/* CASIO's in-house filesystem, now deprecated. */
#define HWFS_CASIOWIN 1
/* Wrapper around Kyoto Software Research's Fugue VFAT implementation. */
#define HWFS_FUGUE 2
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -1,846 +0,0 @@
//---
// gint:image - Image manipulation and rendering
//
// Note: this module is currently only available on fx-CG.
//
// This header provides image manipulation functions. This mainly consists of a
// reference-based image format, various access and modification functions, and
// a number of high-performance transformations and rendering effects. If you
// find yourself limited by rendering time, note that RAM writing speed is
// often the bottleneck, and image rendering is much faster in Azur (which is
// what the renderer was initially designed for).
//
// This module supports 3 bit depths: full-color 16-bit (RGB565), indexed 8-bit
// (P8) and indexed 4-bit (P4). All three have an "alpha" variation where one
// color is treated as transparent, leading to 6 total formats.
//
// The image renderers support so-called *dynamic effects*, which are image
// transformations performed on-the-fly while rendering, without generating an
// intermediate image. They comprise straightforward transformations that
// achieve similar performance to straight rendering and can be combined to
// some extent, which makes them reliable whenever applicable.
//
// For images of the RGB16 and P8 bit depths, the module supports a rich API
// with image subsurfaces and a fairly large sets of geometric and color
// transforms, including some in-place. P4 is not supported in most of these
// functions because the dense bit packing is both impractical and slower for
// these applications.
//---
#ifndef GINT_IMAGE
#define GINT_IMAGE
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/config.h>
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
struct dwindow;
//---
// Image structures
//---
/* Image formats. Note that transparency really only indicates the default
rendering method, as a transparent background can always be added or removed
by a dynamic effect on any image. */
enum {
IMAGE_RGB565 = 0, /* RGB565 without alpha */
IMAGE_RGB565A = 1, /* RGB565 with one transparent color */
IMAGE_P8_RGB565 = 4, /* 8-bit palette, all opaque colors */
IMAGE_P8_RGB565A = 5, /* 8-bit with one transparent color */
IMAGE_P4_RGB565 = 6, /* 4-bit palette, all opaque colors */
IMAGE_P4_RGB565A = 3, /* 4-bit with one transparent color */
IMAGE_DEPRECATED_P8 = 2,
};
/* Quick macros to compare formats by storage size */
#define IMAGE_IS_RGB16(format) \
((format) == IMAGE_RGB565 || (format) == IMAGE_RGB565A)
#define IMAGE_IS_P8(format) \
((format) == IMAGE_P8_RGB565 || (format) == IMAGE_P8_RGB565A)
#define IMAGE_IS_P4(format) \
((format) == IMAGE_P4_RGB565 || (format) == IMAGE_P4_RGB565A)
/* Check whether image format has an alpha color */
#define IMAGE_IS_ALPHA(format) \
((format) == IMAGE_RGB565A || \
(format) == IMAGE_P8_RGB565A || \
(format) == IMAGE_P4_RGB565A)
/* Check whether image format uses a palette */
#define IMAGE_IS_INDEXED(format) \
(IMAGE_IS_P8(format) || IMAGE_IS_P4(format))
/* Image flags. These are used for memory management, mostly. */
enum {
IMAGE_FLAGS_DATA_RO = 0x01, /* Data is read-only */
IMAGE_FLAGS_PALETTE_RO = 0x02, /* Palette is read-only */
IMAGE_FLAGS_DATA_ALLOC = 0x04, /* Data is malloc()'d */
IMAGE_FLAGS_PALETTE_ALLOC = 0x08, /* Palette is malloc()'d */
};
/* image_t: gint's native bitmap image format
Images of this format can be created through this header's API but also by
using the fxSDK's built-in image converters with fxconv. */
typedef struct
{
/* Color format, one of the IMAGE_* values defined above */
uint8_t format;
/* Additional flags, a combination of IMAGE_FLAGS_* values */
uint8_t flags;
/* Number of colors in the palette; this includes alpha for transparent
images, as alpha is always the first entry.
RGB16: 0
P8: Ranges between 1 and 256
P4: 16 */
int16_t color_count;
/* Full width and height, in pixels */
uint16_t width;
uint16_t height;
/* Byte stride between lines */
int stride;
/* Pixel data in row-major order, left to right.
- RGB16: 2 bytes per entry, each row padded to 4 bytes for alignment.
Each 2-byte value is an RGB565 color.
- P8: 1 signed byte per entry. Each byte is a palette index shifted by
128 (to access the color, use palette[<value>+128]).
- P4: 4 bits per entry, each row padded to a full byte. Each entry is a
direct palette index between 0 and 15. */
void *data;
/* For P8 and P4, color palette. The number of entries allocated in the
array is equal to the color_count attribute. */
uint16_t *palette;
} GPACKED(4) image_t;
/* Dynamic effects: these transformations can be applied on images while
rendering. Not all effects can be combined; unless specified otherwise:
- HFLIP and VFLIP can both be added regardless of any other effect
- At most one color effect can be applied */
enum {
/* Value 0x01 is reserved, because it is DIMAGE_NOCLIP, which although
part of the old API still needs to be supported. */
/* [Any]: Skip clipping the command against the source image */
IMAGE_NOCLIP_INPUT = 0x04,
/* [Any]: Skip clipping the command against the output VRAM */
IMAGE_NOCLIP_OUTPUT = 0x08,
/* [Any]: Skip clipping both */
IMAGE_NOCLIP = IMAGE_NOCLIP_INPUT | IMAGE_NOCLIP_OUTPUT,
// Geometric effects. These values should remain at exactly bit 8 and
// following, or change gint_image_mkcmd() along with it.
/* [Any]: Flip image vertically */
IMAGE_VFLIP = 0x0100,
/* [Any]: Flip image horizontally */
IMAGE_HFLIP = 0x0200,
// Color effects
/* [RGB565, P8_RGB565, P4_RGB565]: Make a color transparent
Adds one argument:
* Color to clear (RGB16: 16-bit value; P8/P4: palette index) */
IMAGE_CLEARBG = 0x10,
/* [RGB565, P8_RGB565, P4_RGB565]: Turn a color into another
Adds two arguments:
* Color to replace (RGB16: 16-bit value; P8/P4: palette index)
* Replacement color (16-bit value) */
IMAGE_SWAPCOLOR = 0x20,
/* [RGB565A, P8_RGB565A, P4_RGB565A]: Add a background
Adds one argument:
* Background color (16-bit value) */
IMAGE_ADDBG = 0x40,
/* [RGB565A, P8_RGB565A, P4_RGB565A]: Dye all non-transparent pixels
Adds one argument:
* Dye color (16-bit value) */
IMAGE_DYE = 0x80,
};
//---
// Image creation and destruction
//---
#if GINT_RENDER_RGB
/* image_alloc(): Create a new (uninitialized) image
This function allocates a new image of the specified dimensions and format.
It always allocates a new data array; if you need to reuse a data array, use
the lower-level image_create() or image_create_sub().
The first parameters [width] and [height] specify the dimensions of the new
image in pixels. The [format] should be one of the IMAGE_* formats, for
example IMAGE_RGB565A or IMAGE_P4_RGB565.
This function does not specify or initialize the palette of the new image;
use image_set_palette(), image_alloc_palette() or image_copy_palette()
after calling this function.
The returned image structure must be freed with image_free() after use.
@width Width of the new image
@height Height of the new image
@format Pixel format; one of the IMAGE_* formats defined above */
image_t *image_alloc(int width, int height, int format);
/* image_set_palette(): Specify an external palette for an image
This function sets the image's palette to the provided address. The number
of entries allocated must be specified in size. It is also the caller's
responsibility to ensure that the palette covers all the indices used in the
image data.
The old palette, if owned by the image, is freed. If [owns=true] the
palette's ownership is given to the image, otherwise it is kept external. */
void image_set_palette(image_t *img, uint16_t *palette, int size, bool owns);
/* image_alloc_palette(): Allocate a new palette for an image
This function allocates a new palette for an image. The number of entries is
specified in size; for P8 it can vary between 1 and 256, for P4 it is
ignored (P4 images always have 16 colors).
The old palette, if owned by the image, is freed. The entries of the new
palette are all initialized to 0. If size is -1, the format's default
palette size is used. Returns true on success. */
bool image_alloc_palette(image_t *img, int size);
/* image_copy_palette(): Copy another image's palette
This function allocates a new palette for an image, and initializes it with
a copy of another image's palette. For P8 the palette can be resized by
specifying a value other than -1 as the size; by default, the source image's
palette size is used (within the limits of the new format). Retuns true on
success. */
bool image_copy_palette(image_t const *src, image_t *dst, int size);
/* image_create(): Create a bare image with no data/palette
This function allocates a new image structure but without data or palette.
The [data] and [palette] members are NULL, [color_count] and [stride] are 0.
This function is useful to create images that reuse externally-provided
information. It is intended that the user of this function sets the [data]
and [stride] fields themselves, along with the IMAGE_FLAGS_DATA_ALLOC flag
if the image should own its data.
The [palette] and [color_count] members can be set with image_set_palette(),
image_alloc_palette(), image_copy_palette(), or manually.
The returned image structure must be freed with image_free() after use. */
image_t *image_create(int width, int height, int format);
/* image_create_vram(): Create a reference to gint_vram
This function creates a new RGB565 image that references gint_vram. Using
this image as target for transformation functions can effectively render
transformed images to VRAM.
The value of gint_vram is captured when this function is called, and does
not update after dupdate() when triple-buffering is used. The user should
account for this option. (Using this function twice then replacing one of
the [data] pointers is allowed.)
The VRAM image owns no data but it does own its own structure so it must
still be freed with image_free() after use. */
image_t *image_create_vram(void);
/* image_free(): Free and image and the data it owns
This function frees the provided image structure and the data that it owns.
Images converted by fxconv should not be freed; nonetheless, this functions
distinguishes them and should work. Images are not expected to be created on
the stack.
If the image has the IMAGE_FLAGS_DATA_ALLOC flag, the data pointer is also
freed. Similarly, the image has the IMAGE_FLAGS_PALETTE_ALLOC flag, the
palette is freed. Make sure to not free images when references to them still
exist, as this could cause the reference's pointers to become dangling. */
void image_free(image_t *img);
//---
// Basic image access and information
//---
/* image_valid(): Check if an image is valid
An image is considered valid if it has a valid profile, a non-NULL data
pointer, and for palette formats a valid palette pointer. */
bool image_valid(image_t const *img);
/* image_alpha(): Get the alpha value for an image format
This function returns the alpha value for any specific image format:
* RGB16: 0x0001
* P8: -128 (0x80)
* P4: 0
For non-transparent formats, it returns a value that is different from all
valid pixel values of the format, which means it is always safe to compare a
pixel value to the image_alpha() of the format. */
int image_alpha(int format);
/* image_get_pixel(): Read a pixel from the data array
This function reads a pixel from the image's data array at position (x,y).
It returns the pixel's value, which is either a full-color value (RGB16) or
a possibly-negative palette index (P8/P4). See the description of the [data]
field of image_t for more details. The value of the pixel can be decoded
into a 16-bit color either manually or by using the image_decode_pixel()
function.
Note that reading large amounts of image data with this function will be
slow; if you need reasonable performance, consider iterating on the data
array manually. */
int image_get_pixel(image_t const *img, int x, int y);
/* image_decode_pixel(): Decode a pixel value
This function decodes a pixel's value obtained from the data array (for
instance with image_get_pixel()). For RGB16 formats this does nothing, but
for palette formats this accesses the palette at a suitable position.
Note that reading large amounts of data with this function will be slow; if
you need reasonable performance, consider inlining the format-specific
method or iterating on the data array manually. */
int image_decode_pixel(image_t const *img, int pixel);
/* image_data_size(): Compute the size of the [data] array
This function returns the size of the data array, in bytes. This can be used
to duplicate it. Note that for sub-images this is a subsection of another
image's data array, and might be much larger than the sub-image. */
int image_data_size(image_t const *img);
//---
// Basic image modifications
//---
/* image_set_pixel(): Set a pixel in the data array
This function writes a pixel into the image's data array at position (x,y).
The pixel value must be of the proper format, as specified in the definition
of the [data] field of image_t.
Formats: RGB16, P8, P4 */
void image_set_pixel(image_t const *img, int x, int y, int value);
/* image_copy(): Convert and copy an image
This function copies an image into another image while converting certain
formats. Unlike transforms, this function does clip, so there are no
conditions on the size of the target.
If [copy_alpha] is true, transparent pixels are copied verbatim, which
effectively replaces the top-left corner of [dst] with [src]. If it's false,
transparent pixels of [src] are skipped, effectively rendering [src] over
the top-left corner of [src].
This function converts between all formats except from RGB16 to P8/P4, since
this requires generating a palette (which is a complex endeavour).
Conversions from P8/P4 to RGB16 simply decode the palette. Conversions
between P8/P4 preserve the contents but renumber the palette entries. From
P4 to P8, the image is always preserved. From P8 to P4, the image is only
preserved if it has less than 16 colors (this is intended to allow P4 images
to be converted to P8 for edition by this library, and then back to P4). The
following table summarizes the conversions:
Source format RGB16 P8 P4
Target format +-----------+----------------+------------------+
RGB16 | Copy Decode palette Decode palette |
P8 | - Copy Enlarge palette |
P4 | - Narrow palette Copy |
+-----------+----------------+------------------+
Note that conversions to RGB16 are not lossless because RGB565, P8 and P4
can represent any color; if a color equal to image_alpha(IMAGE_RGB565A) is
found during conversion, this function transforms it slightly to look
similar instead of erroneously generating a transparent pixel.
Formats: RGB16 RGB16, P8 Anything, P4 Anything
Size requirement: none (clipping is performed)
Supports in-place: No (useless) */
void image_copy(image_t const *src, image_t *dst, bool copy_alpha);
/* image_copy_alloc(): Convert and copy into a new image
This function is similar to image_copy(), but it allocates a target image of
the desired format before copying. */
image_t *image_copy_alloc(image_t const *src, int new_format);
/* image_fill(): Fill an image with a single pixel value */
void image_fill(image_t *img, int value);
/* image_clear(): Fill a transparent image with its transparent value */
void image_clear(image_t *img);
//---
// Sub-image extraction
//---
/* image_sub(): Build a reference to a sub-image
This function is used to create references to sub-images of RGB16 and P8
images. The [data] pointer of the sub-image points somewhere within the data
array of the source, and its [palette] pointer is identical to the source's.
The last parameter is a pointer to a preallocated image_t structure (usually
on the stack) that gets filled with the data. Doing this instead of
allocating a new object with malloc() means that there is no need to
image_free() the sub-image, and thus it can be used inline:
image_t tmp;
image_hflip(src, image_sub(dst, x, y, w, h, &tmp));
A preprocessor macro is used to make the last parameter optional. If it's
not specified, a pointer to a static image_t will be returned instead. This
is useful in inline calls as shown above, which then simplify to:
image_hflip(src, image_sub(dst, x, y, w, h));
However, another call to image_sub() or image_at() will override the
sub-image, so you should only use this in such temporary settings. If you
need multiple image_sub() or image_at() calls in the same statement, only
one can use the short form.
If the requested rectangle does not intersect the source, the sub-image will
be of dimension 0x0. If the image format does not support sub-images (P4),
the sub-image will test invalid with image_valid(). */
image_t *image_sub(image_t const *src, int x, int y, int w, int h,
image_t *dst);
/* Make the last parameter optional */
#define image_sub1(src, x, y, w, h, dst, ...) image_sub(src, x, y, w, h, dst)
#define image_sub(...) image_sub1(__VA_ARGS__, NULL)
/* image_at(): Build a reference to a position within a sub-image */
#define image_at(img, x, y) image_sub(img, x, y, -1, -1)
//---
// Geometric image transforms
//
// All geometric transforms render to position (0,0) of the target image and
// fail if the target image is not large enough to hold the transformed result
// (unlike the rendering functions which render only the visible portion).
//
// To render at position (x,y) of the target image, use img_at(). For instance:
// image_hflip(src, image_at(dst, x, y));
//
// Each transform function has an [_alloc] variant which does the same
// transform but allocates the target image on the fly and returns it. Remember
// that allocation can fail, so you need to check whether the returned image is
// valid.
//
// (You can still pass invalid images to transform functions. The invalid image
// will be ignored or returned unchanged, so you can chain calls and check for
// validity at the end of the chain.)
//
// Some functions support in-place transforms. This means they can be called
// with the source as destination, and will transform the image without needing
// new memory. For instance, image_hflip(src, src) flips in-place and replaces
// src with a flipped version of itself.
//
// (However, it is not possible to transform in-place if the source and
// destination intersect in non-trivial ways. The result will be incorrect.)
//
// When transforming to a new image, transparent pixels are ignored, so if the
// destination already has some data, it will not be erased automatically. Use
// image_clear() beforehand to achieve that effect. This allows alpha blending
// while transforming, which is especially useful on the VRAM.
//---
/* image_hflip(): Flip horizontally
Formats: RGB16, P8
Size requirement: destination at least as large as source (no clipping)
Supports in-place: Yes */
void image_hflip(image_t const *src, image_t *dst, bool copy_alpha);
image_t *image_hflip_alloc(image_t const *src);
/* image_vflip(): Flip vertically
Formats: RGB16, P8
Size requirement: destination at least as large as source (no clipping)
Supports in-place: Yes */
void image_vflip(image_t const *src, image_t *dst, bool copy_alpha);
image_t *image_vflip_alloc(image_t const *src);
/* image_linear(): Linear transformation
This function implements a generic linear transformation. This is a powerful
function that can perform any combination of rotation, mirroring and scaling
with nearest-neighbor sampling.
The [image_linear_map] structure defines the settings for the transform.
Users familiar with linear algebra might want to use it directly, but they
are most conveniently generated with the rotation and scaling functions
listed below.
Note: Currently the structure for the transform is modified by the
operation and cannot be reused.
The image_linear_alloc() variant allocates a new image in addition to
performing the transform. The image is created with size (map->dst_w,
map->dst_h) which is always a reasonable default. If a target image of
smaller size is supplied to image_linear(), clipping is performed; only the
top-left corner of the full output is actually rendered.
Formats: RGB16, P8
Size requirement: none (clipping is performed)
Supports in-place: No */
struct image_linear_map {
/* Dimensions of the source and destination */
int src_w, src_h, dst_w, dst_h;
/* Input and output stride in bytes */
int src_stride, dst_stride;
/* The following parameters define the linear transformation as a mapping
from coordinates in the destination image (x and y) into coordinates in
the source image (u and v).
- (u, v) indicate where the top-left corner of the destination lands in
the source image.
- (dx_u, dx_v) indicate the source-image movement for each movement of
x += 1 in the destination.
- (dy_u, dy_v) indicate the source-image movement for each movement of
y += 1 in the destination.
All of these values are specified as 16:16 fixed-point, ie. they encode
decimal values by multiplying them by 65536. */
int u, v, dx_u, dx_v, dy_u, dy_v;
};
void image_linear(image_t const *src, image_t *dst,
struct image_linear_map *map);
image_t *image_linear_alloc(image_t const *src,
struct image_linear_map *map);
/* image_scale(): Upscale or downscale an image
This function generates a linear map to be used in image_linear() to scale
the input image. The scaling factor gamma can be specified independently for
the x and y dimensions. It is expressed as 16:16 fixed-point; you can set
any decimal value multiplied by 65536, for instance 1.5*65536 to increase
the width and height by 50%. */
void image_scale(image_t const *src, int gamma_x, int gamma_y,
struct image_linear_map *map);
/* image_rotate(): Rotate an image around its center
This function generates a linear map to be used in image_linear() to perform
a rotation around the center of an image. If [resize=true], the target is
enlarged to make sure all the rotated pixels can be represented. This can
increase the final surface by a factor of up to 2. If the original image
doesn't extend to its corners, it is recommended to leave [resize=false] as
it noticeably affects performance. */
void image_rotate(image_t const *src, float angle, bool resize,
struct image_linear_map *map);
/* image_rotate_around(): Rotate an image around any point
This function generalizes image_rotate() by allowing rotations around any
center, even a point not within the image. The center is specified through
two coordinates (*center_x, *center_y). If the center is near the side of
the image, a normal rotation would move most of the pixels out of frame;
this function moves the frame to make sure the whole image remains visible.
*center_x and *center_y are updated to indicate the position of the center
of rotation within the new frame (the target image). */
void image_rotate_around(image_t const *src, float angle, bool resize,
int *center_x, int *center_y, struct image_linear_map *map);
/* image_rotate_around_scale(): Rotate an image around any point and scale it
This function generalizes image_rotate_around() by adding a scaling factor
to the transformation. The scaling factor gamma is expressed as 16:16
fixed-point. If [resize=true] the image is further extended to make sure no
parts are cut out, as in other rotation functions. */
void image_rotate_around_scale(
image_t const *src, float angle, int gamma,
bool resize, int *center_x, int *center_y,
struct image_linear_map *map);
//---
// Color transforms
//---
/* TODO: Color transforms */
//---
// Image rendering functions
//
// The following functions extend dimage() and dsubimage(). The [effects]
// parameter takes a combination of IMAGE_* flags and effects, limited to the
// combinations previously described, with additional arguments depending on
// the color effect being applied.
//
// dimage_effect(x, y, img, effects, ...)
// dsubimage_effect(x, y, img, left, top, w, h, effects, ...)
//
// However if you use these super-generic functions you will link the code for
// all effects and all formats into your add-in, which takes a fair amount of
// space. If that's a problem, you can use the more specific functions below:
//
// * dimage_<FORMAT>_<EFFECT>() for one particular format (rgb16, p8, p4) along
// with one particular color effect (clearbg, swapcolor, addbg, dye).
// * dimage_<FORMAT>() is like the above when no color effect is applied.
//
// All of them support the HFLIP and VFLIP flags. For effect-specific functions
// the corresponding effect flag can be omitted (fi. IMAGE_CLEARBG is implicit
// when using dimage_p8_clearbg()).
//---
/* dimage_effect(): Generalized dimage() supporting dynamic effects */
#define dimage_effect(x, y, img, eff, ...) \
dsubimage_effect(x, y, img, 0, 0, (img)->width, (img)->height, eff, \
##__VA_ARGS__)
/* dsubimage_effect(): Generalized dsubimage() supporting dynamic effects */
void dsubimage_effect(int x, int y, image_t const *img,
int left, int top, int w, int h, int effects, ...);
/* Specific versions for each format */
#define DIMAGE_SIG1(NAME, ...) \
void dimage_ ## NAME(int x, int y, image_t const *img,##__VA_ARGS__); \
void dsubimage_ ## NAME(int x, int y, image_t const *img, \
int left, int top, int w, int h, ##__VA_ARGS__);
#define DIMAGE_SIG(NAME, ...) \
DIMAGE_SIG1(rgb16 ## NAME, ##__VA_ARGS__) \
DIMAGE_SIG1(p8 ## NAME, ##__VA_ARGS__) \
DIMAGE_SIG1(p4 ## NAME, ##__VA_ARGS__)
/* d[sub]image_{rgb16,p8,p4}_effect(..., effects, <extra arguments>) */
DIMAGE_SIG(_effect, int effects, ...)
/* d[sub]image_{rgb16,p8,p4}(..., effects) (no color effect, like dimage()) */
DIMAGE_SIG(, int effects)
/* d[sub]image_{rgb16,p8,p4}_clearbg(..., effects, bg_color_or_index) */
DIMAGE_SIG(_clearbg, int effects, int bg_color_or_index)
/* d[sub]image_{rgb16,p8,p4}_swapcolor(..., effects, source, replacement) */
DIMAGE_SIG(_swapcolor, int effects, int source, int replacement)
/* d[sub]image_{rgb16,p8,p4}_addbg(..., effects, bg_color) */
DIMAGE_SIG(_addbg, int effects, int bg_color)
/* d[sub]image_{rgb16,p8,p4}_dye(..., effects, dye_color) */
DIMAGE_SIG(_dye, int effects, int dye_color)
/* d[sub]image_p4_clearbg_alt(..., effects, bg_index)
This is functionally identical to CLEARBG, but it uses an alternative
rendering method that is faster for larger images with wide transparent
areas. You can swap it with the normal CLEARBG freely. */
DIMAGE_SIG1(p4_clearbg_alt, int effects, int bg_index)
#define dimage_rgb16_effect(x, y, img, eff, ...) \
dsubimage_rgb16_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
eff, ##__VA_ARGS__)
#define dimage_p8_effect(x, y, img, eff, ...) \
dsubimage_p8_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
eff, ##__VA_ARGS__)
#define dimage_p4_effect(x, y, img, eff, ...) \
dsubimage_p4_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
eff, ##__VA_ARGS__)
#undef DIMAGE_SIG
#undef DIMAGE_SIG1
//---
// Clipping utilities
//---
/* Double box specifying both a source and target area */
struct gint_image_box
{
/* Target location of top-left corner */
int x, y;
/* Width and height of rendered sub-image */
int w, h;
/* Source bounding box (low included, high excluded) */
int left, top;
};
/* Clip the provided box against the input. If, after clipping, the box no
longer intersects the output window, returns false. Otherwise, returns
true. */
bool gint_image_clip_input(image_t const *img, struct gint_image_box *box,
struct dwindow const *window);
/* Clip the provided box against the output. */
void gint_image_clip_output(struct gint_image_box *b,
struct dwindow const *window);
//---
// Internal image rendering routines
//
// The following functions (or non-functions) are implemented in assembler and
// make up the internal interface of the image renderer. If you just want to
// display images, use dimage() and variations; these are only useful if you
// have a different rendering system and wish to use image rendering with
// dynamic effects in it.
//---
/* Renderer command. This structure includes most of the information used by
the image renderer to perform blits. Some of the information on the target
is also passed as direct arguments, which is more convenient and slightly
faster.
Most of the values here can be set with gint_image_mkcmd(). The last two
members, along with the return values of the gint_image_FORMAT_loop()
functions, are used to update the command if one needs to draw *parts* of
the image and resume the rendering later. This is used in Azur. */
struct gint_image_cmd
{
/* Shader ID. This is used in Azur, and ignored in gint */
uint8_t shader_id;
/* Dynamic effects not already dispatched by renderer
Bit 0: VFLIP
Bit 1: HFLIP */
uint8_t effect;
/* Number of pixels to render per line. For formats that force either x
or width alignment (most of them), this is already adjusted to a
suitable multiple (usually a multiple of 2). */
int16_t columns;
/* Stride of the input image (number of pixels between each row), in
pixels, without subtracting the number of columns */
int16_t input_stride;
/* Number of lines in the command. This can be adjusted freely, and is
particularly useful in Azur for fragmented rendering. */
uint8_t lines;
/* [Any effect]: Offset of first edge */
int8_t edge_1;
/* Core loop; this is an internal label of the renderer */
void const *loop;
/* Output pixel array, offset by target x/y */
void const *output;
/* Input pixel array, offset by source x/y. For formats that force x
alignment, this is already adjusted. */
void const *input;
/* Palette, when applicable */
uint16_t const *palette;
/* [Any effect]: Offset of right edge */
int16_t edge_2;
/* [CLEARBG, SWAPCOLOR]: Source color */
uint16_t color_1;
/* [SWAPCOLOR]: Destination color */
uint16_t color_2;
/* Remaining height (for updates between fragments) */
int16_t height;
/* Local x position (for updates between fragments) */
int16_t x;
};
/* gint_image_mkcmd(): Prepare a rendering command with dynamic effects
This function crafts an image renderer command. It loads all the settings
except for effect-dependent parameters: the [.loop] label, the color section
of [.effect], and color effect settings. See the effect-specific functions
to see how they are defined.
The benefit of this approach is that the rendering code does not need to be
linked in unless an effect is actually used, which avoids blowing up the
size of the add-in as the number of support dynamic effects increases.
@box Requested on-screen box (will be clipped depending on effects)
@img Source image
@effects Set of dynamic effects to be applied, as an [IMAGE_*] bitmask
@left_edge Whether to force 2-alignment on the input (box->left)
@right_edge Whether to force 2-alignment on the width
@cmd Command to be filled
@window Rendering window (usually {0, 0, DWIDTH, DHEIGHT})
Returns false if there is nothing to render because of clipping (in which
case [cmd] is unchanged), true otherwise. [*box] is also updated to reflect
the final box after clipping but not accounting for edges. */
bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img,
int effects, bool left_edge, bool right_edge,
struct gint_image_cmd *cmd, struct dwindow const *window);
/* Entry point of the renderers. These functions can be called normally as long
as you can build the commands (eg. by using gint_image_mkcmd() then filling
the effect-specific information). */
void *gint_image_rgb16_loop (int output_width, struct gint_image_cmd *cmd);
void *gint_image_p8_loop (int output_width, struct gint_image_cmd *cmd);
void *gint_image_p4_loop (int output_width, struct gint_image_cmd *cmd);
/* Renderer fragments. The following can absolutely not be called from C code
as they aren't full functions (and this isn't their prototype). These are
continuations to be specified in the [.loop] field of a command before using
one of the functions above. */
void gint_image_rgb16_normal(void);
void gint_image_rgb16_clearbg(void);
void gint_image_rgb16_swapcolor(void);
void gint_image_rgb16_dye(void);
void gint_image_p8_normal(void);
void gint_image_p8_clearbg(void);
void gint_image_p8_swapcolor(void);
void gint_image_p8_dye(void);
void gint_image_p4_normal(void);
void gint_image_p4_clearbg(void);
void gint_image_p4_clearbg_alt(void);
void gint_image_p4_swapcolor(void);
void gint_image_p4_dye(void);
//---
// Image library utilities
//
// The following functions and macros are mostly internal utilities; they are
// exposed here in case user applications want to extend the set of image
// transforms with custom additions.
//---
/* image_target(): Check if an image can be used as target for a transform
This function is used to quickly check whether a transform from [src] to
[dst] is possible. It requires image_valid(src) and image_valid(dst), plus
any optional constraints specified as variadic arguments. These constraints
can be:
* NOT_P4: fails if [dst] is P4.
* DATA_RW: fails if [dst] is not data-writable.
* PALETTE_RW: fails if [dst] is not palette-writable.
* SAME_SIZE: fails if [dst] is not at least as large as [src].
For example, in image_hflip(), we write:
if(!image_target(src, dst, NOT_P4, DATA_RW, SAME_SIZE)) return; */
enum {
IMAGE_TARGET_NONE,
IMAGE_TARGET_NOT_P4,
IMAGE_TARGET_DATA_RW,
IMAGE_TARGET_PALETTE_RW,
IMAGE_TARGET_SAME_SIZE,
IMAGE_TARGET_SAME_FORMAT,
IMAGE_TARGET_SAME_DEPTH,
};
bool image_target(image_t const *src, image_t *dst, ...);
#define image_target(src, dst, ...) \
image_target(src, dst, image_target_arg1(__VA_ARGS__ __VA_OPT__(,) NONE))
#define image_target_arg1(c, ...) \
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg2(__VA_ARGS__))
#define image_target_arg2(c, ...) \
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg3(__VA_ARGS__))
#define image_target_arg3(c, ...) \
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg4(__VA_ARGS__))
#define image_target_arg4(c, ...) \
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg5(__VA_ARGS__))
#define image_target_arg5(c, ...) \
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg6(__VA_ARGS__))
#define image_target_arg6(c, ...) \
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_too_many_args(__VA_ARGS__))
/* image_alpha_2(): Conditional alpha */
#define image_alpha_2(fmt, copy_alpha) \
((copy_alpha) ? 0x10000 : image_alpha(fmt))
#endif /* GINT_RENDER_RGB */
#ifdef __cplusplus
}
#endif
#endif /* GINT_IMAGE */

View file

@ -41,8 +41,6 @@ enum {
INTC_DMA_DEI4, INTC_DMA_DEI4,
INTC_DMA_DEI5, INTC_DMA_DEI5,
INTC_DMA_DADERR, INTC_DMA_DADERR,
/* Serial Communication Interface with FIFO (SCIF0) */
INTC_SCIF0,
/* Real-Time Clock [RTC]; a single IPR covers all 3 interrupts */ /* Real-Time Clock [RTC]; a single IPR covers all 3 interrupts */
INTC_RTC_ATI, INTC_RTC_ATI,
INTC_RTC_PRI, INTC_RTC_PRI,
@ -132,11 +130,6 @@ void *intc_handler(int event_code, void const *handler, size_t size);
the numerous constraints of intc_handler(), at the cost of always going back the numerous constraints of intc_handler(), at the cost of always going back
to userspace and a small time overhead. to userspace and a small time overhead.
Since gint_inth_callback is used to do the heavy lifting of setting up a sane
context for C code, the gint_call_t object can be built with GINT_CALL_FLAG
if the called function expects a gint_inth_callback_context_t structure as its
first argument.
@event_code Identifier of the interrupt block @event_code Identifier of the interrupt block
@function Function to use as a handler @function Function to use as a handler
Returns true on success, false if the event code is invalid. */ Returns true on success, false if the event code is invalid. */

View file

@ -60,24 +60,6 @@ extern "C" {
* clearevents() reads all pending events from the input queue. * clearevents() reads all pending events from the input queue.
Games are often also interested in whether keys were pressed or released
_since the last frame_. gint doesn't know what a frame is, but you can track
key state flips by using cleareventflips() before reading events and
keypressed() and keyreleased() afterwards.
* keypressed() checks if the specified key is currently down and was up at
the time of the last cleareventflips().
* keyreleased() checks if the specified key is currently up and was down at
the time of the last cleareventflips().
A typical game loop might look like. cleareventflips() must be called
_before_ clearevents().
cleareventflips();
clearevents(); // or pollevent(), waitevent()
if(keydown(KEY_RIGHT)) move();
if(keypressed(KEY_F6)) open_inventory();
The previous functions are quite low-level. GUI programs that look like the The previous functions are quite low-level. GUI programs that look like the
system applications will prefer using a GetKey()-like functions that return system applications will prefer using a GetKey()-like functions that return
a single key press at a time, heeds for releases, for SHIFT and ALPHA a single key press at a time, heeds for releases, for SHIFT and ALPHA
@ -116,13 +98,13 @@ typedef struct
{ {
uint time :16; /* Time of event, unique over short periods */ uint time :16; /* Time of event, unique over short periods */
uint :2; /* Reserved for future use */ uint :3; /* Reserved for future use */
uint mod :1; /* Whether modifiers are used */ uint mod :1; /* Whether modifiers are used */
uint shift :1; /* If mod=1, whether SHIFT was pressed */ uint shift :1; /* If mod=1, whether SHIFT was pressed */
uint alpha :1; /* If mod=1, whether ALPHA was pressed */ uint alpha :1; /* If mod=1, whether ALPHA was pressed */
uint type :3; /* Type of key event */ uint type :2; /* Type of key event */
uint key :8; /* Hit key */ uint key :8; /* Hit key */
} GPACKED(4) key_event_t; } GPACKED(4) key_event_t;
@ -134,7 +116,6 @@ enum
KEYEV_DOWN = 1, /* Key was pressed */ KEYEV_DOWN = 1, /* Key was pressed */
KEYEV_UP = 2, /* Key was released */ KEYEV_UP = 2, /* Key was released */
KEYEV_HOLD = 3, /* A key that was pressed has been held down */ KEYEV_HOLD = 3, /* A key that was pressed has been held down */
KEYEV_OSMENU = 4, /* We went to the main menu and back */
}; };
/* Keyboard frequency analysis is a runtime setting since gint 2.4. This macro /* Keyboard frequency analysis is a runtime setting since gint 2.4. This macro
@ -181,12 +162,6 @@ key_event_t waitevent(volatile int *timeout);
/* clearevents(): Read all events waiting in the queue */ /* clearevents(): Read all events waiting in the queue */
void clearevents(void); void clearevents(void);
/* cleareventflips(): Set the time reference for keypressed()/keyreleased()
The two functions keypressed() and keyreleased() will use the keyboard state
at the time this function was called to determine whether any given key was
just pressed or jut released. */
void cleareventflips(void);
//--- //---
// Key state functions // Key state functions
//--- //---
@ -206,16 +181,6 @@ int keydown_all(int key1, ...);
sequence should be terminated by a 0 integer. */ sequence should be terminated by a 0 integer. */
int keydown_any(int key1, ...); int keydown_any(int key1, ...);
/* keypressed(): Check if a key was just pressed
This function returns non-zero if the specified key is currently down, *and*
it was up at the time of the last call to cleareventflips(). */
int keypressed(int key);
/* keyreleased(): Check if a key was just released
This function returns non-zero if the specified key is currently up, *and*
it was down at the time of the last call to cleareventflips(). */
int keyreleased(int key);
//--- //---
// High-level functions // High-level functions
//--- //---
@ -240,32 +205,30 @@ key_event_t getkey(void);
/* The following are the option bits for getkey_opt(). */ /* The following are the option bits for getkey_opt(). */
enum { enum {
/* Enable modifiers keys */ /* Enable modifiers keys */
GETKEY_MOD_SHIFT = 0x0001, GETKEY_MOD_SHIFT = 0x01,
GETKEY_MOD_ALPHA = 0x0002, GETKEY_MOD_ALPHA = 0x02,
/* SHIFT + OPTN (requires GETKEY_MOD_SHIFT) or LIGHT toggles backlight */ /* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
GETKEY_BACKLIGHT = 0x0004, GETKEY_BACKLIGHT = 0x04,
/* MENU triggers a task switch and displays the main menu */ /* MENU triggers a task switch and displays the main menu */
GETKEY_MENU = 0x0008, GETKEY_MENU = 0x08,
/* Repeat arrow keys, or even all keys */ /* Repeat arrow keys, or even all keys */
GETKEY_REP_ARROWS = 0x0010, GETKEY_REP_ARROWS = 0x10,
GETKEY_REP_ALL = 0x0020, GETKEY_REP_ALL = 0x20,
/* Enable custom repeat profiles; see getkey_set_repeat_profile() */ /* Enable custom repeat profiles; see getkey_set_repeat_profile() */
GETKEY_REP_PROFILE = 0x0040, GETKEY_REP_PROFILE = 0x40,
/* Enable application shortcuts; see getkey_set_feature_function() */ /* Enable application shortcuts; see getkey_set_feature_function() */
GETKEY_FEATURES = 0x0080, GETKEY_FEATURES = 0x80,
/* After coming back from the main menu, redraw the screen with dupdate */
GETKEY_MENU_DUPDATE = 0x0100,
/* After coming back from the main menu, send a KEYEV_OSMENU event */
GETKEY_MENU_EVENT = 0x0200,
/* Enable power off with SHIFT + AC/ON */
GETKEY_POWEROFF = 0x0400,
/* No modifiers */ /* No modifiers */
GETKEY_NONE = 0x0000, GETKEY_NONE = 0x00,
/* Default settings of getkey() */ /* Default settings of getkey() */
GETKEY_DEFAULT = 0x05df, GETKEY_DEFAULT = 0xdf,
}; };
/* getkey_profile_t: Custom repeat profile function
See getkey_set_repeat_profile() for details. */
typedef int (*getkey_profile_t)(int key, int duration, int count);
/* getkey_feature_t: Custom feature function /* getkey_feature_t: Custom feature function
See getkey_set_feature_function() for details. */ See getkey_set_feature_function() for details. */
typedef bool (*getkey_feature_t)(key_event_t event); typedef bool (*getkey_feature_t)(key_event_t event);
@ -283,20 +246,58 @@ typedef bool (*getkey_feature_t)(key_event_t event);
value whenever you want to interrupt the call; using a timer with value whenever you want to interrupt the call; using a timer with
[timer_timeout] as callback is suitable. See <gint/timer.h>. [timer_timeout] as callback is suitable. See <gint/timer.h>.
Event transforms in getkey_opt() (SHIFT, ALPHA and repetitions) are handled
by changing the transform settings on the keyboard device. These settings
are restored when getkey_opt() returns, so if they are originally disabled
(which they are unless set manually) then the status of the SHIFT and ALPHA
keys is lost between calls (this has an effect it getkey_opt() is
interrupted by timeout). Therefore, in order to use modifiers across several
calls to getkey_opt(), make sure to enable the transforms on the keyboard
device; see <gint/drivers/keydev.h> for details.
@options An or-combination of values from the GETKEY_* enumeration @options An or-combination of values from the GETKEY_* enumeration
@timeout Optional pointer to a timeout value @timeout Optional pointer to a timeout value
Returns a key event of type KEYEV_DOWN or KEYEV_HOLD with [mod=1]. */ Returns a key event of type KEYEV_DOWN or KEYEV_HOLD with [mod=1]. */
key_event_t getkey_opt(int options, volatile int *timeout); key_event_t getkey_opt(int options, volatile int *timeout);
/* getkey_repeat(): Set repeat delays for getkey()
This function updates the repeat delays of getkey() and getkey_opt(). The
unit of the argument is in milliseconds, but the granularity of the delay is
dependent on the keyboard scan frequency.
In the default setting (128 Hz scans), the possible repeat delays are
approximately 8 ms, 16 ms, 23 ms, 31 ms... the provided arguments will be
rounded to the closest feasible delays to ensure that repetitions are
perfectly regular, rather than approximating the requested frequency.
The system default is (500 ms, 125 ms). With the 128 Hz setting, this
default is reached exactly without approximation. gint's default is (400 ms,
40 ms) for more reactivity.
@first Delay between key press and first repeat (no more than one hour)
@next Delay between subsequent repeats (no more than one hour) */
void getkey_repeat(int first, int next);
/* getkey_repeat_profile(): Get the current repeat profile function */
getkey_profile_t getkey_repeat_profile(void);
/* getkey_set_repeat_profile(): Set the repeat profile function
The repeat profile is called by getkey() and getkey_opt() when a key is
pressed or held, and getkey() is planning to repeat it. The profile decides
whether such repetition is allowed, and if so, how long it shoud take. The
profile has access to the following information:
@key Key for which a repetition is being considered
@duration Duration since the key was first pressed (us)
@count Number of previous repeats (0 on the first call)
The profile function must either return a positive number of microseconds to
wait until the next repeat, or -1 to block the repeat indefinitely. Note
that the keyboard device typically updates every 7-8 ms, timings are tracked
in microseconds only to limit deviations.
Setting a repeat profile overrides GETKEY_REP_ARROWS, GETKEY_REP_ALL, and
the repeat delays. Calling with profile=NULL restores this behavior.
This mechanism replaces a "repeat filter" that existed until gint 2.4. The
main difference is that the repeat filter was called when the repeat event
arrived, whereas the repeat profile is called one repeat earlier to schedule
the repeat exactly when needed. */
void getkey_set_repeat_profile(getkey_profile_t profile);
/* getkey_feature_function(): Get the current feature function */ /* getkey_feature_function(): Get the current feature function */
getkey_feature_t getkey_feature_function(void); getkey_feature_t getkey_feature_function(void);
@ -336,32 +337,4 @@ int keycode_digit(int keycode);
} }
#endif #endif
//---
// Deprecated functions for repeat control
//
// The following types and functions have been deprecated. The handling of
// repeats is done inside the keyboard device (see <gint/drivers/keydev.h>).
// Until gint 2.9, the device had repeats disabled by default and getkey()
// enabled them. The problem is that repeats usually occur while getkey() is
// not running, so repeats wouldn't work as expected.
//
// Thus, getkey()-specific APIs related to repeat settings are now deprecated
// in favor of the ones provided by <gint/drivers/keydev.h>, which apply
// globally instead of just when getkey() is being run.
//---
typedef void *getkey_profile_t;
__attribute__((deprecated(
"Use keydev_set_standard_repeats(), which is permanent, instead")))
void getkey_repeat(int first, int next);
__attribute__((deprecated(
"Use keydev_transform(keydev_std()).repeater instead")))
getkey_profile_t getkey_repeat_profile(void);
__attribute__((deprecated(
"Set the [repeater] attribute with keydev_set_transform() instead")))
void getkey_set_repeat_profile(getkey_profile_t profile);
#endif /* GINT_KEYBOARD */ #endif /* GINT_KEYBOARD */

View file

@ -75,18 +75,6 @@ enum {
of the matrix one could use a ghosting effect to boot the calc. */ of the matrix one could use a ghosting effect to boot the calc. */
KEY_ACON = 0x07, KEY_ACON = 0x07,
/* Virtual key codes */
KEY_HELP = 0x20, /* fx-9860G Slim: 0x75 */
KEY_LIGHT = 0x10, /* fx-9860G Slim: 0x76 */
/* Key codes for the CP-400 */
KEY_KBD = 0xa1,
KEY_X = 0xa2,
KEY_Y = 0xa3,
KEY_Z = 0xa4,
KEY_EQUALS = 0xa5,
KEY_CLEAR = KEY_EXIT,
/* Key aliases (handle with care =D) */ /* Key aliases (handle with care =D) */
KEY_X2 = KEY_SQUARE, KEY_X2 = KEY_SQUARE,
KEY_CARET = KEY_POWER, KEY_CARET = KEY_POWER,

View file

@ -39,20 +39,6 @@ void *krealloc(void *ptr, size_t size);
/* kfree(): Free memory allocated with kalloc() */ /* kfree(): Free memory allocated with kalloc() */
void kfree(void *ptr); void kfree(void *ptr);
/* kmalloc_max(): Allocate the largest block available in an arena
This function is like kmalloc(), but it find the largest block in the arena
and returns it whole after setting its size in *size. This is useful if you
need memory to manage with another allocator, or you don't yet know the size
of the data to be generated. The block can later be shrunk with realloc().
Currently only gint-managed arenas support this operation.
@size Will be set to size of returned block (if there is one)
@arean_name Name of arena to allocate in (*cannot* be NULL)
Returns the address of the largest block available, NULL on error. */
void *kmalloc_max(size_t *size, char const *arena_name);
//--- //---
// Extension API for new areas and statistics // Extension API for new areas and statistics
//--- //---
@ -67,8 +53,6 @@ typedef struct {
void * (*realloc)(void *ptr, size_t newsize, void *data); void * (*realloc)(void *ptr, size_t newsize, void *data);
/* kfree() handles ptr == NULL*/ /* kfree() handles ptr == NULL*/
void (*free)(void *ptr, void *data); void (*free)(void *ptr, void *data);
/* kmalloc_max() backend */
void * (*malloc_max)(size_t *size, void *data);
/* Name, should be unique; gint reserves names starting with "_" */ /* Name, should be unique; gint reserves names starting with "_" */
char const *name; char const *name;
@ -108,11 +92,6 @@ void kmalloc_init_arena(kmalloc_arena_t *a, bool enable_statistics);
success, false if the maximum number of arenas has been reached. */ success, false if the maximum number of arenas has been reached. */
bool kmalloc_add_arena(kmalloc_arena_t *arena); bool kmalloc_add_arena(kmalloc_arena_t *arena);
/* kmalloc_remove_arena(): Remove an arena from the heap source
Removes an arena from the heap source. The arena should certainly be empty,
although this function will not check that. */
void kmalloc_remove_arena(kmalloc_arena_t *arena);
//--- //---
// Internal functions // Internal functions
//--- //---

View file

@ -10,7 +10,6 @@ extern "C" {
#endif #endif
#include <gint/mpu/mmu.h> #include <gint/mpu/mmu.h>
#include <stdbool.h>
//--- //---
// Unified interface // Unified interface
@ -41,17 +40,6 @@ void *mmu_uram(void);
fx-9860G, and 512k on fx-CG 50. */ fx-9860G, and 512k on fx-CG 50. */
uint32_t mmu_uram_size(void); uint32_t mmu_uram_size(void);
/* mmu_is_rom(): Determine if an address points to ROM
Checks whether the supplied pointer points to ROM or to a virtualized
portion of ROM. For the sake of efficiency, this function uses heuristics
about the structure of P0 rather than actually checking the TLB.
This is useful during filesystem accesses because only data outside of ROM
can be written to files. Pointers for which this function returns true
cannot be used as a source for BFile_Write(). */
bool mmu_is_rom(void const *ptr);
//--- //---
// SH7705 TLB // SH7705 TLB
//--- //---
@ -89,7 +77,7 @@ uint32_t tlb_translate(uint32_t page, uint32_t *size);
/* utlb_addr() - get the P4 address of a UTLB address entry /* utlb_addr() - get the P4 address of a UTLB address entry
@E Entry number (should be in range 0..63) @E Entry number (should be in range 0..63)
Returns a pointer to the entry. */ Returns a pointer to the entry. */
utlb_addr_t const *utlb_addr(uint E); const utlb_addr_t *utlb_addr(uint E);
/* utlb_data() - get the P4 address of a UTLB data entry /* utlb_data() - get the P4 address of a UTLB data entry
@E Entry number (should be in range 0..63) @E Entry number (should be in range 0..63)
@ -110,16 +98,6 @@ void utlb_mapped_memory(uint32_t *rom, uint32_t *ram);
/* utlb_translate(): Get the physical address for a virtual page */ /* utlb_translate(): Get the physical address for a virtual page */
uint32_t utlb_translate(uint32_t page, uint32_t *size); uint32_t utlb_translate(uint32_t page, uint32_t *size);
/* itlb_addr(): Get the P4 address of an ITLB address entry
@E Entry number (0..3)
Returns a pointer to the entry. */
itlb_addr_t const *itlb_addr(uint E);
/* itlb_data(): Get the P4 address of an ITLB data entry
@E Entry number (0..3)
Returns a pointer to the entry. */
itlb_data_t const *itlb_data(uint E);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -1,224 +0,0 @@
//---
// gint:mpu:bsc - Bus State Controller
//---
#ifndef GINT_MPU_BSC
#define GINT_MPU_BSC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 But State Controller. Refer to:
// Renesas SH7705 Group Hardware Manual
// Section 7: Bus State Controller (BSC)
//---
typedef volatile lword_union(sh7705_bsc_CSnBCR_t,
uint32_t :2;
uint32_t IWW :2; /* Wait cycles for Write-Read and Write-Write */
uint32_t :1;
uint32_t IWRWD :2; /* Wait cycles for other-space Read-Write */
uint32_t :1;
uint32_t IWRWS :2; /* Wait cycles for same-space Read-Write */
uint32_t :1;
uint32_t IWRRD :2; /* Wait cycles for other-space Read-Read */
uint32_t :1;
uint32_t IWRRS :2; /* Wait cycles for same-space Read-Read */
uint32_t :1;
uint32_t TYPE :3; /* Memory type */
uint32_t :1;
uint32_t BSZ :2; /* Data bus size */
uint32_t :9;
);
/* Warning: the layout of this register changes with n *and* with the memory
type. This version is not exhaustive. Check the manual! */
typedef volatile lword_union(sh7705_bsc_CSnWCR_t,
uint32_t :13;
uint32_t WW :3; /* Write access wait cycles */
uint32_t :3;
uint32_t SW :2; /* Wait from CSn/address to RD/WEn assertion */
uint32_t WR :4; /* Access wait cycles */
uint32_t WM :1; /* Whether to use external wait */
uint32_t :4;
uint32_t HW :2; /* Wait from RD/WEn to CSn/address negation */
);
typedef volatile struct
{
lword_union(CMNCR,
uint32_t :24;
uint32_t DMAIW :2; /* DMA single-address wait states */
uint32_t DMAIWA :1; /* DMAIW wait states insertion method */
uint32_t :1;
uint32_t const ENDIAN :1; /* Global CPU endianness flag */
uint32_t :1;
uint32_t HIZMEM :1; /* High-Z memory Control*/
uint32_t HIZCNT :1; /* High-Z Control*/
);
sh7705_bsc_CSnBCR_t CS0BCR;
sh7705_bsc_CSnBCR_t CS2BCR;
sh7705_bsc_CSnBCR_t CS3BCR;
sh7705_bsc_CSnBCR_t CS4BCR;
sh7705_bsc_CSnBCR_t CS5ABCR;
sh7705_bsc_CSnBCR_t CS5BBCR;
sh7705_bsc_CSnBCR_t CS6ABCR;
sh7705_bsc_CSnBCR_t CS6BBCR;
sh7705_bsc_CSnWCR_t CS0WCR;
sh7705_bsc_CSnWCR_t CS2WCR;
sh7705_bsc_CSnWCR_t CS3WCR;
sh7705_bsc_CSnWCR_t CS4WCR;
sh7705_bsc_CSnWCR_t CS5AWCR;
sh7705_bsc_CSnWCR_t CS5BWCR;
sh7705_bsc_CSnWCR_t CS6AWCR;
sh7705_bsc_CSnWCR_t CS6BWCR;
/* TODO: There are more registers (not involved in overclocking). */
} GPACKED(4) sh7705_bsc_t;
#define SH7705_BSC (*(sh7705_bsc_t *)0xa4fd0000)
//---
// SH7305 But State Controller. Refer to:
// Renesas SH7730 Group Hardware Manual
// Section 11: Bus State Controller (BSC)
//---
typedef volatile lword_union(sh7305_bsc_CSnBCR_t,
uint32_t :1;
uint32_t IWW :3;
uint32_t IWRWD :3;
uint32_t IWRWS :3;
uint32_t IWRRD :3;
uint32_t IWRRS :3;
uint32_t TYPE :4;
uint32_t :1;
uint32_t BSZ :2;
uint32_t :9;
);
typedef volatile lword_union(sh7305_bsc_CSnWCR_06A6B_t,
uint32_t :11;
uint32_t BAS :1;
uint32_t :1;
uint32_t WW :3;
uint32_t ADRSFIX:1;
uint32_t :2;
uint32_t SW :2;
uint32_t WR :4;
uint32_t WM :1;
uint32_t :4;
uint32_t HW :2;
);
typedef volatile lword_union(sh7305_bsc_CSnWCR_45A5B_t,
uint32_t :11;
uint32_t BAS :1;
uint32_t :1;
uint32_t WW :3;
uint32_t :3;
uint32_t SW :2;
uint32_t WR :4;
uint32_t WM :1;
uint32_t :4;
uint32_t HW :2;
);
typedef volatile struct
{
lword_union(CMNCR,
uint32_t :6;
uint32_t CKOSTP :1;
uint32_t CKODRV :1;
uint32_t :7;
uint32_t DMSTP :1;
uint32_t :1;
uint32_t BSD :1;
uint32_t MAP :2;
uint32_t BLOCK :1;
uint32_t :7;
uint32_t ENDIAN :1;
uint32_t :1;
uint32_t HIZMEM :1;
uint32_t HIZCNT :1;
);
sh7305_bsc_CSnBCR_t CS0BCR;
sh7305_bsc_CSnBCR_t CS2BCR;
sh7305_bsc_CSnBCR_t CS3BCR;
sh7305_bsc_CSnBCR_t CS4BCR;
sh7305_bsc_CSnBCR_t CS5ABCR;
sh7305_bsc_CSnBCR_t CS5BBCR;
sh7305_bsc_CSnBCR_t CS6ABCR;
sh7305_bsc_CSnBCR_t CS6BBCR;
sh7305_bsc_CSnWCR_06A6B_t CS0WCR;
lword_union(CS2WCR,
uint32_t :8;
uint32_t BW :2;
uint32_t PMD :1;
uint32_t BAS :1;
uint32_t :1;
uint32_t WW :3;
uint32_t :3;
uint32_t SW :2;
uint32_t WR :4;
uint32_t WM :1;
uint32_t :4;
uint32_t HW :2;
);
lword_union(CS3WCR,
uint32_t :17;
uint32_t TRP :2;
uint32_t :1;
uint32_t TRCD :2;
uint32_t :1;
uint32_t A3CL :2;
uint32_t :2;
uint32_t TRWL :2;
uint32_t :1;
uint32_t TRC :2;
);
sh7305_bsc_CSnWCR_45A5B_t CS4WCR;
sh7305_bsc_CSnWCR_45A5B_t CS5AWCR;
sh7305_bsc_CSnWCR_45A5B_t CS5BWCR;
sh7305_bsc_CSnWCR_06A6B_t CS6AWCR;
sh7305_bsc_CSnWCR_06A6B_t CS6BWCR;
lword_union(SDCR,
uint32_t :11;
uint32_t A2ROW :2;
uint32_t :1;
uint32_t A2COL :2;
uint32_t :4;
uint32_t RFSH :1;
uint32_t RMODE :1;
uint32_t PDOWN :1;
uint32_t BACTV :1;
uint32_t :3;
uint32_t A3ROW :2;
uint32_t :1;
uint32_t A3COL :2;
);
uint32_t RTCSR;
uint32_t RTCNT;
uint32_t RTCOR;
} GPACKED(4) sh7305_bsc_t;
#define SH7305_BSC (*(sh7305_bsc_t *)0xfec10000)
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_BSC */

View file

@ -32,12 +32,6 @@ typedef volatile struct
uint16_t PFC :2; /* Peripheral clock divider */ uint16_t PFC :2; /* Peripheral clock divider */
); );
byte_union(UCLKCR,
uint8_t USSCS :2; /* Source Clock Selection Bit */
uint8_t USBEN :1; /* USB On-Chip Oscillator Enable */
uint8_t :5;
);
} GPACKED(4) sh7705_cpg_t; } GPACKED(4) sh7705_cpg_t;
#define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80)) #define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80))
@ -53,7 +47,7 @@ typedef volatile struct
documentation. */ documentation. */
typedef volatile struct typedef volatile struct
{ {
lword_union(FRQCR, lword_union(FRQCRA,
uint32_t KICK :1; /* Flush FRQCRA modifications */ uint32_t KICK :1; /* Flush FRQCRA modifications */
uint32_t :1; uint32_t :1;
uint32_t STC :6; /* PLL multiplication [*] */ uint32_t STC :6; /* PLL multiplication [*] */
@ -100,9 +94,7 @@ typedef volatile struct
uint32_t CKOFF :1; /* CKO Output Stop */ uint32_t CKOFF :1; /* CKO Output Stop */
uint32_t :1; uint32_t :1;
); );
pad(0x14);
uint32_t PLL2CR;
pad(0x10);
lword_union(SPUCLKCR, lword_union(SPUCLKCR,
uint32_t :23; uint32_t :23;
@ -125,9 +117,6 @@ typedef volatile struct
uint32_t :3; uint32_t :3;
uint32_t FLF :11; /* FLL Multiplication Ratio */ uint32_t FLF :11; /* FLL Multiplication Ratio */
); );
pad(0x0c);
uint32_t LSTATS;
} GPACKED(4) sh7305_cpg_t; } GPACKED(4) sh7305_cpg_t;

View file

@ -298,8 +298,8 @@ typedef struct
//--- //---
/* Provided by intc/intc.c */ /* Provided by intc/intc.c */
extern sh7705_intc_t const SH7705_INTC; extern sh7705_intc_t SH7705_INTC;
extern sh7305_intc_t const SH7305_INTC; extern sh7305_intc_t SH7305_INTC;
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -49,10 +49,12 @@ typedef struct
} GPACKED(4) tlb_data_t; } GPACKED(4) tlb_data_t;
//--- //---
// SH7305 TLB. Refer to SH4AL-DSP manual, section 7 (MMU) // SH7305 TLB. Refer to:
// "Renesas SH7724 User's Manual: Hardware"
// Section 7: "Memory Management Unit (MMU)"
//--- //---
/* utlb_addr_t: Address part of a UTLB entry */ /* utlb_addr_t - address part of a UTLB entry */
typedef struct typedef struct
{ {
uint VPN :22; uint VPN :22;
@ -62,7 +64,7 @@ typedef struct
} GPACKED(4) utlb_addr_t; } GPACKED(4) utlb_addr_t;
/* utlb_data_t: Data part of a UTLB entry */ /* utlb_data_t - data part of a UTLB entry */
typedef struct typedef struct
{ {
uint :3; uint :3;
@ -79,34 +81,6 @@ typedef struct
} GPACKED(4) utlb_data_t; } GPACKED(4) utlb_data_t;
/* itlb_addr_t: Address part of an ITLB entry */
typedef struct
{
uint VPN :22;
uint :1;
uint V :1;
uint ASID :8;
} GPACKED(4) itlb_addr_t;
/* itlb_data_t: Data part of an ITLB entry */
typedef struct
{
uint :3;
uint PPN :19;
uint :1;
uint V :1;
uint SZ1 :1;
uint PR :1;
uint :1;
uint SZ0 :1;
uint C :1;
uint :1;
uint SH :1;
uint :1;
} GPACKED(4) itlb_data_t;
typedef volatile struct typedef volatile struct
{ {
lword_union(PTEH, lword_union(PTEH,

View file

@ -76,162 +76,9 @@ typedef volatile struct
#define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100)) #define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100))
//--- //---
// SH7305 Pin Function Controller. // TODO: Document the SH7305 Pin Function Controller
// This is somewhat reminiscent of the SH7724, but there are many differences.
// The official emulator is the basis for this module.
//--- //---
/* All port control registers (PCRx) have 2-bit entries specifying 4 pin modes:
00: Special functions
01: Output
10: Input (pull-up ON)
11: Input (pull-up OFF)
Some pins do not accept all settings. The SH7724 has these exceptions:
- PGCR (all pins): Input now allowed (00 and 01 only)
- PJCR.P5MD/P6MD/P7MD: Input not allowed (00 and 01 only)
Also, some entries are not configurable or are not mapped to actual pins.
Again the SH7724 has these exceptions:
- PGCR.P6MD/P7MD: No setting
- PJCR.P4MD: No setting
- PSCR.P7MD: No setting */
typedef volatile word_union(sh7305_pfc_control_t,
uint16_t P7MD :2;
uint16_t P6MD :2;
uint16_t P5MD :2;
uint16_t P4MD :2;
uint16_t P3MD :2;
uint16_t P2MD :2;
uint16_t P1MD :2;
uint16_t P0MD :2;
);
/* Data register; a plain 8-bit value. Each bit can be either read or written
depending on the mode of the corresponding pin. */
typedef volatile byte_union(sh7305_pfc_data_t,
uint8_t P7DT :1;
uint8_t P6DT :1;
uint8_t P5DT :1;
uint8_t P4DT :1;
uint8_t P3DT :1;
uint8_t P2DT :1;
uint8_t P1DT :1;
uint8_t P0DT :1;
);
typedef volatile struct
{
sh7305_pfc_control_t
PACR, PBCR, PCCR, PDCR, PECR, PFCR, PGCR, PHCR,
PJCR, PKCR, PLCR, PMCR, PNCR, PQCR, PRCR, PSCR;
sh7305_pfc_data_t PADR;
pad(1);
sh7305_pfc_data_t PBDR;
pad(1);
sh7305_pfc_data_t PCDR;
pad(1);
sh7305_pfc_data_t PDDR;
pad(1);
sh7305_pfc_data_t PEDR;
pad(1);
sh7305_pfc_data_t PFDR;
pad(1);
sh7305_pfc_data_t PGDR;
pad(1);
sh7305_pfc_data_t PHDR;
pad(1);
sh7305_pfc_data_t PJDR;
pad(1);
sh7305_pfc_data_t PKDR;
pad(1);
sh7305_pfc_data_t PLDR;
pad(1);
sh7305_pfc_data_t PMDR;
pad(1);
sh7305_pfc_data_t PNDR;
pad(1);
sh7305_pfc_data_t PQDR;
pad(1);
sh7305_pfc_data_t PRDR;
pad(1);
sh7305_pfc_data_t PSDR;
pad(1);
sh7305_pfc_control_t PTCR;
sh7305_pfc_control_t PUCR;
sh7305_pfc_control_t PVCR;
pad(6);
sh7305_pfc_control_t PPCR;
/* PSEM*: Multiplexing pin settings. These are highly MPU-dependent and
the assignment is basically unknown.
HIZCR*: High-impedance settings for pins' I/O buffers. */
uint16_t PSELA;
uint16_t PSELB;
uint16_t PSELC;
uint16_t PSELD;
uint16_t PSELE;
uint16_t HIZCRA;
uint16_t HIZCRB;
uint16_t HIZCRC;
uint16_t PSELF;
sh7305_pfc_data_t PTDR;
pad(1);
sh7305_pfc_data_t PUDR;
pad(1);
sh7305_pfc_data_t PVDR;
pad(5);
sh7305_pfc_data_t PPDR;
pad(0x15);
/* Module function selection registers.
WARNING: These are the SH7724 bits, not necessarily the SH7305! */
word_union(MSELCRA,
uint16_t :8;
uint16_t UNKNOWN_USB :2;
uint16_t :6;
);
word_union(MSELCRB,
uint16_t XTAL_USB :2;
uint16_t :6;
uint16_t SCIF2_PORT :1;
uint16_t :1;
uint16_t SCIF3_PORT :1;
uint16_t :3;
uint16_t LDC_VSYNC_DIR :1;
uint16_t :1;
);
// TODO: Doc
uint16_t DRVCRD, DRVCRA, DRVCRB, DRVCRC;
pad(4);
/* Pull-up control registers. Not sure what these are since that's
normally handled in the modes for P*CR? */
uint8_t PULCRA, PULCRB, PULCRC, PULCRD, PULCRE, PULCRF, PULCRG,
PULCRH, PULCRJ, PULCRK, PULCRL, PULCRM, PULCRN, PULCRQ,
PULCRR, PULCRS;
pad(0x20);
uint8_t PULCRT;
uint8_t PULCRU;
uint8_t PULCRV;
uint8_t PULCRBSC;
pad(1);
uint8_t PULCRTRST;
uint8_t PULCRP;
pad(1);
uint16_t PSELG;
pad(12);
uint16_t PSELH;
} sh7305_pfc_t;
#define SH7305_PFC (*((sh7305_pfc_t *)0xa4050100))
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -1,218 +0,0 @@
//---
// gint:mpu:scif - Serial Communication Interface with FIFO (SCIF)
//---
#ifndef GINT_MPU_SCIF
#define GINT_MPU_SCIF
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 SCIF (from: SH7705 manual, section 16)
//---
typedef volatile struct
{
/* Serial Mode Register */
word_union(SCSMR,
uint16_t :5;
uint16_t SRC :3; /* Sampling Control */
uint16_t CA :1; /* Communication Mode */
uint16_t CHR :1; /* Character Length */
uint16_t PE :1; /* Parity Enable */
uint16_t OE :1; /* Parity Mode */
uint16_t STOP :1; /* Stop Bit Length */
uint16_t :1;
uint16_t CKS :2; /* Clock Select */
);
pad(0x2);
/* Bit Rate Register */
uint8_t SCBRR;
pad(0x3);
/* Serial Control Register */
word_union(SCSCR,
uint16_t :4;
uint16_t TSIE :1; /* Transmit Data Stop Interrupt Enable */
uint16_t ERIE :1; /* Receive Error Interrupt Enable */
uint16_t BRIE :1; /* Break Interrupt Enable */
uint16_t DRIE :1; /* Receive Data Ready Interrupt Enable */
uint16_t TIE :1; /* Transmit Interrupt Enable */
uint16_t RIE :1; /* Receive Interrupt Enable */
uint16_t TE :1; /* Transmit Enable */
uint16_t RE :1; /* Receive Mode */
uint16_t :2;
uint16_t CKE :2; /* Clock Enable */
);
pad(0x2);
/* Transmit Data Stop Register */
uint8_t SCTDSR;
pad(0x3);
/* FIFO Error Count Register */
word_union(SCFER,
uint16_t :2;
uint16_t PER :6; /* Parity Error Count */
uint16_t :2;
uint16_t FER :6; /* Framing Error Count */
);
pad(0x2);
/* Serial Status Register */
word_union(SCSSR,
uint16_t :6;
uint16_t ORER :1; /* Overrun Error */
uint16_t TSF :1; /* Transmit Data Stop */
uint16_t ER :1; /* Receive Error */
uint16_t TEND :1; /* Transmit End */
uint16_t TDFE :1; /* Transmit FIFO Data Empty */
uint16_t BRK :1; /* Break Detect */
uint16_t FER :1; /* Framing Error */
uint16_t PER :1; /* Parity Error */
uint16_t RDF :1; /* Receive FIFO Data Full */
uint16_t DR :1; /* Receive Data Ready */
);
pad(0x2);
/* FIFO Control Register */
word_union(SCFCR,
uint16_t TSE :1; /* Transmit Data Stop Enable */
uint16_t TCRST :1; /* Transmit Count Reset */
uint16_t :3;
uint16_t RSTRG :3; /* RTS Output Active Trigger */
uint16_t RTRG :2; /* Receive FIFO Data Number Trigger */
uint16_t TTRG :2; /* Transmit FIFO Data Number Trigger */
uint16_t MCE :1; /* Modem Control Enable */
uint16_t TFRST :1; /* Transmit FIFO Data Register Reset */
uint16_t RFRST :1; /* Receive FIFO Data Register Reset */
uint16_t LOOP :1; /* Loopback Test */
);
pad(0x2);
/* FIFO Data Count Register */
word_union(SCFDR,
uint16_t :1;
uint16_t T :7; /* Pending bytes in transmit FIFO */
uint16_t :1;
uint16_t R :7; /* Received bytes in receive FIFO */
);
pad(0x2);
/* Serial FIFO Transmit Data Register (64 bytes rolling register) */
uint8_t SCFTDR;
pad(0x3);
/* Serial FIFO Receive Data Register (64 bytes rolling register) */
uint8_t SCFRDR;
} GPACKED(4) sh7705_scif_t;
#define SH7705_SCIF (*((sh7705_scif_t *)0xa4410000))
//---
// SH7305 SCIF (from: SH7305 emulator and SH7730 SCIF)
// The module is very close to the SH7724 but it has a couple extra bits in
// SCFCR, which shows that it is closer to the SH7730 SCIF.
//---
typedef volatile struct
{
/* Serial Mode Register */
word_union(SCSMR,
uint16_t :8;
uint16_t CA :1; /* Communication Mode */
uint16_t CHR :1; /* Character Length */
uint16_t PE :1; /* Parity Enable */
uint16_t OE :1; /* Parity Mode */
uint16_t STOP :1; /* Stop Bit Length */
uint16_t :1;
uint16_t CKS :2; /* Clock Select */
);
pad(0x2);
/* Serial Bit Rate Register */
uint8_t SCBRR;
pad(0x3);
/* Serial Control Register */
word_union(SCSCR,
uint16_t :8;
uint16_t TIE :1; /* Transmit Interrupt Enable */
uint16_t RIE :1; /* Receive Interrupt Enable */
uint16_t TE :1; /* Transmit Enable */
uint16_t RE :1; /* Receive Mode */
uint16_t REIE :1; /* Receive Error Interrupt Enable */
uint16_t :1;
uint16_t CKE :2; /* Clock Enable */
);
pad(0x2);
/* Serial FIFO Transmit Data Register */
uint8_t SCFTDR;
pad(0x3);
/* Serial FIFO Status Register */
word_union(SCFSR,
uint16_t const PERC :4; /* Number of Parity Errors */
uint16_t const FERC :4; /* Number of Framing Errors */
uint16_t ER :1; /* Receive Error */
uint16_t TEND :1; /* Transmit End */
uint16_t TDFE :1; /* Transmit FIFO Data Empty */
uint16_t BRK :1; /* Break Detection */
uint16_t const FER :1; /* Framing Error */
uint16_t const PER :1; /* Parity Error */
uint16_t RDF :1; /* Receive FIFO Data Full */
uint16_t DR :1; /* Data Ready */
);
pad(0x2);
/* Serial FIFO Receive Data Register */
uint8_t SCFRDR;
pad(0x3);
/* Serial FIFO Control Register */
word_union(SCFCR,
uint16_t :5;
uint16_t RSTRG :3; /* RTS Output Active Trigger */
uint16_t RTRG :2; /* Receive FIFO Data Trigger */
uint16_t TTRG :2; /* Transmit FIFO Data Trigger */
uint16_t MCE :1; /* Model Control Enable */
uint16_t TFRST :1; /* Transmit FIFO Data Register Reset */
uint16_t RFRST :1; /* Receive FIFO Data Register Reset */
uint16_t LOOP :1; /* Loopback Test */
);
pad(0x2);
/* Serial FIFO Data Count Register */
word_union(SCFDR,
uint16_t :3;
uint16_t TFDC :5; /* Number of Data Bytes in Transmit FIFO */
uint16_t :3;
uint16_t RFDC :5; /* Number of Data Bytes in Receive FIFO */
);
pad(0x6);
/* Serial Line Status */
word_union(SCLSR,
uint16_t :15;
uint16_t ORER :1; /* Overrun Error */
);
pad(0x2);
} GPACKED(4) sh7305_scif_t;
#define SH7305_SCIF (*((sh7305_scif_t *)0xa4410000))
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_SCIF */

View file

@ -1,90 +0,0 @@
//---
// gint:mpu:ubc - User Break Controller
//---
#ifndef GINT_MPU_UBC
#define GINT_MPU_UBC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
typedef volatile struct
{
lword_union(CBR0, /* Match condition setting 0 */
uint32_t MFE :1; /* Match Flag Enable */
uint32_t AIE :1; /* ASID Enable */
uint32_t MFI :6; /* Match Flag Specify */
uint32_t AIV :8; /* ASID Specify */
uint32_t :1;
uint32_t SZ :3; /* Operand Size Select */
uint32_t :4;
uint32_t CD :2; /* Bus Select */
uint32_t ID :2; /* Ins. Fetch / Operand Access Select */
uint32_t :1;
uint32_t RW :2; /* Bus Command Select */
uint32_t CE :1; /* Channel Enable */
);
lword_union(CRR0, /* Match operation setting 0 */
uint32_t :18;
uint32_t _1 :1; /* Always set to 1 */
uint32_t :11;
uint32_t PCB :1; /* PC Break Select */
uint32_t BIE :1; /* Break Enable */
);
uint32_t CAR0; /* Match address setting 0 */
uint32_t CAMR0; /* Match address mask setting 0 */
pad(0x10);
lword_union(CBR1, /* Match condition setting 1 */
uint32_t MFE :1; /* Match Flag Enable */
uint32_t AIE :1; /* ASID Enable */
uint32_t MFI :6; /* Match Flag Specify */
uint32_t AIV :8; /* ASID Specify */
uint32_t DBE :1; /* Data Value Enable */
uint32_t SZ :3; /* Operand Size Select */
uint32_t ETBE :1; /* Execution Count Value Enable */
uint32_t :3;
uint32_t CD :2; /* Bus Select */
uint32_t ID :2; /* Ins. Fetch / Operand Access Select */
uint32_t :1;
uint32_t RW :2; /* Bus Command Select */
uint32_t CE :1; /* Channel Enable */
);
lword_union(CRR1, /* Match operation setting 1 */
uint32_t :18;
uint32_t _1 :1; /* Always set to 1 */
uint32_t :11;
uint32_t PCB :1; /* PC Break Select */
uint32_t BIE :1; /* Break Enable */
);
uint32_t CAR1; /* Match address setting 1 */
uint32_t CAMR1; /* Match address mask setting 1 */
uint32_t CDR1; /* Match data setting 1 */
uint32_t CDMR1; /* Match data mask setting 1 */
lword_union(CETR1, /* Execution count break 1 */
uint32_t :20;
uint32_t CET :12; /* Execution Count */
);
pad(0x5c4);
lword_union(CCMFR, /* Channel match flag */
uint32_t :30;
uint32_t MF1 :1; /* Channel 1 Condition Match Flag */
uint32_t MF0 :1; /* Channel 0 Condition Match Flag */
);
pad(0x1c);
lword_union(CBCR, /* Break control */
uint32_t :31;
uint32_t UBDE :1; /* User Break Debugging Support Function Enable */
);
} GPACKED(4) sh7305_ubc_t;
#define SH7305_UBC (*(sh7305_ubc_t *)0xff200000)
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_UBC */

View file

@ -11,19 +11,6 @@ extern "C" {
#include <gint/defs/types.h> #include <gint/defs/types.h>
typedef struct
{
word_union(TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
} sh7305_usb_pipetr;
typedef volatile struct typedef volatile struct
{ {
/* System Configuration Control Register */ /* System Configuration Control Register */
@ -41,16 +28,12 @@ typedef volatile struct
/* CPU Bus Wait Setting Register */ /* CPU Bus Wait Setting Register */
word_union(BUSWAIT, word_union(BUSWAIT,
uint16_t :8; uint16_t :12;
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t :1;
uint16_t _2 :1; /* Unknown role; can be set */
uint16_t :1;
uint16_t BWAIT :4; /* Bus Wait */ uint16_t BWAIT :4; /* Bus Wait */
); );
/* System Configuration Status Register */ /* System Configuration Status Register */
const word_union(SYSSTS, word_union(SYSSTS,
uint16_t :14; uint16_t :14;
uint16_t LNST :2; /* Line Status */ uint16_t LNST :2; /* Line Status */
); );
@ -58,9 +41,7 @@ typedef volatile struct
/* Device State Control Register */ /* Device State Control Register */
word_union(DVSTCTR, word_union(DVSTCTR,
uint16_t _1 :1; /* Unknown role; can be set */ uint16_t :7;
uint16_t :2;
uint16_t _2 :4; /* Unknown role; can be set */
uint16_t WKUP :1; /* Wakeup Output */ uint16_t WKUP :1; /* Wakeup Output */
uint16_t RWUPE :1; /* Wakeup Detection Enable */ uint16_t RWUPE :1; /* Wakeup Detection Enable */
uint16_t USBRT :1; /* USB Reset Output */ uint16_t USBRT :1; /* USB Reset Output */
@ -111,9 +92,8 @@ typedef volatile struct
uint16_t DREQE :1; /* DMA Transfert Request Enable */ uint16_t DREQE :1; /* DMA Transfert Request Enable */
uint16_t MBW :2; /* Access Bits Width */ uint16_t MBW :2; /* Access Bits Width */
uint16_t :1; uint16_t :1;
uint16_t BIGEND :1; /* Endian Control */ uint16_t BIGEND :1; /* Endiant Control */
uint16_t _1 :1; /* Unknown role; can be set */ uint16_t :4;
uint16_t :3;
uint16_t CURPIPE:4; /* Port Access Pipe Specification */ uint16_t CURPIPE:4; /* Port Access Pipe Specification */
); );
word_union(D0FIFOCTR, word_union(D0FIFOCTR,
@ -131,8 +111,7 @@ typedef volatile struct
uint16_t MBW :2; /* Access Bits Width */ uint16_t MBW :2; /* Access Bits Width */
uint16_t :1; uint16_t :1;
uint16_t BIGEND :1; /* Endian Control */ uint16_t BIGEND :1; /* Endian Control */
uint16_t _1 :1; /* Unknown role; can be set */ uint16_t :4;
uint16_t :3;
uint16_t CURPIPE:4; /* Port Access Pipe Specification */ uint16_t CURPIPE:4; /* Port Access Pipe Specification */
); );
word_union(D1FIFOCTR, word_union(D1FIFOCTR,
@ -155,26 +134,50 @@ typedef volatile struct
uint16_t BRDYE :1; /* Buffer Ready */ uint16_t BRDYE :1; /* Buffer Ready */
uint16_t :8; uint16_t :8;
); );
word_union(INTENB1, pad(4);
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t BCHGE :1; /* Bus Change */
uint16_t :1;
uint16_t DTCHE :1; /* Disconnection Detection */
uint16_t ATTCHE :1; /* Connection Detection */
uint16_t :4;
uint16_t EOFERRE:1; /* EOF Error Detection */
uint16_t SIGNE :1; /* Setup Transaction Error */
uint16_t SACKE :1; /* Setup Transaction Normal Response */
uint16_t :4;
);
pad(2);
/* BRDY Interrupt Enable Register */ /* BRDY Interrupt Enable Register */
uint16_t BRDYENB; word_union(BRDYENB,
uint16_t :6;
uint16_t PIPE9BRDYE :1;
uint16_t PIPE8BRDYE :1;
uint16_t PIPE6BRDYE :1;
uint16_t PIPE7BRDYE :1;
uint16_t PIPE5BRDYE :1;
uint16_t PIPE3BRDYE :1;
uint16_t PIPE4BRDYE :1;
uint16_t PIPE2BRDYE :1;
uint16_t PIPE1BRDYE :1;
uint16_t PIPE0BRDYE :1;
);
/* NRDY Interrupt Enable Register */ /* NRDY Interrupt Enable Register */
uint16_t NRDYENB; word_union(NRDYENB,
uint16_t :6;
uint16_t PIPE9NRDYE :1;
uint16_t PIPE8NRDYE :1;
uint16_t PIPE6NRDYE :1;
uint16_t PIPE7NRDYE :1;
uint16_t PIPE5NRDYE :1;
uint16_t PIPE3NRDYE :1;
uint16_t PIPE4NRDYE :1;
uint16_t PIPE2NRDYE :1;
uint16_t PIPE1NRDYE :1;
uint16_t PIPE0NRDYE :1;
);
/* BEMP Interrupt Enable Register */ /* BEMP Interrupt Enable Register */
uint16_t BEMPENB; word_union(BEMPENB,
uint16_t :6;
uint16_t PIPE9BEMPE :1;
uint16_t PIPE8BEMPE :1;
uint16_t PIPE6BEMPE :1;
uint16_t PIPE7BEMPE :1;
uint16_t PIPE5BEMPE :1;
uint16_t PIPE3BEMPE :1;
uint16_t PIPE4BEMPE :1;
uint16_t PIPE2BEMPE :1;
uint16_t PIPE1BEMPE :1;
uint16_t PIPE0BEMPE :1;
);
/* SOF Control Register */ /* SOF Control Register */
word_union(SOFCFG, word_union(SOFCFG,
@ -183,10 +186,7 @@ typedef volatile struct
uint16_t :1; uint16_t :1;
uint16_t BRDYM :1; /* BRDY Status Clear Timing */ uint16_t BRDYM :1; /* BRDY Status Clear Timing */
uint16_t enable :1; /* SHOULD BE SET TO 1 MANUALLY */ uint16_t enable :1; /* SHOULD BE SET TO 1 MANUALLY */
uint16_t :1; uint16_t :5;
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t _2 :1; /* Unknown role; can be set */
uint16_t :2;
); );
pad(2); pad(2);
@ -205,32 +205,56 @@ typedef volatile struct
uint16_t VALID :1; /* USB Request Reception */ uint16_t VALID :1; /* USB Request Reception */
uint16_t CTSQ :3; /* Control Transfer Stage */ uint16_t CTSQ :3; /* Control Transfer Stage */
); );
word_union(INTSTS1, pad(4);
uint16_t _1 :1; /* Unknown role */
uint16_t BCHG :1; /* Bus Change */
uint16_t :1;
uint16_t DTCH :1; /* Disconnection Detection */
uint16_t ATTCH :1; /* Connection Detection */
uint16_t :4;
uint16_t EOFERR :1; /* EOF Error Detection */
uint16_t SIGN :1; /* Setup Transaction Error */
uint16_t SACK :1; /* Setup Transaction Normal Response */
uint16_t :4;
);
pad(2);
/* BRDY Interrupt Status Register */ /* BRDY Interrupt Status Register */
uint16_t BRDYSTS; word_union(BRDYSTS,
uint16_t :6;
uint16_t PIPE9BRDY :1;
uint16_t PIPE8BRDY :1;
uint16_t PIPE6BRDY :1;
uint16_t PIPE7BRDY :1;
uint16_t PIPE5BRDY :1;
uint16_t PIPE3BRDY :1;
uint16_t PIPE4BRDY :1;
uint16_t PIPE2BRDY :1;
uint16_t PIPE1BRDY :1;
uint16_t PIPE0BRDY :1;
);
/* NRDY Interrupt Status Register */ /* NRDY Interrupt Status Register */
uint16_t NRDYSTS; word_union(NRDYSTS,
uint16_t :6;
uint16_t PIPE9NRDY :1;
uint16_t PIPE8NRDY :1;
uint16_t PIPE6NRDY :1;
uint16_t PIPE7NRDY :1;
uint16_t PIPE5NRDY :1;
uint16_t PIPE3NRDY :1;
uint16_t PIPE4NRDY :1;
uint16_t PIPE2NRDY :1;
uint16_t PIPE1NRDY :1;
uint16_t PIPE0NRDY :1;
);
/* BEMP Interrupt Status Register */ /* BEMP Interrupt Status Register */
uint16_t BEMPSTS; word_union(BEMPSTS,
uint16_t :6;
uint16_t PIPE9BEMP :1;
uint16_t PIPE8BEMP :1;
uint16_t PIPE6BEMP :1;
uint16_t PIPE7BEMP :1;
uint16_t PIPE5BEMP :1;
uint16_t PIPE3BEMP :1;
uint16_t PIPE4BEMP :1;
uint16_t PIPE2BEMP :1;
uint16_t PIPE1BEMP :1;
uint16_t PIPE0BEMP :1;
);
/* Frame Number Registers */ /* Frame Number Registers */
word_union(FRMNUM, word_union(FRMNUM,
uint16_t OVRN :1; /* Overrun/Underrun Detection Status */ uint16_t OVRN :1; /* Overrun/Underrun Detection Status */
uint16_t CRCE :1; /* Receive Data Error */ uint16_t CRCE :1; /* Receive Data Error */
uint16_t const :3; uint16_t cons :3;
uint16_t FRNM :11; /* Frame Number */ uint16_t FRNM :11; /* Frame Number */
); );
word_union(UFRMNUM, word_union(UFRMNUM,
@ -265,10 +289,7 @@ typedef volatile struct
/* DCP Configuration Register */ /* DCP Configuration Register */
word_union(DCPCFG, word_union(DCPCFG,
uint16_t :7; uint16_t :11;
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t _2 :1; /* Unknown role; can be set */
uint16_t :2;
uint16_t DIR :1; /* Transfer Direction */ uint16_t DIR :1; /* Transfer Direction */
uint16_t :4; uint16_t :4;
); );
@ -320,7 +341,7 @@ typedef volatile struct
/* Pipe Buffer Setting Register */ /* Pipe Buffer Setting Register */
word_union(PIPEBUF, word_union(PIPEBUF,
uint16_t :1; uint16_t :1;
uint16_t BUFSIZE:5; /* Buffer Size */ uint16_t BUFSIZE : 5; /* Buffer Size */
uint16_t :2; uint16_t :2;
uint16_t BUFNMB :8; /* Buffer Number */ uint16_t BUFNMB :8; /* Buffer Number */
); );
@ -358,20 +379,56 @@ typedef volatile struct
); );
pad(14); pad(14);
/* Transaction Counter Registers (PIPE1..PIPE5 only) */ /* PIPEn Transaction Counter Enable Registers and */
sh7305_usb_pipetr PIPETR[5]; /* PIPEn Transaction Counter Registers */
word_union(PIPE1TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE1TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
word_union(PIPE2TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE2TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
word_union(PIPE3TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE3TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
word_union(PIPE4TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE4TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
word_union(PIPE5TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE5TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
pad(0x1e); pad(0x1e);
uint16_t REG_C2; uint16_t REG_C2;
pad(12);
word_union(DEVADD[11],
uint16_t :1;
uint16_t UPPHUB :4; /* Address of target's hub */
uint16_t HUBPORT:3; /* Hub port where target connects */
uint16_t USBSPD :2; /* Transfer speed / Target present */
uint16_t :6;
);
} GPACKED(4) sh7305_usb_t; } GPACKED(4) sh7305_usb_t;

View file

@ -1,52 +0,0 @@
//---
// gint:mpu:wdt - Watchdog Timer
//---
#ifndef GINT_MPU_WDT
#define GINT_MPU_WDT
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 WatchDog Timer. Refer to:
// "Renesas SH7705 Group Hardware Manual"
// Section 10: "WatchDog Timer (WDT)"
//---
/* sh7705_wdt_t - Watch Dog Timer */
typedef volatile struct
{
/* WDT registers are unique in access size; reads are performed with
8-bit accesses, but writes are performed with 16-bit accesses. */
union {
uint8_t READ;
uint16_t WRITE;
} WTCNT;
union {
byte_union(READ,
uint8_t TME :1; /* Timer Enable */
uint8_t WTIT :1; /* Watchdog/Interval Select */
uint8_t RSTS :1; /* Watchdog Reset Select */
uint8_t WOVF :1; /* Watchdog Overflow Flag */
uint8_t IOVF :1; /* Interval Overflow Flag */
uint8_t CKS :3; /* Clock Select */
);
uint16_t WRITE;
} WTCSR;
} sh7705_wdt_t;
#define SH7705_WDT (*((sh7705_wdt_t *)0xffffff84))
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_WDT */

View file

@ -17,11 +17,7 @@ extern "C" {
// Time management // Time management
//--- //---
/* rtc_time_t: A point in time, representable in the RTC registers /* rtc_time_t: A point in time, representable in the RTC registers */
WARNING: A copy of this definition is used in fxlibc, make sure to keep it
in sync. (We don't install kernel headers before compiling the fxlibc, it's
kind of a nightmare for the modest build system.) */
typedef struct typedef struct
{ {
uint16_t year; /* Years (exact value, e.g. 2018) */ uint16_t year; /* Years (exact value, e.g. 2018) */

View file

@ -1,64 +0,0 @@
//---
// gint:serial - Serial communication
//---
// TODO: This is a template for a future implementation.
#ifndef GINT_SERIAL
#define GINT_SERIAL
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/defs/call.h>
typedef enum {
SERIAL_PARITY_NONE,
SERIAL_PARITY_EVEN,
SERIAL_PARITY_ODD,
} GPACKEDENUM serial_parity_t;
typedef struct {
/* Baud rate (valid options are 300, 600, 1200, 2400, 4800, 9600,
19200, 38400, 57600 and 115200) */
int baudrate;
/* Parity */
serial_parity_t parity;
/* Data width (valid options are 7 and 8) */
uint8_t data_width;
/* Stop bits (valid options are 1 and 2) */
uint8_t stop_bits;
} serial_config_t;
// TODO: Error management...
int serial_open(serial_config_t const *config);
bool serial_is_open(void);
void serial_write_async(void const *data, size_t size, gint_call_t callback);
void serial_write_sync(void const *data, size_t size);
void serial_read_async(void const *data, size_t size, gint_call_t callback);
void serial_read_sync(void const *data, size_t size);
// Waits for communications to finish
void serial_wait(void);
// Calls serial_wait() automatically
void serial_close(void);
// TODO: Info on how much data is pending in each buffer?
// Ultimately we'll bind this to a file descriptor so we can't really know.
#ifdef __cplusplus
}
#endif
#endif /* GINT_SERIAL */

View file

@ -162,11 +162,10 @@ void timer_stop(int timer);
it may have only paused. If the timer never stops, you're in trouble. */ it may have only paused. If the timer never stops, you're in trouble. */
void timer_wait(int timer); void timer_wait(int timer);
/* timer_spinwait(): Start a timer and actively wait for UNF /* timer_spinwait(): Actively wait for a timer to raise UNF
Waits until the timer raises UNF, without sleeping. This is useful for Waits until the timer raises UNF, without sleeping. This is useful for
delays in driver code that is run when interrupts are disabled. UNIE is delays in driver code that is run when interrupts are disabled. This relies
disabled before starting the timer and waiting, so the callback is never neither on the interrupt signal nor on the UNIE flag. */
called. */
void timer_spinwait(int timer); void timer_spinwait(int timer);
//--- //---

View file

@ -1,58 +0,0 @@
//---
// gint:ubc - User Break Controller driver
//---
#ifndef GINT_UBC
#define GINT_UBC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/gdb.h>
/* Read and write the DBR register */
void ubc_setDBR(void* DBR);
void* ubc_getDBR(void);
/* ubc_dbh(): Low level UBC debug handler
The handler will backup the current CPU state and call ubc_debug_handler(). */
void ubc_dbh(void);
/* ubc_debug_handler(): C level UBC debug handler
The execution will be redirected to the handler set by the user or the break
will be ignored in case no handler has been set. */
void ubc_debug_handler(gdb_cpu_state_t* cpu_state);
/* ubc_set_debug_handler(): Set user debug handler
Set a custom debug handler that will be called when a break condition from
the UBC is reached. */
void ubc_set_debug_handler(void (*h)(gdb_cpu_state_t*));
/* ubc_dbh_lock: Lock set by ubc_dbh() when a UBC break is currently being
handled. */
extern uint8_t ubc_dbh_lock;
/* UBC Breakpoint types */
typedef enum {
UBC_BREAK_BEFORE, /* Break before the instruction is executed */
UBC_BREAK_AFTER, /* Break after the instruction is executed :
at this point PC will point to the next instruction. */
} ubc_break_mode_t;
/* ubc_set_breakpoint(): Set a breakpoint in a UBC channel and enable it
Return false when an invalid channel number is provided, true if the
breakpoint was correctly set up. */
bool ubc_set_breakpoint(int channel, void* break_address, ubc_break_mode_t break_mode);
/* ubc_get_break_address(): Get a breakpoint address if it's enabled
If the channel is disabled the function will return false and *break_address
will not be updated. */
bool ubc_get_break_address(int channel, void** break_address);
/* ubc_disable_channel(): Disable a UBC channel
Return true on success. If an invalid channel number is provided, it will
return false. */
bool ubc_disable_channel(int channel);
#ifdef __cplusplus
}
#endif
#endif /* GINT_UBC */

View file

@ -1,27 +1,5 @@
//--- //---
// gint:usb-ff-bulk - A bulk-based transfer class using the fxlink protocol // gint:usb-ff-bulk - A trivial bulk-based transfer class
//
// This interface (class code 0xff/0x77) implements a simple bidirectional
// communication channel running the fxlink protocol. It basically talks to
// fxlink's interactive mode on the host, and can broadly be used for 4 things:
//
// 1. Sending standard messages to fxlink (screenshots, logs...). Convenient
// functions are provided in this header to minimize the overhead. fxlink
// running in interactive mode (fxlink -t) will handle these messages in a
// suitable way automatically (eg. save screenshots as PNG).
//
// 2. Sending custom messages to fxlink (bench results, memory dumps, crash
// reports...). fxlink running in interactive mode will forward the data to
// appropriate user-provided external programs to handle the messages.
//
// 3. Applications based around on data streams coming from the host (screen
// projector, web browser...). This can be achieved by modifying fxlink or
// using it as a library.
//
// 4. Remote control from the fxlink interactive mode (fxlink -t). This is
// based on fxlink's 'command' and 'keyboard' protocols. This module will
// automatically handle commands and respond to them as long as the message
// handling function is called regularly.
//--- //---
#ifndef GINT_USB_FF_BULK #ifndef GINT_USB_FF_BULK
@ -32,38 +10,164 @@ extern "C" {
#endif #endif
#include <gint/usb.h> #include <gint/usb.h>
#include <gint/config.h>
struct usb_fxlink_header;
/* This bulk transfer interface with class code 0xff/0x77 implements a simple /* The bulk transfer interface with class code 0xff provides a very simple
bidirectional communication channel between the calculator and a host, communication channel between the calculator and a host. There is
running at high-speed USB 2.0 and using the fxlink protocol. You can use it (currently) a single IN pipe that sends data from the calculator to the
to communicate with fxlink's interactive mode on the host. */ host, at high-speed (USB 2.0).
The class code of this interface is 0xff, which means that the protocol is
custom and requires a custom program on the host to receive the data (unlike
for instance LINK on a Graph 90+E which behaves as a USB stick and can be
used with the file browser). fxlink can be used to the effect. */
extern usb_interface_t const usb_ff_bulk; extern usb_interface_t const usb_ff_bulk;
//--- //---
// Sending standard messages // Direct bulk access
//
// The following functions can be used to access the bulk pipes directly. They
// provide the pipe numbers, which can be passed for instance to usb_write
// functions.
//--- //---
/* usb_fxlink_text(): Send raw text /* usb_ff_bulk_output(): Pipe for calculator -> host communication */
Send a string; fxlink will display it in the terminal. This can be used to int usb_ff_bulk_output(void);
back stdout/stderr. Sending lots of small messages can be slow; if that's a
problem, fill in message manually. If size is 0, uses strlen(text). */ //---
void usb_fxlink_text(char const *text, int size); // fxlink protocol
//
// fxlink is a bulk-based communication tool in the fxSDK that can be used to
// conveniently transfer or manipulate information during the execution of an
// add-in. For instance, screenshots can be saved, files can be transferred,
// and text can be logged.
//
// Each communication with fxlink is a message that consists of:
// * A first write with a message header;
// * One or more writes with message data (of a size announced in the header);
// * A commit on the USB pipe (to ensure that everything is sent).
//
// There are two ways to use the fxlink protocol in this header; you can either
// use one of the convenience functions like usb_fxlink_screenshot() that do
// all the steps for you, or you can perform the writes and commits yourself to
// send custom messages.
//---
/* usb_fxlink_header_t: Message header for fxlink
fxlink supports a minimalistic protocol to receive data sent from the
calculator and automatically process it (such as save it to file, convert
to an image, etc). It is designed as a convenience feature, and it can be
extended with custom types rather easily.
A message is categorized with an (application, type) pair; both are UTF-8
strings of up to 16 characters. The application name "fxlink" is reserved
for built-in types supported by fxlink; any other custom application name
can be set (in which case fxlink will call a user-provided program to handle
the message).
The size of the data to be transferred must be specified in order of the
transfer to proceed correctly, as it cannot reliably be guessed on the other
side (and guessing wrong means all further messages will be in trouble).
As with the rest of the USB protocol, all the multi-byte integer fields in
this header are encoded as *little-endian*. */
typedef struct
{
/* Protocol version = 0x00000100 */
uint32_t version;
/* Size of the data to transfer (excluding this header) */
uint32_t size;
/* Size of individual transfers (related to the size of the FIFO) */
uint32_t transfer_size;
/* Application name, UTF-8 (might not be zero-terminated) */
char application[16];
/* Message type */
char type[16];
} usb_fxlink_header_t;
/* usb_fxlink_fill_header(): Fill an fxlink message header
This function will fill the specified fxlink header. You need to specify the
exact amount of data that the fxlink message will contain; if you cannot
determine it, consider splitting the message into several messages each with
their own header, and use the application-specific script on the host to
recombine them.
Returns false if the parameters are invalid or don't fit, in this case the
contents of the header are unchanged. */
bool usb_fxlink_fill_header(usb_fxlink_header_t *header,
char const *application, char const *type, uint32_t data_size);
//---
// Short functions for fxlink built-in types
//---
/* Subheader for the fxlink built-in "image" type */
typedef struct
{
uint32_t width;
uint32_t height;
/* Pixel format, see below */
int pixel_format;
} usb_fxlink_image_t;
/* Pixel formats */
typedef enum
{
/* Image is an array of *big-endian* uint16_t with RGB565 format */
USB_FXLINK_IMAGE_RGB565 = 0,
/* Image is an array of bits in mono format */
USB_FXLINK_IMAGE_MONO,
/* Image is two consecutive mono arrays, one for light, one for dark */
USB_FXLINK_IMAGE_GRAY,
} usb_fxlink_image_format_t;
/* usb_fxlink_screenshot(): Take a screenshot /* usb_fxlink_screenshot(): Take a screenshot
This function sends a copy of the VRAM to fxlink. This is best used just This function takes a screenshot. If (onscreen = false), it sends the
before dupdate() since this ensures the image sent by USB is identical to contents of the VRAM. This mode is best used just before dupdate(), since
the one displayed on screen. the VRAM contents are exactly as they will appear on screen a moment later.
However, this is somewhat unintuitive for interactive GUI as the user has to
press a button after the option is present on-screen, therefore the contents
of the *next* frame are captured.
If `onscreen` is set and there are two VRAMs (on fx-CG or when using the If (onscreen = true), this function tries to send the pixels that are
gray engine on fx-9860G), sends a copy of the other VRAM. This is a bit more currently visible on-screen, with the following heuristic:
intuitive when taking a screenshot of the last shown image as a result of a
key press. Note that this function never reads pixels directly from the * If there is only one VRAM, the VRAM contents are used in the hope they
display (it's usually slow and currently not even implemented). */ haven't changed since the last frame was presented with dupdate().
* If there are two VRAMs (on fx-CG 50 or using the gray engine) the contents
of the VRAM not currently being draw to are sent.
This function does not read pixels directly from the display, as this is
usually slow and currently not even implemented. */
void usb_fxlink_screenshot(bool onscreen); void usb_fxlink_screenshot(bool onscreen);
#ifdef FX9860G
/* usb_fxlink_screenshot_gray(): Take a gray screenshot on fx-9860G
This function is similar to usb_fxlink_screenshot(), but it takes a gray
screenshot. It depends on the gray engine so if you use your add-in will
automatically have the gray engine, that's why it's separate. */
void usb_fxlink_screenshot_gray(bool onscreen);
#endif
/* usb_fxlink_text(): Send raw text
This function sends a string with the "text" type, which fxlink prints on
the terminal. It will send a full fxlink message (with a commit), which is
inefficient if there is a lot of text to send. For better speed, send the
message manually by filling the header and doing the writes and commit
manually.
This function sends the text by blocks of 4 bytes or 2 bytes when alignment
and size allow, and 1 byte otherwise. If size is 0, strlen(text) is used. */
void usb_fxlink_text(char const *text, int size);
/* usb_fxlink_videocapture(): Send a frame for a video recording /* usb_fxlink_videocapture(): Send a frame for a video recording
This function is essentially the same as usb_fxlink_screenshot(). It sends a This function is essentially the same as usb_fxlink_screenshot(). It sends a
@ -75,131 +179,12 @@ void usb_fxlink_screenshot(bool onscreen);
automatically send new frames to fxlink. */ automatically send new frames to fxlink. */
void usb_fxlink_videocapture(bool onscreen); void usb_fxlink_videocapture(bool onscreen);
#if GINT_RENDER_MONO #ifdef FX9860G
/* Similar to usb_fxlink_screenshot(), but takes a gray screenshot if the gray /* usb_fxlink_videocapture_gray(): Send a gray frame for a video recording
engine is currently running. */ Like usb_fxlink_videocapture(), but uses VRAM data from the gray engine. */
void usb_fxlink_screenshot_gray(bool onscreen);
/* Like usb_fxlink_videocapture(), but uses VRAM data from the gray engine. */
void usb_fxlink_videocapture_gray(bool onscreen); void usb_fxlink_videocapture_gray(bool onscreen);
#endif #endif
//---
// Receiving messages
//---
/* usb_fxlink_handle_messages(): Process and return incoming messages
This function processes incoming messages. It handles some types of messages
internally (eg. fxlink commands) and returns the others to the caller by
loading the provided header structure and returning true.
When this function returns true, the caller should read the message contents
on the interface's input pipe. Usually every read will return just a portion
of the message (eg. 2048 bytes) so the contents should be read with a loop.
As long as this function returns true it should be called againt to process
any other messages. When there is no more messages to process, this function
returns false. It is important to call this function regularly from the main
thread when quick responses are expected. */
bool usb_fxlink_handle_messages(struct usb_fxlink_header *header);
/* usb_fxlink_drop_transaction(): Drop incoming data until end of transaction
When a message arrives on the USB port incoming data *must* be processed in
order for the pipe to accept any further data in the future. This function
can be used in error situations to drop data until the end of the
transaction (which is usually the end of the message, at least with the
fxSDK's implementation of fxlink). */
void usb_fxlink_drop_transaction(void);
/* usb_fxlink_set_notifier(): Set up a notification for incoming messages
Registers the provided function to be called when data arrives on the fxlink
input pipe. This signals that usb_fxlink_handle_messages() should be called
later from the main thread. Pass NULL to disable the notification. */
void usb_fxlink_set_notifier(void (*notifier_function)(void));
//---
// Direct bulk access
//
// The following functions can be used to access the bulk pipes directly. They
// provide the pipe numbers, which can be passed for instance to usb_write
// functions.
//---
/* usb_ff_bulk_output(): Pipe number for calculator -> host communication */
int usb_ff_bulk_output(void);
/* usb_ff_bulk_input(): Pipe number for host -> calculator communication */
int usb_ff_bulk_input(void);
//---
// Construction and analysis of fxlink protocol messages
//
// fxlink messages consist of a simple header followed by message contents
// (sometimes with a subheader). In addition to the functions above, it is
// possible to craft custom fxlink messages and send them manually.
//
// To send a message manually, simple write an fxlink header to the output
// pipe, followed by the contents. The message can be built from any number of
// writes to the pipe. After the last write, commit the pipe.
//---
/* usb_fxlink_header_t: Message header for fxlink
Messages are categorized with an (application, type) pair; both are UTF-8
strings of up to 16 bytes (NUL not required). The application name "fxlink"
is reserved for built-in types of messages. Other names can be used freely,
and fxlink's interactive mode on the host side will forward messages to
external programs based on the application name.
The size of the data to be transferred must be specified upfront, but this
restriction might be lifted in the future. As with the rest of the USB
protocol, all the integer are encoded as *little-endian*. */
typedef struct usb_fxlink_header
{
/* Protocol version = 0x00000100 */
uint32_t version;
/* Size of the data to transfer (excluding this header) */
uint32_t size;
/* Size of individual transfers (related to the size of the FIFO) */
uint32_t transfer_size;
/* Application name and message type, UTF-8 (need not be zero-terminated) */
char application[16];
char type[16];
} usb_fxlink_header_t;
/* usb_fxlink_fill_header(): Fill an fxlink message header
Fills an fxlink header's fields with the provided. You need to specify the
exact amount of data that the fxlink message will contain. Returns false if
parameters are invalid or don't fit the fields. */
bool usb_fxlink_fill_header(usb_fxlink_header_t *header,
char const *application, char const *type, uint32_t data_size);
/* Subheader for the fxlink built-in "image" type */
typedef struct
{
/* Image size; storage is row-major */
uint32_t width;
uint32_t height;
/* Pixel format, see below */
int pixel_format;
} usb_fxlink_image_t;
/* Pixel formats */
enum {
/* Image is an array of *big-endian* uint16_t with RGB565 format */
USB_FXLINK_IMAGE_RGB565 = 0,
/* Image is an array of bits in mono format */
USB_FXLINK_IMAGE_MONO,
/* Image is two consecutive mono arrays, one for light, one for dark */
USB_FXLINK_IMAGE_GRAY,
};
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -10,11 +10,9 @@ extern "C" {
#endif #endif
#include <gint/defs/types.h> #include <gint/defs/types.h>
#include <gint/defs/timeout.h> #include <gint/std/endian.h>
#include <gint/gint.h> #include <gint/gint.h>
#include <gint/config.h>
#include <stdarg.h> #include <stdarg.h>
#include <endian.h>
/* See "Interfaces to communicate with USB transfers" below for details */ /* See "Interfaces to communicate with USB transfers" below for details */
typedef struct usb_interface usb_interface_t; typedef struct usb_interface usb_interface_t;
@ -29,35 +27,26 @@ enum {
/* There are no interfaces */ /* There are no interfaces */
USB_OPEN_NO_INTERFACE = 1, USB_OPEN_NO_INTERFACE = 1,
/* There are more interfaces than supported (16) */ /* There are more interfaces than supported (16) */
USB_OPEN_TOO_MANY_INTERFACES = -2, USB_OPEN_TOO_MANY_INTERFACES,
/* There are not enough endpoint numbers for every interface, or there /* There are not enough endpoint numbers for every interface, or there
are not enough pipes to set them up */ are not enough pipes to set them up */
USB_OPEN_TOO_MANY_ENDPOINTS = -3, USB_OPEN_TOO_MANY_ENDPOINTS,
/* There is not enough FIFO memory to use the requested buffer sizes */ /* There is not enough FIFO memory to use the requested buffer sizes */
USB_OPEN_NOT_ENOUGH_MEMORY = -4, USB_OPEN_NOT_ENOUGH_MEMORY,
/* Information is missing, such as buffer size for some endpoints */ /* Information is missing, such as buffer size for some endpoints */
USB_OPEN_MISSING_DATA = -5, USB_OPEN_MISSING_DATA,
/* Invalid parameters: bad endpoint numbers, bad buffer sizes... */ /* Invalid parameters: bad endpoint numbers, bad buffer sizes... */
USB_OPEN_INVALID_PARAMS = -6, USB_OPEN_INVALID_PARAMS,
/* USB interfaces are already opened */
USB_OPEN_ALREADY_OPEN = -7,
/* General timeout for a sync_timeout call */ /* This pipe is busy (returned by usb_write_async()) */
USB_TIMEOUT = -8, USB_WRITE_BUSY,
/* This pipe is busy with another call */ /* Both FIFO controlles are busy, none is available to transfer */
USB_BUSY = -9, USB_WRITE_NOFIFO,
/* Both FIFO controllers are busy, none is available to transfer */ /* This pipe is busy (returned by usb_commit_async()) */
USB_WRITE_NOFIFO = -10, USB_COMMIT_BUSY,
/* This pipe has no ongoing transfer to commit */ /* This pipe has no ongoing transfer to commit */
USB_COMMIT_INACTIVE = -11, USB_COMMIT_INACTIVE,
/* This pipe is currently not receiving any data */
USB_READ_IDLE = -12,
/* No FIFO controller is available */
USB_READ_NOFIFO = -13,
/* (Internal codes) */
USB_ERROR_ZERO_LENGTH = -100,
}; };
/* usb_open(): Open the USB link /* usb_open(): Open the USB link
@ -90,9 +79,6 @@ void usb_open_wait(void);
/* usb_is_open(): Check whether the USB link is active */ /* usb_is_open(): Check whether the USB link is active */
bool usb_is_open(void); bool usb_is_open(void);
/* usb_is_open_interface(): Check whether a particular interface is open */
bool usb_is_open_interface(usb_interface_t const *interface);
/* usb_close(): Close the USB link /* usb_close(): Close the USB link
This function closes the link opened by usb_open(), and notifies the host of This function closes the link opened by usb_open(), and notifies the host of
@ -143,12 +129,8 @@ struct usb_interface {
/* Answer class-specific SETUP requests */ /* Answer class-specific SETUP requests */
/* TODO */ /* TODO */
/* Notification that an endpoint has data to be read. This function is /* Receive data from an endpoint */
called frequently when data is being transmitted; the particular /* TODO */
timings depend on low-level details. The notification should be
passed down to cause the main thread to read later. Do not read in
the notification function! */
void (*notify_read)(int endpoint);
}; };
/* usb_interface_endpoint_t: Parameters for an interface endpoint /* usb_interface_endpoint_t: Parameters for an interface endpoint
@ -187,22 +169,23 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
/* usb_write_sync(): Synchronously write to a USB pipe /* usb_write_sync(): Synchronously write to a USB pipe
This functions writes (size) bytes of (data) into the specified pipe. If the This functions writes (size) bytes of (data) into the specified pipe, by
data fits into the pipe, this function returns right away, and the data is units of (unit_size) bytes. The unit size must be 1, 2 or 4, and both (data)
*not* transmitted. Otherwise, data is written until the pipe is full, at and (size) must be multiples of the unit size. In general, you should try to
which point it is automatically transmitted. After the transfer, this use the largest possible unit size, as it will be much faster. In a sequence
function resumes writing, returning only once everything is written. Even of writes that concludes with a commit, all the writes must use the same
then the last bytes will still not have been transmitted, to allow for other unit size.
writes to follow. After the last write in a sequence, use usb_commit_sync()
or usb_commit_async() to transmit the last bytes.
If (use_dma=true), the write is performed with the DMA instead of the CPU. If the data fits into the pipe, this function returns right away, and the
This requires at least 4-byte alignment on: data is *not* transmitted. Otherwise, data is written until the pipe is
1. The input data; full, at which point it is automatically transmitted. After the transfer,
2. The size of this write; this function resumes writing, returning only once everything is written.
3. The amount of data previously written to the pipe not yet committed. Even then the last bytes will still not have been transmitted, to allow for
This is because using the DMA does not allow any insertion of CPU logic to other writes to follow. After the last write in a sequence, use
handle unaligned stuff. usb_commit_sync() or usb_commit_async() to transmit the last bytes.
If (use_dma=true), the write is performed with the DMA instead of the CPU,
which is generally faster.
This function will use a FIFO controller to access the pipe. The FIFO This function will use a FIFO controller to access the pipe. The FIFO
controller will be reserved for further writes until the contents of the controller will be reserved for further writes until the contents of the
@ -215,15 +198,13 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
waits for the ressources to become available then proceeds normally. waits for the ressources to become available then proceeds normally.
@pipe Pipe to write into @pipe Pipe to write into
@data Source data @data Source data (unit_size-aligned)
@size Size of source @size Size of source (multiple of unit_size)
@unit_size FIFO access size (must be 1, 2, or 4)
@dma Whether to use the DMA to perform the write @dma Whether to use the DMA to perform the write
-> Returns an error code (0 on success). */ -> Returns an error code (0 on success). */
int usb_write_sync(int pipe, void const *data, int size, bool use_dma); int usb_write_sync(int pipe, void const *data, int size, int unit_size,
bool use_dma);
/* usb_write_sync_timeout(): Synchronously write, with a timeout */
int usb_write_sync_timeout(int pipe, void const *data, int size,
bool use_dma, timeout_t const *timeout);
/* usb_write_async(): Asynchronously write to a USB pipe /* usb_write_async(): Asynchronously write to a USB pipe
@ -245,13 +226,14 @@ int usb_write_sync_timeout(int pipe, void const *data, int size,
is idle and USB_WRITE_BUSY otherwise. is idle and USB_WRITE_BUSY otherwise.
@pipe Pipe to write into @pipe Pipe to write into
@data Source data @data Source data (unit_size-aligned)
@size Size of source @size Size of source (multiple of unit_size)
@unit_size FIFO access size (must be 1, 2, or 4)
@dma Whether to use the DMA to perform the write @dma Whether to use the DMA to perform the write
@callback Optional callback to invoke when the write completes @callback Optional callback to invoke when the write completes
-> Returns an error code (0 on success). */ -> Returns an error code (0 on success). */
int usb_write_async(int pipe, void const *data, int size, bool use_dma, int usb_write_async(int pipe, void const *data, int size, int unit_size,
gint_call_t callback); bool use_dma, gint_call_t callback);
/* usb_commit_sync(): Synchronously commit a write /* usb_commit_sync(): Synchronously commit a write
@ -259,9 +241,6 @@ int usb_write_async(int pipe, void const *data, int size, bool use_dma,
transfers whatever data is left, and returns when the transfer completes. */ transfers whatever data is left, and returns when the transfer completes. */
void usb_commit_sync(int pipe); void usb_commit_sync(int pipe);
/* usb_commit_sync_timeout(): Synchronously commit a write, with timeout */
int usb_commit_sync_timeout(int pipe, timeout_t const *timeout);
/* usb_commit_async(): Asynchronously commit a write /* usb_commit_async(): Asynchronously commit a write
This function commits the specified pipe, causing the pipe to transfer This function commits the specified pipe, causing the pipe to transfer
@ -276,142 +255,23 @@ int usb_commit_sync_timeout(int pipe, timeout_t const *timeout);
of the remaining data completes. */ of the remaining data completes. */
int usb_commit_async(int pipe, gint_call_t callback); int usb_commit_async(int pipe, gint_call_t callback);
/* usb_read_sync(): Synchronously read from a USB pipe
This function waits for data to become available on the specified `pipe`,
and then reads up to `size` bytes into `data`. It is "synchronized" with USB
transactions on the pipe in the following ways:
1. If there is no active transaction, it waits for one to arrive.
2. If the active transaction has no data left to read, it is skipped.
3. If the transaction's data is completely read, the transaction is closed
even if `data` is full. (See usb_read_async() for the alternative.)
Basically usb_read_sync() returns the next `size` bytes of data that the
calculator receives on the pipe, with a single exception: it doesn't read
across transactions, so if the active transaction has 100 bytes left and you
request 200, you will only get 100. usb_read_sync() only ever returns 0
bytes if there is an empty transaction, which is rare.
Because this is a blocking function, a call to usb_read_sync() will *FREEZE*
if there is no data to read on the pipe and the host doesn't send any. In
addition, it's not possible to detect how much data is left to read with
usb_read_sync()'s interface. If you want to avoid or debug a freeze, use
use usb_read_async() or set a timeout with usb_read_sync_timeout(). If you
want to read a transaction until the end without knowing its size in
advance, use usb_read_async().
If `use_dma=true`, uses the DMA for transfers. This requires 4-byte
alignment on the buffer, size, and number of bytes previously read in the
current transaction.
Returns the number of bytes read or a negative error code. */
int usb_read_sync(int pipe, void *data, int size, bool use_dma);
/* usb_read_sync_timeout(): Synchronous read with a timeout */
int usb_read_sync_timeout(int pipe, void *data, int size, bool use_dma,
timeout_t const *timeout);
/* Flags for usb_read_async(), see below. */
enum {
/* Use the DMA for data transfer (requires full 4-byte alignment). */
USB_READ_USE_DMA = 0x01,
/* Ignore reads of 0 bytes from exhausted transactions. */
USB_READ_IGNORE_ZEROS = 0x02,
/* Close transactions when exhausted even by non-partial reads. */
USB_READ_AUTOCLOSE = 0x04,
/* Wait for the read to finish before returning. */
USB_READ_WAIT = 0x08,
/* Block for a new transaction if none is currenty active. */
USB_READ_BLOCK = 0x10,
};
/* usb_read_async(): Asynchronously read from a USB pipe
This function is similar to usb_read_sync() but it is asynchronous, ie. it
returns after starting the read and then runs in the background. Without
options, usb_read_async() is guaranteed to return quickly without waiting
for communications. The read itself also never blocks unless there is a
disagreement with the host on the size of transactions.
usb_read_async() is also slightly lower-level than usb_read_sync(). In
particular, it only closes transactions when the data is fully read *and*
the user buffer is not full. If the user requests exactly the number of
bytes left in the transaction, the entire contents will be read but the
transaction will be left in an "active with 0 bytes left" state. Only on the
next read will the transaction be closed.
This behavior is designed so that a read closes a transaction if and only if
it returns fewer bytes than requested, which is useful for detecting the end
of transfers when their size isn't known in advance.
This function returns a preliminary error code. If it is zero, then the
callback will be invoked later with *rc set to the final return value of the
read, which is itself either an error (< 0) or the number of bytes read.
Due to its low-level nature, raw usb_read_async() is a bit impractical to
use and almost always requires repeated calls and callback waits. The
following flags are provided to make it more convenient by incorporating
features of usb_read_sync():
* USB_READ_IGNORE_ZEROS causes usb_read_async() to ignore reads of 0 bytes
from transactions that were exhausted but not closed.
* USB_READ_AUTOCLOSE causes usb_read_async() to always close transactions
when exhausted even if the read is not partial. This is useful when the
size of the transaction is in fact known in advance.
* USB_READ_WAIT causes usb_read_async() to wait for the end of the transfer
before returning. Unless there is a driver bug or USB_READ_BLOCK is also
set, this cannot cause the function to freeze since the read will always
complete in finite time (returning USB_READ_IDLE if no transaction is
active). When USB_READ_WAIT it set, `callback` and `rc` are ignored; the
callback is not invoked, and the status of the entire read is returned.
* USB_READ_BLOCK causes usb_read_async() to wait for a transaction if none
is active. This can stall the transfer indefinitely if the host doesn't
transmit any data, and freeze the call entirely if USB_READ_WAIT is set.
This option really makes usb_read_async() a synchronous function.
* A timeout is supported (pass NULL to ignore).
With all options enabled, usb_read_async() becomes functionally identical to
usb_read_sync_timeout(). */
int usb_read_async(int pipe, void *data, int size, int flags, int *rc,
timeout_t const *timeout, gint_call_t callback);
/* usb_read_cancel(): Cancel an asynchronous read
Once started, an async read will run in the background and keep writing to
the provided buffer. This function cancels the operation so that no further
writes to the buffer are made and the associated memory can be safely
deallocated. */
void usb_read_cancel(int pipe);
//--- //---
// USB debugging functions // USB debugging log
//--- //---
/* usb_set_log(): Set the logging function for the USB driver */ /* usb_set_log(): Set the logging function for the USB driver
The USB driver can produce logs, which are mostly useful to troubleshoot
problems and add new protocols. The logging is disabled by default but can
be enabled by specifying this function.
It is up to you whether to store that in a buffer, rotate logs, send them to
storage memory or a console on the PC. */
void usb_set_log(void (*logger)(char const *format, va_list args)); void usb_set_log(void (*logger)(char const *format, va_list args));
/* usb_get_log(): Get the logging function */
void (*usb_get_log(void))(char const *format, va_list args);
/* usb_log(): Send a message to the USB log */ /* usb_log(): Send a message to the USB log */
void usb_log(char const *format, ...); void usb_log(char const *format, ...);
/* usb_set_trace(): Set the tracing function for the USB driver
The function is called atomically, thus cannot be interrupted, therefore it
is safe to call usb_trace() in interrupt handlers. */
void usb_set_trace(void (*tracer)(char const *message));
/* usb_get_trace(): Get the tracing function */
void (*usb_get_trace(void))(char const *message);
/* usb_trace(): Trace the current state of the driver */
void usb_trace(char const *message);
#ifdef GINT_USB_DEBUG
#define USB_LOG(...) usb_log(__VA_ARGS__)
#define USB_TRACE(...) usb_trace(__VA_ARGS__)
#else
#define USB_LOG(...) do {} while(0)
#define USB_TRACE(...) do {} while(0)
#endif
//--- //---
// Standard descriptors // Standard descriptors
//--- //---
@ -520,14 +380,6 @@ uint16_t usb_dc_string(uint16_t const *literal, size_t len);
This is mostly used by the driver to answer GET_DESCRIPTOR requests. */ This is mostly used by the driver to answer GET_DESCRIPTOR requests. */
usb_dc_string_t *usb_dc_string_get(uint16_t id); usb_dc_string_t *usb_dc_string_get(uint16_t id);
//---
// USB interrupts
//---
/* usb_interrupt_context: Context of the function interrupted by a USB interrupt
The pointer is set back to NULL when the interrupt is finished being handled. */
extern gint_inth_callback_context_t* usb_interrupt_context;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -1,93 +0,0 @@
//---
// gint:video - Generic video interface
//
// This header defines the interface for video (display) drivers. It allows
// high-level code to manipulate the display independently of the underlying
// hardware.
//---
#ifndef GINT_VIDEO
#define GINT_VIDEO
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/drivers.h>
#include <gint/image.h>
/* Video mode offered by a driver for rendering. */
typedef struct {
/* Mode size */
uint16_t width;
uint16_t height;
/* Pixel format */
int16_t format;
/* Refresh frequency, -1 if unknown */
int16_t freq;
} video_mode_t;
/* Flags for the update function of the interface. */
#define VIDEO_UPDATE_NONE 0x00
#define VIDEO_UPDATE_ENABLE_DMA 0x01
#define VIDEO_UPDATE_ATOMIC 0x02
#define VIDEO_UPDATE_FOREIGN_WORLD 0x04
/* Video driver interface. */
typedef struct {
/* Associated driver (NULL if there's none). */
gint_driver_t const *driver;
/* List of modes (terminated by an all-0 element). The first mode is the
default mode and it should always be available. */
video_mode_t const *modes;
/* Get current video mode. */
uint (*mode_get)(void);
/* Set a video mode. */
bool (*mode_set)(uint id);
/* Minimum and maximum brightness settings. */
int brightness_min;
int brightness_max;
/* Set a brightness setting. */
bool (*brightness_set)(int setting);
/* Implements video_update(); bounds are checked befoer calling. */
bool (*update)(int x, int y, image_t const *framebuffer, int flags);
} video_interface_t;
/* Get the video interface currently in use. This can be NULL if the program is
being linked without a display driver. */
video_interface_t const *video_get_current_interface(void);
/* Get the index of the current video mode. This can be -1 if there is no
interface; however, if there is an interface, this is always >= 0. */
int video_get_current_mode_index(void);
/* Get the a pointer to the current video mode's definition. */
video_mode_t const *video_get_current_mode(void);
/* Update the contents of the display from a framebuffer image. A combination
of `VIDEO_UPDATE_*` flags can be specified to select the update method:
- `ENABLE_DMA` allows the driver to copy using DMA, when applicable;
- `ATOMIC` requires the driver to use an interrupt-less method.
Returns true on success.
Update flags will be ignored if not applicable (e.g. `ENABLE_DMA` for a
video interface that doesn't support DMA) but will result in an error if
applicable and the specified method fails (e.g. DMA transfer error).
This function is usually called with (x,y) = (0,0) and a contiguous
framebuffer whose size is the video mode size. Specifying images with other
sizes, positions and strides is allowed only when they result in data
transfers that are byte-aligned. DMA support is only guaranteed for
contiguous input and output. The implied rectangle must be in-bounds. */
bool video_update(int x, int y, image_t const *fb, int flags);
#ifdef __cplusplus
}
#endif
#endif /* GINT_VIDEO */

View file

@ -5,7 +5,6 @@
#include <gint/drivers.h> #include <gint/drivers.h>
#include <gint/drivers/states.h> #include <gint/drivers/states.h>
#include <gint/clock.h> #include <gint/clock.h>
#include <gint/config.h>
#include <gint/hardware.h> #include <gint/hardware.h>
#include <gint/mpu/cpg.h> #include <gint/mpu/cpg.h>
@ -27,9 +26,10 @@ const clock_frequency_t *clock_freq(void)
// SH7705 Clock signals // SH7705 Clock signals
//--- //---
#if GINT_HW_FX #if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
#define CPG SH7705_CPG
void sh7705_probe(void) static void sh7705_probe(void)
{ {
/* According to Sentaro21 in the sources of Ftune 1.0.1, the clock mode /* According to Sentaro21 in the sources of Ftune 1.0.1, the clock mode
is thought to be 5, which means that: is thought to be 5, which means that:
@ -41,24 +41,36 @@ void sh7705_probe(void)
int ckio = xtal * pll2; int ckio = xtal * pll2;
/* This signal is multiplied by the PLL1 circuit */ /* This signal is multiplied by the PLL1 circuit */
int pll1 = SH7705_CPG.FRQCR.STC + 1; int pll1 = CPG.FRQCR.STC + 1;
/* Fill in the setting structure. Iϕ and Pϕ have dividers, while Bϕ is /* Iphi and Pphi have dividers (Bphi is always equal to CKIO) */
always equal to CKIO. */ int idiv = CPG.FRQCR.IFC;
int pdiv = CPG.FRQCR.PFC;
/* Fill in the setting structure */
freq.PLL1 = pll1; freq.PLL1 = pll1;
freq.PLL2 = pll2; freq.PLL2 = pll2;
freq.Bphi_div = 1; freq.Bphi_div = 1;
freq.Iphi_div = SH7705_CPG.FRQCR.IFC + 1; freq.Iphi_div = idiv + 1;
freq.Pphi_div = SH7705_CPG.FRQCR.PFC + 1; freq.Pphi_div = pdiv + 1;
/* Deduce the frequency of the main clocks. This value is ckio/3 */
int ckio_3 = 9830400;
/* Exchange the setting values 2 and 3 (corresponding to /3 and /4)
This means that /1, /2, /4 are now 0, 1, 2, which is perfect for a
quick bit shift */
idiv = idiv ^ (idiv >> 1);
pdiv = pdiv ^ (pdiv >> 1);
/* Deduce the frequency of the main clocks */
freq.CKIO_f = ckio; freq.CKIO_f = ckio;
freq.Bphi_f = ckio; freq.Bphi_f = ckio;
freq.Iphi_f = (ckio * pll1) / freq.Iphi_div; freq.Iphi_f = (idiv == 3) ? ckio_3 : ckio >> idiv;
freq.Pphi_f = (ckio * pll1) / freq.Pphi_div; freq.Pphi_f = (pdiv == 3) ? ckio_3 : ckio >> pdiv;
} }
#endif #undef CPG
#endif /* FX9860G and platform-agnostic */
//--- //---
// SH7305 clock signals // SH7305 clock signals
@ -70,7 +82,7 @@ static void sh7305_probe(void)
{ {
/* The meaning of the PLL setting on SH7305 differs from the /* The meaning of the PLL setting on SH7305 differs from the
documentation of SH7224; the value must not be doubled. */ documentation of SH7224; the value must not be doubled. */
int pll = CPG.FRQCR.STC + 1; int pll = CPG.FRQCRA.STC + 1;
freq.PLL = pll; freq.PLL = pll;
/* The FLL ratio is the value of the setting, halved if SELXM=1 */ /* The FLL ratio is the value of the setting, halved if SELXM=1 */
@ -81,9 +93,9 @@ static void sh7305_probe(void)
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but on /* On SH7724, the divider ratio is given by 1 / (setting + 1), but on
the SH7305 it is 1 / (2^setting + 1). */ the SH7305 it is 1 / (2^setting + 1). */
int divb = CPG.FRQCR.BFC; int divb = CPG.FRQCRA.BFC;
int divi = CPG.FRQCR.IFC; int divi = CPG.FRQCRA.IFC;
int divp = CPG.FRQCR.P1FC; int divp = CPG.FRQCRA.P1FC;
freq.Bphi_div = 1 << (divb + 1); freq.Bphi_div = 1 << (divb + 1);
freq.Iphi_div = 1 << (divi + 1); freq.Iphi_div = 1 << (divi + 1);
@ -103,28 +115,24 @@ static void sh7305_probe(void)
#undef CPG #undef CPG
//--- //---
// Initialization // Initialization
//--- //---
void cpg_compute_freq(void)
{
/* This avoids warnings about sh7705_probe() being undefined when
building for fxcg50 */
#if GINT_HW_FX
isSH3() ? sh7705_probe() :
#endif
sh7305_probe();
}
static void configure(void) static void configure(void)
{ {
/* Disable spread spectrum in SSGSCR */ /* Disable spread spectrum in SSGSCR */
if(isSH4()) if(isSH4())
{
SH7305_CPG.SSCGCR.SSEN = 0; SH7305_CPG.SSCGCR.SSEN = 0;
}
cpg_compute_freq(); /* This avoids warnings about sh7705_probe() being undefined when
building for fxcg50 */
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
isSH3() ? sh7705_probe() :
#endif
sh7305_probe();
} }
//--- //---
@ -133,16 +141,12 @@ static void configure(void)
static void hsave(cpg_state_t *s) static void hsave(cpg_state_t *s)
{ {
if(isSH4()) if(isSH4()) s->SSCGCR = SH7305_CPG.SSCGCR.lword;
s->SSCGCR = SH7305_CPG.SSCGCR.lword;
cpg_get_overclock_setting(&s->speed);
} }
static void hrestore(cpg_state_t const *s) static void hrestore(cpg_state_t const *s)
{ {
if(isSH4()) if(isSH4()) SH7305_CPG.SSCGCR.lword = s->SSCGCR;
SH7305_CPG.SSCGCR.lword = s->SSCGCR;
cpg_set_overclock_setting(&s->speed);
} }
gint_driver_t drv_cpg = { gint_driver_t drv_cpg = {

View file

@ -1,514 +0,0 @@
//---
// gint:cpg:overclock - Clock speed control
//
// Most of the data in this file has been reused from Sentaro21's Ftune and
// Ptune utilities, which have long been the standard for overclocking CASIO
// calculators.
// See: http://pm.matrix.jp/ftune2e.html
//
// SlyVTT also contributed early testing on both the fx-CG 10/20 and fx-CG 50.
//---
#include <gint/clock.h>
#include <gint/gint.h>
#include <gint/hardware.h>
#include <gint/mpu/cpg.h>
#include <gint/mpu/bsc.h>
#include <gint/mpu/wdt.h>
//---
// Low-level clock speed access
//---
#define SH7305_SDMR3_CL2 ((volatile uint8_t *)0xFEC15040)
#define SH7305_SDMR3_CL3 ((volatile uint8_t *)0xFEC15060)
//---
// Predefined clock speeds
//---
/* SH7305 CPG */
#define SH4_PLL_32x 0b011111
#define SH4_PLL_26x 0b011001
#define SH4_PLL_16x 0b001111
#define SH4_DIV_2 0
#define SH4_DIV_4 1
#define SH4_DIV_8 2
#define SH4_DIV_16 3
#define SH4_DIV_32 4
/* SH7705-like CPG */
#define SH3_PLL_1x 0
#define SH3_PLL_2x 1
#define SH3_PLL_3x 2
#define SH3_PLL_4x 3
#define SH3_DIV_1 0
#define SH3_DIV_2 1
#define SH3_DIV_3 2
#define SH3_DIV_4 3
void cpg_get_overclock_setting(struct cpg_overclock_setting *s)
{
if(isSH3()) {
s->FLLFRQ = -1;
s->FRQCR = SH7705_CPG.FRQCR.word;
s->CS0BCR = SH7705_BSC.CS0BCR.lword;
s->CS0WCR = SH7705_BSC.CS0WCR.lword;
s->CS2BCR = SH7705_BSC.CS2BCR.lword;
s->CS2WCR = SH7705_BSC.CS2WCR.lword;
s->CS3BCR = SH7705_BSC.CS3BCR.lword;
s->CS3WCR = SH7705_BSC.CS3WCR.lword;
s->CS5aBCR = SH7705_BSC.CS5ABCR.lword;
s->CS5aWCR = SH7705_BSC.CS5AWCR.lword;
}
else {
s->FLLFRQ = SH7305_CPG.FLLFRQ.lword;
s->FRQCR = SH7305_CPG.FRQCR.lword;
s->CS0BCR = SH7305_BSC.CS0BCR.lword;
s->CS0WCR = SH7305_BSC.CS0WCR.lword;
s->CS2BCR = SH7305_BSC.CS2BCR.lword;
s->CS2WCR = SH7305_BSC.CS2WCR.lword;
s->CS3BCR = SH7305_BSC.CS3BCR.lword;
s->CS3WCR = SH7305_BSC.CS3WCR.lword;
s->CS5aBCR = SH7305_BSC.CS5ABCR.lword;
s->CS5aWCR = SH7305_BSC.CS5AWCR.lword;
}
}
static void cpg_low_level_set_setting(struct cpg_overclock_setting const *s)
{
if(isSH3()) {
SH7705_WDT.WTCNT.WRITE = 0;
SH7705_WDT.WTCSR.WRITE = 0x65;
SH7705_CPG.FRQCR.word = s->FRQCR | 0x1000;
SH7705_BSC.CS0BCR.lword = s->CS0BCR;
SH7705_BSC.CS0WCR.lword = s->CS0WCR;
SH7705_BSC.CS2BCR.lword = s->CS2BCR;
SH7705_BSC.CS2WCR.lword = s->CS2WCR;
SH7705_BSC.CS3BCR.lword = s->CS3BCR;
SH7705_BSC.CS3WCR.lword = s->CS3WCR;
SH7705_BSC.CS5ABCR.lword = s->CS5aBCR;
SH7705_BSC.CS5AWCR.lword = s->CS5aWCR;
}
else {
SH7305_BSC.CS0WCR.WR = 11; /* 18 cycles */
SH7305_CPG.FLLFRQ.lword = s->FLLFRQ;
SH7305_CPG.FRQCR.lword = s->FRQCR;
SH7305_CPG.FRQCR.KICK = 1;
while(SH7305_CPG.LSTATS != 0) {}
SH7305_BSC.CS0BCR.lword = s->CS0BCR;
SH7305_BSC.CS0WCR.lword = s->CS0WCR;
SH7305_BSC.CS2BCR.lword = s->CS2BCR;
SH7305_BSC.CS2WCR.lword = s->CS2WCR;
SH7305_BSC.CS3BCR.lword = s->CS3BCR;
SH7305_BSC.CS3WCR.lword = s->CS3WCR;
if(SH7305_BSC.CS3WCR.A3CL == 1)
*SH7305_SDMR3_CL2 = 0;
else
*SH7305_SDMR3_CL3 = 0;
SH7305_BSC.CS5ABCR.lword = s->CS5aBCR;
SH7305_BSC.CS5AWCR.lword = s->CS5aWCR;
}
}
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s)
{
uint32_t old_Pphi = clock_freq()->Pphi_f;
/* Wait for asynchronous tasks to complete */
gint_world_sync();
/* Disable interrupts during the change */
cpu_atomic_start();
/* Load the clock settings */
cpg_low_level_set_setting(s);
/* Determine the change in frequency for Pϕ and recompute CPG data */
cpg_compute_freq();
uint32_t new_Pphi = clock_freq()->Pphi_f;
/* Update timers' TCNT and TCOR to match the new clock speed */
void timer_rescale(uint32_t old_Pphi, uint32_t new_Pphi);
timer_rescale(old_Pphi, new_Pphi);
cpu_atomic_end();
}
#if GINT_HW_FX
static struct cpg_overclock_setting const settings_fx9860g_sh3[5] = {
/* CLOCK_SPEED_F1 */
{ .FRQCR = 0x1001,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F2 */
{ .FRQCR = (SH3_PLL_2x<<8)+(SH3_DIV_1<<4)+SH3_DIV_2,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F3 */
{ .FRQCR = (SH3_PLL_3x<<8)+(SH3_DIV_1<<4)+SH3_DIV_3,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F4 */
{ .FRQCR = (SH3_PLL_4x<<8)+(SH3_DIV_1<<4)+SH3_DIV_4,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F5 */
{ .FRQCR = (SH3_PLL_4x<<8)+(SH3_DIV_1<<4)+SH3_DIV_4,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000000C0,
.CS2WCR = 0x000100C0,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
};
static struct cpg_overclock_setting const settings_fx9860g_sh4[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004384,
.FRQCR = 0x0F202203,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000005C0,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x24920200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00020140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
.CS0BCR = 0x14900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000003C0,
.CS2WCR = 0x000302C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
};
static struct cpg_overclock_setting const settings_g35pe2[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004384,
.FRQCR = 0x0F112213,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x000001C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00031340 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x24920200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00020140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00031B40 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x14900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00020140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00031B40 },
};
#endif
#if GINT_HW_CG
static struct cpg_overclock_setting const settings_prizm[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = 0x0F102203,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_16<<12)+(SH4_DIV_16<<8)+SH4_DIV_32,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x00000140,
.CS2WCR = 0x000100C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_32,
.CS0BCR = 0x24900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x000002C0,
.CS2WCR = 0x000201C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_32,
.CS0BCR = 0x44900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x00000440,
.CS2WCR = 0x00040340,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
.CS0BCR = 0x34900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x000003C0,
.CS2WCR = 0x000402C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
};
static struct cpg_overclock_setting const settings_fxcg50[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = 0x0F011112,
.CS0BCR = 0x36DA0400,
.CS2BCR = 0x36DA3400,
.CS3BCR = 0x36DB4400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x000003C0,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000340,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000240,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x000002C0,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000440,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
};
#endif
static struct cpg_overclock_setting const *get_settings(void)
{
#if GINT_HW_FX
if(gint[HWCALC] == HWCALC_FX9860G_SH3)
return settings_fx9860g_sh3;
if(gint[HWCALC] == HWCALC_FX9860G_SH4)
return settings_fx9860g_sh4;
if(gint[HWCALC] == HWCALC_G35PE2)
return settings_g35pe2;
#endif
#if GINT_HW_CG
if(gint[HWCALC] == HWCALC_PRIZM)
return settings_prizm;
if(gint[HWCALC] == HWCALC_FXCG50)
return settings_fxcg50;
#endif
return NULL;
}
int clock_get_speed(void)
{
struct cpg_overclock_setting const *settings = get_settings();
if(!settings)
return CLOCK_SPEED_UNKNOWN;
if(isSH3()) {
for(int i = 0; i < 5; i++) {
struct cpg_overclock_setting const *s = &settings[i];
if(SH7705_CPG.FRQCR.word == (s->FRQCR | 0x1000)
&& SH7705_BSC.CS0BCR.lword == s->CS0BCR
&& SH7705_BSC.CS2BCR.lword == s->CS2BCR
&& SH7705_BSC.CS3BCR.lword == s->CS3BCR
&& SH7705_BSC.CS5ABCR.lword == s->CS5aBCR
&& SH7705_BSC.CS0WCR.lword == s->CS0WCR
&& SH7705_BSC.CS2WCR.lword == s->CS2WCR
&& SH7705_BSC.CS3WCR.lword == s->CS3WCR
&& SH7705_BSC.CS5AWCR.lword == s->CS5aWCR)
return CLOCK_SPEED_F1 + i;
}
}
else {
for(int i = 0; i < 5; i++) {
struct cpg_overclock_setting const *s = &settings[i];
if(SH7305_CPG.FLLFRQ.lword == s->FLLFRQ
&& SH7305_CPG.FRQCR.lword == s->FRQCR
&& SH7305_BSC.CS0BCR.lword == s->CS0BCR
&& SH7305_BSC.CS2BCR.lword == s->CS2BCR
&& SH7305_BSC.CS3BCR.lword == s->CS3BCR
&& SH7305_BSC.CS5ABCR.lword == s->CS5aBCR
&& SH7305_BSC.CS0WCR.lword == s->CS0WCR
&& SH7305_BSC.CS2WCR.lword == s->CS2WCR
&& SH7305_BSC.CS3WCR.lword == s->CS3WCR
&& SH7305_BSC.CS5AWCR.lword == s->CS5aWCR)
return CLOCK_SPEED_F1 + i;
}
}
return CLOCK_SPEED_UNKNOWN;
}
void clock_set_speed(int level)
{
if(level < CLOCK_SPEED_F1 || level > CLOCK_SPEED_F5)
return;
if(clock_get_speed() == level)
return;
struct cpg_overclock_setting const *settings = get_settings();
if(!settings)
return;
struct cpg_overclock_setting const *s = &settings[level - CLOCK_SPEED_F1];
cpg_set_overclock_setting(s);
}

View file

@ -45,15 +45,6 @@ static void hsave(cpu_state_t *s)
s->CPUOPM = cpu_getCPUOPM(); s->CPUOPM = cpu_getCPUOPM();
s->SR = cpu_getSR().lword; s->SR = cpu_getSR().lword;
} }
__asm__("stc r0_bank, %0" : "=r"(s->rN_bank[0]));
__asm__("stc r1_bank, %0" : "=r"(s->rN_bank[1]));
__asm__("stc r2_bank, %0" : "=r"(s->rN_bank[2]));
__asm__("stc r3_bank, %0" : "=r"(s->rN_bank[3]));
__asm__("stc r4_bank, %0" : "=r"(s->rN_bank[4]));
__asm__("stc r5_bank, %0" : "=r"(s->rN_bank[5]));
__asm__("stc r6_bank, %0" : "=r"(s->rN_bank[6]));
__asm__("stc r7_bank, %0" : "=r"(s->rN_bank[7]));
} }
static void hrestore(cpu_state_t const *s) static void hrestore(cpu_state_t const *s)
@ -64,15 +55,6 @@ static void hrestore(cpu_state_t const *s)
cpu_setCPUOPM(s->CPUOPM); cpu_setCPUOPM(s->CPUOPM);
cpu_setSR((cpu_sr_t)s->SR); cpu_setSR((cpu_sr_t)s->SR);
} }
__asm__("ldc %0, r0_bank" :: "r"(s->rN_bank[0]));
__asm__("ldc %0, r1_bank" :: "r"(s->rN_bank[1]));
__asm__("ldc %0, r2_bank" :: "r"(s->rN_bank[2]));
__asm__("ldc %0, r3_bank" :: "r"(s->rN_bank[3]));
__asm__("ldc %0, r4_bank" :: "r"(s->rN_bank[4]));
__asm__("ldc %0, r5_bank" :: "r"(s->rN_bank[5]));
__asm__("ldc %0, r6_bank" :: "r"(s->rN_bank[6]));
__asm__("ldc %0, r7_bank" :: "r"(s->rN_bank[7]));
} }
gint_driver_t drv_cpu = { gint_driver_t drv_cpu = {

View file

@ -146,7 +146,6 @@ bool dma_transfer_async(int channel, dma_size_t size, uint blocks,
static void dma_interrupt_transfer_ended(int channel) static void dma_interrupt_transfer_ended(int channel)
{ {
channel_t *ch = dma_channel(channel); channel_t *ch = dma_channel(channel);
ch->CHCR.IE = 0;
ch->CHCR.DE = 0; ch->CHCR.DE = 0;
ch->CHCR.TE = 0; ch->CHCR.TE = 0;
@ -160,40 +159,34 @@ static void dma_interrupt_transfer_ended(int channel)
if(dma_wait_ics[channel]) if(dma_wait_ics[channel])
cpu_csleep_cancel(dma_wait_ics[channel]); cpu_csleep_cancel(dma_wait_ics[channel]);
if(dma_callbacks[channel].function)
{
gint_call(dma_callbacks[channel]); gint_call(dma_callbacks[channel]);
dma_callbacks[channel] = GINT_CALL_NULL; dma_callbacks[channel] = GINT_CALL_NULL;
}
} }
/* dma_channel_wait(): Wait for a particular channel's transfer to finish /* dma_transfer_wait(): Wait for a transfer to finish */
void dma_transfer_wait(int channel)
This function is used both during normal gint operation and during foreign
unbinds of the DMA driver. The waiting method varies with interrupt settings
and device ownership. */
static void dma_channel_wait(int channel, bool foreign)
{ {
if(channel < 0)
{
for(int channel = 0; channel < 6; channel++)
dma_transfer_wait(channel);
return;
}
channel_t *ch = dma_channel(channel); channel_t *ch = dma_channel(channel);
if(!ch) return; if(!ch) return;
/* If interrupts are disabled or we don't own the device, spin-wait by /* Interrupt disabled: spin-wait */
checking either for TE to be set (Transfer Ended) or DE to be gone if(!ch->CHCR.IE)
(channel disabled).
There are definitely race conditions if the DMA is restarted between
our checks; only the context of the calls guarantee soundness.
* If interrupts are disabled, we assume there is no one that could
start the DMA again, since we are the only thread of execution.
* If the device is owned by another kernel, then we're transitioning
so we have to wait for *all* tasks to complete anyway. The risk is
rather to stop too early. */
if(!ch->CHCR.IE || foreign)
{ {
while(ch->CHCR.DE && !ch->CHCR.TE) {} while(ch->CHCR.DE && !ch->CHCR.TE) {}
return; return;
} }
/* Initialize an interrupt-cancellable sleep, to ensure /* Initialize an interrupt-cancellable sleep, to ensure synchronization */
synchronization */
cpu_csleep_t ics; cpu_csleep_t ics;
cpu_csleep_init(&ics); cpu_csleep_init(&ics);
dma_wait_ics[channel] = &ics; dma_wait_ics[channel] = &ics;
@ -206,12 +199,6 @@ static void dma_channel_wait(int channel, bool foreign)
dma_wait_ics[channel] = NULL; dma_wait_ics[channel] = NULL;
} }
/* dma_transfer_wait(): Wait for a transfer to finish */
void dma_transfer_wait(int channel)
{
dma_channel_wait(channel, false);
}
bool dma_transfer_sync(int channel, dma_size_t size, uint length, bool dma_transfer_sync(int channel, dma_size_t size, uint length,
void const *src, dma_address_t src_mode, void *dst, void const *src, dma_address_t src_mode, void *dst,
dma_address_t dst_mode) dma_address_t dst_mode)
@ -296,18 +283,10 @@ static void configure(void)
DMA.OR.DME = 1; DMA.OR.DME = 1;
} }
static void funbind(void) static void universal_unbind(void)
{ {
/* Wait for all OS transfers to finish before taking over */ /* Make sure any DMA transfer is finished before leaving the app */
for(int channel = 0; channel < 6; channel++) dma_transfer_wait(-1);
dma_channel_wait(channel, true);
}
static void unbind(void)
{
/* Make sure all DMA transfers are finished before leaving gint */
for(int channel = 0; channel < 6; channel++)
dma_channel_wait(channel, false);
} }
static bool hpowered(void) static bool hpowered(void)
@ -368,8 +347,8 @@ static void hrestore(dma_state_t const *s)
gint_driver_t drv_dma0 = { gint_driver_t drv_dma0 = {
.name = "DMA", .name = "DMA",
.configure = configure, .configure = configure,
.funbind = funbind, .funbind = universal_unbind,
.unbind = unbind, .unbind = universal_unbind,
.hpowered = hpowered, .hpowered = hpowered,
.hpoweron = hpoweron, .hpoweron = hpoweron,
.hpoweroff = hpoweroff, .hpoweroff = hpoweroff,

View file

@ -1,19 +0,0 @@
#include <unistd.h>
#include <gint/fs.h>
#include <errno.h>
int close(int fd)
{
fs_descriptor_t const *d = fs_get_descriptor(fd);
if(!d) {
errno = EBADF;
return (ssize_t)-1;
}
int rc = 0;
if(d->type->close)
rc = d->type->close(d->data);
fs_free_descriptor(fd);
return rc;
}

View file

@ -1,10 +0,0 @@
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
int closedir(DIR *dp)
{
int rc = close(dp->fd);
free(dp);
return rc;
}

View file

@ -1,6 +0,0 @@
#include <fcntl.h>
int creat(char const *path, mode_t mode)
{
return open(path, O_CREAT | O_WRONLY | O_TRUNC, mode);
}

View file

@ -1,27 +0,0 @@
#include <gint/fs.h>
#include <dirent.h>
#include <stdlib.h>
#include <errno.h>
#include "fugue/fugue.h"
DIR *fdopendir(int fd)
{
fs_descriptor_t const *desc = fs_get_descriptor(fd);
if(desc == NULL) {
errno = EBADF;
return NULL;
}
if(desc->type != &fugue_dir_descriptor_type) {
errno = ENOTDIR;
return NULL;
}
DIR *dp = malloc(sizeof *dp);
if(!dp) {
errno = ENOMEM;
return NULL;
}
dp->fd = fd;
return dp;
}

View file

@ -1,103 +0,0 @@
#include <gint/fs.h>
#include <gint/defs/attributes.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
/* File descriptor table */
static fs_descriptor_t *fdtable;
fs_descriptor_t const *fs_get_descriptor(int fd)
{
if(!fdtable || (unsigned)fd >= FS_FD_MAX)
return NULL;
if(fdtable[fd].type == NULL)
return NULL;
return &fdtable[fd];
}
int fs_create_descriptor(fs_descriptor_t const *data)
{
if(!fdtable || data->type == NULL)
return -1;
/* Leave 0/1/2 for stdin, stdout and stderr */
for(int i = 3; i < FS_FD_MAX; i++) {
if(fdtable[i].type == NULL) {
fdtable[i] = *data;
return i;
}
}
return -1;
}
void fs_free_descriptor(int fd)
{
if(!fdtable || (unsigned)fd >= FS_FD_MAX)
return;
fdtable[fd].type = NULL;
fdtable[fd].data = NULL;
}
int open_generic(fs_descriptor_type_t const *type, void *data, int fd)
{
if(!fdtable) {
errno = ENOMEM;
return -1;
}
if(!type) {
errno = EINVAL;
return -1;
}
fs_descriptor_t d = {
.type = type,
.data = data
};
/* Re-use file descriptor mode */
if(fd >= 0) {
if(fd >= FS_FD_MAX) {
errno = EBADF;
return -1;
}
if(fdtable[fd].type) {
errno = ENFILE;
return -1;
}
fdtable[fd] = d;
return fd;
}
/* Normal allocation mode */
else {
return fs_create_descriptor(&d);
}
}
/* Standard streams */
static fs_descriptor_type_t const devnull = {
.read = NULL,
.write = NULL,
.lseek = NULL,
.close = NULL,
};
GCONSTRUCTOR static void init_fs(void)
{
fdtable = calloc(FS_FD_MAX, sizeof *fdtable);
if(!fdtable)
return;
fdtable[STDIN_FILENO].type = &devnull;
fdtable[STDIN_FILENO].data = NULL;
fdtable[STDOUT_FILENO].type = &devnull;
fdtable[STDOUT_FILENO].data = NULL;
fdtable[STDERR_FILENO].type = &devnull;
fdtable[STDERR_FILENO].data = NULL;
}

View file

@ -1,24 +0,0 @@
#include <gint/bfile.h>
#include "util.h"
int BFile_Ext_Stat(uint16_t const *path, int *type, int *size)
{
int search_handle, rc;
uint16_t found_file[256];
struct BFile_FileInfo fileinfo;
rc = BFile_FindFirst(path, &search_handle, found_file, &fileinfo);
if(rc < 0) {
if(type) *type = -1;
if(size) *size = -1;
rc = -1;
}
else {
if(type) *type = fileinfo.type;
if(size) *size = fileinfo.file_size;
rc = 0;
}
BFile_FindClose(search_handle);
return rc;
}

View file

@ -1,145 +0,0 @@
#include <gint/fs.h>
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/mmu.h>
#include <gint/defs/util.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "fugue.h"
#include "util.h"
ssize_t fugue_read(void *data0, void *buf, size_t size)
{
fugue_fd_t *data = data0;
int fugue_fd = data->fd;
/* Fugue allows to read past EOF up to the end of the sector */
int filesize = BFile_Size(fugue_fd);
if(data->pos + (int)size > filesize)
size = filesize - data->pos;
int rc = BFile_Read(fugue_fd, buf, size, -1);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
return -1;
}
data->pos += rc;
return size;
}
static void *temp_ram(size_t total_size, size_t *block_size)
{
for(size_t candidate = 16384; candidate >= 64; candidate >>= 1) {
if(candidate > 64 && candidate >= 2 * total_size)
continue;
size_t size = min(candidate, total_size);
void *ram = malloc(size);
if(ram) {
*block_size = size;
return ram;
}
}
return NULL;
}
ssize_t fugue_write(void *data0, const void *buf, size_t size)
{
fugue_fd_t *data = data0;
int fugue_fd = data->fd;
/* The main concern of this function is that we cannot write from ROM.
If [buf] is in ROM then we have to copy it to RAM, preferably within
the limits of available heap memory. */
if(mmu_is_rom(buf)) {
size_t alloc_size, written=0;
void *ram = temp_ram(size, &alloc_size);
if(!ram) {
errno = ENOMEM;
return -1;
}
while(written < size) {
size_t block_size = min(size - written, alloc_size);
memcpy(ram, buf + written, block_size);
int rc = BFile_Write(fugue_fd, ram, block_size);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
written = -1;
break;
}
written += rc;
data->pos += rc;
/* Partial write */
if(rc < (int)block_size) break;
}
free(ram);
return written;
}
/* Otherwise, we can write normally */
else {
int rc = BFile_Write(fugue_fd, buf, size);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
return -1;
}
data->pos += rc;
return rc;
}
}
off_t fugue_lseek(void *data0, off_t offset, int whence)
{
fugue_fd_t *data = data0;
int fugue_fd = data->fd;
int filesize = BFile_Size(fugue_fd);
if(whence == SEEK_CUR)
offset += data->pos;
else if(whence == SEEK_END)
offset += filesize;
/* BFile_Seek() clamps to the file size, but still returns the argument
when it does... */
offset = min(offset, filesize);
int rc = BFile_Seek(fugue_fd, offset);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
return -1;
}
data->pos = offset;
/* rc is the amount of space left in the file (including pre-allocated
space), so instead just return offset directly */
return offset;
}
int fugue_close(void *data0)
{
fugue_fd_t *data = data0;
int fugue_fd = data->fd;
int rc = BFile_Close(fugue_fd);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
return -1;
}
free(data);
return 0;
}
const fs_descriptor_type_t fugue_descriptor_type = {
.read = fugue_read,
.write = fugue_write,
.lseek = fugue_lseek,
.close = fugue_close,
};

View file

@ -1,40 +0,0 @@
#ifndef FS_FUGUE_FUGUE_H
#define FS_FUGUE_FUGUE_H
#include <gint/fs.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
/* File descriptor type */
extern const fs_descriptor_type_t fugue_descriptor_type;
/* Directory descriptor type */
extern const fs_descriptor_type_t fugue_dir_descriptor_type;
/* Since on Graph 35+E II / fx-9860G III there is no (known) syscall to update
the file position, we need to track it ourselves, hopefully faithfully. */
typedef struct {
int fd;
int pos;
} fugue_fd_t;
/* Specific implementations of some standard functions */
int fugue_open(char const *path, int flags, mode_t mode);
int fugue_unlink(char const *path);
int fugue_rename(char const *oldpath, char const *newpath);
int fugue_mkdir(char const *path, mode_t mode);
int fugue_rmdir(char const *path);
int fugue_stat(char const * restrict path, struct stat * restrict statbuf);
/* Other functions */
void *fugue_dir_explore(char const *path);
#endif /* FS_FUGUE_FUGUE_H */

View file

@ -1,161 +0,0 @@
#include <gint/fs.h>
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "fugue.h"
typedef struct
{
/* Number of entries */
int count;
/* All entries */
struct dirent **entries;
/* Current position in directory */
off_t pos;
} dir_t;
/* There is no well-standardized API for reading from directory descriptors.
getdents(2) is a thing, but it's mostly Linux-specific and has no glibc
wrapper, so no userspace application is using it directly. We define our
directory descriptor interface to mimic opendir(3) for efficiency. */
ssize_t fugue_dir_read(void *data, void *buf, GUNUSED size_t size)
{
struct dirent **dirent_ptr = buf;
if(size < sizeof *dirent_ptr)
return 0;
dir_t *dp = data;
if(dp->pos >= dp->count)
*dirent_ptr = NULL;
else
*dirent_ptr = dp->entries[dp->pos++];
return sizeof *dirent_ptr;
}
ssize_t fugue_dir_write(GUNUSED void *data, GUNUSED const void *buf,
GUNUSED size_t size)
{
errno = EISDIR;
return -1;
}
off_t fugue_dir_lseek(void *data, off_t offset, int whence)
{
dir_t *dp = data;
if(whence == SEEK_CUR)
offset += dp->pos;
if(whence == SEEK_END)
offset += dp->count;
/* dp->count, being at the end of the enumeration, is a valid offset */
if(offset < 0 || offset >= dp->count + 1) {
errno = EINVAL;
return -1;
}
dp->pos = offset;
return dp->pos;
}
int fugue_dir_close(void *data)
{
dir_t *dp = data;
if(dp && dp->entries) {
for(int i = 0; i < dp->count; i++)
free(dp->entries[i]);
free(dp->entries);
}
free(dp);
return 0;
}
const fs_descriptor_type_t fugue_dir_descriptor_type = {
.read = fugue_dir_read,
.write = fugue_dir_write,
.lseek = fugue_dir_lseek,
.close = fugue_dir_close,
};
void *fugue_dir_explore(char const *path)
{
struct BFile_FileInfo info;
char *wildcard=NULL;
uint16_t *fc_path=NULL, *search=NULL;
/* We allocate by batches of 8 */
int sd=-1, rc, allocated=0;
dir_t *dp = malloc(sizeof *dp);
if(!dp) goto alloc_failure;
dp->count = 0;
dp->entries = NULL;
dp->pos = 0;
fc_path = malloc(512 * sizeof *fc_path);
if(!fc_path) goto alloc_failure;
wildcard = malloc(strlen(path) + 3);
if(!wildcard) goto alloc_failure;
strcpy(wildcard, path);
strcat(wildcard, "/*");
search = fs_path_normalize_fc(wildcard);
if(!search) goto alloc_failure;
rc = BFile_FindFirst(search, &sd, fc_path, &info);
if(rc < 0) {
if(rc != BFile_EntryNotFound)
errno = bfile_error_to_errno(rc);
goto error;
}
do {
if(dp->count+1 > allocated) {
struct dirent **new_entries = realloc(dp->entries,
(allocated + 8) * sizeof *dp->entries);
if(!new_entries)
goto alloc_failure;
dp->entries = new_entries;
allocated += 8;
}
size_t name_length = fc_len(fc_path);
size_t s = sizeof(struct dirent) + name_length + 1;
struct dirent *ent = malloc(s);
if(!ent) goto alloc_failure;
ent->d_ino = 0;
ent->d_type = bfile_type_to_dirent(info.type);
fc_to_utf8(ent->d_name, fc_path, name_length + 1);
dp->entries[dp->count++] = ent;
rc = BFile_FindNext(sd, fc_path, &info);
}
while(rc >= 0);
goto end;
alloc_failure:
errno = ENOMEM;
error:
fugue_dir_close(dp);
dp = NULL;
end:
free(wildcard);
free(search);
free(fc_path);
if(sd >= 0)
BFile_FindClose(sd);
return dp;
}

View file

@ -1,26 +0,0 @@
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/fs.h>
#include <errno.h>
#include "util.h"
int fugue_mkdir(char const *path, GUNUSED mode_t mode)
{
ENOTSUP_IF_NOT_FUGUE(-1);
uint16_t *fcpath = fs_path_normalize_fc(path);
if(!fcpath) {
errno = ENOMEM;
return -1;
}
int rc = BFile_Create(fcpath, BFile_Folder, NULL);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
free(fcpath);
return -1;
}
free(fcpath);
return 0;
}

View file

@ -1,147 +0,0 @@
#include <gint/fs.h>
#include <gint/bfile.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "util.h"
#include "fugue.h"
static int new_file_size;
int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
{
ENOTSUP_IF_NOT_FUGUE(-1);
uint16_t *fcpath = fs_path_normalize_fc(path);
int fugue_fd, err, rc=-1, type, fd=-1;
if(!fcpath) {
errno = ENOMEM;
return -1;
}
/* Open mode */
int bfile_mode = BFile_ReadOnly;
if(flags & O_WRONLY)
bfile_mode = BFile_WriteOnly;
else if(flags & O_RDWR)
bfile_mode = BFile_ReadWrite;
/* Exclusive open means no sense unless creation is also requested */
bool excl = (flags & O_EXCL) && (flags & O_CREAT);
/* Truncation requires the file to be removed/recreated */
bool trunc = (flags & O_TRUNC) && (flags & O_CREAT);
/* Stat the entry. A special case is needed for the root, which doesn't
respond well. fs_path_normalize_fc() normalizes the path so we just
have to check for the fixed string "\\fls0\". */
bool exists;
if(!memcmp(fcpath, u"\\\\fls0\\", 16)) {
exists = true;
type = BFile_Type_Directory;
}
else {
exists = (BFile_Ext_Stat(fcpath, &type, NULL) == 0);
if(!exists) type = -1;
}
/* If the entry exists and O_EXCL was requested, fail. */
if(exists && excl) {
errno = EEXIST;
rc = -1;
goto end;
}
/* If the entry is not a directory but O_DIRECTORY is set, fail. If the
directory doesn't exist, we fail regardless of O_CREAT. */
if((flags & O_DIRECTORY) && type != BFile_Type_Directory) {
errno = (exists ? ENOTDIR : ENOENT);
rc = -1;
goto end;
}
/* If the entry is a directory, open it as such */
if(type == BFile_Type_Directory) {
void *dp = fugue_dir_explore(path);
if(!dp) {
rc = -1;
goto end;
}
fs_descriptor_t data = {
.type = &fugue_dir_descriptor_type,
.data = dp,
};
fd = fs_create_descriptor(&data);
rc = fd;
goto end;
}
/* Try and open the file normally, unless O_TRUNC is specified without
O_EXCL, in which case we simply delete and recreate the file. */
if(trunc)
fugue_fd = BFile_EntryNotFound;
else
fugue_fd = BFile_Open(fcpath, bfile_mode);
/* If O_TRUNC is requested and either the file exists or we can create
it, remove it. (If fugue_fd < 0 an opening error might still have
occurred so we delete it just in case.) */
if((flags & O_TRUNC) && (fugue_fd >= 0 || (flags & O_CREAT))) {
if(fugue_fd >= 0)
BFile_Close(fugue_fd);
BFile_Remove(fcpath);
fugue_fd = BFile_EntryNotFound;
}
/* If the file does not exist and O_CREAT is set, create it */
if((flags & O_CREAT) && ((flags & O_TRUNC) || fugue_fd < 0)) {
new_file_size = 0;
err = BFile_Create(fcpath, BFile_File, &new_file_size);
if(err < 0) {
errno = bfile_error_to_errno(err);
rc = -1;
goto end;
}
fugue_fd = BFile_Open(fcpath, bfile_mode);
}
if(fugue_fd < 0) {
errno = bfile_error_to_errno(fugue_fd);
rc = fugue_fd;
goto end;
}
/* If O_APPEND is set, move to the end of the file */
// TODO: O_APPEND should move the cursor before *each* write
int pos = 0;
if((flags & O_APPEND)) {
pos = BFile_Size(fugue_fd);
BFile_Seek(fugue_fd, pos);
}
/* Return the now-open file descriptor */
fugue_fd_t *data = malloc(sizeof *data);
if(!data) {
BFile_Close(fugue_fd);
goto end;
}
data->fd = fugue_fd;
data->pos = pos;
fs_descriptor_t fd_data = {
.type = &fugue_descriptor_type,
.data = data,
};
rc = fd = fs_create_descriptor(&fd_data);
if(fd == -1) {
BFile_Close(fugue_fd);
free(data);
errno = ENFILE;
goto end;
}
end:
free(fcpath);
return rc;
}

View file

@ -1,38 +0,0 @@
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/fs.h>
#include <errno.h>
#include <sys/stat.h>
#include "util.h"
int fugue_rename(char const *oldpath, char const *newpath)
{
ENOTSUP_IF_NOT_FUGUE(-1);
int rc = -1;
uint16_t *fcpath_1 = NULL;
uint16_t *fcpath_2 = NULL;
fcpath_1 = fs_path_normalize_fc(oldpath);
if(!fcpath_1) {
errno = ENOMEM;
goto end;
}
fcpath_2 = fs_path_normalize_fc(newpath);
if(!fcpath_2) {
errno = ENOMEM;
goto end;
}
rc = BFile_Rename(fcpath_1, fcpath_2);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
rc = -1;
}
else rc = 0;
end:
free(fcpath_1);
free(fcpath_2);
return rc;
}

View file

@ -1,51 +0,0 @@
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/fs.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include "util.h"
#include "fugue.h"
int fugue_rmdir(char const *path)
{
ENOTSUP_IF_NOT_FUGUE(-1);
/* Check if the folder is empty */
DIR *dp = opendir(path);
if(!dp) return -1;
bool empty = true;
struct dirent *ent;
while((ent = readdir(dp))) {
if(strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0) {
empty = false;
break;
}
}
closedir(dp);
if(!empty) {
errno = ENOTEMPTY;
return -1;
}
uint16_t *fcpath = fs_path_normalize_fc(path);
if(!fcpath) {
errno = ENOMEM;
return -1;
}
int rc = BFile_Remove(fcpath);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
rc = -1;
}
else rc = 0;
free(fcpath);
return rc;
}

View file

@ -1,36 +0,0 @@
#include <sys/stat.h>
#include <gint/fs.h>
#include <gint/bfile.h>
#include "fugue.h"
#include <errno.h>
#include <stdlib.h>
#include "util.h"
int fugue_stat(char const * restrict path, struct stat * restrict statbuf)
{
ENOTSUP_IF_NOT_FUGUE(-1);
uint16_t *fcpath = fs_path_normalize_fc(path);
if(!fcpath) {
errno = ENOMEM;
return -1;
}
int type, size, rc;
rc = BFile_Ext_Stat(fcpath, &type, &size);
free(fcpath);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
return -1;
}
statbuf->st_mode = bfile_type_to_mode_t(type) | 0777;
statbuf->st_size = -1;
if(S_ISREG(statbuf->st_mode)) {
statbuf->st_size = size;
}
return 0;
}

View file

@ -1,41 +0,0 @@
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/fs.h>
#include <errno.h>
#include <sys/stat.h>
#include "util.h"
int fugue_unlink(char const *path)
{
ENOTSUP_IF_NOT_FUGUE(-1);
uint16_t *fcpath = fs_path_normalize_fc(path);
if(!fcpath) {
errno = ENOMEM;
return -1;
}
int type, size, rc;
rc = BFile_Ext_Stat(fcpath, &type, &size);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
rc = -1;
goto end;
}
if(bfile_type_to_mode_t(type) != S_IFREG) {
errno = ENOTDIR;
rc = -1;
goto end;
}
rc = BFile_Remove(fcpath);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
rc = -1;
}
else rc = 0;
end:
free(fcpath);
return rc;
}

View file

@ -1,245 +0,0 @@
#include "util.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <gint/bfile.h>
#include <dirent.h>
#include <sys/stat.h>
int bfile_error_to_errno(int e)
{
/* TODO: Find BFile code for too many fds and map it to ENFILE. */
switch(e) {
case BFile_EntryNotFound: return ENOENT;
case BFile_IllegalPath: return EINVAL;
case BFile_DeviceFull: return EDQUOT;
case BFile_IllegalDevice: return EINVAL;
case BFile_AccessDenied: return EACCES;
case BFile_PermissionError: return EACCES;
case BFile_EntryFull: return EDQUOT;
case BFile_AlreadyExists: return EEXIST;
case BFile_ReadOnlyFile: return EACCES;
case BFile_EnumerateEnd: return ENOENT;
case BFile_IllegalSeekPos: return EINVAL;
case BFile_NotMountDevice: return ENOENT;
case BFile_DeviceNotFound: return ENOENT;
default: return errno;
}
}
int bfile_type_to_mode_t(int bfile_type)
{
switch(bfile_type) {
case BFile_Type_Directory: return S_IFDIR;
case BFile_Type_Dot: return S_IFDIR;
case BFile_Type_DotDot: return S_IFDIR;
case BFile_Type_MainMem: return S_IFBLK;
case BFile_Type_File: return S_IFREG;
case BFile_Type_Archived: return S_IFREG;
default: return S_IFREG;
}
}
int bfile_type_to_dirent(int bfile_type)
{
switch(bfile_type) {
case BFile_Type_Directory: return DT_DIR;
case BFile_Type_Dot: return DT_DIR;
case BFile_Type_DotDot: return DT_DIR;
case BFile_Type_MainMem: return DT_BLK;
case BFile_Type_File: return DT_REG;
case BFile_Type_Archived: return DT_REG;
default: return DT_UNKNOWN;
}
}
/* Length of FONTCHARACTER and UTF-8 strings, counting only ASCII characters */
size_t utf8_len(char const *utf8)
{
size_t len = 0;
for(size_t i = 0; utf8[i] != 0; i++)
len += (utf8[i] >= 0 && (uint8_t)utf8[i] <= 0x7f);
return len;
}
size_t fc_len(uint16_t const *fc)
{
size_t len = 0;
for(size_t i = 0; fc[i] != 0 && fc[i] != 0xffff; i++)
len += (fc[i] <= 0x7f);
return len;
}
void utf8_to_fc(uint16_t *fc, char const *utf8, size_t fc_len)
{
size_t j = 0;
for(size_t i = 0; j < fc_len && utf8[i] != 0; i++) {
if(utf8[i] == '/')
fc[j++] = '\\';
else if(utf8[i] >= 0 && (uint8_t)utf8[i] <= 0x7f)
fc[j++] = utf8[i];
}
if(j < fc_len)
fc[j++] = 0;
}
void fc_to_utf8(char *utf8, uint16_t const *fc, size_t utf8_len)
{
size_t j = 0;
for(size_t i = 0; j < utf8_len && fc[i] != 0 && fc[i] != 0xffff; i++) {
if(fc[i] == '\\')
utf8[j++] = '/';
else if(fc[i] <= 0x7f)
utf8[j++] = fc[i];
}
if(j < utf8_len)
utf8[j++] = 0;
}
uint16_t *utf8_to_fc_alloc(uint16_t *prefix, char const *utf8,
uint16_t *suffix)
{
size_t lenp=0, lens=0;
if(prefix) {
while(prefix[lenp] != 0 && prefix[lenp] != 0xffff)
lenp++;
}
if(suffix) {
while(suffix[lens] != 0 && suffix[lens] != 0xffff)
lens++;
}
size_t len = utf8_len(utf8);
uint16_t *fc = malloc((lenp+len+lens+1) * sizeof *fc);
if(fc != NULL) {
if(prefix)
memcpy(fc, prefix, lenp * sizeof *prefix);
utf8_to_fc(fc + lenp, utf8, len);
if(suffix)
memcpy(fc + lenp + len, suffix, lens * sizeof *suffix);
fc[lenp+len+lens] = 0;
}
return fc;
}
char *fc_to_utf8_alloc(uint16_t const *fc)
{
size_t len = fc_len(fc);
char *utf8 = malloc(len+1);
if(utf8 != NULL)
fc_to_utf8(utf8, fc, len+1);
return utf8;
}
/* Split a path into components. Only the top array needs to be freed */
char **fs_split_components(char *path, int *count)
{
char **comps = NULL;
*count = 0;
int allocated = 0;
char *token = strtok(path, "/");
while(token) {
if(*count + 1 > allocated) {
allocated += 8;
char **new_comps = realloc(comps,
allocated * sizeof *comps);
if(!new_comps) {
*count = -1;
return NULL;
}
comps = new_comps;
}
comps[*count] = token;
*count += 1;
token = strtok(NULL, "/");
}
return comps;
}
/* Generalization of fs_path_normalize(). Returns a [char *] if use_fc=false,
otherwise returns an [uint16_t *]. */
static void *path_normalize(char const *path, bool use_fc)
{
char *path_dup = strdup(path);
if(!path_dup) return NULL;
int components=0;
char **comps = fs_split_components(path_dup, &components);
if(components == -1) {
free(path_dup);
return NULL;
}
/* Now rewrite comps[] to skip "." and apply ".." */
int wr = 0;
for(int rd=0; rd < components; rd++) {
char *comp = comps[rd];
if(!strcmp(comp, "."))
continue;
else if(!strcmp(comp, ".."))
wr -= (wr > 0);
else
comps[wr++] = comp;
}
/* Count total length */
int length = (use_fc ? 7 : 1) + (wr >= 1 ? wr - 1 : 0);
for(int i = 0; i < wr; i++) {
length += utf8_len(comps[i]);
}
/* Allocate string then copy */
if(use_fc) {
uint16_t *fc = malloc((length + 1) * sizeof *fc);
uint16_t *fc_init = fc;
memcpy(fc, u"\\\\fls0\\", 7*2);
fc += 7;
for(int i = 0; i < wr; i++) {
if(i > 0) *fc++ = '\\';
utf8_to_fc(fc, comps[i], (size_t)-1);
fc += utf8_len(comps[i]);
}
*fc++ = 0;
free(path_dup);
free(comps);
return fc_init;
}
else {
char *utf8 = malloc(length + 1);
char *utf8_init = utf8;
*utf8++ = '/';
for(int i = 0; i < wr; i++) {
if(i > 0) *utf8++ = '/';
strcpy(utf8, comps[i]);
utf8 += utf8_len(comps[i]);
}
*utf8++ = 0;
free(path_dup);
free(comps);
return utf8_init;
}
}
char *fs_path_normalize(char const *path)
{
return path_normalize(path, false);
}
uint16_t *fs_path_normalize_fc(char const *path)
{
return path_normalize(path, true);
}

View file

@ -1,51 +0,0 @@
#ifndef FS_UTIL_H
#define FS_UTIL_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdlib.h>
#include <gint/hardware.h>
#include <errno.h>
#define ENOTSUP_IF_NOT_FUGUE(rc) \
if(gint[HWFS] != HWFS_FUGUE) { errno = ENOTSUP; return (rc); }
/* Translate common BFile error codes to errno values. */
int bfile_error_to_errno(int bfile_error_code);
/* Translate BFile file types to st_mode values. */
int bfile_type_to_mode_t(int bfile_type);
/* Translate BFile file types to struct dirent values. */
int bfile_type_to_dirent(int bfile_type);
/* TODO: These functions do not actually translate special characters between
encodings, they simply strip them. */
/* Length of UTF-8 string _as copied by utf8_to_fc functions_ */
size_t utf8_len(char const *utf8);
/* Length of FONTCHARACTER string _as copied by fc_to_utf8 functions_ */
size_t fc_len(uint16_t const *fc);
/* Convert UTF-8 to FONTCHARACTER; outputs fc_len characters with padding. If
fc[fc_len-1] is not 0 after the call, then fc is too short. */
void utf8_to_fc(uint16_t *fc, char const *utf8, size_t fc_len);
/* Same in the other direction. */
void fc_to_utf8(char *utf8, uint16_t const *fc, size_t utf8_len);
/* Same as utf8_to_fc() but allocates a string with malloc(). */
uint16_t *utf8_to_fc_alloc(uint16_t *prefix, char const *utf8,
uint16_t *suffix);
/* Same as fc_to_utf8() but allocates a string with malloc(). */
char *fc_to_utf8_alloc(uint16_t const *fc);
#ifdef __cplusplus
}
#endif
#endif /* FS_UTIL_H */

View file

@ -1,23 +0,0 @@
#include <unistd.h>
#include <gint/fs.h>
#include <errno.h>
off_t lseek(int fd, off_t offset, int whence)
{
if(whence != SEEK_SET && whence != SEEK_CUR && whence != SEEK_END) {
errno = EINVAL;
return (off_t)-1;
}
fs_descriptor_t const *d = fs_get_descriptor(fd);
if(!d) {
errno = EBADF;
return (ssize_t)-1;
}
if(d->type->lseek)
return d->type->lseek(d->data, offset, whence);
/* No seek function: cannot seek */
return 0;
}

View file

@ -1,8 +0,0 @@
#include <unistd.h>
#include "fugue/fugue.h"
int mkdir(char const *path, mode_t mode)
{
/* Standard mkdir() is the Fugue filesystem only */
return fugue_mkdir(path, mode);
}

View file

@ -1,14 +0,0 @@
#include <fcntl.h>
#include <stdarg.h>
#include "fugue/fugue.h"
int open(char const *path, int flags, ...)
{
va_list args;
va_start(args, flags);
mode_t mode = va_arg(args, int);
va_end(args);
/* Standard open() is the Fugue filesystem only */
return fugue_open(path, flags, mode);
}

View file

@ -1,23 +0,0 @@
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
DIR *opendir(const char *name)
{
DIR *dp = malloc(sizeof *dp);
if(!dp) {
errno = ENOMEM;
return NULL;
}
int fd = open(name, O_DIRECTORY | O_RDONLY);
if(fd < 0) {
free(dp);
return NULL;
}
dp->fd = fd;
return dp;
}

View file

@ -1,24 +0,0 @@
#include <unistd.h>
#include <gint/fs.h>
#include <errno.h>
ssize_t pread(int fd, void *buf, size_t size, off_t offset)
{
off_t current = lseek(fd, 0, SEEK_CUR);
if(current == (off_t)-1)
return (ssize_t)-1;
ssize_t rc = -1;
if(lseek(fd, offset, SEEK_SET) == (off_t)-1)
goto end;
rc = read(fd, buf, size);
if(rc < 0)
goto end;
end:
/* At the end, always try to restore the current position */
lseek(fd, current, SEEK_CUR);
return rc;
}

View file

@ -1,22 +0,0 @@
#include <unistd.h>
ssize_t pwrite(int fd, const void *buf, size_t size, off_t offset)
{
off_t current = lseek(fd, 0, SEEK_CUR);
if(current == (off_t)-1)
return (ssize_t)-1;
ssize_t rc = -1;
if(lseek(fd, offset, SEEK_SET) == (off_t)-1)
goto end;
rc = write(fd, buf, size);
if(rc < 0)
goto end;
end:
/* At the end, always try to restore the current position */
lseek(fd, current, SEEK_CUR);
return rc;
}

View file

@ -1,18 +0,0 @@
#include <unistd.h>
#include <gint/fs.h>
#include <errno.h>
ssize_t read(int fd, void *buf, size_t size)
{
fs_descriptor_t const *d = fs_get_descriptor(fd);
if(!d) {
errno = EBADF;
return (ssize_t)-1;
}
if(d->type->read)
return d->type->read(d->data, buf, size);
/* No read function: we can't read anything */
return 0;
}

View file

@ -1,9 +0,0 @@
#include <dirent.h>
#include <unistd.h>
struct dirent *readdir(DIR *dp)
{
struct dirent *ent = NULL;
read(dp->fd, &ent, sizeof ent);
return ent;
}

View file

@ -1,8 +0,0 @@
#include <stdio.h>
#include "fugue/fugue.h"
int rename(char const *oldpath, char const *newpath)
{
/* Standard rename() is the Fugue filesystem only */
return fugue_rename(oldpath, newpath);
}

View file

@ -1,7 +0,0 @@
#include <dirent.h>
#include <unistd.h>
void rewinddir(DIR *dp)
{
lseek(dp->fd, 0, SEEK_SET);
}

View file

@ -1,8 +0,0 @@
#include <unistd.h>
#include "fugue/fugue.h"
int rmdir(char const *path)
{
/* Standard rmdir() is the Fugue filesystem only */
return fugue_rmdir(path);
}

View file

@ -1,7 +0,0 @@
#include <dirent.h>
#include <unistd.h>
void seekdir(DIR *dp, long loc)
{
lseek(dp->fd, loc, SEEK_SET);
}

View file

@ -1,8 +0,0 @@
#include <sys/stat.h>
#include "fugue/fugue.h"
int stat(char const * restrict path, struct stat * restrict statbuf)
{
/* Standard stat() is the Fugue filesystem only */
return fugue_stat(path, statbuf);
}

View file

@ -1,7 +0,0 @@
#include <dirent.h>
#include <unistd.h>
long telldir(DIR *dp)
{
return lseek(dp->fd, 0, SEEK_CUR);
}

View file

@ -1,8 +0,0 @@
#include <unistd.h>
#include "fugue/fugue.h"
int unlink(char const *path)
{
/* Standard unlink() is the Fugue filesystem only */
return fugue_unlink(path);
}

View file

@ -1,18 +0,0 @@
#include <unistd.h>
#include <gint/fs.h>
#include <errno.h>
ssize_t write(int fd, const void *buf, size_t size)
{
fs_descriptor_t const *d = fs_get_descriptor(fd);
if(!d) {
errno = EBADF;
return (ssize_t)-1;
}
if(d->type->write)
return d->type->write(d->data, buf, size);
/* No write function: discard the contents but show no error */
return size;
}

View file

@ -1,9 +0,0 @@
icons-i1msb.png:
type: bopti-image
profile: mono
name: gint_gdb_icons_i1msb
icons-rgb565.png:
type: bopti-image
profile: rgb565
name: gint_gdb_icons_rgb565

View file

@ -1,7 +0,0 @@
.global _gdb_stubcall_write
_gdb_stubcall_write:
mov #64, r3
trapa #33
rts
nop

View file

@ -1,881 +0,0 @@
#include <gint/cpu.h>
#include <gint/exc.h>
#include <gint/gdb.h>
#include <gint/ubc.h>
#include <gint/usb-ff-bulk.h>
#include <gint/usb.h>
#include <gint/video.h>
#include <gint/display.h>
#include <gint/config.h>
#include <gint/hardware.h>
#include <gint/fs.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define GDB_VISUAL_FEEDBACK 1
#define GDB_BRIDGE_LOGS 0
/* Note about trap numbers:
- trapa #16...#23 were historically used for Linux's syscall
interface for syscalls with up to 0...7 arguments.
- trapa #31 is unified syscall interface (Linux)
- trapa #32 is GDB software breakpoint
- trapa #33 shall thus be our stubcall. */
#define TRA_SWBREAK 32
#define TRA_STUBCALL 33
#if GDB_VISUAL_FEEDBACK
enum { ICON_WORKING, ICON_ERROR, ICON_COMM, ICON_IDLE };
static void gdb_show_stub_status(int icon)
{
// TODO[3]: Use normal way for both fx and cg (and remove display.h include)
#if GINT_RENDER_MONO
extern bopti_image_t gint_gdb_icons_i1msb;
dsubimage(120, 0, &gint_gdb_icons_i1msb, 8*icon, 0, 8, 5, DIMAGE_NONE);
dupdate();
#else
video_mode_t const *M = video_get_current_mode();
if(!M)
return;
extern image_t gint_gdb_icons_rgb565;
if(M->format == IMAGE_RGB565) {
image_t sub;
image_sub(&gint_gdb_icons_rgb565, 6*icon, 0, 7, 7, &sub);
if(!video_update(M->width-7, 0, &sub, 0))
abort();
}
#endif
}
#else
# define gdb_show_stub_status(...) ((void)0)
#endif
static void gdb_hexlify(char* output_string, const uint8_t* input_buffer, size_t input_size)
{
const char* hex = "0123456789ABCDEF";
for (size_t i = 0; i < input_size; i++) {
uint8_t byte = input_buffer[i];
output_string[i*2 + 0] = hex[(byte & 0xF0) >> 4];
output_string[i*2 + 1] = hex[byte & 0x0F];
}
}
// TODO : bug in fxlibc ? strtoul doesn't support uppercase
static uint32_t gdb_unhexlify_sized(const char* input_string, size_t input_length)
{
uint32_t ret = 0;
for (size_t i = 0; i < input_length; i++) {
uint8_t nibble_hex = tolower(input_string[i]);
uint8_t nibble = nibble_hex >= 'a' && nibble_hex <= 'f' ? nibble_hex - 'a' + 10 :
nibble_hex >= '0' && nibble_hex <= '9' ? nibble_hex - '0' : 0;
ret = (ret << 4) | nibble;
}
return ret;
}
static uint32_t gdb_unhexlify(const char* input_string)
{
return gdb_unhexlify_sized(input_string, strlen(input_string));
}
static bool gdb_started = false;
static void gdb_send(const char *data, size_t size)
{
usb_fxlink_header_t header;
usb_fxlink_fill_header(&header, "gdb", "remote", size);
int pipe = usb_ff_bulk_output();
usb_write_sync(pipe, &header, sizeof(header), false);
usb_write_sync(pipe, data, size, false);
usb_commit_sync(pipe);
}
static void gdb_send_start(void)
{
usb_fxlink_header_t header;
usb_fxlink_fill_header(&header, "gdb", "start", 0);
int pipe = usb_ff_bulk_output();
usb_write_sync(pipe, &header, sizeof(header), false);
usb_commit_sync(pipe);
}
static char *gdb_recv_buffer = NULL;
static const size_t gdb_recv_buffer_capacity = 256;
static size_t gdb_recv_buffer_size = 0;
static ssize_t gdb_recv(char *buffer, size_t buffer_size)
{
if (gdb_recv_buffer_size >= buffer_size) {
memcpy(buffer, gdb_recv_buffer, buffer_size);
memmove(gdb_recv_buffer, &gdb_recv_buffer[buffer_size], gdb_recv_buffer_size - buffer_size);
gdb_recv_buffer_size -= buffer_size;
return buffer_size;
}
usb_fxlink_header_t header;
while (!usb_fxlink_handle_messages(&header)) {
sleep();
}
// TODO : should we abort or find a way to gracefully shutdown the debugger ?
if (strncmp(header.application, "gdb", 16) == 0
&& strncmp(header.type, "remote", 16) == 0) {
if (header.size > gdb_recv_buffer_capacity - gdb_recv_buffer_size) {
abort();
}
usb_read_sync(usb_ff_bulk_input(), &gdb_recv_buffer[gdb_recv_buffer_size], header.size, false);
gdb_recv_buffer_size += header.size;
return gdb_recv(buffer, buffer_size);
} else {
abort();
}
}
static ssize_t gdb_recv_packet(char* buffer, size_t buffer_size)
{
char read_char;
// Waiting for packet start '$'
do {
if (gdb_recv(&read_char, 1) != 1) {
return -1;
}
} while (read_char != '$');
uint8_t checksum = 0;
size_t packet_len = 0;
while (true) {
if (gdb_recv(&read_char, 1) != 1) {
return -1;
}
if (read_char != '#') {
// -1 to ensure space for a NULL terminator
if (packet_len >= (buffer_size - 1)) {
return -1;
}
buffer[packet_len++] = read_char;
checksum += read_char;
} else {
break;
}
}
buffer[packet_len] = '\0';
char read_checksum_hex[3];
if (gdb_recv(read_checksum_hex, 2) != 2) {
return -1;
}
read_checksum_hex[2] = '\0';
uint8_t read_checksum = gdb_unhexlify(read_checksum_hex);
if (read_checksum != checksum) {
read_char = '-';
gdb_send(&read_char, 1);
return -1;
} else {
read_char = '+';
gdb_send(&read_char, 1);
return packet_len;
}
}
static ssize_t gdb_send_packet(const char* packet, size_t packet_length)
{
if (packet == NULL || packet_length == 0) {
// Empty packet
gdb_send("$#00", 4);
return 4;
}
size_t buffer_length = packet_length + 1 + 4;
// TODO : find if it's more efficient to malloc+copy on each packet or send 3 small fxlink messages
char* buffer = malloc(buffer_length);
uint8_t checksum = 0;
for (size_t i = 0; i < packet_length; i++) {
checksum += packet[i];
}
buffer[0] = '$';
memcpy(&buffer[1], packet, packet_length);
snprintf(&buffer[buffer_length - 4], 4, "#%02X", checksum);
// -1 to not send the NULL terminator of snprintf
gdb_send(buffer, buffer_length - 1);
free(buffer);
return buffer_length;
}
#if GDB_BRIDGE_LOGS
static void gdb_send_bridge_log(const char* fmt, ...)
{
char str[256];
va_list args;
va_start(args, fmt);
vsnprintf(str, sizeof str, fmt, args);
va_end(args);
usb_fxlink_text(str, strlen(str));
}
#else
# define gdb_send_bridge_log(...)
#endif
static int gdb_signal_number = 0;
static int gdb_trap_number = 0;
static void gdb_send_stop_reply(void)
{
char str[4] = "S00";
uint8_t num = gdb_signal_number ? gdb_signal_number : 5 /* SIGTRAP */;
gdb_hexlify(str+1, &num, 1);
gdb_send_packet(str, 3);
}
static void gdb_handle_qXfer_packet(const char* packet, const char* data, size_t data_size)
{
char offset_hex[16] = {0}, length_hex[16] = {0};
for (size_t i = 0; i < sizeof(offset_hex); i++) {
offset_hex[i] = *(packet++); // consume offset
if (*packet == ',') break;
}
packet++; // consume ','
for (size_t i = 0; i < sizeof(length_hex); i++) {
length_hex[i] = *(packet++); // consume length
if (*packet == '\0') break;
}
size_t offset = (size_t)gdb_unhexlify(offset_hex);
size_t length = (size_t)gdb_unhexlify(length_hex);
if (offset >= data_size) {
gdb_send_packet("l", 1);
} else if (offset + length >= data_size) {
char *reply_buffer = malloc(data_size - offset + 1);
reply_buffer[0] = 'l';
memcpy(&reply_buffer[1], &data[offset], data_size - offset);
gdb_send_packet(reply_buffer, data_size - offset + 1);
free(reply_buffer);
} else {
char *reply_buffer = malloc(length + 1);
reply_buffer[0] = 'm';
memcpy(&reply_buffer[1], &data[offset], length);
gdb_send_packet(reply_buffer, length + 1);
free(reply_buffer);
}
}
/* We implement the memory-map qXfer extension to mark add-in memory as read-only
* and enforce hardware breakpoints.
* See : https://sourceware.org/gdb/onlinedocs/gdb/Memory-Map-Format.html
* https://sourceware.org/gdb/onlinedocs/gdb/Set-Breaks.html
*/
// TODO : Should we mark other regions as ROM ?
static const char gdb_memory_map_xml[] = "<?xml version=\"1.0\"?>"
"<!DOCTYPE memory-map PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"
"<memory-map>"
// P0 mapping of add-in file
"<memory type=\"rom\" start=\"0x00300000\" length=\"0x00200000\"/>"
// P0 mapping of user RAM area
"<memory type=\"ram\" start=\"0x08100000\" length=\"0x00080000\"/>"
// Physical mapping of RAM chip (fx-CG 50)
"<memory type=\"ram\" start=\"0x8c000000\" length=\"0x01000000\"/>"
// Physical mapping of RAM chip (fx-CG 10/20 + emulator)
"<memory type=\"ram\" start=\"0x88000000\" length=\"0x01000000\"/>"
"</memory-map>";
static void gdb_handle_query_packet(const char* packet)
{
if (strncmp("qSupported", packet, 10) == 0) {
const char* qsupported_ans = "PacketSize=255;qXfer:memory-map:read+";
gdb_send_packet(qsupported_ans, strlen(qsupported_ans));
} else if (strncmp("qXfer:memory-map:read::", packet, 23) == 0) {
// -1 to not send the NULL terminator
gdb_handle_qXfer_packet(&packet[23], gdb_memory_map_xml, sizeof(gdb_memory_map_xml) - 1);
} else {
gdb_send_packet(NULL, 0);
}
}
static void gdb_handle_read_general_registers(gdb_cpu_state_t* cpu_state)
{
char reply_buffer[23*8];
if (!cpu_state) {
memset(reply_buffer, 'x', sizeof(reply_buffer));
memcpy(&reply_buffer[offsetof(gdb_cpu_state_t, reg.pc)*2],
"A0000000", 8); // pc needs to be set to make GDB happy
} else {
gdb_hexlify(reply_buffer, (uint8_t*)cpu_state->regs,
sizeof(cpu_state->regs));
}
gdb_send_packet(reply_buffer, sizeof(reply_buffer));
}
static void gdb_handle_read_register(gdb_cpu_state_t* cpu_state, const char* packet)
{
uint8_t register_id = gdb_unhexlify(&packet[1]);
char reply_buffer[8];
if (!cpu_state || register_id >= sizeof(cpu_state->regs)/sizeof(uint32_t)) {
memset(reply_buffer, 'x', sizeof(reply_buffer));
} else {
gdb_hexlify(reply_buffer, (uint8_t*)&cpu_state->regs[register_id],
sizeof(cpu_state->regs[register_id]));
}
gdb_send_packet(reply_buffer, sizeof(reply_buffer));
}
static void gdb_handle_write_general_registers(gdb_cpu_state_t* cpu_state, const char* packet)
{
if (!cpu_state) {
gdb_send_packet(NULL, 0);
return;
}
packet++; // consume 'G'
// Let's not handle incomplete 'G' packets as they're rarely used anyway
if (strlen(packet) != sizeof(cpu_state->regs)*2) {
gdb_send_packet(NULL, 0);
return;
}
for (size_t i = 0; i < sizeof(cpu_state->regs)/sizeof(uint32_t); i++) {
cpu_state->regs[i] = gdb_unhexlify_sized(&packet[i * sizeof(uint32_t) * 2],
sizeof(uint32_t) * 2);
}
gdb_send_packet("OK", 2);
}
static void gdb_handle_write_register(gdb_cpu_state_t* cpu_state, const char* packet)
{
if (!cpu_state) {
gdb_send_packet(NULL, 0);
return;
}
char register_id_hex[16] = {0}, value_hex[16] = {0};
packet++; // consume 'P'
for (size_t i = 0; i < sizeof(register_id_hex); i++) {
register_id_hex[i] = *(packet++); // consume register id
if (*packet == '=') break;
}
packet++; // consume '='
for (size_t i = 0; i < sizeof(value_hex); i++) {
value_hex[i] = *(packet++); // consume register value
if (*packet == '\0') break;
}
uint32_t register_id = gdb_unhexlify(register_id_hex);
uint32_t value = gdb_unhexlify(value_hex);
if (register_id >= sizeof(cpu_state->regs)/sizeof(uint32_t)) {
gdb_send_packet(NULL, 0);
} else {
cpu_state->regs[register_id] = value;
gdb_send_packet("OK", 2);
}
}
static volatile bool gdb_tlbh_enable = false;
static volatile bool gdb_tlbh_caught = false;
static void gdb_handle_read_memory(const char* packet)
{
char address_hex[16] = {0}, size_hex[16] = {0};
uint8_t* read_address;
size_t read_size;
packet++; // consume 'm'
for (size_t i = 0; i < sizeof(address_hex); i++) {
address_hex[i] = *(packet++); // consume address
if (*packet == ',') break;
}
packet++; // consume ','
for (size_t i = 0; i < sizeof(size_hex); i++) {
size_hex[i] = *(packet++); // consume size
if (*packet == '\0') break;
}
read_address = (uint8_t*) gdb_unhexlify(address_hex);
read_size = (size_t) gdb_unhexlify(size_hex);
char *reply_buffer = malloc(read_size * 2);
gdb_tlbh_enable = true;
gdb_tlbh_caught = false;
for (size_t i = 0; i < read_size && !gdb_tlbh_caught; i++) {
gdb_hexlify(&reply_buffer[i * 2], &read_address[i], 1);
}
gdb_tlbh_enable = false;
if (gdb_tlbh_caught) {
gdb_send_packet("E22", 3); // EINVAL
gdb_tlbh_caught = false;
} else {
gdb_send_packet(reply_buffer, read_size * 2);
}
free(reply_buffer);
}
static void cache_ocbwb(void *start, void *end)
{
/* Cache lines are 32-aligned */
void *p = (void *)((uintptr_t)start & -32);
while(p < end) {
__asm__("ocbwb @%0":: "r"(p));
p += 32;
}
}
static void cache_icbi(void *start, void *end)
{
/* Cache lines are 32-aligned */
void *p = (void *)((uintptr_t)start & -32);
while(p < end) {
__asm__("icbi @%0":: "r"(p));
p += 32;
}
}
static void gdb_handle_write_memory(const char* packet)
{
char address_hex[16] = {0}, size_hex[16] = {0};
uint8_t* read_address;
size_t read_size;
packet++; // consume 'M'
for (size_t i = 0; i < sizeof(address_hex); i++) {
address_hex[i] = *(packet++); // consume address
if (*packet == ',') break;
}
packet++; // consume ','
for (size_t i = 0; i < sizeof(size_hex); i++) {
size_hex[i] = *(packet++); // consume size
if (*packet == ':') break;
}
packet++; // consume ':'
read_address = (uint8_t*) gdb_unhexlify(address_hex);
read_size = (size_t) gdb_unhexlify(size_hex);
gdb_tlbh_enable = true;
gdb_tlbh_caught = false;
for (size_t i = 0; i < read_size && !gdb_tlbh_caught; i++) {
read_address[i] = (uint8_t)gdb_unhexlify_sized(&packet[i * 2], 2);
}
gdb_tlbh_enable = false;
cache_ocbwb(read_address, read_address + read_size);
cache_icbi(read_address, read_address + read_size);
if (gdb_tlbh_caught) {
gdb_send_packet("E22", 3); // EINVAL
gdb_tlbh_caught = false;
} else {
gdb_send_packet("OK", 2);
}
}
static bool gdb_parse_hardware_breakpoint_packet(const char* packet, void** read_address)
{
packet++; // consume 'z' or 'Z'
if (*packet != '1') { // hardware breakpoint
return false;
}
packet++; // consume '1'
packet++; // consume ','
char address_hex[16] = {0}, kind_hex[16] = {0};
for (size_t i = 0; i < sizeof(address_hex); i++) {
address_hex[i] = *(packet++); // consume address
if (*packet == ',') break;
}
packet++; // consume ','
for (size_t i = 0; i < sizeof(kind_hex); i++) {
kind_hex[i] = *(packet++); // consume kind
if (*packet == '\0' || *packet == ';') break;
}
*read_address = (void*) gdb_unhexlify(address_hex);
uint32_t read_kind = gdb_unhexlify(kind_hex);
if (read_kind != 2) { // SuperH instructions are 2 bytes long
return false;
}
return true;
}
static void gdb_handle_insert_hardware_breakpoint(const char* packet)
{
void* read_address;
if (!gdb_parse_hardware_breakpoint_packet(packet, &read_address)) {
gdb_send_bridge_log("bad Z packet\n");
gdb_send_packet(NULL, 0);
return;
}
void *channel0_addr, *channel1_addr;
bool channel0_used = ubc_get_break_address(0, &channel0_addr);
bool channel1_used = ubc_get_break_address(1, &channel1_addr);
/* As stated by GDB doc : "the operations should be implemented in an idempotent way."
* Thus we first check if the breakpoint is already placed in one of the UBC channel.
*/
if ((channel0_used && channel0_addr == read_address) ||
(channel1_used && channel1_addr == read_address)) {
gdb_send_bridge_log("hb %p: already exists\n", read_address);
gdb_send_packet("OK", 2);
} else if (!channel0_used) {
ubc_set_breakpoint(0, read_address, UBC_BREAK_BEFORE);
gdb_send_bridge_log("hb %p: using channel 0\n", read_address);
gdb_send_packet("OK", 2);
} else if (!channel1_used) {
ubc_set_breakpoint(1, read_address, UBC_BREAK_BEFORE);
gdb_send_bridge_log("hb %p: using channel 1\n", read_address);
gdb_send_packet("OK", 2);
} else {
/* TODO : We should find a proper way to inform GDB that we are
* limited by the number of UBC channels.
*/
gdb_send_bridge_log("hb %p: channels used (%p, %p)\n", read_address,
channel0_addr, channel1_addr);
gdb_send_packet(NULL, 0);
}
}
static void gdb_handle_remove_hardware_breakpoint(const char* packet)
{
void* read_address;
if (!gdb_parse_hardware_breakpoint_packet(packet, &read_address)) {
gdb_send_packet(NULL, 0);
return;
}
void *channel0_addr, *channel1_addr;
bool channel0_used = ubc_get_break_address(0, &channel0_addr);
bool channel1_used = ubc_get_break_address(1, &channel1_addr);
if (channel0_used && channel0_addr == read_address) {
ubc_disable_channel(0);
}
if (channel1_used && channel1_addr == read_address) {
ubc_disable_channel(1);
}
gdb_send_packet("OK", 2);
}
static void gdb_handle_continue_with_signal(gdb_cpu_state_t* cpu_state,
const char* packet)
{
packet++; // consume 'C'
int signal = gdb_unhexlify_sized(packet, 2);
char exit[4] = { 'X', packet[0], packet[1], 0 };
packet += 2;
if(*packet == ';')
cpu_state->reg.pc = gdb_unhexlify(packet + 1);
// TODO: This is a heuristic replacing the normal signal system
uint32_t kills =
(1 << 4) /* SIGILL */
+ (1 << 6) /* SIGABRT */
+ (1 << 7) /* SIGEMT */
+ (1 << 8) /* SIGFPE */
+ (1 << 9) /* SIGKILL */
+ (1 << 11) /* SIGSEGV */
+ (1 << 15); /* SIGTERM */
// Abort if the signal is kill by default
if((uint)signal < 32 && (kills >> signal) & 1) {
gdb_send_packet(exit, 3);
abort();
}
}
static struct {
bool single_stepped;
bool channel0_used;
bool channel1_used;
void* channel0_addr;
void* channel1_addr;
} gdb_single_step_backup = { false };
static void gdb_handle_single_step(uint32_t pc, ubc_break_mode_t break_mode)
{
gdb_single_step_backup.channel0_used = ubc_get_break_address(0, &gdb_single_step_backup.channel0_addr);
gdb_single_step_backup.channel1_used = ubc_get_break_address(1, &gdb_single_step_backup.channel1_addr);
ubc_disable_channel(0);
ubc_set_breakpoint(1, (void*)pc, break_mode);
gdb_single_step_backup.single_stepped = true;
}
static bool gdb_handle_stubcall(gdb_cpu_state_t* cpu_state)
{
char str[30];
int sc_num = cpu_state->reg.r3;
if(sc_num == 64) { /* write */
int len = snprintf(str, sizeof str, "Fwrite,%x,%08x,%x",
cpu_state->reg.r4,
cpu_state->reg.r5,
cpu_state->reg.r6);
gdb_send_packet(str, len);
return true;
}
return false;
}
void gdb_main(gdb_cpu_state_t* cpu_state)
{
if (!gdb_started && gdb_start()) {
gdb_show_stub_status(ICON_ERROR);
return;
}
gdb_show_stub_status(ICON_IDLE);
if (gdb_single_step_backup.single_stepped) {
if (gdb_single_step_backup.channel0_used) {
ubc_set_breakpoint(0, gdb_single_step_backup.channel0_addr, UBC_BREAK_BEFORE);
} else {
ubc_disable_channel(0);
}
if (gdb_single_step_backup.channel1_used) {
ubc_set_breakpoint(1, gdb_single_step_backup.channel1_addr, UBC_BREAK_BEFORE);
} else {
ubc_disable_channel(1);
}
gdb_single_step_backup.single_stepped = false;
}
if (cpu_state != NULL) {
/* Ajust PC after a software breakpoint */
if (gdb_trap_number == TRA_SWBREAK)
cpu_state->reg.pc -= 2;
/* Handle stubcall but fallback to normal stop if it fails */
if (gdb_trap_number != TRA_STUBCALL || !gdb_handle_stubcall(cpu_state))
gdb_send_stop_reply();
}
while (1) {
gdb_show_stub_status(ICON_COMM);
char packet_buffer[256];
ssize_t packet_size = gdb_recv_packet(packet_buffer, sizeof(packet_buffer));
if (packet_size <= 0) {
// TODO : Should we break or log on recv error ?
continue;
}
gdb_show_stub_status(ICON_WORKING);
switch (packet_buffer[0]) {
case '?': // Halt reason
gdb_send_stop_reply();
break;
case 'q':
gdb_handle_query_packet(packet_buffer);
break;
case 'g':
gdb_handle_read_general_registers(cpu_state);
break;
case 'p':
gdb_handle_read_register(cpu_state, packet_buffer);
break;
case 'm':
gdb_handle_read_memory(packet_buffer);
break;
case 'G':
gdb_handle_write_general_registers(cpu_state, packet_buffer);
break;
case 'P':
gdb_handle_write_register(cpu_state, packet_buffer);
break;
case 'M':
gdb_handle_write_memory(packet_buffer);
break;
case 'k': // Kill request
abort();
case 'Z':
gdb_handle_insert_hardware_breakpoint(packet_buffer);
break;
case 'z':
gdb_handle_remove_hardware_breakpoint(packet_buffer);
break;
case 's':
gdb_handle_single_step(cpu_state->reg.pc, UBC_BREAK_AFTER);
goto ret;
case 'c': // Continue
goto ret;
case 'C': // Continue with signal
gdb_handle_continue_with_signal(cpu_state, packet_buffer);
// We'll often abort() at the signal rather than continuing
goto ret;
case 'F': // Continue after File I/O call response
// TODO: parse 'F' response packets.
goto ret;
default: // Unsupported packet
gdb_send_packet(NULL, 0);
break;
}
gdb_show_stub_status(ICON_IDLE);
}
ret:
// We're started after the first round of exchanges
gdb_started = true;
gdb_signal_number = 0;
gdb_trap_number = 0;
}
static void gdb_notifier_function(void)
{
// We ignore fxlink notifications when we're already inside GDB code.
if (ubc_dbh_lock || !gdb_started)
return;
// We make sure we are called during a USB interrupt.
if (usb_interrupt_context == NULL)
return;
// And we make sure an other step break is not already set up.
if (gdb_single_step_backup.single_stepped)
return;
gdb_handle_single_step(usb_interrupt_context->spc, UBC_BREAK_AFTER);
}
static int gdb_panic_handler(uint32_t code)
{
// Catch memory access errors from GDB trying to read/print stuff
if (gdb_tlbh_enable) {
// We only handle TLB miss reads (0x040) and writes (0x060)
if (code != 0x040 && code != 0x060)
return 1;
gdb_tlbh_caught = true;
// We skip the offending instruction and continue
gint_exc_skip(1);
return 0;
}
// If we are in user code, let's break
else if (!ubc_dbh_lock) {
// We make sure an other step break is not already set up
if (gdb_single_step_backup.single_stepped)
return 1;
// TODO: This only works for re-execution type exceptions
uint32_t spc;
__asm__("stc spc, %0" : "=r"(spc));
gdb_handle_single_step(spc, UBC_BREAK_BEFORE);
// Break reason
if(code == 0x040 || code == 0x060 || code == 0x0e0 || code == 0x100)
gdb_signal_number = 11; /* SIGSEGV */
if(code == 0x160)
gdb_signal_number = 5; /* SIGTRAP */
if(code == 0x180 || code == 0x1a0)
gdb_signal_number = 4; /* SIGILL */
if(code >= 0x1000 && code != 0x10a0)
gdb_signal_number = 5; /* SIGTRAP */
if(code == 0x10a0)
gdb_signal_number = 7; /* SIGEMT (used here for bad UBC breaks) */
// Specific stop reasons
if(code == 0x160) {
uint32_t TRA = isSH3() ? 0xffffffd0 : 0xff000020;
gdb_trap_number = *(uint32_t volatile *)TRA >> 2;
}
return 0;
}
return 1;
}
static bool gdb_redirect_stdout = false;
static bool gdb_redirect_stderr = false;
static fs_descriptor_type_t const redirect_type = {
.read = NULL,
.write = (void *)gdb_stubcall_write,
.lseek = NULL,
.close = NULL,
};
int gdb_start(void)
{
if (gdb_started)
return 0;
gdb_show_stub_status(ICON_WORKING);
if(usb_is_open() && !usb_is_open_interface(&usb_ff_bulk))
usb_close();
if(!usb_is_open()) {
usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
if(usb_open(interfaces, GINT_CALL_NULL) < 0)
return -1;
usb_open_wait();
}
usb_fxlink_set_notifier(gdb_notifier_function);
gdb_send_start();
if (!gdb_recv_buffer) {
gdb_recv_buffer = malloc(gdb_recv_buffer_capacity);
}
// Redirect standard streams
if(gdb_redirect_stdout) {
close(STDOUT_FILENO);
open_generic(&redirect_type, (void *)STDOUT_FILENO, STDOUT_FILENO);
}
if(gdb_redirect_stderr) {
close(STDERR_FILENO);
open_generic(&redirect_type, (void *)STDERR_FILENO, STDERR_FILENO);
}
// TODO : Should we detect if other panic or debug handlers are setup ?
gint_exc_catch(gdb_panic_handler);
ubc_set_debug_handler(gdb_main);
return 0;
}
void gdb_start_on_exception(void)
{
gint_exc_catch(gdb_panic_handler);
ubc_set_debug_handler(gdb_main);
}
void gdb_redirect_streams(bool stdout, bool stderr)
{
gdb_redirect_stdout = stdout;
gdb_redirect_stderr = stderr;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 B

View file

@ -11,15 +11,6 @@
#include <stdlib.h> #include <stdlib.h>
#include "../render-fx/render-fx.h" #include "../render-fx/render-fx.h"
#include "../render/render.h"
#include <gint/config.h>
#if GINT_HW_CG
#include <gint/drivers/r61524.h>
#endif
// TODO: Move the gray "engine" part into the T6K11 driver.
#if GINT_RENDER_MONO
/* Three additional video RAMS, allocated statically if --static-gray was set /* Three additional video RAMS, allocated statically if --static-gray was set
at configure time, or with malloc() otherwise. */ at configure time, or with malloc() otherwise. */
@ -30,8 +21,6 @@ GBSS static uint32_t gvrams[3][256];
/* Four VRAMs: two to draw and two to display */ /* Four VRAMs: two to draw and two to display */
static uint32_t *vrams[4] = { NULL, NULL, NULL, NULL }; static uint32_t *vrams[4] = { NULL, NULL, NULL, NULL };
#if GINT_HW_FX
/* Current VRAM pair used for drawing; the value can either be 0 (draws to /* Current VRAM pair used for drawing; the value can either be 0 (draws to
VRAMs 0 and 1) or 2 (draws to VRAMs 2 and 3). */ VRAMs 0 and 1) or 2 (draws to VRAMs 2 and 3). */
static int volatile st = 0; static int volatile st = 0;
@ -46,17 +35,12 @@ static int runs = 0;
/* Delays of the light and dark frames for the above setting */ /* Delays of the light and dark frames for the above setting */
GBSS static int delays[2]; GBSS static int delays[2];
static int gray_int(void);
#endif
/* The alternate rendering mode structure used to override d*() */ /* The alternate rendering mode structure used to override d*() */
static struct rendering_mode const gray_mode = { static struct rendering_mode const gray_mode = {
.dupdate = gupdate, .dupdate = gupdate,
.dclear = gclear, .dclear = gclear,
.drect = grect, .drect = grect,
.dpixel = gpixel, .dpixel = gpixel,
.dgetpixel = ggetpixel,
.gint_dhline = gint_ghline, .gint_dhline = gint_ghline,
.gint_dvline = gint_gvline, .gint_dvline = gint_gvline,
.dtext_opt = gtext_opt, .dtext_opt = gtext_opt,
@ -67,7 +51,6 @@ static struct rendering_mode const gray_exit_mode = {
.dclear = NULL, .dclear = NULL,
.drect = NULL, .drect = NULL,
.dpixel = NULL, .dpixel = NULL,
.dgetpixel = NULL,
.gint_dhline = NULL, .gint_dhline = NULL,
.gint_dvline = NULL, .gint_dvline = NULL,
.dtext_opt = NULL, .dtext_opt = NULL,
@ -78,16 +61,13 @@ static struct rendering_mode const gray_exit_mode = {
// Engine control (init/quit and start/stop) // Engine control (init/quit and start/stop)
//--- //---
static int gray_int(void);
static void gray_quit(void); static void gray_quit(void);
/* gray_isinit(): Check whether the engine is initialized and ready to run */ /* gray_isinit(): Check whether the engine is initialized and ready to run */
static int gray_isinit(void) static int gray_isinit(void)
{ {
return (vrams[0] && vrams[1] && vrams[2] && vrams[3] return (vrams[0] && vrams[1] && vrams[2] && vrams[3] && timer >= 0);
#if GINT_HW_FX
&& timer >= 0
#endif
);
} }
/* gray_init(): Initialize the engine /* gray_init(): Initialize the engine
@ -108,7 +88,6 @@ GCONSTRUCTOR static void gray_init(void)
vrams[3] = malloc(1024); vrams[3] = malloc(1024);
#endif /* GINT_STATIC_GRAY */ #endif /* GINT_STATIC_GRAY */
#if GINT_HW_FX
/* Default delays from Graph 35+E II are different from other models */ /* Default delays from Graph 35+E II are different from other models */
if(gint[HWCALC] == HWCALC_G35PE2) if(gint[HWCALC] == HWCALC_G35PE2)
{ {
@ -124,7 +103,6 @@ GCONSTRUCTOR static void gray_init(void)
/* Try to obtain the timer right away */ /* Try to obtain the timer right away */
timer = timer_configure(GRAY_TIMER | GRAY_CLOCK, 1000, timer = timer_configure(GRAY_TIMER | GRAY_CLOCK, 1000,
GINT_CALL(gray_int)); GINT_CALL(gray_int));
#endif
/* On failure, release the resources that we obtained */ /* On failure, release the resources that we obtained */
if(!gray_isinit()) gray_quit(); if(!gray_isinit()) gray_quit();
@ -143,13 +121,10 @@ GDESTRUCTOR static void gray_quit(void)
vrams[3] = NULL; vrams[3] = NULL;
#endif /* GINT_STATIC_GRAY */ #endif /* GINT_STATIC_GRAY */
#if GINT_HW_FX
if(timer >= 0) timer_stop(timer); if(timer >= 0) timer_stop(timer);
timer = -1; timer = -1;
#endif
} }
#if GINT_HW_FX
/* gray_start(): Start the gray engine */ /* gray_start(): Start the gray engine */
static void gray_start(void) static void gray_start(void)
{ {
@ -166,7 +141,6 @@ static void gray_stop(void)
runs = 0; runs = 0;
st = 0; st = 0;
} }
#endif
//--- //---
// Dynamic udpate and rendering mode // Dynamic udpate and rendering mode
@ -220,7 +194,6 @@ int dgray(int mode)
return 0; return 0;
} }
#if GINT_HW_FX
/* gray_int(): Interrupt handler */ /* gray_int(): Interrupt handler */
int gray_int(void) int gray_int(void)
{ {
@ -252,19 +225,6 @@ int gupdate(void)
st ^= 2; st ^= 2;
return 0; return 0;
} }
#elif GINT_HW_CG
int gupdate(void)
{
if(dmode == &gray_exit_mode)
{
dmode = NULL;
return 1;
}
r61524_display_gray_128x64(vrams[0], vrams[1]);
return 0;
}
#endif
//--- //---
// Query and configuration functions // Query and configuration functions
@ -276,7 +236,6 @@ int dgray_enabled(void)
return (dmode == &gray_mode); return (dmode == &gray_mode);
} }
#if GINT_HW_FX
/* dgray_setdelays(): Set the gray engine delays */ /* dgray_setdelays(): Set the gray engine delays */
void dgray_setdelays(uint32_t light, uint32_t dark) void dgray_setdelays(uint32_t light, uint32_t dark)
{ {
@ -308,12 +267,3 @@ void dgray_getscreen(uint32_t **light, uint32_t **dark)
if(light) *light = vrams[base & 2]; if(light) *light = vrams[base & 2];
if(dark) *dark = vrams[base | 1]; if(dark) *dark = vrams[base | 1];
} }
#elif GINT_HW_CG
void dgray_getvram(uint32_t **light, uint32_t **dark)
{
*light = vrams[0];
*dark = vrams[1];
}
#endif
#endif /* GINT_RENDER_MONO && GINT_HW_FX */

View file

@ -1,7 +1,4 @@
#include <gint/gray.h> #include <gint/gray.h>
#include <gint/config.h>
#if GINT_RENDER_MONO
/* gclear(): Fill the screen with a single color */ /* gclear(): Fill the screen with a single color */
void gclear(color_t color) void gclear(color_t color)
@ -38,5 +35,3 @@ void gclear(color_t color)
dark += 8; dark += 8;
} }
} }
#endif /* GINT_RENDER_MONO */

View file

@ -1,20 +0,0 @@
#include <gint/gray.h>
#include <gint/defs/types.h>
#include <gint/config.h>
#if GINT_RENDER_MONO
int ggetpixel(int x, int y)
{
uint32_t *light, *dark;
dgray_getvram(&light, &dark);
int offset = (y << 2) + (x >> 5);
uint32_t mask = 1 << (~x & 31);
bool l = (light[offset] & mask) != 0;
bool d = (dark [offset] & mask) != 0;
return (d << 1) | l;
}
#endif /* GINT_RENDER_MONO */

View file

@ -1,8 +1,5 @@
#include <gint/display.h> #include <gint/display.h>
#include <gint/defs/util.h> #include <gint/defs/util.h>
#include <gint/config.h>
#if GINT_RENDER_MONO
/* gint_ghline(): Optimized horizontal line, but not actually optimized */ /* gint_ghline(): Optimized horizontal line, but not actually optimized */
void gint_ghline(int x1, int x2, int y, int color) void gint_ghline(int x1, int x2, int y, int color)
@ -17,5 +14,3 @@ void gint_gvline(int y1, int y2, int x, int color)
if(y1 > y2) swap(y1, y2); if(y1 > y2) swap(y1, y2);
for(int y = y1; y <= y2; y++) dpixel(x, y, color); for(int y = y1; y <= y2; y++) dpixel(x, y, color);
} }
#endif /* GINT_RENDER_MONO */

View file

@ -1,8 +1,5 @@
#include <gint/gray.h> #include <gint/gray.h>
#include <gint/defs/types.h> #include <gint/defs/types.h>
#include <gint/config.h>
#if GINT_RENDER_MONO
/* gpixel(): Change a pixel's color */ /* gpixel(): Change a pixel's color */
void gpixel(int x, int y, color_t color) void gpixel(int x, int y, color_t color)
@ -58,5 +55,3 @@ void gpixel(int x, int y, color_t color)
break; break;
} }
} }
#endif /* GINT_RENDER_MONO */

View file

@ -1,9 +1,6 @@
#include <gint/defs/util.h> #include <gint/defs/util.h>
#include <gint/gray.h> #include <gint/gray.h>
#include "../render-fx/render-fx.h" #include "../render-fx/render-fx.h"
#include <gint/config.h>
#if GINT_RENDER_MONO
/* grect(): Fill a rectangle on the screen */ /* grect(): Fill a rectangle on the screen */
void grect(int x1, int y1, int x2, int y2, color_t color) void grect(int x1, int y1, int x2, int y2, color_t color)
@ -115,5 +112,3 @@ void grect(int x1, int y1, int x2, int y2, color_t color)
light++, dark++; light++, dark++;
} }
} }
#endif /* GINT_RENDER_MONO */

View file

@ -1,8 +1,6 @@
#include <gint/gray.h> #include <gint/gray.h>
#include "../render-fx/render-fx.h" #include "../render-fx/render-fx.h"
#include <gint/config.h> #include "../render-fx/bopti-asm.h"
#if GINT_RENDER_MONO
#pragma GCC optimize("O3") #pragma GCC optimize("O3")
@ -39,5 +37,3 @@ void gsubimage(bopti_image_t const *img, struct rbox *r, GUNUSED int flags)
bopti_render(img, r, light, dark); bopti_render(img, r, light, dark);
} }
} }
#endif /* GINT_RENDER_MONO */

Some files were not shown because too many files have changed in this diff Show more