Compare commits
129 Commits
Author | SHA1 | Date |
---|---|---|
Lephe | dcb876dfe0 | |
Lephe | 82258b9f89 | |
Lephe | eb49f0258d | |
Lephe | b5f0f08183 | |
Lephe | 1423f40a96 | |
Lephe | 3a2a194693 | |
Lephe | 4983849510 | |
Lephe | fc1f510288 | |
Lephe | c59b2f90fb | |
Lephe | eb1f9a35a1 | |
Lephe | f4e13afa84 | |
Lephe | 911691461f | |
Lephe | aee67a76f3 | |
Lephe | c0671649df | |
Lephe | 53c67af52d | |
Lephe | 6910714c2c | |
Lephe | af5c16a3d3 | |
Lephe | 1a61e97ef0 | |
Lephe | a091efc894 | |
Lephe | 6f758cd36c | |
Lephe | 18e0db3886 | |
Lephe | cf2b86deaa | |
Lephe | db50c9b192 | |
Lephe | 42853103aa | |
Lephe | c28d06002f | |
Lephe | ac6b1c7d70 | |
Lephe | 45881995e9 | |
Lephe | 70dccc29da | |
Lephe | 7e859169fe | |
Lephe | b3416dcc25 | |
Lephe | 736b58f205 | |
Lephe | 478fdaea76 | |
Lephe | d3b29c50e6 | |
Lephe | 1272a6a71a | |
Lephe | 723bae134b | |
Lephe | 3bc3892524 | |
Lephe | 211d236496 | |
Lephe | a4cf3516e7 | |
Lephe | 177879d432 | |
Lephe | feb74a38ec | |
Lephe | 3192078c4c | |
Lephenixnoir | a5fb6d3401 | |
Lephe | e2c507855f | |
mibi88 | 813af222fd | |
mibi88 | 984f162cb2 | |
mibi88 | 6426406043 | |
mibi88 | 2fe67412cc | |
mibi88 | f807b528bb | |
mibi88 | ad0da59de6 | |
mibi88 | 56e5c2452a | |
Lephe | 8442e0b9cf | |
Lephe | 5461d54083 | |
mibi88 | 386303136c | |
mibi88 | 8a6f3bfdce | |
mibi88 | d6e8b096b5 | |
Lephe | 4df3d69d8c | |
Lephe | 74438f5da5 | |
Lephe | 45fa6c87c2 | |
Lephe | 5e35871c98 | |
Lephe | 5f9553f3b8 | |
mibi88 | 93e055cfba | |
Lephe | d110ab608e | |
mibi88 | 45e90b55ba | |
Lephe | bbda77769d | |
Lephe | 7b662987f8 | |
Lephe | 252bd4eb41 | |
Lephenixnoir | 48db9bf09d | |
calamari | 1d5675c99f | |
Lephe | 16890ab71b | |
Lephe | c9df6326e4 | |
Lephenixnoir | 97701e8eed | |
calamari | a735e074d8 | |
calamari | ddf340cccd | |
calamari | 5c331e5fa0 | |
calamari | 98fea4f2ec | |
calamari | a4adc5e1bd | |
calamari | b23ed9629e | |
Lephe | 2b4075c8f9 | |
Lephe | e95a91d5ab | |
Lephe | 3e5c45c5ad | |
Lephe | 9924dc4684 | |
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 |
125
CMakeLists.txt
125
CMakeLists.txt
|
@ -1,14 +1,15 @@
|
|||
# Build system for the gint unikernel
|
||||
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(Gint VERSION 2.7.0 LANGUAGES C ASM)
|
||||
project(Gint VERSION 2.10.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")
|
||||
option(GINT_USB_DEBUG "Enable debug functions for the USB driver")
|
||||
|
||||
set(CMAKE_INSTALL_MESSAGE LAZY)
|
||||
|
||||
|
@ -24,6 +25,7 @@ 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
|
||||
|
@ -49,6 +51,7 @@ set(SOURCES_COMMON
|
|||
src/fs/pwrite.c
|
||||
src/fs/read.c
|
||||
src/fs/readdir.c
|
||||
src/fs/rename.c
|
||||
src/fs/rewinddir.c
|
||||
src/fs/rmdir.c
|
||||
src/fs/seekdir.c
|
||||
|
@ -63,6 +66,7 @@ set(SOURCES_COMMON
|
|||
src/fs/fugue/fugue_open.c
|
||||
src/fs/fugue/fugue_mkdir.c
|
||||
src/fs/fugue/fugue_stat.c
|
||||
src/fs/fugue/fugue_rename.c
|
||||
src/fs/fugue/fugue_rmdir.c
|
||||
src/fs/fugue/fugue_unlink.c
|
||||
src/fs/fugue/util.c
|
||||
|
@ -85,7 +89,12 @@ set(SOURCES_COMMON
|
|||
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
|
||||
|
@ -101,6 +110,7 @@ set(SOURCES_COMMON
|
|||
src/render/dtext.c
|
||||
src/render/dupdate_hook.c
|
||||
src/render/dvline.c
|
||||
src/render/dwindow.c
|
||||
src/render/topti.c
|
||||
# RTC driver
|
||||
src/rtc/rtc.c
|
||||
|
@ -113,17 +123,21 @@ set(SOURCES_COMMON
|
|||
src/tmu/sleep.c
|
||||
src/tmu/tmu.c
|
||||
# USB driver
|
||||
src/usb/asyncio.c
|
||||
src/usb/classes/ff-bulk.c
|
||||
src/usb/configure.c
|
||||
src/usb/pipes.c
|
||||
src/usb/read4.S
|
||||
src/usb/setup.c
|
||||
src/usb/string.c
|
||||
src/usb/usb.c
|
||||
src/usb/write4.S
|
||||
)
|
||||
set(SOURCES_FX
|
||||
# Gray engine
|
||||
src/gray/engine.c
|
||||
src/gray/gclear.c
|
||||
src/gray/ggetpixel.c
|
||||
src/gray/gint_gline.c
|
||||
src/gray/gpixel.c
|
||||
src/gray/grect.c
|
||||
|
@ -136,6 +150,7 @@ set(SOURCES_FX
|
|||
src/render-fx/bopti-asm.s
|
||||
src/render-fx/bopti.c
|
||||
src/render-fx/dclear.c
|
||||
src/render-fx/dgetpixel.c
|
||||
src/render-fx/dpixel.c
|
||||
src/render-fx/drect.c
|
||||
src/render-fx/dsubimage.c
|
||||
|
@ -152,10 +167,40 @@ set(SOURCES_FX
|
|||
set(SOURCES_CG
|
||||
# R61524 driver
|
||||
src/r61524/r61524.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/bopti-asm.s
|
||||
src/render-cg/bopti.c
|
||||
src/render-cg/dclear.c
|
||||
src/render-cg/dgetpixel.c
|
||||
src/render-cg/dpixel.c
|
||||
src/render-cg/drect.c
|
||||
src/render-cg/dsubimage.c
|
||||
|
@ -164,6 +209,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)
|
||||
|
@ -172,37 +248,52 @@ 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)
|
||||
add_compile_definitions(FX9860G)
|
||||
add_library(gint-fx STATIC ${SOURCES_COMMON} ${SOURCES_FX} ${ASSETS_FX})
|
||||
set(NAME "gint-fx")
|
||||
set(LINKER_SCRIPT "fx9860g.ld")
|
||||
set(LINKER_SCRIPTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fx9860g.ld")
|
||||
add_library(gint-fx STATIC ${SOURCES_COMMON} ${SOURCES_FX} ${ASSETS_FX}
|
||||
${LINKER_SCRIPTS})
|
||||
endif()
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
|
||||
add_compile_definitions(FXCG50)
|
||||
add_library(gint-cg STATIC ${SOURCES_COMMON} ${SOURCES_CG} ${ASSETS_CG})
|
||||
set(NAME "gint-cg")
|
||||
set(LINKER_SCRIPT "fxcg50.ld")
|
||||
set(LINKER_SCRIPTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50.ld"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50_fastload.ld")
|
||||
add_library(gint-cg STATIC ${SOURCES_COMMON} ${SOURCES_CG} ${ASSETS_CG}
|
||||
${LINKER_SCRIPTS})
|
||||
endif()
|
||||
|
||||
set_target_properties("${NAME}" PROPERTIES OUTPUT_NAME "${NAME}")
|
||||
|
||||
# Generate linker scripts
|
||||
macro(generate_linker_script OUTPUT INPUT OPTIONS)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT}"
|
||||
COMMAND ${FXSDK_TOOLCHAIN}cpp "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}"
|
||||
-P -C -traditional-cpp ${OPTIONS} -o "${OUTPUT}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}")
|
||||
endmacro()
|
||||
generate_linker_script("fx9860g.ld" "fx9860g.ld.c" "")
|
||||
generate_linker_script("fxcg50.ld" "fxcg50.ld.c" "")
|
||||
generate_linker_script("fxcg50_fastload.ld" "fxcg50.ld.c" "-DFXCG50_FASTLOAD")
|
||||
|
||||
# Library file
|
||||
install(TARGETS "${NAME}" DESTINATION "${FXSDK_COMPILER_INSTALL}")
|
||||
# Linker script
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_SCRIPT}"
|
||||
DESTINATION "${FXSDK_COMPILER_INSTALL}")
|
||||
install(TARGETS "${NAME}" DESTINATION "${FXSDK_LIB}")
|
||||
# Linker scripts
|
||||
install(FILES ${LINKER_SCRIPTS} 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}")
|
||||
|
|
|
@ -32,7 +32,7 @@ The library also offers powerful higher-level features:
|
|||
|
||||
* An enhanced version of the system's GetKey() and GetKeyWait()
|
||||
* A gray engine that works by rapidly swapping monochrome images on fx-9860G II
|
||||
* Blazingly fast rendering functions (image rendering is 10 times faster tha
|
||||
* Blazingly fast rendering functions (image rendering is 10 times faster than
|
||||
MonochromeLib)
|
||||
* Integrated font management
|
||||
|
||||
|
@ -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.
|
||||
|
|
11
TODO
11
TODO
|
@ -1,4 +1,8 @@
|
|||
Bugs to fix:
|
||||
* render: figure out why fx-CG dclear() now takes 4.1 ms instead of 2.6 ms
|
||||
|
||||
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
|
||||
|
@ -8,16 +12,15 @@ Extensions on existing code:
|
|||
* 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
|
||||
* 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
|
||||
* fs: support read-only files backed with GetBlockAddress() on fx-CG
|
||||
* kernel: SH4- or G-III-specific linker scripts?
|
||||
* keysc: global shortcut SHIFT+0+EXIT for abort() as an infinite loop break
|
||||
|
||||
Future directions:
|
||||
* Integrate overclock management
|
||||
* Audio playback using TSWilliamson's libsnd method
|
||||
* Serial communication
|
||||
* Make fx9860g projects work out of the box on fxcg50
|
||||
* Use the DSP to enhance parallel computation
|
||||
* Base for Yatis' threads library
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
|
||||
set(PC cg)
|
||||
set(INTF_DEFN FXCG50)
|
||||
set(INTF_LINK "-T;fxcg50.ld")
|
||||
set(INTF_LINK "-T;$<IF:$<CONFIG:FastLoad>,fxcg50_fastload.ld,fxcg50.ld>")
|
||||
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
set(PC fx)
|
||||
set(INTF_DEFN FX9860G)
|
||||
|
@ -14,18 +14,16 @@ endif()
|
|||
execute_process(
|
||||
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libgint-${PC}.a
|
||||
OUTPUT_VARIABLE GINT_PATH
|
||||
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
|
||||
)
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.a
|
||||
OUTPUT_VARIABLE FXLIBC_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(
|
||||
COMMAND fxsdk path include
|
||||
OUTPUT_VARIABLE FXSDK_INCLUDE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
set(GINT_CONFIG_PATH "${FXSDK_INCLUDE}/gint/config.h")
|
||||
|
||||
if("${GINT_PATH}" STREQUAL "libgint-${PC}.a")
|
||||
unset(PATH_TO_LIBGINT)
|
||||
|
@ -49,8 +47,7 @@ endif()
|
|||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Gint
|
||||
REQUIRED_VARS PATH_TO_FXLIBC PATH_TO_LIBGINT
|
||||
VERSION_VAR GINT_VERSION
|
||||
)
|
||||
VERSION_VAR GINT_VERSION)
|
||||
|
||||
if(Gint_FOUND)
|
||||
if(NOT TARGET Gint::Fxlibc)
|
||||
|
@ -58,8 +55,7 @@ if(Gint_FOUND)
|
|||
endif()
|
||||
set_target_properties(Gint::Fxlibc PROPERTIES
|
||||
IMPORTED_LOCATION "${FXLIBC_PATH}"
|
||||
INTERFACE_LINK_OPTIONS "-lgcc"
|
||||
)
|
||||
INTERFACE_LINK_OPTIONS "-lgcc")
|
||||
|
||||
if(NOT TARGET Gint::Gint)
|
||||
add_library(Gint::Gint UNKNOWN IMPORTED)
|
||||
|
@ -68,10 +64,8 @@ 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 "-lopenlibm;-lgcc"
|
||||
INTERFACE_LINK_OPTIONS "${INTF_LINK}"
|
||||
)
|
||||
INTERFACE_LINK_OPTIONS "${INTF_LINK}")
|
||||
|
||||
target_link_libraries(Gint::Gint INTERFACE Gint::Fxlibc)
|
||||
target_link_libraries(Gint::Fxlibc INTERFACE Gint::Gint)
|
||||
|
|
|
@ -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
|
||||
|
@ -110,6 +109,7 @@ SECTIONS
|
|||
*(.rodata.4)
|
||||
|
||||
*(.rodata .rodata.*)
|
||||
*(.gint.rodata.sh3)
|
||||
} > rom
|
||||
|
||||
|
||||
|
@ -128,7 +128,7 @@ SECTIONS
|
|||
.bss (NOLOAD) : {
|
||||
_rbss = . ;
|
||||
|
||||
*(.bss COMMON)
|
||||
*(.bss .bss.* COMMON)
|
||||
*(B R)
|
||||
|
||||
. = ALIGN(16);
|
||||
|
@ -188,29 +188,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);
|
||||
|
||||
|
||||
|
||||
|
@ -237,9 +226,6 @@ SECTIONS
|
|||
*/
|
||||
|
||||
/DISCARD/ : {
|
||||
/* Debug sections (often from libgcc) */
|
||||
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
|
||||
.debug_ranges .debug_line .debug_str)
|
||||
/* Java class registration (why are they even here?!) */
|
||||
*(.jcr)
|
||||
/* Asynchronous unwind tables: no C++ exception handling */
|
|
@ -10,19 +10,24 @@ OUTPUT_FORMAT(elf32-sh)
|
|||
/* Located in core/start.c */
|
||||
ENTRY(_start)
|
||||
|
||||
#ifdef FXCG50_FASTLOAD
|
||||
# define _ROM_REGION 0x8c200000
|
||||
#else
|
||||
# define _ROM_REGION 0x00300000
|
||||
#endif
|
||||
|
||||
MEMORY
|
||||
{
|
||||
/* Userspace mapping of the add-in (without G3A header) */
|
||||
rom (rx): o = 0x00300000, l = 2M
|
||||
rom (rx): o = _ROM_REGION, l = 2M
|
||||
/* 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
|
||||
|
@ -32,7 +37,7 @@ SECTIONS
|
|||
*/
|
||||
|
||||
/* First address to be mapped to ROM */
|
||||
_brom = 0x00300000;
|
||||
_brom = ORIGIN(rom);
|
||||
/* Size of ROM mappings */
|
||||
_srom = SIZEOF(.text) + SIZEOF(.rodata)
|
||||
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
|
||||
|
@ -102,8 +107,7 @@ SECTIONS
|
|||
.bss (NOLOAD) : {
|
||||
_rbss = . ;
|
||||
|
||||
*(.bss.vram)
|
||||
*(.bss COMMON)
|
||||
*(.bss .bss.* COMMON)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > ram :NONE
|
||||
|
@ -144,29 +148,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 */
|
||||
|
@ -188,10 +181,7 @@ SECTIONS
|
|||
|
||||
/DISCARD/ : {
|
||||
/* SH3-only data 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)
|
||||
*(.gint.rodata.sh3 .gint.data.sh3 .gint.bss.sh3)
|
||||
/* Java class registration (why are they even here?!) */
|
||||
*(.jcr)
|
||||
/* Asynchronous unwind tables: no C++ exception handling */
|
|
@ -73,6 +73,9 @@ extern "C" {
|
|||
/* Remove a file or folder (also works if the entry does not exist). */
|
||||
int BFile_Remove(uint16_t const *path);
|
||||
|
||||
/* Rename a file (can move folders; Fugue only). */
|
||||
int BFile_Rename(uint16_t const *oldpath, uint16_t const *newpath);
|
||||
|
||||
#define BFile_File 1
|
||||
#define BFile_Folder 5
|
||||
|
||||
|
|
|
@ -52,11 +52,108 @@ 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, though many more are involved.
|
||||
|
||||
On SH3 fx-9860G-like:
|
||||
F1: CPU @ 29 MHz [Default speed]
|
||||
F2: CPU @ 58 MHz [Similar to G-III default]
|
||||
F3: CPU @ 88 MHz
|
||||
F4: CPU @ 118 MHz [Fastest CPU option]
|
||||
F5: CPU @ 118 MHz [Reduced memory wait times]
|
||||
On SH4 fx-9860G-like:
|
||||
F1: CPU @ 29 MHz, BFC @ 29 MHz [Default speed]
|
||||
F2: CPU @ 58 MHz, BFC @ 29 MHz [Similar to G-III default]
|
||||
F3: CPU @ 29 MHz, BFC @ 29 MHz [SH3 default]
|
||||
F4: CPU @ 118 MHz, BFC @ 59 MHz
|
||||
F5: CPU @ 236 MHz, BFC @ 118 MHz [Fastest option]
|
||||
On G-III / Graph 35+E II:
|
||||
F1: CPU @ 58 MHz, BFC @ 29 MHz [Default speed]
|
||||
F2: CPU @ 58 MHz, BFC @ 29 MHz [fx-CG 10/20 default]
|
||||
F3: CPU @ 29 MHz, BFC @ 29 MHz [SH3 default]
|
||||
F4: CPU @ 118 MHz, BFC @ 58 MHz
|
||||
F5: CPU @ 235 MHz, BFC @ 58 MHz [Fastest option]
|
||||
On fx-CG 10/20:
|
||||
F1: CPU @ 58 MHz, BFC @ 29 MHz [Default speed]
|
||||
F2: CPU @ 58 MHz, BFC @ 29 MHz [Improved memory speed]
|
||||
F3: CPU @ 118 MHz, BFC @ 58 MHz [Faster than F2]
|
||||
F4: CPU @ 118 MHz, BFC @ 118 MHz [Fastest bus option]
|
||||
F5: CPU @ 191 MHz, BFC @ 94 MHz [Fastest CPU option]
|
||||
On fx-CG 50:
|
||||
F1: CPU @ 116 MHz, BFC @ 58 MHz [Default speed]
|
||||
F2: CPU @ 58 MHz, BFC @ 29 MHz [fx-CG 10/20 default]
|
||||
F3: CPU @ 94 MHz, BFC @ 47 MHz [Clearly slow: F2 < F3 < F1]
|
||||
F4: CPU @ 232 MHz, BFC @ 58 MHz [Fastest CPU option]
|
||||
F5: CPU @ 189 MHz, BFC @ 94 MHz [Fastest bus option] */
|
||||
|
||||
CLOCK_SPEED_F1 = 1,
|
||||
CLOCK_SPEED_F2 = 2,
|
||||
CLOCK_SPEED_F3 = 3,
|
||||
CLOCK_SPEED_F4 = 4,
|
||||
CLOCK_SPEED_F5 = 5,
|
||||
|
||||
/* The default clock speed is always Ftune's F1 */
|
||||
CLOCK_SPEED_DEFAULT = CLOCK_SPEED_F1,
|
||||
};
|
||||
|
||||
/* clock_get_speed(): Determine the current clock speed
|
||||
|
||||
This function compares the current hardware state with the settings for each
|
||||
speed level and returns the current one. If the hardware state does not
|
||||
correspond to any of Ftune's settings, CLOCK_SPEED_UNKNOWN is returned. */
|
||||
int clock_get_speed(void);
|
||||
|
||||
/* clock_set_speed(): Set the current clock speed
|
||||
|
||||
This function sets the clock speed to the desired level. This is "the
|
||||
overclock function", although depending on the model or settings it is also
|
||||
the downclocking function.
|
||||
|
||||
The process of changing clock speeds is non-trivial, requires waiting for
|
||||
the DMA to finish its work and slightly affects running timers. You should
|
||||
avoid changing the clock speed constantly if not necessary. If this function
|
||||
detects that the desired clock speed is already in use, it returns without
|
||||
performing any change.
|
||||
|
||||
Currently the clock speed is not reset during a world switch nor when
|
||||
leaving the add-in. */
|
||||
void clock_set_speed(int speed);
|
||||
|
||||
/* If you want to faithfully save and restore the clock state while properly
|
||||
handling clock speeds that are not Ftune/PTune's defaults, you can get a
|
||||
full copy of the settings.
|
||||
|
||||
WARNING: Applying random settings with cpg_set_overclock_setting() might
|
||||
damage your calculator! */
|
||||
|
||||
struct cpg_overclock_setting
|
||||
{
|
||||
uint32_t FLLFRQ, FRQCR;
|
||||
uint32_t CS0BCR, CS2BCR, CS3BCR, CS5aBCR;
|
||||
uint32_t CS0WCR, CS2WCR, CS3WCR, CS5aWCR;
|
||||
};
|
||||
|
||||
/* Queries the clock setting from the hardware. */
|
||||
void cpg_get_overclock_setting(struct cpg_overclock_setting *s);
|
||||
|
||||
/* Applies the specified overclock setting. */
|
||||
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s);
|
||||
|
||||
//---
|
||||
// Sleep functions
|
||||
|
|
|
@ -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
|
||||
|
@ -30,4 +38,7 @@
|
|||
enabled or disabled at runtime. */
|
||||
#cmakedefine GINT_KMALLOC_DEBUG
|
||||
|
||||
/* GINT_USB_DEBUG: Selects whether USB debug functions are enabled */
|
||||
#cmakedefine GINT_USB_DEBUG
|
||||
|
||||
#endif /* GINT_CONFIG */
|
||||
|
|
|
@ -10,12 +10,13 @@
|
|||
/* Objects from the gint's uninitialized BSS section */
|
||||
#define GBSS __attribute__((section(".gint.bss")))
|
||||
/* Additional sections that are only needed on SH3 */
|
||||
#define GRODATA3 __attribute__((section(".gint.rodata.sh3")))
|
||||
#define GDATA3 __attribute__((section(".gint.data.sh3")))
|
||||
#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))
|
||||
|
|
|
@ -153,7 +153,8 @@ static GINLINE int gint_call(gint_call_t cb)
|
|||
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);
|
||||
gint_call_arg_t *args = cb.args;
|
||||
return f ? f(args[0].i, args[1].i, args[2].i, args[3].i) : -1;
|
||||
}
|
||||
|
||||
//---
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
//---
|
||||
// gint:defs:timeout - RTC-based timeouts
|
||||
//
|
||||
// This header provides an interface for simplistic timers used for timeout
|
||||
// waiting. Currently they are based on the RTC with a resolution of 1/128 s.
|
||||
//---
|
||||
|
||||
#ifndef GINT_DEFS_TIMEOUT
|
||||
#define GINT_DEFS_TIMEOUT
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Object holding information about a timeout (specifically, when it expires).
|
||||
TODO: timeout: consider using a struct timespec with clock_gettime()? */
|
||||
typedef clock_t timeout_t;
|
||||
|
||||
static inline timeout_t timeout_make_ms(int ms)
|
||||
{
|
||||
return clock() + (int64_t)ms * CLOCKS_PER_SEC / 1000;
|
||||
}
|
||||
|
||||
static inline bool timeout_elapsed(timeout_t const *t)
|
||||
{
|
||||
return t && clock() >= *t;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DEFS_TIMEOUT */
|
|
@ -15,6 +15,8 @@
|
|||
#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;
|
||||
|
|
|
@ -14,6 +14,11 @@
|
|||
#define GAUTOTYPE __auto_type
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <algorithm>
|
||||
using std::min;
|
||||
using std::max;
|
||||
#else
|
||||
/* min(), max() (without double evaluation) */
|
||||
#define min(a, b) ({ \
|
||||
GAUTOTYPE _a = (a); \
|
||||
|
@ -25,6 +30,7 @@
|
|||
GAUTOTYPE _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
#endif
|
||||
|
||||
/* sgn() (without double evaluation) */
|
||||
#define sgn(s) ({ \
|
||||
|
|
|
@ -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
|
||||
|
@ -18,6 +22,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/image.h>
|
||||
|
||||
/* Dimensions of the VRAM */
|
||||
#define DWIDTH 396
|
||||
|
@ -57,79 +62,35 @@ 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). In
|
||||
P8_RGB565A, the value assigned to alpha is always 0. */
|
||||
uint16_t alpha;
|
||||
|
||||
/* Full width and height, in pixels */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
|
||||
/* Here we lose structure because of the flexible array.
|
||||
|
||||
RGB565, RGB565A:
|
||||
* Pixels in row-major order, 16 bits per pixel
|
||||
P8:
|
||||
* Palette with 256 entries (512 bytes total)
|
||||
* Pixels in row-major order, 8 bits per pixel
|
||||
P8_RGB565A, P8_RGB565:
|
||||
* Number of entries in palette, N (2 bytes)
|
||||
* Palette with N entries (2N bytes)
|
||||
* Pixels in row-major order, 8 bits per pixel (signed indices in
|
||||
an uint16_t array starting at <palette>+<256 bytes>)
|
||||
P4/P4_RGB565A, P4_RGB565:
|
||||
* Palette with 16 entries (32 bytes total)
|
||||
* Pixels in row-major order, 4 bits per pixel, each row
|
||||
byte-padded */
|
||||
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
|
||||
//---
|
||||
|
||||
/* dsetvram() - Control video RAM address and triple buffering
|
||||
/* dsetvram(): Control video RAM address and triple buffering
|
||||
|
||||
Normal rendering under gint uses double-buffering: there is one image
|
||||
displayed on the screen and one in memory, in a region called the video RAM
|
||||
(VRAM). The application draws frames in the VRAM then sends them to the
|
||||
screen only when they are finished, using dupdate().
|
||||
|
||||
On fxcg50, the performance bottleneck is almost always the graphical
|
||||
rendering (especially in games) because the high amount of data, 173 kB per
|
||||
frame in full-resolution, makes graphics manipulation computationally
|
||||
expensive. The transfer also takes about 10 ms in itself.
|
||||
On fx-CG, sending frames with dupdate() is a common bottleneck because it
|
||||
takes about 11 ms. Fortunately, while the DMA is sending the frame to the
|
||||
display, the CPU is available to do work in parallel. This function sets up
|
||||
triple buffering (ie. a second VRAM) so that the CPU can start working on
|
||||
the next frame while the DMA is sending the current one.
|
||||
|
||||
Since gint transfers data to the screen using the DMA, it is possible to run
|
||||
the application while the finished frame is being transferred. However,
|
||||
writing to the VRAM during this period will cause display artifacts since
|
||||
the VRAM it is still being read by the DMA.
|
||||
However, experience shows minimal performance improvements, because writing
|
||||
to main RAM does not parallelize with DMA transfers. Since gint 2.8, this
|
||||
is no longer the default, and the memory for the extra VRAM is instead
|
||||
available via malloc().
|
||||
|
||||
The solution to this is to use triple-buffering with the display and two
|
||||
VRAMs that are alternately being written to while the other is being
|
||||
transferred. The VRAM switching is handled by dupdate() and is activated
|
||||
whenever two VRAMs are configured.
|
||||
|
||||
By default gint uses triple buffering with two VRAMs in the system stack.
|
||||
|
||||
VRAMs must be contiguous, 32-aligned, (2*396*224)-byte buffers.
|
||||
VRAMs must be contiguous, 32-aligned, (2*396*224)-byte buffers with 32 bytes
|
||||
of extra data on each side (ie. 32 bytes into a 32-aligned buffer of size
|
||||
177472).
|
||||
|
||||
@main Main VRAM area, used alone if [secondary] is NULL
|
||||
@secondary Additional VRAM area, enables triple buffering if non-NULL */
|
||||
|
|
|
@ -27,8 +27,6 @@ extern "C" {
|
|||
#include <gint/display-cg.h>
|
||||
#endif
|
||||
|
||||
/* TODO: dinfo() or similar */
|
||||
|
||||
//---
|
||||
// Video RAM management
|
||||
//---
|
||||
|
@ -82,6 +80,31 @@ 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);
|
||||
|
||||
//---
|
||||
// Rendering mode control
|
||||
//---
|
||||
|
||||
/* dmode: Rendering mode settings
|
||||
|
||||
This structure indicates the current window settings. Rendering is limited
|
||||
to the rectangle spanning from (left,top) included, to (right,bottom)
|
||||
excluded. The default is from (0,0) to (DWIDTH,DHEIGHT). */
|
||||
struct dwindow {
|
||||
int left, top;
|
||||
int right, bottom;
|
||||
};
|
||||
extern struct dwindow dwindow;
|
||||
|
||||
/* dwindow_set(): Set the rendering window
|
||||
|
||||
This function changes [dwindow] settings to limit rendering to a sub-surface
|
||||
of the VRAM. Note that this doesn't change the coordinate system; the pixel
|
||||
at (DWIDTH/2, DHEIGHT/2) is always in the middle of the screen regardless of
|
||||
the window setting. However, it might be masked out by the window.
|
||||
|
||||
Returns the old dwindow settings (if it needs to be restored later). */
|
||||
struct dwindow dwindow_set(struct dwindow new_mode);
|
||||
|
||||
//---
|
||||
// Area rendering functions
|
||||
//---
|
||||
|
@ -151,6 +174,11 @@ void drect_border(int x1, int y1, int x2, int y2,
|
|||
*: When the gray engine is on, see dgray(). */
|
||||
void dpixel(int x, int y, int color);
|
||||
|
||||
/* dgetpixel(): Get a pixel's color
|
||||
Returns the current color of any pixel in the VRAM. This function ignores
|
||||
the rendering window. Returns -1 if (x,y) is out of bounds. */
|
||||
int dgetpixel(int x, int y);
|
||||
|
||||
/* dline(): Render a straight line
|
||||
|
||||
This function draws a line using a Bresenham-style algorithm. Please note
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
//---
|
||||
// gint:usb:asyncio - Asynchronous I/O common definitions
|
||||
//---
|
||||
|
||||
#ifndef GINT_USB_ASYNCIO
|
||||
#define GINT_USB_ASYNCIO
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
|
||||
/* Data tracking the progress of a multi-part multi-round async I/O operation.
|
||||
|
||||
* Writes are multi-part because they are constructed over several calls to
|
||||
write(2) followed by a "commit" with fynsc(2). They are additionally
|
||||
multi-round because each call to write(2) requires mutiple rounds of
|
||||
hardware communication when the hardware buffer is smaller than the data.
|
||||
|
||||
* Reads are multi-part because each transaction from the host requires
|
||||
several calls to read(2) ("user segments") if the user's buffer is shorter
|
||||
than the full transaction. In addition, reads are multi-round because a
|
||||
single read(2) to a user buffer takes multiple rounds of hardware
|
||||
communication ("hardware segments") whenever the user buffer is larger
|
||||
than the hardware buffer.
|
||||
|
||||
The process of performing such an I/O operation, as tracked by this
|
||||
structure and use throughout gint, is as follows.
|
||||
|
||||
## Writes
|
||||
|
||||
WRITING ---------------------.
|
||||
^ | | HW buffer
|
||||
Start writing | | Not full | full: start
|
||||
| | | transmission
|
||||
write(2) | v v
|
||||
--> IDLE ------------------> PENDING <------------- FLYING-WRITE
|
||||
^ ^ | DONE interrupt
|
||||
| DONE write(2) | |
|
||||
| interrupt | |
|
||||
| | | Data exhausted
|
||||
| fsync(2): start | v
|
||||
FLYING-SYNC <------------ IN-PROGRESS
|
||||
transmission
|
||||
|
||||
Initially the operation is in the IDLE state. When a write(2) is issued, it
|
||||
interacts with hardware then transitions to the IN-PROGRESS state, where it
|
||||
remains for any subsequent write(2). A fsync(2) will properly commit data to
|
||||
the hardware, finish the operation and return to the IDLE state.
|
||||
|
||||
The FLYING-WRITE and FLYING-SYNC states refer to waiting periods, after
|
||||
issuing hardware commands, during which hardware communicates. Usually an
|
||||
interrupt signals when hardware is ready to resume work.
|
||||
|
||||
Note that in a series of write(2), hardware is only instructed to send data
|
||||
once the hardware buffer is full. Therefore, a write(2) might transition
|
||||
directly from IDLE or IN-PROGRESS, to PENDING, to IN-PROGRESS, without
|
||||
actually communicating with the outside world.
|
||||
|
||||
An asynchronous write(2) might return to the caller as soon as writing is
|
||||
finished even if the operation is left in the FLYING-WRITE state, and it may
|
||||
even return while the operation is in the WRITING state if the DMA is used.
|
||||
|
||||
The invariants and meaning for each state are as follow:
|
||||
|
||||
State(s) Characterization Description
|
||||
============================================================================
|
||||
IDLE type == ASYNCIO_NONE No I/O operation
|
||||
----------------------------------------------------------------------------
|
||||
PENDING data_w && round_size == 0 Ready to write pending data
|
||||
----------------------------------------------------------------------------
|
||||
WRITING, round_size > 0 CPU/DMA write to HW in progress
|
||||
FLYING-WRITE HW transmission in progress
|
||||
----------------------------------------------------------------------------
|
||||
IN-PROGRESS !data_w && type == WRITE Waiting for write(2) or fsync(2)
|
||||
----------------------------------------------------------------------------
|
||||
FLYING-SYNC type == ASYNCIO_SYNC HW commit in progress
|
||||
============================================================================
|
||||
|
||||
The series of asyncio_op function calls for a write is as follows:
|
||||
|
||||
transaction ::= write* fsync
|
||||
write ::= asyncio_op_start_write round+ asyncio_op_finish_write
|
||||
round ::= asyncio_op_start_write_round asyncio_op_finish_write_round
|
||||
fsync ::= asyncio_op_start_sync asyncio_op_finish_sync
|
||||
|
||||
Each write(2) (with a single user-provided buffer) is split into multiple
|
||||
rounds that each fill the (small) hardware buffer. More writes can follow
|
||||
until an fynsc(2) commits the pipe.
|
||||
|
||||
## Reads
|
||||
|
||||
IN interrupt
|
||||
--> IDLE-EMPTY --------------> IDLE-READY
|
||||
| \ read(2) | ^
|
||||
read(2) | \ Transaction | | User buffer
|
||||
| \ exhausted* | | filled
|
||||
| '----<----------. | |
|
||||
| \ | |
|
||||
| IN interrupt \ | |
|
||||
v .--------->--------. \ v | .---. Read from
|
||||
WAITING READING v hardware
|
||||
'---------<--------' '---'
|
||||
HW buffer exhausted with
|
||||
user buffer not full
|
||||
|
||||
On this diagram, the right side indicates the presence of data to read from
|
||||
hardware while the bottom side indicates a read(2) request by the user.
|
||||
Notice the diagonal arrow back to IDLE-EMPTY insteaf of WAITING, which
|
||||
highlights that read(2) will always return at the end of a transaction even
|
||||
if the user-provided buffer is not full (to avoid waiting).
|
||||
|
||||
A read(2) request (a "user segment") might consume several full hardware
|
||||
buffers ("hardware segments") if the user buffer is large, thus looping
|
||||
repeatedly between WAITING and READING. Conversely, each hardware segment
|
||||
might fulfill many read(2) requests if the user buffer is small, thus
|
||||
looping between IDLE-READY and READING.
|
||||
|
||||
* Note that if the transaction finishes right as the user buffer fills up,
|
||||
we return to IDLE-READY and the next call to read(2) will successfully read
|
||||
0 bytes and transition back to IDLE-EMPTY. This allows the user to read the
|
||||
entire transaction by reading data until they get fewer bytes than requested
|
||||
without running the risk of blocking on the next transaction.
|
||||
|
||||
The invariants and meaning for each state are as follow. The right side
|
||||
(presence of a hardware segment) is indicated by `buffer_used >= 0`, while
|
||||
the bottom side (presence of a read(2) request) is indicated by `size > 0`.
|
||||
As an exception, IDLE-EMPTY uses `buffer_used == 0` so that the default
|
||||
zero-initialization of transfer data is sufficient.
|
||||
|
||||
State Invariant characterization Description
|
||||
============================================================================
|
||||
IDLE-EMPTY type == ASYNCIO_NONE No I/O operation, the pipe is
|
||||
&& buffer_used == 0 idle with no request and no
|
||||
&& size == 0 hardware segment (but we might
|
||||
&& round_size == 0 still be mid-transaction)
|
||||
----------------------------------------------------------------------------
|
||||
IDLE-READY type == ASYNCIO_READ There is a hardware segment not
|
||||
&& buffer_used >= 0 marked as complete, but no
|
||||
&& size == 0 read(2) request to consume it
|
||||
&& round_size == 0
|
||||
----------------------------------------------------------------------------
|
||||
WAITING type == ASYNCIO_READ There is a read(2) request but
|
||||
&& buffer_used < 0 no hardware segment, and either
|
||||
&& size > 0 the request is new or the
|
||||
&& round_size == 0 transaction isn't exhausted
|
||||
----------------------------------------------------------------------------
|
||||
READING type == ASYNCIO_READ A read round in progress and
|
||||
&& buffer_used >= 0 either the read(2) request or
|
||||
&& size > 0 hardware segment will be
|
||||
&& round_size > 0 exhausted when it ends
|
||||
============================================================================
|
||||
|
||||
The series of asyncio_op function calls for a read is a bit more complicated
|
||||
because transactions are divided into two non-comparable sequences of
|
||||
segments: one for packets received by the hardware buffer (on BRDY), one for
|
||||
the data being copied to user buffers (on read(2)).
|
||||
|
||||
|<------ Transaction from the host (no size limit) ------>|
|
||||
|
||||
v BRDY v BRDY v BRDY v BRDY v BRDY
|
||||
+-----------+-----------+-----------+-----------+---------+
|
||||
| HW buffer | HW buffer | HW buffer | HW buffer | (short) | HW segments
|
||||
+-----------+------+----+-----------+-----------+---------+
|
||||
| R1 | R2 | R3 | R4 | R5 | R6 | Read rounds
|
||||
+-----------+------+----+-----------+-----------+---------+
|
||||
| User buffer #1 | User buffer #2 | (short) | User segments
|
||||
+------------------+----------------------------+---------+
|
||||
^ read(2) ^ read(2) ^ read(2)
|
||||
|
||||
Reads rounds are exactly the intersections between hardware segments and
|
||||
read(2) user segments.
|
||||
|
||||
States can be checked and transitioned with the API functions below. */
|
||||
|
||||
enum { ASYNCIO_NONE, ASYNCIO_READ, ASYNCIO_WRITE, ASYNCIO_SYNC };
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* Type of I/O operation (NONE/WRITE/SYNC/READ) */
|
||||
uint8_t type;
|
||||
/* Whether the DMA should be used for hardware access */
|
||||
bool dma :1;
|
||||
/* For reading pipes, whether the transaction is expected to continue with
|
||||
another hardware segment after the current one */
|
||||
bool cont_r :1;
|
||||
/* For reading pipes, interrupt flag signaling an incoming hardware segment
|
||||
not yet added to the operation */
|
||||
bool interrupt_r :1;
|
||||
/* For reading pipes, whether the current read call should close the
|
||||
current hardware segment if all the data is read even if the read call
|
||||
is not partial */
|
||||
bool autoclose_r :1;
|
||||
/* Hardware resource being used for access (meaning depends on hardware).
|
||||
Usually, this is assigned for the duration of hardware transaction.
|
||||
This value is user-managed and not modified by asyncio_op functions. */
|
||||
uint8_t controller;
|
||||
|
||||
/* Number of bytes in short buffer (0..3) */
|
||||
uint8_t shbuf_size;
|
||||
/* Short buffer */
|
||||
uint32_t shbuf;
|
||||
|
||||
/* Size of data currently in the hardware buffer */
|
||||
int16_t buffer_used;
|
||||
/* Size of data being read/written in the current round (which may itself
|
||||
be asynchronous if it's using the DMA) */
|
||||
uint16_t round_size;
|
||||
|
||||
union {
|
||||
/* Address of data to transfer, incremented gradually [write] */
|
||||
void const *data_w;
|
||||
/* Address of buffer to store data to, incremented gradually [read] */
|
||||
void *data_r;
|
||||
};
|
||||
/* Size of data left to transfer to satisfy the complete request */
|
||||
int size;
|
||||
/* For reading operations, pointer to total amount of transferred data */
|
||||
int *realized_size_r;
|
||||
/* Callback at the end of the current write, final commit, or read */
|
||||
gint_call_t callback;
|
||||
|
||||
} asyncio_op_t;
|
||||
|
||||
//---
|
||||
// Initialization and query functions
|
||||
//---
|
||||
|
||||
/* asyncio_op_clear(): Initialize/clear the storage for an I/O operation */
|
||||
void asyncio_op_clear(asyncio_op_t *op);
|
||||
|
||||
/* asyncio_op_busy(): Check whether the transfer is busy for syscalls
|
||||
|
||||
This function checks whether the transfer is in a state where the CPU is
|
||||
busy wrt. starting a new syscall, ie. read(2), write(2) or fsync(2). Returns
|
||||
true if the CPU is busy and the call has to wait, false if the call can
|
||||
proceed immediately. */
|
||||
bool asyncio_op_busy(asyncio_op_t const *op);
|
||||
|
||||
//---
|
||||
// I/O functions
|
||||
//---
|
||||
|
||||
/* Start/finish a write(2) call. */
|
||||
void asyncio_op_start_write(asyncio_op_t *op,
|
||||
void const *data, size_t size, bool use_dma, gint_call_t const *callback);
|
||||
void asyncio_op_finish_write(asyncio_op_t *op);
|
||||
|
||||
/* Start/finish a single-block write to hardware. */
|
||||
void asyncio_op_start_write_round(asyncio_op_t *op, size_t size);
|
||||
void asyncio_op_finish_write_round(asyncio_op_t *op);
|
||||
|
||||
/* Start an fsync(2) operation (after one or more writes) and finish it. */
|
||||
void asyncio_op_start_sync(asyncio_op_t *op, gint_call_t const *callback);
|
||||
void asyncio_op_finish_sync(asyncio_op_t *op);
|
||||
|
||||
/* Start a read(2) call. The call will finish automatically when the final
|
||||
round finishes. If `autoclose` is set, the current hardware segment will
|
||||
be marked as completed if the round reads it entirely, even if the request
|
||||
is fulfilled. */
|
||||
void asyncio_op_start_read(asyncio_op_t *op, void *data, size_t size,
|
||||
bool use_dma, int *realized_size, bool autoclose,
|
||||
gint_call_t const *callback);
|
||||
|
||||
/* Start a hardware segment. `cont` should be true if there will be another
|
||||
segment in the same transaction. The segment will finish automatically when
|
||||
it is completely consumed by a read round. */
|
||||
void asyncio_op_start_read_hwseg(asyncio_op_t *op, size_t size, bool cont);
|
||||
|
||||
bool asyncio_op_has_read_call(asyncio_op_t const *op);
|
||||
|
||||
bool asyncio_op_has_read_hwseg(asyncio_op_t const *op);
|
||||
|
||||
/* Start a single-block read from hardware. The requested size is automatically
|
||||
t->size, however the round may of course be smaller depending on how much
|
||||
data is available. Returns the round size. */
|
||||
int asyncio_op_start_read_round(asyncio_op_t *op);
|
||||
|
||||
enum {
|
||||
ASYNCIO_HWSEG_EXHAUSTED = 0x01,
|
||||
ASYNCIO_REQUEST_FINISHED = 0x02,
|
||||
ASYNCIO_TRANSACTION_EXHAUSTED = 0x04,
|
||||
};
|
||||
|
||||
/* Finish a single-block read from hardware. This function also finishes the
|
||||
current hardware segment and read call if appropriate, *except* that it
|
||||
doesn't invoke the read(2) callback. You should make a copy of it before
|
||||
calling and invoke it manually after. Returns a combination of the above
|
||||
flags indicating what finished along with the round. */
|
||||
int asyncio_op_finish_read_round(asyncio_op_t *op);
|
||||
|
||||
/* Cancel a read call. This keeps the hardware segment part intact. */
|
||||
void asyncio_op_cancel_read(asyncio_op_t *op);
|
||||
|
||||
#endif /* GINT_USB_ASYNCIO */
|
|
@ -10,23 +10,11 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <gint/keyboard.h>
|
||||
#include <stdbool.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
|
||||
|
@ -84,14 +72,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
|
||||
|
@ -101,6 +101,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
|
||||
|
@ -122,10 +125,13 @@ 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;
|
||||
|
||||
/* keydev_async_filter_t: Low-level asynchronous event filter. */
|
||||
typedef bool (*keydev_async_filter_t)(key_event_t event);
|
||||
|
||||
/* keydev_t: Keyboard device
|
||||
|
||||
This structure represents the state and settings of a keyboard device that
|
||||
|
@ -133,9 +139,9 @@ typedef struct {
|
|||
is useful for demo/replays that input events without the physical keyboard,
|
||||
and a couple of corner uses like control over USB.
|
||||
|
||||
The keyboard device has built-in event transformations, which modifty the
|
||||
The keyboard device has built-in event transformations, which modify the
|
||||
stream of events by adding information, combining modifiers, and removing
|
||||
undesired events. Because the event transformation reky on the current state
|
||||
undesired events. Because the event transformation rely on the current state
|
||||
of the keyboard, they must be run by the driver whenever events are read, so
|
||||
they are tied to the device.
|
||||
|
||||
|
@ -143,35 +149,27 @@ 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;
|
||||
|
||||
/* Asynchronous event filter. This is a low-level filter which is
|
||||
called when events are generated to process and filter them. It
|
||||
provides the unique ability to run keyboard-triggered code even if
|
||||
the program's main thread is busy, for instance running some sort of
|
||||
infinite loop. */
|
||||
keydev_async_filter_t async_filter;
|
||||
|
||||
// <Delayed Modifiers>
|
||||
|
||||
/* delayed_* is set when the delayed modifier is active (after a press/
|
||||
|
@ -186,13 +184,29 @@ typedef struct {
|
|||
|
||||
/* Candidate key for repeats (or 0 if no key is candidate yet) */
|
||||
int rep_key;
|
||||
/* Number of repeats alreay sent */
|
||||
/* Number of repeats already sent */
|
||||
int rep_count;
|
||||
/* Time since key was first pressed (us) */
|
||||
int rep_time;
|
||||
/* 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
|
||||
difference between this and the fresh information is queued and this
|
||||
is updated. state_now is identical to the real state obtained from
|
||||
the device unless earlier events failed to be queued, in which case
|
||||
a difference is maintained so they will be reconsidered later. */
|
||||
GALIGNED(4) uint8_t state_now[12];
|
||||
/* State of keys based on produced events. (state_queue + queue) is
|
||||
always identical to (state_now). When the queue is empty both states
|
||||
are the same. This is the user's view of the keyboard. */
|
||||
GALIGNED(4) uint8_t state_queue[12];
|
||||
/* Event queue (circular buffer) */
|
||||
key_event_t queue[KEYBOARD_QUEUE_SIZE];
|
||||
|
||||
/* Parameters for the standard repeat function */
|
||||
int rep_standard_first, rep_standard_next;
|
||||
|
||||
} keydev_t;
|
||||
|
||||
/* keydev_std(): Standard keyboard input device
|
||||
|
@ -218,6 +232,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_*()
|
||||
|
@ -231,24 +251,22 @@ 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. */
|
||||
bool keydev_idle(keydev_t *d, ...);
|
||||
|
||||
/* keydev_async_filter(): Obtain current async filter */
|
||||
keydev_async_filter_t keydev_async_filter(keydev_t const *d);
|
||||
|
||||
/* keydev_set_async_filter(): Set low-level async filter */
|
||||
void keydev_set_async_filter(keydev_t *d, keydev_async_filter_t filter);
|
||||
|
||||
//---
|
||||
// High-level API to read from the device
|
||||
//---
|
||||
|
@ -262,8 +280,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_read(): Retrieve the next transformed event */
|
||||
key_event_t keydev_read(keydev_t *d);
|
||||
/* 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
|
||||
If there is no event, returns an event with type KEYEV_NONE, unless
|
||||
[wait=true], in which case waits for an event to occur or *timeout to
|
||||
become non-zero (if timeout is not NULL), whichever comes first. */
|
||||
key_event_t keydev_read(keydev_t *d, bool wait, volatile int *timeout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -35,16 +35,33 @@ 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
|
||||
liens [start] to [start+height-1] (both included) of the current window.
|
||||
This is the initial step of r61524_display(), which is normally followed by
|
||||
writing all the data to 0xb4000000.
|
||||
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 start, int height);
|
||||
void r61524_start_frame(int xmin, int xmax, int ymin, int ymax);
|
||||
|
||||
/* r162524_win_get() and r61524_win_set(): Manipulate the display window
|
||||
|
||||
|
|
|
@ -13,11 +13,14 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/config.h>
|
||||
#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) */
|
||||
|
@ -86,13 +89,23 @@ typedef struct {
|
|||
typedef struct {
|
||||
/* Control and power-up. We don't save power-related registers from other
|
||||
modules nor UPONCR, because they must be changed to use the module */
|
||||
uint16_t SYSCFG, DVSTCTR, TESTMODE, REG_C2;
|
||||
/* FIFO configuration */
|
||||
uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL;
|
||||
uint16_t SYSCFG, BUSWAIT, DVSTCTR, SOFCFG, TESTMODE, REG_C2;
|
||||
/* Interrupt configuration */
|
||||
uint16_t INTENB0, BRDYENB, NRDYENB, BEMPENB, SOFCFG;
|
||||
/* Default Control Pipe (maybe not needed) */
|
||||
uint16_t DCPCFG, DCPMAXP, DCPCTR;
|
||||
uint16_t INTENB0, INTENB1, BRDYENB, NRDYENB, BEMPENB;
|
||||
/* Default Control Pipe */
|
||||
uint16_t DCPMAXP;
|
||||
|
||||
#ifdef GINT_USB_DEBUG
|
||||
/* Registers tracked read-only for state analysis and debugging */
|
||||
uint16_t SYSSTS, FRMNUM, UFRMNUM;
|
||||
uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL, CFIFOCTR, D0FIFOCTR, D1FIFOCTR;
|
||||
uint16_t INTSTS0, INTSTS1, BRDYSTS, NRDYSTS, BEMPSTS;
|
||||
uint16_t DCPCFG, DCPCTR;
|
||||
uint16_t USBADDR, USBREQ, USBVAL, USBINDX, USBLENG;
|
||||
uint16_t PIPESEL, PIPECFG[9], PIPEnCTR[9], PIPEBUF[9];
|
||||
/* Ignored: UPONCR, PIPEnMAXP, PIPEnPERI, PIPEnTRN, PIPEnTRE, DEVADDn */
|
||||
#endif
|
||||
|
||||
} usb_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -13,24 +13,17 @@ extern "C" {
|
|||
#include <stddef.h>
|
||||
|
||||
/* Maximum number of file descriptors */
|
||||
#define FS_FD_MAX 32
|
||||
#define FS_FD_MAX 16
|
||||
|
||||
/* fs_descriptor_type_t: Overloaded file descriptor functions
|
||||
|
||||
This structure provides the base functions for every type of file
|
||||
descriptor. The prototypes are standard, except that the first argument
|
||||
(int fd) is replaced by (void *data) which points to the custom data
|
||||
allocator for the descriptor.
|
||||
|
||||
pread() is normally implemented in terms of lseek() and read(), however on
|
||||
Fugue file descriptors on the G-III series there is no way to determine the
|
||||
current position so this method won't work, and pread() is instead provided
|
||||
by a non-standard parameter of BFile_Read(). The overloaded entry supports
|
||||
this alternative. */
|
||||
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 (*pread)(void *data, void *buf, size_t size, off_t offset);
|
||||
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);
|
||||
|
|
|
@ -41,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().
|
||||
|
@ -52,14 +59,23 @@ void gint_switch(void (*function)(void));
|
|||
call to getkey(), but can also be called manually. */
|
||||
void gint_osmenu(void);
|
||||
|
||||
/* gint_osmenu_native(): Like gint_osmenu() without the world switch
|
||||
This is a replacement for gint_osmenu() which can be used when the current
|
||||
kernel is already the native OS kernel. */
|
||||
void gint_osmenu_native(void);
|
||||
|
||||
/* gint_setrestart(): Set whether to restart the add-in after exiting
|
||||
|
||||
An add-in that reaches the end of its code exits. On the calculator, except
|
||||
using OS-dependent settings, it cannot be started again unless another
|
||||
application is launched first.
|
||||
An add-in that returns from its main() function automatically exits to the
|
||||
OS' main menu. However, when this happens the OS does not allow the add-in
|
||||
to be restarted unless another add-in is launched first. (This is because
|
||||
the OS tries to *resume* the current add-in, which then proceeds to exit
|
||||
again immediately.)
|
||||
|
||||
This setting allows the add-in to restart by calling gint_osmenu() instead
|
||||
of exiting. This can give a proper illusion of restarting if used correctly.
|
||||
This function enables a gint trick where after main() returns the add-in
|
||||
will invoke the main menu with gint_osmenu() rather than exiting. If the
|
||||
add-in is selected again, gint will jump back to the entry point, creating
|
||||
the illusion that the add-in exited and was then restarted.
|
||||
|
||||
@restart 0 to exit, 1 to restart by using gint_osmenu() */
|
||||
void gint_setrestart(int restart);
|
||||
|
@ -93,6 +109,26 @@ 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);
|
||||
|
||||
/* gint_set_quit_handler(): Setup a call to be invoked when leaving the add-in
|
||||
|
||||
This function sets up the provided GINT_CALL() to be invoked when the
|
||||
add-in is unloaded, which is either when we exit from main() or when
|
||||
starting another application from the main menu. Crucially, this is only
|
||||
*after* selecting an application, not before opening the main menu. The
|
||||
quit handler is not invoked if the user re-enters the add-in.
|
||||
|
||||
This is based on the SetQuitHandler() syscall, and therefore the callback
|
||||
runs in the OS world by default. If [run_in_os_world] is set to false, a
|
||||
world switch will be performed to run the callback in the gint world.
|
||||
|
||||
TODO: Currently the quit handler is not called when exiting from main().
|
||||
TODO: Detail how this interacts with destructor functions!
|
||||
TODO: [run_in_os_world == false] is not honored yet (because unstable)
|
||||
|
||||
@call Callback to be performed when leaving add-in
|
||||
@run_in_os_world true to stay in OS world, false to use gint world */
|
||||
void gint_set_quit_handler(gint_call_t gcall, bool run_in_os_world);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -45,6 +45,12 @@ void hw_detect(void);
|
|||
#define isSH4() (!isSH3())
|
||||
#endif
|
||||
|
||||
#if defined(FX9860G)
|
||||
#define isSlim() (gint[HWCALC] == HWCALC_FX9860G_SLIM)
|
||||
#else
|
||||
#define isSlim() 0
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
#define isSH3() 0
|
||||
#define isSH4() 1
|
||||
|
@ -105,6 +111,8 @@ void hw_detect(void);
|
|||
#define HWCALC_FXCG50 5
|
||||
/* fx-CG 50 emulator, hardcoded in kernel/inth.S */
|
||||
#define HWCALC_FXCG_MANAGER 6
|
||||
/* fx-9860G Slim, SH-3-based fx-9860G with hardware differences */
|
||||
#define HWCALC_FX9860G_SLIM 7
|
||||
|
||||
/*
|
||||
** Keyboard
|
||||
|
|
|
@ -0,0 +1,847 @@
|
|||
//---
|
||||
// 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>
|
||||
|
||||
struct dwindow;
|
||||
|
||||
//---
|
||||
// Image structures
|
||||
//---
|
||||
|
||||
/* Image formats. Note that transparency really only indicates the default
|
||||
rendering method, as a transparent background can always be added or removed
|
||||
by a dynamic effect on any image. */
|
||||
enum {
|
||||
IMAGE_RGB565 = 0, /* RGB565 without alpha */
|
||||
IMAGE_RGB565A = 1, /* RGB565 with one transparent color */
|
||||
IMAGE_P8_RGB565 = 4, /* 8-bit palette, all opaque colors */
|
||||
IMAGE_P8_RGB565A = 5, /* 8-bit with one transparent color */
|
||||
IMAGE_P4_RGB565 = 6, /* 4-bit palette, all opaque colors */
|
||||
IMAGE_P4_RGB565A = 3, /* 4-bit with one transparent color */
|
||||
|
||||
IMAGE_DEPRECATED_P8 = 2,
|
||||
};
|
||||
|
||||
/* Quick macros to compare formats by storage size */
|
||||
#define IMAGE_IS_RGB16(format) \
|
||||
((format) == IMAGE_RGB565 || (format) == IMAGE_RGB565A)
|
||||
#define IMAGE_IS_P8(format) \
|
||||
((format) == IMAGE_P8_RGB565 || (format) == IMAGE_P8_RGB565A)
|
||||
#define IMAGE_IS_P4(format) \
|
||||
((format) == IMAGE_P4_RGB565 || (format) == IMAGE_P4_RGB565A)
|
||||
/* Check whether image format has an alpha color */
|
||||
#define IMAGE_IS_ALPHA(format) \
|
||||
((format) == IMAGE_RGB565A || \
|
||||
(format) == IMAGE_P8_RGB565A || \
|
||||
(format) == IMAGE_P4_RGB565A)
|
||||
/* Check whether image format uses a palette */
|
||||
#define IMAGE_IS_INDEXED(format) \
|
||||
(IMAGE_IS_P8(format) || IMAGE_IS_P4(format))
|
||||
|
||||
/* Image flags. These are used for memory management, mostly. */
|
||||
enum {
|
||||
IMAGE_FLAGS_DATA_RO = 0x01, /* Data is read-only */
|
||||
IMAGE_FLAGS_PALETTE_RO = 0x02, /* Palette is read-only */
|
||||
IMAGE_FLAGS_DATA_ALLOC = 0x04, /* Data is malloc()'d */
|
||||
IMAGE_FLAGS_PALETTE_ALLOC = 0x08, /* Palette is malloc()'d */
|
||||
};
|
||||
|
||||
/* image_t: gint's native bitmap image format
|
||||
Images of this format can be created through this header's API but also by
|
||||
using the fxSDK's built-in image converters with fxconv. */
|
||||
typedef struct
|
||||
{
|
||||
/* Color format, one of the IMAGE_* values defined above */
|
||||
uint8_t format;
|
||||
/* Additional flags, a combination of IMAGE_FLAGS_* values */
|
||||
uint8_t flags;
|
||||
/* Number of colors in the palette; this includes alpha for transparent
|
||||
images, as alpha is always the first entry.
|
||||
RGB16: 0
|
||||
P8: Ranges between 1 and 256
|
||||
P4: 16 */
|
||||
int16_t color_count;
|
||||
/* Full width and height, in pixels */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
/* Byte stride between lines */
|
||||
int stride;
|
||||
|
||||
/* Pixel data in row-major order, left to right.
|
||||
- RGB16: 2 bytes per entry, each row padded to 4 bytes for alignment.
|
||||
Each 2-byte value is an RGB565 color.
|
||||
- P8: 1 signed byte per entry. Each byte is a palette index shifted by
|
||||
128 (to access the color, use palette[<value>+128]).
|
||||
- P4: 4 bits per entry, each row padded to a full byte. Each entry is a
|
||||
direct palette index between 0 and 15. */
|
||||
void *data;
|
||||
|
||||
/* For P8 and P4, color palette. The number of entries allocated in the
|
||||
array is equal to the color_count attribute. */
|
||||
uint16_t *palette;
|
||||
|
||||
} GPACKED(4) image_t;
|
||||
|
||||
/* Dynamic effects: these transformations can be applied on images while
|
||||
rendering. Not all effects can be combined; unless specified otherwise:
|
||||
- HFLIP and VFLIP can both be added regardless of any other effect
|
||||
- At most one color effect can be applied */
|
||||
enum {
|
||||
/* Value 0x01 is reserved, because it is DIMAGE_NOCLIP, which although
|
||||
part of the old API still needs to be supported. */
|
||||
|
||||
/* [Any]: Skip clipping the command against the source image */
|
||||
IMAGE_NOCLIP_INPUT = 0x04,
|
||||
/* [Any]: Skip clipping the command against the output VRAM */
|
||||
IMAGE_NOCLIP_OUTPUT = 0x08,
|
||||
/* [Any]: Skip clipping both */
|
||||
IMAGE_NOCLIP = IMAGE_NOCLIP_INPUT | IMAGE_NOCLIP_OUTPUT,
|
||||
|
||||
// Geometric effects. These values should remain at exactly bit 8 and
|
||||
// following, or change gint_image_mkcmd() along with it.
|
||||
|
||||
/* [Any]: Flip image vertically */
|
||||
IMAGE_VFLIP = 0x0100,
|
||||
/* [Any]: Flip image horizontally */
|
||||
IMAGE_HFLIP = 0x0200,
|
||||
|
||||
// Color effects
|
||||
|
||||
/* [RGB565, P8_RGB565, P4_RGB565]: Make a color transparent
|
||||
Adds one argument:
|
||||
* Color to clear (RGB16: 16-bit value; P8/P4: palette index) */
|
||||
IMAGE_CLEARBG = 0x10,
|
||||
/* [RGB565, P8_RGB565, P4_RGB565]: Turn a color into another
|
||||
Adds two arguments:
|
||||
* Color to replace (RGB16: 16-bit value; P8/P4: palette index)
|
||||
* Replacement color (16-bit value) */
|
||||
IMAGE_SWAPCOLOR = 0x20,
|
||||
/* [RGB565A, P8_RGB565A, P4_RGB565A]: Add a background
|
||||
Adds one argument:
|
||||
* Background color (16-bit value) */
|
||||
IMAGE_ADDBG = 0x40,
|
||||
/* [RGB565A, P8_RGB565A, P4_RGB565A]: Dye all non-transparent pixels
|
||||
Adds one argument:
|
||||
* Dye color (16-bit value) */
|
||||
IMAGE_DYE = 0x80,
|
||||
};
|
||||
|
||||
//---
|
||||
// Image creation and destruction
|
||||
//---
|
||||
|
||||
/* 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 window, returns false. Otherwise, returns
|
||||
true. */
|
||||
bool gint_image_clip_input(image_t const *img, struct gint_image_box *box,
|
||||
struct dwindow const *window);
|
||||
|
||||
/* Clip the provided box against the output. */
|
||||
void gint_image_clip_output(struct gint_image_box *b,
|
||||
struct dwindow const *window);
|
||||
|
||||
//---
|
||||
// Internal image rendering routines
|
||||
//
|
||||
// The following functions (or non-functions) are implemented in assembler and
|
||||
// make up the internal interface of the image renderer. If you just want to
|
||||
// display images, use dimage() and variations; these are only useful if you
|
||||
// have a different rendering system and wish to use image rendering with
|
||||
// dynamic effects in it.
|
||||
//---
|
||||
|
||||
/* Renderer command. This structure includes most of the information used by
|
||||
the image renderer to perform blits. Some of the information on the target
|
||||
is also passed as direct arguments, which is more convenient and slightly
|
||||
faster.
|
||||
|
||||
Most of the values here can be set with gint_image_mkcmd(). The last two
|
||||
members, along with the return values of the gint_image_FORMAT_loop()
|
||||
functions, are used to update the command if one needs to draw *parts* of
|
||||
the image and resume the rendering later. This is used in Azur. */
|
||||
struct gint_image_cmd
|
||||
{
|
||||
/* Shader ID. This is used in Azur, and ignored in gint */
|
||||
uint8_t shader_id;
|
||||
/* Dynamic effects not already dispatched by renderer
|
||||
Bit 0: VFLIP
|
||||
Bit 1: HFLIP */
|
||||
uint8_t effect;
|
||||
|
||||
/* Number of pixels to render per line. For formats that force either x
|
||||
or width alignment (most of them), this is already adjusted to a
|
||||
suitable multiple (usually a multiple of 2). */
|
||||
int16_t columns;
|
||||
|
||||
/* Stride of the input image (number of pixels between each row), in
|
||||
pixels, without subtracting the number of columns */
|
||||
int16_t input_stride;
|
||||
|
||||
/* Number of lines in the command. This can be adjusted freely, and is
|
||||
particularly useful in Azur for fragmented rendering. */
|
||||
uint8_t lines;
|
||||
|
||||
/* [Any effect]: Offset of first edge */
|
||||
int8_t edge_1;
|
||||
|
||||
/* Core loop; this is an internal label of the renderer */
|
||||
void const *loop;
|
||||
/* Output pixel array, offset by target x/y */
|
||||
void const *output;
|
||||
/* Input pixel array, offset by source x/y. For formats that force x
|
||||
alignment, this is already adjusted. */
|
||||
void const *input;
|
||||
/* Palette, when applicable */
|
||||
uint16_t const *palette;
|
||||
|
||||
/* [Any effect]: Offset of right edge */
|
||||
int16_t edge_2;
|
||||
/* [CLEARBG, SWAPCOLOR]: Source color */
|
||||
uint16_t color_1;
|
||||
/* [SWAPCOLOR]: Destination color */
|
||||
uint16_t color_2;
|
||||
|
||||
/* Remaining height (for updates between fragments) */
|
||||
int16_t height;
|
||||
/* Local x position (for updates between fragments) */
|
||||
int16_t x;
|
||||
};
|
||||
|
||||
/* gint_image_mkcmd(): Prepare a rendering command with dynamic effects
|
||||
|
||||
This function crafts an image renderer command. It loads all the settings
|
||||
except for effect-dependent parameters: the [.loop] label, the color section
|
||||
of [.effect], and color effect settings. See the effect-specific functions
|
||||
to see how they are defined.
|
||||
|
||||
The benefit of this approach is that the rendering code does not need to be
|
||||
linked in unless an effect is actually used, which avoids blowing up the
|
||||
size of the add-in as the number of support dynamic effects increases.
|
||||
|
||||
@box Requested on-screen box (will be clipped depending on effects)
|
||||
@img Source image
|
||||
@effects Set of dynamic effects to be applied, as an [IMAGE_*] bitmask
|
||||
@left_edge Whether to force 2-alignment on the input (box->left)
|
||||
@right_edge Whether to force 2-alignment on the width
|
||||
@cmd Command to be filled
|
||||
@window Rendering window (usually {0, 0, DWIDTH, DHEIGHT})
|
||||
|
||||
Returns false if there is nothing to render because of clipping (in which
|
||||
case [cmd] is unchanged), true otherwise. [*box] is also updated to reflect
|
||||
the final box after clipping but not accounting for edges. */
|
||||
bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img,
|
||||
int effects, bool left_edge, bool right_edge,
|
||||
struct gint_image_cmd *cmd, struct dwindow const *window);
|
||||
|
||||
/* Entry point of the renderers. These functions can be called normally as long
|
||||
as you can build the commands (eg. by using gint_image_mkcmd() then filling
|
||||
the effect-specific information). */
|
||||
void *gint_image_rgb16_loop (int output_width, struct gint_image_cmd *cmd);
|
||||
void *gint_image_p8_loop (int output_width, struct gint_image_cmd *cmd);
|
||||
void *gint_image_p4_loop (int output_width, struct gint_image_cmd *cmd);
|
||||
|
||||
/* Renderer fragments. The following can absolutely not be called from C code
|
||||
as they aren't full functions (and this isn't their prototype). These are
|
||||
continuations to be specified in the [.loop] field of a command before using
|
||||
one of the functions above. */
|
||||
|
||||
void gint_image_rgb16_normal(void);
|
||||
void gint_image_rgb16_clearbg(void);
|
||||
void gint_image_rgb16_swapcolor(void);
|
||||
void gint_image_rgb16_dye(void);
|
||||
|
||||
void gint_image_p8_normal(void);
|
||||
void gint_image_p8_clearbg(void);
|
||||
void gint_image_p8_swapcolor(void);
|
||||
void gint_image_p8_dye(void);
|
||||
|
||||
void gint_image_p4_normal(void);
|
||||
void gint_image_p4_clearbg(void);
|
||||
void gint_image_p4_clearbg_alt(void);
|
||||
void gint_image_p4_swapcolor(void);
|
||||
void gint_image_p4_dye(void);
|
||||
|
||||
//---
|
||||
// Image library utilities
|
||||
//
|
||||
// The following functions and macros are mostly internal utilities; they are
|
||||
// exposed here in case user applications want to extend the set of image
|
||||
// transforms with custom additions.
|
||||
//---
|
||||
|
||||
/* image_target(): Check if an image can be used as target for a transform
|
||||
|
||||
This function is used to quickly check whether a transform from [src] to
|
||||
[dst] is possible. It requires image_valid(src) and image_valid(dst), plus
|
||||
any optional constraints specified as variadic arguments. These constraints
|
||||
can be:
|
||||
|
||||
* NOT_P4: fails if [dst] is P4.
|
||||
* DATA_RW: fails if [dst] is not data-writable.
|
||||
* PALETTE_RW: fails if [dst] is not palette-writable.
|
||||
* SAME_SIZE: fails if [dst] is not at least as large as [src].
|
||||
|
||||
For example, in image_hflip(), we write:
|
||||
if(!image_target(src, dst, NOT_P4, DATA_RW, SAME_SIZE)) return; */
|
||||
|
||||
enum {
|
||||
IMAGE_TARGET_NONE,
|
||||
IMAGE_TARGET_NOT_P4,
|
||||
IMAGE_TARGET_DATA_RW,
|
||||
IMAGE_TARGET_PALETTE_RW,
|
||||
IMAGE_TARGET_SAME_SIZE,
|
||||
IMAGE_TARGET_SAME_FORMAT,
|
||||
IMAGE_TARGET_SAME_DEPTH,
|
||||
};
|
||||
bool image_target(image_t const *src, image_t *dst, ...);
|
||||
|
||||
#define image_target(src, dst, ...) \
|
||||
image_target(src, dst, image_target_arg1(__VA_ARGS__ __VA_OPT__(,) NONE))
|
||||
#define image_target_arg1(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg2(__VA_ARGS__))
|
||||
#define image_target_arg2(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg3(__VA_ARGS__))
|
||||
#define image_target_arg3(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg4(__VA_ARGS__))
|
||||
#define image_target_arg4(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg5(__VA_ARGS__))
|
||||
#define image_target_arg5(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg6(__VA_ARGS__))
|
||||
#define image_target_arg6(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_too_many_args(__VA_ARGS__))
|
||||
|
||||
/* image_alpha_2(): Conditional alpha */
|
||||
#define image_alpha_2(fmt, copy_alpha) \
|
||||
((copy_alpha) ? 0x10000 : image_alpha(fmt))
|
||||
|
||||
#endif /* FXCG50 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_IMAGE */
|
|
@ -207,7 +207,7 @@ enum {
|
|||
/* Enable modifiers keys */
|
||||
GETKEY_MOD_SHIFT = 0x01,
|
||||
GETKEY_MOD_ALPHA = 0x02,
|
||||
/* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
|
||||
/* SHIFT + OPTN (requires GETKEY_MOD_SHIFT) or LIGHT toggles backlight */
|
||||
GETKEY_BACKLIGHT = 0x04,
|
||||
/* MENU triggers a task switch and displays the main menu */
|
||||
GETKEY_MENU = 0x08,
|
||||
|
@ -225,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);
|
||||
|
@ -260,53 +256,6 @@ typedef bool (*getkey_feature_t)(key_event_t event);
|
|||
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);
|
||||
|
||||
|
@ -346,4 +295,32 @@ int keycode_digit(int keycode);
|
|||
}
|
||||
#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 */
|
||||
|
|
|
@ -75,6 +75,10 @@ enum {
|
|||
of the matrix one could use a ghosting effect to boot the calc. */
|
||||
KEY_ACON = 0x07,
|
||||
|
||||
/* Virtual key codes */
|
||||
KEY_HELP = 0x20, /* fx-9860G Slim: 0x75 */
|
||||
KEY_LIGHT = 0x10, /* fx-9860G Slim: 0x76 */
|
||||
|
||||
/* Key aliases (handle with care =D) */
|
||||
KEY_X2 = KEY_SQUARE,
|
||||
KEY_CARET = KEY_POWER,
|
||||
|
|
|
@ -39,6 +39,20 @@ void *krealloc(void *ptr, size_t size);
|
|||
/* kfree(): Free memory allocated with kalloc() */
|
||||
void kfree(void *ptr);
|
||||
|
||||
/* kmalloc_max(): Allocate the largest block available in an arena
|
||||
|
||||
This function is like kmalloc(), but it find the largest block in the arena
|
||||
and returns it whole after setting its size in *size. This is useful if you
|
||||
need memory to manage with another allocator, or you don't yet know the size
|
||||
of the data to be generated. The block can later be shrunk with realloc().
|
||||
|
||||
Currently only gint-managed arenas support this operation.
|
||||
|
||||
@size Will be set to size of returned block (if there is one)
|
||||
@arean_name Name of arena to allocate in (*cannot* be NULL)
|
||||
Returns the address of the largest block available, NULL on error. */
|
||||
void *kmalloc_max(size_t *size, char const *arena_name);
|
||||
|
||||
//---
|
||||
// Extension API for new areas and statistics
|
||||
//---
|
||||
|
@ -53,6 +67,8 @@ typedef struct {
|
|||
void * (*realloc)(void *ptr, size_t newsize, void *data);
|
||||
/* kfree() handles ptr == NULL*/
|
||||
void (*free)(void *ptr, void *data);
|
||||
/* kmalloc_max() backend */
|
||||
void * (*malloc_max)(size_t *size, void *data);
|
||||
|
||||
/* Name, should be unique; gint reserves names starting with "_" */
|
||||
char const *name;
|
||||
|
|
|
@ -10,6 +10,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <gint/mpu/mmu.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
//---
|
||||
// Unified interface
|
||||
|
@ -40,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
|
||||
//---
|
||||
|
@ -77,7 +89,7 @@ uint32_t tlb_translate(uint32_t page, uint32_t *size);
|
|||
/* utlb_addr() - get the P4 address of a UTLB address entry
|
||||
@E Entry number (should be in range 0..63)
|
||||
Returns a pointer to the entry. */
|
||||
const utlb_addr_t *utlb_addr(uint E);
|
||||
utlb_addr_t const *utlb_addr(uint E);
|
||||
|
||||
/* utlb_data() - get the P4 address of a UTLB data entry
|
||||
@E Entry number (should be in range 0..63)
|
||||
|
@ -98,6 +110,16 @@ 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);
|
||||
|
||||
/* itlb_addr(): Get the P4 address of an ITLB address entry
|
||||
@E Entry number (0..3)
|
||||
Returns a pointer to the entry. */
|
||||
itlb_addr_t const *itlb_addr(uint E);
|
||||
|
||||
/* itlb_data(): Get the P4 address of an ITLB data entry
|
||||
@E Entry number (0..3)
|
||||
Returns a pointer to the entry. */
|
||||
itlb_data_t const *itlb_data(uint E);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
//---
|
||||
// gint:mpu:bsc - Bus State Controller
|
||||
//---
|
||||
|
||||
#ifndef GINT_MPU_BSC
|
||||
#define GINT_MPU_BSC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 But State Controller. Refer to:
|
||||
// Renesas SH7705 Group Hardware Manual
|
||||
// Section 7: Bus State Controller (BSC)
|
||||
//---
|
||||
|
||||
typedef volatile lword_union(sh7705_bsc_CSnBCR_t,
|
||||
uint32_t :2;
|
||||
uint32_t IWW :2; /* Wait cycles for Write-Read and Write-Write */
|
||||
uint32_t :1;
|
||||
uint32_t IWRWD :2; /* Wait cycles for other-space Read-Write */
|
||||
uint32_t :1;
|
||||
uint32_t IWRWS :2; /* Wait cycles for same-space Read-Write */
|
||||
uint32_t :1;
|
||||
uint32_t IWRRD :2; /* Wait cycles for other-space Read-Read */
|
||||
uint32_t :1;
|
||||
uint32_t IWRRS :2; /* Wait cycles for same-space Read-Read */
|
||||
uint32_t :1;
|
||||
uint32_t TYPE :3; /* Memory type */
|
||||
uint32_t :1;
|
||||
uint32_t BSZ :2; /* Data bus size */
|
||||
uint32_t :9;
|
||||
);
|
||||
|
||||
/* Warning: the layout of this register changes with n *and* with the memory
|
||||
type. This version is not exhaustive. Check the manual! */
|
||||
typedef volatile lword_union(sh7705_bsc_CSnWCR_t,
|
||||
uint32_t :13;
|
||||
uint32_t WW :3; /* Write access wait cycles */
|
||||
uint32_t :3;
|
||||
uint32_t SW :2; /* Wait from CSn/address to RD/WEn assertion */
|
||||
uint32_t WR :4; /* Access wait cycles */
|
||||
uint32_t WM :1; /* Whether to use external wait */
|
||||
uint32_t :4;
|
||||
uint32_t HW :2; /* Wait from RD/WEn to CSn/address negation */
|
||||
);
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
lword_union(CMNCR,
|
||||
uint32_t :24;
|
||||
uint32_t DMAIW :2; /* DMA single-address wait states */
|
||||
uint32_t DMAIWA :1; /* DMAIW wait states insertion method */
|
||||
uint32_t :1;
|
||||
uint32_t const ENDIAN :1; /* Global CPU endianness flag */
|
||||
uint32_t :1;
|
||||
uint32_t HIZMEM :1; /* High-Z memory Control*/
|
||||
uint32_t HIZCNT :1; /* High-Z Control*/
|
||||
);
|
||||
|
||||
sh7705_bsc_CSnBCR_t CS0BCR;
|
||||
sh7705_bsc_CSnBCR_t CS2BCR;
|
||||
sh7705_bsc_CSnBCR_t CS3BCR;
|
||||
sh7705_bsc_CSnBCR_t CS4BCR;
|
||||
sh7705_bsc_CSnBCR_t CS5ABCR;
|
||||
sh7705_bsc_CSnBCR_t CS5BBCR;
|
||||
sh7705_bsc_CSnBCR_t CS6ABCR;
|
||||
sh7705_bsc_CSnBCR_t CS6BBCR;
|
||||
|
||||
sh7705_bsc_CSnWCR_t CS0WCR;
|
||||
sh7705_bsc_CSnWCR_t CS2WCR;
|
||||
sh7705_bsc_CSnWCR_t CS3WCR;
|
||||
sh7705_bsc_CSnWCR_t CS4WCR;
|
||||
sh7705_bsc_CSnWCR_t CS5AWCR;
|
||||
sh7705_bsc_CSnWCR_t CS5BWCR;
|
||||
sh7705_bsc_CSnWCR_t CS6AWCR;
|
||||
sh7705_bsc_CSnWCR_t CS6BWCR;
|
||||
|
||||
/* TODO: There are more registers (not involved in overclocking). */
|
||||
} GPACKED(4) sh7705_bsc_t;
|
||||
|
||||
#define SH7705_BSC (*(sh7705_bsc_t *)0xa4fd0000)
|
||||
|
||||
|
||||
//---
|
||||
// SH7305 But State Controller. Refer to:
|
||||
// Renesas SH7730 Group Hardware Manual
|
||||
// Section 11: Bus State Controller (BSC)
|
||||
//---
|
||||
|
||||
typedef volatile lword_union(sh7305_bsc_CSnBCR_t,
|
||||
uint32_t :1;
|
||||
uint32_t IWW :3;
|
||||
uint32_t IWRWD :3;
|
||||
uint32_t IWRWS :3;
|
||||
uint32_t IWRRD :3;
|
||||
uint32_t IWRRS :3;
|
||||
uint32_t TYPE :4;
|
||||
uint32_t :1;
|
||||
uint32_t BSZ :2;
|
||||
uint32_t :9;
|
||||
);
|
||||
|
||||
typedef volatile lword_union(sh7305_bsc_CSnWCR_06A6B_t,
|
||||
uint32_t :11;
|
||||
uint32_t BAS :1;
|
||||
uint32_t :1;
|
||||
uint32_t WW :3;
|
||||
uint32_t ADRSFIX:1;
|
||||
uint32_t :2;
|
||||
uint32_t SW :2;
|
||||
uint32_t WR :4;
|
||||
uint32_t WM :1;
|
||||
uint32_t :4;
|
||||
uint32_t HW :2;
|
||||
);
|
||||
|
||||
typedef volatile lword_union(sh7305_bsc_CSnWCR_45A5B_t,
|
||||
uint32_t :11;
|
||||
uint32_t BAS :1;
|
||||
uint32_t :1;
|
||||
uint32_t WW :3;
|
||||
uint32_t :3;
|
||||
uint32_t SW :2;
|
||||
uint32_t WR :4;
|
||||
uint32_t WM :1;
|
||||
uint32_t :4;
|
||||
uint32_t HW :2;
|
||||
);
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
lword_union(CMNCR,
|
||||
uint32_t :6;
|
||||
uint32_t CKOSTP :1;
|
||||
uint32_t CKODRV :1;
|
||||
uint32_t :7;
|
||||
uint32_t DMSTP :1;
|
||||
uint32_t :1;
|
||||
uint32_t BSD :1;
|
||||
uint32_t MAP :2;
|
||||
uint32_t BLOCK :1;
|
||||
uint32_t :7;
|
||||
uint32_t ENDIAN :1;
|
||||
uint32_t :1;
|
||||
uint32_t HIZMEM :1;
|
||||
uint32_t HIZCNT :1;
|
||||
);
|
||||
|
||||
sh7305_bsc_CSnBCR_t CS0BCR;
|
||||
sh7305_bsc_CSnBCR_t CS2BCR;
|
||||
sh7305_bsc_CSnBCR_t CS3BCR;
|
||||
sh7305_bsc_CSnBCR_t CS4BCR;
|
||||
sh7305_bsc_CSnBCR_t CS5ABCR;
|
||||
sh7305_bsc_CSnBCR_t CS5BBCR;
|
||||
sh7305_bsc_CSnBCR_t CS6ABCR;
|
||||
sh7305_bsc_CSnBCR_t CS6BBCR;
|
||||
|
||||
sh7305_bsc_CSnWCR_06A6B_t CS0WCR;
|
||||
lword_union(CS2WCR,
|
||||
uint32_t :8;
|
||||
uint32_t BW :2;
|
||||
uint32_t PMD :1;
|
||||
uint32_t BAS :1;
|
||||
uint32_t :1;
|
||||
uint32_t WW :3;
|
||||
uint32_t :3;
|
||||
uint32_t SW :2;
|
||||
uint32_t WR :4;
|
||||
uint32_t WM :1;
|
||||
uint32_t :4;
|
||||
uint32_t HW :2;
|
||||
);
|
||||
lword_union(CS3WCR,
|
||||
uint32_t :17;
|
||||
uint32_t TRP :2;
|
||||
uint32_t :1;
|
||||
uint32_t TRCD :2;
|
||||
uint32_t :1;
|
||||
uint32_t A3CL :2;
|
||||
uint32_t :2;
|
||||
uint32_t TRWL :2;
|
||||
uint32_t :1;
|
||||
uint32_t TRC :2;
|
||||
);
|
||||
sh7305_bsc_CSnWCR_45A5B_t CS4WCR;
|
||||
sh7305_bsc_CSnWCR_45A5B_t CS5AWCR;
|
||||
sh7305_bsc_CSnWCR_45A5B_t CS5BWCR;
|
||||
sh7305_bsc_CSnWCR_06A6B_t CS6AWCR;
|
||||
sh7305_bsc_CSnWCR_06A6B_t CS6BWCR;
|
||||
|
||||
lword_union(SDCR,
|
||||
uint32_t :11;
|
||||
uint32_t A2ROW :2;
|
||||
uint32_t :1;
|
||||
uint32_t A2COL :2;
|
||||
uint32_t :4;
|
||||
uint32_t RFSH :1;
|
||||
uint32_t RMODE :1;
|
||||
uint32_t PDOWN :1;
|
||||
uint32_t BACTV :1;
|
||||
uint32_t :3;
|
||||
uint32_t A3ROW :2;
|
||||
uint32_t :1;
|
||||
uint32_t A3COL :2;
|
||||
);
|
||||
|
||||
uint32_t RTCSR;
|
||||
uint32_t RTCNT;
|
||||
uint32_t RTCOR;
|
||||
|
||||
} GPACKED(4) sh7305_bsc_t;
|
||||
|
||||
#define SH7305_BSC (*(sh7305_bsc_t *)0xfec10000)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_BSC */
|
|
@ -32,6 +32,12 @@ typedef volatile struct
|
|||
uint16_t PFC :2; /* Peripheral clock divider */
|
||||
);
|
||||
|
||||
byte_union(UCLKCR,
|
||||
uint8_t USSCS :2; /* Source Clock Selection Bit */
|
||||
uint8_t USBEN :1; /* USB On-Chip Oscillator Enable */
|
||||
uint8_t :5;
|
||||
);
|
||||
|
||||
} GPACKED(4) sh7705_cpg_t;
|
||||
|
||||
#define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80))
|
||||
|
@ -47,7 +53,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 [*] */
|
||||
|
@ -94,7 +100,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;
|
||||
|
@ -117,6 +125,9 @@ typedef volatile struct
|
|||
uint32_t :3;
|
||||
uint32_t FLF :11; /* FLL Multiplication Ratio */
|
||||
);
|
||||
pad(0x0c);
|
||||
|
||||
uint32_t LSTATS;
|
||||
|
||||
} GPACKED(4) sh7305_cpg_t;
|
||||
|
||||
|
|
|
@ -298,8 +298,8 @@ typedef struct
|
|||
//---
|
||||
|
||||
/* Provided by intc/intc.c */
|
||||
extern sh7705_intc_t SH7705_INTC;
|
||||
extern sh7305_intc_t SH7305_INTC;
|
||||
extern sh7705_intc_t const SH7705_INTC;
|
||||
extern sh7305_intc_t const SH7305_INTC;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -49,12 +49,10 @@ typedef struct
|
|||
} GPACKED(4) tlb_data_t;
|
||||
|
||||
//---
|
||||
// SH7305 TLB. Refer to:
|
||||
// "Renesas SH7724 User's Manual: Hardware"
|
||||
// Section 7: "Memory Management Unit (MMU)"
|
||||
// SH7305 TLB. Refer to SH4AL-DSP manual, section 7 (MMU)
|
||||
//---
|
||||
|
||||
/* utlb_addr_t - address part of a UTLB entry */
|
||||
/* utlb_addr_t: Address part of a UTLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint VPN :22;
|
||||
|
@ -64,7 +62,7 @@ typedef struct
|
|||
|
||||
} GPACKED(4) utlb_addr_t;
|
||||
|
||||
/* utlb_data_t - data part of a UTLB entry */
|
||||
/* utlb_data_t: Data part of a UTLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint :3;
|
||||
|
@ -81,6 +79,34 @@ typedef struct
|
|||
|
||||
} GPACKED(4) utlb_data_t;
|
||||
|
||||
/* itlb_addr_t: Address part of an ITLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint VPN :22;
|
||||
uint :1;
|
||||
uint V :1;
|
||||
uint ASID :8;
|
||||
|
||||
} GPACKED(4) itlb_addr_t;
|
||||
|
||||
/* itlb_data_t: Data part of an ITLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint :3;
|
||||
uint PPN :19;
|
||||
uint :1;
|
||||
uint V :1;
|
||||
uint SZ1 :1;
|
||||
uint PR :1;
|
||||
uint :1;
|
||||
uint SZ0 :1;
|
||||
uint C :1;
|
||||
uint :1;
|
||||
uint SH :1;
|
||||
uint :1;
|
||||
|
||||
} GPACKED(4) itlb_data_t;
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
lword_union(PTEH,
|
||||
|
|
|
@ -76,9 +76,162 @@ typedef volatile struct
|
|||
#define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100))
|
||||
|
||||
//---
|
||||
// TODO: Document the SH7305 Pin Function Controller
|
||||
// SH7305 Pin Function Controller.
|
||||
// This is somewhat reminiscent of the SH7724, but there are many differences.
|
||||
// The official emulator is the basis for this module.
|
||||
//---
|
||||
|
||||
/* All port control registers (PCRx) have 2-bit entries specifying 4 pin modes:
|
||||
00: Special functions
|
||||
01: Output
|
||||
10: Input (pull-up ON)
|
||||
11: Input (pull-up OFF)
|
||||
|
||||
Some pins do not accept all settings. The SH7724 has these exceptions:
|
||||
- PGCR (all pins): Input now allowed (00 and 01 only)
|
||||
- PJCR.P5MD/P6MD/P7MD: Input not allowed (00 and 01 only)
|
||||
|
||||
Also, some entries are not configurable or are not mapped to actual pins.
|
||||
Again the SH7724 has these exceptions:
|
||||
- PGCR.P6MD/P7MD: No setting
|
||||
- PJCR.P4MD: No setting
|
||||
- PSCR.P7MD: No setting */
|
||||
typedef volatile word_union(sh7305_pfc_control_t,
|
||||
uint16_t P7MD :2;
|
||||
uint16_t P6MD :2;
|
||||
uint16_t P5MD :2;
|
||||
uint16_t P4MD :2;
|
||||
uint16_t P3MD :2;
|
||||
uint16_t P2MD :2;
|
||||
uint16_t P1MD :2;
|
||||
uint16_t P0MD :2;
|
||||
);
|
||||
|
||||
/* Data register; a plain 8-bit value. Each bit can be either read or written
|
||||
depending on the mode of the corresponding pin. */
|
||||
typedef volatile byte_union(sh7305_pfc_data_t,
|
||||
uint8_t P7DT :1;
|
||||
uint8_t P6DT :1;
|
||||
uint8_t P5DT :1;
|
||||
uint8_t P4DT :1;
|
||||
uint8_t P3DT :1;
|
||||
uint8_t P2DT :1;
|
||||
uint8_t P1DT :1;
|
||||
uint8_t P0DT :1;
|
||||
);
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
sh7305_pfc_control_t
|
||||
PACR, PBCR, PCCR, PDCR, PECR, PFCR, PGCR, PHCR,
|
||||
PJCR, PKCR, PLCR, PMCR, PNCR, PQCR, PRCR, PSCR;
|
||||
|
||||
sh7305_pfc_data_t PADR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PBDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PCDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PDDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PEDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PFDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PGDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PHDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PJDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PKDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PLDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PMDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PNDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PQDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PRDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PSDR;
|
||||
pad(1);
|
||||
|
||||
sh7305_pfc_control_t PTCR;
|
||||
sh7305_pfc_control_t PUCR;
|
||||
sh7305_pfc_control_t PVCR;
|
||||
pad(6);
|
||||
sh7305_pfc_control_t PPCR;
|
||||
|
||||
/* PSEM*: Multiplexing pin settings. These are highly MPU-dependent and
|
||||
the assignment is basically unknown.
|
||||
HIZCR*: High-impedance settings for pins' I/O buffers. */
|
||||
uint16_t PSELA;
|
||||
uint16_t PSELB;
|
||||
uint16_t PSELC;
|
||||
uint16_t PSELD;
|
||||
uint16_t PSELE;
|
||||
uint16_t HIZCRA;
|
||||
uint16_t HIZCRB;
|
||||
uint16_t HIZCRC;
|
||||
uint16_t PSELF;
|
||||
|
||||
sh7305_pfc_data_t PTDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PUDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PVDR;
|
||||
pad(5);
|
||||
sh7305_pfc_data_t PPDR;
|
||||
pad(0x15);
|
||||
|
||||
/* Module function selection registers.
|
||||
WARNING: These are the SH7724 bits, not necessarily the SH7305! */
|
||||
word_union(MSELCRA,
|
||||
uint16_t :8;
|
||||
uint16_t UNKNOWN_USB :2;
|
||||
uint16_t :6;
|
||||
);
|
||||
word_union(MSELCRB,
|
||||
uint16_t XTAL_USB :2;
|
||||
uint16_t :6;
|
||||
uint16_t SCIF2_PORT :1;
|
||||
uint16_t :1;
|
||||
uint16_t SCIF3_PORT :1;
|
||||
uint16_t :3;
|
||||
uint16_t LDC_VSYNC_DIR :1;
|
||||
uint16_t :1;
|
||||
);
|
||||
|
||||
// TODO: Doc
|
||||
uint16_t DRVCRD, DRVCRA, DRVCRB, DRVCRC;
|
||||
pad(4);
|
||||
|
||||
/* Pull-up control registers. Not sure what these are since that's
|
||||
normally handled in the modes for P*CR? */
|
||||
uint8_t PULCRA, PULCRB, PULCRC, PULCRD, PULCRE, PULCRF, PULCRG,
|
||||
PULCRH, PULCRJ, PULCRK, PULCRL, PULCRM, PULCRN, PULCRQ,
|
||||
PULCRR, PULCRS;
|
||||
pad(0x20);
|
||||
uint8_t PULCRT;
|
||||
uint8_t PULCRU;
|
||||
uint8_t PULCRV;
|
||||
uint8_t PULCRBSC;
|
||||
pad(1);
|
||||
uint8_t PULCRTRST;
|
||||
uint8_t PULCRP;
|
||||
pad(1);
|
||||
|
||||
uint16_t PSELG;
|
||||
pad(12);
|
||||
uint16_t PSELH;
|
||||
|
||||
} sh7305_pfc_t;
|
||||
|
||||
#define SH7305_PFC (*((sh7305_pfc_t *)0xa4050100))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
//---
|
||||
// gint:mpu:scif - Serial Communication Interface with FIFO (SCIF)
|
||||
//---
|
||||
|
||||
#ifndef GINT_MPU_SCIF
|
||||
#define GINT_MPU_SCIF
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 SCIF (from: SH7705 manual, section 16)
|
||||
//---
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* Serial Mode Register */
|
||||
word_union(SCSMR,
|
||||
uint16_t :5;
|
||||
uint16_t SRC :3; /* Sampling Control */
|
||||
uint16_t CA :1; /* Communication Mode */
|
||||
uint16_t CHR :1; /* Character Length */
|
||||
uint16_t PE :1; /* Parity Enable */
|
||||
uint16_t OE :1; /* Parity Mode */
|
||||
uint16_t STOP :1; /* Stop Bit Length */
|
||||
uint16_t :1;
|
||||
uint16_t CKS :2; /* Clock Select */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Bit Rate Register */
|
||||
uint8_t SCBRR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial Control Register */
|
||||
word_union(SCSCR,
|
||||
uint16_t :4;
|
||||
uint16_t TSIE :1; /* Transmit Data Stop Interrupt Enable */
|
||||
uint16_t ERIE :1; /* Receive Error Interrupt Enable */
|
||||
uint16_t BRIE :1; /* Break Interrupt Enable */
|
||||
uint16_t DRIE :1; /* Receive Data Ready Interrupt Enable */
|
||||
uint16_t TIE :1; /* Transmit Interrupt Enable */
|
||||
uint16_t RIE :1; /* Receive Interrupt Enable */
|
||||
uint16_t TE :1; /* Transmit Enable */
|
||||
uint16_t RE :1; /* Receive Mode */
|
||||
uint16_t :2;
|
||||
uint16_t CKE :2; /* Clock Enable */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Transmit Data Stop Register */
|
||||
uint8_t SCTDSR;
|
||||
pad(0x3);
|
||||
|
||||
/* FIFO Error Count Register */
|
||||
word_union(SCFER,
|
||||
uint16_t :2;
|
||||
uint16_t PER :6; /* Parity Error Count */
|
||||
uint16_t :2;
|
||||
uint16_t FER :6; /* Framing Error Count */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial Status Register */
|
||||
word_union(SCSSR,
|
||||
uint16_t :6;
|
||||
uint16_t ORER :1; /* Overrun Error */
|
||||
uint16_t TSF :1; /* Transmit Data Stop */
|
||||
uint16_t ER :1; /* Receive Error */
|
||||
uint16_t TEND :1; /* Transmit End */
|
||||
uint16_t TDFE :1; /* Transmit FIFO Data Empty */
|
||||
uint16_t BRK :1; /* Break Detect */
|
||||
uint16_t FER :1; /* Framing Error */
|
||||
uint16_t PER :1; /* Parity Error */
|
||||
uint16_t RDF :1; /* Receive FIFO Data Full */
|
||||
uint16_t DR :1; /* Receive Data Ready */
|
||||
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* FIFO Control Register */
|
||||
word_union(SCFCR,
|
||||
uint16_t TSE :1; /* Transmit Data Stop Enable */
|
||||
uint16_t TCRST :1; /* Transmit Count Reset */
|
||||
uint16_t :3;
|
||||
uint16_t RSTRG :3; /* RTS Output Active Trigger */
|
||||
uint16_t RTRG :2; /* Receive FIFO Data Number Trigger */
|
||||
uint16_t TTRG :2; /* Transmit FIFO Data Number Trigger */
|
||||
uint16_t MCE :1; /* Modem Control Enable */
|
||||
uint16_t TFRST :1; /* Transmit FIFO Data Register Reset */
|
||||
uint16_t RFRST :1; /* Receive FIFO Data Register Reset */
|
||||
uint16_t LOOP :1; /* Loopback Test */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* FIFO Data Count Register */
|
||||
word_union(SCFDR,
|
||||
uint16_t :1;
|
||||
uint16_t T :7; /* Pending bytes in transmit FIFO */
|
||||
uint16_t :1;
|
||||
uint16_t R :7; /* Received bytes in receive FIFO */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial FIFO Transmit Data Register (64 bytes rolling register) */
|
||||
uint8_t SCFTDR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial FIFO Receive Data Register (64 bytes rolling register) */
|
||||
uint8_t SCFRDR;
|
||||
|
||||
} GPACKED(4) sh7705_scif_t;
|
||||
|
||||
#define SH7705_SCIF (*((sh7705_scif_t *)0xa4410000))
|
||||
|
||||
//---
|
||||
// SH7305 SCIF (from: SH7305 emulator and SH7730 SCIF)
|
||||
// The module is very close to the SH7724 but it has a couple extra bits in
|
||||
// SCFCR, which shows that it is closer to the SH7730 SCIF.
|
||||
//---
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* Serial Mode Register */
|
||||
word_union(SCSMR,
|
||||
uint16_t :8;
|
||||
uint16_t CA :1; /* Communication Mode */
|
||||
uint16_t CHR :1; /* Character Length */
|
||||
uint16_t PE :1; /* Parity Enable */
|
||||
uint16_t OE :1; /* Parity Mode */
|
||||
uint16_t STOP :1; /* Stop Bit Length */
|
||||
uint16_t :1;
|
||||
uint16_t CKS :2; /* Clock Select */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial Bit Rate Register */
|
||||
uint8_t SCBRR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial Control Register */
|
||||
word_union(SCSCR,
|
||||
uint16_t :8;
|
||||
uint16_t TIE :1; /* Transmit Interrupt Enable */
|
||||
uint16_t RIE :1; /* Receive Interrupt Enable */
|
||||
uint16_t TE :1; /* Transmit Enable */
|
||||
uint16_t RE :1; /* Receive Mode */
|
||||
uint16_t REIE :1; /* Receive Error Interrupt Enable */
|
||||
uint16_t :1;
|
||||
uint16_t CKE :2; /* Clock Enable */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial FIFO Transmit Data Register */
|
||||
uint8_t SCFTDR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial FIFO Status Register */
|
||||
word_union(SCFSR,
|
||||
uint16_t const PERC :4; /* Number of Parity Errors */
|
||||
uint16_t const FERC :4; /* Number of Framing Errors */
|
||||
uint16_t ER :1; /* Receive Error */
|
||||
uint16_t TEND :1; /* Transmit End */
|
||||
uint16_t TDFE :1; /* Transmit FIFO Data Empty */
|
||||
uint16_t BRK :1; /* Break Detection */
|
||||
uint16_t const FER :1; /* Framing Error */
|
||||
uint16_t const PER :1; /* Parity Error */
|
||||
uint16_t RDF :1; /* Receive FIFO Data Full */
|
||||
uint16_t DR :1; /* Data Ready */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial FIFO Receive Data Register */
|
||||
uint8_t SCFRDR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial FIFO Control Register */
|
||||
word_union(SCFCR,
|
||||
uint16_t :5;
|
||||
uint16_t RSTRG :3; /* RTS Output Active Trigger */
|
||||
uint16_t RTRG :2; /* Receive FIFO Data Trigger */
|
||||
uint16_t TTRG :2; /* Transmit FIFO Data Trigger */
|
||||
uint16_t MCE :1; /* Model Control Enable */
|
||||
uint16_t TFRST :1; /* Transmit FIFO Data Register Reset */
|
||||
uint16_t RFRST :1; /* Receive FIFO Data Register Reset */
|
||||
uint16_t LOOP :1; /* Loopback Test */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial FIFO Data Count Register */
|
||||
word_union(SCFDR,
|
||||
uint16_t :3;
|
||||
uint16_t TFDC :5; /* Number of Data Bytes in Transmit FIFO */
|
||||
uint16_t :3;
|
||||
uint16_t RFDC :5; /* Number of Data Bytes in Receive FIFO */
|
||||
);
|
||||
pad(0x6);
|
||||
|
||||
/* Serial Line Status */
|
||||
word_union(SCLSR,
|
||||
uint16_t :15;
|
||||
uint16_t ORER :1; /* Overrun Error */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
} GPACKED(4) sh7305_scif_t;
|
||||
|
||||
#define SH7305_SCIF (*((sh7305_scif_t *)0xa4410000))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_SCIF */
|
|
@ -11,6 +11,19 @@ extern "C" {
|
|||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
word_union(TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
} sh7305_usb_pipetr;
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* System Configuration Control Register */
|
||||
|
@ -28,12 +41,16 @@ typedef volatile struct
|
|||
|
||||
/* CPU Bus Wait Setting Register */
|
||||
word_union(BUSWAIT,
|
||||
uint16_t :12;
|
||||
uint16_t :8;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t :1;
|
||||
uint16_t _2 :1; /* Unknown role; can be set */
|
||||
uint16_t :1;
|
||||
uint16_t BWAIT :4; /* Bus Wait */
|
||||
);
|
||||
|
||||
/* System Configuration Status Register */
|
||||
word_union(SYSSTS,
|
||||
const word_union(SYSSTS,
|
||||
uint16_t :14;
|
||||
uint16_t LNST :2; /* Line Status */
|
||||
);
|
||||
|
@ -41,7 +58,9 @@ typedef volatile struct
|
|||
|
||||
/* Device State Control Register */
|
||||
word_union(DVSTCTR,
|
||||
uint16_t :7;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t :2;
|
||||
uint16_t _2 :4; /* Unknown role; can be set */
|
||||
uint16_t WKUP :1; /* Wakeup Output */
|
||||
uint16_t RWUPE :1; /* Wakeup Detection Enable */
|
||||
uint16_t USBRT :1; /* USB Reset Output */
|
||||
|
@ -92,8 +111,9 @@ typedef volatile struct
|
|||
uint16_t DREQE :1; /* DMA Transfert Request Enable */
|
||||
uint16_t MBW :2; /* Access Bits Width */
|
||||
uint16_t :1;
|
||||
uint16_t BIGEND :1; /* Endiant Control */
|
||||
uint16_t :4;
|
||||
uint16_t BIGEND :1; /* Endian Control */
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t :3;
|
||||
uint16_t CURPIPE:4; /* Port Access Pipe Specification */
|
||||
);
|
||||
word_union(D0FIFOCTR,
|
||||
|
@ -111,7 +131,8 @@ typedef volatile struct
|
|||
uint16_t MBW :2; /* Access Bits Width */
|
||||
uint16_t :1;
|
||||
uint16_t BIGEND :1; /* Endian Control */
|
||||
uint16_t :4;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t :3;
|
||||
uint16_t CURPIPE:4; /* Port Access Pipe Specification */
|
||||
);
|
||||
word_union(D1FIFOCTR,
|
||||
|
@ -134,50 +155,26 @@ typedef volatile struct
|
|||
uint16_t BRDYE :1; /* Buffer Ready */
|
||||
uint16_t :8;
|
||||
);
|
||||
pad(4);
|
||||
word_union(INTENB1,
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t BCHGE :1; /* Bus Change */
|
||||
uint16_t :1;
|
||||
uint16_t DTCHE :1; /* Disconnection Detection */
|
||||
uint16_t ATTCHE :1; /* Connection Detection */
|
||||
uint16_t :4;
|
||||
uint16_t EOFERRE:1; /* EOF Error Detection */
|
||||
uint16_t SIGNE :1; /* Setup Transaction Error */
|
||||
uint16_t SACKE :1; /* Setup Transaction Normal Response */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
/* BRDY Interrupt Enable Register */
|
||||
word_union(BRDYENB,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9BRDYE :1;
|
||||
uint16_t PIPE8BRDYE :1;
|
||||
uint16_t PIPE6BRDYE :1;
|
||||
uint16_t PIPE7BRDYE :1;
|
||||
uint16_t PIPE5BRDYE :1;
|
||||
uint16_t PIPE3BRDYE :1;
|
||||
uint16_t PIPE4BRDYE :1;
|
||||
uint16_t PIPE2BRDYE :1;
|
||||
uint16_t PIPE1BRDYE :1;
|
||||
uint16_t PIPE0BRDYE :1;
|
||||
);
|
||||
uint16_t BRDYENB;
|
||||
/* NRDY Interrupt Enable Register */
|
||||
word_union(NRDYENB,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9NRDYE :1;
|
||||
uint16_t PIPE8NRDYE :1;
|
||||
uint16_t PIPE6NRDYE :1;
|
||||
uint16_t PIPE7NRDYE :1;
|
||||
uint16_t PIPE5NRDYE :1;
|
||||
uint16_t PIPE3NRDYE :1;
|
||||
uint16_t PIPE4NRDYE :1;
|
||||
uint16_t PIPE2NRDYE :1;
|
||||
uint16_t PIPE1NRDYE :1;
|
||||
uint16_t PIPE0NRDYE :1;
|
||||
);
|
||||
uint16_t NRDYENB;
|
||||
/* BEMP Interrupt Enable Register */
|
||||
word_union(BEMPENB,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9BEMPE :1;
|
||||
uint16_t PIPE8BEMPE :1;
|
||||
uint16_t PIPE6BEMPE :1;
|
||||
uint16_t PIPE7BEMPE :1;
|
||||
uint16_t PIPE5BEMPE :1;
|
||||
uint16_t PIPE3BEMPE :1;
|
||||
uint16_t PIPE4BEMPE :1;
|
||||
uint16_t PIPE2BEMPE :1;
|
||||
uint16_t PIPE1BEMPE :1;
|
||||
uint16_t PIPE0BEMPE :1;
|
||||
);
|
||||
uint16_t BEMPENB;
|
||||
|
||||
/* SOF Control Register */
|
||||
word_union(SOFCFG,
|
||||
|
@ -186,7 +183,10 @@ typedef volatile struct
|
|||
uint16_t :1;
|
||||
uint16_t BRDYM :1; /* BRDY Status Clear Timing */
|
||||
uint16_t enable :1; /* SHOULD BE SET TO 1 MANUALLY */
|
||||
uint16_t :5;
|
||||
uint16_t :1;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t _2 :1; /* Unknown role; can be set */
|
||||
uint16_t :2;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
|
@ -205,56 +205,32 @@ typedef volatile struct
|
|||
uint16_t VALID :1; /* USB Request Reception */
|
||||
uint16_t CTSQ :3; /* Control Transfer Stage */
|
||||
);
|
||||
pad(4);
|
||||
word_union(INTSTS1,
|
||||
uint16_t _1 :1; /* Unknown role */
|
||||
uint16_t BCHG :1; /* Bus Change */
|
||||
uint16_t :1;
|
||||
uint16_t DTCH :1; /* Disconnection Detection */
|
||||
uint16_t ATTCH :1; /* Connection Detection */
|
||||
uint16_t :4;
|
||||
uint16_t EOFERR :1; /* EOF Error Detection */
|
||||
uint16_t SIGN :1; /* Setup Transaction Error */
|
||||
uint16_t SACK :1; /* Setup Transaction Normal Response */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
/* BRDY Interrupt Status Register */
|
||||
word_union(BRDYSTS,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9BRDY :1;
|
||||
uint16_t PIPE8BRDY :1;
|
||||
uint16_t PIPE6BRDY :1;
|
||||
uint16_t PIPE7BRDY :1;
|
||||
uint16_t PIPE5BRDY :1;
|
||||
uint16_t PIPE3BRDY :1;
|
||||
uint16_t PIPE4BRDY :1;
|
||||
uint16_t PIPE2BRDY :1;
|
||||
uint16_t PIPE1BRDY :1;
|
||||
uint16_t PIPE0BRDY :1;
|
||||
);
|
||||
uint16_t BRDYSTS;
|
||||
/* NRDY Interrupt Status Register */
|
||||
word_union(NRDYSTS,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9NRDY :1;
|
||||
uint16_t PIPE8NRDY :1;
|
||||
uint16_t PIPE6NRDY :1;
|
||||
uint16_t PIPE7NRDY :1;
|
||||
uint16_t PIPE5NRDY :1;
|
||||
uint16_t PIPE3NRDY :1;
|
||||
uint16_t PIPE4NRDY :1;
|
||||
uint16_t PIPE2NRDY :1;
|
||||
uint16_t PIPE1NRDY :1;
|
||||
uint16_t PIPE0NRDY :1;
|
||||
);
|
||||
uint16_t NRDYSTS;
|
||||
/* BEMP Interrupt Status Register */
|
||||
word_union(BEMPSTS,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9BEMP :1;
|
||||
uint16_t PIPE8BEMP :1;
|
||||
uint16_t PIPE6BEMP :1;
|
||||
uint16_t PIPE7BEMP :1;
|
||||
uint16_t PIPE5BEMP :1;
|
||||
uint16_t PIPE3BEMP :1;
|
||||
uint16_t PIPE4BEMP :1;
|
||||
uint16_t PIPE2BEMP :1;
|
||||
uint16_t PIPE1BEMP :1;
|
||||
uint16_t PIPE0BEMP :1;
|
||||
);
|
||||
uint16_t BEMPSTS;
|
||||
|
||||
/* Frame Number Registers */
|
||||
word_union(FRMNUM,
|
||||
uint16_t OVRN :1; /* Overrun/Underrun Detection Status */
|
||||
uint16_t CRCE :1; /* Receive Data Error */
|
||||
uint16_t cons :3;
|
||||
uint16_t const :3;
|
||||
uint16_t FRNM :11; /* Frame Number */
|
||||
);
|
||||
word_union(UFRMNUM,
|
||||
|
@ -289,7 +265,10 @@ typedef volatile struct
|
|||
|
||||
/* DCP Configuration Register */
|
||||
word_union(DCPCFG,
|
||||
uint16_t :11;
|
||||
uint16_t :7;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t _2 :1; /* Unknown role; can be set */
|
||||
uint16_t :2;
|
||||
uint16_t DIR :1; /* Transfer Direction */
|
||||
uint16_t :4;
|
||||
);
|
||||
|
@ -341,7 +320,7 @@ typedef volatile struct
|
|||
/* Pipe Buffer Setting Register */
|
||||
word_union(PIPEBUF,
|
||||
uint16_t :1;
|
||||
uint16_t BUFSIZE : 5; /* Buffer Size */
|
||||
uint16_t BUFSIZE:5; /* Buffer Size */
|
||||
uint16_t :2;
|
||||
uint16_t BUFNMB :8; /* Buffer Number */
|
||||
);
|
||||
|
@ -379,56 +358,20 @@ typedef volatile struct
|
|||
);
|
||||
pad(14);
|
||||
|
||||
/* PIPEn Transaction Counter Enable Registers and */
|
||||
/* PIPEn Transaction Counter Registers */
|
||||
word_union(PIPE1TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE1TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
word_union(PIPE2TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE2TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
word_union(PIPE3TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE3TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
word_union(PIPE4TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE4TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
word_union(PIPE5TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE5TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
/* Transaction Counter Registers (PIPE1..PIPE5 only) */
|
||||
sh7305_usb_pipetr PIPETR[5];
|
||||
pad(0x1e);
|
||||
|
||||
uint16_t REG_C2;
|
||||
pad(12);
|
||||
|
||||
word_union(DEVADD[11],
|
||||
uint16_t :1;
|
||||
uint16_t UPPHUB :4; /* Address of target's hub */
|
||||
uint16_t HUBPORT:3; /* Hub port where target connects */
|
||||
uint16_t USBSPD :2; /* Transfer speed / Target present */
|
||||
uint16_t :6;
|
||||
);
|
||||
|
||||
} GPACKED(4) sh7305_usb_t;
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
//---
|
||||
// gint:mpu:wdt - Watchdog Timer
|
||||
//---
|
||||
|
||||
#ifndef GINT_MPU_WDT
|
||||
#define GINT_MPU_WDT
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 WatchDog Timer. Refer to:
|
||||
// "Renesas SH7705 Group Hardware Manual"
|
||||
// Section 10: "WatchDog Timer (WDT)"
|
||||
//---
|
||||
|
||||
/* sh7705_wdt_t - Watch Dog Timer */
|
||||
typedef volatile struct
|
||||
{
|
||||
/* WDT registers are unique in access size; reads are performed with
|
||||
8-bit accesses, but writes are performed with 16-bit accesses. */
|
||||
|
||||
union {
|
||||
uint8_t READ;
|
||||
uint16_t WRITE;
|
||||
} WTCNT;
|
||||
|
||||
union {
|
||||
byte_union(READ,
|
||||
uint8_t TME :1; /* Timer Enable */
|
||||
uint8_t WTIT :1; /* Watchdog/Interval Select */
|
||||
uint8_t RSTS :1; /* Watchdog Reset Select */
|
||||
uint8_t WOVF :1; /* Watchdog Overflow Flag */
|
||||
uint8_t IOVF :1; /* Interval Overflow Flag */
|
||||
uint8_t CKS :3; /* Clock Select */
|
||||
);
|
||||
uint16_t WRITE;
|
||||
} WTCSR;
|
||||
|
||||
} sh7705_wdt_t;
|
||||
|
||||
#define SH7705_WDT (*((sh7705_wdt_t *)0xffffff84))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_WDT */
|
|
@ -17,7 +17,11 @@ extern "C" {
|
|||
// 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) */
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
//---
|
||||
// gint:serial - Serial communication
|
||||
//---
|
||||
|
||||
// TODO: This is a template for a future implementation.
|
||||
|
||||
#ifndef GINT_SERIAL
|
||||
#define GINT_SERIAL
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
|
||||
typedef enum {
|
||||
SERIAL_PARITY_NONE,
|
||||
SERIAL_PARITY_EVEN,
|
||||
SERIAL_PARITY_ODD,
|
||||
|
||||
} GPACKEDENUM serial_parity_t;
|
||||
|
||||
typedef struct {
|
||||
/* Baud rate (valid options are 300, 600, 1200, 2400, 4800, 9600,
|
||||
19200, 38400, 57600 and 115200) */
|
||||
int baudrate;
|
||||
/* Parity */
|
||||
serial_parity_t parity;
|
||||
/* Data width (valid options are 7 and 8) */
|
||||
uint8_t data_width;
|
||||
/* Stop bits (valid options are 1 and 2) */
|
||||
uint8_t stop_bits;
|
||||
|
||||
} serial_config_t;
|
||||
|
||||
// TODO: Error management...
|
||||
|
||||
int serial_open(serial_config_t const *config);
|
||||
|
||||
bool serial_is_open(void);
|
||||
|
||||
void serial_write_async(void const *data, size_t size, gint_call_t callback);
|
||||
|
||||
void serial_write_sync(void const *data, size_t size);
|
||||
|
||||
void serial_read_async(void const *data, size_t size, gint_call_t callback);
|
||||
|
||||
void serial_read_sync(void const *data, size_t size);
|
||||
|
||||
// Waits for communications to finish
|
||||
void serial_wait(void);
|
||||
|
||||
// Calls serial_wait() automatically
|
||||
void serial_close(void);
|
||||
|
||||
// TODO: Info on how much data is pending in each buffer?
|
||||
// Ultimately we'll bind this to a file descriptor so we can't really know.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_SERIAL */
|
|
@ -1,5 +1,27 @@
|
|||
//---
|
||||
// gint:usb-ff-bulk - A trivial bulk-based transfer class
|
||||
// gint:usb-ff-bulk - A bulk-based transfer class using the fxlink protocol
|
||||
//
|
||||
// This interface (class code 0xff/0x77) implements a simple bidirectional
|
||||
// communication channel running the fxlink protocol. It basically talks to
|
||||
// fxlink's interactive mode on the host, and can broadly be used for 4 things:
|
||||
//
|
||||
// 1. Sending standard messages to fxlink (screenshots, logs...). Convenient
|
||||
// functions are provided in this header to minimize the overhead. fxlink
|
||||
// running in interactive mode (fxlink -t) will handle these messages in a
|
||||
// suitable way automatically (eg. save screenshots as PNG).
|
||||
//
|
||||
// 2. Sending custom messages to fxlink (bench results, memory dumps, crash
|
||||
// reports...). fxlink running in interactive mode will forward the data to
|
||||
// appropriate user-provided external programs to handle the messages.
|
||||
//
|
||||
// 3. Applications based around on data streams coming from the host (screen
|
||||
// projector, web browser...). This can be achieved by modifying fxlink or
|
||||
// using it as a library.
|
||||
//
|
||||
// 4. Remote control from the fxlink interactive mode (fxlink -t). This is
|
||||
// based on fxlink's 'command' and 'keyboard' protocols. This module will
|
||||
// automatically handle commands and respond to them as long as the message
|
||||
// handling function is called regularly.
|
||||
//---
|
||||
|
||||
#ifndef GINT_USB_FF_BULK
|
||||
|
@ -10,164 +32,37 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <gint/usb.h>
|
||||
struct usb_fxlink_header;
|
||||
|
||||
/* The bulk transfer interface with class code 0xff provides a very simple
|
||||
communication channel between the calculator and a host. There is
|
||||
(currently) a single IN pipe that sends data from the calculator to the
|
||||
host, at high-speed (USB 2.0).
|
||||
|
||||
The class code of this interface is 0xff, which means that the protocol is
|
||||
custom and requires a custom program on the host to receive the data (unlike
|
||||
for instance LINK on a Graph 90+E which behaves as a USB stick and can be
|
||||
used with the file browser). fxlink can be used to the effect. */
|
||||
|
||||
/* This bulk transfer interface with class code 0xff/0x77 implements a simple
|
||||
bidirectional communication channel between the calculator and a host,
|
||||
running at high-speed USB 2.0 and using the fxlink protocol. You can use it
|
||||
to communicate with fxlink's interactive mode on the host. */
|
||||
extern usb_interface_t const usb_ff_bulk;
|
||||
|
||||
//---
|
||||
// Direct bulk access
|
||||
//
|
||||
// The following functions can be used to access the bulk pipes directly. They
|
||||
// provide the pipe numbers, which can be passed for instance to usb_write
|
||||
// functions.
|
||||
// Sending standard messages
|
||||
//---
|
||||
|
||||
/* usb_ff_bulk_output(): Pipe for calculator -> host communication */
|
||||
int usb_ff_bulk_output(void);
|
||||
|
||||
//---
|
||||
// fxlink protocol
|
||||
//
|
||||
// fxlink is a bulk-based communication tool in the fxSDK that can be used to
|
||||
// conveniently transfer or manipulate information during the execution of an
|
||||
// add-in. For instance, screenshots can be saved, files can be transferred,
|
||||
// and text can be logged.
|
||||
//
|
||||
// Each communication with fxlink is a message that consists of:
|
||||
// * A first write with a message header;
|
||||
// * One or more writes with message data (of a size announced in the header);
|
||||
// * A commit on the USB pipe (to ensure that everything is sent).
|
||||
//
|
||||
// There are two ways to use the fxlink protocol in this header; you can either
|
||||
// use one of the convenience functions like usb_fxlink_screenshot() that do
|
||||
// all the steps for you, or you can perform the writes and commits yourself to
|
||||
// send custom messages.
|
||||
//---
|
||||
|
||||
/* usb_fxlink_header_t: Message header for fxlink
|
||||
|
||||
fxlink supports a minimalistic protocol to receive data sent from the
|
||||
calculator and automatically process it (such as save it to file, convert
|
||||
to an image, etc). It is designed as a convenience feature, and it can be
|
||||
extended with custom types rather easily.
|
||||
|
||||
A message is categorized with an (application, type) pair; both are UTF-8
|
||||
strings of up to 16 characters. The application name "fxlink" is reserved
|
||||
for built-in types supported by fxlink; any other custom application name
|
||||
can be set (in which case fxlink will call a user-provided program to handle
|
||||
the message).
|
||||
|
||||
The size of the data to be transferred must be specified in order of the
|
||||
transfer to proceed correctly, as it cannot reliably be guessed on the other
|
||||
side (and guessing wrong means all further messages will be in trouble).
|
||||
|
||||
As with the rest of the USB protocol, all the multi-byte integer fields in
|
||||
this header are encoded as *little-endian*. */
|
||||
typedef struct
|
||||
{
|
||||
/* Protocol version = 0x00000100 */
|
||||
uint32_t version;
|
||||
/* Size of the data to transfer (excluding this header) */
|
||||
uint32_t size;
|
||||
/* Size of individual transfers (related to the size of the FIFO) */
|
||||
uint32_t transfer_size;
|
||||
/* Application name, UTF-8 (might not be zero-terminated) */
|
||||
char application[16];
|
||||
/* Message type */
|
||||
char type[16];
|
||||
|
||||
} usb_fxlink_header_t;
|
||||
|
||||
/* usb_fxlink_fill_header(): Fill an fxlink message header
|
||||
|
||||
This function will fill the specified fxlink header. You need to specify the
|
||||
exact amount of data that the fxlink message will contain; if you cannot
|
||||
determine it, consider splitting the message into several messages each with
|
||||
their own header, and use the application-specific script on the host to
|
||||
recombine them.
|
||||
|
||||
Returns false if the parameters are invalid or don't fit, in this case the
|
||||
contents of the header are unchanged. */
|
||||
bool usb_fxlink_fill_header(usb_fxlink_header_t *header,
|
||||
char const *application, char const *type, uint32_t data_size);
|
||||
|
||||
//---
|
||||
// Short functions for fxlink built-in types
|
||||
//---
|
||||
|
||||
/* Subheader for the fxlink built-in "image" type */
|
||||
typedef struct
|
||||
{
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
/* Pixel format, see below */
|
||||
int pixel_format;
|
||||
|
||||
} usb_fxlink_image_t;
|
||||
|
||||
/* Pixel formats */
|
||||
typedef enum
|
||||
{
|
||||
/* Image is an array of *big-endian* uint16_t with RGB565 format */
|
||||
USB_FXLINK_IMAGE_RGB565 = 0,
|
||||
/* Image is an array of bits in mono format */
|
||||
USB_FXLINK_IMAGE_MONO,
|
||||
/* Image is two consecutive mono arrays, one for light, one for dark */
|
||||
USB_FXLINK_IMAGE_GRAY,
|
||||
|
||||
} usb_fxlink_image_format_t;
|
||||
/* usb_fxlink_text(): Send raw text
|
||||
Send a string; fxlink will display it in the terminal. This can be used to
|
||||
back stdout/stderr. Sending lots of small messages can be slow; if that's a
|
||||
problem, fill in message manually. If size is 0, uses strlen(text). */
|
||||
void usb_fxlink_text(char const *text, int size);
|
||||
|
||||
/* usb_fxlink_screenshot(): Take a screenshot
|
||||
|
||||
This function takes a screenshot. If (onscreen = false), it sends the
|
||||
contents of the VRAM. This mode is best used just before dupdate(), since
|
||||
the VRAM contents are exactly as they will appear on screen a moment later.
|
||||
However, this is somewhat unintuitive for interactive GUI as the user has to
|
||||
press a button after the option is present on-screen, therefore the contents
|
||||
of the *next* frame are captured.
|
||||
This function sends a copy of the VRAM to fxlink. This is best used just
|
||||
before dupdate() since this ensures the image sent by USB is identical to
|
||||
the one displayed on screen.
|
||||
|
||||
If (onscreen = true), this function tries to send the pixels that are
|
||||
currently visible on-screen, with the following heuristic:
|
||||
|
||||
* If there is only one VRAM, the VRAM contents are used in the hope they
|
||||
haven't changed since the last frame was presented with dupdate().
|
||||
* If there are two VRAMs (on fx-CG 50 or using the gray engine) the contents
|
||||
of the VRAM not currently being draw to are sent.
|
||||
|
||||
This function does not read pixels directly from the display, as this is
|
||||
usually slow and currently not even implemented. */
|
||||
If `onscreen` is set and there are two VRAMs (on fx-CG or when using the
|
||||
gray engine on fx-9860G), sends a copy of the other VRAM. This is a bit more
|
||||
intuitive when taking a screenshot of the last shown image as a result of a
|
||||
key press. Note that this function never reads pixels directly from the
|
||||
display (it's usually slow and currently not even implemented). */
|
||||
void usb_fxlink_screenshot(bool onscreen);
|
||||
|
||||
#ifdef FX9860G
|
||||
/* usb_fxlink_screenshot_gray(): Take a gray screenshot on fx-9860G
|
||||
|
||||
This function is similar to usb_fxlink_screenshot(), but it takes a gray
|
||||
screenshot. It depends on the gray engine so if you use your add-in will
|
||||
automatically have the gray engine, that's why it's separate. */
|
||||
void usb_fxlink_screenshot_gray(bool onscreen);
|
||||
#endif
|
||||
|
||||
/* usb_fxlink_text(): Send raw text
|
||||
|
||||
This function sends a string with the "text" type, which fxlink prints on
|
||||
the terminal. It will send a full fxlink message (with a commit), which is
|
||||
inefficient if there is a lot of text to send. For better speed, send the
|
||||
message manually by filling the header and doing the writes and commit
|
||||
manually.
|
||||
|
||||
This function sends the text by blocks of 4 bytes or 2 bytes when alignment
|
||||
and size allow, and 1 byte otherwise. If size is 0, strlen(text) is used. */
|
||||
void usb_fxlink_text(char const *text, int size);
|
||||
|
||||
/* usb_fxlink_videocapture(): Send a frame for a video recording
|
||||
|
||||
This function is essentially the same as usb_fxlink_screenshot(). It sends a
|
||||
|
@ -180,11 +75,130 @@ void usb_fxlink_text(char const *text, int size);
|
|||
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. */
|
||||
/* Similar to usb_fxlink_screenshot(), but takes a gray screenshot if the gray
|
||||
engine is currently running. */
|
||||
void usb_fxlink_screenshot_gray(bool onscreen);
|
||||
|
||||
/* Like usb_fxlink_videocapture(), but uses VRAM data from the gray engine. */
|
||||
void usb_fxlink_videocapture_gray(bool onscreen);
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Receiving messages
|
||||
//---
|
||||
|
||||
/* usb_fxlink_handle_messages(): Process and return incoming messages
|
||||
|
||||
This function processes incoming messages. It handles some types of messages
|
||||
internally (eg. fxlink commands) and returns the others to the caller by
|
||||
loading the provided header structure and returning true.
|
||||
|
||||
When this function returns true, the caller should read the message contents
|
||||
on the interface's input pipe. Usually every read will return just a portion
|
||||
of the message (eg. 2048 bytes) so the contents should be read with a loop.
|
||||
|
||||
As long as this function returns true it should be called againt to process
|
||||
any other messages. When there is no more messages to process, this function
|
||||
returns false. It is important to call this function regularly from the main
|
||||
thread when quick responses are expected. */
|
||||
bool usb_fxlink_handle_messages(struct usb_fxlink_header *header);
|
||||
|
||||
/* usb_fxlink_drop_transaction(): Drop incoming data until end of transaction
|
||||
|
||||
When a message arrives on the USB port incoming data *must* be processed in
|
||||
order for the pipe to accept any further data in the future. This function
|
||||
can be used in error situations to drop data until the end of the
|
||||
transaction (which is usually the end of the message, at least with the
|
||||
fxSDK's implementation of fxlink). */
|
||||
void usb_fxlink_drop_transaction(void);
|
||||
|
||||
/* usb_fxlink_set_notifier(): Set up a notification for incoming messages
|
||||
|
||||
Registers the provided function to be called when data arrives on the fxlink
|
||||
input pipe. This signals that usb_fxlink_handle_messages() should be called
|
||||
later from the main thread. Pass NULL to disable the notification. */
|
||||
void usb_fxlink_set_notifier(void (*notifier_function)(void));
|
||||
|
||||
//---
|
||||
// Direct bulk access
|
||||
//
|
||||
// The following functions can be used to access the bulk pipes directly. They
|
||||
// provide the pipe numbers, which can be passed for instance to usb_write
|
||||
// functions.
|
||||
//---
|
||||
|
||||
/* usb_ff_bulk_output(): Pipe number for calculator -> host communication */
|
||||
int usb_ff_bulk_output(void);
|
||||
|
||||
/* usb_ff_bulk_input(): Pipe number for host -> calculator communication */
|
||||
int usb_ff_bulk_input(void);
|
||||
|
||||
//---
|
||||
// Construction and analysis of fxlink protocol messages
|
||||
//
|
||||
// fxlink messages consist of a simple header followed by message contents
|
||||
// (sometimes with a subheader). In addition to the functions above, it is
|
||||
// possible to craft custom fxlink messages and send them manually.
|
||||
//
|
||||
// To send a message manually, simple write an fxlink header to the output
|
||||
// pipe, followed by the contents. The message can be built from any number of
|
||||
// writes to the pipe. After the last write, commit the pipe.
|
||||
//---
|
||||
|
||||
/* usb_fxlink_header_t: Message header for fxlink
|
||||
|
||||
Messages are categorized with an (application, type) pair; both are UTF-8
|
||||
strings of up to 16 bytes (NUL not required). The application name "fxlink"
|
||||
is reserved for built-in types of messages. Other names can be used freely,
|
||||
and fxlink's interactive mode on the host side will forward messages to
|
||||
external programs based on the application name.
|
||||
|
||||
The size of the data to be transferred must be specified upfront, but this
|
||||
restriction might be lifted in the future. As with the rest of the USB
|
||||
protocol, all the integer are encoded as *little-endian*. */
|
||||
typedef struct usb_fxlink_header
|
||||
{
|
||||
/* Protocol version = 0x00000100 */
|
||||
uint32_t version;
|
||||
/* Size of the data to transfer (excluding this header) */
|
||||
uint32_t size;
|
||||
/* Size of individual transfers (related to the size of the FIFO) */
|
||||
uint32_t transfer_size;
|
||||
/* Application name and message type, UTF-8 (need not be zero-terminated) */
|
||||
char application[16];
|
||||
char type[16];
|
||||
|
||||
} usb_fxlink_header_t;
|
||||
|
||||
/* usb_fxlink_fill_header(): Fill an fxlink message header
|
||||
|
||||
Fills an fxlink header's fields with the provided. You need to specify the
|
||||
exact amount of data that the fxlink message will contain. Returns false if
|
||||
parameters are invalid or don't fit the fields. */
|
||||
bool usb_fxlink_fill_header(usb_fxlink_header_t *header,
|
||||
char const *application, char const *type, uint32_t data_size);
|
||||
|
||||
/* Subheader for the fxlink built-in "image" type */
|
||||
typedef struct
|
||||
{
|
||||
/* Image size; storage is row-major */
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
/* Pixel format, see below */
|
||||
int pixel_format;
|
||||
|
||||
} usb_fxlink_image_t;
|
||||
|
||||
/* Pixel formats */
|
||||
enum {
|
||||
/* Image is an array of *big-endian* uint16_t with RGB565 format */
|
||||
USB_FXLINK_IMAGE_RGB565 = 0,
|
||||
/* Image is an array of bits in mono format */
|
||||
USB_FXLINK_IMAGE_MONO,
|
||||
/* Image is two consecutive mono arrays, one for light, one for dark */
|
||||
USB_FXLINK_IMAGE_GRAY,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -10,9 +10,11 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/std/endian.h>
|
||||
#include <gint/defs/timeout.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/config.h>
|
||||
#include <stdarg.h>
|
||||
#include <endian.h>
|
||||
|
||||
/* See "Interfaces to communicate with USB transfers" below for details */
|
||||
typedef struct usb_interface usb_interface_t;
|
||||
|
@ -27,26 +29,35 @@ enum {
|
|||
/* There are no interfaces */
|
||||
USB_OPEN_NO_INTERFACE = 1,
|
||||
/* There are more interfaces than supported (16) */
|
||||
USB_OPEN_TOO_MANY_INTERFACES,
|
||||
USB_OPEN_TOO_MANY_INTERFACES = -2,
|
||||
/* There are not enough endpoint numbers for every interface, or there
|
||||
are not enough pipes to set them up */
|
||||
USB_OPEN_TOO_MANY_ENDPOINTS,
|
||||
USB_OPEN_TOO_MANY_ENDPOINTS = -3,
|
||||
/* There is not enough FIFO memory to use the requested buffer sizes */
|
||||
USB_OPEN_NOT_ENOUGH_MEMORY,
|
||||
USB_OPEN_NOT_ENOUGH_MEMORY = -4,
|
||||
/* Information is missing, such as buffer size for some endpoints */
|
||||
USB_OPEN_MISSING_DATA,
|
||||
USB_OPEN_MISSING_DATA = -5,
|
||||
/* Invalid parameters: bad endpoint numbers, bad buffer sizes... */
|
||||
USB_OPEN_INVALID_PARAMS,
|
||||
USB_OPEN_INVALID_PARAMS = -6,
|
||||
/* USB interfaces are already opened */
|
||||
USB_OPEN_ALREADY_OPEN = -7,
|
||||
|
||||
/* This pipe is busy (returned by usb_write_async()) */
|
||||
USB_WRITE_BUSY,
|
||||
/* Both FIFO controlles are busy, none is available to transfer */
|
||||
USB_WRITE_NOFIFO,
|
||||
/* General timeout for a sync_timeout call */
|
||||
USB_TIMEOUT = -8,
|
||||
/* This pipe is busy with another call */
|
||||
USB_BUSY = -9,
|
||||
|
||||
/* This pipe is busy (returned by usb_commit_async()) */
|
||||
USB_COMMIT_BUSY,
|
||||
/* Both FIFO controllers are busy, none is available to transfer */
|
||||
USB_WRITE_NOFIFO = -10,
|
||||
/* This pipe has no ongoing transfer to commit */
|
||||
USB_COMMIT_INACTIVE,
|
||||
USB_COMMIT_INACTIVE = -11,
|
||||
/* This pipe is currently not receiving any data */
|
||||
USB_READ_IDLE = -12,
|
||||
/* No FIFO controller is available */
|
||||
USB_READ_NOFIFO = -13,
|
||||
|
||||
/* (Internal codes) */
|
||||
USB_ERROR_ZERO_LENGTH = -100,
|
||||
};
|
||||
|
||||
/* usb_open(): Open the USB link
|
||||
|
@ -129,8 +140,12 @@ struct usb_interface {
|
|||
/* Answer class-specific SETUP requests */
|
||||
/* TODO */
|
||||
|
||||
/* Receive data from an endpoint */
|
||||
/* TODO */
|
||||
/* Notification that an endpoint has data to be read. This function is
|
||||
called frequently when data is being transmitted; the particular
|
||||
timings depend on low-level details. The notification should be
|
||||
passed down to cause the main thread to read later. Do not read in
|
||||
the notification function! */
|
||||
void (*notify_read)(int endpoint);
|
||||
};
|
||||
|
||||
/* usb_interface_endpoint_t: Parameters for an interface endpoint
|
||||
|
@ -169,23 +184,22 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
|
|||
|
||||
/* usb_write_sync(): Synchronously write to a USB pipe
|
||||
|
||||
This functions writes (size) bytes of (data) into the specified pipe, by
|
||||
units of (unit_size) bytes. The unit size must be 1, 2 or 4, and both (data)
|
||||
and (size) must be multiples of the unit size. In general, you should try to
|
||||
use the largest possible unit size, as it will be much faster. In a sequence
|
||||
of writes that concludes with a commit, all the writes must use the same
|
||||
unit size.
|
||||
This functions writes (size) bytes of (data) into the specified pipe. If the
|
||||
data fits into the pipe, this function returns right away, and the data is
|
||||
*not* transmitted. Otherwise, data is written until the pipe is full, at
|
||||
which point it is automatically transmitted. After the transfer, this
|
||||
function resumes writing, returning only once everything is written. Even
|
||||
then the last bytes will still not have been transmitted, to allow for other
|
||||
writes to follow. After the last write in a sequence, use usb_commit_sync()
|
||||
or usb_commit_async() to transmit the last bytes.
|
||||
|
||||
If the data fits into the pipe, this function returns right away, and the
|
||||
data is *not* transmitted. Otherwise, data is written until the pipe is
|
||||
full, at which point it is automatically transmitted. After the transfer,
|
||||
this function resumes writing, returning only once everything is written.
|
||||
Even then the last bytes will still not have been transmitted, to allow for
|
||||
other writes to follow. After the last write in a sequence, use
|
||||
usb_commit_sync() or usb_commit_async() to transmit the last bytes.
|
||||
|
||||
If (use_dma=true), the write is performed with the DMA instead of the CPU,
|
||||
which is generally faster.
|
||||
If (use_dma=true), the write is performed with the DMA instead of the CPU.
|
||||
This requires at least 4-byte alignment on:
|
||||
1. The input data;
|
||||
2. The size of this write;
|
||||
3. The amount of data previously written to the pipe not yet committed.
|
||||
This is because using the DMA does not allow any insertion of CPU logic to
|
||||
handle unaligned stuff.
|
||||
|
||||
This function will use a FIFO controller to access the pipe. The FIFO
|
||||
controller will be reserved for further writes until the contents of the
|
||||
|
@ -198,13 +212,15 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
|
|||
waits for the ressources to become available then proceeds normally.
|
||||
|
||||
@pipe Pipe to write into
|
||||
@data Source data (unit_size-aligned)
|
||||
@size Size of source (multiple of unit_size)
|
||||
@unit_size FIFO access size (must be 1, 2, or 4)
|
||||
@data Source data
|
||||
@size Size of source
|
||||
@dma Whether to use the DMA to perform the write
|
||||
-> Returns an error code (0 on success). */
|
||||
int usb_write_sync(int pipe, void const *data, int size, int unit_size,
|
||||
bool use_dma);
|
||||
int usb_write_sync(int pipe, void const *data, int size, bool use_dma);
|
||||
|
||||
/* usb_write_sync_timeout(): Synchronously write, with a timeout */
|
||||
int usb_write_sync_timeout(int pipe, void const *data, int size,
|
||||
bool use_dma, timeout_t const *timeout);
|
||||
|
||||
/* usb_write_async(): Asynchronously write to a USB pipe
|
||||
|
||||
|
@ -226,14 +242,13 @@ int usb_write_sync(int pipe, void const *data, int size, int unit_size,
|
|||
is idle and USB_WRITE_BUSY otherwise.
|
||||
|
||||
@pipe Pipe to write into
|
||||
@data Source data (unit_size-aligned)
|
||||
@size Size of source (multiple of unit_size)
|
||||
@unit_size FIFO access size (must be 1, 2, or 4)
|
||||
@data Source data
|
||||
@size Size of source
|
||||
@dma Whether to use the DMA to perform the write
|
||||
@callback Optional callback to invoke when the write completes
|
||||
-> Returns an error code (0 on success). */
|
||||
int usb_write_async(int pipe, void const *data, int size, int unit_size,
|
||||
bool use_dma, gint_call_t callback);
|
||||
int usb_write_async(int pipe, void const *data, int size, bool use_dma,
|
||||
gint_call_t callback);
|
||||
|
||||
/* usb_commit_sync(): Synchronously commit a write
|
||||
|
||||
|
@ -241,6 +256,9 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size,
|
|||
transfers whatever data is left, and returns when the transfer completes. */
|
||||
void usb_commit_sync(int pipe);
|
||||
|
||||
/* usb_commit_sync_timeout(): Synchronously commit a write, with timeout */
|
||||
int usb_commit_sync_timeout(int pipe, timeout_t const *timeout);
|
||||
|
||||
/* usb_commit_async(): Asynchronously commit a write
|
||||
|
||||
This function commits the specified pipe, causing the pipe to transfer
|
||||
|
@ -255,23 +273,142 @@ void usb_commit_sync(int pipe);
|
|||
of the remaining data completes. */
|
||||
int usb_commit_async(int pipe, gint_call_t callback);
|
||||
|
||||
/* usb_read_sync(): Synchronously read from a USB pipe
|
||||
|
||||
This function waits for data to become available on the specified `pipe`,
|
||||
and then reads up to `size` bytes into `data`. It is "synchronized" with USB
|
||||
transactions on the pipe in the following ways:
|
||||
|
||||
1. If there is no active transaction, it waits for one to arrive.
|
||||
2. If the active transaction has no data left to read, it is skipped.
|
||||
3. If the transaction's data is completely read, the transaction is closed
|
||||
even if `data` is full. (See usb_read_async() for the alternative.)
|
||||
|
||||
Basically usb_read_sync() returns the next `size` bytes of data that the
|
||||
calculator receives on the pipe, with a single exception: it doesn't read
|
||||
across transactions, so if the active transaction has 100 bytes left and you
|
||||
request 200, you will only get 100. usb_read_sync() only ever returns 0
|
||||
bytes if there is an empty transaction, which is rare.
|
||||
|
||||
Because this is a blocking function, a call to usb_read_sync() will *FREEZE*
|
||||
if there is no data to read on the pipe and the host doesn't send any. In
|
||||
addition, it's not possible to detect how much data is left to read with
|
||||
usb_read_sync()'s interface. If you want to avoid or debug a freeze, use
|
||||
use usb_read_async() or set a timeout with usb_read_sync_timeout(). If you
|
||||
want to read a transaction until the end without knowing its size in
|
||||
advance, use usb_read_async().
|
||||
|
||||
If `use_dma=true`, uses the DMA for transfers. This requires 4-byte
|
||||
alignment on the buffer, size, and number of bytes previously read in the
|
||||
current transaction.
|
||||
|
||||
Returns the number of bytes read or a negative error code. */
|
||||
int usb_read_sync(int pipe, void *data, int size, bool use_dma);
|
||||
|
||||
/* usb_read_sync_timeout(): Synchronous read with a timeout */
|
||||
int usb_read_sync_timeout(int pipe, void *data, int size, bool use_dma,
|
||||
timeout_t const *timeout);
|
||||
|
||||
/* Flags for usb_read_async(), see below. */
|
||||
enum {
|
||||
/* Use the DMA for data transfer (requires full 4-byte alignment). */
|
||||
USB_READ_USE_DMA = 0x01,
|
||||
/* Ignore reads of 0 bytes from exhausted transactions. */
|
||||
USB_READ_IGNORE_ZEROS = 0x02,
|
||||
/* Close transactions when exhausted even by non-partial reads. */
|
||||
USB_READ_AUTOCLOSE = 0x04,
|
||||
/* Wait for the read to finish before returning. */
|
||||
USB_READ_WAIT = 0x08,
|
||||
/* Block for a new transaction if none is currenty active. */
|
||||
USB_READ_BLOCK = 0x10,
|
||||
};
|
||||
|
||||
/* usb_read_async(): Asynchronously read from a USB pipe
|
||||
|
||||
This function is similar to usb_read_sync() but it is asynchronous, ie. it
|
||||
returns after starting the read and then runs in the background. Without
|
||||
options, usb_read_async() is guaranteed to return quickly without waiting
|
||||
for communications. The read itself also never blocks unless there is a
|
||||
disagreement with the host on the size of transactions.
|
||||
|
||||
usb_read_async() is also slightly lower-level than usb_read_sync(). In
|
||||
particular, it only closes transactions when the data is fully read *and*
|
||||
the user buffer is not full. If the user requests exactly the number of
|
||||
bytes left in the transaction, the entire contents will be read but the
|
||||
transaction will be left in an "active with 0 bytes left" state. Only on the
|
||||
next read will the transaction be closed.
|
||||
|
||||
This behavior is designed so that a read closes a transaction if and only if
|
||||
it returns fewer bytes than requested, which is useful for detecting the end
|
||||
of transfers when their size isn't known in advance.
|
||||
|
||||
This function returns a preliminary error code. If it is zero, then the
|
||||
callback will be invoked later with *rc set to the final return value of the
|
||||
read, which is itself either an error (< 0) or the number of bytes read.
|
||||
|
||||
Due to its low-level nature, raw usb_read_async() is a bit impractical to
|
||||
use and almost always requires repeated calls and callback waits. The
|
||||
following flags are provided to make it more convenient by incorporating
|
||||
features of usb_read_sync():
|
||||
|
||||
* USB_READ_IGNORE_ZEROS causes usb_read_async() to ignore reads of 0 bytes
|
||||
from transactions that were exhausted but not closed.
|
||||
* USB_READ_AUTOCLOSE causes usb_read_async() to always close transactions
|
||||
when exhausted even if the read is not partial. This is useful when the
|
||||
size of the transaction is in fact known in advance.
|
||||
* USB_READ_WAIT causes usb_read_async() to wait for the end of the transfer
|
||||
before returning. Unless there is a driver bug or USB_READ_BLOCK is also
|
||||
set, this cannot cause the function to freeze since the read will always
|
||||
complete in finite time (returning USB_READ_IDLE if no transaction is
|
||||
active). When USB_READ_WAIT it set, `callback` and `rc` are ignored; the
|
||||
callback is not invoked, and the status of the entire read is returned.
|
||||
* USB_READ_BLOCK causes usb_read_async() to wait for a transaction if none
|
||||
is active. This can stall the transfer indefinitely if the host doesn't
|
||||
transmit any data, and freeze the call entirely if USB_READ_WAIT is set.
|
||||
This option really makes usb_read_async() a synchronous function.
|
||||
* A timeout is supported (pass NULL to ignore).
|
||||
|
||||
With all options enabled, usb_read_async() becomes functionally identical to
|
||||
usb_read_sync_timeout(). */
|
||||
int usb_read_async(int pipe, void *data, int size, int flags, int *rc,
|
||||
timeout_t const *timeout, gint_call_t callback);
|
||||
|
||||
/* usb_read_cancel(): Cancel an asynchronous read
|
||||
|
||||
Once started, an async read will run in the background and keep writing to
|
||||
the provided buffer. This function cancels the operation so that no further
|
||||
writes to the buffer are made and the associated memory can be safely
|
||||
deallocated. */
|
||||
void usb_read_cancel(int pipe);
|
||||
|
||||
//---
|
||||
// USB debugging log
|
||||
// USB debugging functions
|
||||
//---
|
||||
|
||||
/* usb_set_log(): Set the logging function for the USB driver
|
||||
|
||||
The USB driver can produce logs, which are mostly useful to troubleshoot
|
||||
problems and add new protocols. The logging is disabled by default but can
|
||||
be enabled by specifying this function.
|
||||
|
||||
It is up to you whether to store that in a buffer, rotate logs, send them to
|
||||
storage memory or a console on the PC. */
|
||||
/* usb_set_log(): Set the logging function for the USB driver */
|
||||
void usb_set_log(void (*logger)(char const *format, va_list args));
|
||||
|
||||
/* usb_get_log(): Get the logging function */
|
||||
void (*usb_get_log(void))(char const *format, va_list args);
|
||||
/* usb_log(): Send a message to the USB log */
|
||||
void usb_log(char const *format, ...);
|
||||
|
||||
/* usb_set_trace(): Set the tracing function for the USB driver
|
||||
The function is called atomically, thus cannot be interrupted, therefore it
|
||||
is safe to call usb_trace() in interrupt handlers. */
|
||||
void usb_set_trace(void (*tracer)(char const *message));
|
||||
/* usb_get_trace(): Get the tracing function */
|
||||
void (*usb_get_trace(void))(char const *message);
|
||||
/* usb_trace(): Trace the current state of the driver */
|
||||
void usb_trace(char const *message);
|
||||
|
||||
#ifdef GINT_USB_DEBUG
|
||||
#define USB_LOG(...) usb_log(__VA_ARGS__)
|
||||
#define USB_TRACE(...) usb_trace(__VA_ARGS__)
|
||||
#else
|
||||
#define USB_LOG(...) do {} while(0)
|
||||
#define USB_TRACE(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Standard descriptors
|
||||
//---
|
||||
|
|
|
@ -27,9 +27,8 @@ 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:
|
||||
|
@ -41,35 +40,23 @@ static void sh7705_probe(void)
|
|||
int ckio = xtal * pll2;
|
||||
|
||||
/* This signal is multiplied by the PLL1 circuit */
|
||||
int pll1 = CPG.FRQCR.STC + 1;
|
||||
int pll1 = SH7705_CPG.FRQCR.STC + 1;
|
||||
|
||||
/* Iphi and Pphi have dividers (Bphi is always equal to CKIO) */
|
||||
int idiv = CPG.FRQCR.IFC;
|
||||
int pdiv = CPG.FRQCR.PFC;
|
||||
|
||||
/* Fill in the setting structure */
|
||||
/* Fill in the setting structure. Iϕ and Pϕ have dividers, while Bϕ is
|
||||
always equal to CKIO. */
|
||||
freq.PLL1 = pll1;
|
||||
freq.PLL2 = pll2;
|
||||
freq.Bphi_div = 1;
|
||||
freq.Iphi_div = idiv + 1;
|
||||
freq.Pphi_div = pdiv + 1;
|
||||
|
||||
/* Deduce the frequency of the main clocks. This value is ckio/3 */
|
||||
int ckio_3 = 9830400;
|
||||
|
||||
/* Exchange the setting values 2 and 3 (corresponding to /3 and /4)
|
||||
This means that /1, /2, /4 are now 0, 1, 2, which is perfect for a
|
||||
quick bit shift */
|
||||
idiv = idiv ^ (idiv >> 1);
|
||||
pdiv = pdiv ^ (pdiv >> 1);
|
||||
freq.Iphi_div = SH7705_CPG.FRQCR.IFC + 1;
|
||||
freq.Pphi_div = SH7705_CPG.FRQCR.PFC + 1;
|
||||
|
||||
/* Deduce the frequency of the main clocks */
|
||||
freq.CKIO_f = ckio;
|
||||
freq.Bphi_f = ckio;
|
||||
freq.Iphi_f = (idiv == 3) ? ckio_3 : ckio >> idiv;
|
||||
freq.Pphi_f = (pdiv == 3) ? ckio_3 : ckio >> pdiv;
|
||||
freq.Iphi_f = (ckio * pll1) / freq.Iphi_div;
|
||||
freq.Pphi_f = (ckio * pll1) / freq.Pphi_div;
|
||||
}
|
||||
|
||||
#undef CPG
|
||||
#endif /* FX9860G and platform-agnostic */
|
||||
|
||||
//---
|
||||
|
@ -82,7 +69,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 +80,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,18 +102,13 @@ static void sh7305_probe(void)
|
|||
|
||||
#undef CPG
|
||||
|
||||
|
||||
//---
|
||||
// Initialization
|
||||
//---
|
||||
|
||||
static void configure(void)
|
||||
void cpg_compute_freq(void)
|
||||
{
|
||||
/* Disable spread spectrum in SSGSCR */
|
||||
if(isSH4())
|
||||
{
|
||||
SH7305_CPG.SSCGCR.SSEN = 0;
|
||||
}
|
||||
|
||||
/* This avoids warnings about sh7705_probe() being undefined when
|
||||
building for fxcg50 */
|
||||
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
|
||||
|
@ -135,18 +117,31 @@ static void configure(void)
|
|||
sh7305_probe();
|
||||
}
|
||||
|
||||
static void configure(void)
|
||||
{
|
||||
/* Disable spread spectrum in SSGSCR */
|
||||
if(isSH4())
|
||||
SH7305_CPG.SSCGCR.SSEN = 0;
|
||||
|
||||
cpg_compute_freq();
|
||||
}
|
||||
|
||||
//---
|
||||
// State and driver metadata
|
||||
//---
|
||||
|
||||
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,514 @@
|
|||
//---
|
||||
// gint:cpg:overclock - Clock speed control
|
||||
//
|
||||
// Most of the data in this file has been reused from Sentaro21's Ftune and
|
||||
// Ptune utilities, which have long been the standard for overclocking CASIO
|
||||
// calculators.
|
||||
// See: http://pm.matrix.jp/ftune2e.html
|
||||
//
|
||||
// SlyVTT also contributed early testing on both the fx-CG 10/20 and fx-CG 50.
|
||||
//---
|
||||
|
||||
#include <gint/clock.h>
|
||||
#include <gint/gint.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/mpu/cpg.h>
|
||||
#include <gint/mpu/bsc.h>
|
||||
#include <gint/mpu/wdt.h>
|
||||
|
||||
//---
|
||||
// Low-level clock speed access
|
||||
//---
|
||||
|
||||
#define SH7305_SDMR3_CL2 ((volatile uint8_t *)0xFEC15040)
|
||||
#define SH7305_SDMR3_CL3 ((volatile uint8_t *)0xFEC15060)
|
||||
|
||||
//---
|
||||
// Predefined clock speeds
|
||||
//---
|
||||
|
||||
/* SH7305 CPG */
|
||||
#define SH4_PLL_32x 0b011111
|
||||
#define SH4_PLL_26x 0b011001
|
||||
#define SH4_PLL_16x 0b001111
|
||||
#define SH4_DIV_2 0
|
||||
#define SH4_DIV_4 1
|
||||
#define SH4_DIV_8 2
|
||||
#define SH4_DIV_16 3
|
||||
#define SH4_DIV_32 4
|
||||
|
||||
/* SH7705-like CPG */
|
||||
#define SH3_PLL_1x 0
|
||||
#define SH3_PLL_2x 1
|
||||
#define SH3_PLL_3x 2
|
||||
#define SH3_PLL_4x 3
|
||||
#define SH3_DIV_1 0
|
||||
#define SH3_DIV_2 1
|
||||
#define SH3_DIV_3 2
|
||||
#define SH3_DIV_4 3
|
||||
|
||||
void cpg_get_overclock_setting(struct cpg_overclock_setting *s)
|
||||
{
|
||||
if(isSH3()) {
|
||||
s->FLLFRQ = -1;
|
||||
s->FRQCR = SH7705_CPG.FRQCR.word;
|
||||
|
||||
s->CS0BCR = SH7705_BSC.CS0BCR.lword;
|
||||
s->CS0WCR = SH7705_BSC.CS0WCR.lword;
|
||||
s->CS2BCR = SH7705_BSC.CS2BCR.lword;
|
||||
s->CS2WCR = SH7705_BSC.CS2WCR.lword;
|
||||
s->CS3BCR = SH7705_BSC.CS3BCR.lword;
|
||||
s->CS3WCR = SH7705_BSC.CS3WCR.lword;
|
||||
s->CS5aBCR = SH7705_BSC.CS5ABCR.lword;
|
||||
s->CS5aWCR = SH7705_BSC.CS5AWCR.lword;
|
||||
}
|
||||
else {
|
||||
s->FLLFRQ = SH7305_CPG.FLLFRQ.lword;
|
||||
s->FRQCR = SH7305_CPG.FRQCR.lword;
|
||||
|
||||
s->CS0BCR = SH7305_BSC.CS0BCR.lword;
|
||||
s->CS0WCR = SH7305_BSC.CS0WCR.lword;
|
||||
s->CS2BCR = SH7305_BSC.CS2BCR.lword;
|
||||
s->CS2WCR = SH7305_BSC.CS2WCR.lword;
|
||||
s->CS3BCR = SH7305_BSC.CS3BCR.lword;
|
||||
s->CS3WCR = SH7305_BSC.CS3WCR.lword;
|
||||
s->CS5aBCR = SH7305_BSC.CS5ABCR.lword;
|
||||
s->CS5aWCR = SH7305_BSC.CS5AWCR.lword;
|
||||
}
|
||||
}
|
||||
|
||||
static void cpg_low_level_set_setting(struct cpg_overclock_setting const *s)
|
||||
{
|
||||
if(isSH3()) {
|
||||
SH7705_WDT.WTCNT.WRITE = 0;
|
||||
SH7705_WDT.WTCSR.WRITE = 0x65;
|
||||
SH7705_CPG.FRQCR.word = s->FRQCR | 0x1000;
|
||||
SH7705_BSC.CS0BCR.lword = s->CS0BCR;
|
||||
SH7705_BSC.CS0WCR.lword = s->CS0WCR;
|
||||
SH7705_BSC.CS2BCR.lword = s->CS2BCR;
|
||||
SH7705_BSC.CS2WCR.lword = s->CS2WCR;
|
||||
SH7705_BSC.CS3BCR.lword = s->CS3BCR;
|
||||
SH7705_BSC.CS3WCR.lword = s->CS3WCR;
|
||||
SH7705_BSC.CS5ABCR.lword = s->CS5aBCR;
|
||||
SH7705_BSC.CS5AWCR.lword = s->CS5aWCR;
|
||||
}
|
||||
else {
|
||||
SH7305_BSC.CS0WCR.WR = 11; /* 18 cycles */
|
||||
|
||||
SH7305_CPG.FLLFRQ.lword = s->FLLFRQ;
|
||||
SH7305_CPG.FRQCR.lword = s->FRQCR;
|
||||
SH7305_CPG.FRQCR.KICK = 1;
|
||||
while(SH7305_CPG.LSTATS != 0) {}
|
||||
|
||||
SH7305_BSC.CS0BCR.lword = s->CS0BCR;
|
||||
SH7305_BSC.CS0WCR.lword = s->CS0WCR;
|
||||
SH7305_BSC.CS2BCR.lword = s->CS2BCR;
|
||||
SH7305_BSC.CS2WCR.lword = s->CS2WCR;
|
||||
SH7305_BSC.CS3BCR.lword = s->CS3BCR;
|
||||
SH7305_BSC.CS3WCR.lword = s->CS3WCR;
|
||||
|
||||
if(SH7305_BSC.CS3WCR.A3CL == 1)
|
||||
*SH7305_SDMR3_CL2 = 0;
|
||||
else
|
||||
*SH7305_SDMR3_CL3 = 0;
|
||||
|
||||
SH7305_BSC.CS5ABCR.lword = s->CS5aBCR;
|
||||
SH7305_BSC.CS5AWCR.lword = s->CS5aWCR;
|
||||
}
|
||||
}
|
||||
|
||||
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s)
|
||||
{
|
||||
uint32_t old_Pphi = clock_freq()->Pphi_f;
|
||||
|
||||
/* Wait for asynchronous tasks to complete */
|
||||
gint_world_sync();
|
||||
|
||||
/* Disable interrupts during the change */
|
||||
cpu_atomic_start();
|
||||
|
||||
/* Load the clock settings */
|
||||
cpg_low_level_set_setting(s);
|
||||
|
||||
/* Determine the change in frequency for Pϕ and recompute CPG data */
|
||||
cpg_compute_freq();
|
||||
uint32_t new_Pphi = clock_freq()->Pphi_f;
|
||||
|
||||
/* Update timers' TCNT and TCOR to match the new clock speed */
|
||||
void timer_rescale(uint32_t old_Pphi, uint32_t new_Pphi);
|
||||
timer_rescale(old_Pphi, new_Pphi);
|
||||
|
||||
cpu_atomic_end();
|
||||
}
|
||||
|
||||
#ifdef FX9860G
|
||||
|
||||
static struct cpg_overclock_setting const settings_fx9860g_sh3[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FRQCR = 0x1001,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FRQCR = (SH3_PLL_2x<<8)+(SH3_DIV_1<<4)+SH3_DIV_2,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FRQCR = (SH3_PLL_3x<<8)+(SH3_DIV_1<<4)+SH3_DIV_3,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FRQCR = (SH3_PLL_4x<<8)+(SH3_DIV_1<<4)+SH3_DIV_4,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FRQCR = (SH3_PLL_4x<<8)+(SH3_DIV_1<<4)+SH3_DIV_4,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000000C0,
|
||||
.CS2WCR = 0x000100C0,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting const settings_fx9860g_sh4[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = 0x0F202203,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000005C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x24920200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x14900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000302C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting const settings_g35pe2[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = 0x0F112213,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x000001C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031340 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x24920200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031B40 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x14900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031B40 },
|
||||
};
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
||||
#ifdef FXCG50
|
||||
|
||||
static struct cpg_overclock_setting const settings_prizm[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = 0x0F102203,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_16<<12)+(SH4_DIV_16<<8)+SH4_DIV_32,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x000100C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_32,
|
||||
.CS0BCR = 0x24900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x000002C0,
|
||||
.CS2WCR = 0x000201C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_32,
|
||||
.CS0BCR = 0x44900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x00000440,
|
||||
.CS2WCR = 0x00040340,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x34900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000402C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting const settings_fxcg50[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = 0x0F011112,
|
||||
.CS0BCR = 0x36DA0400,
|
||||
.CS2BCR = 0x36DA3400,
|
||||
.CS3BCR = 0x36DB4400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000340,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000240,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x000002C0,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000440,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
};
|
||||
|
||||
#endif /* FXCG50 */
|
||||
|
||||
static struct cpg_overclock_setting const *get_settings(void)
|
||||
{
|
||||
#ifdef FX9860G
|
||||
if(gint[HWCALC] == HWCALC_FX9860G_SH3)
|
||||
return settings_fx9860g_sh3;
|
||||
if(gint[HWCALC] == HWCALC_FX9860G_SH4)
|
||||
return settings_fx9860g_sh4;
|
||||
if(gint[HWCALC] == HWCALC_G35PE2)
|
||||
return settings_g35pe2;
|
||||
#endif /* FX9860G */
|
||||
|
||||
#ifdef FXCG50
|
||||
if(gint[HWCALC] == HWCALC_PRIZM)
|
||||
return settings_prizm;
|
||||
if(gint[HWCALC] == HWCALC_FXCG50)
|
||||
return settings_fxcg50;
|
||||
#endif /* FXCG50 */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int clock_get_speed(void)
|
||||
{
|
||||
struct cpg_overclock_setting const *settings = get_settings();
|
||||
if(!settings)
|
||||
return CLOCK_SPEED_UNKNOWN;
|
||||
|
||||
if(isSH3()) {
|
||||
for(int i = 0; i < 5; i++) {
|
||||
struct cpg_overclock_setting const *s = &settings[i];
|
||||
|
||||
if(SH7705_CPG.FRQCR.word == (s->FRQCR | 0x1000)
|
||||
&& SH7705_BSC.CS0BCR.lword == s->CS0BCR
|
||||
&& SH7705_BSC.CS2BCR.lword == s->CS2BCR
|
||||
&& SH7705_BSC.CS3BCR.lword == s->CS3BCR
|
||||
&& SH7705_BSC.CS5ABCR.lword == s->CS5aBCR
|
||||
&& SH7705_BSC.CS0WCR.lword == s->CS0WCR
|
||||
&& SH7705_BSC.CS2WCR.lword == s->CS2WCR
|
||||
&& SH7705_BSC.CS3WCR.lword == s->CS3WCR
|
||||
&& SH7705_BSC.CS5AWCR.lword == s->CS5aWCR)
|
||||
return CLOCK_SPEED_F1 + i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(int i = 0; i < 5; i++) {
|
||||
struct cpg_overclock_setting const *s = &settings[i];
|
||||
|
||||
if(SH7305_CPG.FLLFRQ.lword == s->FLLFRQ
|
||||
&& SH7305_CPG.FRQCR.lword == s->FRQCR
|
||||
&& SH7305_BSC.CS0BCR.lword == s->CS0BCR
|
||||
&& SH7305_BSC.CS2BCR.lword == s->CS2BCR
|
||||
&& SH7305_BSC.CS3BCR.lword == s->CS3BCR
|
||||
&& SH7305_BSC.CS5ABCR.lword == s->CS5aBCR
|
||||
&& SH7305_BSC.CS0WCR.lword == s->CS0WCR
|
||||
&& SH7305_BSC.CS2WCR.lword == s->CS2WCR
|
||||
&& SH7305_BSC.CS3WCR.lword == s->CS3WCR
|
||||
&& SH7305_BSC.CS5AWCR.lword == s->CS5aWCR)
|
||||
return CLOCK_SPEED_F1 + i;
|
||||
}
|
||||
}
|
||||
|
||||
return CLOCK_SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
void clock_set_speed(int level)
|
||||
{
|
||||
if(level < CLOCK_SPEED_F1 || level > CLOCK_SPEED_F5)
|
||||
return;
|
||||
if(clock_get_speed() == level)
|
||||
return;
|
||||
|
||||
struct cpg_overclock_setting const *settings = get_settings();
|
||||
if(!settings)
|
||||
return;
|
||||
|
||||
struct cpg_overclock_setting const *s = &settings[level - CLOCK_SPEED_F1];
|
||||
cpg_set_overclock_setting(s);
|
||||
}
|
|
@ -146,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;
|
||||
|
||||
|
@ -159,34 +160,40 @@ static void dma_interrupt_transfer_ended(int channel)
|
|||
if(dma_wait_ics[channel])
|
||||
cpu_csleep_cancel(dma_wait_ics[channel]);
|
||||
|
||||
if(dma_callbacks[channel].function)
|
||||
{
|
||||
gint_call(dma_callbacks[channel]);
|
||||
dma_callbacks[channel] = GINT_CALL_NULL;
|
||||
}
|
||||
gint_call(dma_callbacks[channel]);
|
||||
dma_callbacks[channel] = GINT_CALL_NULL;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Interrupt disabled: spin-wait */
|
||||
if(!ch->CHCR.IE)
|
||||
/* If interrupts are disabled or we don't own the device, spin-wait by
|
||||
checking either for TE to be set (Transfer 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)
|
||||
{
|
||||
while(ch->CHCR.DE && !ch->CHCR.TE) {}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize an interrupt-cancellable sleep, to ensure synchronization */
|
||||
/* Initialize an interrupt-cancellable sleep, to ensure
|
||||
synchronization */
|
||||
cpu_csleep_t ics;
|
||||
cpu_csleep_init(&ics);
|
||||
dma_wait_ics[channel] = &ics;
|
||||
|
@ -199,6 +206,12 @@ void dma_transfer_wait(int channel)
|
|||
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,
|
||||
void const *src, dma_address_t src_mode, void *dst,
|
||||
dma_address_t dst_mode)
|
||||
|
@ -283,10 +296,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)
|
||||
|
@ -347,8 +368,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,
|
||||
|
|
21
src/fs/fs.c
21
src/fs/fs.c
|
@ -2,13 +2,14 @@
|
|||
#include <gint/defs/attributes.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* File descriptor table */
|
||||
static fs_descriptor_t fdtable[FS_FD_MAX] = { 0 };
|
||||
static fs_descriptor_t *fdtable;
|
||||
|
||||
fs_descriptor_t const *fs_get_descriptor(int fd)
|
||||
{
|
||||
if((unsigned)fd >= FS_FD_MAX)
|
||||
if(!fdtable || (unsigned)fd >= FS_FD_MAX)
|
||||
return NULL;
|
||||
if(fdtable[fd].type == NULL)
|
||||
return NULL;
|
||||
|
@ -18,7 +19,7 @@ fs_descriptor_t const *fs_get_descriptor(int fd)
|
|||
|
||||
int fs_create_descriptor(fs_descriptor_t const *data)
|
||||
{
|
||||
if(data->type == NULL)
|
||||
if(!fdtable || data->type == NULL)
|
||||
return -1;
|
||||
|
||||
/* Leave 0/1/2 for stdin, stdout and stderr */
|
||||
|
@ -34,7 +35,7 @@ int fs_create_descriptor(fs_descriptor_t const *data)
|
|||
|
||||
void fs_free_descriptor(int fd)
|
||||
{
|
||||
if((unsigned)fd >= FS_FD_MAX)
|
||||
if(!fdtable || (unsigned)fd >= FS_FD_MAX)
|
||||
return;
|
||||
|
||||
fdtable[fd].type = NULL;
|
||||
|
@ -43,6 +44,10 @@ void fs_free_descriptor(int fd)
|
|||
|
||||
int open_generic(fs_descriptor_type_t *type, void *data, int fd)
|
||||
{
|
||||
if(!fdtable) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
if(!type) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
@ -76,14 +81,17 @@ int open_generic(fs_descriptor_type_t *type, void *data, int fd)
|
|||
|
||||
static fs_descriptor_type_t devnull = {
|
||||
.read = NULL,
|
||||
.pread = NULL,
|
||||
.write = NULL,
|
||||
.lseek = NULL,
|
||||
.close = NULL,
|
||||
};
|
||||
|
||||
GCONSTRUCTOR static void init_standard_streams(void)
|
||||
GCONSTRUCTOR static void init_fs(void)
|
||||
{
|
||||
fdtable = calloc(FS_FD_MAX, sizeof *fdtable);
|
||||
if(!fdtable)
|
||||
return;
|
||||
|
||||
fdtable[STDIN_FILENO].type = &devnull;
|
||||
fdtable[STDIN_FILENO].data = NULL;
|
||||
|
||||
|
@ -92,5 +100,4 @@ GCONSTRUCTOR static void init_standard_streams(void)
|
|||
|
||||
fdtable[STDERR_FILENO].type = &devnull;
|
||||
fdtable[STDERR_FILENO].data = NULL;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,91 +1,144 @@
|
|||
#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 *data, void *buf, size_t size)
|
||||
ssize_t fugue_read(void *data0, void *buf, size_t size)
|
||||
{
|
||||
int fugue_fd = (int)data;
|
||||
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;
|
||||
}
|
||||
|
||||
ssize_t fugue_pread(void *data, void *buf, size_t size, off_t offset)
|
||||
static void *temp_ram(size_t total_size, size_t *block_size)
|
||||
{
|
||||
int fugue_fd = (int)data;
|
||||
for(size_t candidate = 16384; candidate >= 64; candidate >>= 1) {
|
||||
if(candidate > 64 && candidate >= 2 * total_size)
|
||||
continue;
|
||||
|
||||
/* Thanks to the extra argument to BFile_Read(), we can perform this
|
||||
call without knowing the current offset, even on G-III models */
|
||||
int rc = BFile_Read(fugue_fd, buf, size, offset);
|
||||
if(rc < 0) {
|
||||
errno = bfile_error_to_errno(rc);
|
||||
return -1;
|
||||
size_t size = min(candidate, total_size);
|
||||
void *ram = malloc(size);
|
||||
if(ram) {
|
||||
*block_size = size;
|
||||
return ram;
|
||||
}
|
||||
}
|
||||
BFile_Seek(fugue_fd, -size);
|
||||
return rc;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ssize_t fugue_write(void *data, const void *buf, size_t size)
|
||||
ssize_t fugue_write(void *data0, const void *buf, size_t size)
|
||||
{
|
||||
int fugue_fd = (int)data;
|
||||
fugue_fd_t *data = data0;
|
||||
int fugue_fd = data->fd;
|
||||
|
||||
int rc = BFile_Write(fugue_fd, buf, size);
|
||||
if(rc < 0) {
|
||||
errno = bfile_error_to_errno(rc);
|
||||
return -1;
|
||||
/* 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;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
off_t fugue_lseek(void *data, off_t offset, int whence)
|
||||
off_t fugue_lseek(void *data0, off_t offset, int whence)
|
||||
{
|
||||
int fugue_fd = (int)data;
|
||||
fugue_fd_t *data = data0;
|
||||
int fugue_fd = data->fd;
|
||||
|
||||
/* On Graph 35+E II, there is no documented way to know the offset. */
|
||||
if(gint[HWCALC] == HWCALC_G35PE2 && whence == SEEK_CUR) {
|
||||
errno = ENOTSUP;
|
||||
return (off_t)-1;
|
||||
}
|
||||
int filesize = BFile_Size(fugue_fd);
|
||||
|
||||
if(whence == SEEK_CUR)
|
||||
offset += BFile_GetPos(fugue_fd);
|
||||
offset += data->pos;
|
||||
else if(whence == SEEK_END)
|
||||
offset += BFile_Size(fugue_fd);
|
||||
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 *data)
|
||||
int fugue_close(void *data0)
|
||||
{
|
||||
int fugue_fd = (int)data;
|
||||
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,
|
||||
.pread = fugue_pread,
|
||||
.write = fugue_write,
|
||||
.lseek = fugue_lseek,
|
||||
.close = fugue_close,
|
||||
|
|
|
@ -12,12 +12,21 @@ extern const fs_descriptor_type_t fugue_descriptor_type;
|
|||
/* Directory descriptor type */
|
||||
extern const fs_descriptor_type_t fugue_dir_descriptor_type;
|
||||
|
||||
/* Since on Graph 35+E II / fx-9860G III there is no (known) syscall to update
|
||||
the file position, we need to track it ourselves, hopefully faithfully. */
|
||||
typedef struct {
|
||||
int fd;
|
||||
int pos;
|
||||
} fugue_fd_t;
|
||||
|
||||
/* Specific implementations of some standard functions */
|
||||
|
||||
int fugue_open(char const *path, int flags, mode_t mode);
|
||||
|
||||
int fugue_unlink(char const *path);
|
||||
|
||||
int fugue_rename(char const *oldpath, char const *newpath);
|
||||
|
||||
int fugue_mkdir(char const *path, mode_t mode);
|
||||
|
||||
int fugue_rmdir(char const *path);
|
||||
|
|
|
@ -41,13 +41,6 @@ ssize_t fugue_dir_read(void *data, void *buf, GUNUSED size_t size)
|
|||
return sizeof *dirent_ptr;
|
||||
}
|
||||
|
||||
ssize_t fugue_dir_pread(GUNUSED void *data, GUNUSED void *buf,
|
||||
GUNUSED size_t size, GUNUSED off_t offset)
|
||||
{
|
||||
errno = EISDIR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t fugue_dir_write(GUNUSED void *data, GUNUSED const void *buf,
|
||||
GUNUSED size_t size)
|
||||
{
|
||||
|
@ -90,7 +83,6 @@ int fugue_dir_close(void *data)
|
|||
|
||||
const fs_descriptor_type_t fugue_dir_descriptor_type = {
|
||||
.read = fugue_dir_read,
|
||||
.pread = fugue_dir_pread,
|
||||
.write = fugue_dir_write,
|
||||
.lseek = fugue_dir_lseek,
|
||||
.close = fugue_dir_close,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "util.h"
|
||||
#include "fugue.h"
|
||||
|
||||
/* TODO: fugue_open(): Handle trailing '/' and filesystem root */
|
||||
static int new_file_size;
|
||||
|
||||
int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
|
||||
{
|
||||
|
@ -91,8 +91,8 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
|
|||
|
||||
/* If the file does not exist and O_CREAT is set, create it */
|
||||
if((flags & O_CREAT) && ((flags & O_TRUNC) || fugue_fd < 0)) {
|
||||
int size = 0;
|
||||
err = BFile_Create(fcpath, BFile_File, &size);
|
||||
new_file_size = 0;
|
||||
err = BFile_Create(fcpath, BFile_File, &new_file_size);
|
||||
if(err < 0) {
|
||||
errno = bfile_error_to_errno(err);
|
||||
rc = -1;
|
||||
|
@ -109,18 +109,30 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
|
|||
|
||||
/* If O_APPEND is set, move to the end of the file */
|
||||
// TODO: O_APPEND should move the cursor before *each* write
|
||||
if((flags & O_APPEND))
|
||||
BFile_Seek(fugue_fd, BFile_Size(fugue_fd));
|
||||
int pos = 0;
|
||||
if((flags & O_APPEND)) {
|
||||
pos = BFile_Size(fugue_fd);
|
||||
BFile_Seek(fugue_fd, pos);
|
||||
}
|
||||
|
||||
/* Return the now-open file descriptor */
|
||||
fs_descriptor_t data = {
|
||||
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 = (void *)fugue_fd,
|
||||
.data = data,
|
||||
};
|
||||
rc = fd = fs_create_descriptor(&data);
|
||||
rc = fd = fs_create_descriptor(&fd_data);
|
||||
|
||||
if(fd == -1) {
|
||||
BFile_Close(fugue_fd);
|
||||
free(data);
|
||||
errno = ENFILE;
|
||||
goto end;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#include <gint/hardware.h>
|
||||
#include <gint/bfile.h>
|
||||
#include <gint/fs.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include "util.h"
|
||||
|
||||
int fugue_rename(char const *oldpath, char const *newpath)
|
||||
{
|
||||
ENOTSUP_IF_NOT_FUGUE(-1);
|
||||
int rc = -1;
|
||||
|
||||
uint16_t *fcpath_1 = NULL;
|
||||
uint16_t *fcpath_2 = NULL;
|
||||
|
||||
fcpath_1 = fs_path_normalize_fc(oldpath);
|
||||
if(!fcpath_1) {
|
||||
errno = ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
fcpath_2 = fs_path_normalize_fc(newpath);
|
||||
if(!fcpath_2) {
|
||||
errno = ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = BFile_Rename(fcpath_1, fcpath_2);
|
||||
if(rc < 0) {
|
||||
errno = bfile_error_to_errno(rc);
|
||||
rc = -1;
|
||||
}
|
||||
else rc = 0;
|
||||
|
||||
end:
|
||||
free(fcpath_1);
|
||||
free(fcpath_2);
|
||||
return rc;
|
||||
}
|
|
@ -4,15 +4,21 @@
|
|||
|
||||
ssize_t pread(int fd, void *buf, size_t size, off_t offset)
|
||||
{
|
||||
fs_descriptor_t const *d = fs_get_descriptor(fd);
|
||||
if(!d) {
|
||||
errno = EBADF;
|
||||
off_t current = lseek(fd, 0, SEEK_CUR);
|
||||
if(current == (off_t)-1)
|
||||
return (ssize_t)-1;
|
||||
}
|
||||
|
||||
if(d->type->pread)
|
||||
return d->type->pread(d->data, buf, size, offset);
|
||||
ssize_t rc = -1;
|
||||
|
||||
/* No seek function: cannot seek */
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
ssize_t pwrite(int fd, const void *buf, size_t size, off_t offset)
|
||||
{
|
||||
off_t current = lseek(fd, 0, SEEK_CUR);
|
||||
|
||||
/* This fails on G-III BFile and non-seekable file descriptors */
|
||||
if(current == (off_t)-1)
|
||||
return (ssize_t)-1;
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#include <stdio.h>
|
||||
#include "fugue/fugue.h"
|
||||
|
||||
int rename(char const *oldpath, char const *newpath)
|
||||
{
|
||||
/* Standard rename() is the Fugue filesystem only */
|
||||
return fugue_rename(oldpath, newpath);
|
||||
}
|
|
@ -41,6 +41,7 @@ static struct rendering_mode const gray_mode = {
|
|||
.dclear = gclear,
|
||||
.drect = grect,
|
||||
.dpixel = gpixel,
|
||||
.dgetpixel = ggetpixel,
|
||||
.gint_dhline = gint_ghline,
|
||||
.gint_dvline = gint_gvline,
|
||||
.dtext_opt = gtext_opt,
|
||||
|
@ -51,6 +52,7 @@ static struct rendering_mode const gray_exit_mode = {
|
|||
.dclear = NULL,
|
||||
.drect = NULL,
|
||||
.dpixel = NULL,
|
||||
.dgetpixel = NULL,
|
||||
.gint_dhline = NULL,
|
||||
.gint_dvline = NULL,
|
||||
.dtext_opt = NULL,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#include <gint/gray.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
int ggetpixel(int x, int y)
|
||||
{
|
||||
uint32_t *light, *dark;
|
||||
dgray_getvram(&light, &dark);
|
||||
|
||||
int offset = (y << 2) + (x >> 5);
|
||||
uint32_t mask = 1 << (~x & 31);
|
||||
|
||||
bool l = (light[offset] & mask) != 0;
|
||||
bool d = (dark [offset] & mask) != 0;
|
||||
return (d << 1) | l;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
int image_get_pixel(image_t const *img, int x, int y)
|
||||
{
|
||||
if((unsigned)x >= img->width || (unsigned)y >= img->height)
|
||||
return 0;
|
||||
|
||||
void *data = img->data + y * img->stride;
|
||||
uint8_t *data_u8 = data;
|
||||
uint16_t *data_u16 = data;
|
||||
|
||||
if(IMAGE_IS_RGB16(img->format)) {
|
||||
return data_u16[x];
|
||||
}
|
||||
else if(IMAGE_IS_P8(img->format)) {
|
||||
return (int8_t)data_u8[x];
|
||||
}
|
||||
else if(IMAGE_IS_P4(img->format)) {
|
||||
if(x & 1)
|
||||
return data_u8[x >> 1] & 0x0f;
|
||||
else
|
||||
return data_u8[x >> 1] >> 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
void image_hflip(image_t const *src, image_t *dst, bool copy_alpha)
|
||||
{
|
||||
if(!image_target(src, dst, DATA_RW, SAME_DEPTH, SAME_SIZE))
|
||||
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);
|
||||
int h = src->height;
|
||||
|
||||
if(IMAGE_IS_RGB16(src->format)) {
|
||||
while(h-- > 0) {
|
||||
for(int x1 = 0; x1 < (src->width + 1) >> 1; x1++) {
|
||||
int x2 = src->width - 1 - x1;
|
||||
int px1 = ((uint16_t *)src_px)[x1];
|
||||
int px2 = ((uint16_t *)src_px)[x2];
|
||||
|
||||
if(px1 != src_alpha)
|
||||
((uint16_t *)dst_px)[x2] = px1 - (px1 == dst_alpha);
|
||||
if(px2 != src_alpha)
|
||||
((uint16_t *)dst_px)[x1] = px2 - (px2 == dst_alpha);
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
}
|
||||
}
|
||||
else if(IMAGE_IS_P8(src->format)) {
|
||||
while(h-- > 0) {
|
||||
for(int x1 = 0; x1 < (src->width + 1) >> 1; x1++) {
|
||||
int x2 = src->width - 1 - x1;
|
||||
|
||||
int px1 = ((int8_t *)src_px)[x1];
|
||||
int px2 = ((int8_t *)src_px)[x2];
|
||||
|
||||
if(px1 != src_alpha)
|
||||
((int8_t *)dst_px)[x2] = px1;
|
||||
if(px2 != src_alpha)
|
||||
((int8_t *)dst_px)[x1] = px2;
|
||||
}
|
||||
src_px += src->stride;
|
||||
dst_px += dst->stride;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
image_t *image_hflip_alloc(image_t const *src)
|
||||
{
|
||||
if(!image_valid(src))
|
||||
return NULL;
|
||||
|
||||
image_t *dst = image_alloc(src->width, src->height, src->format);
|
||||
if(!dst || !image_copy_palette(src, dst, -1)) {
|
||||
image_free(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_hflip(src, dst, true);
|
||||
return dst;
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
.global _image_linear_rgb16
|
||||
.global _image_linear_p8
|
||||
|
||||
/* The loop nest for the rotation + scaling code, manually optimized.
|
||||
r0, r1: (temporary), u
|
||||
r2, r3: dx_u, dx_v
|
||||
r4: input_pixels
|
||||
r5: output_pixels
|
||||
r6, r7: drow_u, drow_v
|
||||
r8: line counter
|
||||
r9: dst_w
|
||||
r10: src_w << 16 (for bound checks)
|
||||
r11: src_h << 16 (for bound checks)
|
||||
r12: v
|
||||
r13: (temporary)
|
||||
r14: src_stride (for index access to input_pixels)
|
||||
@-4: dst_stride
|
||||
|
||||
This loop maintains the value of (u,v) at each pixel by adding (dx_u, dx_v)
|
||||
every pixel and (drow_u, drow_v) every row. For each position, it then
|
||||
checks whether 0 <= u < src_w and 0 <= v < src_height as fixed-point; if
|
||||
yes, input[(int)v * src_w + (int)u] is extracted; otherwise, the pixel is
|
||||
skipped. */
|
||||
.macro GEN_LINEAR_LOOP MEM, DEPTH
|
||||
mov.l r8, @-r15
|
||||
mov.l r9, @-r15
|
||||
mov.l r10, @-r15
|
||||
mov.l r11, @-r15
|
||||
mov.l r12, @-r15
|
||||
mov.l r13, @-r15
|
||||
mov.l r14, @-r15
|
||||
mov.l @r6+, r10 /* map.src_w */
|
||||
mov.l @r6+, r11 /* map.src_h */
|
||||
mov.l @r6+, r9 /* map.dst_w */
|
||||
mov.l @r6+, r8 /* map.dst_h */
|
||||
mov.l @r6+, r14 /* map.src_stride */
|
||||
mov.l @r6+, r0 /* map.dst_stride */
|
||||
mov.l @r6+, r1 /* map.u */
|
||||
mov.l @r6+, r12 /* map.v */
|
||||
mov.l @r6+, r2 /* map.dx_u */
|
||||
mov.l @r6+, r3 /* map.dx_v */
|
||||
|
||||
mov.l @(4, r6), r7 /* map.dy_v (replaced with drow_v) */
|
||||
shll16 r10
|
||||
|
||||
mov.l @r6, r6 /* map.dy_u (replaced with drow_u) */
|
||||
shll16 r11
|
||||
|
||||
/* Compute the output stride as map.dst_stride - (DEPTH * map.dst_w) */
|
||||
|
||||
ldrs 1f
|
||||
sub r9, r0
|
||||
|
||||
ldre 2f
|
||||
.if \DEPTH == 2
|
||||
sub r9, r0
|
||||
.else
|
||||
nop
|
||||
.endif
|
||||
|
||||
mov.l r0, @-r15
|
||||
nop
|
||||
|
||||
4: ldrc r9
|
||||
nop
|
||||
|
||||
1: cmp/hs r10, r1
|
||||
nop
|
||||
|
||||
bt 3f
|
||||
cmp/hs r11, r12
|
||||
|
||||
bt 3f
|
||||
swap.w r12, r13
|
||||
|
||||
mov r1, r0
|
||||
mulu.w r13, r14
|
||||
|
||||
shlr16 r0
|
||||
sts macl, r13
|
||||
|
||||
.if \DEPTH == 2
|
||||
shll r0
|
||||
nop
|
||||
.endif
|
||||
|
||||
add r13, r0
|
||||
\MEM @(r0, r4), r13
|
||||
|
||||
\MEM r13, @r5
|
||||
3: add #\DEPTH, r5
|
||||
|
||||
add r2, r1
|
||||
nop
|
||||
|
||||
add r3, r12
|
||||
2: nop
|
||||
|
||||
dt r8
|
||||
mov.l @r15, r0 /* Stride between lines, excluding content */
|
||||
|
||||
add r6, r1
|
||||
nop
|
||||
|
||||
add r7, r12
|
||||
nop
|
||||
|
||||
bf.s 4b
|
||||
add r0, r5
|
||||
|
||||
mov.l @r15+, r0
|
||||
mov.l @r15+, r14
|
||||
mov.l @r15+, r13
|
||||
mov.l @r15+, r12
|
||||
mov.l @r15+, r11
|
||||
mov.l @r15+, r10
|
||||
mov.l @r15+, r9
|
||||
rts
|
||||
mov.l @r15+, r8
|
||||
.endm
|
||||
|
||||
_image_linear_rgb16:
|
||||
GEN_LINEAR_LOOP mov.w, 2
|
||||
|
||||
_image_linear_p8:
|
||||
GEN_LINEAR_LOOP mov.b, 1
|
|
@ -0,0 +1,34 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include "fixed.h"
|
||||
|
||||
void image_linear_rgb16(void *src, void *dst, struct image_linear_map *map);
|
||||
void image_linear_p8(void *src, void *dst, struct image_linear_map *map);
|
||||
|
||||
void image_linear(image_t const *src, image_t *dst,
|
||||
struct image_linear_map *map)
|
||||
{
|
||||
if(!image_target(src, dst, NOT_P4, SAME_DEPTH))
|
||||
return;
|
||||
|
||||
/* Clip the destination */
|
||||
map->dst_w = min(map->dst_w, dst->width);
|
||||
map->dst_h = min(map->dst_h, dst->height);
|
||||
|
||||
int drow_u = -map->dx_u * map->dst_w + map->dy_u;
|
||||
int drow_v = -map->dx_v * map->dst_w + map->dy_v;
|
||||
|
||||
/* Change dy to mean drow before calling the assembler code */
|
||||
map->dy_u = drow_u;
|
||||
map->dy_v = drow_v;
|
||||
|
||||
/* Record strides */
|
||||
map->src_stride = src->stride;
|
||||
map->dst_stride = dst->stride;
|
||||
|
||||
/* Call the assembler implementation */
|
||||
if(IMAGE_IS_RGB16(src->format))
|
||||
image_linear_rgb16(src->data, dst->data, map);
|
||||
else if(IMAGE_IS_P8(src->format))
|
||||
image_linear_p8(src->data, dst->data, map);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
image_t *image_linear_alloc(image_t const *src, struct image_linear_map *map)
|
||||
{
|
||||
if(!image_valid(src) || IMAGE_IS_P4(src->format))
|
||||
return NULL;
|
||||
|
||||
int f = IMAGE_IS_RGB16(src->format) ? IMAGE_RGB565A : IMAGE_P8_RGB565A;
|
||||
image_t *dst = image_alloc(map->dst_w, map->dst_h, f);
|
||||
if(!dst)
|
||||
return NULL;
|
||||
if(f == IMAGE_P8_RGB565A && !image_copy_palette(src, dst, -1)) {
|
||||
image_free(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_clear(dst);
|
||||
image_linear(src, dst, map);
|
||||
return dst;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
void image_rotate(image_t const *src, float angle, bool resize,
|
||||
struct image_linear_map *map)
|
||||
{
|
||||
if(!image_valid(src))
|
||||
return;
|
||||
|
||||
int center_x = src->width / 2;
|
||||
int center_y = src->height / 2;
|
||||
|
||||
image_rotate_around(src, angle, resize, ¢er_x, ¢er_y, map);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <gint/image.h>
|
||||
#include "fixed.h"
|
||||
|
||||
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(src, angle, fconst(1.0), resize, center_x,
|
||||
center_y, map);
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
#include <gint/image.h>
|
||||
#include <math.h>
|
||||
#include "fixed.h"
|
||||
|
||||
void image_rotate_around_scale(image_t const *src, float alpha, int gamma,
|
||||
bool resize, int *center_x, int *center_y, struct image_linear_map *map)
|
||||
{
|
||||
if(!image_valid(src))
|
||||
return;
|
||||
|
||||
map->src_w = src->width;
|
||||
map->src_h = src->height;
|
||||
|
||||
/* Compute the rotation basis */
|
||||
int cos_alpha = fconst(cosf(alpha));
|
||||
int sin_alpha = fconst(sinf(alpha));
|
||||
int inv_gamma = fdiv(fconst(1.0), gamma);
|
||||
|
||||
map->dx_u = fmul(cos_alpha, inv_gamma);
|
||||
map->dx_v = fmul(sin_alpha, inv_gamma);
|
||||
|
||||
map->dy_u = -fmul(sin_alpha, inv_gamma);
|
||||
map->dy_v = fmul(cos_alpha, inv_gamma);
|
||||
|
||||
/* Don't try to resize cleanly; just make the longest diagonal the width if
|
||||
[resize=true] to make sure everything fits */
|
||||
if(resize) {
|
||||
int diag = isqrt(src->width * src->width + src->height * src->height);
|
||||
map->dst_w = fround(gamma * diag);
|
||||
map->dst_h = fround(gamma * diag);
|
||||
}
|
||||
else {
|
||||
map->dst_w = fround(gamma * src->width);
|
||||
map->dst_h = fround(gamma * src->height);
|
||||
}
|
||||
|
||||
/* Compute the new location of the anchor relative to the image center.
|
||||
This is found by a neat trick: rotate it with the same angle */
|
||||
int ax = *center_x - map->src_w / 2;
|
||||
int ay = *center_y - map->src_h / 2;
|
||||
|
||||
int ax2 = fround(fmul(gamma, cos_alpha * ax + sin_alpha * ay));
|
||||
int ay2 = fround(fmul(gamma, -sin_alpha * ax + cos_alpha * ay));
|
||||
|
||||
int new_center_x = ax2 + map->dst_w / 2;
|
||||
int new_center_y = ay2 + map->dst_h / 2;
|
||||
|
||||
/* Finally, determine the initial value of (u,v). We now that it evaluates
|
||||
to (center_x, center_y) when on the new center point (new_center_x,
|
||||
new_center_y); apply the difference accordingly. */
|
||||
map->u = fconst(*center_x)
|
||||
- map->dx_u * new_center_x
|
||||
- map->dy_u * new_center_y;
|
||||
map->v = fconst(*center_y)
|
||||
- map->dx_v * new_center_x
|
||||
- map->dy_v * new_center_y;
|
||||
|
||||
*center_x = new_center_x;
|
||||
*center_y = new_center_y;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#include <gint/image.h>
|
||||
#include "fixed.h"
|
||||
|
||||
void image_scale(image_t const *src, int gamma_x, int gamma_y,
|
||||
struct image_linear_map *map)
|
||||
{
|
||||
if(!image_valid(src))
|
||||
return;
|
||||
|
||||
int inv_gamma_x = fdiv(fconst(1.0), gamma_x);
|
||||
int inv_gamma_y = fdiv(fconst(1.0), gamma_y);
|
||||
|
||||
map->u = fconst(0);
|
||||
map->v = fconst(0);
|
||||
map->dx_u = inv_gamma_x;
|
||||
map->dx_v = 0;
|
||||
map->dy_u = 0;
|
||||
map->dy_v = inv_gamma_y;
|
||||
|
||||
map->src_w = src->width;
|
||||
map->src_h = src->height;
|
||||
map->dst_w = fround(src->width * gamma_x);
|
||||
map->dst_h = fround(src->height * gamma_y);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#include <gint/image.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void image_set_palette(image_t *img, uint16_t *palette, int size, bool owns)
|
||||
{
|
||||
if(!img || !IMAGE_IS_INDEXED(img->format))
|
||||
return;
|
||||
if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC)
|
||||
free(img->palette);
|
||||
|
||||
img->palette = palette;
|
||||
img->color_count = size;
|
||||
|
||||
if(owns)
|
||||
img->flags |= IMAGE_FLAGS_PALETTE_ALLOC;
|
||||
else
|
||||
img->flags &= ~IMAGE_FLAGS_PALETTE_ALLOC;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
void image_set_pixel(image_t const *img, int x, int y, int value)
|
||||
{
|
||||
if(!image_valid(img))
|
||||
return;
|
||||
if((unsigned)x >= img->width || (unsigned)y >= img->height)
|
||||
return;
|
||||
|
||||
void *data = img->data + y * img->stride;
|
||||
uint8_t *data_u8 = data;
|
||||
uint16_t *data_u16 = data;
|
||||
|
||||
if(IMAGE_IS_RGB16(img->format)) {
|
||||
data_u16[x] = value;
|
||||
}
|
||||
else if(IMAGE_IS_P8(img->format)) {
|
||||
data_u8[x] = value;
|
||||
}
|
||||
else if(IMAGE_IS_P4(img->format)) {
|
||||
if(x & 1)
|
||||
data_u8[x >> 1] = (data_u8[x >> 1] & 0xf0) | (value & 0x0f);
|
||||
else
|
||||
data_u8[x >> 1] = (data_u8[x >> 1] & 0x0f) | (value << 4);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/display.h>
|
||||
#include <string.h>
|
||||
#undef image_sub
|
||||
|
||||
static image_t image_sub_default;
|
||||
|
||||
image_t *image_sub(image_t const *src, int left, int top, int w, int h,
|
||||
image_t *dst)
|
||||
{
|
||||
if(!dst)
|
||||
dst = &image_sub_default;
|
||||
if(w < 0)
|
||||
w = src->width - left;
|
||||
if(h < 0)
|
||||
h = src->height - top;
|
||||
|
||||
struct gint_image_box box = { 0, 0, w, h, left, top };
|
||||
struct dwindow in_window = { 0, 0, w, h };
|
||||
if(!image_valid(src) || IMAGE_IS_P4(src->format) ||
|
||||
!gint_image_clip_input(src, &box, &in_window)) {
|
||||
memset(dst, 0, sizeof *dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
int const ro_flags = IMAGE_FLAGS_DATA_RO | IMAGE_FLAGS_PALETTE_RO;
|
||||
dst->format = src->format;
|
||||
dst->flags = src->flags & ro_flags;
|
||||
dst->color_count = src->color_count;
|
||||
dst->width = box.w;
|
||||
dst->height = box.h;
|
||||
dst->stride = src->stride;
|
||||
|
||||
if(IMAGE_IS_RGB16(src->format))
|
||||
dst->data = src->data + box.top * src->stride + 2 * box.left;
|
||||
else if(IMAGE_IS_P8(src->format))
|
||||
dst->data = src->data + box.top * src->stride + box.left;
|
||||
|
||||
dst->palette = src->palette;
|
||||
return dst;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#include <gint/image.h>
|
||||
#undef image_target
|
||||
|
||||
bool image_target(image_t const *src, image_t *dst, ...)
|
||||
{
|
||||
if(!image_valid(src) || !image_valid(dst))
|
||||
return false;
|
||||
|
||||
va_list args;
|
||||
va_start(args, dst);
|
||||
|
||||
int req = -1;
|
||||
while((req = va_arg(args, int)) != IMAGE_TARGET_NONE) {
|
||||
if(req == IMAGE_TARGET_NOT_P4 && IMAGE_IS_P4(dst->format))
|
||||
return false;
|
||||
if(req == IMAGE_TARGET_DATA_RW && (dst->flags & IMAGE_FLAGS_DATA_RO))
|
||||
return false;
|
||||
if(req == IMAGE_TARGET_PALETTE_RW && (dst->flags &
|
||||
IMAGE_FLAGS_PALETTE_RO))
|
||||
return false;
|
||||
if(req == IMAGE_TARGET_SAME_SIZE &&
|
||||
(dst->width < src->width || dst->height < src->height))
|
||||
return false;
|
||||
if(req == IMAGE_TARGET_SAME_FORMAT && src->format != dst->format)
|
||||
return false;
|
||||
if(req == IMAGE_TARGET_SAME_DEPTH) {
|
||||
if(IMAGE_IS_RGB16(src->format) && IMAGE_IS_RGB16(dst->format))
|
||||
continue;
|
||||
if(IMAGE_IS_P8(src->format) && IMAGE_IS_P8(dst->format))
|
||||
continue;
|
||||
if(IMAGE_IS_P4(src->format) && IMAGE_IS_P4(dst->format))
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
bool image_valid(image_t const *img)
|
||||
{
|
||||
if(!img)
|
||||
return false;
|
||||
|
||||
if(IMAGE_IS_RGB16(img->format)) {
|
||||
return (img->data != NULL);
|
||||
}
|
||||
if(IMAGE_IS_P8(img->format) || IMAGE_IS_P4(img->format)) {
|
||||
return (img->data != NULL) && (img->palette != NULL) &&
|
||||
(img->color_count != 0);
|
||||
}
|
||||
|
||||
/* Invalid format */
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#include <gint/image.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void copy_row_rgb16(uint16_t *src, uint16_t *dst, int src_alpha,
|
||||
int dst_alpha, int width)
|
||||
{
|
||||
for(int x = 0; x < width; x++) {
|
||||
int px = src[x];
|
||||
if(px != src_alpha)
|
||||
dst[x] = px - (px == dst_alpha);
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_row_p8(int8_t *src, int8_t *dst, int src_alpha, int width)
|
||||
{
|
||||
for(int x = 0; x < width; x++) {
|
||||
int px = src[x];
|
||||
if(px != src_alpha)
|
||||
dst[x] = px;
|
||||
}
|
||||
}
|
||||
|
||||
void image_vflip(image_t const *src, image_t *dst, bool copy_alpha)
|
||||
{
|
||||
if(!image_target(src, dst, DATA_RW, SAME_DEPTH, SAME_SIZE))
|
||||
return;
|
||||
|
||||
int h = src->height;
|
||||
void *src_top = src->data;
|
||||
void *src_bot = src->data + (h - 1) * src->stride;
|
||||
void *dst_top = dst->data;
|
||||
void *dst_bot = dst->data + (h - 1) * dst->stride;
|
||||
|
||||
int src_alpha = image_alpha_2(src->format, copy_alpha);
|
||||
int dst_alpha = image_alpha_2(dst->format, copy_alpha);
|
||||
|
||||
int row_length = src->stride;
|
||||
void *row = malloc(row_length);
|
||||
if(!row)
|
||||
return;
|
||||
|
||||
for(int y = 0; y < (h + 1) >> 1; y++) {
|
||||
memcpy(row, src_top, row_length);
|
||||
|
||||
if(IMAGE_IS_RGB16(src->format)) {
|
||||
copy_row_rgb16(src_bot, dst_top, src_alpha, dst_alpha, src->width);
|
||||
copy_row_rgb16(row, dst_bot, src_alpha, dst_alpha, src->width);
|
||||
}
|
||||
else {
|
||||
copy_row_p8(src_bot, dst_top, src_alpha, src->width);
|
||||
copy_row_p8(row, dst_bot, src_alpha, src->width);
|
||||
}
|
||||
|
||||
src_top += src->stride;
|
||||
src_bot -= src->stride;
|
||||
dst_top += dst->stride;
|
||||
dst_bot -= dst->stride;
|
||||
}
|
||||
|
||||
free(row);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#include <gint/image.h>
|
||||
|
||||
image_t *image_vflip_alloc(image_t const *src)
|
||||
{
|
||||
if(!image_valid(src))
|
||||
return NULL;
|
||||
|
||||
image_t *dst = image_alloc(src->width, src->height, src->format);
|
||||
if(!dst || !image_copy_palette(src, dst, -1)) {
|
||||
image_free(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image_vflip(src, dst, true);
|
||||
return dst;
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
// Interrupt controllers
|
||||
//---
|
||||
|
||||
GDATA3 sh7705_intc_t SH7705_INTC = {
|
||||
GRODATA3 sh7705_intc_t const SH7705_INTC = {
|
||||
.IPR = {
|
||||
(void *)0xfffffee2, (void *)0xfffffee4,
|
||||
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
|
||||
|
@ -21,7 +21,7 @@ GDATA3 sh7705_intc_t SH7705_INTC = {
|
|||
.ICR1 = (void *)0xa4000010,
|
||||
};
|
||||
|
||||
sh7305_intc_t SH7305_INTC = {
|
||||
sh7305_intc_t const SH7305_INTC = {
|
||||
.IPR = (void *)0xa4080000,
|
||||
.MSK = (void *)0xa4080080,
|
||||
.MSKCLR = (void *)0xa40800c0,
|
||||
|
|
|
@ -2,16 +2,43 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/mpu/dma.h>
|
||||
#include <gint/drivers/keydev.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/gint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void __Reset(void);
|
||||
|
||||
#define dprint(x, y, ...) dprint(x, y, C_BLACK, __VA_ARGS__)
|
||||
#define dtext(x, y, str) dtext (x, y, C_BLACK, str)
|
||||
|
||||
/* Weak implementation of driver functions, which are used if the keyboard and
|
||||
display drivers are not linked in. This allows add-ins to not link in these
|
||||
drivers (which is useful for low-level debugging). */
|
||||
GWEAK void _WEAK_dupdate(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GWEAK keydev_t *_WEAK_keydev_std(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
GWEAK bool _WEAK_keydev_keydown(GUNUSED keydev_t *d, GUNUSED int key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
GWEAK key_event_t _WEAK_keydev_unqueue_event(GUNUSED keydev_t *d)
|
||||
{
|
||||
return (key_event_t){ .type = KEYEV_NONE };
|
||||
}
|
||||
|
||||
/* gint_panic_default(): Default panic handler */
|
||||
GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
||||
{
|
||||
uint32_t TEA, TRA;
|
||||
keydev_t *kd = _WEAK_keydev_std();
|
||||
|
||||
if(isSH3())
|
||||
{
|
||||
|
@ -31,9 +58,9 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
|||
if(isSH4()) __asm__("stc sgr, %0" : "=r"(SGR));
|
||||
|
||||
dfont(NULL);
|
||||
dclear(C_WHITE);
|
||||
|
||||
#ifdef FX9860G
|
||||
memset(gint_vram, 0, 1024);
|
||||
dtext(1, 0, "Exception! (SysERROR)");
|
||||
for(int i = 0; i < 32; i++) gint_vram[i] = ~gint_vram[i];
|
||||
|
||||
|
@ -55,16 +82,23 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
|||
if(name[0]) dtext(1, 9, name);
|
||||
else dprint(1, 9, "%03x", code);
|
||||
|
||||
dprint(1, 17, " PC :%08x", PC);
|
||||
dprint(1, 25, "TEA :%08x", TEA);
|
||||
dprint(1, 33, "TRA :%08x", TRA);
|
||||
if(isSH4()) dprint(1, 41, "SGR :%08x", SGR);
|
||||
dprint(1, 17, " PC:%08x TRA:%d", PC, TRA);
|
||||
dprint(1, 25, "TEA:%08x", TEA);
|
||||
|
||||
dtext(1, 49, "The add-in crashed.");
|
||||
dtext(1, 57, "Please reset the calc");
|
||||
dtext(1, 33, "The add-in crashed.");
|
||||
if(kd == NULL) {
|
||||
dtext(1, 41, "Please reset the calc");
|
||||
}
|
||||
else {
|
||||
dtext(1, 41, "[EXIT]:Quit add-in");
|
||||
dtext(1, 49, "[MENU]:Main menu");
|
||||
dtext(1, 57, "[F1]+[F6]: RESET calc");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
/* Don't require the DMA driver just for a clear */
|
||||
memset(gint_vram, 0xff, DWIDTH*DHEIGHT*2);
|
||||
dtext(6, 3, "An exception occured! (System ERROR)");
|
||||
|
||||
uint32_t *long_vram = (void *)gint_vram;
|
||||
|
@ -99,34 +133,63 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
|||
dprint(38, 75, "= %#x", TRA);
|
||||
dtext(281, 75, "(Trap number)");
|
||||
|
||||
dtext(6, 95, "An unrecoverable error occurred in the add-in.");
|
||||
dtext(6, 108, "Please press the RESET button to restart the");
|
||||
dtext(6, 121, "calculator.");
|
||||
dtext(6, 95, "The add-in crashed!");
|
||||
if(kd == NULL) {
|
||||
dtext(6, 108, "Please press the RESET button to restart the");
|
||||
dtext(6, 121, "calculator.");
|
||||
}
|
||||
else {
|
||||
dtext(6, 121, "[EXIT]: Exit the program with abort()");
|
||||
dtext(6, 134, "[MENU]: Leave to main menu");
|
||||
dtext(6, 147, "[F1]+[F6]: RESET the calculator");
|
||||
}
|
||||
|
||||
/* DMA address error handler */
|
||||
if(code == 0x1020)
|
||||
{
|
||||
#define DMA SH7305_DMA
|
||||
dprint(6, 141, "SAR0: %08x DAR0: %08x TCR0: %08x",
|
||||
dprint(6, 167, "SAR0: %08x DAR0: %08x TCR0: %08x",
|
||||
DMA.DMA0.SAR, DMA.DMA0.DAR, DMA.DMA0.TCR);
|
||||
dprint(6, 154, "CHCR0: %08x", DMA.DMA0.CHCR);
|
||||
dprint(6, 167, "SAR1: %08x DAR1: %08x TCR1: %08x",
|
||||
dprint(6, 180, "CHCR0: %08x", DMA.DMA0.CHCR);
|
||||
dprint(6, 193, "SAR1: %08x DAR1: %08x TCR1: %08x",
|
||||
DMA.DMA1.SAR, DMA.DMA1.DAR, DMA.DMA1.TCR);
|
||||
dprint(6, 180, "CHCR1: %08x", DMA.DMA1.CHCR);
|
||||
dprint(6, 193, "DMAOR: %04x", DMA.OR);
|
||||
dprint(6, 206, "CHCR1: %08x DMAOR:%04x", DMA.DMA1.CHCR, DMA.OR);
|
||||
#undef DMA
|
||||
}
|
||||
/* Illegal instruction handler */
|
||||
if(code == 0x180)
|
||||
{
|
||||
uint16_t *opcodes = (void *)PC;
|
||||
dprint(6, 141, "Opcodes: %04x %04x [%04x] %04x",
|
||||
dprint(6, 160, "Opcodes: %04x %04x [%04x] %04x",
|
||||
opcodes[-2], opcodes[-1], opcodes[0], opcodes[1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
dupdate();
|
||||
while(1) sleep();
|
||||
_WEAK_dupdate();
|
||||
|
||||
/* Make sure relevant keys are released before taking in events; we don't
|
||||
want the user to RESET through this screen by holding a key */
|
||||
bool has_released = false;
|
||||
|
||||
while(1) {
|
||||
while(_WEAK_keydev_unqueue_event(kd).type != KEYEV_NONE) {}
|
||||
|
||||
bool exit = _WEAK_keydev_keydown(kd, KEY_EXIT);
|
||||
bool menu = _WEAK_keydev_keydown(kd, KEY_MENU);
|
||||
bool f1 = _WEAK_keydev_keydown(kd, KEY_F1);
|
||||
bool f6 = _WEAK_keydev_keydown(kd, KEY_F6);
|
||||
|
||||
if(has_released && exit)
|
||||
abort();
|
||||
if(has_released && menu)
|
||||
gint_osmenu();
|
||||
if(has_released && f1 && f6)
|
||||
__Reset();
|
||||
if(!exit && !menu && !f1 && !f6)
|
||||
has_released = true;
|
||||
|
||||
sleep();
|
||||
}
|
||||
}
|
||||
|
||||
/* Panic handler */
|
||||
|
|
|
@ -69,7 +69,8 @@ void hw_detect(void)
|
|||
gint[HWCALC] = HWCALC_FX9860G_SH4;
|
||||
if(gint[HWMPU] == HWMPU_SH7337 || gint[HWMPU] == HWMPU_SH7355)
|
||||
{
|
||||
gint[HWCALC] = HWCALC_FX9860G_SH3;
|
||||
gint[HWCALC] = (SH7705_PFC.PEDR & 0x08) ? HWCALC_FX9860G_SH3 :
|
||||
HWCALC_FX9860G_SLIM;
|
||||
}
|
||||
|
||||
/* Tell Graph 35+E II from OS version (this is accurate unless someone
|
||||
|
@ -138,7 +139,7 @@ void hw_detect(void)
|
|||
gint[HWCALC] = HWCALC_FXCG_MANAGER;
|
||||
|
||||
/* Basic memory information */
|
||||
gint[HWRAM] = (2 << 20);
|
||||
gint[HWRAM] = (gint[HWCALC] == HWCALC_PRIZM) ? (2 << 20) : (8 << 20);
|
||||
gint[HWROM] = (32 << 20);
|
||||
|
||||
/* Mapped memory */
|
||||
|
|
|
@ -102,7 +102,7 @@ _gint_inth_7305:
|
|||
|
||||
#ifdef FX9860G
|
||||
|
||||
/* SH7705-TYPE INTERRUT HANDLER ENTRY - 56 BYTES */
|
||||
/* SH7705-TYPE INTERRUPT HANDLER ENTRY - 56 BYTES */
|
||||
|
||||
_gint_inth_7705:
|
||||
/* Save caller-saved registers as before */
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <gint/kmalloc.h>
|
||||
#include <gint/cpu.h>
|
||||
#include <gint/exc.h>
|
||||
#include <gint/config.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -91,15 +92,35 @@ void kinit(void)
|
|||
static_ram.is_default = isSH4();
|
||||
static_ram.start = mmu_uram() + ((uint32_t)&euram - 0x08100000);
|
||||
static_ram.end = (void *)uram_end;
|
||||
|
||||
kmalloc_init_arena(&static_ram, true);
|
||||
kmalloc_add_arena(&static_ram);
|
||||
|
||||
/* Create an arena in the OS stack as well, for VRAM and more data */
|
||||
#if defined(FXCG50) && !defined(GINT_NO_OS_STACK)
|
||||
static kmalloc_arena_t os_stack = { 0 };
|
||||
os_stack.name = "_ostk";
|
||||
os_stack.is_default = true;
|
||||
if(gint[HWCALC] == HWCALC_PRIZM || gint[HWCALC] == HWCALC_FXCG_MANAGER)
|
||||
os_stack.start = (void *)0x880f0000;
|
||||
else
|
||||
os_stack.start = (void *)0x8c0f0000;
|
||||
os_stack.end = os_stack.start + (350 * 1024);
|
||||
kmalloc_init_arena(&os_stack, true);
|
||||
kmalloc_add_arena(&os_stack);
|
||||
#endif /* FXCG50 && !GINT_NO_OS_STACK */
|
||||
|
||||
/* Allocate world buffers for the OS and for gint */
|
||||
gint_world_os = gint_world_alloc();
|
||||
gint_world_addin = gint_world_alloc();
|
||||
gint_driver_flags = malloc(gint_driver_count());
|
||||
|
||||
#ifdef FXCG50
|
||||
/* Allocate VRAMs, which is important for panic screens */
|
||||
extern bool dvram_init(void);
|
||||
if(!dvram_init())
|
||||
abort();
|
||||
#endif
|
||||
|
||||
if(!gint_world_os || !gint_world_addin || !gint_driver_flags)
|
||||
gint_panic(0x1060);
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <gint/gint.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/hardware.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
@ -12,18 +13,22 @@ int __GetKeyWait(int *col,int *row,int type,int time,int menu,uint16_t *key);
|
|||
void __ClearKeyBuffer(void); /* ? */
|
||||
void *__GetVRAMAddress(void);
|
||||
void __ConfigureStatusArea(int mode);
|
||||
void __SetQuitHandler(void (*callback)(void));
|
||||
|
||||
static int __osmenu_id;
|
||||
|
||||
static void __osmenu_handler(void)
|
||||
{
|
||||
__PutKeyCode(0x04, 0x09, 0);
|
||||
if(isSlim())
|
||||
__PutKeyCode(0x07, 0x0A, 0);
|
||||
else
|
||||
__PutKeyCode(0x04, 0x09, 0);
|
||||
|
||||
__Timer_Stop(__osmenu_id);
|
||||
__Timer_Deinstall(__osmenu_id);
|
||||
}
|
||||
|
||||
static void __osmenu(void)
|
||||
void gint_osmenu_native(void)
|
||||
{
|
||||
__ClearKeyBuffer();
|
||||
|
||||
|
@ -70,5 +75,30 @@ static void __osmenu(void)
|
|||
/* gint_osmenu() - switch out of gint and call the calculator's main menu */
|
||||
void gint_osmenu(void)
|
||||
{
|
||||
gint_world_switch(GINT_CALL(__osmenu));
|
||||
gint_world_switch(GINT_CALL(gint_osmenu_native));
|
||||
}
|
||||
|
||||
static gint_call_t __gcall;
|
||||
static bool __do_world_switch;
|
||||
|
||||
static void __handler()
|
||||
{
|
||||
if(__do_world_switch){
|
||||
gint_call(__gcall);
|
||||
}else{
|
||||
/* TODO: quit the world switch */
|
||||
gint_call(__gcall);
|
||||
}
|
||||
}
|
||||
|
||||
static void __sethandler()
|
||||
{
|
||||
__SetQuitHandler((void *)__handler);
|
||||
}
|
||||
|
||||
void gint_set_quit_handler(gint_call_t gcall, bool do_world_switch)
|
||||
{
|
||||
__gcall = gcall;
|
||||
__do_world_switch = do_world_switch;
|
||||
gint_world_switch(GINT_CALL(__sethandler));
|
||||
}
|
||||
|
|
|
@ -23,8 +23,7 @@ extern uint32_t
|
|||
brom, srom, /* Limits of ROM mappings */
|
||||
ldata, sdata, rdata, /* User's data section */
|
||||
lilram, silram, rilram, /* IL memory section */
|
||||
lxram, sxram, rxram, /* X memory section */
|
||||
lyram, syram, ryram, /* Y memory section */
|
||||
lxyram, sxyram, rxyram, /* X and Y memory section */
|
||||
sbss, rbss; /* User's BSS section */
|
||||
#ifdef FX9860G
|
||||
extern uint32_t
|
||||
|
@ -95,11 +94,7 @@ static void callarray(void (**f)(void), void (**l)(void))
|
|||
while(f < l) (*(*f++))();
|
||||
}
|
||||
|
||||
/* start(): Where it all starts
|
||||
Returns a status code. Invoking main menu is better than returning, see
|
||||
gint_setrestart() for that. */
|
||||
GSECTION(".text.entry")
|
||||
int start(int isappli, int optnum)
|
||||
static int start2(int isappli, int optnum)
|
||||
{
|
||||
/* We are currently in a dynamic userspace mapping of an add-in run
|
||||
from the storage memory. We are running in privileged mode with one
|
||||
|
@ -151,8 +146,7 @@ int start(int isappli, int optnum)
|
|||
if(!isSH3())
|
||||
{
|
||||
regcpy(&lilram, &silram, &rilram);
|
||||
regcpy(&lxram, &sxram, &rxram);
|
||||
regcpy(&lyram, &syram, &ryram);
|
||||
regcpy(&lxyram, &sxyram, &rxyram);
|
||||
}
|
||||
|
||||
#ifdef FX9860G
|
||||
|
@ -181,20 +175,14 @@ int start(int isappli, int optnum)
|
|||
hosted user application, which has its own constructors and
|
||||
destructors to work with. */
|
||||
|
||||
while(1)
|
||||
{
|
||||
/* Here, we use exit() to allow the standard library to do
|
||||
what it wants in exit() after main() finishes executing */
|
||||
if(!setjmp(gint_exitbuf)) {
|
||||
callarray(&bctors, &ectors);
|
||||
exit(main(isappli, optnum));
|
||||
}
|
||||
else {
|
||||
callarray(&bdtors, &edtors);
|
||||
}
|
||||
|
||||
if(!gint_restart) break;
|
||||
gint_osmenu();
|
||||
/* Here, we use exit() to allow the standard library to do
|
||||
what it wants in exit() after main() finishes executing */
|
||||
if(!setjmp(gint_exitbuf)) {
|
||||
callarray(&bctors, &ectors);
|
||||
exit(main(isappli, optnum));
|
||||
}
|
||||
else {
|
||||
callarray(&bdtors, &edtors);
|
||||
}
|
||||
|
||||
/* Before leaving the application, we need to clean everything we
|
||||
|
@ -208,6 +196,18 @@ int start(int isappli, int optnum)
|
|||
return gint_exitcode;
|
||||
}
|
||||
|
||||
GSECTION(".text.entry")
|
||||
int start(int isappli, int optnum)
|
||||
{
|
||||
int rc;
|
||||
while(1) {
|
||||
rc = start2(isappli, optnum);
|
||||
if(!gint_restart) break;
|
||||
gint_osmenu_native();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Standard _Exit, used by the fxlibc exit() to leave control */
|
||||
void _Exit(int rc)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
/* Bfile driver */
|
||||
.global _BFile_Remove
|
||||
.global _BFile_Rename
|
||||
.global _BFile_Create
|
||||
.global _BFile_Open
|
||||
.global _BFile_Close
|
||||
|
@ -41,6 +42,10 @@
|
|||
.global ___ClearKeyBuffer
|
||||
.global ___GetVRAMAddress
|
||||
.global ___ConfigureStatusArea
|
||||
.global ___SetQuitHandler
|
||||
|
||||
/* Reset */
|
||||
.global ___Reset
|
||||
|
||||
#define syscall_(id, syscall_table) \
|
||||
mov.l syscall_table, r2 ;\
|
||||
|
@ -110,6 +115,13 @@ ___ClearKeyBuffer:
|
|||
syscall(0x241)
|
||||
___GetVRAMAddress:
|
||||
syscall(0x135)
|
||||
___SetQuitHandler:
|
||||
syscall(0x494)
|
||||
|
||||
/* Reset */
|
||||
|
||||
___Reset:
|
||||
syscall(0x236)
|
||||
|
||||
syscall_table:
|
||||
.long 0x80010070
|
||||
|
@ -131,6 +143,8 @@ ___realloc:
|
|||
|
||||
_BFile_Remove:
|
||||
syscall(0x1db4)
|
||||
_BFile_Rename:
|
||||
syscall(0x1db3)
|
||||
_BFile_Create:
|
||||
syscall(0x1dae)
|
||||
_BFile_Open:
|
||||
|
@ -175,11 +189,18 @@ ___GetVRAMAddress:
|
|||
syscall(0x1e6)
|
||||
___ConfigureStatusArea:
|
||||
syscall(0x2b7)
|
||||
___SetQuitHandler:
|
||||
syscall(0x1e6e)
|
||||
|
||||
.global ___SpecialMatrixCodeProcessing
|
||||
___SpecialMatrixCodeProcessing:
|
||||
syscall(0x1e60)
|
||||
|
||||
/* Reset */
|
||||
|
||||
___Reset:
|
||||
syscall(0x1187)
|
||||
|
||||
syscall_table:
|
||||
.long 0x80020070
|
||||
|
||||
|
|
|
@ -38,6 +38,20 @@ void gint_world_free(gint_world_t world)
|
|||
free(world);
|
||||
}
|
||||
|
||||
//---
|
||||
// Synchronization
|
||||
//---
|
||||
|
||||
void gint_world_sync(void)
|
||||
{
|
||||
/* Unbind all drivers, which waits for async tasks to complete */
|
||||
for(int i = gint_driver_count() - 1; i >= 0; i--)
|
||||
{
|
||||
gint_driver_t *d = &gint_drivers[i];
|
||||
if(d->unbind) d->unbind();
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// World switch with driver state saves
|
||||
//---
|
||||
|
@ -135,7 +149,7 @@ int gint_world_switch(gint_call_t call)
|
|||
if(canary)
|
||||
*canary = 0xb7c0ffee;
|
||||
|
||||
int rc = call.function ? gint_call(call) : 0;
|
||||
int rc = gint_call(call);
|
||||
|
||||
/* The canary check needs to occur before switching in the gint world;
|
||||
otherwise we just crash due to the overflow. gint_panic() isn't
|
||||
|
|
|
@ -11,25 +11,8 @@
|
|||
#include <gint/drivers/t6k11.h>
|
||||
#endif
|
||||
|
||||
/* Delay before first repeat and subsequent repeats, in milliseconds */
|
||||
static int rep_first = 400, rep_next = 40;
|
||||
/* Repeat profile function */
|
||||
static getkey_profile_t repeat_profile = NULL;
|
||||
/* Feature function */
|
||||
static getkey_feature_t feature_function = NULL;
|
||||
/* Repeater specification */
|
||||
static enum { NONE, ALL, ARROWS, FILTER } repeat_mode;
|
||||
|
||||
static int getkey_repeater(int key, int duration, int count)
|
||||
{
|
||||
if(repeat_mode == NONE) return -1;
|
||||
if(repeat_mode == FILTER) return repeat_profile(key, duration, count);
|
||||
|
||||
if(repeat_mode == ARROWS && key != KEY_LEFT && key != KEY_RIGHT
|
||||
&& key != KEY_UP && key != KEY_DOWN) return -1;
|
||||
|
||||
return (count ? rep_next : rep_first) * 1000;
|
||||
}
|
||||
|
||||
/* getkey(): Wait for a key press */
|
||||
key_event_t getkey(void)
|
||||
|
@ -41,72 +24,65 @@ key_event_t getkey(void)
|
|||
key_event_t getkey_opt(int opt, volatile int *timeout)
|
||||
{
|
||||
keydev_t *d = keydev_std();
|
||||
keydev_transform_t tr0 = keydev_transform(d);
|
||||
keydev_transform_t tr = keydev_transform(d);
|
||||
key_event_t e;
|
||||
|
||||
int o = KEYDEV_TR_REPEATS |
|
||||
KEYDEV_TR_DELETE_MODIFIERS |
|
||||
KEYDEV_TR_DELETE_RELEASES |
|
||||
((opt & GETKEY_MOD_SHIFT) ? KEYDEV_TR_DELAYED_SHIFT : 0) |
|
||||
((opt & GETKEY_MOD_ALPHA) ? KEYDEV_TR_DELAYED_ALPHA : 0);
|
||||
keydev_set_transform(d, (keydev_transform_t){ o, getkey_repeater });
|
||||
|
||||
repeat_mode = NONE;
|
||||
if(opt & GETKEY_REP_ARROWS) repeat_mode = ARROWS;
|
||||
if(opt & GETKEY_REP_ALL) repeat_mode = ALL;
|
||||
if(opt & GETKEY_REP_PROFILE && repeat_profile) repeat_mode = FILTER;
|
||||
int o = KEYDEV_TR_REPEATS +
|
||||
KEYDEV_TR_DELETE_MODIFIERS +
|
||||
KEYDEV_TR_DELETE_RELEASES +
|
||||
(opt & (GETKEY_MOD_SHIFT + GETKEY_MOD_ALPHA));
|
||||
keydev_set_transform(d, (keydev_transform_t){ o, tr.repeater });
|
||||
|
||||
while(1)
|
||||
{
|
||||
e = keydev_read(d);
|
||||
e = keydev_read(d, true, timeout);
|
||||
if(e.type == KEYEV_NONE && timeout && *timeout) break;
|
||||
|
||||
/* Skip repeat events that are not enabled by options */
|
||||
if(e.type == KEYEV_HOLD && !(opt & GETKEY_REP_ALL))
|
||||
{
|
||||
if(!(opt & GETKEY_REP_ARROWS))
|
||||
continue;
|
||||
if(e.key != KEY_LEFT && e.key != KEY_RIGHT &&
|
||||
e.key != KEY_UP && e.key != KEY_DOWN)
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef FX9860G
|
||||
/* Backlight toggle */
|
||||
else if((opt & GETKEY_BACKLIGHT) && e.type == KEYEV_DOWN &&
|
||||
e.key == KEY_OPTN && e.shift && !e.alpha)
|
||||
if((opt & GETKEY_BACKLIGHT) && e.type == KEYEV_DOWN &&
|
||||
((e.key == KEY_LIGHT) ||
|
||||
(e.key == KEY_OPTN && e.shift && !e.alpha)))
|
||||
{
|
||||
t6k11_backlight(-1);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Return-to-menu */
|
||||
else if((opt & GETKEY_MENU) && e.type == KEYEV_DOWN &&
|
||||
if((opt & GETKEY_MENU) && e.type == KEYEV_DOWN &&
|
||||
e.key == KEY_MENU && !e.shift && !e.alpha)
|
||||
{
|
||||
gint_osmenu();
|
||||
continue;
|
||||
}
|
||||
|
||||
else if(e.type == KEYEV_DOWN || e.type == KEYEV_HOLD)
|
||||
if(e.type == KEYEV_DOWN || e.type == KEYEV_HOLD)
|
||||
{
|
||||
/* Custom global features */
|
||||
bool accepted = false;
|
||||
if((opt & GETKEY_FEATURES) && feature_function)
|
||||
accepted = feature_function(e);
|
||||
/* Return if the even has not been accepted yet */
|
||||
/* Return if the event has not been accepted yet */
|
||||
if(!accepted) break;
|
||||
}
|
||||
}
|
||||
|
||||
keydev_set_transform(d, tr0);
|
||||
/* Restore previous transform settings */
|
||||
keydev_set_transform(d, tr);
|
||||
return e;
|
||||
}
|
||||
|
||||
/* getkey_repeat(): Set repeat delays for getkey() */
|
||||
void getkey_repeat(int first, int next)
|
||||
{
|
||||
rep_first = first;
|
||||
rep_next = next;
|
||||
}
|
||||
|
||||
/* getkey_repeat_profile(): Get the current repeat profile function */
|
||||
getkey_profile_t getkey_repeat_profile(void)
|
||||
{
|
||||
return repeat_profile;
|
||||
}
|
||||
|
||||
/* getkey_set_repeat_profile(): Set the repeat profile function */
|
||||
void getkey_set_repeat_profile(getkey_profile_t profile)
|
||||
{
|
||||
repeat_profile = profile;
|
||||
}
|
||||
|
||||
/* getkey_feature_function(): Get the current feature function */
|
||||
getkey_feature_t getkey_feature_function(void)
|
||||
{
|
||||
|
@ -119,4 +95,22 @@ void getkey_set_feature_function(getkey_feature_t function)
|
|||
feature_function = function;
|
||||
}
|
||||
|
||||
/* Deprecated repeat functions */
|
||||
|
||||
void getkey_repeat(int first, int next)
|
||||
{
|
||||
keydev_set_standard_repeats(keydev_std(), first * 1000, next * 1000);
|
||||
}
|
||||
|
||||
getkey_profile_t getkey_repeat_profile(void)
|
||||
{
|
||||
return keydev_transform(keydev_std()).repeater;
|
||||
}
|
||||
|
||||
/* getkey_set_repeat_profile(): Set the repeat profile function */
|
||||
void getkey_set_repeat_profile(getkey_profile_t profile)
|
||||
{
|
||||
keydev_transform_t tr = keydev_transform(keydev_std());
|
||||
tr.repeater = profile;
|
||||
keydev_set_transform(keydev_std(), tr);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/mpu/pfc.h>
|
||||
#include <gint/hardware.h>
|
||||
|
||||
/* This file is SH7705-only. */
|
||||
#ifdef FX9860G
|
||||
|
@ -54,7 +55,6 @@ static void iokbd_delay(void)
|
|||
uint8_t iokbd_row(int row)
|
||||
{
|
||||
if((unsigned)row > 9) return 0x00;
|
||||
row ^= 1;
|
||||
|
||||
int orig_PBCR = PFC.PBCR;
|
||||
int orig_PMCR = PFC.PMCR;
|
||||
|
@ -111,12 +111,55 @@ uint8_t iokbd_row(int row)
|
|||
return input;
|
||||
}
|
||||
|
||||
static const uint16_t SLIM_SC[] = {
|
||||
0x0940, 0x0920, 0x0910, 0x0908,
|
||||
0x0840, 0x0820, 0x0810, 0x0808, 0x0804, 0x0802,
|
||||
0x0740, 0x0720, 0x0710, 0x0708, 0x0704, 0x0702,
|
||||
0x0640, 0x0620, 0x0610, 0x0608, 0x0604, 0x0602,
|
||||
0x0540, 0x0520, 0x0510, 0x0508, 0x0504, 0x0502,
|
||||
0x0440, 0x0420, 0x0410, 0x0408, 0x0404, 0x0402,
|
||||
0x0340, 0x0320, 0x0310, 0x0308, 0x0304, 0x0302,
|
||||
0x0240, 0x0220, 0x0210, 0x0208, 0x0204, 0x0202,
|
||||
0x0120, 0x0110, 0x0108, 0x0104, 0x0102,
|
||||
0x0001
|
||||
};
|
||||
|
||||
#define SCANCODE_COUNT (sizeof(SLIM_SC) / sizeof(uint16_t))
|
||||
|
||||
static const uint16_t SLIM_TR[] = {
|
||||
0x0808, 0x0640, 0x0840, 0x0740,
|
||||
0x0940, 0x0620, 0x0720, 0x0710, 0x0804, 0x0802,
|
||||
0x0920, 0x0610, 0x0504, 0x0820, 0x0704, 0x0702,
|
||||
0x0910, 0x0608, 0x0502, 0x0810, 0x0280, 0x0180,
|
||||
0x0908, 0x0604, 0x0440, 0x0340, 0x0240, 0x0140,
|
||||
0x0904, 0x0602, 0x0420, 0x0320, 0x0220, 0x0120,
|
||||
0x0902, 0x0540, 0x0410, 0x0310, 0x0210, 0x0110,
|
||||
0x0708, 0x0520, 0x0408, 0x0308, 0x0208, 0x0108,
|
||||
0x0510, 0x0508, 0x0304, 0x0104, 0x0204,
|
||||
0x0001
|
||||
};
|
||||
|
||||
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data */
|
||||
void iokbd_scan(uint8_t *scan)
|
||||
{
|
||||
/* Scan each row independently; the gain from scanning them altogether
|
||||
/* Scan each row independently; the gain from scanning them all together
|
||||
is probably not worth it */
|
||||
for(int i = 0; i < 12; i++) scan[i] = iokbd_row(i);
|
||||
|
||||
/* Translate fx-9860G Slim scancodes to standard scancodes */
|
||||
if(isSlim())
|
||||
{
|
||||
uint8_t slim_scan[12];
|
||||
for(uint i = 0; i < 10; i++)
|
||||
{
|
||||
slim_scan[i] = scan[i];
|
||||
scan[i] = 0x00;
|
||||
}
|
||||
|
||||
for(uint i = 0; i < SCANCODE_COUNT; i++)
|
||||
if(slim_scan[SLIM_SC[i] >> 8] & (SLIM_SC[i] & 0xFF))
|
||||
scan[SLIM_TR[i] >> 8] |= (SLIM_TR[i] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//---
|
||||
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/cpu.h>
|
||||
#include <gint/drivers/keydev.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/util.h>
|
||||
|
@ -15,14 +16,24 @@ void keydev_init(keydev_t *d)
|
|||
memset(d, 0, sizeof *d);
|
||||
}
|
||||
|
||||
static int standard_repeater(GUNUSED int key, GUNUSED int duration, int count)
|
||||
{
|
||||
/* FIXME: Do not use the delays from keydev_std() on every device */
|
||||
keydev_t *d = keydev_std();
|
||||
return count ? d->rep_standard_next : d->rep_standard_first;
|
||||
}
|
||||
|
||||
//---
|
||||
// Driver event generation
|
||||
//---
|
||||
|
||||
/* queue_push(): Add an event in a device's buffer
|
||||
/* keydev_queue_push(): Add an event in a device's buffer
|
||||
Returns false if the event cannot be pushed. */
|
||||
static bool queue_push(keydev_t *d, keydev_event_t e)
|
||||
bool keydev_queue_push(keydev_t *d, key_event_t ev)
|
||||
{
|
||||
if(d->async_filter && !d->async_filter(ev))
|
||||
return true;
|
||||
|
||||
int next = (d->queue_end + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
if(next == d->queue_next)
|
||||
{
|
||||
|
@ -30,18 +41,18 @@ static bool queue_push(keydev_t *d, keydev_event_t e)
|
|||
return false;
|
||||
}
|
||||
|
||||
d->queue[d->queue_end] = e;
|
||||
d->queue[d->queue_end] = ev;
|
||||
d->queue_end = next;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* queue_poll(): Generate key events from the buffer
|
||||
Sets (*e) and returns true on success, otherwise false. */
|
||||
static bool queue_poll(keydev_t *d, keydev_event_t *e)
|
||||
static bool queue_poll(keydev_t *d, key_event_t *ev)
|
||||
{
|
||||
if(d->queue_next == d->queue_end) return false;
|
||||
|
||||
*e = d->queue[d->queue_next];
|
||||
*ev = d->queue[d->queue_next];
|
||||
d->queue_next = (d->queue_next + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
return true;
|
||||
}
|
||||
|
@ -49,65 +60,41 @@ static bool queue_poll(keydev_t *d, keydev_event_t *e)
|
|||
/* keydev_process_state(): Process the new keyboard states for events */
|
||||
void keydev_process_state(keydev_t *d, uint8_t scan[12])
|
||||
{
|
||||
key_event_t ev = { 0 };
|
||||
ev.time = d->time;
|
||||
|
||||
/* Compare new data with the internal state. Push releases before
|
||||
presses so that a key change occurring within a single analysis
|
||||
frame can be performed. This happens all the time when going back to
|
||||
the main MENU via gint_osmenu() on a keybind. */
|
||||
for(int row = 0; row < 12; row++)
|
||||
{
|
||||
int diff = ~scan[row] & d->state_now[row];
|
||||
if(!diff) continue;
|
||||
|
||||
/* Update internal status if the event can be pushed */
|
||||
keydev_event_t e = { d->time, diff, row, KEYEV_UP };
|
||||
if(queue_push(d, e)) d->state_now[row] &= scan[row];
|
||||
}
|
||||
for(int row = 0; row < 12; row++)
|
||||
ev.type = KEYEV_UP;
|
||||
for(int mode = 0; mode < 2; mode++)
|
||||
{
|
||||
int diff = scan[row] & ~d->state_now[row];
|
||||
if(!diff) continue;
|
||||
for(int row = 0; row < 12; row++)
|
||||
{
|
||||
int diff = mode
|
||||
? ~d->state_now[row] & scan[row]
|
||||
: d->state_now[row] & ~scan[row];
|
||||
if(!diff) continue;
|
||||
|
||||
keydev_event_t e = { d->time, diff, row, KEYEV_DOWN };
|
||||
if(queue_push(d, e)) d->state_now[row] |= scan[row];
|
||||
ev.key = (row << 4);
|
||||
|
||||
for(int mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
/* Update state only if the push succeeds */
|
||||
if((diff & mask) && keydev_queue_push(d, ev))
|
||||
d->state_now[row] = mode
|
||||
? d->state_now[row] | mask
|
||||
: d->state_now[row] & ~mask;
|
||||
ev.key++;
|
||||
}
|
||||
}
|
||||
|
||||
ev.type = KEYEV_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/* keydev_process_key(): Process a new key state for events */
|
||||
void keydev_process_key(keydev_t *d, int keycode, bool state)
|
||||
{
|
||||
/* If the key has changed state, push an event */
|
||||
int row = (keycode >> 4) ^ 1;
|
||||
int col = 0x80 >> (keycode & 0x7);
|
||||
|
||||
int prev = d->state_now[row] & col;
|
||||
if(state && !prev)
|
||||
{
|
||||
keydev_event_t e = { d->time, col, row, KEYEV_DOWN };
|
||||
if(queue_push(d, e)) d->state_now[row] |= col;
|
||||
}
|
||||
else if(!state && prev)
|
||||
{
|
||||
keydev_event_t e = { d->time, col, row, KEYEV_UP };
|
||||
if(queue_push(d, e)) d->state_now[row] &= ~col;
|
||||
}
|
||||
}
|
||||
|
||||
void keydev_tick(keydev_t *d, uint us)
|
||||
{
|
||||
d->time++;
|
||||
|
||||
if(d->rep_key != 0)
|
||||
{
|
||||
if(d->rep_delay >= 0)
|
||||
d->rep_delay = max(d->rep_delay - (int)us, 0);
|
||||
d->rep_time += us;
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
// Keyboard event generation
|
||||
//---
|
||||
|
||||
static bool can_repeat(keydev_t *d, int key)
|
||||
{
|
||||
int tr = d->tr.enabled;
|
||||
|
@ -117,79 +104,17 @@ static bool can_repeat(keydev_t *d, int key)
|
|||
return !(key == KEY_SHIFT && shift) && !(key == KEY_ALPHA && alpha);
|
||||
}
|
||||
|
||||
/* keydev_unqueue_event(): Retrieve the next keyboard event in queue */
|
||||
key_event_t keydev_unqueue_event(keydev_t *d)
|
||||
{
|
||||
/* Every device event is unfolded into up to 8 keyboard events, stored
|
||||
temporarily in the driver's structure. */
|
||||
keydev_event_t e;
|
||||
key_event_t kev = { .type = KEYEV_NONE, .time = d->time };
|
||||
|
||||
/* If there are no events, generate some */
|
||||
if(d->out_size == 0)
|
||||
{
|
||||
if(!queue_poll(d, &e)) return kev;
|
||||
int changed = e.changed;
|
||||
kev.type = e.kind;
|
||||
|
||||
for(int code = ((e.row ^ 1) << 4) | 0x7; code & 0x7; code--)
|
||||
{
|
||||
if(changed & 1)
|
||||
{
|
||||
kev.key = code;
|
||||
d->out[d->out_size++] = kev;
|
||||
}
|
||||
changed >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return one of the available events */
|
||||
kev = d->out[--(d->out_size)];
|
||||
|
||||
/* Update the event state accordingly */
|
||||
int row = (kev.key >> 4) ^ 1;
|
||||
int col = 0x80 >> (kev.key & 0x7);
|
||||
|
||||
if(kev.type == KEYEV_DOWN)
|
||||
{
|
||||
d->state_queue[row] |= col;
|
||||
/* Mark this key as the currently repeating one */
|
||||
if(d->rep_key == 0 && can_repeat(d, kev.key))
|
||||
{
|
||||
d->rep_key = kev.key;
|
||||
d->rep_count = -1;
|
||||
d->rep_time = 0;
|
||||
d->rep_delay = 0;
|
||||
}
|
||||
}
|
||||
if(kev.type == KEYEV_UP)
|
||||
{
|
||||
d->state_queue[row] &= ~col;
|
||||
/* End the current repeating streak */
|
||||
if(d->rep_key == kev.key)
|
||||
{
|
||||
d->rep_key = 0;
|
||||
d->rep_count = -1;
|
||||
d->rep_time = -1;
|
||||
d->rep_delay = -1;
|
||||
d->delayed_shift = 0;
|
||||
d->delayed_alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return kev;
|
||||
}
|
||||
|
||||
/* keydev_repeat_event(): Generate a repeat event if applicable */
|
||||
key_event_t keydev_repeat_event(keydev_t *d)
|
||||
{
|
||||
key_event_t e = { .type = KEYEV_NONE, .time = d->time };
|
||||
key_event_t ev = { 0 };
|
||||
ev.time = d->time;
|
||||
/* <Repeats> is disabled */
|
||||
if(!(d->tr.enabled & KEYDEV_TR_REPEATS)) return e;
|
||||
if(!(d->tr.enabled & KEYDEV_TR_REPEATS)) return ev;
|
||||
/* No key is being repeated, or it's too early */
|
||||
if(!d->rep_key || d->rep_delay != 0) return e;
|
||||
if(!d->rep_key || d->rep_delay != 0) return ev;
|
||||
/* Key is blocked by transform options modified during the streak */
|
||||
if(!can_repeat(d, d->rep_key)) return e;
|
||||
if(!can_repeat(d, d->rep_key)) return ev;
|
||||
|
||||
/* Plan the next repeat the currently-pressed key */
|
||||
int elapsed = (int16_t)(d->time - d->rep_time);
|
||||
|
@ -201,41 +126,98 @@ key_event_t keydev_repeat_event(keydev_t *d)
|
|||
d->rep_delay = d->tr.repeater(d->rep_key,elapsed,d->rep_count);
|
||||
|
||||
/* Don't return an event on the first call (it's a KEYEV_DOWN) */
|
||||
if(!d->rep_count) return e;
|
||||
if(!d->rep_count) return ev;
|
||||
|
||||
e.key = d->rep_key;
|
||||
e.type = KEYEV_HOLD;
|
||||
return e;
|
||||
ev.type = KEYEV_HOLD;
|
||||
ev.key = d->rep_key;
|
||||
return ev;
|
||||
}
|
||||
|
||||
void keydev_tick(keydev_t *d, uint us)
|
||||
{
|
||||
/* Generate the next repeat */
|
||||
key_event_t repeat = keydev_repeat_event(d);
|
||||
if(repeat.type != KEYEV_NONE)
|
||||
keydev_queue_push(d, repeat);
|
||||
|
||||
d->time++;
|
||||
|
||||
if(d->rep_key != 0)
|
||||
{
|
||||
if(d->rep_delay >= 0)
|
||||
d->rep_delay = max(d->rep_delay - (int)us, 0);
|
||||
d->rep_time += us;
|
||||
}
|
||||
}
|
||||
|
||||
keydev_async_filter_t keydev_async_filter(keydev_t const *d)
|
||||
{
|
||||
return d->async_filter;
|
||||
}
|
||||
|
||||
void keydev_set_async_filter(keydev_t *d, keydev_async_filter_t filter)
|
||||
{
|
||||
d->async_filter = filter;
|
||||
}
|
||||
|
||||
//---
|
||||
// Keyboard event generation
|
||||
//---
|
||||
|
||||
/* keydev_unqueue_event(): Retrieve the next keyboard event in queue */
|
||||
key_event_t keydev_unqueue_event(keydev_t *d)
|
||||
{
|
||||
key_event_t ev = { 0 };
|
||||
ev.time = d->time;
|
||||
if(!queue_poll(d, &ev))
|
||||
return ev;
|
||||
|
||||
/* Update the event state accordingly */
|
||||
int row = (ev.key >> 4);
|
||||
int col = 0x80 >> (ev.key & 0x7);
|
||||
|
||||
if(ev.type == KEYEV_DOWN)
|
||||
{
|
||||
d->state_queue[row] |= col;
|
||||
/* Mark this key as the currently repeating one */
|
||||
if(d->rep_key == 0 && can_repeat(d, ev.key))
|
||||
{
|
||||
d->rep_key = ev.key;
|
||||
d->rep_count = -1;
|
||||
d->rep_time = 0;
|
||||
d->rep_delay = 0;
|
||||
}
|
||||
}
|
||||
else if(ev.type == KEYEV_UP)
|
||||
{
|
||||
d->state_queue[row] &= ~col;
|
||||
/* End the current repeating streak */
|
||||
if(d->rep_key == ev.key)
|
||||
{
|
||||
d->rep_key = 0;
|
||||
d->rep_count = -1;
|
||||
d->rep_time = -1;
|
||||
d->rep_delay = -1;
|
||||
d->delayed_shift = 0;
|
||||
d->delayed_alpha = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ev;
|
||||
}
|
||||
__attribute__((alias("keydev_unqueue_event")))
|
||||
key_event_t _WEAK_keydev_unqueue_event(keydev_t *d);
|
||||
|
||||
/* keydev_keydown(): Check if a key is down according to generated events */
|
||||
bool keydev_keydown(keydev_t *d, int key)
|
||||
{
|
||||
int row = (key >> 4) ^ 1;
|
||||
int row = (key >> 4);
|
||||
int col = 0x80 >> (key & 0x7);
|
||||
|
||||
return (d->state_queue[row] & col) != 0;
|
||||
}
|
||||
|
||||
/* keydev_idle(): Check if all keys are released */
|
||||
bool keydev_idle(keydev_t *d, ...)
|
||||
{
|
||||
uint32_t *state = (void *)d->state_queue;
|
||||
uint32_t check[3] = { state[0], state[1], state[2] };
|
||||
int key;
|
||||
|
||||
va_list args;
|
||||
va_start(args, d);
|
||||
while((key = va_arg(args, int)))
|
||||
{
|
||||
int row = (key >> 4) ^ 1;
|
||||
int col = 0x80 >> (key & 0x7);
|
||||
((uint8_t *)check)[row] &= ~col;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return (check[0] == 0) && (check[1] == 0) && (check[2] == 0);
|
||||
}
|
||||
__attribute__((alias("keydev_keydown")))
|
||||
bool _WEAK_keydev_keydown(keydev_t *d, int key);
|
||||
|
||||
//---
|
||||
// Event transforms
|
||||
|
@ -266,30 +248,33 @@ void keydev_set_transform(keydev_t *d, keydev_transform_t tr)
|
|||
d->tr = tr;
|
||||
}
|
||||
|
||||
/* keydev_set_standard_repeats(): Enable a simple repeater */
|
||||
void keydev_set_standard_repeats(keydev_t *d, int first, int next)
|
||||
{
|
||||
d->rep_standard_first = first;
|
||||
d->rep_standard_next = next;
|
||||
d->tr.repeater = standard_repeater;
|
||||
}
|
||||
|
||||
/* keydev_read(): Retrieve the next transformed event */
|
||||
key_event_t keydev_read(keydev_t *d)
|
||||
key_event_t keydev_read(keydev_t *d, bool wait, volatile int *timeout)
|
||||
{
|
||||
#define opt(NAME) (d->tr.enabled & KEYDEV_TR_ ## NAME)
|
||||
key_event_t e;
|
||||
|
||||
while(1)
|
||||
{
|
||||
/* Repeat at the end of every tick, or if we're late */
|
||||
bool empty = (d->queue_next == d->queue_end) && !d->out_size;
|
||||
bool end_of_tick = (d->time_repeats == d->time - 1) && empty;
|
||||
bool late_repeat = (d->time_repeats <= d->time - 2);
|
||||
|
||||
if(end_of_tick || late_repeat)
|
||||
e = keydev_repeat_event(d);
|
||||
e = keydev_unqueue_event(d);
|
||||
if(e.type == KEYEV_NONE)
|
||||
e = keydev_unqueue_event(d);
|
||||
if(e.type == KEYEV_NONE)
|
||||
return e;
|
||||
{
|
||||
if(!wait || (timeout && *timeout))
|
||||
return e;
|
||||
sleep();
|
||||
continue;
|
||||
}
|
||||
|
||||
int k = e.key;
|
||||
|
||||
if(opt(INSTANT_SHIFT) || opt(INSTANT_ALPHA)) e.mod = 1;
|
||||
if(opt(DELAYED_SHIFT) || opt(DELAYED_ALPHA)) e.mod = 1;
|
||||
e.mod = (opt(ALL_MODS) != 0);
|
||||
|
||||
// <Instant SHIFT> and <Instant ALPHA>
|
||||
|
||||
|
@ -299,7 +284,6 @@ key_event_t keydev_read(keydev_t *d)
|
|||
e.shift |= keydev_keydown(d, KEY_SHIFT);
|
||||
if(opt(INSTANT_ALPHA) && k != KEY_ALPHA)
|
||||
e.alpha |= keydev_keydown(d, KEY_ALPHA);
|
||||
|
||||
}
|
||||
|
||||
// <Delayed SHIFT> and <Delayed ALPHA>
|
||||
|
@ -308,10 +292,9 @@ key_event_t keydev_read(keydev_t *d)
|
|||
{
|
||||
if(e.type == KEYEV_DOWN && k == KEY_SHIFT)
|
||||
{
|
||||
if(d->delayed_shift)
|
||||
d->delayed_shift = 0;
|
||||
else if(keydev_idle(d,KEY_SHIFT,0))
|
||||
if(!d->delayed_shift)
|
||||
d->pressed_shift = 1;
|
||||
d->delayed_shift = 0;
|
||||
}
|
||||
else if(e.type != KEYEV_UP && k == d->rep_key)
|
||||
{
|
||||
|
@ -329,10 +312,9 @@ key_event_t keydev_read(keydev_t *d)
|
|||
{
|
||||
if(e.type == KEYEV_DOWN && k == KEY_ALPHA)
|
||||
{
|
||||
if(d->delayed_alpha)
|
||||
d->delayed_alpha = 0;
|
||||
else if(keydev_idle(d,KEY_ALPHA,0))
|
||||
if(!d->delayed_alpha)
|
||||
d->pressed_alpha = 1;
|
||||
d->delayed_alpha = 0;
|
||||
}
|
||||
else if(e.type != KEYEV_UP && k == d->rep_key)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#include <gint/drivers/keydev.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* keydev_idle(): Check if all keys are released */
|
||||
bool keydev_idle(keydev_t *d, ...)
|
||||
{
|
||||
uint32_t *state = (void *)d->state_queue;
|
||||
uint32_t check[3] = { state[0], state[1], state[2] };
|
||||
int key;
|
||||
|
||||
va_list args;
|
||||
va_start(args, d);
|
||||
while((key = va_arg(args, int)))
|
||||
{
|
||||
int row = (key >> 4);
|
||||
int col = 0x80 >> (key & 0x7);
|
||||
((uint8_t *)check)[row] &= ~col;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return (check[0] == 0) && (check[1] == 0) && (check[2] == 0);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#include <gint/drivers/keydev.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool keydev_queue_push(keydev_t *d, key_event_t ev);
|
||||
|
||||
/* keydev_process_key(): Process a new key state for events */
|
||||
void keydev_process_key(keydev_t *d, int keycode, bool state)
|
||||
{
|
||||
/* If the key has changed state, push an event */
|
||||
int row = (keycode >> 4);
|
||||
int col = 0x80 >> (keycode & 0x7);
|
||||
|
||||
int prev = d->state_now[row] & col;
|
||||
if(state && !prev)
|
||||
{
|
||||
key_event_t ev = { 0 };
|
||||
ev.time = d->time;
|
||||
ev.type = KEYEV_DOWN;
|
||||
ev.key = keycode;
|
||||
if(keydev_queue_push(d, ev)) d->state_now[row] |= col;
|
||||
}
|
||||
else if(!state && prev)
|
||||
{
|
||||
key_event_t ev = { 0 };
|
||||
ev.time = d->time;
|
||||
ev.type = KEYEV_UP;
|
||||
ev.key = keycode;
|
||||
if(keydev_queue_push(d, ev)) d->state_now[row] &= ~col;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#include <gint/keyboard.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* keydown_all(): Check a set of keys for simultaneous input
|
||||
Returns non-zero if all provided keys are down. The list should end with an
|
||||
integer 0 as terminator. */
|
||||
int keydown_all(int key, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, key);
|
||||
|
||||
int st = 1;
|
||||
while(key && st)
|
||||
{
|
||||
st = keydown(key);
|
||||
key = va_arg(args, int);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
return st;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue