forked from Lephenixnoir/gint
Compare commits
80 Commits
Author | SHA1 | Date |
---|---|---|
Lephe | 4f9b141b79 | |
Lephe | 02150d90e5 | |
Lephe | 126d1506ac | |
Lephe | 6ea2f991a3 | |
Lephe | 4e1136d0ac | |
Lephe | 1c3c9727a5 | |
Lephe | e8b4bcc9cb | |
Lephe | b17d4de4a8 | |
Lephe | f69f92b938 | |
Lephe | 8c9c48a91e | |
Lephe | 291c3cef17 | |
Lephe | b942bc5d19 | |
Lephe | c2ff07427b | |
Lephe | 09c13676d3 | |
Lephe | 780acb3fc9 | |
Lephe | 818f950fff | |
Lephe | 9468a8d725 | |
Lephe | fc6f7d3051 | |
Lephe | 5a69e44078 | |
Lephe | 7822899b1f | |
Lephe | d2f788a3fc | |
Lephe | 667f43b45c | |
Lephe | ede19fc878 | |
Lephe | a4df076214 | |
Lephe | 7a3604ccbb | |
Lephe | f219e5c882 | |
Lephe | 904ab74984 | |
Lephe | 8210524152 | |
Lephe | 26c5b76037 | |
Lephe | e57efb5e37 | |
Lephe | 0c2935055e | |
Lephe | fdadb0dd71 | |
Lephe | b10c065abe | |
Lephe | a3ce29b7b8 | |
Lephe | a7bcf6cd77 | |
Lephe | 4223164063 | |
Lephe | 93169e8803 | |
Lephe | 2bf5dd93f4 | |
Lephe | f300338a57 | |
Lephe | 48325fc54d | |
Lephe | 36d66a6317 | |
Lephe | d8b85a9fab | |
Lephe | 227c06631b | |
Lephe | d6ada7f11f | |
Lephe | 8aea762e7a | |
Lephe | 59a3b39fb4 | |
Lephe | ef8707ee9d | |
Lephe | 3aa1471ac5 | |
Lephe | 9b02f5f1db | |
Lephe | 472f1245c7 | |
Lephe | b549fd68ba | |
Lephe | ed30895a49 | |
Lephe | aed90d9b3c | |
Lephe | 9cae0040b5 | |
Lephe | 34c73ba0ba | |
Lephe | 6903bd58d5 | |
Lephe | 321d6937e0 | |
Lephe | 8635880bbb | |
Lephe | 71de4dcb95 | |
Lephe | 2e5e56f82e | |
Lephe | 86fad757e1 | |
Lephe | b7173a5109 | |
Lephe | 055c8f405b | |
Lephe | caf585b0a1 | |
Lephe | 1384c54b5f | |
Lephe | 73446aae61 | |
Lephe | 192ff17303 | |
Lephe | 7adcdea5f1 | |
Lephe | 2d0e18f2d8 | |
Lephe | 97934cc18e | |
Lephe | d3a2cf07a0 | |
Lephe | 8713d2644f | |
Lephe | 545db2f9ce | |
Lephe | 02c1b551cd | |
Lephe | 89d540ee91 | |
Lephe | 8d444b4f78 | |
Lephe | 03ef59521c | |
Lephe | 5bd04a9613 | |
Lephe | 658413ba19 | |
Lephe | 10180d31bc |
139
CMakeLists.txt
139
CMakeLists.txt
|
@ -1,12 +1,12 @@
|
|||
# Build system for the gint unikernel
|
||||
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(Gint VERSION 2.5.3 LANGUAGES C ASM)
|
||||
project(Gint VERSION 2.9.0 LANGUAGES C ASM)
|
||||
|
||||
include(GitVersionNumber)
|
||||
include(Fxconv)
|
||||
|
||||
option(GINT_USER_VRAM "Put all VRAMs into the user stack (fx-CG 50 only)")
|
||||
option(GINT_NO_OS_STACK "Do not use the OS stack as a memory pool (fx-CG 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")
|
||||
|
||||
|
@ -22,17 +22,55 @@ endif()
|
|||
configure_file(include/gint/config.h.in include/gint/config.h)
|
||||
|
||||
set(SOURCES_COMMON
|
||||
# Clock Pulse Generator driver
|
||||
src/cpg/cpg.c
|
||||
src/cpg/overclock.c
|
||||
# CPU driver
|
||||
src/cpu/atomic.c
|
||||
src/cpu/cpu.c
|
||||
src/cpu/ics.s
|
||||
src/cpu/registers.s
|
||||
src/cpu/sleep.c
|
||||
# Direct Memory Access driver
|
||||
src/dma/dma.c
|
||||
src/dma/inth.s
|
||||
src/dma/memcpy.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/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_rmdir.c
|
||||
src/fs/fugue/fugue_unlink.c
|
||||
src/fs/fugue/util.c
|
||||
# Interrupt Controller driver
|
||||
src/intc/intc.c
|
||||
src/intc/inth.s
|
||||
# Kernel
|
||||
src/kernel/exch.c
|
||||
src/kernel/exch.s
|
||||
src/kernel/hardware.c
|
||||
|
@ -43,30 +81,44 @@ set(SOURCES_COMMON
|
|||
src/kernel/syscalls.S
|
||||
src/kernel/tlbh.S
|
||||
src/kernel/world.c
|
||||
# Key Scan Interface driver
|
||||
src/keysc/getkey.c
|
||||
src/keysc/iokbd.c
|
||||
src/keysc/keycodes.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/scan_frequency.c
|
||||
# Memory allocator
|
||||
src/kmalloc/arena_gint.c
|
||||
src/kmalloc/arena_osheap.c
|
||||
src/kmalloc/kmalloc.c
|
||||
# MMU driver
|
||||
src/mmu/mmu.c
|
||||
# Rendering
|
||||
src/render/dhline.c
|
||||
src/render/dimage.c
|
||||
src/render/dline.c
|
||||
src/render/dprint.c
|
||||
src/render/drect_border.c
|
||||
src/render/dtext.c
|
||||
src/render/dupdate_hook.c
|
||||
src/render/dvline.c
|
||||
src/render/topti.c
|
||||
# RTC driver
|
||||
src/rtc/rtc.c
|
||||
src/rtc/rtc_ticks.c
|
||||
# Sound Processing Unit driver
|
||||
src/spu/spu.c
|
||||
# Timer Unit driver
|
||||
src/tmu/inth-etmu.s
|
||||
src/tmu/inth-tmu.s
|
||||
src/tmu/sleep.c
|
||||
src/tmu/tmu.c
|
||||
# USB driver
|
||||
src/usb/classes/ff-bulk.c
|
||||
src/usb/configure.c
|
||||
src/usb/pipes.c
|
||||
|
@ -75,6 +127,7 @@ set(SOURCES_COMMON
|
|||
src/usb/usb.c
|
||||
)
|
||||
set(SOURCES_FX
|
||||
# Gray engine
|
||||
src/gray/engine.c
|
||||
src/gray/gclear.c
|
||||
src/gray/gint_gline.c
|
||||
|
@ -82,6 +135,7 @@ set(SOURCES_FX
|
|||
src/gray/grect.c
|
||||
src/gray/gsubimage.c
|
||||
src/gray/gtext.c
|
||||
# Rendering
|
||||
src/render-fx/bopti-asm-gray-scsp.s
|
||||
src/render-fx/bopti-asm-gray.s
|
||||
src/render-fx/bopti-asm-mono-scsp.s
|
||||
|
@ -96,13 +150,46 @@ set(SOURCES_FX
|
|||
src/render-fx/masks.c
|
||||
src/render-fx/topti-asm.s
|
||||
src/render-fx/topti.c
|
||||
# T6K11 driver
|
||||
src/t6k11/t6k11.c
|
||||
|
||||
src/usb/classes/ff-bulk-gray.c
|
||||
)
|
||||
set(SOURCES_CG
|
||||
# R61524 driver
|
||||
src/r61524/r61524.c
|
||||
src/render-cg/bopti-asm.s
|
||||
src/render-cg/bopti.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
|
||||
# Rendering
|
||||
src/render-cg/dclear.c
|
||||
src/render-cg/dpixel.c
|
||||
src/render-cg/drect.c
|
||||
|
@ -112,6 +199,37 @@ set(SOURCES_CG
|
|||
src/render-cg/gint_dline.c
|
||||
src/render-cg/topti-asm.s
|
||||
src/render-cg/topti.c
|
||||
# Fast image renderer
|
||||
src/render-cg/image/image.c
|
||||
src/render-cg/image/image_rgb16.S
|
||||
src/render-cg/image/image_rgb16_normal.S
|
||||
src/render-cg/image/image_rgb16_clearbg_dye.S
|
||||
src/render-cg/image/image_rgb16_swapcolor.S
|
||||
src/render-cg/image/image_p8.S
|
||||
src/render-cg/image/image_p8_normal.S
|
||||
src/render-cg/image/image_p8_clearbg.S
|
||||
src/render-cg/image/image_p8_swapcolor.S
|
||||
src/render-cg/image/image_p8_dye.S
|
||||
src/render-cg/image/image_p4.S
|
||||
src/render-cg/image/image_p4_normal.S
|
||||
src/render-cg/image/image_p4_clearbg.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 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
|
||||
)
|
||||
|
||||
set(ASSETS_FX src/font5x7.png)
|
||||
|
@ -120,8 +238,7 @@ fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG})
|
|||
|
||||
include_directories(
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
"${FXSDK_COMPILER_INSTALL}/include/openlibm")
|
||||
"${PROJECT_BINARY_DIR}/include")
|
||||
add_compile_options(-Wall -Wextra -std=c11 -Os -fstrict-volatile-bitfields -mtas)
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
|
@ -141,16 +258,16 @@ endif()
|
|||
set_target_properties("${NAME}" PROPERTIES OUTPUT_NAME "${NAME}")
|
||||
|
||||
# Library file
|
||||
install(TARGETS "${NAME}" DESTINATION "${FXSDK_COMPILER_INSTALL}")
|
||||
install(TARGETS "${NAME}" DESTINATION "${FXSDK_LIB}")
|
||||
# Linker script
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_SCRIPT}"
|
||||
DESTINATION "${FXSDK_COMPILER_INSTALL}")
|
||||
DESTINATION "${FXSDK_LIB}")
|
||||
# Headers
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
DESTINATION "${FXSDK_COMPILER_INSTALL}"
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
|
||||
DESTINATION "${FXSDK_INCLUDE}"
|
||||
FILES_MATCHING PATTERN "*.h")
|
||||
# Auto-generated config header
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/gint/config.h"
|
||||
DESTINATION "${FXSDK_COMPILER_INSTALL}/include/gint")
|
||||
DESTINATION "${FXSDK_INCLUDE}/gint")
|
||||
# CMake module to find gint
|
||||
install(FILES cmake/FindGint.cmake DESTINATION "${FXSDK_CMAKE_MODULE_PATH}")
|
||||
|
|
12
README.md
12
README.md
|
@ -66,12 +66,6 @@ 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
|
||||
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
|
||||
easily (essentially copy-paste the command from the respective READMEs). You
|
||||
can technically use another libc but there be dragons.
|
||||
|
@ -89,6 +83,7 @@ Run without `-c` to build. This configures automatically.
|
|||
|
||||
```
|
||||
% fxsdk build-fx
|
||||
% fxsdk build-fx install
|
||||
```
|
||||
|
||||
The available options are:
|
||||
|
@ -105,6 +100,11 @@ The available options are:
|
|||
|
||||
* `-DGINT_USER_VRAM=1`: Store all VRAMs in the user stack (takes up 350k/512k)
|
||||
|
||||
```
|
||||
% fxsdk build-cg
|
||||
% fxsdk build-cg install
|
||||
```
|
||||
|
||||
## Using in CMake-based add-ins
|
||||
|
||||
Find the `Gint` module and link against `Gint::Gint`. gint declares the include
|
||||
|
|
14
TODO
14
TODO
|
@ -1,22 +1,22 @@
|
|||
Extensions on existing code:
|
||||
* clock: mono support
|
||||
* usb: add PC->calc reading, 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
|
||||
* kernel: better restore to userspace before panic (ensure BL=0 IMASK=0)
|
||||
* project: add license 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
|
||||
* 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 rtc: use qdiv10 to massively improve division performance
|
||||
|
||||
Future directions.
|
||||
* A complete file system abstraction
|
||||
* Integrate overclock management
|
||||
Future directions:
|
||||
* Audio playback using TSWilliamson's libsnd method
|
||||
* Serial communication
|
||||
* USB communication, using Yatis' reverse-engineering of the module
|
||||
* Make fx9860g projects work out of the box on fxcg50
|
||||
* Use the DSP to enhance parallel computation
|
||||
* Base for Yatis' threads library
|
||||
|
|
|
@ -14,13 +14,16 @@ endif()
|
|||
execute_process(
|
||||
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libgint-${PC}.a
|
||||
OUTPUT_VARIABLE GINT_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_C_COMPILER} -print-file-name=include/gint/config.h
|
||||
OUTPUT_VARIABLE GINT_CONFIG_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.a
|
||||
OUTPUT_VARIABLE FXLIBC_PATH
|
||||
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")
|
||||
unset(PATH_TO_LIBGINT)
|
||||
|
@ -35,13 +38,25 @@ else()
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if("${FXLIBC_PATH}" STREQUAL "libc.a")
|
||||
unset(PATH_TO_FXLIBC)
|
||||
else()
|
||||
set(PATH_TO_FXLIBC TRUE)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Gint
|
||||
REQUIRED_VARS PATH_TO_LIBGINT
|
||||
VERSION_VAR GINT_VERSION
|
||||
)
|
||||
REQUIRED_VARS PATH_TO_FXLIBC PATH_TO_LIBGINT
|
||||
VERSION_VAR GINT_VERSION)
|
||||
|
||||
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)
|
||||
add_library(Gint::Gint UNKNOWN IMPORTED)
|
||||
endif()
|
||||
|
@ -49,8 +64,9 @@ if(Gint_FOUND)
|
|||
IMPORTED_LOCATION "${GINT_PATH}"
|
||||
INTERFACE_COMPILE_OPTIONS -fstrict-volatile-bitfields
|
||||
INTERFACE_COMPILE_DEFINITIONS "${INTF_DEFN}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${FXSDK_COMPILER_INSTALL}/include/openlibm"
|
||||
INTERFACE_LINK_LIBRARIES "-lc;-lopenlibm;-lgcc"
|
||||
INTERFACE_LINK_OPTIONS "${INTF_LINK}"
|
||||
)
|
||||
INTERFACE_LINK_LIBRARIES "-lopenlibm;-lgcc"
|
||||
INTERFACE_LINK_OPTIONS "${INTF_LINK}")
|
||||
|
||||
target_link_libraries(Gint::Gint INTERFACE Gint::Fxlibc)
|
||||
target_link_libraries(Gint::Fxlibc INTERFACE Gint::Gint)
|
||||
endif()
|
||||
|
|
33
fx9860g.ld
33
fx9860g.ld
|
@ -35,8 +35,7 @@ MEMORY
|
|||
/* On-chip IL memory */
|
||||
ilram (rwx): o = 0xe5200000, l = 4k
|
||||
/* On-chip X and Y memory */
|
||||
xram (rwx): o = 0xe5007000, l = 8k
|
||||
yram (rwx): o = 0xe5017000, l = 8k
|
||||
xyram (rwx): o = 0xe500e000, l = 16k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
@ -128,7 +127,7 @@ SECTIONS
|
|||
.bss (NOLOAD) : {
|
||||
_rbss = . ;
|
||||
|
||||
*(.bss COMMON)
|
||||
*(.bss .bss.* COMMON)
|
||||
*(B R)
|
||||
|
||||
. = ALIGN(16);
|
||||
|
@ -188,29 +187,18 @@ SECTIONS
|
|||
. = ALIGN(16);
|
||||
} > ilram AT> rom
|
||||
|
||||
. = ORIGIN(xram);
|
||||
.xram ALIGN(4) : ALIGN(4) {
|
||||
_lxram = LOADADDR(.xram);
|
||||
_rxram = . ;
|
||||
. = ORIGIN(xyram);
|
||||
.xyram ALIGN(4) : ALIGN(4) {
|
||||
_lxyram = LOADADDR(.xyram);
|
||||
_rxyram = . ;
|
||||
|
||||
*(.xram)
|
||||
*(.xram .yram .xyram)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > xram AT> rom
|
||||
|
||||
. = ORIGIN(yram);
|
||||
.yram ALIGN(4) : ALIGN(4) {
|
||||
_lyram = LOADADDR(.yram);
|
||||
_ryram = . ;
|
||||
|
||||
*(.yram)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > yram AT> rom
|
||||
} > xyram AT> rom
|
||||
|
||||
_silram = SIZEOF(.ilram);
|
||||
_sxram = SIZEOF(.xram);
|
||||
_syram = SIZEOF(.yram);
|
||||
_sxyram = SIZEOF(.xyram);
|
||||
|
||||
|
||||
|
||||
|
@ -239,7 +227,8 @@ SECTIONS
|
|||
/DISCARD/ : {
|
||||
/* Debug sections (often from libgcc) */
|
||||
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
|
||||
.debug_ranges .debug_line .debug_str)
|
||||
.debug_ranges .debug_line .debug_str .debug_frame
|
||||
.debug_loclists .debug_rnglists)
|
||||
/* Java class registration (why are they even here?!) */
|
||||
*(.jcr)
|
||||
/* Asynchronous unwind tables: no C++ exception handling */
|
||||
|
|
36
fxcg50.ld
36
fxcg50.ld
|
@ -17,12 +17,11 @@ MEMORY
|
|||
/* Static RAM; stack grows down from the end of this region.
|
||||
The first 5k (0x1400 bytes) are reserved by gint for the VBR space,
|
||||
which is loaded dynamically and accessed through P1 */
|
||||
ram (rw): o = 0x08101400, l = 507k
|
||||
ram (rw): o = 0x08101400, l = 491k
|
||||
/* On-chip IL memory */
|
||||
ilram (rwx): o = 0xe5200000, l = 4k
|
||||
/* On-chip X and Y memory */
|
||||
xram (rwx): o = 0xe5007000, l = 8k
|
||||
yram (rwx): o = 0xe5017000, l = 8k
|
||||
xyram (rwx): o = 0xe500e000, l = 16k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
@ -102,8 +101,7 @@ SECTIONS
|
|||
.bss (NOLOAD) : {
|
||||
_rbss = . ;
|
||||
|
||||
*(.bss.vram)
|
||||
*(.bss COMMON)
|
||||
*(.bss .bss.* COMMON)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > ram :NONE
|
||||
|
@ -144,29 +142,18 @@ SECTIONS
|
|||
. = ALIGN(16);
|
||||
} > ilram AT> rom
|
||||
|
||||
. = ORIGIN(xram);
|
||||
.xram ALIGN(4) : ALIGN(4) {
|
||||
_lxram = LOADADDR(.xram);
|
||||
_rxram = . ;
|
||||
. = ORIGIN(xyram);
|
||||
.xyram ALIGN(4) : ALIGN(4) {
|
||||
_lxyram = LOADADDR(.xyram);
|
||||
_rxyram = . ;
|
||||
|
||||
*(.xram)
|
||||
*(.xram .yram .xyram)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > xram AT> rom
|
||||
|
||||
. = ORIGIN(yram);
|
||||
.yram ALIGN(4) : ALIGN(4) {
|
||||
_lyram = LOADADDR(.yram);
|
||||
_ryram = . ;
|
||||
|
||||
*(.yram)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > yram AT> rom
|
||||
} > xyram AT> rom
|
||||
|
||||
_silram = SIZEOF(.ilram);
|
||||
_sxram = SIZEOF(.xram);
|
||||
_syram = SIZEOF(.yram);
|
||||
_sxyram = SIZEOF(.xyram);
|
||||
|
||||
/* gint's uninitialized BSS section, going to static RAM. All the large
|
||||
data arrays will be located here */
|
||||
|
@ -191,7 +178,8 @@ SECTIONS
|
|||
*(.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)
|
||||
.debug_ranges .debug_line .debug_str .debug_frame
|
||||
.debug_loclists .debug_rnglists)
|
||||
/* Java class registration (why are they even here?!) */
|
||||
*(.jcr)
|
||||
/* Asynchronous unwind tables: no C++ exception handling */
|
||||
|
|
|
@ -1,98 +1,232 @@
|
|||
//---
|
||||
// gint:bfile - BFile interface
|
||||
// gint:bfile - BFile interface
|
||||
//
|
||||
// The system's file system is a nightmare and the risk of corrupting it
|
||||
// or the flash by driving it manually is too great to risk. This module
|
||||
// interfaces with the BFile syscalls for file management.
|
||||
// BFile is the OS's native interface to the filesystem. It has quite a bit of
|
||||
// legacy, so it's not very easy to use. Please note that BFile cannot be used
|
||||
// from within gint, so do a gint_world_switch() (<gint/gint.h>) to call BFile
|
||||
// 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
|
||||
#define GINT_BFILE
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* BFile_Remove(): Remove a file
|
||||
Also works if the file does not exist.
|
||||
//---
|
||||
// Common file access functions
|
||||
//---
|
||||
|
||||
@file FONTCHARACTER file path
|
||||
Returns a BFile error code. */
|
||||
int BFile_Remove(uint16_t const *file);
|
||||
/* Remove a file or folder (also works if the entry does not exist). */
|
||||
int BFile_Remove(uint16_t const *path);
|
||||
|
||||
#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
|
||||
to the desired file size (which is fixed), for a folder it is ignored.
|
||||
to the desired file size (which is fixed), for a folder it is ignored. With
|
||||
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.
|
||||
|
||||
@file FONTCHARACTER file path
|
||||
@type Entry type
|
||||
@size Pointer to file size if [type = BFile_File], use NULL otherwise
|
||||
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);
|
||||
@path FONTCHARACTER file path
|
||||
@type Entry type (BFile_File or BFile_Folder)
|
||||
@size Pointer to file size if type is BFile_File, use NULL otherwise */
|
||||
int BFile_Create(uint16_t const *path, int type, int *size);
|
||||
|
||||
/* BFile_Open(): Open an existing file
|
||||
The file must exist.
|
||||
#define BFile_ReadOnly 0x01
|
||||
#define BFile_WriteOnly 0x02
|
||||
#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)
|
||||
|
||||
@file FONTCHARACTER file path
|
||||
/* BFile_Open(): Open a file for reading or writing
|
||||
|
||||
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
|
||||
Returns a file descriptor on success, or a negative BFile error code. */
|
||||
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);
|
||||
Returns a file descriptor (or a negative error code). */
|
||||
int BFile_Open(uint16_t const *path, int mode);
|
||||
|
||||
/* BFile_Close(): Close a file descriptor
|
||||
@fd Open file descriptor
|
||||
Returns a BFile error code. */
|
||||
/* Close an open file descriptor. */
|
||||
int BFile_Close(int fd);
|
||||
|
||||
/* BFile_Size(): Retrieve size of an open file
|
||||
@fd Open file descriptor
|
||||
Returns the file size in bytes, or a negative BFile error code*/
|
||||
/* Get the size of an open file. */
|
||||
int BFile_Size(int fd);
|
||||
|
||||
/* BFile_Write(): Write data to an open file
|
||||
Second and third argument specify the data and length to write.
|
||||
|
||||
WARNING: The file systems has shown to become inconsistent if an odd number
|
||||
of bytes is written with BFile_Write(). Keep it even!
|
||||
WARNING: The CASIOWIN fs is known to become inconsistent if an odd number of
|
||||
bytes is written with BFile_Write(). Always keep it even!
|
||||
|
||||
@fd File descriptor open for writing
|
||||
@data Data to write
|
||||
@even_size Size to write (must be even, yes)
|
||||
Returns a BFile error code. */
|
||||
With CASIOWIN, returns the new file offset after writing (or an error code).
|
||||
With Fugue, returns the amount of data written (or an error code). */
|
||||
int BFile_Write(int fd, void const *data, int even_size);
|
||||
|
||||
/* BFile_Read(): Read data from an open file
|
||||
The second and third argument specify where to store and how much to read.
|
||||
The location from where the data is read depends on [whence]:
|
||||
|
||||
The extra argument [whence] specifies where data is read from in the style
|
||||
of pread(2), and is supported with both filesystems.
|
||||
|
||||
* If [whence >= 0], it is taken as an absolute location within the file;
|
||||
* If [whence == -1], BFile_Read() reads from the current position.
|
||||
|
||||
@fd File descriptor open for reading
|
||||
@data Buffer of at least [size] bytes to store data to
|
||||
@size Number of bytes to read
|
||||
@whence Starting position of the data to read in the file
|
||||
Returns a BFile error code. */
|
||||
With Fugue this function can read past end of file and return the requested
|
||||
amount of bytes even when the file does not have enough data to read that
|
||||
much. It seems that extra bytes read as zeros. Reading past the end does
|
||||
*not* extend the file size.
|
||||
|
||||
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);
|
||||
|
||||
/* 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
|
||||
to close them to be able to reuse them. Search is NOT case sensitive and *
|
||||
can be used as a wildcard.
|
||||
/* BFile_Seek(): Seek to a relative or absolute position within an open file
|
||||
|
||||
With CASIOWIN, moves [offset] bytes relative to the current position, and
|
||||
returns how much data can be read from the new position. BFile_Seek(fd, 0)
|
||||
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
|
||||
{
|
||||
uint16_t index;
|
||||
|
@ -105,36 +239,74 @@ struct BFile_FileInfo
|
|||
/* Address of first fragment (do not use directly) */
|
||||
void *address;
|
||||
};
|
||||
enum BFile_FileType
|
||||
{
|
||||
BFile_Type_Directory = 0x0000,
|
||||
BFile_Type_File = 0x0001,
|
||||
BFile_Type_Addin = 0x0002,
|
||||
BFile_Type_Eact = 0x0003,
|
||||
BFile_Type_Language = 0x0004,
|
||||
BFile_Type_Bitmap = 0x0005,
|
||||
BFile_Type_MainMem = 0x0006,
|
||||
BFile_Type_Temp = 0x0007,
|
||||
BFile_Type_Dot = 0x0008,
|
||||
BFile_Type_DotDot = 0x0009,
|
||||
BFile_Type_Volume = 0x000a,
|
||||
BFile_Type_Archived = 0x0041,
|
||||
};
|
||||
int BFile_FindFirst(uint16_t const *search, int *shandle, uint16_t *foundfile,
|
||||
|
||||
#define BFile_Type_Directory 0x0000
|
||||
#define BFile_Type_File 0x0001
|
||||
#define BFile_Type_Addin 0x0002
|
||||
#define BFile_Type_Eact 0x0003
|
||||
#define BFile_Type_Language 0x0004
|
||||
#define BFile_Type_Bitmap 0x0005
|
||||
#define BFile_Type_MainMem 0x0006
|
||||
#define BFile_Type_Temp 0x0007
|
||||
#define BFile_Type_Dot 0x0008
|
||||
#define BFile_Type_DotDot 0x0009
|
||||
#define BFile_Type_Volume 0x000a
|
||||
#define BFile_Type_Archived 0x0041
|
||||
|
||||
/* BFile_FindFirst(): Search the storage memory for paths matching a pattern
|
||||
|
||||
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);
|
||||
|
||||
/* BFile_FindNext(): Continue a search a directory for file with matching name
|
||||
/* BFile_FindNext(): Continue a search
|
||||
|
||||
@shandle Search handle from BFile_FindFirst
|
||||
@founfile FONTCHARACTER found file path
|
||||
@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. */
|
||||
Continues the search for matches. The search handle is the value set in
|
||||
*shandle in BFile_FindFirst(). As before, *foundfile receives the matching
|
||||
entry's path and *fileinfo its metadata.
|
||||
|
||||
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,
|
||||
struct BFile_FileInfo *fileinfo);
|
||||
|
||||
/* BFile_FindClose(): Close the specified search handle
|
||||
@shandle Search handle from BFile_FindFirst
|
||||
Return 0 on success or negative value on error. */
|
||||
/* Close a search handle (with or without exhausting the matches). */
|
||||
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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_BFILE */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_CLOCK
|
||||
#define GINT_CLOCK
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
/* This header used to expose the sleep() function; include <gint/cpu.h> to
|
||||
ensure this is still the case */
|
||||
|
@ -48,11 +52,78 @@ typedef struct
|
|||
module; this address never changes. */
|
||||
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
|
||||
//---
|
||||
|
||||
/* TODO: All overclock */
|
||||
/* The following enumerations define the clock speed settings supported by
|
||||
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,
|
||||
thoug many more are involved.
|
||||
|
||||
On SH4 fx-9860G-likr:
|
||||
(Not supported yet)
|
||||
|
||||
On the fx G-III series:
|
||||
(Not supported yet)
|
||||
|
||||
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 [Clearly slower: F2 < F3 < F1]
|
||||
F3: CPU @ 94 MHz, BFC @ 47 MHz [Clearly slower: 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,
|
||||
};
|
||||
|
||||
#ifdef FXCG50
|
||||
/* 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);
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Sleep functions
|
||||
|
@ -72,4 +143,29 @@ void sleep_us_spin(uint64_t delay_us);
|
|||
/* sleep_ms(): Sleep for a fixed duration in milliseconds */
|
||||
#define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull)
|
||||
|
||||
//---
|
||||
// Low-level overclock functions
|
||||
//
|
||||
// These low-level functions directly read or write registers involved in
|
||||
// setting the overclock level. Don't use them directly unless you understand
|
||||
// how their interactions with the environment; instead, use clock_set_speed().
|
||||
//---
|
||||
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_CLOCK */
|
||||
|
|
|
@ -16,10 +16,18 @@
|
|||
0x03f7c0a0 = Commit 3f7c0a0 */
|
||||
#define GINT_HASH 0x@GINT_GIT_HASH@
|
||||
|
||||
/* GINT_USER_VRAM: Selects whether to store VRAMs in the user stack (occupying
|
||||
350k/512k of the area) or in the system stack (the default). (fx-CG 50) */
|
||||
/* 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
|
||||
|
||||
#ifdef GINT_USER_VRAM
|
||||
# define GINT_NO_OS_STACK
|
||||
#endif
|
||||
|
||||
/* GINT_STATIC_GRAY: Selects whether additional gray VRAMs are allocated
|
||||
statically or in the system heap (fx-9860G) */
|
||||
#cmakedefine GINT_STATIC_GRAY
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_CPU
|
||||
#define GINT_CPU
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
|
@ -107,6 +111,36 @@ void sleep_block(void);
|
|||
/* sleep_unblock(): Cancel a processor sleep block */
|
||||
void sleep_unblock(void);
|
||||
|
||||
//---
|
||||
// Interrupt-cancellable sleeps
|
||||
//
|
||||
// The sleep() function has the significant drawback of not synchronizing with
|
||||
// interrupts. Programs usually run [while(<interrupt not occurred>) sleep()],
|
||||
// which fails if the interrupt occurs between the time the condition is
|
||||
// checked and time the sleep instruction is executed.
|
||||
//
|
||||
// Interrupt-cancellable sleep is a software mechanism by which the interrupt
|
||||
// disables the sleep instruction itself (by replacing it with a nop), which
|
||||
// ensures that the CPU cannot go to sleep after the interrupt occurs.
|
||||
//---
|
||||
|
||||
/* cpu_csleep_t: A cancellable sleep function
|
||||
This object holds sleep code that can be disabled by an interrupt. The size
|
||||
and layout of this variable is dependent on some assembler code. This should
|
||||
be allocated on the stack, heap, or on-chip memory, because the data segment
|
||||
is not executable! */
|
||||
typedef GALIGNED(4) uint8_t cpu_csleep_t[20];
|
||||
|
||||
/* cpu_csleep_init(): Create an ICS function
|
||||
This function initializes the provided ICS routine. */
|
||||
void cpu_csleep_init(cpu_csleep_t *ics);
|
||||
|
||||
/* cpu_csleep(): Run the sleep function until the sleep is cancelled */
|
||||
void cpu_csleep(cpu_csleep_t *ics);
|
||||
|
||||
/* cpu_csleep_cancel(): Cancel the sleep function from within an interrupt */
|
||||
void cpu_csleep_cancel(cpu_csleep_t *ics);
|
||||
|
||||
//---
|
||||
// Configuration
|
||||
//---
|
||||
|
@ -117,4 +151,8 @@ void sleep_unblock(void);
|
|||
configure(), this is reset to 0. */
|
||||
void cpu_configure_VBR(uint32_t VBR);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_CPU */
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
#define GBSS3 __attribute__((section(".gint.bss.sh3")))
|
||||
/* Objects for the ILRAM, XRAM and YRAM regions */
|
||||
#define GILRAM __attribute__((section(".ilram")))
|
||||
#define GXRAM __attribute__((section(".xram")))
|
||||
#define GYRAM __attribute__((section(".yram")))
|
||||
#define GXRAM __attribute__((section(".xyram")))
|
||||
#define GYRAM __attribute__((section(".xyram")))
|
||||
|
||||
/* Unused parameters or variables */
|
||||
#define GUNUSED __attribute__((unused))
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
#ifndef GINT_DEFS_CALL
|
||||
#define GINT_DEFS_CALL
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* gint_call_arg_t: All types of arguments allowed in an indirect call
|
||||
|
||||
Because a function call cannot be easily pieced together, there are
|
||||
|
@ -111,19 +115,31 @@ typedef struct {
|
|||
* error: cast to union type from type not present in union
|
||||
-> This is emitted if you pass a parameter of an invalid type. */
|
||||
#define GINT_CALL(func, ...) \
|
||||
((gint_call_t){ .function = func, .args = { \
|
||||
((gint_call_t){ .function = (void *)func, .args = { \
|
||||
__VA_OPT__(GINT_CALL_ARGS1(__VA_ARGS__)) \
|
||||
}})
|
||||
#define GINT_CALL_ARGS1(a1, ...) \
|
||||
(gint_call_arg_t)(a1), __VA_OPT__(GINT_CALL_ARGS2(__VA_ARGS__))
|
||||
GINT_CALL_ARG(a1), __VA_OPT__(GINT_CALL_ARGS2(__VA_ARGS__))
|
||||
#define GINT_CALL_ARGS2(a2, ...) \
|
||||
(gint_call_arg_t)(a2), __VA_OPT__(GINT_CALL_ARGS3(__VA_ARGS__))
|
||||
GINT_CALL_ARG(a2), __VA_OPT__(GINT_CALL_ARGS3(__VA_ARGS__))
|
||||
#define GINT_CALL_ARGS3(a3, ...) \
|
||||
(gint_call_arg_t)(a3), __VA_OPT__(GINT_CALL_ARGS4(__VA_ARGS__))
|
||||
GINT_CALL_ARG(a3), __VA_OPT__(GINT_CALL_ARGS4(__VA_ARGS__))
|
||||
#define GINT_CALL_ARGS4(a4, ...) \
|
||||
({ __VA_OPT__(_Static_assert(0, \
|
||||
"GINT_CALL: too many arguments (maximum 4)");) \
|
||||
(gint_call_arg_t)(a4); })
|
||||
GINT_CALL_ARG(a4); })
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* Kind of ugly but I don't have super cool templated logic right now.
|
||||
TODO: Allow any 4-byte type using C++ magic. */
|
||||
#define GINT_CALL_ARG(expr) ({ \
|
||||
auto __arg = (expr); \
|
||||
(*reinterpret_cast<gint_call_arg_t *>(&__arg)); \
|
||||
})
|
||||
#else
|
||||
/* GCC extension: Cast to union */
|
||||
#define GINT_CALL_ARG(expr) (gint_call_arg_t)(expr)
|
||||
#endif
|
||||
|
||||
/* GINT_CALL_NULL: Empty function call */
|
||||
#define GINT_CALL_NULL ((gint_call_t){ .function = NULL, .args = {} })
|
||||
|
@ -131,7 +147,12 @@ typedef struct {
|
|||
/* gint_call(): Perform an indirect call */
|
||||
static GINLINE int gint_call(gint_call_t cb)
|
||||
{
|
||||
#ifdef __cplusplus
|
||||
int (*f)(int r4, int r5, int r6, int r7) = (decltype(f))cb.function;
|
||||
#else
|
||||
int (*f)(int r4, int r5, int r6, int r7) = cb.function;
|
||||
#endif
|
||||
|
||||
return f(cb.args[0].i, cb.args[1].i, cb.args[2].i, cb.args[3].i);
|
||||
}
|
||||
|
||||
|
@ -195,4 +216,8 @@ static GINLINE gint_call_t GINT_CALL_INC_STOP(int volatile *pointer)
|
|||
return GINT_CALL(GINT_CALL_INC_STOP_function, pointer);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DEFS_CALL */
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
#include <stdint.h>
|
||||
/* For human-readable boolean types */
|
||||
#include <stdbool.h>
|
||||
/* Common system types: ssize_t, off_t, etc. */
|
||||
#include <sys/types.h>
|
||||
/* For va_list */
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Fixed-width types for bit fields are quite meaningless */
|
||||
typedef unsigned int uint;
|
||||
/* Signed size_t */
|
||||
typedef signed int ssize_t;
|
||||
/* Offset in file (used in standard functions) */
|
||||
typedef unsigned int off_t;
|
||||
|
||||
//---
|
||||
// Structure elements
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
//---
|
||||
// gint:display-cg - fxcg50 rendering functions
|
||||
// gint:display-cg - fx-CG 50 rendering functions
|
||||
//
|
||||
// This module covers all 16-bit opaque rendering functions. For
|
||||
// gamma-related functions, color composition, check out a color library.
|
||||
// This module covers rendering functions specific to the fx-CG 50. In addition
|
||||
// to triple-buffering management, this mainly includes image manipulation
|
||||
// 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.
|
||||
//
|
||||
// All the functions in this module work on a 396x224 resolution - gint
|
||||
// lets you use the full surface!
|
||||
// The fx-CG OS restricts the display to a 384x216 rectangle rougly around the
|
||||
// center, leaving margins on three sides. However, gint configures the display
|
||||
// to use the full 396x224 surface!
|
||||
//---
|
||||
|
||||
#ifndef GINT_DISPLAY_CG
|
||||
|
@ -13,7 +17,12 @@
|
|||
|
||||
#ifdef FXCG50
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/image.h>
|
||||
|
||||
/* Dimensions of the VRAM */
|
||||
#define DWIDTH 396
|
||||
|
@ -53,37 +62,9 @@ enum {
|
|||
green is not used). */
|
||||
#define C_RGB(r,g,b) (((r) << 11) | ((g) << 6) | (b))
|
||||
|
||||
//---
|
||||
// Image rendering (bopti)
|
||||
//---
|
||||
/* See <gint/image.h> for the details on image manipulation. */
|
||||
typedef image_t bopti_image_t;
|
||||
|
||||
/* 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
|
||||
|
@ -123,6 +104,10 @@ void dsetvram(uint16_t *main, uint16_t *secondary);
|
|||
Returns the VRAM buffer addresses used to render on fx-CG 50. */
|
||||
void dgetvram(uint16_t **main, uint16_t **secondary);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FXCG50 */
|
||||
|
||||
#endif /* GINT_DISPLAY_CG */
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
|
||||
#ifdef FX9860G
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* Dimensions of the VRAM */
|
||||
|
@ -83,6 +87,10 @@ typedef struct
|
|||
|
||||
} GPACKED(4) bopti_image_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
||||
#endif /* GINT_DISPLAY_FX */
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
#ifndef GINT_DISPLAY
|
||||
#define GINT_DISPLAY
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
|
||||
/* Platform-specific functions include VRAM management and the definition of
|
||||
the color_t type. */
|
||||
|
@ -58,6 +63,25 @@
|
|||
fx-CG 50: 11 ms */
|
||||
void dupdate(void);
|
||||
|
||||
/* dupdate_set_hook(): Define a function to be called after each dupdate()
|
||||
|
||||
This functions configures the update hook, which is called after each
|
||||
dupdate() has sent VRAM to the display (but before VRAMs are switched when
|
||||
triple-buffering is used on fx-CG 50).
|
||||
|
||||
The hook is mostly useful to send a copy of the frame to another medium,
|
||||
typically through a USB connection to a projector-style application. See
|
||||
usb_fxlink_videocapture() in <gint/usb-ff-bulk.h> for an example.
|
||||
|
||||
The function is an indirect call; create one with the GINT_CALL() macro from
|
||||
<gint/defs/call.h>. Pass GINT_CALL_NULL to disable the feature.
|
||||
|
||||
@function Indirect call to perform after each dupdate(). */
|
||||
void dupdate_set_hook(gint_call_t function);
|
||||
|
||||
/* dupdate_get_hook(): Get a copy of the dupdate() hook */
|
||||
gint_call_t dupdate_get_hook(void);
|
||||
|
||||
//---
|
||||
// Area rendering functions
|
||||
//---
|
||||
|
@ -398,4 +422,8 @@ enum {
|
|||
void dsubimage(int x, int y, bopti_image_t const *image, int left, int top,
|
||||
int width, int height, int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DISPLAY */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_DMA
|
||||
#define GINT_DMA
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
|
||||
|
@ -117,4 +121,8 @@ void *dma_memset(void *dst, uint32_t pattern, size_t size);
|
|||
void *dma_memcpy(void * __restrict dst, const void * __restrict src,
|
||||
size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DMA */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_DRIVERS
|
||||
#define GINT_DRIVERS
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
|
@ -300,4 +304,8 @@ void gint_world_switch_in(gint_world_t world_os, gint_world_t world_addin);
|
|||
/* Switch from a gint-managed world to the OS world */
|
||||
void gint_world_switch_out(gint_world_t world_addin, gint_world_t world_os);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DRIVERS */
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
#ifndef GINT_DRIVERS_IOKBD
|
||||
#define GINT_DRIVERS_IOKBD
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data
|
||||
|
@ -14,4 +18,8 @@
|
|||
@scan 12-byte buffer filled with row data */
|
||||
void iokbd_scan(uint8_t *scan);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DRIVERS_IOKBD */
|
||||
|
|
|
@ -2,27 +2,18 @@
|
|||
// gint:keydev - Kernel's keyboard input devices
|
||||
//---
|
||||
|
||||
#include <gint/keyboard.h>
|
||||
|
||||
#ifndef GINT_KEYDEV
|
||||
#define GINT_KEYDEV
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/keyboard.h>
|
||||
|
||||
/* Size of the buffer event queue */
|
||||
#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
|
||||
|
||||
Every keyboard input device has a built-in event stream editor that can
|
||||
|
@ -80,14 +71,26 @@ typedef struct {
|
|||
enum {
|
||||
/* Delayed SHIFT: Pressing then immediately releasing SHIFT when the
|
||||
keyboard is idle applies SHIFT to the next repeat streak. */
|
||||
KEYDEV_TR_DELAYED_SHIFT = 0x01,
|
||||
KEYDEV_TR_DELAYED_SHIFT = 0x01, /* = GETKEY_MOD_SHIFT */
|
||||
/* Delayed ALPHA: Idem with the ALPHA key */
|
||||
KEYDEV_TR_DELAYED_ALPHA = 0x02,
|
||||
KEYDEV_TR_DELAYED_ALPHA = 0x02, /* = GETKEY_MOD_ALPHA */
|
||||
/* 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
|
||||
SHIFT applied if SHIFT is pressed at the time of the repeat. */
|
||||
KEYDEV_TR_INSTANT_SHIFT = 0x04,
|
||||
/* Instant ALPHA: Idem with the ALPHA key */
|
||||
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 */
|
||||
KEYDEV_TR_REPEATS = 0x10,
|
||||
/* Delete Modifiers: Remove modifier keys from generated events, which
|
||||
|
@ -97,6 +100,9 @@ enum {
|
|||
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 */
|
||||
typedef struct {
|
||||
/* List of enabled transforms. The order is cannot be changed because
|
||||
|
@ -118,7 +124,7 @@ typedef struct {
|
|||
that the precision of the delay is limited by the speed at which the
|
||||
keyboard is scanned, which is nowhere near microsecond-level. By
|
||||
default (128 Hz) the precision is about 7.8 ms. */
|
||||
int (*repeater)(int key, int duration, int count);
|
||||
keydev_repeat_profile_t repeater;
|
||||
|
||||
} GPACKEDENUM keydev_transform_t;
|
||||
|
||||
|
@ -139,31 +145,16 @@ typedef struct {
|
|||
keydown() are shortcuts for keydev functions using the physical keyboard as
|
||||
their input. */
|
||||
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 */
|
||||
uint time;
|
||||
/* Last time when repeats were considered */
|
||||
uint time_repeats;
|
||||
|
||||
/* Event queue (circular buffer) */
|
||||
keydev_event_t queue[KEYBOARD_QUEUE_SIZE];
|
||||
/* Next event in queue, position after last event in queue */
|
||||
int8_t queue_next;
|
||||
int8_t queue_end;
|
||||
/* Number of events lost because of missing queue space */
|
||||
uint events_lost;
|
||||
/* Crafted events waiting to be picked up */
|
||||
key_event_t out[8];
|
||||
int out_size;
|
||||
|
||||
/* Event transforms */
|
||||
keydev_transform_t tr;
|
||||
|
@ -189,6 +180,22 @@ typedef struct {
|
|||
/* Delay until next repeat, set by the repeat planner (us) */
|
||||
int rep_delay;
|
||||
|
||||
/* 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];
|
||||
/* Event queue (circular buffer) */
|
||||
key_event_t queue[KEYBOARD_QUEUE_SIZE];
|
||||
|
||||
/* Parameters for the standard repeat function */
|
||||
int rep_standard_first, rep_standard_next;
|
||||
|
||||
} keydev_t;
|
||||
|
||||
/* keydev_std(): Standard keyboard input device
|
||||
|
@ -214,6 +221,12 @@ void keydev_process_state(keydev_t *d, uint8_t state[12]);
|
|||
devices (such as demo replays) to feed in new events. */
|
||||
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
|
||||
This function maintains time trackers in the device and should be called in
|
||||
each frame after the scanning is finished and the keydev_process_*()
|
||||
|
@ -227,19 +240,11 @@ void keydev_tick(keydev_t *d, uint us);
|
|||
|
||||
/* keydev_unqueue_event(): Retrieve the next keyboard event in queue
|
||||
|
||||
This source provides the queued KEYEV_UP and KEYEV_DOWN events generated
|
||||
from the regular scans in chronological order. It does not generate the
|
||||
KEYEV_HOLD events though, keyev_repeat_event() must be used to obtain these
|
||||
at the end of every tick. */
|
||||
This source provides the queued KEYEV_UP, KEYEV_DOWN and KEYEV_HOLD events
|
||||
generated from the regular scans in chronological order. It does not apply
|
||||
transforms; to do this, use keydev_read(). */
|
||||
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
|
||||
A list of keys to ignore can be specified as variable arguments. The list
|
||||
must be terminated by a 0 keycode. */
|
||||
|
@ -258,7 +263,32 @@ keydev_transform_t keydev_transform(keydev_t *d);
|
|||
/* keydev_set_transform(): Set transform parameters */
|
||||
void keydev_set_transform(keydev_t *d, keydev_transform_t tr);
|
||||
|
||||
/* keydev_set_standard_repeats(): Enable a simple repeater
|
||||
|
||||
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 */
|
||||
key_event_t keydev_read(keydev_t *d);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_KEYDEV */
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
#ifndef GINT_DRIVERS_R61524
|
||||
#define GINT_DRIVERS_R61524
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
enum {
|
||||
|
@ -31,6 +35,34 @@ enum {
|
|||
@method Transfer method, see above */
|
||||
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_start_frame(): Prepare the display for a region 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
|
||||
the initial step of r61524_display(), which is normally followed by 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
|
||||
methods or alternate rendering pipelines. */
|
||||
void r61524_start_frame(int xmin, int xmax, int ymin, int ymax);
|
||||
|
||||
/* r162524_win_get() and r61524_win_set(): Manipulate the display window
|
||||
|
||||
These functions change the screen rectangle where data is shown. Normally
|
||||
|
@ -43,4 +75,20 @@ void r61524_display(uint16_t *vram, int start, int height, int method);
|
|||
void r61524_win_get(uint16_t *HSA, uint16_t *HEA, uint16_t *VSA,uint16_t *VEA);
|
||||
void r61524_win_set(uint16_t HSA, uint16_t HEA, uint16_t VSA, uint16_t VEA);
|
||||
|
||||
//---
|
||||
// Low-level functions
|
||||
//---
|
||||
|
||||
/* r61524_get(): Read the value of an R61524 register
|
||||
This is provided for testing and if you know what you're doing. */
|
||||
uint16_t r61524_get(int ID);
|
||||
|
||||
/* r61524_set(): Write the value of an R61524 register
|
||||
This is provided for testing and if you know what you're doing. */
|
||||
void r61524_set(int ID, uint16_t value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DRIVERS_R61524 */
|
||||
|
|
|
@ -9,11 +9,17 @@
|
|||
#ifndef GINT_DRIVERS_STATES
|
||||
#define GINT_DRIVERS_STATES
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/mpu/dma.h>
|
||||
#include <gint/clock.h>
|
||||
|
||||
/* Clock Pulse Generator (see cpg/cpg.c) */
|
||||
typedef struct {
|
||||
uint32_t SSCGCR;
|
||||
struct cpg_overclock_setting speed;
|
||||
} cpg_state_t;
|
||||
|
||||
/* CPU (see cpu/cpu.c) */
|
||||
|
@ -91,4 +97,8 @@ typedef struct {
|
|||
uint16_t DCPCFG, DCPMAXP, DCPCTR;
|
||||
} usb_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DRIVERS_STATES */
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
#ifndef GINT_DRIVERS_T6K11
|
||||
#define GINT_DRIVERS_T6K11
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* t6k11_display() - send vram data to the LCD device
|
||||
|
@ -51,4 +55,8 @@ void t6k11_contrast(int contrast);
|
|||
@setting Requested backlight setting */
|
||||
void t6k11_backlight(int setting);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DRIVERS_T6K11 */
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
#ifndef GINT_EXC
|
||||
#define GINT_EXC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -64,4 +68,8 @@ void gint_exc_catch(int (*handler)(uint32_t code));
|
|||
@instructions Number of instructions to skip (usually only one) */
|
||||
void gint_exc_skip(int instructions);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_EXC */
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
//---
|
||||
// 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 32
|
||||
|
||||
/* 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 *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 */
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_GINT
|
||||
#define GINT_GINT
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
#include <gint/config.h>
|
||||
|
@ -37,6 +41,13 @@ int gint_world_switch(gint_call_t function);
|
|||
__attribute__((deprecated("Use gint_world_switch() instead")))
|
||||
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
|
||||
|
||||
This function safely invokes the calculator's main menu with gint_switch().
|
||||
|
@ -89,4 +100,8 @@ static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
|
|||
Returns the return value of the callback. */
|
||||
extern int (*gint_inth_callback)(gint_call_t const *call);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_GINT */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_GRAY
|
||||
#define GINT_GRAY
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
|
@ -148,4 +152,8 @@ void dgray_getvram(uint32_t **light, uint32_t **dark);
|
|||
These pointers can be used to make screen captures. */
|
||||
void dgray_getscreen(uint32_t **light, uint32_t **dark);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_GRAY */
|
||||
|
|
|
@ -14,6 +14,10 @@
|
|||
#ifndef GINT_HARDWARE
|
||||
#define GINT_HARDWARE
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* For compatibility with ASM, include the following bits only in C code */
|
||||
#ifndef CPP_ASM
|
||||
|
||||
|
@ -66,6 +70,7 @@ void hw_detect(void);
|
|||
#define HWKBD 8 /* Keyboard */
|
||||
#define HWKBDSF /* Deprecated: use keysc_scan_frequency() */
|
||||
#define HWDD /* Deprecated: use the T6K11/R61524 API */
|
||||
#define HWFS 11 /* Filesystem type */
|
||||
|
||||
/*
|
||||
** MPU type
|
||||
|
@ -114,4 +119,19 @@ void hw_detect(void);
|
|||
/* The keyboard uses a KEYSC-based scan method. This is only possible on SH4 */
|
||||
#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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_HARDWARE */
|
||||
|
|
|
@ -0,0 +1,845 @@
|
|||
//---
|
||||
// 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
|
||||
|
||||
#ifndef FXCG50
|
||||
#error <gint/image.h> is only supported on FXCG50
|
||||
#else
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// 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
|
||||
//---
|
||||
|
||||
/* 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_sub(__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 an invalid image to libimg functions when chaining
|
||||
// transforms. The invalid image will be ignored or returned unchanged, so you
|
||||
// can check for it at the end of any large 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 (whose size is specified as out_w/out_h),
|
||||
returns false. Otherwise, returns true. */
|
||||
bool gint_image_clip_input(image_t const *img, struct gint_image_box *box,
|
||||
int out_w, int out_h);
|
||||
|
||||
/* Clip the provided box against the output. */
|
||||
void gint_image_clip_output(struct gint_image_box *b, int out_w, int out_h);
|
||||
|
||||
//---
|
||||
// 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
|
||||
@out_width Output width (usually DWIDTH)
|
||||
@out_height Output height (usually 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, int out_width, int out_height);
|
||||
|
||||
/* 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 /* FXCG50 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_IMAGE */
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_INTC
|
||||
#define GINT_INTC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
|
||||
|
@ -131,4 +135,8 @@ void *intc_handler(int event_code, void const *handler, size_t size);
|
|||
Returns true on success, false if the event code is invalid. */
|
||||
bool intc_handler_function(int event_code, gint_call_t function);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_INTC */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_KEYBOARD
|
||||
#define GINT_KEYBOARD
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Keyboard, key events, keydown() and getkey()
|
||||
|
||||
gint's keyboard driver regularly scans the keyboard matrix and produces *key
|
||||
|
@ -221,10 +225,6 @@ enum {
|
|||
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
|
||||
See getkey_set_feature_function() for details. */
|
||||
typedef bool (*getkey_feature_t)(key_event_t event);
|
||||
|
@ -242,58 +242,20 @@ typedef bool (*getkey_feature_t)(key_event_t event);
|
|||
value whenever you want to interrupt the call; using a timer with
|
||||
[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
|
||||
@timeout Optional pointer to a timeout value
|
||||
Returns a key event of type KEYEV_DOWN or KEYEV_HOLD with [mod=1]. */
|
||||
key_event_t getkey_opt(int options, volatile int *timeout);
|
||||
|
||||
/* getkey_repeat(): Set repeat delays for getkey()
|
||||
|
||||
This function updates the repeat delays of getkey() and getkey_opt(). The
|
||||
unit of the argument is in milliseconds, but the granularity of the delay is
|
||||
dependent on the keyboard scan frequency.
|
||||
|
||||
In the default setting (128 Hz scans), the possible repeat delays are
|
||||
approximately 8 ms, 16 ms, 23 ms, 31 ms... the provided arguments will be
|
||||
rounded to the closest feasible delays to ensure that repetitions are
|
||||
perfectly regular, rather than approximating the requested frequency.
|
||||
|
||||
The system default is (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_t getkey_feature_function(void);
|
||||
|
||||
|
@ -329,4 +291,36 @@ int keycode_function(int keycode);
|
|||
returns 7 for KEY_7) and -1 for other keys. */
|
||||
int keycode_digit(int keycode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#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 */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_KEYCODES
|
||||
#define GINT_KEYCODES
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Raw matrix codes */
|
||||
enum {
|
||||
KEY_F1 = 0x91,
|
||||
|
@ -83,4 +87,8 @@ enum {
|
|||
KEY_MINUS = KEY_SUB,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_KEYCODES */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_KMALLOC
|
||||
#define GINT_KMALLOC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/config.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
|
@ -164,4 +168,8 @@ int kmallocdbg_sequence_length(kmalloc_arena_t *a);
|
|||
|
||||
#endif /* GINT_KMALLOC_DEBUG */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_KMALLOC */
|
||||
|
|
|
@ -5,7 +5,12 @@
|
|||
#ifndef GINT_MMU
|
||||
#define GINT_MMU
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/mpu/mmu.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//---
|
||||
// Unified interface
|
||||
|
@ -36,6 +41,17 @@ void *mmu_uram(void);
|
|||
fx-9860G, and 512k on fx-CG 50. */
|
||||
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
|
||||
//---
|
||||
|
@ -94,4 +110,8 @@ void utlb_mapped_memory(uint32_t *rom, uint32_t *ram);
|
|||
/* utlb_translate(): Get the physical address for a virtual page */
|
||||
uint32_t utlb_translate(uint32_t page, uint32_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MMU */
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
//---
|
||||
// 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>
|
||||
|
||||
//---
|
||||
// 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 */
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_MPU_CPG
|
||||
#define GINT_MPU_CPG
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
|
@ -43,7 +47,7 @@ typedef volatile struct
|
|||
documentation. */
|
||||
typedef volatile struct
|
||||
{
|
||||
lword_union(FRQCRA,
|
||||
lword_union(FRQCR,
|
||||
uint32_t KICK :1; /* Flush FRQCRA modifications */
|
||||
uint32_t :1;
|
||||
uint32_t STC :6; /* PLL multiplication [*] */
|
||||
|
@ -64,7 +68,15 @@ typedef volatile struct
|
|||
uint32_t SRC :2; /* Clock source select */
|
||||
uint32_t DIVA :6; /* Division ratio for port A */
|
||||
);
|
||||
pad(0x08);
|
||||
pad(0x04);
|
||||
|
||||
lword_union(DDCLKCR,
|
||||
uint32_t :23;
|
||||
uint32_t CLKSTP :1; /* Clock Stop */
|
||||
uint32_t _ :1; /* Unknown */
|
||||
uint32_t :1;
|
||||
uint32_t DIV :6;
|
||||
);
|
||||
|
||||
lword_union(USBCLKCR,
|
||||
uint32_t :23;
|
||||
|
@ -82,7 +94,9 @@ typedef volatile struct
|
|||
uint32_t CKOFF :1; /* CKO Output Stop */
|
||||
uint32_t :1;
|
||||
);
|
||||
pad(0x14);
|
||||
|
||||
uint32_t PLL2CR;
|
||||
pad(0x10);
|
||||
|
||||
lword_union(SPUCLKCR,
|
||||
uint32_t :23;
|
||||
|
@ -105,9 +119,16 @@ typedef volatile struct
|
|||
uint32_t :3;
|
||||
uint32_t FLF :11; /* FLL Multiplication Ratio */
|
||||
);
|
||||
pad(0x0c);
|
||||
|
||||
uint32_t LSTATS;
|
||||
|
||||
} GPACKED(4) sh7305_cpg_t;
|
||||
|
||||
#define SH7305_CPG (*((sh7305_cpg_t *)0xa4150000))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_CPG */
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
#ifndef GINT_MPU_DMA
|
||||
#define GINT_MPU_DMA
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
|
@ -86,4 +90,8 @@ typedef volatile struct
|
|||
|
||||
#define SH7305_DMA (*((sh7305_dma_t *)0xfe008020))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_DMA */
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#ifndef GINT_MPU_INTC
|
||||
#define GINT_MPU_INTC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
|
@ -297,4 +301,8 @@ typedef struct
|
|||
extern sh7705_intc_t SH7705_INTC;
|
||||
extern sh7305_intc_t SH7305_INTC;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_INTC */
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
#ifndef GINT_MPU_MMU
|
||||
#define GINT_MPU_MMU
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
|
@ -152,4 +156,8 @@ typedef volatile struct
|
|||
|
||||
#define SH7305_MMU (*(sh7305_mmu_t *)0xff000000)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_MMU */
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
#ifndef GINT_MPU_PFC
|
||||
#define GINT_MPU_PFC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
|
@ -75,4 +79,8 @@ typedef volatile struct
|
|||
// TODO: Document the SH7305 Pin Function Controller
|
||||
//---
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_PFC */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_MPU_POWER
|
||||
#define GINT_MPU_POWER
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
|
@ -103,4 +107,8 @@ typedef volatile struct
|
|||
|
||||
#define SH7305_POWER (*((sh7305_power_t *)0xa4150020))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_POWER */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_MPU_RTC
|
||||
#define GINT_MPU_RTC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
|
||||
//---
|
||||
|
@ -75,4 +79,8 @@ typedef volatile struct
|
|||
#define SH7705_RTC (*((rtc_t *)0xfffffec0))
|
||||
#define SH7305_RTC (*((rtc_t *)0xa413fec0))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_RTC */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_MPU_SPU
|
||||
#define GINT_MPU_SPU
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
typedef volatile struct
|
||||
|
@ -94,4 +98,8 @@ typedef volatile struct
|
|||
#define SH7305_DSP0 (*(spu_dsp_t *)0xfe2ffd00)
|
||||
#define SH7305_DSP1 (*(spu_dsp_t *)0xfe3ffd00)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_SPU */
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
#ifndef GINT_MPU_TMU
|
||||
#define GINT_MPU_TMU
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
|
@ -101,4 +105,8 @@ typedef volatile struct
|
|||
typedef volatile etmu_t sh7305_etmu_t[6];
|
||||
#define SH7305_ETMU (*(sh7305_etmu_t *)0xa44d0030)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_TMU */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_MPU_USB
|
||||
#define GINT_MPU_USB
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
typedef volatile struct
|
||||
|
@ -437,4 +441,8 @@ typedef volatile word_union(sh7305_usb_uponcr_t,
|
|||
#define SH7305_USB (*(sh7305_usb_t *)0xa4d80000)
|
||||
#define SH7305_USB_UPONCR (*(sh7305_usb_uponcr_t *)0xa40501d4)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_USB */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_RTC
|
||||
#define GINT_RTC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
#include <gint/timer.h>
|
||||
|
@ -13,7 +17,11 @@
|
|||
// 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
|
||||
{
|
||||
uint16_t year; /* Years (exact value, e.g. 2018) */
|
||||
|
@ -96,4 +104,8 @@ int rtc_start_timer(int frequency, timer_callback_t callback, ...);
|
|||
__attribute__((deprecated("Use rtc_periodic_disable() instead")))
|
||||
void rtc_stop_timer(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_RTC */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_TIMER
|
||||
#define GINT_TIMER
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/mpu/tmu.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
@ -158,10 +162,11 @@ void timer_stop(int timer);
|
|||
it may have only paused. If the timer never stops, you're in trouble. */
|
||||
void timer_wait(int timer);
|
||||
|
||||
/* timer_spinwait(): Actively wait for a timer to raise UNF
|
||||
/* timer_spinwait(): Start a timer and actively wait for UNF
|
||||
Waits until the timer raises UNF, without sleeping. This is useful for
|
||||
delays in driver code that is run when interrupts are disabled. This relies
|
||||
neither on the interrupt signal nor on the UNIE flag. */
|
||||
delays in driver code that is run when interrupts are disabled. UNIE is
|
||||
disabled before starting the timer and waiting, so the callback is never
|
||||
called. */
|
||||
void timer_spinwait(int timer);
|
||||
|
||||
//---
|
||||
|
@ -236,4 +241,8 @@ int timer_setup(int timer, uint64_t delay_us, timer_callback_t callback, ...);
|
|||
Use GINT_CALL_SET_STOP() with timer_configure() instead. */
|
||||
int timer_timeout(volatile void *arg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_TIMER */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_USB_FF_BULK
|
||||
#define GINT_USB_FF_BULK
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/usb.h>
|
||||
|
||||
/* The bulk transfer interface with class code 0xff provides a very simple
|
||||
|
@ -51,7 +55,7 @@ int usb_ff_bulk_output(void);
|
|||
|
||||
/* usb_fxlink_header_t: Message header for fxlink
|
||||
|
||||
fxlink supports a minimalistic protocol to receive data sent from the
|
||||
fxlink supports a minimalist 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.
|
||||
|
@ -93,8 +97,8 @@ typedef struct
|
|||
|
||||
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 *application,
|
||||
char *type, uint32_t data_size);
|
||||
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
|
||||
|
@ -164,4 +168,25 @@ void usb_fxlink_screenshot_gray(bool onscreen);
|
|||
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
|
||||
|
||||
This function is essentially the same as usb_fxlink_screenshot(). It sends a
|
||||
capture of the VRAM to fxlink but uses the "video" type, which fxlink
|
||||
displays in real-time or saves as a video file. The meaning of the onscreen
|
||||
setting is identical to usb_fxlink_screenshot().
|
||||
|
||||
This function can be called with onscreen=false as a dupdate() hook to
|
||||
automatically send new frames to fxlink. */
|
||||
void usb_fxlink_videocapture(bool onscreen);
|
||||
|
||||
#ifdef FX9860G
|
||||
/* usb_fxlink_videocapture_gray(): Send a gray frame for a video recording
|
||||
Like usb_fxlink_videocapture(), but uses VRAM data from the gray engine. */
|
||||
void usb_fxlink_videocapture_gray(bool onscreen);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_USB_FF_BULK */
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#ifndef GINT_USB
|
||||
#define GINT_USB
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/std/endian.h>
|
||||
#include <gint/gint.h>
|
||||
|
@ -36,7 +40,7 @@ enum {
|
|||
|
||||
/* This pipe is busy (returned by usb_write_async()) */
|
||||
USB_WRITE_BUSY,
|
||||
/* Both FIFO controlles are busy, none is available to transfer */
|
||||
/* Both FIFO controllers are busy, none is available to transfer */
|
||||
USB_WRITE_NOFIFO,
|
||||
|
||||
/* This pipe is busy (returned by usb_commit_async()) */
|
||||
|
@ -376,4 +380,8 @@ uint16_t usb_dc_string(uint16_t const *literal, size_t len);
|
|||
This is mostly used by the driver to answer GET_DESCRIPTOR requests. */
|
||||
usb_dc_string_t *usb_dc_string_get(uint16_t id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_USB */
|
||||
|
|
|
@ -29,7 +29,7 @@ const clock_frequency_t *clock_freq(void)
|
|||
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
|
||||
#define CPG SH7705_CPG
|
||||
|
||||
static void sh7705_probe(void)
|
||||
void sh7705_probe(void)
|
||||
{
|
||||
/* According to Sentaro21 in the sources of Ftune 1.0.1, the clock mode
|
||||
is thought to be 5, which means that:
|
||||
|
@ -82,7 +82,7 @@ static void sh7305_probe(void)
|
|||
{
|
||||
/* The meaning of the PLL setting on SH7305 differs from the
|
||||
documentation of SH7224; the value must not be doubled. */
|
||||
int pll = CPG.FRQCRA.STC + 1;
|
||||
int pll = CPG.FRQCR.STC + 1;
|
||||
freq.PLL = pll;
|
||||
|
||||
/* The FLL ratio is the value of the setting, halved if SELXM=1 */
|
||||
|
@ -93,9 +93,9 @@ static void sh7305_probe(void)
|
|||
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but on
|
||||
the SH7305 it is 1 / (2^setting + 1). */
|
||||
|
||||
int divb = CPG.FRQCRA.BFC;
|
||||
int divi = CPG.FRQCRA.IFC;
|
||||
int divp = CPG.FRQCRA.P1FC;
|
||||
int divb = CPG.FRQCR.BFC;
|
||||
int divi = CPG.FRQCR.IFC;
|
||||
int divp = CPG.FRQCR.P1FC;
|
||||
|
||||
freq.Bphi_div = 1 << (divb + 1);
|
||||
freq.Iphi_div = 1 << (divi + 1);
|
||||
|
@ -115,10 +115,21 @@ static void sh7305_probe(void)
|
|||
|
||||
#undef CPG
|
||||
|
||||
|
||||
//---
|
||||
// Initialization
|
||||
//---
|
||||
|
||||
void cpg_compute_freq(void)
|
||||
{
|
||||
/* 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();
|
||||
}
|
||||
|
||||
static void configure(void)
|
||||
{
|
||||
/* Disable spread spectrum in SSGSCR */
|
||||
|
@ -127,12 +138,7 @@ static void configure(void)
|
|||
SH7305_CPG.SSCGCR.SSEN = 0;
|
||||
}
|
||||
|
||||
/* 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();
|
||||
cpg_compute_freq();
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -141,12 +147,18 @@ static void configure(void)
|
|||
|
||||
static void hsave(cpg_state_t *s)
|
||||
{
|
||||
if(isSH4()) s->SSCGCR = SH7305_CPG.SSCGCR.lword;
|
||||
if(isSH4()) {
|
||||
s->SSCGCR = SH7305_CPG.SSCGCR.lword;
|
||||
cpg_get_overclock_setting(&s->speed);
|
||||
}
|
||||
}
|
||||
|
||||
static void hrestore(cpg_state_t const *s)
|
||||
{
|
||||
if(isSH4()) SH7305_CPG.SSCGCR.lword = s->SSCGCR;
|
||||
if(isSH4()) {
|
||||
SH7305_CPG.SSCGCR.lword = s->SSCGCR;
|
||||
cpg_set_overclock_setting(&s->speed);
|
||||
}
|
||||
}
|
||||
|
||||
gint_driver_t drv_cpg = {
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
//---
|
||||
// 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>
|
||||
|
||||
#define CPG SH7305_CPG
|
||||
#define BSC SH7305_BSC
|
||||
|
||||
//---
|
||||
// Low-level clock speed access
|
||||
//---
|
||||
|
||||
#define SDMR3_CL2 ((volatile uint8_t *)0xFEC15040)
|
||||
#define SDMR3_CL3 ((volatile uint8_t *)0xFEC15060)
|
||||
|
||||
void cpg_get_overclock_setting(struct cpg_overclock_setting *s)
|
||||
{
|
||||
if(!isSH4())
|
||||
return;
|
||||
|
||||
s->FLLFRQ = CPG.FLLFRQ.lword;
|
||||
s->FRQCR = CPG.FRQCR.lword;
|
||||
|
||||
s->CS0BCR = BSC.CS0BCR.lword;
|
||||
s->CS0WCR = BSC.CS0WCR.lword;
|
||||
s->CS2BCR = BSC.CS2BCR.lword;
|
||||
s->CS2WCR = BSC.CS2WCR.lword;
|
||||
s->CS3BCR = BSC.CS3BCR.lword;
|
||||
s->CS3WCR = BSC.CS3WCR.lword;
|
||||
s->CS5aBCR = BSC.CS5ABCR.lword;
|
||||
s->CS5aWCR = BSC.CS5AWCR.lword;
|
||||
}
|
||||
|
||||
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s)
|
||||
{
|
||||
if(!isSH4())
|
||||
return;
|
||||
|
||||
BSC.CS0WCR.WR = 11; /* 18 cycles */
|
||||
|
||||
CPG.FLLFRQ.lword = s->FLLFRQ;
|
||||
CPG.FRQCR.lword = s->FRQCR;
|
||||
CPG.FRQCR.KICK = 1;
|
||||
while(CPG.LSTATS != 0) {}
|
||||
|
||||
BSC.CS0BCR.lword = s->CS0BCR;
|
||||
BSC.CS0WCR.lword = s->CS0WCR;
|
||||
BSC.CS2BCR.lword = s->CS2BCR;
|
||||
BSC.CS2WCR.lword = s->CS2WCR;
|
||||
BSC.CS3BCR.lword = s->CS3BCR;
|
||||
BSC.CS3WCR.lword = s->CS3WCR;
|
||||
|
||||
if(BSC.CS3WCR.A3CL == 1)
|
||||
*SDMR3_CL2 = 0;
|
||||
else
|
||||
*SDMR3_CL3 = 0;
|
||||
|
||||
BSC.CS5ABCR.lword = s->CS5aBCR;
|
||||
BSC.CS5AWCR.lword = s->CS5aWCR;
|
||||
}
|
||||
|
||||
//---
|
||||
// Predefined clock speeds
|
||||
//---
|
||||
|
||||
#ifdef FXCG50
|
||||
|
||||
#define PLL_32x 0b011111
|
||||
#define PLL_26x 0b011001
|
||||
#define PLL_16x 0b001111
|
||||
#define DIV_2 0
|
||||
#define DIV_4 1
|
||||
#define DIV_8 2
|
||||
#define DIV_16 3
|
||||
#define DIV_32 4
|
||||
|
||||
static struct cpg_overclock_setting settings_cg50[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 = (PLL_16x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+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 = (PLL_26x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+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 = (PLL_32x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_8<<8)+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 = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000440,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting settings_cg20[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 = (PLL_32x<<24)+(DIV_8<<20)+(DIV_16<<12)+(DIV_16<<8)+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 = (PLL_32x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+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 = (PLL_32x<<24)+(DIV_4<<20)+(DIV_4<<12)+(DIV_4<<8)+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 = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_16,
|
||||
.CS0BCR = 0x34900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000402C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting *get_settings(void)
|
||||
{
|
||||
if(gint[HWCALC] == HWCALC_FXCG50)
|
||||
return settings_cg50;
|
||||
if(gint[HWCALC] == HWCALC_PRIZM)
|
||||
return settings_cg20;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int clock_get_speed(void)
|
||||
{
|
||||
struct cpg_overclock_setting *settings = get_settings();
|
||||
if(!settings)
|
||||
return CLOCK_SPEED_UNKNOWN;
|
||||
|
||||
for(int i = 0; i < 5; i++) {
|
||||
struct cpg_overclock_setting *s = &settings[i];
|
||||
|
||||
if(CPG.FLLFRQ.lword == s->FLLFRQ
|
||||
&& CPG.FRQCR.lword == s->FRQCR
|
||||
&& BSC.CS0BCR.lword == s->CS0BCR
|
||||
&& BSC.CS2BCR.lword == s->CS2BCR
|
||||
&& BSC.CS3BCR.lword == s->CS3BCR
|
||||
&& BSC.CS5ABCR.lword == s->CS5aBCR
|
||||
&& BSC.CS0WCR.lword == s->CS0WCR
|
||||
&& BSC.CS2WCR.lword == s->CS2WCR
|
||||
&& BSC.CS3WCR.lword == s->CS3WCR
|
||||
&& 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 *settings = get_settings();
|
||||
if(!settings)
|
||||
return;
|
||||
|
||||
struct cpg_overclock_setting *s = &settings[level - CLOCK_SPEED_F1];
|
||||
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();
|
||||
|
||||
/* Set the clock settings */
|
||||
cpg_set_overclock_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();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
.global _cpu_csleep_init
|
||||
.global _cpu_csleep
|
||||
.global _cpu_csleep_cancel
|
||||
|
||||
_cpu_csleep_init:
|
||||
mov.l .memcpy, r1
|
||||
mova sleep, r0
|
||||
mov r0, r5
|
||||
jmp @r1
|
||||
mov #(sleep_end - sleep), r6
|
||||
|
||||
.align 4
|
||||
.memcpy:
|
||||
.long _memcpy
|
||||
|
||||
_cpu_csleep:
|
||||
mov.l r8, @-r15
|
||||
sts.l pr, @-r15
|
||||
mov r4, r8
|
||||
|
||||
/* Check if the sleep instruction is still there */
|
||||
1: mov.w @(8, r8), r0
|
||||
extu.w r0, r0
|
||||
cmp/eq #0x001b, r0
|
||||
bf 2f
|
||||
|
||||
/* Invalidate the cache in case of previous ICS being cached */
|
||||
mov r8, r0
|
||||
icbi @r0
|
||||
add #18, r0
|
||||
icbi @r0
|
||||
|
||||
/* Execute the sleep, and loop */
|
||||
jsr @r8
|
||||
nop
|
||||
bra 1b
|
||||
nop
|
||||
|
||||
2: lds.l @r15+, pr
|
||||
rts
|
||||
mov.l @r15+, r8
|
||||
|
||||
_cpu_csleep_cancel:
|
||||
mov #0x0009, r0
|
||||
add #8, r4
|
||||
mov.w r0, @r4
|
||||
icbi @r4
|
||||
rts
|
||||
nop
|
||||
|
||||
.align 4
|
||||
|
||||
/* This is identical in functionality to the CPU driver's sleep() function */
|
||||
sleep:
|
||||
mov.l 2f, r0
|
||||
mov.l @r0, r0
|
||||
cmp/pl r0
|
||||
bt 1f
|
||||
sleep
|
||||
1: rts
|
||||
nop
|
||||
nop
|
||||
2: .long _cpu_sleep_block_counter
|
||||
sleep_end:
|
|
@ -8,6 +8,7 @@
|
|||
#include <gint/drivers/states.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/exc.h>
|
||||
#include <gint/cpu.h>
|
||||
|
||||
#define DMA SH7305_DMA
|
||||
#define POWER SH7305_POWER
|
||||
|
@ -18,6 +19,8 @@ typedef volatile sh7305_dma_channel_t channel_t;
|
|||
static gint_call_t dma_callbacks[6] = { 0 };
|
||||
/* Sleep blocking flags for all channels */
|
||||
static bool dma_sleep_blocking[6] = { 0 };
|
||||
/* ICS for dma_channel_wait() for all channels */
|
||||
static cpu_csleep_t *dma_wait_ics[6] = { 0 };
|
||||
|
||||
/* dma_channel(): Get address of a DMA channel */
|
||||
static channel_t *dma_channel(int channel)
|
||||
|
@ -113,6 +116,11 @@ static int dma_setup(int channel, dma_size_t size, uint blocks,
|
|||
if(ch->DAR >= 0xe5007000 && ch->DAR <= 0xe5204000)
|
||||
dma_sleep_blocking[channel] = true;
|
||||
|
||||
if(ch->SAR >= 0xfe200000 && ch->SAR <= 0xfe3fffff)
|
||||
dma_sleep_blocking[channel] = true;
|
||||
if(ch->DAR >= 0xfe200000 && ch->DAR <= 0xfe3fffff)
|
||||
dma_sleep_blocking[channel] = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -138,6 +146,7 @@ bool dma_transfer_async(int channel, dma_size_t size, uint blocks,
|
|||
static void dma_interrupt_transfer_ended(int channel)
|
||||
{
|
||||
channel_t *ch = dma_channel(channel);
|
||||
ch->CHCR.IE = 0;
|
||||
ch->CHCR.DE = 0;
|
||||
ch->CHCR.TE = 0;
|
||||
|
||||
|
@ -147,6 +156,10 @@ static void dma_interrupt_transfer_ended(int channel)
|
|||
if(dma_sleep_blocking[channel])
|
||||
sleep_unblock();
|
||||
|
||||
/* Cancel any sleep operation that is synchronized with this interrupt */
|
||||
if(dma_wait_ics[channel])
|
||||
cpu_csleep_cancel(dma_wait_ics[channel]);
|
||||
|
||||
if(dma_callbacks[channel].function)
|
||||
{
|
||||
gint_call(dma_callbacks[channel]);
|
||||
|
@ -154,24 +167,52 @@ static void dma_interrupt_transfer_ended(int channel)
|
|||
}
|
||||
}
|
||||
|
||||
/* dma_transfer_wait(): Wait for a transfer to finish */
|
||||
void dma_transfer_wait(int channel)
|
||||
{
|
||||
if(channel < 0)
|
||||
{
|
||||
for(int channel = 0; channel < 6; channel++)
|
||||
dma_transfer_wait(channel);
|
||||
return;
|
||||
}
|
||||
/* dma_channel_wait(): Wait for a particular channel's transfer to finish
|
||||
|
||||
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)
|
||||
{
|
||||
channel_t *ch = dma_channel(channel);
|
||||
if(!ch) return;
|
||||
|
||||
while(ch->CHCR.DE && !ch->CHCR.TE)
|
||||
/* If interrupts are disabled or we don't own the device, spin-wait by
|
||||
checking either for TE to be set (Transfere Ended) or DE to be gone
|
||||
(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)
|
||||
{
|
||||
/* Sleep only if the interrupt is enabled to wake us up */
|
||||
if(ch->CHCR.IE) sleep();
|
||||
while(ch->CHCR.DE && !ch->CHCR.TE) {}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize an interrupt-cancellable sleep, to ensure
|
||||
synchronization */
|
||||
cpu_csleep_t ics;
|
||||
cpu_csleep_init(&ics);
|
||||
dma_wait_ics[channel] = &ics;
|
||||
|
||||
/* Now the ICS is set; if the interrupt has not occurred yet then the
|
||||
handler is guaranteed to cancel the sleep at some point */
|
||||
if(ch->CHCR.DE && !ch->CHCR.TE) cpu_csleep(&ics);
|
||||
|
||||
/* Clear the ICS pointer for next time */
|
||||
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,
|
||||
|
@ -258,10 +299,18 @@ static void configure(void)
|
|||
DMA.OR.DME = 1;
|
||||
}
|
||||
|
||||
static void universal_unbind(void)
|
||||
static void funbind(void)
|
||||
{
|
||||
/* Make sure any DMA transfer is finished before leaving the app */
|
||||
dma_transfer_wait(-1);
|
||||
/* Wait for all OS transfers to finish before taking over */
|
||||
for(int channel = 0; channel < 6; channel++)
|
||||
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)
|
||||
|
@ -322,8 +371,8 @@ static void hrestore(dma_state_t const *s)
|
|||
gint_driver_t drv_dma0 = {
|
||||
.name = "DMA",
|
||||
.configure = configure,
|
||||
.funbind = universal_unbind,
|
||||
.unbind = universal_unbind,
|
||||
.funbind = funbind,
|
||||
.unbind = unbind,
|
||||
.hpowered = hpowered,
|
||||
.hpoweron = hpoweron,
|
||||
.hpoweroff = hpoweroff,
|
||||
|
|
|
@ -4,6 +4,18 @@
|
|||
void *dma_memcpy(void * __restrict dst, const void * __restrict src,
|
||||
size_t size)
|
||||
{
|
||||
dma_transfer_sync(1, DMA_32B, size >> 5, src, DMA_INC, dst, DMA_INC);
|
||||
int block_size = DMA_32B;
|
||||
int block_count = size >> 5;
|
||||
|
||||
/* Use 4-byte transfers to access SPU memory */
|
||||
if(((uint32_t)src >= 0xfe200000 && (uint32_t)src < 0xfe400000) ||
|
||||
((uint32_t)dst >= 0xfe200000 && (uint32_t)dst < 0xfe400000))
|
||||
{
|
||||
block_size = DMA_4B;
|
||||
block_count = size >> 2;
|
||||
}
|
||||
|
||||
dma_transfer_sync(1, block_size, block_count, src, DMA_INC, dst,
|
||||
DMA_INC);
|
||||
return dst;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,17 @@ void *dma_memset(void *dst, uint32_t l, size_t size)
|
|||
different memory regions, making the DMA faster than the CPU. */
|
||||
for(int i = 0; i < 8; i++) ILbuf[i] = l;
|
||||
|
||||
dma_transfer_sync(1, DMA_32B, size>>5, ILbuf, DMA_FIXED, dst, DMA_INC);
|
||||
int block_size = DMA_32B;
|
||||
int block_count = size >> 5;
|
||||
|
||||
/* Use 4-byte transfers to access SPU memory */
|
||||
if((uint32_t)dst >= 0xfe200000 && (uint32_t)dst < 0xfe400000)
|
||||
{
|
||||
block_size = DMA_4B;
|
||||
block_count = size >> 2;
|
||||
}
|
||||
|
||||
dma_transfer_sync(1, block_size, block_count, ILbuf, DMA_FIXED, dst,
|
||||
DMA_INC);
|
||||
return dst;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int closedir(DIR *dp)
|
||||
{
|
||||
int rc = close(dp->fd);
|
||||
free(dp);
|
||||
return rc;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#include <fcntl.h>
|
||||
|
||||
int creat(char const *path, mode_t mode)
|
||||
{
|
||||
return open(path, O_CREAT | O_WRONLY | O_TRUNC, mode);
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
#include <gint/fs.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* File descriptor table */
|
||||
static fs_descriptor_t fdtable[FS_FD_MAX] = { 0 };
|
||||
|
||||
fs_descriptor_t const *fs_get_descriptor(int fd)
|
||||
{
|
||||
if((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(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((unsigned)fd >= FS_FD_MAX)
|
||||
return;
|
||||
|
||||
fdtable[fd].type = NULL;
|
||||
fdtable[fd].data = NULL;
|
||||
}
|
||||
|
||||
int open_generic(fs_descriptor_type_t *type, void *data, int fd)
|
||||
{
|
||||
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 devnull = {
|
||||
.read = NULL,
|
||||
.write = NULL,
|
||||
.lseek = NULL,
|
||||
.close = NULL,
|
||||
};
|
||||
|
||||
GCONSTRUCTOR static void init_standard_streams(void)
|
||||
{
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
#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,
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
#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_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 */
|
|
@ -0,0 +1,159 @@
|
|||
#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;
|
||||
|
||||
dir_t *dp = malloc(sizeof *dp);
|
||||
if(!dp) goto alloc_failure;
|
||||
|
||||
dp->count = 0;
|
||||
dp->entries = NULL;
|
||||
dp->pos = 0;
|
||||
/* We allocate by batches of 8 */
|
||||
int sd=-1, rc, allocated=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 end;
|
||||
}
|
||||
|
||||
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;
|
||||
fugue_dir_close(dp);
|
||||
end:
|
||||
free(wildcard);
|
||||
free(search);
|
||||
free(fc_path);
|
||||
if(sd >= 0)
|
||||
BFile_FindClose(sd);
|
||||
return dp;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
#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);
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
#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);
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#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 */
|
|
@ -0,0 +1,23 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#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);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#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);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct dirent *readdir(DIR *dp)
|
||||
{
|
||||
struct dirent *ent = NULL;
|
||||
read(dp->fd, &ent, sizeof ent);
|
||||
return ent;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void rewinddir(DIR *dp)
|
||||
{
|
||||
lseek(dp->fd, 0, SEEK_SET);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <unistd.h>
|
||||
#include "fugue/fugue.h"
|
||||
|
||||
int rmdir(char const *path)
|
||||
{
|
||||
/* Standard rmdir() is the Fugue filesystem only */
|
||||
return fugue_rmdir(path);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void seekdir(DIR *dp, long loc)
|
||||
{
|
||||
lseek(dp->fd, loc, SEEK_SET);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#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);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
long telldir(DIR *dp)
|
||||
{
|
||||
return lseek(dp->fd, 0, SEEK_CUR);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <unistd.h>
|
||||
#include "fugue/fugue.h"
|
||||
|
||||
int unlink(char const *path)
|
||||
{
|
||||
/* Standard unlink() is the Fugue filesystem only */
|
||||
return fugue_unlink(path);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#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;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
//---
|
||||
// gint:image:fixed - Minimal fixed-point interface for linear transformations
|
||||
//---
|
||||
|
||||
#ifndef GINT_IMAGE_FIXED
|
||||
#define GINT_IMAGE_FIXED
|
||||
|
||||
/* Constants */
|
||||
#define fconst(x) ((x) * 65536)
|
||||
|
||||
/* Multiplication */
|
||||
static inline int fmul(int x, int y)
|
||||
{
|
||||
return ((int64_t)x * (int64_t)y) >> 16;
|
||||
}
|
||||
|
||||
/* Division */
|
||||
static inline int fdiv(int x, int y)
|
||||
{
|
||||
return ((int64_t)x << 16) / y;
|
||||
}
|
||||
|
||||
/* Integer square root */
|
||||
static inline int isqrt(int n)
|
||||
{
|
||||
if(n <= 0) return 0;
|
||||
if(n < 4) return 1;
|
||||
|
||||
int low_bound = isqrt(n / 4) * 2;
|
||||
int high_bound = low_bound + 1;
|
||||
|
||||
return (high_bound * high_bound <= n) ? high_bound : low_bound;
|
||||
}
|
||||
|
||||
/* Floor operation */
|
||||
static inline int ffloor(int x)
|
||||
{
|
||||
return (x >> 16);
|
||||
}
|
||||
|
||||
/* Round operation */
|
||||
static inline int fround(int x)
|
||||
{
|
||||
return ffloor(x + fconst(0.5));
|
||||
}
|
||||
|
||||
#endif /* GINT_IMAGE_FIXED */
|
|
@ -0,0 +1,27 @@
|
|||
#include <gint/image.h>
|
||||
#include <stdlib.h>
|
||||
#include <gint/defs/util.h>
|
||||
|
||||
image_t *image_alloc(int width, int height, int format)
|
||||
{
|
||||
image_t *img = image_create(width, height, format);
|
||||
if(!img)
|
||||
return NULL;
|
||||
|
||||
if(IMAGE_IS_RGB16(format))
|
||||
img->stride = ((width + 1) >> 1) * 4;
|
||||
else if(IMAGE_IS_P8(format))
|
||||
img->stride = width;
|
||||
else if(IMAGE_IS_P4(format))
|
||||
img->stride = ((width + 1) >> 1);
|
||||
|
||||
void *data = malloc(height * img->stride);
|
||||
if(!data) {
|
||||
image_free(img);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
img->data = data;
|
||||
img->flags |= IMAGE_FLAGS_DATA_ALLOC;
|
||||
return img;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
bool image_alloc_palette(image_t *img, int size)
|
||||
{
|
||||
if(!img || !IMAGE_IS_INDEXED(img->format))
|
||||
return false;
|
||||
if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC)
|
||||
free(img->palette);
|
||||
|
||||
if(IMAGE_IS_P8(img->format)) {
|
||||
size = (size <= 0) ? 256 : min(size, 256);
|
||||
}
|
||||
if(IMAGE_IS_P4(img->format)) {
|
||||
size = 16;
|
||||
}
|
||||
|
||||
img->palette = calloc(size, 2);
|
||||
img->color_count = 0;
|
||||
img->flags &= ~IMAGE_FLAGS_PALETTE_ALLOC;
|
||||
|
||||
if(!img->palette)
|
||||
return false;
|
||||
|
||||
memset(img->palette, 0, 2*size);
|
||||
img->color_count = size;
|
||||
img->flags |= IMAGE_FLAGS_PALETTE_ALLOC;
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
int image_alpha(int format)
|
||||
{
|
||||
switch(format) {
|
||||
case IMAGE_RGB565A:
|
||||
return 0x0001;
|
||||
case IMAGE_P8_RGB565A:
|
||||
return -128;
|
||||
case IMAGE_P4_RGB565A:
|
||||
return 0;
|
||||
default:
|
||||
/* A value that cannot be found in any pixel of any format */
|
||||
return 0x10000;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
void image_clear(image_t *img)
|
||||
{
|
||||
if(!IMAGE_IS_ALPHA(img->format))
|
||||
return;
|
||||
|
||||
image_fill(img, image_alpha(img->format));
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
|
||||
void image_copy(image_t const *src, image_t *dst, bool copy_alpha)
|
||||
{
|
||||
if(!image_target(src, dst, DATA_RW))
|
||||
return;
|
||||
if(!IMAGE_IS_ALPHA(src->format))
|
||||
copy_alpha = true;
|
||||
|
||||
/* Clip the input to match the size of the output */
|
||||
int w = min(src->width, dst->width);
|
||||
int h = min(src->height, dst->height);
|
||||
if(w <= 0 || h <= 0)
|
||||
return;
|
||||
|
||||
void *src_px = src->data;
|
||||
void *dst_px = dst->data;
|
||||
int src_alpha = image_alpha_2(src->format, copy_alpha);
|
||||
int dst_alpha = image_alpha_2(dst->format, copy_alpha);
|
||||
|
||||
if(IMAGE_IS_RGB16(src->format) && IMAGE_IS_RGB16(dst->format)) {
|
||||
do {
|
||||
for(int x = 0; x < w; x++) {
|
||||
int px = ((uint16_t *)src_px)[x];
|
||||
if(px != src_alpha) {
|
||||
/* Don't copy opaque pixels of value 0x0001 into an RGB565A
|
||||
array. We can use -= which is faster (subc) without
|
||||
changing the visuals because dst_alpha != 0. */
|
||||
((uint16_t *)dst_px)[x] = px - (px == dst_alpha);
|
||||
}
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
} while(--h > 0);
|
||||
}
|
||||
else if(IMAGE_IS_P8(src->format) && IMAGE_IS_RGB16(dst->format)) {
|
||||
uint16_t *palette = src->palette + 128;
|
||||
|
||||
do {
|
||||
for(int x = 0; x < w; x++) {
|
||||
int px = ((int8_t *)src_px)[x];
|
||||
if(px != src_alpha) {
|
||||
px = palette[px];
|
||||
((uint16_t *)dst_px)[x] = px - (px == dst_alpha);
|
||||
}
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
} while(--h > 0);
|
||||
}
|
||||
else if(IMAGE_IS_P8(src->format) && IMAGE_IS_P8(dst->format)) {
|
||||
do {
|
||||
for(int x = 0; x < w; x++) {
|
||||
int px = ((int8_t *)src_px)[x];
|
||||
if(px != src_alpha)
|
||||
((int8_t *)dst_px)[x] = px;
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
} while(--h > 0);
|
||||
}
|
||||
else if(IMAGE_IS_P8(src->format) && IMAGE_IS_P4(dst->format)) {
|
||||
do {
|
||||
for(int x = 0; x < w; x++) {
|
||||
int px = ((int8_t *)src_px)[x];
|
||||
if(px != src_alpha) {
|
||||
uint8_t *cell = dst_px + (x >> 1);
|
||||
if(x & 1)
|
||||
*cell = (*cell & 0xf0) | (px & 0x0f);
|
||||
else
|
||||
*cell = (*cell & 0x0f) | (px << 4);
|
||||
}
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
} while(--h > 0);
|
||||
}
|
||||
else if(IMAGE_IS_P4(src->format) && IMAGE_IS_P4(dst->format)) {
|
||||
do {
|
||||
for(int x = 0; x < w; x++) {
|
||||
int px = ((uint8_t *)src_px)[x >> 1];
|
||||
px = (x & 1) ? (px & 0x0f) : (px >> 4);
|
||||
if(px != src_alpha) {
|
||||
uint8_t *cell = dst_px + (x >> 1);
|
||||
if(x & 1)
|
||||
*cell = (*cell & 0xf0) | (px & 0x0f);
|
||||
else
|
||||
*cell = (*cell & 0x0f) | (px << 4);
|
||||
}
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
} while(--h > 0);
|
||||
}
|
||||
else if(IMAGE_IS_P4(src->format) && IMAGE_IS_P8(dst->format)) {
|
||||
do {
|
||||
for(int x = 0; x < w; x++) {
|
||||
int px = ((uint8_t *)src_px)[x >> 1];
|
||||
px = (x & 1) ? (px & 0x0f) : (px >> 4);
|
||||
if(px != src_alpha)
|
||||
((int8_t *)dst_px)[x] = px;
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
} while(--h > 0);
|
||||
}
|
||||
else if(IMAGE_IS_P4(src->format) && IMAGE_IS_RGB16(dst->format)) {
|
||||
do {
|
||||
for(int x = 0; x < w; x++) {
|
||||
int px = ((uint8_t *)src_px)[x >> 1];
|
||||
px = (x & 1) ? (px & 0x0f) : (px >> 4);
|
||||
if(px != src_alpha) {
|
||||
px = src->palette[px];
|
||||
((uint16_t *)dst_px)[x] = px - (px == dst_alpha);
|
||||
}
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
} while(--h > 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <string.h>
|
||||
|
||||
image_t *image_copy_alloc(image_t const *src, int new_format)
|
||||
{
|
||||
if(!image_valid(src))
|
||||
return NULL;
|
||||
|
||||
image_t *dst = image_alloc(src->width, src->height, new_format);
|
||||
if(!dst)
|
||||
return NULL;
|
||||
if(!image_copy_palette(src, dst, -1)) {
|
||||
image_free(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_copy(src, dst, true);
|
||||
return dst;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <string.h>
|
||||
|
||||
bool image_copy_palette(image_t const *src, image_t *dst, int size)
|
||||
{
|
||||
if(!image_valid(src) || !dst)
|
||||
return false;
|
||||
if(!IMAGE_IS_INDEXED(dst->format))
|
||||
return true;
|
||||
|
||||
if(size < 0)
|
||||
size = src->color_count;
|
||||
if(!image_alloc_palette(dst, size))
|
||||
return false;
|
||||
|
||||
int N = min(src->color_count, dst->color_count);
|
||||
memcpy(dst->palette, src->palette, 2*N);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#include <gint/image.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
image_t *image_create(int width, int height, int format)
|
||||
{
|
||||
if(!IMAGE_IS_RGB16(format) && !IMAGE_IS_P8(format) && !IMAGE_IS_P4(format))
|
||||
return NULL;
|
||||
if(width <= 0 || width > 0xffff || height <= 0 || height > 0xffff)
|
||||
return NULL;
|
||||
|
||||
image_t *img = malloc(sizeof *img);
|
||||
if(!img)
|
||||
return NULL;
|
||||
|
||||
img->format = format;
|
||||
img->flags = 0;
|
||||
img->color_count = 0;
|
||||
img->width = width;
|
||||
img->height = height;
|
||||
img->stride = 0;
|
||||
img->data = NULL;
|
||||
img->palette = NULL;
|
||||
|
||||
return img;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
image_t *image_create_vram(void)
|
||||
{
|
||||
image_t *img = image_create(DWIDTH, DHEIGHT, IMAGE_RGB565);
|
||||
if(!img)
|
||||
return NULL;
|
||||
|
||||
img->stride = 2 * DWIDTH;
|
||||
img->data = gint_vram;
|
||||
return img;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
int image_data_size(image_t const *img)
|
||||
{
|
||||
return img->stride * img->height;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
int image_decode_pixel(image_t const *img, int pixel)
|
||||
{
|
||||
if(IMAGE_IS_RGB16(img->format))
|
||||
return pixel;
|
||||
else if(IMAGE_IS_P8(img->format))
|
||||
return img->palette[pixel+128];
|
||||
else if(IMAGE_IS_P4(img->format))
|
||||
return img->palette[pixel];
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
void image_fill(image_t *img, int value)
|
||||
{
|
||||
if(!image_target(img, img, NOT_P4, DATA_RW))
|
||||
return;
|
||||
|
||||
void *img_px = img->data;
|
||||
|
||||
if(IMAGE_IS_RGB16(img->format)) {
|
||||
for(int y = 0; y < img->height; y++) {
|
||||
for(int x = 0; x < img->width; x++) {
|
||||
((uint16_t *)img_px)[x] = value;
|
||||
}
|
||||
img_px += img->stride;
|
||||
}
|
||||
}
|
||||
else if(IMAGE_IS_P8(img->format)) {
|
||||
for(int y = 0; y < img->height; y++) {
|
||||
for(int x = 0; x < img->width; x++) {
|
||||
((int8_t *)img_px)[x] = value;
|
||||
}
|
||||
img_px += img->stride;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/mmu.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void image_free(image_t *img)
|
||||
{
|
||||
if(!img || mmu_is_rom(img))
|
||||
return;
|
||||
|
||||
if(img->flags & IMAGE_FLAGS_DATA_ALLOC)
|
||||
free(img->data);
|
||||
if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC)
|
||||
free(img->palette);
|
||||
|
||||
free(img);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue