mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2025-04-04 09:37:10 +02:00
Compare commits
185 commits
Author | SHA1 | Date | |
---|---|---|---|
|
badbd0fd2b | ||
|
52c17ccb05 | ||
|
88eb5413a6 | ||
|
da05e89b1c | ||
|
f70f1d9ae3 | ||
|
371e593f1d | ||
|
58eb5d92d7 | ||
|
1fc7290b09 | ||
|
84a4dd7ca9 | ||
|
335326692f | ||
|
ea5f21d1dc | ||
|
4a2b60b785 | ||
|
e3105701d9 | ||
|
5b16888d9d | ||
|
5626713f40 | ||
|
789ba7caa5 | ||
|
5c3dc3220a | ||
|
7ac2ae25f2 | ||
|
d065a17063 | ||
|
0afd05848a | ||
|
b4c0fc7cea | ||
|
2755596d59 | ||
|
ab528b27a8 | ||
|
d9414bb6f2 | ||
|
4623d790cc | ||
|
faead4bc1d | ||
|
f8208d093b | ||
|
946d4d9a6f | ||
|
3a42b5d386 | ||
|
ee109e4977 | ||
|
5548bf68ab | ||
|
28bea2e1ce | ||
|
b2b1f00d04 | ||
|
5ff6a518f6 | ||
|
bc74586a2c | ||
|
e0ac25fbb0 | ||
|
b802e8edef | ||
|
d8005b5d49 | ||
|
e50769c824 | ||
|
542b4f0a81 | ||
|
1e220af616 | ||
|
caa68b08bf | ||
|
833025f5dd | ||
|
fd5a70e21b | ||
|
5655699cd8 | ||
|
235fa8a361 | ||
|
18a7b9ae5b | ||
|
2bb5294578 | ||
|
6d86c54507 | ||
|
16259deb20 | ||
|
6f53fa7842 | ||
|
5087a91101 | ||
|
7d3663483f | ||
|
6efcfa6018 | ||
|
0b7d8d6800 | ||
|
9c22ecea8d | ||
|
02a97719ac | ||
|
33dae5d218 | ||
|
21ff5c1d53 | ||
|
76c82beec6 | ||
|
eca05ec64c | ||
|
2f6ee59d43 | ||
|
3aa11b4252 | ||
|
eab3184dbb | ||
|
238bccddbe | ||
|
aa0ff7b10b | ||
|
0bea485f4b | ||
|
dcb876dfe0 | ||
|
82258b9f89 | ||
|
eb49f0258d | ||
|
b5f0f08183 | ||
|
1423f40a96 | ||
|
3a2a194693 | ||
|
4983849510 | ||
|
fc1f510288 | ||
|
c59b2f90fb | ||
|
eb1f9a35a1 | ||
|
f4e13afa84 | ||
|
911691461f | ||
|
aee67a76f3 | ||
|
c0671649df | ||
|
53c67af52d | ||
|
6910714c2c | ||
|
af5c16a3d3 | ||
|
1a61e97ef0 | ||
|
a091efc894 | ||
|
6f758cd36c | ||
|
18e0db3886 | ||
|
cf2b86deaa | ||
|
db50c9b192 | ||
|
42853103aa | ||
|
c28d06002f | ||
|
ac6b1c7d70 | ||
|
45881995e9 | ||
|
70dccc29da | ||
|
7e859169fe | ||
|
b3416dcc25 | ||
|
736b58f205 | ||
|
478fdaea76 | ||
|
d3b29c50e6 | ||
|
1272a6a71a | ||
|
723bae134b | ||
|
3bc3892524 | ||
|
211d236496 | ||
|
a4cf3516e7 | ||
|
177879d432 | ||
|
feb74a38ec | ||
|
3192078c4c | ||
|
a5fb6d3401 | ||
|
e2c507855f | ||
|
813af222fd | ||
|
984f162cb2 | ||
|
6426406043 | ||
|
2fe67412cc | ||
|
f807b528bb | ||
|
ad0da59de6 | ||
|
56e5c2452a | ||
|
8442e0b9cf | ||
|
5461d54083 | ||
|
386303136c | ||
|
8a6f3bfdce | ||
|
d6e8b096b5 | ||
|
4df3d69d8c | ||
|
74438f5da5 | ||
|
45fa6c87c2 | ||
|
5e35871c98 | ||
|
5f9553f3b8 | ||
|
93e055cfba | ||
|
d110ab608e | ||
|
45e90b55ba | ||
|
bbda77769d | ||
|
7b662987f8 | ||
|
252bd4eb41 | ||
|
48db9bf09d | ||
|
1d5675c99f | ||
|
16890ab71b | ||
|
c9df6326e4 | ||
|
97701e8eed | ||
|
a735e074d8 | ||
|
ddf340cccd | ||
|
5c331e5fa0 | ||
|
98fea4f2ec | ||
|
a4adc5e1bd | ||
|
b23ed9629e | ||
|
2b4075c8f9 | ||
|
e95a91d5ab | ||
|
3e5c45c5ad | ||
|
9924dc4684 | ||
|
4f9b141b79 | ||
|
02150d90e5 | ||
|
126d1506ac | ||
|
6ea2f991a3 | ||
|
4e1136d0ac | ||
|
1c3c9727a5 | ||
|
e8b4bcc9cb | ||
|
b17d4de4a8 | ||
|
f69f92b938 | ||
|
8c9c48a91e | ||
|
291c3cef17 | ||
|
b942bc5d19 | ||
|
c2ff07427b | ||
|
09c13676d3 | ||
|
780acb3fc9 | ||
|
818f950fff | ||
|
9468a8d725 | ||
|
fc6f7d3051 | ||
|
5a69e44078 | ||
|
7822899b1f | ||
|
d2f788a3fc | ||
|
667f43b45c | ||
|
ede19fc878 | ||
|
a4df076214 | ||
|
7a3604ccbb | ||
|
f219e5c882 | ||
|
904ab74984 | ||
|
8210524152 | ||
|
26c5b76037 | ||
|
e57efb5e37 | ||
|
0c2935055e | ||
|
fdadb0dd71 | ||
|
b10c065abe | ||
|
a3ce29b7b8 | ||
|
a7bcf6cd77 | ||
|
4223164063 | ||
|
93169e8803 |
221 changed files with 14610 additions and 3080 deletions
266
CMakeLists.txt
266
CMakeLists.txt
|
@ -1,14 +1,15 @@
|
|||
# Build system for the gint unikernel
|
||||
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(Gint VERSION 2.7.1 LANGUAGES C ASM)
|
||||
project(Gint VERSION 2.11.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)
|
||||
|
||||
|
@ -21,9 +22,10 @@ else()
|
|||
endif()
|
||||
configure_file(include/gint/config.h.in include/gint/config.h)
|
||||
|
||||
set(SOURCES_COMMON
|
||||
set(SOURCES
|
||||
# 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,9 +66,53 @@ 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
|
||||
# GDB remote serial protocol
|
||||
src/gdb/gdb.c
|
||||
src/gdb/gdb.S
|
||||
# Gray engine
|
||||
src/gray/engine.c
|
||||
src/gray/gclear.c
|
||||
src/gray/ggetpixel.c
|
||||
src/gray/gint_gline.c
|
||||
src/gray/gpixel.c
|
||||
src/gray/grect.c
|
||||
src/gray/gsubimage.c
|
||||
src/gray/gtext.c
|
||||
# Image library
|
||||
src/image/image_alloc.c
|
||||
src/image/image_alloc_palette.c
|
||||
src/image/image_alpha.c
|
||||
src/image/image_clear.c
|
||||
src/image/image_copy.c
|
||||
src/image/image_copy_alloc.c
|
||||
src/image/image_copy_palette.c
|
||||
src/image/image_create.c
|
||||
src/image/image_create_vram.c
|
||||
src/image/image_data_size.c
|
||||
src/image/image_decode_pixel.c
|
||||
src/image/image_fill.c
|
||||
src/image/image_free.c
|
||||
src/image/image_get_pixel.c
|
||||
src/image/image_hflip.c
|
||||
src/image/image_hflip_alloc.c
|
||||
src/image/image_linear.c
|
||||
src/image/image_linear.S
|
||||
src/image/image_linear_alloc.c
|
||||
src/image/image_rotate.c
|
||||
src/image/image_rotate_around.c
|
||||
src/image/image_rotate_around_scale.c
|
||||
src/image/image_scale.c
|
||||
src/image/image_set_palette.c
|
||||
src/image/image_set_pixel.c
|
||||
src/image/image_sub.c
|
||||
src/image/image_target.c
|
||||
src/image/image_valid.c
|
||||
src/image/image_vflip.c
|
||||
src/image/image_vflip_alloc.c
|
||||
# Interrupt Controller driver
|
||||
src/intc/intc.c
|
||||
src/intc/inth.s
|
||||
|
@ -77,6 +124,7 @@ set(SOURCES_COMMON
|
|||
src/kernel/kernel.c
|
||||
src/kernel/osmenu.c
|
||||
src/kernel/start.c
|
||||
src/kernel/start.S
|
||||
src/kernel/syscalls.S
|
||||
src/kernel/tlbh.S
|
||||
src/kernel/world.c
|
||||
|
@ -85,124 +133,200 @@ 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
|
||||
src/kmalloc/kmalloc.c
|
||||
# MMU driver
|
||||
src/mmu/mmu.c
|
||||
# Rendering
|
||||
# R61523 display driver
|
||||
src/r61523/r61523.c
|
||||
# R61524 display driver
|
||||
src/r61524/r61524.c
|
||||
# Format-agnostic rendering
|
||||
src/render/dcircle.c
|
||||
src/render/dellipse.c
|
||||
src/render/dhline.c
|
||||
src/render/dimage.c
|
||||
src/render/dline.c
|
||||
src/render/dpoly.c
|
||||
src/render/dprint.c
|
||||
src/render/drect_border.c
|
||||
src/render/dtext.c
|
||||
src/render/dupdate_hook.c
|
||||
src/render/dvline.c
|
||||
src/render/dwindow.c
|
||||
src/render/topti.c
|
||||
# RTC driver
|
||||
src/rtc/rtc.c
|
||||
src/rtc/rtc_ticks.c
|
||||
# Sound Processing Unit driver
|
||||
src/spu/spu.c
|
||||
# Timer Unit driver
|
||||
src/tmu/inth-etmu.s
|
||||
src/tmu/inth-tmu.s
|
||||
src/tmu/sleep.c
|
||||
src/tmu/tmu.c
|
||||
# USB driver
|
||||
src/usb/classes/ff-bulk.c
|
||||
src/usb/configure.c
|
||||
src/usb/pipes.c
|
||||
src/usb/setup.c
|
||||
src/usb/string.c
|
||||
src/usb/usb.c
|
||||
)
|
||||
set(SOURCES_FX
|
||||
# Gray engine
|
||||
src/gray/engine.c
|
||||
src/gray/gclear.c
|
||||
src/gray/gint_gline.c
|
||||
src/gray/gpixel.c
|
||||
src/gray/grect.c
|
||||
src/gray/gsubimage.c
|
||||
src/gray/gtext.c
|
||||
# Rendering
|
||||
src/render-fx/bopti-asm-gray-scsp.s
|
||||
src/render-fx/bopti-asm-gray.s
|
||||
src/render-fx/bopti-asm-mono-scsp.s
|
||||
src/render-fx/bopti-asm.s
|
||||
# RGB Rendering
|
||||
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
|
||||
src/render-cg/dupdate.c
|
||||
src/render-cg/dvram.c
|
||||
src/render-cg/dvram.S
|
||||
src/render-cg/gint_dline.c
|
||||
src/render-cg/topti-asm.S
|
||||
src/render-cg/topti.c
|
||||
# Fast RGB 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 RGB image renderer
|
||||
src/render-cg/image/image_rgb16.c
|
||||
src/render-cg/image/image_rgb16_effect.c
|
||||
src/render-cg/image/image_rgb16_swapcolor.c
|
||||
src/render-cg/image/image_rgb16_dye.c
|
||||
src/render-cg/image/image_p8.c
|
||||
src/render-cg/image/image_p8_effect.c
|
||||
src/render-cg/image/image_p8_swapcolor.c
|
||||
src/render-cg/image/image_p8_dye.c
|
||||
src/render-cg/image/image_p4.c
|
||||
src/render-cg/image/image_p4_clearbg_alt.c
|
||||
src/render-cg/image/image_p4_effect.c
|
||||
src/render-cg/image/image_p4_swapcolor.c
|
||||
src/render-cg/image/image_p4_dye.c
|
||||
# Mono rendering
|
||||
src/render-fx/bopti-asm-gray-scsp.S
|
||||
src/render-fx/bopti-asm-gray.S
|
||||
src/render-fx/bopti-asm-mono-scsp.S
|
||||
src/render-fx/bopti-asm.S
|
||||
src/render-fx/bopti.c
|
||||
src/render-fx/dclear.c
|
||||
src/render-fx/dgetpixel.c
|
||||
src/render-fx/dpixel.c
|
||||
src/render-fx/drect.c
|
||||
src/render-fx/dsubimage.c
|
||||
src/render-fx/dupdate.c
|
||||
src/render-fx/gint_dline.c
|
||||
src/render-fx/masks.c
|
||||
src/render-fx/topti-asm.s
|
||||
src/render-fx/topti-asm.S
|
||||
src/render-fx/topti.c
|
||||
# T6K11 driver
|
||||
# RTC driver
|
||||
src/rtc/rtc.c
|
||||
src/rtc/rtc_ticks.c
|
||||
# Sound Processing Unit driver
|
||||
src/spu/spu.c
|
||||
# T6K11 display driver
|
||||
src/t6k11/t6k11.c
|
||||
|
||||
# Timer Unit driver
|
||||
src/tmu/inth-etmu.s
|
||||
src/tmu/inth-tmu.s
|
||||
src/tmu/sleep.c
|
||||
src/tmu/tmu.c
|
||||
# UBC driver
|
||||
src/ubc/ubc.c
|
||||
src/ubc/ubc.S
|
||||
# USB driver
|
||||
src/usb/asyncio.c
|
||||
src/usb/classes/ff-bulk.c
|
||||
src/usb/classes/ff-bulk-gray.c
|
||||
)
|
||||
set(SOURCES_CG
|
||||
# R61524 driver
|
||||
src/r61524/r61524.c
|
||||
# Rendering
|
||||
src/render-cg/bopti-asm.s
|
||||
src/render-cg/bopti.c
|
||||
src/render-cg/dclear.c
|
||||
src/render-cg/dpixel.c
|
||||
src/render-cg/drect.c
|
||||
src/render-cg/dsubimage.c
|
||||
src/render-cg/dupdate.c
|
||||
src/render-cg/dvram.c
|
||||
src/render-cg/gint_dline.c
|
||||
src/render-cg/topti-asm.s
|
||||
src/render-cg/topti.c
|
||||
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
|
||||
# Video driver interface
|
||||
src/video/video.c
|
||||
)
|
||||
|
||||
set(ASSETS_FX src/font5x7.png)
|
||||
set(ASSETS_CG src/font8x9.png)
|
||||
set(ASSETS_FX src/font5x7.png src/gdb/icons-i1msb.png)
|
||||
set(ASSETS_CG src/font8x9.png src/gdb/icons-rgb565.png)
|
||||
set(ASSETS_CP ${ASSETS_CG})
|
||||
fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG})
|
||||
|
||||
include_directories(
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
"${FXSDK_COMPILER_INSTALL}/include/openlibm")
|
||||
add_compile_options(-Wall -Wextra -std=c11 -Os -fstrict-volatile-bitfields -mtas)
|
||||
"${PROJECT_BINARY_DIR}/include")
|
||||
add_compile_options(-Wall -Wextra -std=c11 -Os -g -fstrict-volatile-bitfields -mtas -flto)
|
||||
|
||||
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(${NAME} STATIC ${SOURCES} ${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(${NAME} STATIC ${SOURCES} ${ASSETS_CG} ${LINKER_SCRIPTS})
|
||||
endif()
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_G3A)
|
||||
add_compile_definitions(FX9860G_G3A)
|
||||
set(NAME "gint-fxg3a")
|
||||
set(LINKER_SCRIPTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50.ld"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50_fastload.ld")
|
||||
add_library(${NAME} STATIC ${SOURCES} ${ASSETS_FX} ${LINKER_SCRIPTS})
|
||||
endif()
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCP)
|
||||
add_compile_definitions(FXCP)
|
||||
set(NAME "gint-cp")
|
||||
set(LINKER_SCRIPTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcp_hh2.ld")
|
||||
add_library(${NAME} STATIC ${SOURCES} ${ASSETS_CP} ${LINKER_SCRIPTS})
|
||||
endif()
|
||||
|
||||
set_target_properties("${NAME}" PROPERTIES OUTPUT_NAME "${NAME}")
|
||||
|
||||
# Generate the archive with gcc-ar instead of ar as it will load the LTO plugin
|
||||
# which is required to generate a usable archive.
|
||||
set(CMAKE_C_ARCHIVE_CREATE "${CMAKE_C_COMPILER_AR} qcs <TARGET> <OBJECTS>")
|
||||
# Also the ranlib rule (useless because ar is passed the s flag anyway)
|
||||
set(CMAKE_C_ARCHIVE_FINISH "${CMAKE_C_COMPILER_RANLIB} <TARGET>")
|
||||
|
||||
# Generate linker scripts
|
||||
macro(generate_linker_script OUTPUT INPUT OPTIONS)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT}"
|
||||
COMMAND ${FXSDK_TOOLCHAIN}cpp "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}"
|
||||
-P -C -traditional-cpp ${OPTIONS} -o "${OUTPUT}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}")
|
||||
endmacro()
|
||||
generate_linker_script("fx9860g.ld" "fx9860g.ld.c" "")
|
||||
generate_linker_script("fxcg50.ld" "fxcg50.ld.c" "")
|
||||
generate_linker_script("fxcg50_fastload.ld" "fxcg50.ld.c" "-DFXCG50_FASTLOAD")
|
||||
generate_linker_script("fxcp_hh2.ld" "fxcp_hh2.ld.c" "")
|
||||
|
||||
# Library file
|
||||
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}")
|
||||
|
|
27
README.md
27
README.md
|
@ -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.
|
||||
|
@ -111,6 +105,17 @@ The available options are:
|
|||
% fxsdk build-cg install
|
||||
```
|
||||
|
||||
**"Cross-Building" a fx-9860G II project for fx-CG 50**
|
||||
|
||||
Programs written for fx-9860G can also be built to run on the fx-CG series,
|
||||
provided no low-level function or hardware-specific behavior (like syscalls) is
|
||||
used by the program.
|
||||
|
||||
```
|
||||
% fxsdk build-fxg3a
|
||||
% fxsdk build-fxg3a install
|
||||
```
|
||||
|
||||
## Using in CMake-based add-ins
|
||||
|
||||
Find the `Gint` module and link against `Gint::Gint`. gint declares the include
|
||||
|
@ -127,10 +132,16 @@ target_link_libraries(<target_name> Gint::Gint)
|
|||
Projects created with the fxSDK link with gint out-of-the-box. If you're not
|
||||
using the fxSDK, you will need to:
|
||||
|
||||
* Build with `-ffreestanding -fstrict-volatile-bitfields`;
|
||||
* Build with `-ffreestanding -fstrict-volatile-bitfields` and either
|
||||
`-DFX9860G` or `-DFXGC50`;
|
||||
* Link with `-T fx9860g.ld -lgint-fx -lopenlibm -lc` on fx-9860G;
|
||||
* Link with `-T fxcg50.ld -lgint-cg -lopenlibm -lc` on fx-CG 50.
|
||||
|
||||
To manually build the fx-CG executable for an fx-9860G program, mix as follows:
|
||||
|
||||
* Use platform flags `-DFXCG50 -DFX9860G_G3A`;
|
||||
* Link with `-T fxcg50.ld -lgint-fxg3a -lopenlibm -lc`.
|
||||
|
||||
If you don't have a standard library such as
|
||||
[Memallox's port of newlib](/PlaneteCasio/libc), you also need `-nostdlib`. I
|
||||
typically use `-m3 -mb` or `-m4-nofpu -mb` to specify the platform, but that
|
||||
|
|
69
TODO
69
TODO
|
@ -1,5 +1,9 @@
|
|||
Bugs to fix:
|
||||
* render: figure out why fx-CG dclear() now takes 4.1 ms instead of 2.6 ms
|
||||
|
||||
Extensions on existing code:
|
||||
* usb: add PC->calc reading, and interrupt pipes
|
||||
* clock: mono support
|
||||
* usb: and interrupt pipes
|
||||
* fs: support RAM files
|
||||
* fs: support USB streams as generic file descriptors
|
||||
* fugue: support glob() to abstract away BFile entirely
|
||||
|
@ -8,16 +12,71 @@ 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?
|
||||
* render: Properly document bopti fx. Also make it faster maybe?
|
||||
|
||||
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
|
||||
|
||||
Bits that need to abstract out into APIs:
|
||||
- display: Toggle backlight (or set level)
|
||||
-> Have a backlight property with min/max values
|
||||
@ getkey
|
||||
@ justui jscene
|
||||
- os: OS interface to access OS info, like in fxos
|
||||
@ usb/setup.c set_strings
|
||||
@ usb/classes/ff-bulk.c identify command
|
||||
- render: current video mode
|
||||
@ usb/classes/ff-bulk.c capture_vram
|
||||
- gray: make gray engine a subset of t6k11 which then supports two modes
|
||||
|
||||
Video formats:
|
||||
- i1msb
|
||||
- 2i1msb
|
||||
- rgb565
|
||||
|
||||
T6K11:
|
||||
mode 0 : i1msb 128x64 @64 Hz
|
||||
mode 1 : 2i1msb 128x64 @64 Hz
|
||||
R61524:
|
||||
mode 0 : rgb565 396x224 @? Hz
|
||||
mode 1 : i3??? 396x224 @? Hz
|
||||
mode 2 : i1msb 128x64 @? Hz # <- for g1a emulation :)
|
||||
mode 3 : 2i1msb 128x64 @? Hz # <- for g1a emulation :)
|
||||
|
||||
enum {
|
||||
/* Indexed 1-bit, 0 is white, 1 is black. Row-major, left-to-right, each
|
||||
row a set of 4-byte values with leftmost pixel on the MSB. */
|
||||
IMAGE_FORMAT_I1MSB,
|
||||
/* Indexed 2-bit: white, light gray, dark gray, black. Represented as a
|
||||
pair of I1MSB buffers, the first being bit #0, the second bit #1. */
|
||||
IMAGE_FORMAT_2I1MSB,
|
||||
};
|
||||
|
||||
fx-CP port progress.
|
||||
DONE:
|
||||
- kernel can start add-ins, take over interrupts (but no interrupt sources yet)
|
||||
- panic handler works (no interactive choices, only RESET)
|
||||
- no OS menu (likely never) and poweroff (maybe later)
|
||||
- memory: 64 kB total + OS heap (no gint arena, no MMU)
|
||||
- r61523: prototype display driver
|
||||
- can use the rendering pipeline with no DMA acceleration
|
||||
IN-PROGRESS:
|
||||
- unification of the image/video interface
|
||||
TODO:
|
||||
- hardware info: rom and fs type
|
||||
- quit handler
|
||||
- DMA acceleration for dclear and dupdate
|
||||
- keyboard and touch screen input
|
||||
- drivers: cpg, dma, intc, rtc, tmu
|
||||
- find more memory, 64 kB is not enough
|
||||
- benchmark memory and display performance
|
||||
- improve API and performance of display driver
|
||||
- way off: filesystem, remote debugging, usb
|
||||
|
|
|
@ -1,12 +1,30 @@
|
|||
macro(get_linker_script_path OUTVAR NAME)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_C_COMPILER} -print-file-name=${NAME}
|
||||
OUTPUT_VARIABLE ${OUTVAR}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
endmacro()
|
||||
|
||||
# Determine platform Code
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
|
||||
set(PC cg)
|
||||
set(INTF_DEFN FXCG50)
|
||||
set(INTF_LINK "-T;fxcg50.ld")
|
||||
get_linker_script_path(LS1 fxcg50_fastload.ld)
|
||||
get_linker_script_path(LS2 fxcg50.ld)
|
||||
set(INTF_LINKER_SCRIPT
|
||||
"$<IF:$<CONFIG:FastLoad>,${LS1},${LS2}>")
|
||||
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
set(PC fx)
|
||||
set(INTF_DEFN FX9860G)
|
||||
set(INTF_LINK "-T;fx9860g.ld")
|
||||
get_linker_script_path(INTF_LINKER_SCRIPT "fx9860g.ld")
|
||||
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_G3A)
|
||||
set(PC fxg3a)
|
||||
set(INTF_DEFN FX9860G_G3A)
|
||||
get_linker_script_path(INTF_LINKER_SCRIPT "fxcg50.ld")
|
||||
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fxCP)
|
||||
set(PC cp)
|
||||
set(INTF_DEFN FXCP)
|
||||
get_linker_script_path(INTF_LINKER_SCRIPT "fxcp_hh2.ld")
|
||||
else()
|
||||
message(FATAL_ERROR "gint: unknown fxSDK platform '${FXSDK_PLATFORM}'")
|
||||
endif()
|
||||
|
@ -14,18 +32,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 +65,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 +73,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 +82,9 @@ if(Gint_FOUND)
|
|||
IMPORTED_LOCATION "${GINT_PATH}"
|
||||
INTERFACE_COMPILE_OPTIONS -fstrict-volatile-bitfields
|
||||
INTERFACE_COMPILE_DEFINITIONS "${INTF_DEFN}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${FXSDK_COMPILER_INSTALL}/include/openlibm"
|
||||
INTERFACE_LINK_LIBRARIES "-lopenlibm;-lgcc"
|
||||
INTERFACE_LINK_OPTIONS "${INTF_LINK}"
|
||||
)
|
||||
INTERFACE_LINK_OPTIONS "-T;${INTF_LINKER_SCRIPT}"
|
||||
INTERFACE_LINK_DEPENDS "${INTF_LINKER_SCRIPT}")
|
||||
|
||||
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
|
||||
|
@ -62,19 +61,19 @@ SECTIONS
|
|||
*(.text.entry)
|
||||
|
||||
_bctors = . ;
|
||||
*(.ctors .ctors.*)
|
||||
KEEP(*(.ctors .ctors.*))
|
||||
_ectors = . ;
|
||||
|
||||
_bdtors = . ;
|
||||
*(.dtors .dtors.*)
|
||||
KEEP(*(.dtors .dtors.*))
|
||||
_edtors = . ;
|
||||
|
||||
_gint_exch_start = . ;
|
||||
*(.gint.exch)
|
||||
KEEP(*(.gint.exch))
|
||||
_gint_exch_size = ABSOLUTE(. - _gint_exch_start);
|
||||
|
||||
_gint_tlbh_start = . ;
|
||||
*(.gint.tlbh)
|
||||
KEEP(*(.gint.tlbh))
|
||||
_gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start);
|
||||
|
||||
*(.text .text.*)
|
||||
|
@ -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,10 +226,6 @@ SECTIONS
|
|||
*/
|
||||
|
||||
/DISCARD/ : {
|
||||
/* Debug sections (often from libgcc) */
|
||||
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
|
||||
.debug_ranges .debug_line .debug_str .debug_frame
|
||||
.debug_loclists .debug_rnglists)
|
||||
/* Java class registration (why are they even here?!) */
|
||||
*(.jcr)
|
||||
/* Asynchronous unwind tables: no C++ exception handling */
|
|
@ -10,10 +10,16 @@ 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 */
|
||||
|
@ -21,8 +27,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
|
||||
|
@ -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);
|
||||
|
@ -45,19 +50,19 @@ SECTIONS
|
|||
*(.text.entry)
|
||||
|
||||
_bctors = . ;
|
||||
*(.ctors .ctors.*)
|
||||
KEEP(*(.ctors .ctors.*))
|
||||
_ectors = . ;
|
||||
|
||||
_bdtors = . ;
|
||||
*(.dtors .dtors.*)
|
||||
KEEP(*(.dtors .dtors.*))
|
||||
_edtors = . ;
|
||||
|
||||
_gint_exch_start = . ;
|
||||
*(.gint.exch)
|
||||
KEEP(*(.gint.exch))
|
||||
_gint_exch_size = ABSOLUTE(. - _gint_exch_start);
|
||||
|
||||
_gint_tlbh_start = . ;
|
||||
*(.gint.tlbh)
|
||||
KEEP(*(.gint.tlbh))
|
||||
_gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start);
|
||||
|
||||
*(.text .text.*)
|
||||
|
@ -102,8 +107,7 @@ SECTIONS
|
|||
.bss (NOLOAD) : {
|
||||
_rbss = . ;
|
||||
|
||||
*(.bss.vram)
|
||||
*(.bss COMMON)
|
||||
*(.bss .bss.* COMMON)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > ram :NONE
|
||||
|
@ -144,29 +148,23 @@ 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);
|
||||
|
||||
_lgmapped = ABSOLUTE(0);
|
||||
_sgmapped = ABSOLUTE(0);
|
||||
_lreloc = ABSOLUTE(0);
|
||||
_sreloc = ABSOLUTE(0);
|
||||
|
||||
/* gint's uninitialized BSS section, going to static RAM. All the large
|
||||
data arrays will be located here */
|
||||
|
@ -188,11 +186,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 .debug_frame
|
||||
.debug_loclists .debug_rnglists)
|
||||
*(.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 */
|
206
fxcp_hh2.ld.c
Normal file
206
fxcp_hh2.ld.c
Normal file
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
Linker script for fxCP add-ins linking with HollyHock2.
|
||||
*/
|
||||
|
||||
OUTPUT_ARCH(sh4)
|
||||
OUTPUT_FORMAT(elf32-sh)
|
||||
ENTRY(_start_header)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
/* VRAM backup - our main fixed area to load code, size 320x528x2 */
|
||||
ram (rwx): o = 0x8c052800, l = 337920
|
||||
/* End-of-RAM area, 104 kB are safe to use according to experience. What
|
||||
happens is HollyHock itself is loaded at 0x8cfe0000 (128 kB before the
|
||||
end) so we have to fit after it. CPBoy uses 0x8cfe6000, follow that. */
|
||||
eram (rwx): o = 0x8cfe6000, l = 102144 /* 104k - 0x1100 */
|
||||
/* Space for the vbr */
|
||||
vbr (rwx): o = 0x8cffef00, l = 0x1100
|
||||
/* On-chip IL memory */
|
||||
ilram (rwx): o = 0xe5200000, l = 4k
|
||||
/* On-chip X and Y memory */
|
||||
xyram (rwx): o = 0xe500e000, l = 16k
|
||||
/* Flat binary space, used to have everything in the correct order in the
|
||||
binary. This is just for the bin file layout. */
|
||||
bin (rwx): o = 0x00000000, l = 1M
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/*
|
||||
** ROM sections
|
||||
*/
|
||||
|
||||
/* First address to be mapped to ROM */
|
||||
_brom = ORIGIN(ram);
|
||||
/* Size of ROM mappings */
|
||||
_srom = SIZEOF(.text) + SIZEOF(.rodata)
|
||||
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
|
||||
|
||||
/* Machine code going to ROM:
|
||||
- Entry function (.text.entry)
|
||||
- Compiler-provided constructors (.ctors) and destructors (.dtors)
|
||||
- All text from .text and .text.* (including user code) */
|
||||
.hh2 : {
|
||||
KEEP(*(.hh2.header))
|
||||
KEEP(*(.hh2.info))
|
||||
KEEP(*(.hh2.stage2))
|
||||
. = ALIGN(16);
|
||||
} > eram AT> bin
|
||||
|
||||
_gint_hh2_stage2_offset = SIZEOF(.hh2);
|
||||
_gint_hh2_stage2_load = ORIGIN(ram);
|
||||
|
||||
.text : {
|
||||
*(.text.entry)
|
||||
|
||||
_bctors = . ;
|
||||
*(.ctors .ctors.*)
|
||||
_ectors = . ;
|
||||
|
||||
_bdtors = . ;
|
||||
*(.dtors .dtors.*)
|
||||
_edtors = . ;
|
||||
|
||||
. = ALIGN(0x10);
|
||||
_gint_exch_start = . ;
|
||||
*(.gint.exch)
|
||||
_gint_exch_size = ABSOLUTE(. - _gint_exch_start);
|
||||
|
||||
. = ALIGN(0x10);
|
||||
_gint_tlbh_start = . ;
|
||||
*(.gint.tlbh)
|
||||
_gint_tlbh_size = ABSOLUTE(. - _gint_tlbh_start);
|
||||
|
||||
*(.text .text.*)
|
||||
} > ram AT> bin
|
||||
|
||||
/* gint's interrupt handler blocks (.gint.blocks)
|
||||
Although gint's blocks end up in VBR space, they are relocated at
|
||||
startup by the library/drivers, so we store them here for now */
|
||||
.gint.blocks ALIGN(4) : ALIGN(4) {
|
||||
KEEP(*(.gint.blocks));
|
||||
} > ram AT> bin
|
||||
|
||||
/* Exposed driver interfaces (.gint.drivers)
|
||||
The driver information is required to start and configure the
|
||||
driver, even if the symbols are not referenced */
|
||||
.gint.drivers ALIGN(4) : ALIGN(4) {
|
||||
_gint_drivers = . ;
|
||||
KEEP(*(SORT_BY_NAME(.gint.drivers.*)));
|
||||
_gint_drivers_end = . ;
|
||||
} > ram AT> bin
|
||||
|
||||
/* Read-only data going to ROM:
|
||||
- Resources or assets from fxconv or similar converters
|
||||
- Data marked read-only by the compiler (.rodata and .rodata.*) */
|
||||
.rodata ALIGN(4) : ALIGN(4) SUBALIGN(4) {
|
||||
/* Put these first, they need to be 4-aligned */
|
||||
*(.rodata.4)
|
||||
|
||||
*(.rodata .rodata.*)
|
||||
} > ram AT> bin
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** RAM sections
|
||||
*/
|
||||
|
||||
/* Read-write data sections going to RAM (.data and .data.*) */
|
||||
.data ALIGN(4) : ALIGN(4) {
|
||||
_ldata = LOADADDR(.data);
|
||||
_rdata = . ;
|
||||
|
||||
*(.data .data.*)
|
||||
/* Code that must remain mapped; no MMU on HH2, so fine */
|
||||
*(.gint.mapped)
|
||||
/* References to mapped code - no relocation needed */
|
||||
*(.gint.mappedrel)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > ram AT> bin
|
||||
|
||||
/* Read-write data sub-aligned to 4 bytes (mainly from fxconv) */
|
||||
.data.4 ALIGN(4) : ALIGN(4) SUBALIGN(4) {
|
||||
*(.data.4)
|
||||
. = ALIGN(16);
|
||||
} > ram AT> bin
|
||||
|
||||
_sdata = SIZEOF(.data) + SIZEOF(.data.4);
|
||||
|
||||
/* On-chip memory sections: IL, X and Y memory */
|
||||
|
||||
. = ORIGIN(ilram);
|
||||
.ilram ALIGN(4) : ALIGN(4) {
|
||||
_lilram = ABSOLUTE(LOADADDR(.ilram) + ORIGIN(ram));
|
||||
_rilram = . ;
|
||||
|
||||
*(.ilram)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > ilram AT> bin
|
||||
|
||||
. = ORIGIN(xyram);
|
||||
.xyram ALIGN(4) : ALIGN(4) {
|
||||
_lxyram = ABSOLUTE(LOADADDR(.xyram) + ORIGIN(ram));
|
||||
_rxyram = . ;
|
||||
|
||||
*(.xram .yram .xyram)
|
||||
|
||||
. = ALIGN(16);
|
||||
|
||||
} > xyram AT> bin
|
||||
|
||||
/* .text and .xyram are the first and last section in the stage-2 load. */
|
||||
_gint_hh2_stage2_size = LOADADDR(.xyram) + SIZEOF(.xyram) - LOADADDR(.text);
|
||||
|
||||
_silram = SIZEOF(.ilram);
|
||||
_sxyram = SIZEOF(.xyram);
|
||||
|
||||
_lgmapped = ABSOLUTE(0);
|
||||
_sgmapped = ABSOLUTE(0);
|
||||
_lreloc = ABSOLUTE(0);
|
||||
_sreloc = ABSOLUTE(0);
|
||||
_gint_region_vbr = ORIGIN(vbr);
|
||||
|
||||
/* BSS data going to RAM. The BSS section is to be stripped from the
|
||||
ELF file later, and wiped at startup */
|
||||
.bss (NOLOAD) : {
|
||||
_rbss = . ;
|
||||
|
||||
*(.bss .bss.* COMMON)
|
||||
|
||||
. = ALIGN(16);
|
||||
} > ram :NONE
|
||||
|
||||
_sbss = SIZEOF(.bss);
|
||||
|
||||
/* gint's uninitialized BSS section, going to static RAM. All the large
|
||||
data arrays will be located here */
|
||||
.gint.bss (NOLOAD) : {
|
||||
*(.gint.bss)
|
||||
. = ALIGN(16);
|
||||
|
||||
/* End of user RAM */
|
||||
_euram = . ;
|
||||
} > ram :NONE
|
||||
|
||||
_sgbss = SIZEOF(.gint.bss);
|
||||
|
||||
/*
|
||||
** Unused sections
|
||||
*/
|
||||
|
||||
/DISCARD/ : {
|
||||
/* SH3-only data sections */
|
||||
*(.gint.rodata.sh3 .gint.data.sh3 .gint.bss.sh3)
|
||||
/* Java class registration (why are they even here?!) */
|
||||
*(.jcr)
|
||||
/* Asynchronous unwind tables: no C++ exception handling */
|
||||
*(.eh_frame_hdr)
|
||||
*(.eh_frame)
|
||||
/* Comments or anything the compiler might generate */
|
||||
*(.comment)
|
||||
}
|
||||
}
|
|
@ -5,14 +5,17 @@
|
|||
configure:
|
||||
@ fxsdk build-fx -c $(GINT_CMAKE_OPTIONS)
|
||||
@ fxsdk build-cg -c $(GINT_CMAKE_OPTIONS)
|
||||
@ fxsdk build-fxg3a -c $(GINT_CMAKE_OPTIONS)
|
||||
|
||||
build:
|
||||
@ fxsdk build-fx
|
||||
@ fxsdk build-cg
|
||||
@ fxsdk build-fxg3a
|
||||
|
||||
install:
|
||||
@ fxsdk build-fx install
|
||||
@ fxsdk build-cg install
|
||||
@ fxsdk build-fxg3a install
|
||||
|
||||
uninstall:
|
||||
@ if [ -e build-fx/install_manifest.txt ]; then \
|
||||
|
@ -21,5 +24,8 @@ uninstall:
|
|||
@ if [ -e build-cg/install_manifest.txt ]; then \
|
||||
xargs rm -f < build-cg/install_manifest.txt; \
|
||||
fi
|
||||
@ if [ -e build-fxg3a/install_manifest.txt ]; then \
|
||||
xargs rm -f < build-fxg3a/install_manifest.txt; \
|
||||
fi
|
||||
|
||||
.PHONY: configure build install uninstall
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
#ifndef GINT_CONFIG
|
||||
#define GINT_CONFIG
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* GINT_VERSION: Latest tag and number of additional commits
|
||||
"2.1.0" = Release 2.1.0
|
||||
"2.1.1-5" = 5 commits after release 2.1.1 */
|
||||
|
@ -16,10 +14,50 @@
|
|||
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_HW_{FX,CG}: Identifies the type of hardware running the program. */
|
||||
#if defined(FX9860G)
|
||||
# define GINT_HW_FX 1
|
||||
# define GINT_HW_CG 0
|
||||
# define GINT_HW_CP 0
|
||||
# define GINT_HW_SWITCH(FX,CG,CP) (FX)
|
||||
#elif defined(FXCG50)
|
||||
# define GINT_HW_FX 0
|
||||
# define GINT_HW_CG 1
|
||||
# define GINT_HW_CP 0
|
||||
# define GINT_HW_SWITCH(FX,CG,CP) (CG)
|
||||
#elif defined(FXCP)
|
||||
# define GINT_HW_FX 0
|
||||
# define GINT_HW_CG 0
|
||||
# define GINT_HW_CP 1
|
||||
# define GINT_HW_SWITCH(FX,CG,CP) (CP)
|
||||
#endif
|
||||
|
||||
/* Shorthand to simplify definitions below. Won't be needed for long. */
|
||||
#if defined(FX9860G_G3A)
|
||||
# define GINT_FX9860G_G3A 1
|
||||
#else
|
||||
# define GINT_FX9860G_G3A 0
|
||||
#endif
|
||||
|
||||
/* GINT_OS_{FX,CG}: Identifies the type of OS API we're assuming. Currently I
|
||||
see no reason this would be different from hardware, but who knows. */
|
||||
#define GINT_OS_FX GINT_HW_FX
|
||||
#define GINT_OS_CG GINT_HW_CG
|
||||
#define GINT_OS_CP GINT_HW_CP
|
||||
#define GINT_OS_SWITCH GINT_HW_SWITCH
|
||||
|
||||
/* GINT_NO_OS_STACK: Disables using a chunk of the OS stack as a heap. The top
|
||||
section covering 355/512 ko is otherwise used. (fx-CG 50) */
|
||||
#cmakedefine GINT_NO_OS_STACK
|
||||
|
||||
/* GINT_USER_VRAM: Selects whether to store VRAMs in the user stack or in the
|
||||
OS stack. Deprecated, now controlled by GINT_NO_OS_STACK. (fx-CG 50) */
|
||||
#cmakedefine GINT_USER_VRAM
|
||||
|
||||
#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 +68,16 @@
|
|||
enabled or disabled at runtime. */
|
||||
#cmakedefine GINT_KMALLOC_DEBUG
|
||||
|
||||
/* GINT_USB_DEBUG: Selects whether USB debug functions are enabled */
|
||||
#cmakedefine GINT_USB_DEBUG
|
||||
|
||||
/* GINT_RENDER_DMODE: Selects whether the dmode override is available on
|
||||
rendering functions. */
|
||||
#define GINT_RENDER_DMODE (GINT_HW_FX || GINT_FX9860G_G3A)
|
||||
|
||||
/* GINT_RENDER_{MONO,RGB}: Enable the mono/rgb rendering API.
|
||||
Currently these are exclusive. */
|
||||
#define GINT_RENDER_MONO (GINT_HW_FX || GINT_FX9860G_G3A)
|
||||
#define GINT_RENDER_RGB ((GINT_HW_CG || GINT_HW_CP) && !GINT_FX9860G_G3A)
|
||||
|
||||
#endif /* GINT_CONFIG */
|
||||
|
|
|
@ -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))
|
||||
|
@ -33,6 +34,9 @@
|
|||
/* Transparent unions */
|
||||
#define GTRANSPARENT __attribute__((transparent_union))
|
||||
|
||||
/* Functions and globals that are visible through whole-program optimization */
|
||||
#define GVISIBLE __attribute__((externally_visible))
|
||||
|
||||
/* Weak symbols */
|
||||
#define GWEAK __attribute__((weak))
|
||||
|
||||
|
|
|
@ -144,6 +144,19 @@ typedef struct {
|
|||
/* GINT_CALL_NULL: Empty function call */
|
||||
#define GINT_CALL_NULL ((gint_call_t){ .function = NULL, .args = {} })
|
||||
|
||||
/* GINT_CALL_FLAG(): Build an indirect call to an odd address
|
||||
|
||||
This is used as a workaround to have an extra flag in gint_call_t structures
|
||||
without altering their size and exploits the fact that SuperH code has to be
|
||||
aligned on even addresses.
|
||||
It is up to the caller to notice the presence of the flag and realign the
|
||||
address properly.
|
||||
|
||||
This flag is currently only checked by gint_inth_callback to specify that the
|
||||
called function will take the interrupt context as its first argument. */
|
||||
#define GINT_CALL_FLAG(func, ...) \
|
||||
GINT_CALL((void*)((uint32_t)(func) | 1), __VA_ARGS__)
|
||||
|
||||
/* gint_call(): Perform an indirect call */
|
||||
static GINLINE int gint_call(gint_call_t cb)
|
||||
{
|
||||
|
@ -153,7 +166,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;
|
||||
}
|
||||
|
||||
//---
|
||||
|
|
36
include/gint/defs/timeout.h
Normal file
36
include/gint/defs/timeout.h
Normal file
|
@ -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,10 +15,26 @@
|
|||
#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;
|
||||
|
||||
//---
|
||||
// Short types
|
||||
//---
|
||||
|
||||
typedef unsigned int uint;
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t i8;
|
||||
typedef int16_t i16;
|
||||
typedef int32_t i32;
|
||||
typedef int64_t i64;
|
||||
|
||||
//---
|
||||
// Structure elements
|
||||
//----
|
||||
|
|
|
@ -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,27 +1,37 @@
|
|||
//---
|
||||
// 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
|
||||
#define GINT_DISPLAY_CG
|
||||
|
||||
#ifdef FXCG50
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/image.h>
|
||||
|
||||
/* Dimensions of the VRAM */
|
||||
#if GINT_HW_CG
|
||||
#define DWIDTH 396
|
||||
#define DHEIGHT 224
|
||||
#elif GINT_HW_CP
|
||||
#define DWIDTH 320
|
||||
#define DHEIGHT 528
|
||||
#endif
|
||||
|
||||
/* gint VRAM address. This value must always point to a 32-aligned buffer of
|
||||
size 177408. Any function can use it freely to perform rendering or store
|
||||
|
@ -57,92 +67,81 @@ 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 */
|
||||
// TODO: In gint 3 the triple buffering mechanism will be removed. Applications
|
||||
// that want to change VRAMs in-between every frame will be able to do so by
|
||||
// talking directly to the video interface to set VRAM, and wrapping dupdate.
|
||||
// Basically it will just no longer be handled by gint itself.
|
||||
void dsetvram(uint16_t *main, uint16_t *secondary);
|
||||
|
||||
/* dgetvram() - Get VRAM addresses
|
||||
Returns the VRAM buffer addresses used to render on fx-CG 50. */
|
||||
void dgetvram(uint16_t **main, uint16_t **secondary);
|
||||
|
||||
|
||||
//---
|
||||
// VRAM backup
|
||||
// On the fx-CP gint backs up the VRAM when loading and restores it when
|
||||
// leaving. While this is a transparent mechanism, the following parts of the
|
||||
// implementation are exposed at the internal API level.
|
||||
//---
|
||||
|
||||
/* Encode the VRAM contents between [in_start] and [in_end] at [output]. While
|
||||
[*in_end] is excluded from the encoding, it will be modified temporarily to
|
||||
serve as bounds check, so it must not be accessed asynchronously during the
|
||||
encoding. The size of the output is not known but is at most the distance
|
||||
between in_end and in_start in bytes. Setting output = in_start to compress
|
||||
in-place is supported. Returns the end pointer after encoding. */
|
||||
uint8_t *gint_vrambackup_encode(
|
||||
uint8_t *output, uint16_t *in_start, uint16_t *in_end);
|
||||
|
||||
/* Predefine palette based on the GUI at the Hollyhock loading screen. Contains
|
||||
109 entries plus a 110th dummy entry used internally as bounds check. */
|
||||
extern uint16_t gint_vrambackup_palette[110];
|
||||
|
||||
/* Get the pointer to the encoded VRAM backup created at load time. If [size]
|
||||
is not NULL, sets the size in [*size]. The pointer is heap allocated and
|
||||
remains owned by gint. */
|
||||
void *gint_vrambackup_get(int *size);
|
||||
|
||||
/* Decode the load-time VRAM backup back to VRAM. */
|
||||
void gint_vrambackup_show(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FXCG50 */
|
||||
#endif /* GINT_RENDER_RGB */
|
||||
|
||||
#endif /* GINT_DISPLAY_CG */
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef GINT_DISPLAY_FX
|
||||
#define GINT_DISPLAY_FX
|
||||
|
||||
#ifdef FX9860G
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -83,14 +83,33 @@ typedef struct
|
|||
uint height :12;
|
||||
|
||||
/* Raw layer data */
|
||||
uint8_t data[];
|
||||
uint8_t *data;
|
||||
|
||||
} GPACKED(4) bopti_image_t;
|
||||
|
||||
/* Image formats ("profiles") */
|
||||
enum {
|
||||
/* MONO: black/white, 1 layer (bw) */
|
||||
IMAGE_MONO = 0,
|
||||
/* MONO_ALPHA: black/white/transparent, 2 layers (alpha+bw) */
|
||||
IMAGE_MONO_ALPHA = 1,
|
||||
/* GRAY: black/dark/light/white, 2 layers (light+dark) */
|
||||
IMAGE_GRAY = 2,
|
||||
/* GRAY_ALPHA: black/dark/light/white/transparent, 3 layres
|
||||
(alpha+light+dark) */
|
||||
IMAGE_GRAY_ALPHA = 3,
|
||||
};
|
||||
|
||||
/* Number of layers in the image. */
|
||||
GINLINE static int image_layer_count(int profile)
|
||||
{
|
||||
return profile + (profile <= 1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FX9860G */
|
||||
#endif /* GINT_RENDER_MONO */
|
||||
|
||||
#endif /* GINT_DISPLAY_FX */
|
||||
|
|
|
@ -15,20 +15,19 @@ extern "C" {
|
|||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
#include <gint/config.h>
|
||||
|
||||
/* Platform-specific functions include VRAM management and the definition of
|
||||
the color_t type. */
|
||||
|
||||
#ifdef FX9860G
|
||||
#if GINT_RENDER_MONO
|
||||
#include <gint/display-fx.h>
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
#if GINT_RENDER_RGB
|
||||
#include <gint/display-cg.h>
|
||||
#endif
|
||||
|
||||
/* TODO: dinfo() or similar */
|
||||
|
||||
//---
|
||||
// Video RAM management
|
||||
//---
|
||||
|
@ -82,6 +81,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 +175,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
|
||||
|
@ -184,6 +213,53 @@ void dhline(int y, int color);
|
|||
@color Line color (same values as dline() are allowed) */
|
||||
void dvline(int x, int color);
|
||||
|
||||
//---
|
||||
// Other geometric shapes
|
||||
//---
|
||||
|
||||
/* dcircle(): Circle with border
|
||||
|
||||
This function renders a circle defined by its middle point (xm, ym) and a
|
||||
radius r. The border and fill can be set separately. This function uses
|
||||
Bresenham's algorithm and only renders circles of odd diameter; if you want
|
||||
an even diameter, use dellipse() with a bounding box.
|
||||
|
||||
@xm @ym Coordinates of center
|
||||
@r Radius (integer, >= 0)
|
||||
@fill_color Color of the disc inside the circle, C_NONE to disable
|
||||
@border_color Color of the circle itself, C_NONE to disable */
|
||||
void dcircle(int xm, int ym, int r, int fill_color, int border_color);
|
||||
|
||||
/* dellipse(): Ellipse with border
|
||||
|
||||
This function renders a non-rotated ellipse, defined by its bounding box.
|
||||
The border and fill can be set separately. The border is as 1-pixel thin
|
||||
border given by Bresenham's algorithm.
|
||||
|
||||
To render an ellipse from its center coordinates (x,y) and semi-major/minor
|
||||
axes a/b, use dellipse(x-a, y-b, x+a, y+b, fill_color, border_color).
|
||||
|
||||
@x1 @y1 @x2 @y2 Ellipse's bounding box (both bounds included, like drect())
|
||||
@fill_color Color of the surface inside the ellipse, C_NONE to disable
|
||||
@border_color Color of the ellipse itself, C_NONE to disable */
|
||||
void dellipse(int x1, int y1, int x2, int y2, int fill_color,
|
||||
int border_color);
|
||||
|
||||
/* dpoly(): Render an arbitrary polygon
|
||||
|
||||
Renders the polygon defined by vertices (x[i], y[i]) for 0 < i < N. A fill
|
||||
color and border color can be specified separately. For filling, N must be
|
||||
at least 3. For border, N must be at least 2.
|
||||
|
||||
Note: this is a fairly slow function, not designed for rendering triangles
|
||||
in 3D games. There are faster methods for that.
|
||||
|
||||
@x @y Arrays of vertex coordinates
|
||||
@N Number of vertices (length of x and y)
|
||||
@fill_color Color of the polygon's interior, C_NONE to disable
|
||||
@border_color Color of the polygon's border, C_NONE to disable */
|
||||
void dpoly(int const *x, int const *y, int N, int fill_color, int border_color);
|
||||
|
||||
//---
|
||||
// Text rendering (topti)
|
||||
//---
|
||||
|
@ -385,6 +461,28 @@ void dprint_opt(int x, int y, int fg, int bg, int halign, int valign,
|
|||
Calls dprint_opt() with bg=C_NONE, halign=DTEXT_LEFT and valign=DTEXT_TOP */
|
||||
void dprint(int x, int y, int fg, char const *format, ...);
|
||||
|
||||
//---
|
||||
// Text rendering utilities
|
||||
//---
|
||||
|
||||
/* dfont_glyph_index(): Obtain the glyph index of a Unicode code point
|
||||
|
||||
Returns the position of code_point in the character table of the given font,
|
||||
or -1 if code_point is not part of that set. */
|
||||
int dfont_glyph_index(font_t const *font, uint32_t code_point);
|
||||
|
||||
/* topti_glyph_offset(): Use a font index to find the location of a glyph
|
||||
|
||||
The provided glyph value (usuall obtained by dfont_glyph_index()) must be
|
||||
nonnegative. Returns the offset the this glyph's data in the font's data
|
||||
array. When using a proportional font, the size array is ignored. */
|
||||
int dfont_glyph_offset(font_t const *font, uint glyph_number);
|
||||
|
||||
/* dtext_utf8_next(): Read the next UTF-8 code point of a string
|
||||
Returns the next code point and advances the string. Returns 0 (NUL) at the
|
||||
end of the string. */
|
||||
uint32_t dtext_utf8_next(uint8_t const **str_pointer);
|
||||
|
||||
//---
|
||||
// Image rendering (bopti)
|
||||
//---
|
||||
|
|
|
@ -267,7 +267,7 @@ typedef void **gint_world_t;
|
|||
the section name and the linker then sorts by name. If your driver has a
|
||||
level lower than 10, you must add a leading 0. */
|
||||
#define GINT_DECLARE_DRIVER(level, name) \
|
||||
GSECTION(".gint.drivers." #level) extern gint_driver_t name;
|
||||
GSECTION(".gint.drivers." #level) GVISIBLE extern gint_driver_t name;
|
||||
|
||||
//---
|
||||
// Internal driver control
|
||||
|
|
293
include/gint/drivers/asyncio.h
Normal file
293
include/gint/drivers/asyncio.h
Normal file
|
@ -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;
|
||||
uint16_t events_lost;
|
||||
|
||||
/* 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/
|
||||
|
@ -185,14 +183,33 @@ typedef struct {
|
|||
// <Repeats>
|
||||
|
||||
/* Candidate key for repeats (or 0 if no key is candidate yet) */
|
||||
int rep_key;
|
||||
/* Number of repeats alreay sent */
|
||||
int rep_count;
|
||||
int16_t rep_key;
|
||||
/* Number of repeats already sent */
|
||||
int16_t 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;
|
||||
|
||||
/* State of keys that have changes since the last flip monitoring reset. */
|
||||
GALIGNED(4) uint8_t state_flips[12];
|
||||
|
||||
} keydev_t;
|
||||
|
||||
/* keydev_std(): Standard keyboard input device
|
||||
|
@ -218,6 +235,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 +254,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
|
||||
//---
|
||||
|
@ -256,14 +277,49 @@ bool keydev_idle(keydev_t *d, ...);
|
|||
/* keydev_keydown(): Check if a key is down according to generated events */
|
||||
bool keydev_keydown(keydev_t *d, int key);
|
||||
|
||||
/* keydev_keypressed(): Check if a key was pressed
|
||||
This compares to the state at the time of the last keydev_clear_flips(). */
|
||||
bool keydev_keypressed(keydev_t *d, int key);
|
||||
|
||||
/* keydev_keyreleased(): Check if a key was released
|
||||
This compares to the state at the time of the last keydev_clear_flips(). */
|
||||
bool keydev_keyreleased(keydev_t *d, int key);
|
||||
|
||||
/* keydev_clear_flips(): Reset flip info used by keypressed()/keyreleased() */
|
||||
void keydev_clear_flips(keydev_t *d);
|
||||
|
||||
/* keydev_transform(): Obtain current transform parameters */
|
||||
keydev_transform_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
|
||||
}
|
||||
|
|
26
include/gint/drivers/r61523.h
Normal file
26
include/gint/drivers/r61523.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
//---
|
||||
// gint:drivers:r61523 - Reneses R61523 driver
|
||||
//
|
||||
// This driver is used to control the 16-bit color LCD of the fx-CP 400.
|
||||
//---
|
||||
|
||||
#ifndef GINT_DRIVERS_R61523
|
||||
#define GINT_DRIVERS_R61523
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
/* r61523_display(): Update the entire display (320x528) */
|
||||
void r61523_display(uint16_t *vram);
|
||||
|
||||
/* r61523_win_set(): Set the display window */
|
||||
void r61523_win_set(int x1, int x2, int y1, int y2);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DRIVERS_R61523 */
|
|
@ -35,16 +35,45 @@ 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_display_mono_128x64(): Display a mono-style VRAM
|
||||
This experimental function updates the display with the contents of a 128x64
|
||||
VRAM, used with the fxg3a compilation target.
|
||||
TODO: Make that a video mode. */
|
||||
void r61524_display_mono_128x64(uint32_t *vram);
|
||||
|
||||
/* r61524_display_mono_128x64(): Display a gray-style VRAM
|
||||
This experimental function updates the display with the contents of a 128x64
|
||||
gray VRAM pair, used with the fxg3a compilation target.
|
||||
TODO: Make that a video mode. */
|
||||
void r61524_display_gray_128x64(uint32_t *light, uint32_t *dark);
|
||||
|
||||
/* r61524_start_frame(): Prepare the display for a region update
|
||||
|
||||
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) */
|
||||
|
@ -25,6 +28,7 @@ typedef struct {
|
|||
uint32_t SR;
|
||||
uint32_t VBR;
|
||||
uint32_t CPUOPM;
|
||||
uint32_t rN_bank[8];
|
||||
} cpu_state_t;
|
||||
|
||||
/* Direct Memory Access controller (see dma/dma.c) */
|
||||
|
@ -86,13 +90,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,7 +13,7 @@ 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
|
||||
|
||||
|
@ -78,7 +78,7 @@ void fs_free_descriptor(int fd);
|
|||
exact file descriptor reuse_fd is used. (This is useful to reopen standard
|
||||
streams.) In this case, the only possible return values are -1 and reuse_fd
|
||||
itself. */
|
||||
int open_generic(fs_descriptor_type_t *type, void *data, int reuse_fd);
|
||||
int open_generic(fs_descriptor_type_t const *type, void *data, int reuse_fd);
|
||||
|
||||
/* fs_path_normalize(): Normalize a path to eliminate ., .. and redundant /
|
||||
|
||||
|
|
105
include/gint/gdb.h
Normal file
105
include/gint/gdb.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
//---
|
||||
// gint:gdb - GDB remote serial protocol
|
||||
//---
|
||||
|
||||
#ifndef GINT_GDB
|
||||
#define GINT_GDB
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* gdb_cpu_state_t: State of the CPU when breaking
|
||||
This struct keep the same register indices as those declared by GDB to allow
|
||||
easy R/W without needing a "translation" table.
|
||||
See : https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/sh-tdep.c;
|
||||
h=c402961b80a0b4589243023ea5362d43f644a9ec;hb=4f3e26ac6ee31f7bc4b04abd
|
||||
8bdb944e7f1fc5d2#l361
|
||||
*/
|
||||
// TODO : Should we expose r*b*, ssr, spc ? are they double-saved when breaking
|
||||
// inside an interrupt handler ?
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t r0;
|
||||
uint32_t r1;
|
||||
uint32_t r2;
|
||||
uint32_t r3;
|
||||
uint32_t r4;
|
||||
uint32_t r5;
|
||||
uint32_t r6;
|
||||
uint32_t r7;
|
||||
uint32_t r8;
|
||||
uint32_t r9;
|
||||
uint32_t r10;
|
||||
uint32_t r11;
|
||||
uint32_t r12;
|
||||
uint32_t r13;
|
||||
uint32_t r14;
|
||||
uint32_t r15;
|
||||
uint32_t pc;
|
||||
uint32_t pr;
|
||||
uint32_t gbr;
|
||||
uint32_t vbr;
|
||||
uint32_t mach;
|
||||
uint32_t macl;
|
||||
uint32_t sr;
|
||||
} reg;
|
||||
uint32_t regs[23];
|
||||
};
|
||||
} gdb_cpu_state_t;
|
||||
|
||||
/* gdb_start(): Start the GDB remote serial protocol server
|
||||
|
||||
This function starts the GDB stub (program that communicates with GDB). It
|
||||
takes over USB, and returns once communication is established with a GDB
|
||||
instance on the computer. Normally this is called by the panic handler after
|
||||
a crash. It can also be called manually, in which case it should be followed
|
||||
by a call to gdb_main() with the current state (NULL if you don't have any).
|
||||
|
||||
If the GDB stub is already started, this is a no-op. Returns 0 on success
|
||||
and a negative value if the USB setup fails. */
|
||||
int gdb_start(void);
|
||||
|
||||
/* gdb_main(): Main GDB loop
|
||||
Main loop for when the program is in a paused state. Communicates with GDB
|
||||
over USB and mutates the cpu_state struct, memory and the UBC configuration
|
||||
accordingly. Returns once the program should resume. */
|
||||
void gdb_main(gdb_cpu_state_t *cpu_state);
|
||||
|
||||
/* gdb_start_on_exception(): Set the GDB stub to autostart on crashes
|
||||
This function sets a crash handler that starts the GDB stub whenever a
|
||||
System ERROR occurs. */
|
||||
// TODO: Get some visible signal on-screen when that happens
|
||||
void gdb_start_on_exception(void);
|
||||
|
||||
/* gdb_redirect_streams(): Select whether to redirect stdout/stderr
|
||||
|
||||
This function specifies whether stdout and stderr shall be redirected when
|
||||
debugging. If redirected, stdout and stderr will change from their default
|
||||
implementation (or the one supplied by the user via open_generic()) to a
|
||||
stubcall that prints text in the GDB console. This setting must be set
|
||||
before GDB starts, so usually just after gdb_start_on_exception().
|
||||
|
||||
This is intended to be used with debug methods that print text. For example,
|
||||
if the program has a status() function that prints information useful when
|
||||
debugging, one might want to invoke it from GDB with "call status()". With
|
||||
default stdout/stderr this would be at best impractical to read the output
|
||||
on the calculator, at worst buggy due to the program being interrupted.
|
||||
Redirecting stdout/stderr allows the result to be printed in GDB.
|
||||
|
||||
The default is to not redirect stdout/stderr. */
|
||||
void gdb_redirect_streams(bool redirect_stdout, bool redirect_stderr);
|
||||
|
||||
/** Stubcalls **/
|
||||
|
||||
/* Write to a file descriptor on the remote debugger. */
|
||||
void gdb_stubcall_write(int fd, void const *buf, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_GDB */
|
|
@ -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,18 +59,77 @@ 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);
|
||||
|
||||
/* gint_poweroff(): World switch and power off the calculator
|
||||
Shows the CASIO logo / poweroff screen if show_logo is true. The program
|
||||
will resume execution normally after powering on again. Don't call this
|
||||
function with show_logo=false as a result of pressing AC/ON because the
|
||||
calculator will restart immediately unless the user releases the AC/ON key
|
||||
extremely quickly. */
|
||||
void gint_poweroff(bool show_logo);
|
||||
|
||||
/* gint_set_onchip_save_mode(): Specify memory save policy for world switches
|
||||
|
||||
World switches can cause corruption in on-chip memory in two ways. First, if
|
||||
the calculator powers off while in the OS world, on-chip memory will be
|
||||
wiped because it's not powered when the calculator is off. Second, the OS
|
||||
may run code that overwrites on-chip memory (we don't have any examples of
|
||||
that but it's not part of the add-in interface).
|
||||
|
||||
As a result, the best option is to backup on-chip memory and restore it when
|
||||
loading back into the add-in. The issue is that there's 20 kB of on-chip
|
||||
memory (4 kB ILRAM + 16 kB XYRAM) and on some machines (fx-9860G-like) or in
|
||||
some applications we don't have that much memory lying around.
|
||||
|
||||
This function selects between three modes for handling this save:
|
||||
|
||||
* [Reinitialization mode]
|
||||
Don't save on-chip memory at all, instead just reinitialize the globals
|
||||
and leave the rest corrupted. This is useful if on-chip memory is used
|
||||
only for temporaries (e.g. frames in Azur), for which we don't care if
|
||||
they're corrupted, or code (e.g gint interrupt handling code), which will
|
||||
be reloaded and is otherwise a constant.
|
||||
|
||||
* [Backup mode]
|
||||
Save on-chip memory to a user-provided buffer of size GINT_ONCHIP_BUFSIZE.
|
||||
|
||||
* [Nothing mode]
|
||||
Don't do anything, and let the world switch function choose its preferred
|
||||
saving method. This allows application-specific compromises.
|
||||
|
||||
This is not a problem on SH3 because on-chip memory is only used on SH4. */
|
||||
enum {
|
||||
GINT_ONCHIP_REINITIALIZE = 0,
|
||||
GINT_ONCHIP_BACKUP = 1,
|
||||
GINT_ONCHIP_NOTHING = 2,
|
||||
};
|
||||
#define GINT_ONCHIP_BUFSIZE (20 << 10)
|
||||
|
||||
void gint_set_onchip_save_mode(int mode, void *ptr);
|
||||
|
||||
/* gint_get_onchip_save_mode(): Get the current on-chip memory save policy */
|
||||
int gint_get_onchip_save_mode(void **ptr);
|
||||
|
||||
/* This function has been moved to the INTC driver */
|
||||
__attribute__((deprecated("Use intc_handler() instead")))
|
||||
static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
|
||||
|
@ -89,10 +155,49 @@ static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
|
|||
.callback:
|
||||
.long _gint_inth_callback
|
||||
|
||||
The gint_call_t object can be built with GINT_CALL_FLAG to specify that the
|
||||
called function should get a pointer to a gint_inth_callback_context_t
|
||||
strcture as its first argument.
|
||||
|
||||
@call Address of a gint_call_t object
|
||||
Returns the return value of the callback. */
|
||||
extern int (*gint_inth_callback)(gint_call_t const *call);
|
||||
|
||||
/* gint_inth_callback_context_t: Context of the interrupted function when an
|
||||
interrupt is handled by gint_inth_callback. */
|
||||
typedef struct {
|
||||
uint32_t ssr;
|
||||
uint32_t spc;
|
||||
uint32_t r7;
|
||||
uint32_t r6;
|
||||
uint32_t r5;
|
||||
uint32_t r4;
|
||||
uint32_t r3;
|
||||
uint32_t r2;
|
||||
uint32_t r1;
|
||||
uint32_t r0;
|
||||
} gint_inth_callback_context_t;
|
||||
|
||||
/* gint_set_quit_handler(): Setup a call to be invoked when leaving the add-in
|
||||
|
||||
This function sets up the provided GINT_CALL() to be invoked when the
|
||||
add-in is unloaded, which is either when we exit from main() or when
|
||||
starting another application from the main menu. Crucially, this is only
|
||||
*after* selecting an application, not before opening the main menu. The
|
||||
quit handler is not invoked if the user re-enters the add-in.
|
||||
|
||||
This is based on the SetQuitHandler() syscall, and therefore the callback
|
||||
runs in the OS world by default. If [run_in_os_world] is set to false, a
|
||||
world switch will be performed to run the callback in the gint world.
|
||||
|
||||
TODO: Currently the quit handler is not called when exiting from main().
|
||||
TODO: Detail how this interacts with destructor functions!
|
||||
TODO: [run_in_os_world == false] is not honored yet (because unstable)
|
||||
|
||||
@call Callback to be performed when leaving add-in
|
||||
@run_in_os_world true to stay in OS world, false to use gint world */
|
||||
void gint_set_quit_handler(gint_call_t gcall, bool run_in_os_world);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/config.h>
|
||||
|
||||
/* For compatibility with ASM, include the following bits only in C code */
|
||||
#ifndef CPP_ASM
|
||||
|
||||
|
@ -40,14 +42,14 @@ void hw_detect(void);
|
|||
dual-platform version for libraries.
|
||||
Warning: this macro is also used hardcoded in exch.s. */
|
||||
|
||||
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
|
||||
#define isSH3() (gint[HWMPU] & 1)
|
||||
#define isSH4() (!isSH3())
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
#define isSH3() 0
|
||||
#define isSH4() 1
|
||||
#if GINT_HW_FX
|
||||
# define isSH3() (gint[HWMPU] & 1)
|
||||
# define isSH4() (!isSH3())
|
||||
# define isSlim() (gint[HWCALC] == HWCALC_FX9860G_SLIM)
|
||||
#elif GINT_HW_CG || GINT_HW_CP
|
||||
# define isSH3() 0
|
||||
# define isSH4() 1
|
||||
# define isSlim() 0
|
||||
#endif
|
||||
|
||||
/* This bit should be set in all data longwords except HWMPU, HWCPUVR, HWCPUPR
|
||||
|
@ -105,6 +107,10 @@ 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
|
||||
/* fx-CP 400 */
|
||||
#define HWCALC_FXCP400 8
|
||||
|
||||
/*
|
||||
** Keyboard
|
||||
|
|
846
include/gint/image.h
Normal file
846
include/gint/image.h
Normal file
|
@ -0,0 +1,846 @@
|
|||
//---
|
||||
// gint:image - Image manipulation and rendering
|
||||
//
|
||||
// Note: this module is currently only available on fx-CG.
|
||||
//
|
||||
// This header provides image manipulation functions. This mainly consists of a
|
||||
// reference-based image format, various access and modification functions, and
|
||||
// a number of high-performance transformations and rendering effects. If you
|
||||
// find yourself limited by rendering time, note that RAM writing speed is
|
||||
// often the bottleneck, and image rendering is much faster in Azur (which is
|
||||
// what the renderer was initially designed for).
|
||||
//
|
||||
// This module supports 3 bit depths: full-color 16-bit (RGB565), indexed 8-bit
|
||||
// (P8) and indexed 4-bit (P4). All three have an "alpha" variation where one
|
||||
// color is treated as transparent, leading to 6 total formats.
|
||||
//
|
||||
// The image renderers support so-called *dynamic effects*, which are image
|
||||
// transformations performed on-the-fly while rendering, without generating an
|
||||
// intermediate image. They comprise straightforward transformations that
|
||||
// achieve similar performance to straight rendering and can be combined to
|
||||
// some extent, which makes them reliable whenever applicable.
|
||||
//
|
||||
// For images of the RGB16 and P8 bit depths, the module supports a rich API
|
||||
// with image subsurfaces and a fairly large sets of geometric and color
|
||||
// transforms, including some in-place. P4 is not supported in most of these
|
||||
// functions because the dense bit packing is both impractical and slower for
|
||||
// these applications.
|
||||
//---
|
||||
|
||||
#ifndef GINT_IMAGE
|
||||
#define GINT_IMAGE
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/config.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
struct dwindow;
|
||||
|
||||
//---
|
||||
// Image structures
|
||||
//---
|
||||
|
||||
/* Image formats. Note that transparency really only indicates the default
|
||||
rendering method, as a transparent background can always be added or removed
|
||||
by a dynamic effect on any image. */
|
||||
enum {
|
||||
IMAGE_RGB565 = 0, /* RGB565 without alpha */
|
||||
IMAGE_RGB565A = 1, /* RGB565 with one transparent color */
|
||||
IMAGE_P8_RGB565 = 4, /* 8-bit palette, all opaque colors */
|
||||
IMAGE_P8_RGB565A = 5, /* 8-bit with one transparent color */
|
||||
IMAGE_P4_RGB565 = 6, /* 4-bit palette, all opaque colors */
|
||||
IMAGE_P4_RGB565A = 3, /* 4-bit with one transparent color */
|
||||
|
||||
IMAGE_DEPRECATED_P8 = 2,
|
||||
};
|
||||
|
||||
/* Quick macros to compare formats by storage size */
|
||||
#define IMAGE_IS_RGB16(format) \
|
||||
((format) == IMAGE_RGB565 || (format) == IMAGE_RGB565A)
|
||||
#define IMAGE_IS_P8(format) \
|
||||
((format) == IMAGE_P8_RGB565 || (format) == IMAGE_P8_RGB565A)
|
||||
#define IMAGE_IS_P4(format) \
|
||||
((format) == IMAGE_P4_RGB565 || (format) == IMAGE_P4_RGB565A)
|
||||
/* Check whether image format has an alpha color */
|
||||
#define IMAGE_IS_ALPHA(format) \
|
||||
((format) == IMAGE_RGB565A || \
|
||||
(format) == IMAGE_P8_RGB565A || \
|
||||
(format) == IMAGE_P4_RGB565A)
|
||||
/* Check whether image format uses a palette */
|
||||
#define IMAGE_IS_INDEXED(format) \
|
||||
(IMAGE_IS_P8(format) || IMAGE_IS_P4(format))
|
||||
|
||||
/* Image flags. These are used for memory management, mostly. */
|
||||
enum {
|
||||
IMAGE_FLAGS_DATA_RO = 0x01, /* Data is read-only */
|
||||
IMAGE_FLAGS_PALETTE_RO = 0x02, /* Palette is read-only */
|
||||
IMAGE_FLAGS_DATA_ALLOC = 0x04, /* Data is malloc()'d */
|
||||
IMAGE_FLAGS_PALETTE_ALLOC = 0x08, /* Palette is malloc()'d */
|
||||
};
|
||||
|
||||
/* image_t: gint's native bitmap image format
|
||||
Images of this format can be created through this header's API but also by
|
||||
using the fxSDK's built-in image converters with fxconv. */
|
||||
typedef struct
|
||||
{
|
||||
/* Color format, one of the IMAGE_* values defined above */
|
||||
uint8_t format;
|
||||
/* Additional flags, a combination of IMAGE_FLAGS_* values */
|
||||
uint8_t flags;
|
||||
/* Number of colors in the palette; this includes alpha for transparent
|
||||
images, as alpha is always the first entry.
|
||||
RGB16: 0
|
||||
P8: Ranges between 1 and 256
|
||||
P4: 16 */
|
||||
int16_t color_count;
|
||||
/* Full width and height, in pixels */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
/* Byte stride between lines */
|
||||
int stride;
|
||||
|
||||
/* Pixel data in row-major order, left to right.
|
||||
- RGB16: 2 bytes per entry, each row padded to 4 bytes for alignment.
|
||||
Each 2-byte value is an RGB565 color.
|
||||
- P8: 1 signed byte per entry. Each byte is a palette index shifted by
|
||||
128 (to access the color, use palette[<value>+128]).
|
||||
- P4: 4 bits per entry, each row padded to a full byte. Each entry is a
|
||||
direct palette index between 0 and 15. */
|
||||
void *data;
|
||||
|
||||
/* For P8 and P4, color palette. The number of entries allocated in the
|
||||
array is equal to the color_count attribute. */
|
||||
uint16_t *palette;
|
||||
|
||||
} GPACKED(4) image_t;
|
||||
|
||||
/* Dynamic effects: these transformations can be applied on images while
|
||||
rendering. Not all effects can be combined; unless specified otherwise:
|
||||
- HFLIP and VFLIP can both be added regardless of any other effect
|
||||
- At most one color effect can be applied */
|
||||
enum {
|
||||
/* Value 0x01 is reserved, because it is DIMAGE_NOCLIP, which although
|
||||
part of the old API still needs to be supported. */
|
||||
|
||||
/* [Any]: Skip clipping the command against the source image */
|
||||
IMAGE_NOCLIP_INPUT = 0x04,
|
||||
/* [Any]: Skip clipping the command against the output VRAM */
|
||||
IMAGE_NOCLIP_OUTPUT = 0x08,
|
||||
/* [Any]: Skip clipping both */
|
||||
IMAGE_NOCLIP = IMAGE_NOCLIP_INPUT | IMAGE_NOCLIP_OUTPUT,
|
||||
|
||||
// Geometric effects. These values should remain at exactly bit 8 and
|
||||
// following, or change gint_image_mkcmd() along with it.
|
||||
|
||||
/* [Any]: Flip image vertically */
|
||||
IMAGE_VFLIP = 0x0100,
|
||||
/* [Any]: Flip image horizontally */
|
||||
IMAGE_HFLIP = 0x0200,
|
||||
|
||||
// Color effects
|
||||
|
||||
/* [RGB565, P8_RGB565, P4_RGB565]: Make a color transparent
|
||||
Adds one argument:
|
||||
* Color to clear (RGB16: 16-bit value; P8/P4: palette index) */
|
||||
IMAGE_CLEARBG = 0x10,
|
||||
/* [RGB565, P8_RGB565, P4_RGB565]: Turn a color into another
|
||||
Adds two arguments:
|
||||
* Color to replace (RGB16: 16-bit value; P8/P4: palette index)
|
||||
* Replacement color (16-bit value) */
|
||||
IMAGE_SWAPCOLOR = 0x20,
|
||||
/* [RGB565A, P8_RGB565A, P4_RGB565A]: Add a background
|
||||
Adds one argument:
|
||||
* Background color (16-bit value) */
|
||||
IMAGE_ADDBG = 0x40,
|
||||
/* [RGB565A, P8_RGB565A, P4_RGB565A]: Dye all non-transparent pixels
|
||||
Adds one argument:
|
||||
* Dye color (16-bit value) */
|
||||
IMAGE_DYE = 0x80,
|
||||
};
|
||||
|
||||
//---
|
||||
// Image creation and destruction
|
||||
//---
|
||||
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
/* image_alloc(): Create a new (uninitialized) image
|
||||
|
||||
This function allocates a new image of the specified dimensions and format.
|
||||
It always allocates a new data array; if you need to reuse a data array, use
|
||||
the lower-level image_create() or image_create_sub().
|
||||
|
||||
The first parameters [width] and [height] specify the dimensions of the new
|
||||
image in pixels. The [format] should be one of the IMAGE_* formats, for
|
||||
example IMAGE_RGB565A or IMAGE_P4_RGB565.
|
||||
|
||||
This function does not specify or initialize the palette of the new image;
|
||||
use image_set_palette(), image_alloc_palette() or image_copy_palette()
|
||||
after calling this function.
|
||||
|
||||
The returned image structure must be freed with image_free() after use.
|
||||
|
||||
@width Width of the new image
|
||||
@height Height of the new image
|
||||
@format Pixel format; one of the IMAGE_* formats defined above */
|
||||
image_t *image_alloc(int width, int height, int format);
|
||||
|
||||
/* image_set_palette(): Specify an external palette for an image
|
||||
|
||||
This function sets the image's palette to the provided address. The number
|
||||
of entries allocated must be specified in size. It is also the caller's
|
||||
responsibility to ensure that the palette covers all the indices used in the
|
||||
image data.
|
||||
|
||||
The old palette, if owned by the image, is freed. If [owns=true] the
|
||||
palette's ownership is given to the image, otherwise it is kept external. */
|
||||
void image_set_palette(image_t *img, uint16_t *palette, int size, bool owns);
|
||||
|
||||
/* image_alloc_palette(): Allocate a new palette for an image
|
||||
|
||||
This function allocates a new palette for an image. The number of entries is
|
||||
specified in size; for P8 it can vary between 1 and 256, for P4 it is
|
||||
ignored (P4 images always have 16 colors).
|
||||
|
||||
The old palette, if owned by the image, is freed. The entries of the new
|
||||
palette are all initialized to 0. If size is -1, the format's default
|
||||
palette size is used. Returns true on success. */
|
||||
bool image_alloc_palette(image_t *img, int size);
|
||||
|
||||
/* image_copy_palette(): Copy another image's palette
|
||||
|
||||
This function allocates a new palette for an image, and initializes it with
|
||||
a copy of another image's palette. For P8 the palette can be resized by
|
||||
specifying a value other than -1 as the size; by default, the source image's
|
||||
palette size is used (within the limits of the new format). Retuns true on
|
||||
success. */
|
||||
bool image_copy_palette(image_t const *src, image_t *dst, int size);
|
||||
|
||||
/* image_create(): Create a bare image with no data/palette
|
||||
|
||||
This function allocates a new image structure but without data or palette.
|
||||
The [data] and [palette] members are NULL, [color_count] and [stride] are 0.
|
||||
|
||||
This function is useful to create images that reuse externally-provided
|
||||
information. It is intended that the user of this function sets the [data]
|
||||
and [stride] fields themselves, along with the IMAGE_FLAGS_DATA_ALLOC flag
|
||||
if the image should own its data.
|
||||
|
||||
The [palette] and [color_count] members can be set with image_set_palette(),
|
||||
image_alloc_palette(), image_copy_palette(), or manually.
|
||||
|
||||
The returned image structure must be freed with image_free() after use. */
|
||||
image_t *image_create(int width, int height, int format);
|
||||
|
||||
/* image_create_vram(): Create a reference to gint_vram
|
||||
|
||||
This function creates a new RGB565 image that references gint_vram. Using
|
||||
this image as target for transformation functions can effectively render
|
||||
transformed images to VRAM.
|
||||
|
||||
The value of gint_vram is captured when this function is called, and does
|
||||
not update after dupdate() when triple-buffering is used. The user should
|
||||
account for this option. (Using this function twice then replacing one of
|
||||
the [data] pointers is allowed.)
|
||||
|
||||
The VRAM image owns no data but it does own its own structure so it must
|
||||
still be freed with image_free() after use. */
|
||||
image_t *image_create_vram(void);
|
||||
|
||||
/* image_free(): Free and image and the data it owns
|
||||
|
||||
This function frees the provided image structure and the data that it owns.
|
||||
Images converted by fxconv should not be freed; nonetheless, this functions
|
||||
distinguishes them and should work. Images are not expected to be created on
|
||||
the stack.
|
||||
|
||||
If the image has the IMAGE_FLAGS_DATA_ALLOC flag, the data pointer is also
|
||||
freed. Similarly, the image has the IMAGE_FLAGS_PALETTE_ALLOC flag, the
|
||||
palette is freed. Make sure to not free images when references to them still
|
||||
exist, as this could cause the reference's pointers to become dangling. */
|
||||
void image_free(image_t *img);
|
||||
|
||||
//---
|
||||
// Basic image access and information
|
||||
//---
|
||||
|
||||
/* image_valid(): Check if an image is valid
|
||||
An image is considered valid if it has a valid profile, a non-NULL data
|
||||
pointer, and for palette formats a valid palette pointer. */
|
||||
bool image_valid(image_t const *img);
|
||||
|
||||
/* image_alpha(): Get the alpha value for an image format
|
||||
|
||||
This function returns the alpha value for any specific image format:
|
||||
* RGB16: 0x0001
|
||||
* P8: -128 (0x80)
|
||||
* P4: 0
|
||||
For non-transparent formats, it returns a value that is different from all
|
||||
valid pixel values of the format, which means it is always safe to compare a
|
||||
pixel value to the image_alpha() of the format. */
|
||||
int image_alpha(int format);
|
||||
|
||||
/* image_get_pixel(): Read a pixel from the data array
|
||||
|
||||
This function reads a pixel from the image's data array at position (x,y).
|
||||
It returns the pixel's value, which is either a full-color value (RGB16) or
|
||||
a possibly-negative palette index (P8/P4). See the description of the [data]
|
||||
field of image_t for more details. The value of the pixel can be decoded
|
||||
into a 16-bit color either manually or by using the image_decode_pixel()
|
||||
function.
|
||||
|
||||
Note that reading large amounts of image data with this function will be
|
||||
slow; if you need reasonable performance, consider iterating on the data
|
||||
array manually. */
|
||||
int image_get_pixel(image_t const *img, int x, int y);
|
||||
|
||||
/* image_decode_pixel(): Decode a pixel value
|
||||
|
||||
This function decodes a pixel's value obtained from the data array (for
|
||||
instance with image_get_pixel()). For RGB16 formats this does nothing, but
|
||||
for palette formats this accesses the palette at a suitable position.
|
||||
|
||||
Note that reading large amounts of data with this function will be slow; if
|
||||
you need reasonable performance, consider inlining the format-specific
|
||||
method or iterating on the data array manually. */
|
||||
int image_decode_pixel(image_t const *img, int pixel);
|
||||
|
||||
/* image_data_size(): Compute the size of the [data] array
|
||||
This function returns the size of the data array, in bytes. This can be used
|
||||
to duplicate it. Note that for sub-images this is a subsection of another
|
||||
image's data array, and might be much larger than the sub-image. */
|
||||
int image_data_size(image_t const *img);
|
||||
|
||||
//---
|
||||
// Basic image modifications
|
||||
//---
|
||||
|
||||
/* image_set_pixel(): Set a pixel in the data array
|
||||
|
||||
This function writes a pixel into the image's data array at position (x,y).
|
||||
The pixel value must be of the proper format, as specified in the definition
|
||||
of the [data] field of image_t.
|
||||
|
||||
Formats: RGB16, P8, P4 */
|
||||
void image_set_pixel(image_t const *img, int x, int y, int value);
|
||||
|
||||
/* image_copy(): Convert and copy an image
|
||||
|
||||
This function copies an image into another image while converting certain
|
||||
formats. Unlike transforms, this function does clip, so there are no
|
||||
conditions on the size of the target.
|
||||
|
||||
If [copy_alpha] is true, transparent pixels are copied verbatim, which
|
||||
effectively replaces the top-left corner of [dst] with [src]. If it's false,
|
||||
transparent pixels of [src] are skipped, effectively rendering [src] over
|
||||
the top-left corner of [src].
|
||||
|
||||
This function converts between all formats except from RGB16 to P8/P4, since
|
||||
this requires generating a palette (which is a complex endeavour).
|
||||
Conversions from P8/P4 to RGB16 simply decode the palette. Conversions
|
||||
between P8/P4 preserve the contents but renumber the palette entries. From
|
||||
P4 to P8, the image is always preserved. From P8 to P4, the image is only
|
||||
preserved if it has less than 16 colors (this is intended to allow P4 images
|
||||
to be converted to P8 for edition by this library, and then back to P4). The
|
||||
following table summarizes the conversions:
|
||||
|
||||
Source format → RGB16 P8 P4
|
||||
Target format ↓ +-----------+----------------+------------------+
|
||||
RGB16 | Copy Decode palette Decode palette |
|
||||
P8 | - Copy Enlarge palette |
|
||||
P4 | - Narrow palette Copy |
|
||||
+-----------+----------------+------------------+
|
||||
|
||||
Note that conversions to RGB16 are not lossless because RGB565, P8 and P4
|
||||
can represent any color; if a color equal to image_alpha(IMAGE_RGB565A) is
|
||||
found during conversion, this function transforms it slightly to look
|
||||
similar instead of erroneously generating a transparent pixel.
|
||||
|
||||
Formats: RGB16 → RGB16, P8 → Anything, P4 → Anything
|
||||
Size requirement: none (clipping is performed)
|
||||
Supports in-place: No (useless) */
|
||||
void image_copy(image_t const *src, image_t *dst, bool copy_alpha);
|
||||
|
||||
/* image_copy_alloc(): Convert and copy into a new image
|
||||
This function is similar to image_copy(), but it allocates a target image of
|
||||
the desired format before copying. */
|
||||
image_t *image_copy_alloc(image_t const *src, int new_format);
|
||||
|
||||
/* image_fill(): Fill an image with a single pixel value */
|
||||
void image_fill(image_t *img, int value);
|
||||
|
||||
/* image_clear(): Fill a transparent image with its transparent value */
|
||||
void image_clear(image_t *img);
|
||||
|
||||
//---
|
||||
// Sub-image extraction
|
||||
//---
|
||||
|
||||
/* image_sub(): Build a reference to a sub-image
|
||||
|
||||
This function is used to create references to sub-images of RGB16 and P8
|
||||
images. The [data] pointer of the sub-image points somewhere within the data
|
||||
array of the source, and its [palette] pointer is identical to the source's.
|
||||
|
||||
The last parameter is a pointer to a preallocated image_t structure (usually
|
||||
on the stack) that gets filled with the data. Doing this instead of
|
||||
allocating a new object with malloc() means that there is no need to
|
||||
image_free() the sub-image, and thus it can be used inline:
|
||||
|
||||
image_t tmp;
|
||||
image_hflip(src, image_sub(dst, x, y, w, h, &tmp));
|
||||
|
||||
A preprocessor macro is used to make the last parameter optional. If it's
|
||||
not specified, a pointer to a static image_t will be returned instead. This
|
||||
is useful in inline calls as shown above, which then simplify to:
|
||||
|
||||
image_hflip(src, image_sub(dst, x, y, w, h));
|
||||
|
||||
However, another call to image_sub() or image_at() will override the
|
||||
sub-image, so you should only use this in such temporary settings. If you
|
||||
need multiple image_sub() or image_at() calls in the same statement, only
|
||||
one can use the short form.
|
||||
|
||||
If the requested rectangle does not intersect the source, the sub-image will
|
||||
be of dimension 0x0. If the image format does not support sub-images (P4),
|
||||
the sub-image will test invalid with image_valid(). */
|
||||
image_t *image_sub(image_t const *src, int x, int y, int w, int h,
|
||||
image_t *dst);
|
||||
|
||||
/* Make the last parameter optional */
|
||||
#define image_sub1(src, x, y, w, h, dst, ...) image_sub(src, x, y, w, h, dst)
|
||||
#define image_sub(...) image_sub1(__VA_ARGS__, NULL)
|
||||
|
||||
/* image_at(): Build a reference to a position within a sub-image */
|
||||
#define image_at(img, x, y) image_sub(img, x, y, -1, -1)
|
||||
|
||||
//---
|
||||
// Geometric image transforms
|
||||
//
|
||||
// All geometric transforms render to position (0,0) of the target image and
|
||||
// fail if the target image is not large enough to hold the transformed result
|
||||
// (unlike the rendering functions which render only the visible portion).
|
||||
//
|
||||
// To render at position (x,y) of the target image, use img_at(). For instance:
|
||||
// image_hflip(src, image_at(dst, x, y));
|
||||
//
|
||||
// Each transform function has an [_alloc] variant which does the same
|
||||
// transform but allocates the target image on the fly and returns it. Remember
|
||||
// that allocation can fail, so you need to check whether the returned image is
|
||||
// valid.
|
||||
//
|
||||
// (You can still pass invalid images to transform functions. The invalid image
|
||||
// will be ignored or returned unchanged, so you can chain calls and check for
|
||||
// validity at the end of the chain.)
|
||||
//
|
||||
// Some functions support in-place transforms. This means they can be called
|
||||
// with the source as destination, and will transform the image without needing
|
||||
// new memory. For instance, image_hflip(src, src) flips in-place and replaces
|
||||
// src with a flipped version of itself.
|
||||
//
|
||||
// (However, it is not possible to transform in-place if the source and
|
||||
// destination intersect in non-trivial ways. The result will be incorrect.)
|
||||
//
|
||||
// When transforming to a new image, transparent pixels are ignored, so if the
|
||||
// destination already has some data, it will not be erased automatically. Use
|
||||
// image_clear() beforehand to achieve that effect. This allows alpha blending
|
||||
// while transforming, which is especially useful on the VRAM.
|
||||
//---
|
||||
|
||||
/* image_hflip(): Flip horizontally
|
||||
Formats: RGB16, P8
|
||||
Size requirement: destination at least as large as source (no clipping)
|
||||
Supports in-place: Yes */
|
||||
void image_hflip(image_t const *src, image_t *dst, bool copy_alpha);
|
||||
image_t *image_hflip_alloc(image_t const *src);
|
||||
|
||||
/* image_vflip(): Flip vertically
|
||||
Formats: RGB16, P8
|
||||
Size requirement: destination at least as large as source (no clipping)
|
||||
Supports in-place: Yes */
|
||||
void image_vflip(image_t const *src, image_t *dst, bool copy_alpha);
|
||||
image_t *image_vflip_alloc(image_t const *src);
|
||||
|
||||
/* image_linear(): Linear transformation
|
||||
|
||||
This function implements a generic linear transformation. This is a powerful
|
||||
function that can perform any combination of rotation, mirroring and scaling
|
||||
with nearest-neighbor sampling.
|
||||
|
||||
The [image_linear_map] structure defines the settings for the transform.
|
||||
Users familiar with linear algebra might want to use it directly, but they
|
||||
are most conveniently generated with the rotation and scaling functions
|
||||
listed below.
|
||||
|
||||
Note: Currently the structure for the transform is modified by the
|
||||
operation and cannot be reused.
|
||||
|
||||
The image_linear_alloc() variant allocates a new image in addition to
|
||||
performing the transform. The image is created with size (map->dst_w,
|
||||
map->dst_h) which is always a reasonable default. If a target image of
|
||||
smaller size is supplied to image_linear(), clipping is performed; only the
|
||||
top-left corner of the full output is actually rendered.
|
||||
|
||||
Formats: RGB16, P8
|
||||
Size requirement: none (clipping is performed)
|
||||
Supports in-place: No */
|
||||
|
||||
struct image_linear_map {
|
||||
/* Dimensions of the source and destination */
|
||||
int src_w, src_h, dst_w, dst_h;
|
||||
/* Input and output stride in bytes */
|
||||
int src_stride, dst_stride;
|
||||
|
||||
/* The following parameters define the linear transformation as a mapping
|
||||
from coordinates in the destination image (x and y) into coordinates in
|
||||
the source image (u and v).
|
||||
- (u, v) indicate where the top-left corner of the destination lands in
|
||||
the source image.
|
||||
- (dx_u, dx_v) indicate the source-image movement for each movement of
|
||||
x += 1 in the destination.
|
||||
- (dy_u, dy_v) indicate the source-image movement for each movement of
|
||||
y += 1 in the destination.
|
||||
All of these values are specified as 16:16 fixed-point, ie. they encode
|
||||
decimal values by multiplying them by 65536. */
|
||||
int u, v, dx_u, dx_v, dy_u, dy_v;
|
||||
};
|
||||
|
||||
void image_linear(image_t const *src, image_t *dst,
|
||||
struct image_linear_map *map);
|
||||
image_t *image_linear_alloc(image_t const *src,
|
||||
struct image_linear_map *map);
|
||||
|
||||
/* image_scale(): Upscale or downscale an image
|
||||
|
||||
This function generates a linear map to be used in image_linear() to scale
|
||||
the input image. The scaling factor gamma can be specified independently for
|
||||
the x and y dimensions. It is expressed as 16:16 fixed-point; you can set
|
||||
any decimal value multiplied by 65536, for instance 1.5*65536 to increase
|
||||
the width and height by 50%. */
|
||||
void image_scale(image_t const *src, int gamma_x, int gamma_y,
|
||||
struct image_linear_map *map);
|
||||
|
||||
/* image_rotate(): Rotate an image around its center
|
||||
|
||||
This function generates a linear map to be used in image_linear() to perform
|
||||
a rotation around the center of an image. If [resize=true], the target is
|
||||
enlarged to make sure all the rotated pixels can be represented. This can
|
||||
increase the final surface by a factor of up to 2. If the original image
|
||||
doesn't extend to its corners, it is recommended to leave [resize=false] as
|
||||
it noticeably affects performance. */
|
||||
void image_rotate(image_t const *src, float angle, bool resize,
|
||||
struct image_linear_map *map);
|
||||
|
||||
/* image_rotate_around(): Rotate an image around any point
|
||||
|
||||
This function generalizes image_rotate() by allowing rotations around any
|
||||
center, even a point not within the image. The center is specified through
|
||||
two coordinates (*center_x, *center_y). If the center is near the side of
|
||||
the image, a normal rotation would move most of the pixels out of frame;
|
||||
this function moves the frame to make sure the whole image remains visible.
|
||||
*center_x and *center_y are updated to indicate the position of the center
|
||||
of rotation within the new frame (the target image). */
|
||||
void image_rotate_around(image_t const *src, float angle, bool resize,
|
||||
int *center_x, int *center_y, struct image_linear_map *map);
|
||||
|
||||
/* image_rotate_around_scale(): Rotate an image around any point and scale it
|
||||
|
||||
This function generalizes image_rotate_around() by adding a scaling factor
|
||||
to the transformation. The scaling factor gamma is expressed as 16:16
|
||||
fixed-point. If [resize=true] the image is further extended to make sure no
|
||||
parts are cut out, as in other rotation functions. */
|
||||
void image_rotate_around_scale(
|
||||
image_t const *src, float angle, int gamma,
|
||||
bool resize, int *center_x, int *center_y,
|
||||
struct image_linear_map *map);
|
||||
|
||||
//---
|
||||
// Color transforms
|
||||
//---
|
||||
|
||||
/* TODO: Color transforms */
|
||||
|
||||
//---
|
||||
// Image rendering functions
|
||||
//
|
||||
// The following functions extend dimage() and dsubimage(). The [effects]
|
||||
// parameter takes a combination of IMAGE_* flags and effects, limited to the
|
||||
// combinations previously described, with additional arguments depending on
|
||||
// the color effect being applied.
|
||||
//
|
||||
// dimage_effect(x, y, img, effects, ...)
|
||||
// dsubimage_effect(x, y, img, left, top, w, h, effects, ...)
|
||||
//
|
||||
// However if you use these super-generic functions you will link the code for
|
||||
// all effects and all formats into your add-in, which takes a fair amount of
|
||||
// space. If that's a problem, you can use the more specific functions below:
|
||||
//
|
||||
// * dimage_<FORMAT>_<EFFECT>() for one particular format (rgb16, p8, p4) along
|
||||
// with one particular color effect (clearbg, swapcolor, addbg, dye).
|
||||
// * dimage_<FORMAT>() is like the above when no color effect is applied.
|
||||
//
|
||||
// All of them support the HFLIP and VFLIP flags. For effect-specific functions
|
||||
// the corresponding effect flag can be omitted (fi. IMAGE_CLEARBG is implicit
|
||||
// when using dimage_p8_clearbg()).
|
||||
//---
|
||||
|
||||
/* dimage_effect(): Generalized dimage() supporting dynamic effects */
|
||||
#define dimage_effect(x, y, img, eff, ...) \
|
||||
dsubimage_effect(x, y, img, 0, 0, (img)->width, (img)->height, eff, \
|
||||
##__VA_ARGS__)
|
||||
/* dsubimage_effect(): Generalized dsubimage() supporting dynamic effects */
|
||||
void dsubimage_effect(int x, int y, image_t const *img,
|
||||
int left, int top, int w, int h, int effects, ...);
|
||||
|
||||
/* Specific versions for each format */
|
||||
#define DIMAGE_SIG1(NAME, ...) \
|
||||
void dimage_ ## NAME(int x, int y, image_t const *img,##__VA_ARGS__); \
|
||||
void dsubimage_ ## NAME(int x, int y, image_t const *img, \
|
||||
int left, int top, int w, int h, ##__VA_ARGS__);
|
||||
#define DIMAGE_SIG(NAME, ...) \
|
||||
DIMAGE_SIG1(rgb16 ## NAME, ##__VA_ARGS__) \
|
||||
DIMAGE_SIG1(p8 ## NAME, ##__VA_ARGS__) \
|
||||
DIMAGE_SIG1(p4 ## NAME, ##__VA_ARGS__)
|
||||
|
||||
/* d[sub]image_{rgb16,p8,p4}_effect(..., effects, <extra arguments>) */
|
||||
DIMAGE_SIG(_effect, int effects, ...)
|
||||
/* d[sub]image_{rgb16,p8,p4}(..., effects) (no color effect, like dimage()) */
|
||||
DIMAGE_SIG(, int effects)
|
||||
/* d[sub]image_{rgb16,p8,p4}_clearbg(..., effects, bg_color_or_index) */
|
||||
DIMAGE_SIG(_clearbg, int effects, int bg_color_or_index)
|
||||
/* d[sub]image_{rgb16,p8,p4}_swapcolor(..., effects, source, replacement) */
|
||||
DIMAGE_SIG(_swapcolor, int effects, int source, int replacement)
|
||||
/* d[sub]image_{rgb16,p8,p4}_addbg(..., effects, bg_color) */
|
||||
DIMAGE_SIG(_addbg, int effects, int bg_color)
|
||||
/* d[sub]image_{rgb16,p8,p4}_dye(..., effects, dye_color) */
|
||||
DIMAGE_SIG(_dye, int effects, int dye_color)
|
||||
|
||||
/* d[sub]image_p4_clearbg_alt(..., effects, bg_index)
|
||||
This is functionally identical to CLEARBG, but it uses an alternative
|
||||
rendering method that is faster for larger images with wide transparent
|
||||
areas. You can swap it with the normal CLEARBG freely. */
|
||||
DIMAGE_SIG1(p4_clearbg_alt, int effects, int bg_index)
|
||||
|
||||
#define dimage_rgb16_effect(x, y, img, eff, ...) \
|
||||
dsubimage_rgb16_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
|
||||
eff, ##__VA_ARGS__)
|
||||
#define dimage_p8_effect(x, y, img, eff, ...) \
|
||||
dsubimage_p8_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
|
||||
eff, ##__VA_ARGS__)
|
||||
#define dimage_p4_effect(x, y, img, eff, ...) \
|
||||
dsubimage_p4_effect(x, y, img, 0, 0, (img)->width, (img)->height, \
|
||||
eff, ##__VA_ARGS__)
|
||||
|
||||
#undef DIMAGE_SIG
|
||||
#undef DIMAGE_SIG1
|
||||
|
||||
//---
|
||||
// Clipping utilities
|
||||
//---
|
||||
|
||||
/* Double box specifying both a source and target area */
|
||||
struct gint_image_box
|
||||
{
|
||||
/* Target location of top-left corner */
|
||||
int x, y;
|
||||
/* Width and height of rendered sub-image */
|
||||
int w, h;
|
||||
/* Source bounding box (low included, high excluded) */
|
||||
int left, top;
|
||||
};
|
||||
|
||||
/* Clip the provided box against the input. If, after clipping, the box no
|
||||
longer intersects the output window, returns false. Otherwise, returns
|
||||
true. */
|
||||
bool gint_image_clip_input(image_t const *img, struct gint_image_box *box,
|
||||
struct dwindow const *window);
|
||||
|
||||
/* Clip the provided box against the output. */
|
||||
void gint_image_clip_output(struct gint_image_box *b,
|
||||
struct dwindow const *window);
|
||||
|
||||
//---
|
||||
// Internal image rendering routines
|
||||
//
|
||||
// The following functions (or non-functions) are implemented in assembler and
|
||||
// make up the internal interface of the image renderer. If you just want to
|
||||
// display images, use dimage() and variations; these are only useful if you
|
||||
// have a different rendering system and wish to use image rendering with
|
||||
// dynamic effects in it.
|
||||
//---
|
||||
|
||||
/* Renderer command. This structure includes most of the information used by
|
||||
the image renderer to perform blits. Some of the information on the target
|
||||
is also passed as direct arguments, which is more convenient and slightly
|
||||
faster.
|
||||
|
||||
Most of the values here can be set with gint_image_mkcmd(). The last two
|
||||
members, along with the return values of the gint_image_FORMAT_loop()
|
||||
functions, are used to update the command if one needs to draw *parts* of
|
||||
the image and resume the rendering later. This is used in Azur. */
|
||||
struct gint_image_cmd
|
||||
{
|
||||
/* Shader ID. This is used in Azur, and ignored in gint */
|
||||
uint8_t shader_id;
|
||||
/* Dynamic effects not already dispatched by renderer
|
||||
Bit 0: VFLIP
|
||||
Bit 1: HFLIP */
|
||||
uint8_t effect;
|
||||
|
||||
/* Number of pixels to render per line. For formats that force either x
|
||||
or width alignment (most of them), this is already adjusted to a
|
||||
suitable multiple (usually a multiple of 2). */
|
||||
int16_t columns;
|
||||
|
||||
/* Stride of the input image (number of pixels between each row), in
|
||||
pixels, without subtracting the number of columns */
|
||||
int16_t input_stride;
|
||||
|
||||
/* Number of lines in the command. This can be adjusted freely, and is
|
||||
particularly useful in Azur for fragmented rendering. */
|
||||
uint8_t lines;
|
||||
|
||||
/* [Any effect]: Offset of first edge */
|
||||
int8_t edge_1;
|
||||
|
||||
/* Core loop; this is an internal label of the renderer */
|
||||
void const *loop;
|
||||
/* Output pixel array, offset by target x/y */
|
||||
void const *output;
|
||||
/* Input pixel array, offset by source x/y. For formats that force x
|
||||
alignment, this is already adjusted. */
|
||||
void const *input;
|
||||
/* Palette, when applicable */
|
||||
uint16_t const *palette;
|
||||
|
||||
/* [Any effect]: Offset of right edge */
|
||||
int16_t edge_2;
|
||||
/* [CLEARBG, SWAPCOLOR]: Source color */
|
||||
uint16_t color_1;
|
||||
/* [SWAPCOLOR]: Destination color */
|
||||
uint16_t color_2;
|
||||
|
||||
/* Remaining height (for updates between fragments) */
|
||||
int16_t height;
|
||||
/* Local x position (for updates between fragments) */
|
||||
int16_t x;
|
||||
};
|
||||
|
||||
/* gint_image_mkcmd(): Prepare a rendering command with dynamic effects
|
||||
|
||||
This function crafts an image renderer command. It loads all the settings
|
||||
except for effect-dependent parameters: the [.loop] label, the color section
|
||||
of [.effect], and color effect settings. See the effect-specific functions
|
||||
to see how they are defined.
|
||||
|
||||
The benefit of this approach is that the rendering code does not need to be
|
||||
linked in unless an effect is actually used, which avoids blowing up the
|
||||
size of the add-in as the number of support dynamic effects increases.
|
||||
|
||||
@box Requested on-screen box (will be clipped depending on effects)
|
||||
@img Source image
|
||||
@effects Set of dynamic effects to be applied, as an [IMAGE_*] bitmask
|
||||
@left_edge Whether to force 2-alignment on the input (box->left)
|
||||
@right_edge Whether to force 2-alignment on the width
|
||||
@cmd Command to be filled
|
||||
@window Rendering window (usually {0, 0, DWIDTH, DHEIGHT})
|
||||
|
||||
Returns false if there is nothing to render because of clipping (in which
|
||||
case [cmd] is unchanged), true otherwise. [*box] is also updated to reflect
|
||||
the final box after clipping but not accounting for edges. */
|
||||
bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img,
|
||||
int effects, bool left_edge, bool right_edge,
|
||||
struct gint_image_cmd *cmd, struct dwindow const *window);
|
||||
|
||||
/* Entry point of the renderers. These functions can be called normally as long
|
||||
as you can build the commands (eg. by using gint_image_mkcmd() then filling
|
||||
the effect-specific information). */
|
||||
void *gint_image_rgb16_loop (int output_width, struct gint_image_cmd *cmd);
|
||||
void *gint_image_p8_loop (int output_width, struct gint_image_cmd *cmd);
|
||||
void *gint_image_p4_loop (int output_width, struct gint_image_cmd *cmd);
|
||||
|
||||
/* Renderer fragments. The following can absolutely not be called from C code
|
||||
as they aren't full functions (and this isn't their prototype). These are
|
||||
continuations to be specified in the [.loop] field of a command before using
|
||||
one of the functions above. */
|
||||
|
||||
void gint_image_rgb16_normal(void);
|
||||
void gint_image_rgb16_clearbg(void);
|
||||
void gint_image_rgb16_swapcolor(void);
|
||||
void gint_image_rgb16_dye(void);
|
||||
|
||||
void gint_image_p8_normal(void);
|
||||
void gint_image_p8_clearbg(void);
|
||||
void gint_image_p8_swapcolor(void);
|
||||
void gint_image_p8_dye(void);
|
||||
|
||||
void gint_image_p4_normal(void);
|
||||
void gint_image_p4_clearbg(void);
|
||||
void gint_image_p4_clearbg_alt(void);
|
||||
void gint_image_p4_swapcolor(void);
|
||||
void gint_image_p4_dye(void);
|
||||
|
||||
//---
|
||||
// Image library utilities
|
||||
//
|
||||
// The following functions and macros are mostly internal utilities; they are
|
||||
// exposed here in case user applications want to extend the set of image
|
||||
// transforms with custom additions.
|
||||
//---
|
||||
|
||||
/* image_target(): Check if an image can be used as target for a transform
|
||||
|
||||
This function is used to quickly check whether a transform from [src] to
|
||||
[dst] is possible. It requires image_valid(src) and image_valid(dst), plus
|
||||
any optional constraints specified as variadic arguments. These constraints
|
||||
can be:
|
||||
|
||||
* NOT_P4: fails if [dst] is P4.
|
||||
* DATA_RW: fails if [dst] is not data-writable.
|
||||
* PALETTE_RW: fails if [dst] is not palette-writable.
|
||||
* SAME_SIZE: fails if [dst] is not at least as large as [src].
|
||||
|
||||
For example, in image_hflip(), we write:
|
||||
if(!image_target(src, dst, NOT_P4, DATA_RW, SAME_SIZE)) return; */
|
||||
|
||||
enum {
|
||||
IMAGE_TARGET_NONE,
|
||||
IMAGE_TARGET_NOT_P4,
|
||||
IMAGE_TARGET_DATA_RW,
|
||||
IMAGE_TARGET_PALETTE_RW,
|
||||
IMAGE_TARGET_SAME_SIZE,
|
||||
IMAGE_TARGET_SAME_FORMAT,
|
||||
IMAGE_TARGET_SAME_DEPTH,
|
||||
};
|
||||
bool image_target(image_t const *src, image_t *dst, ...);
|
||||
|
||||
#define image_target(src, dst, ...) \
|
||||
image_target(src, dst, image_target_arg1(__VA_ARGS__ __VA_OPT__(,) NONE))
|
||||
#define image_target_arg1(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg2(__VA_ARGS__))
|
||||
#define image_target_arg2(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg3(__VA_ARGS__))
|
||||
#define image_target_arg3(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg4(__VA_ARGS__))
|
||||
#define image_target_arg4(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg5(__VA_ARGS__))
|
||||
#define image_target_arg5(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_arg6(__VA_ARGS__))
|
||||
#define image_target_arg6(c, ...) \
|
||||
IMAGE_TARGET_ ## c __VA_OPT__(, image_target_too_many_args(__VA_ARGS__))
|
||||
|
||||
/* image_alpha_2(): Conditional alpha */
|
||||
#define image_alpha_2(fmt, copy_alpha) \
|
||||
((copy_alpha) ? 0x10000 : image_alpha(fmt))
|
||||
|
||||
#endif /* GINT_RENDER_RGB */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_IMAGE */
|
|
@ -41,6 +41,8 @@ enum {
|
|||
INTC_DMA_DEI4,
|
||||
INTC_DMA_DEI5,
|
||||
INTC_DMA_DADERR,
|
||||
/* Serial Communication Interface with FIFO (SCIF0) */
|
||||
INTC_SCIF0,
|
||||
/* Real-Time Clock [RTC]; a single IPR covers all 3 interrupts */
|
||||
INTC_RTC_ATI,
|
||||
INTC_RTC_PRI,
|
||||
|
@ -130,6 +132,11 @@ void *intc_handler(int event_code, void const *handler, size_t size);
|
|||
the numerous constraints of intc_handler(), at the cost of always going back
|
||||
to userspace and a small time overhead.
|
||||
|
||||
Since gint_inth_callback is used to do the heavy lifting of setting up a sane
|
||||
context for C code, the gint_call_t object can be built with GINT_CALL_FLAG
|
||||
if the called function expects a gint_inth_callback_context_t structure as its
|
||||
first argument.
|
||||
|
||||
@event_code Identifier of the interrupt block
|
||||
@function Function to use as a handler
|
||||
Returns true on success, false if the event code is invalid. */
|
||||
|
|
|
@ -60,6 +60,24 @@ extern "C" {
|
|||
|
||||
* clearevents() reads all pending events from the input queue.
|
||||
|
||||
Games are often also interested in whether keys were pressed or released
|
||||
_since the last frame_. gint doesn't know what a frame is, but you can track
|
||||
key state flips by using cleareventflips() before reading events and
|
||||
keypressed() and keyreleased() afterwards.
|
||||
|
||||
* keypressed() checks if the specified key is currently down and was up at
|
||||
the time of the last cleareventflips().
|
||||
* keyreleased() checks if the specified key is currently up and was down at
|
||||
the time of the last cleareventflips().
|
||||
|
||||
A typical game loop might look like. cleareventflips() must be called
|
||||
_before_ clearevents().
|
||||
|
||||
cleareventflips();
|
||||
clearevents(); // or pollevent(), waitevent()
|
||||
if(keydown(KEY_RIGHT)) move();
|
||||
if(keypressed(KEY_F6)) open_inventory();
|
||||
|
||||
The previous functions are quite low-level. GUI programs that look like the
|
||||
system applications will prefer using a GetKey()-like functions that return
|
||||
a single key press at a time, heeds for releases, for SHIFT and ALPHA
|
||||
|
@ -98,13 +116,13 @@ typedef struct
|
|||
{
|
||||
uint time :16; /* Time of event, unique over short periods */
|
||||
|
||||
uint :3; /* Reserved for future use */
|
||||
uint :2; /* Reserved for future use */
|
||||
|
||||
uint mod :1; /* Whether modifiers are used */
|
||||
uint shift :1; /* If mod=1, whether SHIFT was pressed */
|
||||
uint alpha :1; /* If mod=1, whether ALPHA was pressed */
|
||||
|
||||
uint type :2; /* Type of key event */
|
||||
uint type :3; /* Type of key event */
|
||||
uint key :8; /* Hit key */
|
||||
|
||||
} GPACKED(4) key_event_t;
|
||||
|
@ -112,10 +130,11 @@ typedef struct
|
|||
/* Keyboard event types, as in the [type] field of key_event_t */
|
||||
enum
|
||||
{
|
||||
KEYEV_NONE = 0, /* No event available (poll() only) */
|
||||
KEYEV_DOWN = 1, /* Key was pressed */
|
||||
KEYEV_UP = 2, /* Key was released */
|
||||
KEYEV_HOLD = 3, /* A key that was pressed has been held down */
|
||||
KEYEV_NONE = 0, /* No event available (poll() only) */
|
||||
KEYEV_DOWN = 1, /* Key was pressed */
|
||||
KEYEV_UP = 2, /* Key was released */
|
||||
KEYEV_HOLD = 3, /* A key that was pressed has been held down */
|
||||
KEYEV_OSMENU = 4, /* We went to the main menu and back */
|
||||
};
|
||||
|
||||
/* Keyboard frequency analysis is a runtime setting since gint 2.4. This macro
|
||||
|
@ -162,6 +181,12 @@ key_event_t waitevent(volatile int *timeout);
|
|||
/* clearevents(): Read all events waiting in the queue */
|
||||
void clearevents(void);
|
||||
|
||||
/* cleareventflips(): Set the time reference for keypressed()/keyreleased()
|
||||
The two functions keypressed() and keyreleased() will use the keyboard state
|
||||
at the time this function was called to determine whether any given key was
|
||||
just pressed or jut released. */
|
||||
void cleareventflips(void);
|
||||
|
||||
//---
|
||||
// Key state functions
|
||||
//---
|
||||
|
@ -181,6 +206,16 @@ int keydown_all(int key1, ...);
|
|||
sequence should be terminated by a 0 integer. */
|
||||
int keydown_any(int key1, ...);
|
||||
|
||||
/* keypressed(): Check if a key was just pressed
|
||||
This function returns non-zero if the specified key is currently down, *and*
|
||||
it was up at the time of the last call to cleareventflips(). */
|
||||
int keypressed(int key);
|
||||
|
||||
/* keyreleased(): Check if a key was just released
|
||||
This function returns non-zero if the specified key is currently up, *and*
|
||||
it was down at the time of the last call to cleareventflips(). */
|
||||
int keyreleased(int key);
|
||||
|
||||
//---
|
||||
// High-level functions
|
||||
//---
|
||||
|
@ -205,30 +240,32 @@ key_event_t getkey(void);
|
|||
/* The following are the option bits for getkey_opt(). */
|
||||
enum {
|
||||
/* Enable modifiers keys */
|
||||
GETKEY_MOD_SHIFT = 0x01,
|
||||
GETKEY_MOD_ALPHA = 0x02,
|
||||
/* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
|
||||
GETKEY_BACKLIGHT = 0x04,
|
||||
GETKEY_MOD_SHIFT = 0x0001,
|
||||
GETKEY_MOD_ALPHA = 0x0002,
|
||||
/* SHIFT + OPTN (requires GETKEY_MOD_SHIFT) or LIGHT toggles backlight */
|
||||
GETKEY_BACKLIGHT = 0x0004,
|
||||
/* MENU triggers a task switch and displays the main menu */
|
||||
GETKEY_MENU = 0x08,
|
||||
GETKEY_MENU = 0x0008,
|
||||
/* Repeat arrow keys, or even all keys */
|
||||
GETKEY_REP_ARROWS = 0x10,
|
||||
GETKEY_REP_ALL = 0x20,
|
||||
GETKEY_REP_ARROWS = 0x0010,
|
||||
GETKEY_REP_ALL = 0x0020,
|
||||
/* Enable custom repeat profiles; see getkey_set_repeat_profile() */
|
||||
GETKEY_REP_PROFILE = 0x40,
|
||||
GETKEY_REP_PROFILE = 0x0040,
|
||||
/* Enable application shortcuts; see getkey_set_feature_function() */
|
||||
GETKEY_FEATURES = 0x80,
|
||||
GETKEY_FEATURES = 0x0080,
|
||||
/* After coming back from the main menu, redraw the screen with dupdate */
|
||||
GETKEY_MENU_DUPDATE = 0x0100,
|
||||
/* After coming back from the main menu, send a KEYEV_OSMENU event */
|
||||
GETKEY_MENU_EVENT = 0x0200,
|
||||
/* Enable power off with SHIFT + AC/ON */
|
||||
GETKEY_POWEROFF = 0x0400,
|
||||
|
||||
/* No modifiers */
|
||||
GETKEY_NONE = 0x00,
|
||||
GETKEY_NONE = 0x0000,
|
||||
/* Default settings of getkey() */
|
||||
GETKEY_DEFAULT = 0xdf,
|
||||
GETKEY_DEFAULT = 0x05df,
|
||||
};
|
||||
|
||||
/* 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 +297,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 +336,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,18 @@ 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 codes for the CP-400 */
|
||||
KEY_KBD = 0xa1,
|
||||
KEY_X = 0xa2,
|
||||
KEY_Y = 0xa3,
|
||||
KEY_Z = 0xa4,
|
||||
KEY_EQUALS = 0xa5,
|
||||
KEY_CLEAR = KEY_EXIT,
|
||||
|
||||
/* Key aliases (handle with care =D) */
|
||||
KEY_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;
|
||||
|
@ -92,6 +108,11 @@ void kmalloc_init_arena(kmalloc_arena_t *a, bool enable_statistics);
|
|||
success, false if the maximum number of arenas has been reached. */
|
||||
bool kmalloc_add_arena(kmalloc_arena_t *arena);
|
||||
|
||||
/* kmalloc_remove_arena(): Remove an arena from the heap source
|
||||
Removes an arena from the heap source. The arena should certainly be empty,
|
||||
although this function will not check that. */
|
||||
void kmalloc_remove_arena(kmalloc_arena_t *arena);
|
||||
|
||||
//---
|
||||
// Internal functions
|
||||
//---
|
||||
|
|
|
@ -89,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)
|
||||
|
@ -110,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
|
||||
|
|
224
include/gint/mpu/bsc.h
Normal file
224
include/gint/mpu/bsc.h
Normal file
|
@ -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
|
||||
|
|
218
include/gint/mpu/scif.h
Normal file
218
include/gint/mpu/scif.h
Normal file
|
@ -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 */
|
90
include/gint/mpu/ubc.h
Normal file
90
include/gint/mpu/ubc.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
//---
|
||||
// gint:mpu:ubc - User Break Controller
|
||||
//---
|
||||
|
||||
#ifndef GINT_MPU_UBC
|
||||
#define GINT_MPU_UBC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
lword_union(CBR0, /* Match condition setting 0 */
|
||||
uint32_t MFE :1; /* Match Flag Enable */
|
||||
uint32_t AIE :1; /* ASID Enable */
|
||||
uint32_t MFI :6; /* Match Flag Specify */
|
||||
uint32_t AIV :8; /* ASID Specify */
|
||||
uint32_t :1;
|
||||
uint32_t SZ :3; /* Operand Size Select */
|
||||
uint32_t :4;
|
||||
uint32_t CD :2; /* Bus Select */
|
||||
uint32_t ID :2; /* Ins. Fetch / Operand Access Select */
|
||||
uint32_t :1;
|
||||
uint32_t RW :2; /* Bus Command Select */
|
||||
uint32_t CE :1; /* Channel Enable */
|
||||
);
|
||||
lword_union(CRR0, /* Match operation setting 0 */
|
||||
uint32_t :18;
|
||||
uint32_t _1 :1; /* Always set to 1 */
|
||||
uint32_t :11;
|
||||
uint32_t PCB :1; /* PC Break Select */
|
||||
uint32_t BIE :1; /* Break Enable */
|
||||
);
|
||||
uint32_t CAR0; /* Match address setting 0 */
|
||||
uint32_t CAMR0; /* Match address mask setting 0 */
|
||||
pad(0x10);
|
||||
|
||||
lword_union(CBR1, /* Match condition setting 1 */
|
||||
uint32_t MFE :1; /* Match Flag Enable */
|
||||
uint32_t AIE :1; /* ASID Enable */
|
||||
uint32_t MFI :6; /* Match Flag Specify */
|
||||
uint32_t AIV :8; /* ASID Specify */
|
||||
uint32_t DBE :1; /* Data Value Enable */
|
||||
uint32_t SZ :3; /* Operand Size Select */
|
||||
uint32_t ETBE :1; /* Execution Count Value Enable */
|
||||
uint32_t :3;
|
||||
uint32_t CD :2; /* Bus Select */
|
||||
uint32_t ID :2; /* Ins. Fetch / Operand Access Select */
|
||||
uint32_t :1;
|
||||
uint32_t RW :2; /* Bus Command Select */
|
||||
uint32_t CE :1; /* Channel Enable */
|
||||
);
|
||||
lword_union(CRR1, /* Match operation setting 1 */
|
||||
uint32_t :18;
|
||||
uint32_t _1 :1; /* Always set to 1 */
|
||||
uint32_t :11;
|
||||
uint32_t PCB :1; /* PC Break Select */
|
||||
uint32_t BIE :1; /* Break Enable */
|
||||
);
|
||||
uint32_t CAR1; /* Match address setting 1 */
|
||||
uint32_t CAMR1; /* Match address mask setting 1 */
|
||||
uint32_t CDR1; /* Match data setting 1 */
|
||||
uint32_t CDMR1; /* Match data mask setting 1 */
|
||||
lword_union(CETR1, /* Execution count break 1 */
|
||||
uint32_t :20;
|
||||
uint32_t CET :12; /* Execution Count */
|
||||
);
|
||||
pad(0x5c4);
|
||||
|
||||
lword_union(CCMFR, /* Channel match flag */
|
||||
uint32_t :30;
|
||||
uint32_t MF1 :1; /* Channel 1 Condition Match Flag */
|
||||
uint32_t MF0 :1; /* Channel 0 Condition Match Flag */
|
||||
);
|
||||
pad(0x1c);
|
||||
lword_union(CBCR, /* Break control */
|
||||
uint32_t :31;
|
||||
uint32_t UBDE :1; /* User Break Debugging Support Function Enable */
|
||||
);
|
||||
} GPACKED(4) sh7305_ubc_t;
|
||||
#define SH7305_UBC (*(sh7305_ubc_t *)0xff200000)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_UBC */
|
|
@ -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;
|
||||
|
||||
|
|
52
include/gint/mpu/wdt.h
Normal file
52
include/gint/mpu/wdt.h
Normal file
|
@ -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 */
|
64
include/gint/serial.h
Normal file
64
include/gint/serial.h
Normal file
|
@ -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 */
|
58
include/gint/ubc.h
Normal file
58
include/gint/ubc.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
//---
|
||||
// gint:ubc - User Break Controller driver
|
||||
//---
|
||||
|
||||
#ifndef GINT_UBC
|
||||
#define GINT_UBC
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/gdb.h>
|
||||
|
||||
/* Read and write the DBR register */
|
||||
void ubc_setDBR(void* DBR);
|
||||
void* ubc_getDBR(void);
|
||||
|
||||
/* ubc_dbh(): Low level UBC debug handler
|
||||
The handler will backup the current CPU state and call ubc_debug_handler(). */
|
||||
void ubc_dbh(void);
|
||||
/* ubc_debug_handler(): C level UBC debug handler
|
||||
The execution will be redirected to the handler set by the user or the break
|
||||
will be ignored in case no handler has been set. */
|
||||
void ubc_debug_handler(gdb_cpu_state_t* cpu_state);
|
||||
/* ubc_set_debug_handler(): Set user debug handler
|
||||
Set a custom debug handler that will be called when a break condition from
|
||||
the UBC is reached. */
|
||||
void ubc_set_debug_handler(void (*h)(gdb_cpu_state_t*));
|
||||
|
||||
/* ubc_dbh_lock: Lock set by ubc_dbh() when a UBC break is currently being
|
||||
handled. */
|
||||
extern uint8_t ubc_dbh_lock;
|
||||
|
||||
/* UBC Breakpoint types */
|
||||
typedef enum {
|
||||
UBC_BREAK_BEFORE, /* Break before the instruction is executed */
|
||||
UBC_BREAK_AFTER, /* Break after the instruction is executed :
|
||||
at this point PC will point to the next instruction. */
|
||||
} ubc_break_mode_t;
|
||||
|
||||
/* ubc_set_breakpoint(): Set a breakpoint in a UBC channel and enable it
|
||||
Return false when an invalid channel number is provided, true if the
|
||||
breakpoint was correctly set up. */
|
||||
bool ubc_set_breakpoint(int channel, void* break_address, ubc_break_mode_t break_mode);
|
||||
/* ubc_get_break_address(): Get a breakpoint address if it's enabled
|
||||
If the channel is disabled the function will return false and *break_address
|
||||
will not be updated. */
|
||||
bool ubc_get_break_address(int channel, void** break_address);
|
||||
/* ubc_disable_channel(): Disable a UBC channel
|
||||
Return true on success. If an invalid channel number is provided, it will
|
||||
return false. */
|
||||
bool ubc_disable_channel(int channel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_UBC */
|
|
@ -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,38 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <gint/usb.h>
|
||||
#include <gint/config.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
|
||||
|
@ -179,12 +75,131 @@ void usb_fxlink_text(char const *text, int size);
|
|||
automatically send new frames to fxlink. */
|
||||
void usb_fxlink_videocapture(bool onscreen);
|
||||
|
||||
#ifdef FX9860G
|
||||
/* usb_fxlink_videocapture_gray(): Send a gray frame for a video recording
|
||||
Like usb_fxlink_videocapture(), but uses VRAM data from the gray engine. */
|
||||
#if GINT_RENDER_MONO
|
||||
/* 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
|
||||
|
@ -79,6 +90,9 @@ void usb_open_wait(void);
|
|||
/* usb_is_open(): Check whether the USB link is active */
|
||||
bool usb_is_open(void);
|
||||
|
||||
/* usb_is_open_interface(): Check whether a particular interface is open */
|
||||
bool usb_is_open_interface(usb_interface_t const *interface);
|
||||
|
||||
/* usb_close(): Close the USB link
|
||||
|
||||
This function closes the link opened by usb_open(), and notifies the host of
|
||||
|
@ -129,8 +143,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 +187,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 +215,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 +245,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 +259,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 +276,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
|
||||
//---
|
||||
|
@ -380,6 +520,14 @@ uint16_t usb_dc_string(uint16_t const *literal, size_t len);
|
|||
This is mostly used by the driver to answer GET_DESCRIPTOR requests. */
|
||||
usb_dc_string_t *usb_dc_string_get(uint16_t id);
|
||||
|
||||
//---
|
||||
// USB interrupts
|
||||
//---
|
||||
|
||||
/* usb_interrupt_context: Context of the function interrupted by a USB interrupt
|
||||
The pointer is set back to NULL when the interrupt is finished being handled. */
|
||||
extern gint_inth_callback_context_t* usb_interrupt_context;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
93
include/gint/video.h
Normal file
93
include/gint/video.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
//---
|
||||
// gint:video - Generic video interface
|
||||
//
|
||||
// This header defines the interface for video (display) drivers. It allows
|
||||
// high-level code to manipulate the display independently of the underlying
|
||||
// hardware.
|
||||
//---
|
||||
|
||||
#ifndef GINT_VIDEO
|
||||
#define GINT_VIDEO
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/drivers.h>
|
||||
#include <gint/image.h>
|
||||
|
||||
/* Video mode offered by a driver for rendering. */
|
||||
typedef struct {
|
||||
/* Mode size */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
/* Pixel format */
|
||||
int16_t format;
|
||||
/* Refresh frequency, -1 if unknown */
|
||||
int16_t freq;
|
||||
} video_mode_t;
|
||||
|
||||
/* Flags for the update function of the interface. */
|
||||
#define VIDEO_UPDATE_NONE 0x00
|
||||
#define VIDEO_UPDATE_ENABLE_DMA 0x01
|
||||
#define VIDEO_UPDATE_ATOMIC 0x02
|
||||
#define VIDEO_UPDATE_FOREIGN_WORLD 0x04
|
||||
|
||||
/* Video driver interface. */
|
||||
typedef struct {
|
||||
/* Associated driver (NULL if there's none). */
|
||||
gint_driver_t const *driver;
|
||||
|
||||
/* List of modes (terminated by an all-0 element). The first mode is the
|
||||
default mode and it should always be available. */
|
||||
video_mode_t const *modes;
|
||||
/* Get current video mode. */
|
||||
uint (*mode_get)(void);
|
||||
/* Set a video mode. */
|
||||
bool (*mode_set)(uint id);
|
||||
|
||||
/* Minimum and maximum brightness settings. */
|
||||
int brightness_min;
|
||||
int brightness_max;
|
||||
/* Set a brightness setting. */
|
||||
bool (*brightness_set)(int setting);
|
||||
|
||||
/* Implements video_update(); bounds are checked befoer calling. */
|
||||
bool (*update)(int x, int y, image_t const *framebuffer, int flags);
|
||||
|
||||
} video_interface_t;
|
||||
|
||||
/* Get the video interface currently in use. This can be NULL if the program is
|
||||
being linked without a display driver. */
|
||||
video_interface_t const *video_get_current_interface(void);
|
||||
|
||||
/* Get the index of the current video mode. This can be -1 if there is no
|
||||
interface; however, if there is an interface, this is always >= 0. */
|
||||
int video_get_current_mode_index(void);
|
||||
|
||||
/* Get the a pointer to the current video mode's definition. */
|
||||
video_mode_t const *video_get_current_mode(void);
|
||||
|
||||
/* Update the contents of the display from a framebuffer image. A combination
|
||||
of `VIDEO_UPDATE_*` flags can be specified to select the update method:
|
||||
- `ENABLE_DMA` allows the driver to copy using DMA, when applicable;
|
||||
- `ATOMIC` requires the driver to use an interrupt-less method.
|
||||
Returns true on success.
|
||||
|
||||
Update flags will be ignored if not applicable (e.g. `ENABLE_DMA` for a
|
||||
video interface that doesn't support DMA) but will result in an error if
|
||||
applicable and the specified method fails (e.g. DMA transfer error).
|
||||
|
||||
This function is usually called with (x,y) = (0,0) and a contiguous
|
||||
framebuffer whose size is the video mode size. Specifying images with other
|
||||
sizes, positions and strides is allowed only when they result in data
|
||||
transfers that are byte-aligned. DMA support is only guaranteed for
|
||||
contiguous input and output. The implied rectangle must be in-bounds. */
|
||||
bool video_update(int x, int y, image_t const *fb, int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_VIDEO */
|
|
@ -5,6 +5,7 @@
|
|||
#include <gint/drivers.h>
|
||||
#include <gint/drivers/states.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/config.h>
|
||||
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/mpu/cpg.h>
|
||||
|
@ -26,10 +27,9 @@ const clock_frequency_t *clock_freq(void)
|
|||
// SH7705 Clock signals
|
||||
//---
|
||||
|
||||
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
|
||||
#define CPG SH7705_CPG
|
||||
#if GINT_HW_FX
|
||||
|
||||
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,36 +41,24 @@ 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 */
|
||||
#endif
|
||||
|
||||
//---
|
||||
// SH7305 clock signals
|
||||
|
@ -82,7 +70,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 +81,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,24 +103,28 @@ static void sh7305_probe(void)
|
|||
|
||||
#undef CPG
|
||||
|
||||
|
||||
//---
|
||||
// Initialization
|
||||
//---
|
||||
|
||||
void cpg_compute_freq(void)
|
||||
{
|
||||
/* This avoids warnings about sh7705_probe() being undefined when
|
||||
building for fxcg50 */
|
||||
#if GINT_HW_FX
|
||||
isSH3() ? sh7705_probe() :
|
||||
#endif
|
||||
sh7305_probe();
|
||||
}
|
||||
|
||||
static void configure(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))
|
||||
isSH3() ? sh7705_probe() :
|
||||
#endif
|
||||
sh7305_probe();
|
||||
cpg_compute_freq();
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -141,12 +133,16 @@ static void configure(void)
|
|||
|
||||
static void hsave(cpg_state_t *s)
|
||||
{
|
||||
if(isSH4()) s->SSCGCR = SH7305_CPG.SSCGCR.lword;
|
||||
if(isSH4())
|
||||
s->SSCGCR = SH7305_CPG.SSCGCR.lword;
|
||||
cpg_get_overclock_setting(&s->speed);
|
||||
}
|
||||
|
||||
static void hrestore(cpg_state_t const *s)
|
||||
{
|
||||
if(isSH4()) SH7305_CPG.SSCGCR.lword = s->SSCGCR;
|
||||
if(isSH4())
|
||||
SH7305_CPG.SSCGCR.lword = s->SSCGCR;
|
||||
cpg_set_overclock_setting(&s->speed);
|
||||
}
|
||||
|
||||
gint_driver_t drv_cpg = {
|
||||
|
|
514
src/cpg/overclock.c
Normal file
514
src/cpg/overclock.c
Normal file
|
@ -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();
|
||||
}
|
||||
|
||||
#if GINT_HW_FX
|
||||
|
||||
static struct cpg_overclock_setting const settings_fx9860g_sh3[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FRQCR = 0x1001,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FRQCR = (SH3_PLL_2x<<8)+(SH3_DIV_1<<4)+SH3_DIV_2,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FRQCR = (SH3_PLL_3x<<8)+(SH3_DIV_1<<4)+SH3_DIV_3,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FRQCR = (SH3_PLL_4x<<8)+(SH3_DIV_1<<4)+SH3_DIV_4,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FRQCR = (SH3_PLL_4x<<8)+(SH3_DIV_1<<4)+SH3_DIV_4,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000000C0,
|
||||
.CS2WCR = 0x000100C0,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting const settings_fx9860g_sh4[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = 0x0F202203,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000005C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x24920200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x14900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000302C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting const settings_g35pe2[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = 0x0F112213,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x000001C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031340 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x24920200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031B40 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x14900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031B40 },
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if GINT_HW_CG
|
||||
|
||||
static struct cpg_overclock_setting const settings_prizm[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = 0x0F102203,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_16<<12)+(SH4_DIV_16<<8)+SH4_DIV_32,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x000100C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_32,
|
||||
.CS0BCR = 0x24900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x000002C0,
|
||||
.CS2WCR = 0x000201C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_32,
|
||||
.CS0BCR = 0x44900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x00000440,
|
||||
.CS2WCR = 0x00040340,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x34900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x15140400,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000402C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00010240 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting const settings_fxcg50[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = 0x0F011112,
|
||||
.CS0BCR = 0x36DA0400,
|
||||
.CS2BCR = 0x36DA3400,
|
||||
.CS3BCR = 0x36DB4400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000340,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000240,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x000002C0,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000440,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static struct cpg_overclock_setting const *get_settings(void)
|
||||
{
|
||||
#if GINT_HW_FX
|
||||
if(gint[HWCALC] == HWCALC_FX9860G_SH3)
|
||||
return settings_fx9860g_sh3;
|
||||
if(gint[HWCALC] == HWCALC_FX9860G_SH4)
|
||||
return settings_fx9860g_sh4;
|
||||
if(gint[HWCALC] == HWCALC_G35PE2)
|
||||
return settings_g35pe2;
|
||||
#endif
|
||||
|
||||
#if GINT_HW_CG
|
||||
if(gint[HWCALC] == HWCALC_PRIZM)
|
||||
return settings_prizm;
|
||||
if(gint[HWCALC] == HWCALC_FXCG50)
|
||||
return settings_fxcg50;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int clock_get_speed(void)
|
||||
{
|
||||
struct cpg_overclock_setting const *settings = get_settings();
|
||||
if(!settings)
|
||||
return CLOCK_SPEED_UNKNOWN;
|
||||
|
||||
if(isSH3()) {
|
||||
for(int i = 0; i < 5; i++) {
|
||||
struct cpg_overclock_setting const *s = &settings[i];
|
||||
|
||||
if(SH7705_CPG.FRQCR.word == (s->FRQCR | 0x1000)
|
||||
&& SH7705_BSC.CS0BCR.lword == s->CS0BCR
|
||||
&& SH7705_BSC.CS2BCR.lword == s->CS2BCR
|
||||
&& SH7705_BSC.CS3BCR.lword == s->CS3BCR
|
||||
&& SH7705_BSC.CS5ABCR.lword == s->CS5aBCR
|
||||
&& SH7705_BSC.CS0WCR.lword == s->CS0WCR
|
||||
&& SH7705_BSC.CS2WCR.lword == s->CS2WCR
|
||||
&& SH7705_BSC.CS3WCR.lword == s->CS3WCR
|
||||
&& SH7705_BSC.CS5AWCR.lword == s->CS5aWCR)
|
||||
return CLOCK_SPEED_F1 + i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(int i = 0; i < 5; i++) {
|
||||
struct cpg_overclock_setting const *s = &settings[i];
|
||||
|
||||
if(SH7305_CPG.FLLFRQ.lword == s->FLLFRQ
|
||||
&& SH7305_CPG.FRQCR.lword == s->FRQCR
|
||||
&& SH7305_BSC.CS0BCR.lword == s->CS0BCR
|
||||
&& SH7305_BSC.CS2BCR.lword == s->CS2BCR
|
||||
&& SH7305_BSC.CS3BCR.lword == s->CS3BCR
|
||||
&& SH7305_BSC.CS5ABCR.lword == s->CS5aBCR
|
||||
&& SH7305_BSC.CS0WCR.lword == s->CS0WCR
|
||||
&& SH7305_BSC.CS2WCR.lword == s->CS2WCR
|
||||
&& SH7305_BSC.CS3WCR.lword == s->CS3WCR
|
||||
&& SH7305_BSC.CS5AWCR.lword == s->CS5aWCR)
|
||||
return CLOCK_SPEED_F1 + i;
|
||||
}
|
||||
}
|
||||
|
||||
return CLOCK_SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
void clock_set_speed(int level)
|
||||
{
|
||||
if(level < CLOCK_SPEED_F1 || level > CLOCK_SPEED_F5)
|
||||
return;
|
||||
if(clock_get_speed() == level)
|
||||
return;
|
||||
|
||||
struct cpg_overclock_setting const *settings = get_settings();
|
||||
if(!settings)
|
||||
return;
|
||||
|
||||
struct cpg_overclock_setting const *s = &settings[level - CLOCK_SPEED_F1];
|
||||
cpg_set_overclock_setting(s);
|
||||
}
|
|
@ -45,6 +45,15 @@ static void hsave(cpu_state_t *s)
|
|||
s->CPUOPM = cpu_getCPUOPM();
|
||||
s->SR = cpu_getSR().lword;
|
||||
}
|
||||
|
||||
__asm__("stc r0_bank, %0" : "=r"(s->rN_bank[0]));
|
||||
__asm__("stc r1_bank, %0" : "=r"(s->rN_bank[1]));
|
||||
__asm__("stc r2_bank, %0" : "=r"(s->rN_bank[2]));
|
||||
__asm__("stc r3_bank, %0" : "=r"(s->rN_bank[3]));
|
||||
__asm__("stc r4_bank, %0" : "=r"(s->rN_bank[4]));
|
||||
__asm__("stc r5_bank, %0" : "=r"(s->rN_bank[5]));
|
||||
__asm__("stc r6_bank, %0" : "=r"(s->rN_bank[6]));
|
||||
__asm__("stc r7_bank, %0" : "=r"(s->rN_bank[7]));
|
||||
}
|
||||
|
||||
static void hrestore(cpu_state_t const *s)
|
||||
|
@ -55,6 +64,15 @@ static void hrestore(cpu_state_t const *s)
|
|||
cpu_setCPUOPM(s->CPUOPM);
|
||||
cpu_setSR((cpu_sr_t)s->SR);
|
||||
}
|
||||
|
||||
__asm__("ldc %0, r0_bank" :: "r"(s->rN_bank[0]));
|
||||
__asm__("ldc %0, r1_bank" :: "r"(s->rN_bank[1]));
|
||||
__asm__("ldc %0, r2_bank" :: "r"(s->rN_bank[2]));
|
||||
__asm__("ldc %0, r3_bank" :: "r"(s->rN_bank[3]));
|
||||
__asm__("ldc %0, r4_bank" :: "r"(s->rN_bank[4]));
|
||||
__asm__("ldc %0, r5_bank" :: "r"(s->rN_bank[5]));
|
||||
__asm__("ldc %0, r6_bank" :: "r"(s->rN_bank[6]));
|
||||
__asm__("ldc %0, r7_bank" :: "r"(s->rN_bank[7]));
|
||||
}
|
||||
|
||||
gint_driver_t drv_cpu = {
|
||||
|
|
|
@ -160,11 +160,8 @@ 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_channel_wait(): Wait for a particular channel's transfer to finish
|
||||
|
@ -178,7 +175,7 @@ static void dma_channel_wait(int channel, bool foreign)
|
|||
if(!ch) return;
|
||||
|
||||
/* If interrupts are disabled or we don't own the device, spin-wait by
|
||||
checking either for TE to be set (Transfere Ended) or DE to be gone
|
||||
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
|
||||
|
|
23
src/fs/fs.c
23
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,15 +35,19 @@ 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;
|
||||
fdtable[fd].data = NULL;
|
||||
}
|
||||
|
||||
int open_generic(fs_descriptor_type_t *type, void *data, int fd)
|
||||
int open_generic(fs_descriptor_type_t const *type, void *data, int fd)
|
||||
{
|
||||
if(!fdtable) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
if(!type) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
@ -74,15 +79,19 @@ int open_generic(fs_descriptor_type_t *type, void *data, int fd)
|
|||
|
||||
/* Standard streams */
|
||||
|
||||
static fs_descriptor_type_t devnull = {
|
||||
static fs_descriptor_type_t const devnull = {
|
||||
.read = 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;
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ 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);
|
||||
|
|
|
@ -93,6 +93,8 @@ void *fugue_dir_explore(char const *path)
|
|||
struct BFile_FileInfo info;
|
||||
char *wildcard=NULL;
|
||||
uint16_t *fc_path=NULL, *search=NULL;
|
||||
/* We allocate by batches of 8 */
|
||||
int sd=-1, rc, allocated=0;
|
||||
|
||||
dir_t *dp = malloc(sizeof *dp);
|
||||
if(!dp) goto alloc_failure;
|
||||
|
@ -100,8 +102,6 @@ void *fugue_dir_explore(char const *path)
|
|||
dp->count = 0;
|
||||
dp->entries = NULL;
|
||||
dp->pos = 0;
|
||||
/* We allocate by batches of 8 */
|
||||
int sd=-1, rc, allocated=0;
|
||||
|
||||
fc_path = malloc(512 * sizeof *fc_path);
|
||||
if(!fc_path) goto alloc_failure;
|
||||
|
@ -118,7 +118,7 @@ void *fugue_dir_explore(char const *path)
|
|||
if(rc < 0) {
|
||||
if(rc != BFile_EntryNotFound)
|
||||
errno = bfile_error_to_errno(rc);
|
||||
goto end;
|
||||
goto error;
|
||||
}
|
||||
|
||||
do {
|
||||
|
@ -148,7 +148,9 @@ void *fugue_dir_explore(char const *path)
|
|||
|
||||
alloc_failure:
|
||||
errno = ENOMEM;
|
||||
error:
|
||||
fugue_dir_close(dp);
|
||||
dp = NULL;
|
||||
end:
|
||||
free(wildcard);
|
||||
free(search);
|
||||
|
|
|
@ -63,6 +63,10 @@ int fugue_open(char const *path, int flags, GUNUSED mode_t mode)
|
|||
/* If the entry is a directory, open it as such */
|
||||
if(type == BFile_Type_Directory) {
|
||||
void *dp = fugue_dir_explore(path);
|
||||
if(!dp) {
|
||||
rc = -1;
|
||||
goto end;
|
||||
}
|
||||
fs_descriptor_t data = {
|
||||
.type = &fugue_dir_descriptor_type,
|
||||
.data = dp,
|
||||
|
|
38
src/fs/fugue/fugue_rename.c
Normal file
38
src/fs/fugue/fugue_rename.c
Normal file
|
@ -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;
|
||||
}
|
8
src/fs/rename.c
Normal file
8
src/fs/rename.c
Normal file
|
@ -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);
|
||||
}
|
9
src/gdb/fxconv-metadata.txt
Normal file
9
src/gdb/fxconv-metadata.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
icons-i1msb.png:
|
||||
type: bopti-image
|
||||
profile: mono
|
||||
name: gint_gdb_icons_i1msb
|
||||
|
||||
icons-rgb565.png:
|
||||
type: bopti-image
|
||||
profile: rgb565
|
||||
name: gint_gdb_icons_rgb565
|
7
src/gdb/gdb.S
Normal file
7
src/gdb/gdb.S
Normal file
|
@ -0,0 +1,7 @@
|
|||
.global _gdb_stubcall_write
|
||||
|
||||
_gdb_stubcall_write:
|
||||
mov #64, r3
|
||||
trapa #33
|
||||
rts
|
||||
nop
|
881
src/gdb/gdb.c
Normal file
881
src/gdb/gdb.c
Normal file
|
@ -0,0 +1,881 @@
|
|||
#include <gint/cpu.h>
|
||||
#include <gint/exc.h>
|
||||
#include <gint/gdb.h>
|
||||
#include <gint/ubc.h>
|
||||
#include <gint/usb-ff-bulk.h>
|
||||
#include <gint/usb.h>
|
||||
#include <gint/video.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/config.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/fs.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define GDB_VISUAL_FEEDBACK 1
|
||||
#define GDB_BRIDGE_LOGS 0
|
||||
|
||||
/* Note about trap numbers:
|
||||
- trapa #16...#23 were historically used for Linux's syscall
|
||||
interface for syscalls with up to 0...7 arguments.
|
||||
- trapa #31 is unified syscall interface (Linux)
|
||||
- trapa #32 is GDB software breakpoint
|
||||
- trapa #33 shall thus be our stubcall. */
|
||||
#define TRA_SWBREAK 32
|
||||
#define TRA_STUBCALL 33
|
||||
|
||||
#if GDB_VISUAL_FEEDBACK
|
||||
|
||||
enum { ICON_WORKING, ICON_ERROR, ICON_COMM, ICON_IDLE };
|
||||
|
||||
static void gdb_show_stub_status(int icon)
|
||||
{
|
||||
// TODO[3]: Use normal way for both fx and cg (and remove display.h include)
|
||||
#if GINT_RENDER_MONO
|
||||
extern bopti_image_t gint_gdb_icons_i1msb;
|
||||
dsubimage(120, 0, &gint_gdb_icons_i1msb, 8*icon, 0, 8, 5, DIMAGE_NONE);
|
||||
dupdate();
|
||||
#else
|
||||
video_mode_t const *M = video_get_current_mode();
|
||||
if(!M)
|
||||
return;
|
||||
|
||||
extern image_t gint_gdb_icons_rgb565;
|
||||
|
||||
if(M->format == IMAGE_RGB565) {
|
||||
image_t sub;
|
||||
image_sub(&gint_gdb_icons_rgb565, 6*icon, 0, 7, 7, &sub);
|
||||
if(!video_update(M->width-7, 0, &sub, 0))
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
# define gdb_show_stub_status(...) ((void)0)
|
||||
#endif
|
||||
|
||||
static void gdb_hexlify(char* output_string, const uint8_t* input_buffer, size_t input_size)
|
||||
{
|
||||
const char* hex = "0123456789ABCDEF";
|
||||
for (size_t i = 0; i < input_size; i++) {
|
||||
uint8_t byte = input_buffer[i];
|
||||
output_string[i*2 + 0] = hex[(byte & 0xF0) >> 4];
|
||||
output_string[i*2 + 1] = hex[byte & 0x0F];
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : bug in fxlibc ? strtoul doesn't support uppercase
|
||||
static uint32_t gdb_unhexlify_sized(const char* input_string, size_t input_length)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
for (size_t i = 0; i < input_length; i++) {
|
||||
uint8_t nibble_hex = tolower(input_string[i]);
|
||||
uint8_t nibble = nibble_hex >= 'a' && nibble_hex <= 'f' ? nibble_hex - 'a' + 10 :
|
||||
nibble_hex >= '0' && nibble_hex <= '9' ? nibble_hex - '0' : 0;
|
||||
ret = (ret << 4) | nibble;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t gdb_unhexlify(const char* input_string)
|
||||
{
|
||||
return gdb_unhexlify_sized(input_string, strlen(input_string));
|
||||
}
|
||||
|
||||
static bool gdb_started = false;
|
||||
|
||||
static void gdb_send(const char *data, size_t size)
|
||||
{
|
||||
usb_fxlink_header_t header;
|
||||
usb_fxlink_fill_header(&header, "gdb", "remote", size);
|
||||
|
||||
int pipe = usb_ff_bulk_output();
|
||||
usb_write_sync(pipe, &header, sizeof(header), false);
|
||||
usb_write_sync(pipe, data, size, false);
|
||||
usb_commit_sync(pipe);
|
||||
}
|
||||
|
||||
static void gdb_send_start(void)
|
||||
{
|
||||
usb_fxlink_header_t header;
|
||||
usb_fxlink_fill_header(&header, "gdb", "start", 0);
|
||||
|
||||
int pipe = usb_ff_bulk_output();
|
||||
usb_write_sync(pipe, &header, sizeof(header), false);
|
||||
usb_commit_sync(pipe);
|
||||
}
|
||||
|
||||
static char *gdb_recv_buffer = NULL;
|
||||
static const size_t gdb_recv_buffer_capacity = 256;
|
||||
static size_t gdb_recv_buffer_size = 0;
|
||||
static ssize_t gdb_recv(char *buffer, size_t buffer_size)
|
||||
{
|
||||
if (gdb_recv_buffer_size >= buffer_size) {
|
||||
memcpy(buffer, gdb_recv_buffer, buffer_size);
|
||||
memmove(gdb_recv_buffer, &gdb_recv_buffer[buffer_size], gdb_recv_buffer_size - buffer_size);
|
||||
gdb_recv_buffer_size -= buffer_size;
|
||||
return buffer_size;
|
||||
}
|
||||
|
||||
usb_fxlink_header_t header;
|
||||
while (!usb_fxlink_handle_messages(&header)) {
|
||||
sleep();
|
||||
}
|
||||
|
||||
// TODO : should we abort or find a way to gracefully shutdown the debugger ?
|
||||
if (strncmp(header.application, "gdb", 16) == 0
|
||||
&& strncmp(header.type, "remote", 16) == 0) {
|
||||
if (header.size > gdb_recv_buffer_capacity - gdb_recv_buffer_size) {
|
||||
abort();
|
||||
}
|
||||
usb_read_sync(usb_ff_bulk_input(), &gdb_recv_buffer[gdb_recv_buffer_size], header.size, false);
|
||||
gdb_recv_buffer_size += header.size;
|
||||
return gdb_recv(buffer, buffer_size);
|
||||
} else {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t gdb_recv_packet(char* buffer, size_t buffer_size)
|
||||
{
|
||||
char read_char;
|
||||
|
||||
// Waiting for packet start '$'
|
||||
do {
|
||||
if (gdb_recv(&read_char, 1) != 1) {
|
||||
return -1;
|
||||
}
|
||||
} while (read_char != '$');
|
||||
|
||||
uint8_t checksum = 0;
|
||||
size_t packet_len = 0;
|
||||
while (true) {
|
||||
if (gdb_recv(&read_char, 1) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (read_char != '#') {
|
||||
// -1 to ensure space for a NULL terminator
|
||||
if (packet_len >= (buffer_size - 1)) {
|
||||
return -1;
|
||||
}
|
||||
buffer[packet_len++] = read_char;
|
||||
checksum += read_char;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
buffer[packet_len] = '\0';
|
||||
|
||||
char read_checksum_hex[3];
|
||||
if (gdb_recv(read_checksum_hex, 2) != 2) {
|
||||
return -1;
|
||||
}
|
||||
read_checksum_hex[2] = '\0';
|
||||
uint8_t read_checksum = gdb_unhexlify(read_checksum_hex);
|
||||
|
||||
if (read_checksum != checksum) {
|
||||
read_char = '-';
|
||||
gdb_send(&read_char, 1);
|
||||
return -1;
|
||||
} else {
|
||||
read_char = '+';
|
||||
gdb_send(&read_char, 1);
|
||||
return packet_len;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t gdb_send_packet(const char* packet, size_t packet_length)
|
||||
{
|
||||
if (packet == NULL || packet_length == 0) {
|
||||
// Empty packet
|
||||
gdb_send("$#00", 4);
|
||||
return 4;
|
||||
}
|
||||
|
||||
size_t buffer_length = packet_length + 1 + 4;
|
||||
// TODO : find if it's more efficient to malloc+copy on each packet or send 3 small fxlink messages
|
||||
char* buffer = malloc(buffer_length);
|
||||
|
||||
uint8_t checksum = 0;
|
||||
for (size_t i = 0; i < packet_length; i++) {
|
||||
checksum += packet[i];
|
||||
}
|
||||
|
||||
buffer[0] = '$';
|
||||
memcpy(&buffer[1], packet, packet_length);
|
||||
snprintf(&buffer[buffer_length - 4], 4, "#%02X", checksum);
|
||||
|
||||
// -1 to not send the NULL terminator of snprintf
|
||||
gdb_send(buffer, buffer_length - 1);
|
||||
free(buffer);
|
||||
return buffer_length;
|
||||
}
|
||||
|
||||
#if GDB_BRIDGE_LOGS
|
||||
static void gdb_send_bridge_log(const char* fmt, ...)
|
||||
{
|
||||
char str[256];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(str, sizeof str, fmt, args);
|
||||
va_end(args);
|
||||
usb_fxlink_text(str, strlen(str));
|
||||
}
|
||||
#else
|
||||
# define gdb_send_bridge_log(...)
|
||||
#endif
|
||||
|
||||
static int gdb_signal_number = 0;
|
||||
static int gdb_trap_number = 0;
|
||||
|
||||
static void gdb_send_stop_reply(void)
|
||||
{
|
||||
char str[4] = "S00";
|
||||
uint8_t num = gdb_signal_number ? gdb_signal_number : 5 /* SIGTRAP */;
|
||||
gdb_hexlify(str+1, &num, 1);
|
||||
gdb_send_packet(str, 3);
|
||||
}
|
||||
|
||||
static void gdb_handle_qXfer_packet(const char* packet, const char* data, size_t data_size)
|
||||
{
|
||||
char offset_hex[16] = {0}, length_hex[16] = {0};
|
||||
for (size_t i = 0; i < sizeof(offset_hex); i++) {
|
||||
offset_hex[i] = *(packet++); // consume offset
|
||||
if (*packet == ',') break;
|
||||
}
|
||||
packet++; // consume ','
|
||||
for (size_t i = 0; i < sizeof(length_hex); i++) {
|
||||
length_hex[i] = *(packet++); // consume length
|
||||
if (*packet == '\0') break;
|
||||
}
|
||||
|
||||
size_t offset = (size_t)gdb_unhexlify(offset_hex);
|
||||
size_t length = (size_t)gdb_unhexlify(length_hex);
|
||||
|
||||
if (offset >= data_size) {
|
||||
gdb_send_packet("l", 1);
|
||||
} else if (offset + length >= data_size) {
|
||||
char *reply_buffer = malloc(data_size - offset + 1);
|
||||
reply_buffer[0] = 'l';
|
||||
memcpy(&reply_buffer[1], &data[offset], data_size - offset);
|
||||
gdb_send_packet(reply_buffer, data_size - offset + 1);
|
||||
free(reply_buffer);
|
||||
} else {
|
||||
char *reply_buffer = malloc(length + 1);
|
||||
reply_buffer[0] = 'm';
|
||||
memcpy(&reply_buffer[1], &data[offset], length);
|
||||
gdb_send_packet(reply_buffer, length + 1);
|
||||
free(reply_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/* We implement the memory-map qXfer extension to mark add-in memory as read-only
|
||||
* and enforce hardware breakpoints.
|
||||
* See : https://sourceware.org/gdb/onlinedocs/gdb/Memory-Map-Format.html
|
||||
* https://sourceware.org/gdb/onlinedocs/gdb/Set-Breaks.html
|
||||
*/
|
||||
// TODO : Should we mark other regions as ROM ?
|
||||
static const char gdb_memory_map_xml[] = "<?xml version=\"1.0\"?>"
|
||||
"<!DOCTYPE memory-map PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\" \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"
|
||||
"<memory-map>"
|
||||
// P0 mapping of add-in file
|
||||
"<memory type=\"rom\" start=\"0x00300000\" length=\"0x00200000\"/>"
|
||||
// P0 mapping of user RAM area
|
||||
"<memory type=\"ram\" start=\"0x08100000\" length=\"0x00080000\"/>"
|
||||
// Physical mapping of RAM chip (fx-CG 50)
|
||||
"<memory type=\"ram\" start=\"0x8c000000\" length=\"0x01000000\"/>"
|
||||
// Physical mapping of RAM chip (fx-CG 10/20 + emulator)
|
||||
"<memory type=\"ram\" start=\"0x88000000\" length=\"0x01000000\"/>"
|
||||
"</memory-map>";
|
||||
|
||||
static void gdb_handle_query_packet(const char* packet)
|
||||
{
|
||||
if (strncmp("qSupported", packet, 10) == 0) {
|
||||
const char* qsupported_ans = "PacketSize=255;qXfer:memory-map:read+";
|
||||
gdb_send_packet(qsupported_ans, strlen(qsupported_ans));
|
||||
} else if (strncmp("qXfer:memory-map:read::", packet, 23) == 0) {
|
||||
// -1 to not send the NULL terminator
|
||||
gdb_handle_qXfer_packet(&packet[23], gdb_memory_map_xml, sizeof(gdb_memory_map_xml) - 1);
|
||||
} else {
|
||||
gdb_send_packet(NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void gdb_handle_read_general_registers(gdb_cpu_state_t* cpu_state)
|
||||
{
|
||||
char reply_buffer[23*8];
|
||||
if (!cpu_state) {
|
||||
memset(reply_buffer, 'x', sizeof(reply_buffer));
|
||||
memcpy(&reply_buffer[offsetof(gdb_cpu_state_t, reg.pc)*2],
|
||||
"A0000000", 8); // pc needs to be set to make GDB happy
|
||||
} else {
|
||||
gdb_hexlify(reply_buffer, (uint8_t*)cpu_state->regs,
|
||||
sizeof(cpu_state->regs));
|
||||
}
|
||||
gdb_send_packet(reply_buffer, sizeof(reply_buffer));
|
||||
}
|
||||
|
||||
static void gdb_handle_read_register(gdb_cpu_state_t* cpu_state, const char* packet)
|
||||
{
|
||||
uint8_t register_id = gdb_unhexlify(&packet[1]);
|
||||
char reply_buffer[8];
|
||||
if (!cpu_state || register_id >= sizeof(cpu_state->regs)/sizeof(uint32_t)) {
|
||||
memset(reply_buffer, 'x', sizeof(reply_buffer));
|
||||
} else {
|
||||
gdb_hexlify(reply_buffer, (uint8_t*)&cpu_state->regs[register_id],
|
||||
sizeof(cpu_state->regs[register_id]));
|
||||
}
|
||||
gdb_send_packet(reply_buffer, sizeof(reply_buffer));
|
||||
}
|
||||
|
||||
static void gdb_handle_write_general_registers(gdb_cpu_state_t* cpu_state, const char* packet)
|
||||
{
|
||||
if (!cpu_state) {
|
||||
gdb_send_packet(NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
packet++; // consume 'G'
|
||||
|
||||
// Let's not handle incomplete 'G' packets as they're rarely used anyway
|
||||
if (strlen(packet) != sizeof(cpu_state->regs)*2) {
|
||||
gdb_send_packet(NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(cpu_state->regs)/sizeof(uint32_t); i++) {
|
||||
cpu_state->regs[i] = gdb_unhexlify_sized(&packet[i * sizeof(uint32_t) * 2],
|
||||
sizeof(uint32_t) * 2);
|
||||
}
|
||||
gdb_send_packet("OK", 2);
|
||||
}
|
||||
|
||||
static void gdb_handle_write_register(gdb_cpu_state_t* cpu_state, const char* packet)
|
||||
{
|
||||
if (!cpu_state) {
|
||||
gdb_send_packet(NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
char register_id_hex[16] = {0}, value_hex[16] = {0};
|
||||
|
||||
packet++; // consume 'P'
|
||||
for (size_t i = 0; i < sizeof(register_id_hex); i++) {
|
||||
register_id_hex[i] = *(packet++); // consume register id
|
||||
if (*packet == '=') break;
|
||||
}
|
||||
packet++; // consume '='
|
||||
for (size_t i = 0; i < sizeof(value_hex); i++) {
|
||||
value_hex[i] = *(packet++); // consume register value
|
||||
if (*packet == '\0') break;
|
||||
}
|
||||
|
||||
uint32_t register_id = gdb_unhexlify(register_id_hex);
|
||||
uint32_t value = gdb_unhexlify(value_hex);
|
||||
|
||||
if (register_id >= sizeof(cpu_state->regs)/sizeof(uint32_t)) {
|
||||
gdb_send_packet(NULL, 0);
|
||||
} else {
|
||||
cpu_state->regs[register_id] = value;
|
||||
gdb_send_packet("OK", 2);
|
||||
}
|
||||
}
|
||||
|
||||
static volatile bool gdb_tlbh_enable = false;
|
||||
static volatile bool gdb_tlbh_caught = false;
|
||||
|
||||
static void gdb_handle_read_memory(const char* packet)
|
||||
{
|
||||
char address_hex[16] = {0}, size_hex[16] = {0};
|
||||
uint8_t* read_address;
|
||||
size_t read_size;
|
||||
|
||||
packet++; // consume 'm'
|
||||
for (size_t i = 0; i < sizeof(address_hex); i++) {
|
||||
address_hex[i] = *(packet++); // consume address
|
||||
if (*packet == ',') break;
|
||||
}
|
||||
packet++; // consume ','
|
||||
for (size_t i = 0; i < sizeof(size_hex); i++) {
|
||||
size_hex[i] = *(packet++); // consume size
|
||||
if (*packet == '\0') break;
|
||||
}
|
||||
|
||||
read_address = (uint8_t*) gdb_unhexlify(address_hex);
|
||||
read_size = (size_t) gdb_unhexlify(size_hex);
|
||||
|
||||
char *reply_buffer = malloc(read_size * 2);
|
||||
|
||||
gdb_tlbh_enable = true;
|
||||
gdb_tlbh_caught = false;
|
||||
for (size_t i = 0; i < read_size && !gdb_tlbh_caught; i++) {
|
||||
gdb_hexlify(&reply_buffer[i * 2], &read_address[i], 1);
|
||||
}
|
||||
gdb_tlbh_enable = false;
|
||||
|
||||
if (gdb_tlbh_caught) {
|
||||
gdb_send_packet("E22", 3); // EINVAL
|
||||
gdb_tlbh_caught = false;
|
||||
} else {
|
||||
gdb_send_packet(reply_buffer, read_size * 2);
|
||||
}
|
||||
free(reply_buffer);
|
||||
}
|
||||
|
||||
static void cache_ocbwb(void *start, void *end)
|
||||
{
|
||||
/* Cache lines are 32-aligned */
|
||||
void *p = (void *)((uintptr_t)start & -32);
|
||||
|
||||
while(p < end) {
|
||||
__asm__("ocbwb @%0":: "r"(p));
|
||||
p += 32;
|
||||
}
|
||||
}
|
||||
|
||||
static void cache_icbi(void *start, void *end)
|
||||
{
|
||||
/* Cache lines are 32-aligned */
|
||||
void *p = (void *)((uintptr_t)start & -32);
|
||||
|
||||
while(p < end) {
|
||||
__asm__("icbi @%0":: "r"(p));
|
||||
p += 32;
|
||||
}
|
||||
}
|
||||
|
||||
static void gdb_handle_write_memory(const char* packet)
|
||||
{
|
||||
char address_hex[16] = {0}, size_hex[16] = {0};
|
||||
uint8_t* read_address;
|
||||
size_t read_size;
|
||||
|
||||
packet++; // consume 'M'
|
||||
for (size_t i = 0; i < sizeof(address_hex); i++) {
|
||||
address_hex[i] = *(packet++); // consume address
|
||||
if (*packet == ',') break;
|
||||
}
|
||||
packet++; // consume ','
|
||||
for (size_t i = 0; i < sizeof(size_hex); i++) {
|
||||
size_hex[i] = *(packet++); // consume size
|
||||
if (*packet == ':') break;
|
||||
}
|
||||
packet++; // consume ':'
|
||||
|
||||
read_address = (uint8_t*) gdb_unhexlify(address_hex);
|
||||
read_size = (size_t) gdb_unhexlify(size_hex);
|
||||
|
||||
gdb_tlbh_enable = true;
|
||||
gdb_tlbh_caught = false;
|
||||
for (size_t i = 0; i < read_size && !gdb_tlbh_caught; i++) {
|
||||
read_address[i] = (uint8_t)gdb_unhexlify_sized(&packet[i * 2], 2);
|
||||
}
|
||||
gdb_tlbh_enable = false;
|
||||
|
||||
cache_ocbwb(read_address, read_address + read_size);
|
||||
cache_icbi(read_address, read_address + read_size);
|
||||
|
||||
if (gdb_tlbh_caught) {
|
||||
gdb_send_packet("E22", 3); // EINVAL
|
||||
gdb_tlbh_caught = false;
|
||||
} else {
|
||||
gdb_send_packet("OK", 2);
|
||||
}
|
||||
}
|
||||
|
||||
static bool gdb_parse_hardware_breakpoint_packet(const char* packet, void** read_address)
|
||||
{
|
||||
packet++; // consume 'z' or 'Z'
|
||||
if (*packet != '1') { // hardware breakpoint
|
||||
return false;
|
||||
}
|
||||
packet++; // consume '1'
|
||||
packet++; // consume ','
|
||||
|
||||
char address_hex[16] = {0}, kind_hex[16] = {0};
|
||||
for (size_t i = 0; i < sizeof(address_hex); i++) {
|
||||
address_hex[i] = *(packet++); // consume address
|
||||
if (*packet == ',') break;
|
||||
}
|
||||
packet++; // consume ','
|
||||
for (size_t i = 0; i < sizeof(kind_hex); i++) {
|
||||
kind_hex[i] = *(packet++); // consume kind
|
||||
if (*packet == '\0' || *packet == ';') break;
|
||||
}
|
||||
|
||||
*read_address = (void*) gdb_unhexlify(address_hex);
|
||||
uint32_t read_kind = gdb_unhexlify(kind_hex);
|
||||
|
||||
if (read_kind != 2) { // SuperH instructions are 2 bytes long
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gdb_handle_insert_hardware_breakpoint(const char* packet)
|
||||
{
|
||||
void* read_address;
|
||||
if (!gdb_parse_hardware_breakpoint_packet(packet, &read_address)) {
|
||||
gdb_send_bridge_log("bad Z packet\n");
|
||||
gdb_send_packet(NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void *channel0_addr, *channel1_addr;
|
||||
bool channel0_used = ubc_get_break_address(0, &channel0_addr);
|
||||
bool channel1_used = ubc_get_break_address(1, &channel1_addr);
|
||||
|
||||
/* As stated by GDB doc : "the operations should be implemented in an idempotent way."
|
||||
* Thus we first check if the breakpoint is already placed in one of the UBC channel.
|
||||
*/
|
||||
if ((channel0_used && channel0_addr == read_address) ||
|
||||
(channel1_used && channel1_addr == read_address)) {
|
||||
gdb_send_bridge_log("hb %p: already exists\n", read_address);
|
||||
gdb_send_packet("OK", 2);
|
||||
} else if (!channel0_used) {
|
||||
ubc_set_breakpoint(0, read_address, UBC_BREAK_BEFORE);
|
||||
gdb_send_bridge_log("hb %p: using channel 0\n", read_address);
|
||||
gdb_send_packet("OK", 2);
|
||||
} else if (!channel1_used) {
|
||||
ubc_set_breakpoint(1, read_address, UBC_BREAK_BEFORE);
|
||||
gdb_send_bridge_log("hb %p: using channel 1\n", read_address);
|
||||
gdb_send_packet("OK", 2);
|
||||
} else {
|
||||
/* TODO : We should find a proper way to inform GDB that we are
|
||||
* limited by the number of UBC channels.
|
||||
*/
|
||||
gdb_send_bridge_log("hb %p: channels used (%p, %p)\n", read_address,
|
||||
channel0_addr, channel1_addr);
|
||||
gdb_send_packet(NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void gdb_handle_remove_hardware_breakpoint(const char* packet)
|
||||
{
|
||||
void* read_address;
|
||||
if (!gdb_parse_hardware_breakpoint_packet(packet, &read_address)) {
|
||||
gdb_send_packet(NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
void *channel0_addr, *channel1_addr;
|
||||
bool channel0_used = ubc_get_break_address(0, &channel0_addr);
|
||||
bool channel1_used = ubc_get_break_address(1, &channel1_addr);
|
||||
|
||||
if (channel0_used && channel0_addr == read_address) {
|
||||
ubc_disable_channel(0);
|
||||
}
|
||||
if (channel1_used && channel1_addr == read_address) {
|
||||
ubc_disable_channel(1);
|
||||
}
|
||||
gdb_send_packet("OK", 2);
|
||||
}
|
||||
|
||||
static void gdb_handle_continue_with_signal(gdb_cpu_state_t* cpu_state,
|
||||
const char* packet)
|
||||
{
|
||||
packet++; // consume 'C'
|
||||
int signal = gdb_unhexlify_sized(packet, 2);
|
||||
char exit[4] = { 'X', packet[0], packet[1], 0 };
|
||||
packet += 2;
|
||||
|
||||
if(*packet == ';')
|
||||
cpu_state->reg.pc = gdb_unhexlify(packet + 1);
|
||||
|
||||
// TODO: This is a heuristic replacing the normal signal system
|
||||
uint32_t kills =
|
||||
(1 << 4) /* SIGILL */
|
||||
+ (1 << 6) /* SIGABRT */
|
||||
+ (1 << 7) /* SIGEMT */
|
||||
+ (1 << 8) /* SIGFPE */
|
||||
+ (1 << 9) /* SIGKILL */
|
||||
+ (1 << 11) /* SIGSEGV */
|
||||
+ (1 << 15); /* SIGTERM */
|
||||
// Abort if the signal is kill by default
|
||||
if((uint)signal < 32 && (kills >> signal) & 1) {
|
||||
gdb_send_packet(exit, 3);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static struct {
|
||||
bool single_stepped;
|
||||
bool channel0_used;
|
||||
bool channel1_used;
|
||||
void* channel0_addr;
|
||||
void* channel1_addr;
|
||||
} gdb_single_step_backup = { false };
|
||||
static void gdb_handle_single_step(uint32_t pc, ubc_break_mode_t break_mode)
|
||||
{
|
||||
gdb_single_step_backup.channel0_used = ubc_get_break_address(0, &gdb_single_step_backup.channel0_addr);
|
||||
gdb_single_step_backup.channel1_used = ubc_get_break_address(1, &gdb_single_step_backup.channel1_addr);
|
||||
|
||||
ubc_disable_channel(0);
|
||||
ubc_set_breakpoint(1, (void*)pc, break_mode);
|
||||
|
||||
gdb_single_step_backup.single_stepped = true;
|
||||
}
|
||||
|
||||
static bool gdb_handle_stubcall(gdb_cpu_state_t* cpu_state)
|
||||
{
|
||||
char str[30];
|
||||
int sc_num = cpu_state->reg.r3;
|
||||
|
||||
if(sc_num == 64) { /* write */
|
||||
int len = snprintf(str, sizeof str, "Fwrite,%x,%08x,%x",
|
||||
cpu_state->reg.r4,
|
||||
cpu_state->reg.r5,
|
||||
cpu_state->reg.r6);
|
||||
gdb_send_packet(str, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void gdb_main(gdb_cpu_state_t* cpu_state)
|
||||
{
|
||||
if (!gdb_started && gdb_start()) {
|
||||
gdb_show_stub_status(ICON_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
gdb_show_stub_status(ICON_IDLE);
|
||||
|
||||
if (gdb_single_step_backup.single_stepped) {
|
||||
if (gdb_single_step_backup.channel0_used) {
|
||||
ubc_set_breakpoint(0, gdb_single_step_backup.channel0_addr, UBC_BREAK_BEFORE);
|
||||
} else {
|
||||
ubc_disable_channel(0);
|
||||
}
|
||||
if (gdb_single_step_backup.channel1_used) {
|
||||
ubc_set_breakpoint(1, gdb_single_step_backup.channel1_addr, UBC_BREAK_BEFORE);
|
||||
} else {
|
||||
ubc_disable_channel(1);
|
||||
}
|
||||
|
||||
gdb_single_step_backup.single_stepped = false;
|
||||
}
|
||||
|
||||
if (cpu_state != NULL) {
|
||||
/* Ajust PC after a software breakpoint */
|
||||
if (gdb_trap_number == TRA_SWBREAK)
|
||||
cpu_state->reg.pc -= 2;
|
||||
|
||||
/* Handle stubcall but fallback to normal stop if it fails */
|
||||
if (gdb_trap_number != TRA_STUBCALL || !gdb_handle_stubcall(cpu_state))
|
||||
gdb_send_stop_reply();
|
||||
}
|
||||
|
||||
while (1) {
|
||||
gdb_show_stub_status(ICON_COMM);
|
||||
|
||||
char packet_buffer[256];
|
||||
ssize_t packet_size = gdb_recv_packet(packet_buffer, sizeof(packet_buffer));
|
||||
if (packet_size <= 0) {
|
||||
// TODO : Should we break or log on recv error ?
|
||||
continue;
|
||||
}
|
||||
|
||||
gdb_show_stub_status(ICON_WORKING);
|
||||
|
||||
switch (packet_buffer[0]) {
|
||||
case '?': // Halt reason
|
||||
gdb_send_stop_reply();
|
||||
break;
|
||||
case 'q':
|
||||
gdb_handle_query_packet(packet_buffer);
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
gdb_handle_read_general_registers(cpu_state);
|
||||
break;
|
||||
case 'p':
|
||||
gdb_handle_read_register(cpu_state, packet_buffer);
|
||||
break;
|
||||
case 'm':
|
||||
gdb_handle_read_memory(packet_buffer);
|
||||
break;
|
||||
case 'G':
|
||||
gdb_handle_write_general_registers(cpu_state, packet_buffer);
|
||||
break;
|
||||
case 'P':
|
||||
gdb_handle_write_register(cpu_state, packet_buffer);
|
||||
break;
|
||||
case 'M':
|
||||
gdb_handle_write_memory(packet_buffer);
|
||||
break;
|
||||
|
||||
case 'k': // Kill request
|
||||
abort();
|
||||
|
||||
case 'Z':
|
||||
gdb_handle_insert_hardware_breakpoint(packet_buffer);
|
||||
break;
|
||||
case 'z':
|
||||
gdb_handle_remove_hardware_breakpoint(packet_buffer);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
gdb_handle_single_step(cpu_state->reg.pc, UBC_BREAK_AFTER);
|
||||
goto ret;
|
||||
case 'c': // Continue
|
||||
goto ret;
|
||||
case 'C': // Continue with signal
|
||||
gdb_handle_continue_with_signal(cpu_state, packet_buffer);
|
||||
// We'll often abort() at the signal rather than continuing
|
||||
goto ret;
|
||||
case 'F': // Continue after File I/O call response
|
||||
// TODO: parse 'F' response packets.
|
||||
goto ret;
|
||||
|
||||
default: // Unsupported packet
|
||||
gdb_send_packet(NULL, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
gdb_show_stub_status(ICON_IDLE);
|
||||
}
|
||||
|
||||
ret:
|
||||
// We're started after the first round of exchanges
|
||||
gdb_started = true;
|
||||
|
||||
gdb_signal_number = 0;
|
||||
gdb_trap_number = 0;
|
||||
}
|
||||
|
||||
static void gdb_notifier_function(void)
|
||||
{
|
||||
// We ignore fxlink notifications when we're already inside GDB code.
|
||||
if (ubc_dbh_lock || !gdb_started)
|
||||
return;
|
||||
|
||||
// We make sure we are called during a USB interrupt.
|
||||
if (usb_interrupt_context == NULL)
|
||||
return;
|
||||
|
||||
// And we make sure an other step break is not already set up.
|
||||
if (gdb_single_step_backup.single_stepped)
|
||||
return;
|
||||
|
||||
gdb_handle_single_step(usb_interrupt_context->spc, UBC_BREAK_AFTER);
|
||||
}
|
||||
|
||||
static int gdb_panic_handler(uint32_t code)
|
||||
{
|
||||
// Catch memory access errors from GDB trying to read/print stuff
|
||||
if (gdb_tlbh_enable) {
|
||||
// We only handle TLB miss reads (0x040) and writes (0x060)
|
||||
if (code != 0x040 && code != 0x060)
|
||||
return 1;
|
||||
|
||||
gdb_tlbh_caught = true;
|
||||
|
||||
// We skip the offending instruction and continue
|
||||
gint_exc_skip(1);
|
||||
return 0;
|
||||
}
|
||||
// If we are in user code, let's break
|
||||
else if (!ubc_dbh_lock) {
|
||||
// We make sure an other step break is not already set up
|
||||
if (gdb_single_step_backup.single_stepped)
|
||||
return 1;
|
||||
|
||||
// TODO: This only works for re-execution type exceptions
|
||||
uint32_t spc;
|
||||
__asm__("stc spc, %0" : "=r"(spc));
|
||||
gdb_handle_single_step(spc, UBC_BREAK_BEFORE);
|
||||
|
||||
// Break reason
|
||||
if(code == 0x040 || code == 0x060 || code == 0x0e0 || code == 0x100)
|
||||
gdb_signal_number = 11; /* SIGSEGV */
|
||||
if(code == 0x160)
|
||||
gdb_signal_number = 5; /* SIGTRAP */
|
||||
if(code == 0x180 || code == 0x1a0)
|
||||
gdb_signal_number = 4; /* SIGILL */
|
||||
if(code >= 0x1000 && code != 0x10a0)
|
||||
gdb_signal_number = 5; /* SIGTRAP */
|
||||
if(code == 0x10a0)
|
||||
gdb_signal_number = 7; /* SIGEMT (used here for bad UBC breaks) */
|
||||
|
||||
// Specific stop reasons
|
||||
if(code == 0x160) {
|
||||
uint32_t TRA = isSH3() ? 0xffffffd0 : 0xff000020;
|
||||
gdb_trap_number = *(uint32_t volatile *)TRA >> 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool gdb_redirect_stdout = false;
|
||||
static bool gdb_redirect_stderr = false;
|
||||
|
||||
static fs_descriptor_type_t const redirect_type = {
|
||||
.read = NULL,
|
||||
.write = (void *)gdb_stubcall_write,
|
||||
.lseek = NULL,
|
||||
.close = NULL,
|
||||
};
|
||||
|
||||
int gdb_start(void)
|
||||
{
|
||||
if (gdb_started)
|
||||
return 0;
|
||||
|
||||
gdb_show_stub_status(ICON_WORKING);
|
||||
|
||||
if(usb_is_open() && !usb_is_open_interface(&usb_ff_bulk))
|
||||
usb_close();
|
||||
|
||||
if(!usb_is_open()) {
|
||||
usb_interface_t const *interfaces[] = { &usb_ff_bulk, NULL };
|
||||
if(usb_open(interfaces, GINT_CALL_NULL) < 0)
|
||||
return -1;
|
||||
usb_open_wait();
|
||||
}
|
||||
|
||||
usb_fxlink_set_notifier(gdb_notifier_function);
|
||||
gdb_send_start();
|
||||
|
||||
if (!gdb_recv_buffer) {
|
||||
gdb_recv_buffer = malloc(gdb_recv_buffer_capacity);
|
||||
}
|
||||
|
||||
// Redirect standard streams
|
||||
if(gdb_redirect_stdout) {
|
||||
close(STDOUT_FILENO);
|
||||
open_generic(&redirect_type, (void *)STDOUT_FILENO, STDOUT_FILENO);
|
||||
}
|
||||
if(gdb_redirect_stderr) {
|
||||
close(STDERR_FILENO);
|
||||
open_generic(&redirect_type, (void *)STDERR_FILENO, STDERR_FILENO);
|
||||
}
|
||||
|
||||
// TODO : Should we detect if other panic or debug handlers are setup ?
|
||||
gint_exc_catch(gdb_panic_handler);
|
||||
ubc_set_debug_handler(gdb_main);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gdb_start_on_exception(void)
|
||||
{
|
||||
gint_exc_catch(gdb_panic_handler);
|
||||
ubc_set_debug_handler(gdb_main);
|
||||
}
|
||||
|
||||
void gdb_redirect_streams(bool stdout, bool stderr)
|
||||
{
|
||||
gdb_redirect_stdout = stdout;
|
||||
gdb_redirect_stderr = stderr;
|
||||
}
|
BIN
src/gdb/icons-i1msb.png
Normal file
BIN
src/gdb/icons-i1msb.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 626 B |
BIN
src/gdb/icons-rgb565.png
Normal file
BIN
src/gdb/icons-rgb565.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 614 B |
|
@ -11,6 +11,15 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "../render-fx/render-fx.h"
|
||||
#include "../render/render.h"
|
||||
#include <gint/config.h>
|
||||
|
||||
#if GINT_HW_CG
|
||||
#include <gint/drivers/r61524.h>
|
||||
#endif
|
||||
|
||||
// TODO: Move the gray "engine" part into the T6K11 driver.
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
/* Three additional video RAMS, allocated statically if --static-gray was set
|
||||
at configure time, or with malloc() otherwise. */
|
||||
|
@ -21,6 +30,8 @@ GBSS static uint32_t gvrams[3][256];
|
|||
/* Four VRAMs: two to draw and two to display */
|
||||
static uint32_t *vrams[4] = { NULL, NULL, NULL, NULL };
|
||||
|
||||
#if GINT_HW_FX
|
||||
|
||||
/* Current VRAM pair used for drawing; the value can either be 0 (draws to
|
||||
VRAMs 0 and 1) or 2 (draws to VRAMs 2 and 3). */
|
||||
static int volatile st = 0;
|
||||
|
@ -35,12 +46,17 @@ static int runs = 0;
|
|||
/* Delays of the light and dark frames for the above setting */
|
||||
GBSS static int delays[2];
|
||||
|
||||
static int gray_int(void);
|
||||
|
||||
#endif
|
||||
|
||||
/* The alternate rendering mode structure used to override d*() */
|
||||
static struct rendering_mode const gray_mode = {
|
||||
.dupdate = gupdate,
|
||||
.dclear = gclear,
|
||||
.drect = grect,
|
||||
.dpixel = gpixel,
|
||||
.dgetpixel = ggetpixel,
|
||||
.gint_dhline = gint_ghline,
|
||||
.gint_dvline = gint_gvline,
|
||||
.dtext_opt = gtext_opt,
|
||||
|
@ -51,6 +67,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,
|
||||
|
@ -61,13 +78,16 @@ static struct rendering_mode const gray_exit_mode = {
|
|||
// Engine control (init/quit and start/stop)
|
||||
//---
|
||||
|
||||
static int gray_int(void);
|
||||
static void gray_quit(void);
|
||||
|
||||
/* gray_isinit(): Check whether the engine is initialized and ready to run */
|
||||
static int gray_isinit(void)
|
||||
{
|
||||
return (vrams[0] && vrams[1] && vrams[2] && vrams[3] && timer >= 0);
|
||||
return (vrams[0] && vrams[1] && vrams[2] && vrams[3]
|
||||
#if GINT_HW_FX
|
||||
&& timer >= 0
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
/* gray_init(): Initialize the engine
|
||||
|
@ -88,6 +108,7 @@ GCONSTRUCTOR static void gray_init(void)
|
|||
vrams[3] = malloc(1024);
|
||||
#endif /* GINT_STATIC_GRAY */
|
||||
|
||||
#if GINT_HW_FX
|
||||
/* Default delays from Graph 35+E II are different from other models */
|
||||
if(gint[HWCALC] == HWCALC_G35PE2)
|
||||
{
|
||||
|
@ -103,6 +124,7 @@ GCONSTRUCTOR static void gray_init(void)
|
|||
/* Try to obtain the timer right away */
|
||||
timer = timer_configure(GRAY_TIMER | GRAY_CLOCK, 1000,
|
||||
GINT_CALL(gray_int));
|
||||
#endif
|
||||
|
||||
/* On failure, release the resources that we obtained */
|
||||
if(!gray_isinit()) gray_quit();
|
||||
|
@ -121,10 +143,13 @@ GDESTRUCTOR static void gray_quit(void)
|
|||
vrams[3] = NULL;
|
||||
#endif /* GINT_STATIC_GRAY */
|
||||
|
||||
#if GINT_HW_FX
|
||||
if(timer >= 0) timer_stop(timer);
|
||||
timer = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if GINT_HW_FX
|
||||
/* gray_start(): Start the gray engine */
|
||||
static void gray_start(void)
|
||||
{
|
||||
|
@ -141,6 +166,7 @@ static void gray_stop(void)
|
|||
runs = 0;
|
||||
st = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Dynamic udpate and rendering mode
|
||||
|
@ -194,6 +220,7 @@ int dgray(int mode)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if GINT_HW_FX
|
||||
/* gray_int(): Interrupt handler */
|
||||
int gray_int(void)
|
||||
{
|
||||
|
@ -225,6 +252,19 @@ int gupdate(void)
|
|||
st ^= 2;
|
||||
return 0;
|
||||
}
|
||||
#elif GINT_HW_CG
|
||||
int gupdate(void)
|
||||
{
|
||||
if(dmode == &gray_exit_mode)
|
||||
{
|
||||
dmode = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
r61524_display_gray_128x64(vrams[0], vrams[1]);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Query and configuration functions
|
||||
|
@ -236,6 +276,7 @@ int dgray_enabled(void)
|
|||
return (dmode == &gray_mode);
|
||||
}
|
||||
|
||||
#if GINT_HW_FX
|
||||
/* dgray_setdelays(): Set the gray engine delays */
|
||||
void dgray_setdelays(uint32_t light, uint32_t dark)
|
||||
{
|
||||
|
@ -267,3 +308,12 @@ void dgray_getscreen(uint32_t **light, uint32_t **dark)
|
|||
if(light) *light = vrams[base & 2];
|
||||
if(dark) *dark = vrams[base | 1];
|
||||
}
|
||||
#elif GINT_HW_CG
|
||||
void dgray_getvram(uint32_t **light, uint32_t **dark)
|
||||
{
|
||||
*light = vrams[0];
|
||||
*dark = vrams[1];
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_RENDER_MONO && GINT_HW_FX */
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#include <gint/gray.h>
|
||||
#include <gint/config.h>
|
||||
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
/* gclear(): Fill the screen with a single color */
|
||||
void gclear(color_t color)
|
||||
|
@ -35,3 +38,5 @@ void gclear(color_t color)
|
|||
dark += 8;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* GINT_RENDER_MONO */
|
||||
|
|
20
src/gray/ggetpixel.c
Normal file
20
src/gray/ggetpixel.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <gint/gray.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/config.h>
|
||||
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
int ggetpixel(int x, int y)
|
||||
{
|
||||
uint32_t *light, *dark;
|
||||
dgray_getvram(&light, &dark);
|
||||
|
||||
int offset = (y << 2) + (x >> 5);
|
||||
uint32_t mask = 1 << (~x & 31);
|
||||
|
||||
bool l = (light[offset] & mask) != 0;
|
||||
bool d = (dark [offset] & mask) != 0;
|
||||
return (d << 1) | l;
|
||||
}
|
||||
|
||||
#endif /* GINT_RENDER_MONO */
|
|
@ -1,5 +1,8 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/config.h>
|
||||
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
/* gint_ghline(): Optimized horizontal line, but not actually optimized */
|
||||
void gint_ghline(int x1, int x2, int y, int color)
|
||||
|
@ -14,3 +17,5 @@ void gint_gvline(int y1, int y2, int x, int color)
|
|||
if(y1 > y2) swap(y1, y2);
|
||||
for(int y = y1; y <= y2; y++) dpixel(x, y, color);
|
||||
}
|
||||
|
||||
#endif /* GINT_RENDER_MONO */
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include <gint/gray.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/config.h>
|
||||
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
/* gpixel(): Change a pixel's color */
|
||||
void gpixel(int x, int y, color_t color)
|
||||
|
@ -55,3 +58,5 @@ void gpixel(int x, int y, color_t color)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* GINT_RENDER_MONO */
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include <gint/defs/util.h>
|
||||
#include <gint/gray.h>
|
||||
#include "../render-fx/render-fx.h"
|
||||
#include <gint/config.h>
|
||||
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
/* grect(): Fill a rectangle on the screen */
|
||||
void grect(int x1, int y1, int x2, int y2, color_t color)
|
||||
|
@ -112,3 +115,5 @@ void grect(int x1, int y1, int x2, int y2, color_t color)
|
|||
light++, dark++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* GINT_RENDER_MONO */
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include <gint/gray.h>
|
||||
#include "../render-fx/render-fx.h"
|
||||
#include "../render-fx/bopti-asm.h"
|
||||
#include <gint/config.h>
|
||||
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
#pragma GCC optimize("O3")
|
||||
|
||||
|
@ -37,3 +39,5 @@ void gsubimage(bopti_image_t const *img, struct rbox *r, GUNUSED int flags)
|
|||
bopti_render(img, r, light, dark);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* GINT_RENDER_MONO */
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#include <gint/gray.h>
|
||||
#include "../render/render.h"
|
||||
#include "../render-fx/topti-asm.h"
|
||||
#include "../render-fx/render-fx.h"
|
||||
#include <gint/config.h>
|
||||
|
||||
#if GINT_RENDER_MONO
|
||||
|
||||
/* gtext_opt(): Display a string of text */
|
||||
void gtext_opt(int x, int y, int fg, int bg, int halign, int valign,
|
||||
|
@ -23,3 +26,5 @@ void gtext_opt(int x, int y, int fg, int bg, int halign, int valign,
|
|||
topti_render(x, y, str, topti_font, topti_asm_text[fg],
|
||||
topti_asm_text[bg], light, dark, size);
|
||||
}
|
||||
|
||||
#endif /* GINT_RENDER_MONO */
|
||||
|
|
49
src/image/fixed.h
Normal file
49
src/image/fixed.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
//---
|
||||
// gint:image:fixed - Minimal fixed-point interface for linear transformations
|
||||
//---
|
||||
|
||||
#ifndef GINT_IMAGE_FIXED
|
||||
#define GINT_IMAGE_FIXED
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* 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 */
|
31
src/image/image_alloc.c
Normal file
31
src/image/image_alloc.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <gint/image.h>
|
||||
#include <stdlib.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
36
src/image/image_alloc_palette.c
Normal file
36
src/image/image_alloc_palette.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
20
src/image/image_alpha.c
Normal file
20
src/image/image_alpha.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
13
src/image/image_clear.c
Normal file
13
src/image/image_clear.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
void image_clear(image_t *img)
|
||||
{
|
||||
if(!IMAGE_IS_ALPHA(img->format))
|
||||
return;
|
||||
|
||||
image_fill(img, image_alpha(img->format));
|
||||
}
|
||||
|
||||
#endif
|
126
src/image/image_copy.c
Normal file
126
src/image/image_copy.c
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
25
src/image/image_copy_alloc.c
Normal file
25
src/image/image_copy_alloc.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
25
src/image/image_copy_palette.c
Normal file
25
src/image/image_copy_palette.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
30
src/image/image_create.c
Normal file
30
src/image/image_create.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <gint/image.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
17
src/image/image_create_vram.c
Normal file
17
src/image/image_create_vram.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
10
src/image/image_data_size.c
Normal file
10
src/image/image_data_size.c
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
int image_data_size(image_t const *img)
|
||||
{
|
||||
return img->stride * img->height;
|
||||
}
|
||||
|
||||
#endif
|
16
src/image/image_decode_pixel.c
Normal file
16
src/image/image_decode_pixel.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
30
src/image/image_fill.c
Normal file
30
src/image/image_fill.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
21
src/image/image_free.c
Normal file
21
src/image/image_free.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/mmu.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
29
src/image/image_get_pixel.c
Normal file
29
src/image/image_get_pixel.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
51
src/image/image_hflip.c
Normal file
51
src/image/image_hflip.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
20
src/image/image_hflip_alloc.c
Normal file
20
src/image/image_hflip_alloc.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
131
src/image/image_linear.S
Normal file
131
src/image/image_linear.S
Normal file
|
@ -0,0 +1,131 @@
|
|||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
.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
|
||||
|
||||
#endif
|
39
src/image/image_linear.c
Normal file
39
src/image/image_linear.c
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include "fixed.h"
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
24
src/image/image_linear_alloc.c
Normal file
24
src/image/image_linear_alloc.c
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
17
src/image/image_rotate.c
Normal file
17
src/image/image_rotate.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
14
src/image/image_rotate_around.c
Normal file
14
src/image/image_rotate_around.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <gint/image.h>
|
||||
#include "fixed.h"
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
65
src/image/image_rotate_around_scale.c
Normal file
65
src/image/image_rotate_around_scale.c
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include <gint/image.h>
|
||||
#include <math.h>
|
||||
#include "fixed.h"
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
29
src/image/image_scale.c
Normal file
29
src/image/image_scale.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include <gint/image.h>
|
||||
#include "fixed.h"
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
23
src/image/image_set_palette.c
Normal file
23
src/image/image_set_palette.c
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include <gint/image.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
30
src/image/image_set_pixel.c
Normal file
30
src/image/image_set_pixel.c
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
46
src/image/image_sub.c
Normal file
46
src/image/image_sub.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/display.h>
|
||||
#include <string.h>
|
||||
#undef image_sub
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
44
src/image/image_target.c
Normal file
44
src/image/image_target.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include <gint/image.h>
|
||||
#undef image_target
|
||||
|
||||
#include <gint/config.h>
|
||||
#if GINT_RENDER_RGB
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue