forked from Lephenixnoir/gint
Compare commits
89 Commits
Author | SHA1 | Date |
---|---|---|
Sylvain PILLOT | 4720a69fdc | |
Sylvain PILLOT | cd5db9a26c | |
Lephe | 3a2a194693 | |
Sylvain PILLOT | e960ec3463 | |
Sylvain PILLOT | 0e3bd1e2e5 | |
Sylvain PILLOT | 5e086917eb | |
Sylvain PILLOT | c8903fdc58 | |
Lephe | 4983849510 | |
Sylvain PILLOT | fa7885a287 | |
Sylvain PILLOT | e4653c0ace | |
Sylvain PILLOT | a41c44988b | |
Sylvain PILLOT | 469be10609 | |
Sylvain PILLOT | 69b5478e40 | |
Lephe | fc1f510288 | |
Sylvain PILLOT | 68ef7b2507 | |
Sylvain PILLOT | 9633ec52f0 | |
Lephe | c59b2f90fb | |
Lephe | eb1f9a35a1 | |
Lephe | f4e13afa84 | |
Lephe | 911691461f | |
Lephe | aee67a76f3 | |
Lephe | c0671649df | |
Lephe | 53c67af52d | |
Lephe | 6910714c2c | |
Lephe | af5c16a3d3 | |
Lephe | 1a61e97ef0 | |
Lephe | a091efc894 | |
Lephe | 6f758cd36c | |
Lephe | 18e0db3886 | |
Lephe | cf2b86deaa | |
Lephe | db50c9b192 | |
Lephe | 42853103aa | |
Lephe | c28d06002f | |
Lephe | ac6b1c7d70 | |
Lephe | 45881995e9 | |
Lephe | 70dccc29da | |
Lephe | 7e859169fe | |
Lephe | b3416dcc25 | |
Lephe | 736b58f205 | |
Lephe | 478fdaea76 | |
Lephe | d3b29c50e6 | |
Lephe | 1272a6a71a | |
Lephe | 723bae134b | |
Lephe | 3bc3892524 | |
Lephe | 211d236496 | |
Lephe | a4cf3516e7 | |
Lephe | 177879d432 | |
Lephe | feb74a38ec | |
Lephe | 3192078c4c | |
Lephenixnoir | a5fb6d3401 | |
Lephe | e2c507855f | |
mibi88 | 813af222fd | |
mibi88 | 984f162cb2 | |
mibi88 | 6426406043 | |
mibi88 | 2fe67412cc | |
mibi88 | f807b528bb | |
mibi88 | ad0da59de6 | |
mibi88 | 56e5c2452a | |
Lephe | 8442e0b9cf | |
Lephe | 5461d54083 | |
mibi88 | 386303136c | |
mibi88 | 8a6f3bfdce | |
mibi88 | d6e8b096b5 | |
Lephe | 4df3d69d8c | |
Lephe | 74438f5da5 | |
Lephe | 45fa6c87c2 | |
Lephe | 5e35871c98 | |
Lephe | 5f9553f3b8 | |
mibi88 | 93e055cfba | |
Lephe | d110ab608e | |
mibi88 | 45e90b55ba | |
Lephe | bbda77769d | |
Lephe | 7b662987f8 | |
Lephe | 252bd4eb41 | |
Lephenixnoir | 48db9bf09d | |
calamari | 1d5675c99f | |
Lephe | 16890ab71b | |
Lephe | c9df6326e4 | |
Lephenixnoir | 97701e8eed | |
calamari | a735e074d8 | |
calamari | ddf340cccd | |
calamari | 5c331e5fa0 | |
calamari | 98fea4f2ec | |
calamari | a4adc5e1bd | |
calamari | b23ed9629e | |
Lephe | 2b4075c8f9 | |
Lephe | e95a91d5ab | |
Lephe | 3e5c45c5ad | |
Lephe | 9924dc4684 |
|
@ -9,6 +9,7 @@ include(Fxconv)
|
|||
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)
|
||||
|
||||
|
@ -50,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
|
||||
|
@ -64,6 +66,7 @@ set(SOURCES_COMMON
|
|||
src/fs/fugue/fugue_open.c
|
||||
src/fs/fugue/fugue_mkdir.c
|
||||
src/fs/fugue/fugue_stat.c
|
||||
src/fs/fugue/fugue_rename.c
|
||||
src/fs/fugue/fugue_rmdir.c
|
||||
src/fs/fugue/fugue_unlink.c
|
||||
src/fs/fugue/util.c
|
||||
|
@ -107,7 +110,9 @@ set(SOURCES_COMMON
|
|||
src/render/dtext.c
|
||||
src/render/dupdate_hook.c
|
||||
src/render/dvline.c
|
||||
src/render/dwindow.c
|
||||
src/render/topti.c
|
||||
src/render/dellipse.c
|
||||
# RTC driver
|
||||
src/rtc/rtc.c
|
||||
src/rtc/rtc_ticks.c
|
||||
|
@ -119,17 +124,21 @@ set(SOURCES_COMMON
|
|||
src/tmu/sleep.c
|
||||
src/tmu/tmu.c
|
||||
# USB driver
|
||||
src/usb/asyncio.c
|
||||
src/usb/classes/ff-bulk.c
|
||||
src/usb/configure.c
|
||||
src/usb/pipes.c
|
||||
src/usb/read4.S
|
||||
src/usb/setup.c
|
||||
src/usb/string.c
|
||||
src/usb/usb.c
|
||||
src/usb/write4.S
|
||||
)
|
||||
set(SOURCES_FX
|
||||
# Gray engine
|
||||
src/gray/engine.c
|
||||
src/gray/gclear.c
|
||||
src/gray/ggetpixel.c
|
||||
src/gray/gint_gline.c
|
||||
src/gray/gpixel.c
|
||||
src/gray/grect.c
|
||||
|
@ -142,6 +151,7 @@ set(SOURCES_FX
|
|||
src/render-fx/bopti-asm.s
|
||||
src/render-fx/bopti.c
|
||||
src/render-fx/dclear.c
|
||||
src/render-fx/dgetpixel.c
|
||||
src/render-fx/dpixel.c
|
||||
src/render-fx/drect.c
|
||||
src/render-fx/dsubimage.c
|
||||
|
@ -191,6 +201,7 @@ set(SOURCES_CG
|
|||
src/image/image_vflip_alloc.c
|
||||
# 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
|
||||
|
@ -232,8 +243,47 @@ set(SOURCES_CG
|
|||
src/render-cg/image/image_p4_dye.c
|
||||
)
|
||||
|
||||
set(ASSETS_FX src/font5x7.png)
|
||||
set(ASSETS_CG src/font8x9.png)
|
||||
set(SOURCES_FXASCG
|
||||
# Gray engine
|
||||
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
|
||||
# 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/gint_dline.c
|
||||
src/render-fx/masks.c
|
||||
src/render-fx/topti-asm.s
|
||||
src/render-fx/topti.c
|
||||
#special dupdate functions to treat a FX VRAM and convert to a CG VRAM
|
||||
src/render-fxascg/dupdate.c
|
||||
src/render-fxascg/engine_fxascg.c
|
||||
# R61524 driver as we are working with a CG50
|
||||
src/r61524/r61524.c
|
||||
|
||||
src/usb/classes/ff-bulk-gray.c
|
||||
)
|
||||
|
||||
set(ASSETS_FX
|
||||
src/font5x7.png
|
||||
)
|
||||
|
||||
set(ASSETS_CG
|
||||
src/font8x9.png
|
||||
)
|
||||
|
||||
fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG})
|
||||
|
||||
include_directories(
|
||||
|
@ -243,25 +293,52 @@ add_compile_options(-Wall -Wextra -std=c11 -Os -fstrict-volatile-bitfields -mtas
|
|||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
add_compile_definitions(FX9860G)
|
||||
add_library(gint-fx STATIC ${SOURCES_COMMON} ${SOURCES_FX} ${ASSETS_FX})
|
||||
set(NAME "gint-fx")
|
||||
set(LINKER_SCRIPT "fx9860g.ld")
|
||||
set(LINKER_SCRIPTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fx9860g.ld")
|
||||
add_library(gint-fx STATIC ${SOURCES_COMMON} ${SOURCES_FX} ${ASSETS_FX}
|
||||
${LINKER_SCRIPTS})
|
||||
endif()
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
|
||||
add_compile_definitions(FXCG50)
|
||||
add_library(gint-cg STATIC ${SOURCES_COMMON} ${SOURCES_CG} ${ASSETS_CG})
|
||||
set(NAME "gint-cg")
|
||||
set(LINKER_SCRIPT "fxcg50.ld")
|
||||
set(LINKER_SCRIPTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50.ld"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50_fastload.ld")
|
||||
add_library(gint-cg STATIC ${SOURCES_COMMON} ${SOURCES_CG} ${ASSETS_CG}
|
||||
${LINKER_SCRIPTS})
|
||||
endif()
|
||||
|
||||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_AS_CG)
|
||||
add_compile_options(-DFXCG50)
|
||||
add_compile_definitions(FX9860G_AS_CG)
|
||||
set(NAME "gint-fxascg")
|
||||
set(LINKER_SCRIPTS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50.ld"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50_fastload.ld")
|
||||
add_library(gint-fxascg STATIC ${SOURCES_COMMON} ${SOURCES_FXASCG} ${ASSETS_FX}
|
||||
${LINKER_SCRIPTS})
|
||||
endif()
|
||||
|
||||
set_target_properties("${NAME}" PROPERTIES OUTPUT_NAME "${NAME}")
|
||||
|
||||
# Generate linker scripts
|
||||
macro(generate_linker_script OUTPUT INPUT OPTIONS)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT}"
|
||||
COMMAND ${FXSDK_TOOLCHAIN}cpp "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}"
|
||||
-P -C -traditional-cpp ${OPTIONS} -o "${OUTPUT}"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}")
|
||||
endmacro()
|
||||
generate_linker_script("fx9860g.ld" "fx9860g.ld.c" "")
|
||||
generate_linker_script("fxcg50.ld" "fxcg50.ld.c" "")
|
||||
generate_linker_script("fxcg50_fastload.ld" "fxcg50.ld.c" "-DFXCG50_FASTLOAD")
|
||||
|
||||
# Library file
|
||||
install(TARGETS "${NAME}" DESTINATION "${FXSDK_LIB}")
|
||||
# Linker script
|
||||
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_SCRIPT}"
|
||||
DESTINATION "${FXSDK_LIB}")
|
||||
# Linker scripts
|
||||
install(FILES ${LINKER_SCRIPTS} DESTINATION "${FXSDK_LIB}")
|
||||
# Headers
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
|
||||
DESTINATION "${FXSDK_INCLUDE}"
|
||||
|
|
22
README.md
22
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
|
||||
|
||||
|
@ -105,6 +105,22 @@ The available options are:
|
|||
% fxsdk build-cg install
|
||||
```
|
||||
|
||||
**"Cross-Building" a fx-9860G II project for fx-CG 50**
|
||||
|
||||
fx-9860G II sources can be automatically converted to run on fx-CG 50, provided no
|
||||
low level function/specific hardware manipulation/or syscall is used by the program.
|
||||
|
||||
The command is very similar to fx-9860G II one, except it is `fxsdk build-fx-as-cg`
|
||||
instead of `fxsdk build-fx`.
|
||||
|
||||
The available options are:
|
||||
|
||||
```
|
||||
% fxsdk build-fx-as-cg
|
||||
% fxsdk build-fx-as-cg install
|
||||
```
|
||||
|
||||
|
||||
## Using in CMake-based add-ins
|
||||
|
||||
Find the `Gint` module and link against `Gint::Gint`. gint declares the include
|
||||
|
@ -125,6 +141,10 @@ using the fxSDK, you will need to:
|
|||
* 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.
|
||||
|
||||
Manually building a fx-9860G project to run on a fx-CG50 can be done :
|
||||
* Build with `-m4-nofpu -mb -DFXCG50 -DFX9860G_AS_CG`;
|
||||
* Link with `-T fxcg50.ld -lgint-fxascg -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
|
||||
|
|
8
TODO
8
TODO
|
@ -1,3 +1,6 @@
|
|||
Bugs to fix:
|
||||
* render: figure out why fx-CG dclear() now takes 4.1 ms instead of 2.6 ms
|
||||
|
||||
Extensions on existing code:
|
||||
* clock: mono support
|
||||
* usb: add PC->calc reading, and interrupt pipes
|
||||
|
@ -9,11 +12,12 @@ Extensions on existing code:
|
|||
* kernel: better restore to userspace before panic (ensure BL=0 IMASK=0)
|
||||
* project: add license file
|
||||
* kernel: group linker script symbols in a single header file
|
||||
* core: try to leave add-in without reset in case of panic
|
||||
* core: use cmp/str for memchr()
|
||||
* r61524: brightness control and clean the file
|
||||
* core: review forgotten globals and MPU addresses not in <gint/mpu/*.h>
|
||||
* core: run destructors when a task-switch results in leaving the app
|
||||
* fs: support read-only files backed with GetBlockAddress() on fx-CG
|
||||
* kernel: SH4- or G-III-specific linker scripts?
|
||||
* keysc: global shortcut SHIFT+0+EXIT for abort() as an infinite loop break
|
||||
|
||||
Future directions:
|
||||
* Audio playback using TSWilliamson's libsnd method
|
||||
|
|
|
@ -2,11 +2,15 @@
|
|||
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
|
||||
set(PC cg)
|
||||
set(INTF_DEFN FXCG50)
|
||||
set(INTF_LINK "-T;fxcg50.ld")
|
||||
set(INTF_LINK "-T;$<IF:$<CONFIG:FastLoad>,fxcg50_fastload.ld,fxcg50.ld>")
|
||||
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
|
||||
set(PC fx)
|
||||
set(INTF_DEFN FX9860G)
|
||||
set(INTF_LINK "-T;fx9860g.ld")
|
||||
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_AS_CG)
|
||||
set(PC fxascg)
|
||||
set(INTF_DEFN FX9860G_AS_CG)
|
||||
set(INTF_LINK "-T;fxcg50.ld")
|
||||
else()
|
||||
message(FATAL_ERROR "gint: unknown fxSDK platform '${FXSDK_PLATFORM}'")
|
||||
endif()
|
||||
|
|
|
@ -109,6 +109,7 @@ SECTIONS
|
|||
*(.rodata.4)
|
||||
|
||||
*(.rodata .rodata.*)
|
||||
*(.gint.rodata.sh3)
|
||||
} > rom
|
||||
|
||||
|
||||
|
@ -225,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 */
|
||||
|
@ -31,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);
|
||||
|
@ -175,11 +181,7 @@ SECTIONS
|
|||
|
||||
/DISCARD/ : {
|
||||
/* SH3-only data sections */
|
||||
*(.gint.data.sh3 .gint.bss.sh3)
|
||||
/* Debug sections (often from libgcc) */
|
||||
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
|
||||
.debug_ranges .debug_line .debug_str .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 */
|
|
@ -5,14 +5,17 @@
|
|||
configure:
|
||||
@ fxsdk build-fx -c $(GINT_CMAKE_OPTIONS)
|
||||
@ fxsdk build-cg -c $(GINT_CMAKE_OPTIONS)
|
||||
@ fxsdk build-fx-as-cg -c $(GINT_CMAKE_OPTIONS)
|
||||
|
||||
build:
|
||||
@ fxsdk build-fx
|
||||
@ fxsdk build-cg
|
||||
@ fxsdk build-fx-as-cg
|
||||
|
||||
install:
|
||||
@ fxsdk build-fx install
|
||||
@ fxsdk build-cg install
|
||||
@ fxsdk build-fx-as-cg 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-fx-as-cg/install_manifest.txt ]; then \
|
||||
xargs rm -f < build-fxascg/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
|
||||
|
||||
|
|
|
@ -68,28 +68,40 @@ enum {
|
|||
/* Combinations of hardware settings that are none of Ftune's levels */
|
||||
CLOCK_SPEED_UNKNOWN = 0,
|
||||
|
||||
/* Ftune's 5 default overclock levels. The main settings are listed below,
|
||||
thoug many more are involved.
|
||||
|
||||
On SH4 fx-9860G-likr:
|
||||
(Not supported yet)
|
||||
|
||||
On the fx G-III series:
|
||||
(Not supported yet)
|
||||
/* 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]
|
||||
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] */
|
||||
|
||||
On fx-CG 50:
|
||||
F1: CPU @ 116 MHz, BFC @ 58 MHz [Default speed]
|
||||
F2: CPU @ 58 MHz, BFC @ 29 MHz [Clearly slower: F2 < F3 < F1]
|
||||
F3: CPU @ 94 MHz, BFC @ 47 MHz [Clearly slower: F2 < F3 < F1]
|
||||
F4: CPU @ 232 MHz, BFC @ 58 MHz [Fastest CPU option]
|
||||
F5: CPU @ 189 MHz, BFC @ 94 MHz [Fastest bus option] */
|
||||
CLOCK_SPEED_F1 = 1,
|
||||
CLOCK_SPEED_F2 = 2,
|
||||
CLOCK_SPEED_F3 = 3,
|
||||
|
@ -100,7 +112,6 @@ enum {
|
|||
CLOCK_SPEED_DEFAULT = CLOCK_SPEED_F1,
|
||||
};
|
||||
|
||||
#ifdef FXCG50
|
||||
/* clock_get_speed(): Determine the current clock speed
|
||||
|
||||
This function compares the current hardware state with the settings for each
|
||||
|
@ -123,7 +134,26 @@ int clock_get_speed(void);
|
|||
Currently the clock speed is not reset during a world switch nor when
|
||||
leaving the add-in. */
|
||||
void clock_set_speed(int speed);
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
|
@ -143,27 +173,6 @@ void sleep_us_spin(uint64_t delay_us);
|
|||
/* sleep_ms(): Sleep for a fixed duration in milliseconds */
|
||||
#define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull)
|
||||
|
||||
//---
|
||||
// Low-level overclock functions
|
||||
//
|
||||
// These low-level functions directly read or write registers involved in
|
||||
// setting the overclock level. Don't use them directly unless you understand
|
||||
// how their interactions with the environment; instead, use clock_set_speed().
|
||||
//---
|
||||
|
||||
struct cpg_overclock_setting
|
||||
{
|
||||
uint32_t FLLFRQ, FRQCR;
|
||||
uint32_t CS0BCR, CS2BCR, CS3BCR, CS5aBCR;
|
||||
uint32_t CS0WCR, CS2WCR, CS3WCR, CS5aWCR;
|
||||
};
|
||||
|
||||
/* Queries the clock setting from the hardware. */
|
||||
void cpg_get_overclock_setting(struct cpg_overclock_setting *s);
|
||||
|
||||
/* Applies the specified overclock setting. */
|
||||
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -38,4 +38,7 @@
|
|||
enabled or disabled at runtime. */
|
||||
#cmakedefine GINT_KMALLOC_DEBUG
|
||||
|
||||
/* GINT_USB_DEBUG: Selects whether USB debug functions are enabled */
|
||||
#cmakedefine GINT_USB_DEBUG
|
||||
|
||||
#endif /* GINT_CONFIG */
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
/* 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 */
|
||||
|
|
|
@ -153,7 +153,8 @@ static GINLINE int gint_call(gint_call_t cb)
|
|||
int (*f)(int r4, int r5, int r6, int r7) = cb.function;
|
||||
#endif
|
||||
|
||||
return f(cb.args[0].i, cb.args[1].i, cb.args[2].i, cb.args[3].i);
|
||||
gint_call_arg_t *args = cb.args;
|
||||
return f ? f(args[0].i, args[1].i, args[2].i, args[3].i) : -1;
|
||||
}
|
||||
|
||||
//---
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
//---
|
||||
// gint:defs:timeout - RTC-based timeouts
|
||||
//
|
||||
// This header provides an interface for simplistic timers used for timeout
|
||||
// waiting. Currently they are based on the RTC with a resolution of 1/128 s.
|
||||
//---
|
||||
|
||||
#ifndef GINT_DEFS_TIMEOUT
|
||||
#define GINT_DEFS_TIMEOUT
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Object holding information about a timeout (specifically, when it expires).
|
||||
TODO: timeout: consider using a struct timespec with clock_gettime()? */
|
||||
typedef clock_t timeout_t;
|
||||
|
||||
static inline timeout_t timeout_make_ms(int ms)
|
||||
{
|
||||
return clock() + (int64_t)ms * CLOCKS_PER_SEC / 1000;
|
||||
}
|
||||
|
||||
static inline bool timeout_elapsed(timeout_t const *t)
|
||||
{
|
||||
return t && clock() >= *t;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_DEFS_TIMEOUT */
|
|
@ -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) ({ \
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
#ifndef GINT_DISPLAY_CG
|
||||
#define GINT_DISPLAY_CG
|
||||
|
||||
#ifdef FXCG50
|
||||
#if defined(FXCG50) && !defined(FX9860G_AS_CG)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -70,31 +70,27 @@ typedef image_t 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 */
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#ifndef GINT_DISPLAY_FX
|
||||
#define GINT_DISPLAY_FX
|
||||
|
||||
#ifdef FX9860G
|
||||
#if defined(FX9860G) || defined(FX9860G_AS_CG)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
@ -19,16 +19,14 @@ extern "C" {
|
|||
/* Platform-specific functions include VRAM management and the definition of
|
||||
the color_t type. */
|
||||
|
||||
#ifdef FX9860G
|
||||
#if defined(FX9860G) || defined(FX9860G_AS_CG)
|
||||
#include <gint/display-fx.h>
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
#if defined(FXCG50) && ! defined(FX9860G_AS_CG)
|
||||
#include <gint/display-cg.h>
|
||||
#endif
|
||||
|
||||
/* TODO: dinfo() or similar */
|
||||
|
||||
//---
|
||||
// Video RAM management
|
||||
//---
|
||||
|
@ -82,6 +80,31 @@ void dupdate_set_hook(gint_call_t function);
|
|||
/* dupdate_get_hook(): Get a copy of the dupdate() hook */
|
||||
gint_call_t dupdate_get_hook(void);
|
||||
|
||||
//---
|
||||
// Rendering mode control
|
||||
//---
|
||||
|
||||
/* dmode: Rendering mode settings
|
||||
|
||||
This structure indicates the current window settings. Rendering is limited
|
||||
to the rectangle spanning from (left,top) included, to (right,bottom)
|
||||
excluded. The default is from (0,0) to (DWIDTH,DHEIGHT). */
|
||||
struct dwindow {
|
||||
int left, top;
|
||||
int right, bottom;
|
||||
};
|
||||
extern struct dwindow dwindow;
|
||||
|
||||
/* dwindow_set(): Set the rendering window
|
||||
|
||||
This function changes [dwindow] settings to limit rendering to a sub-surface
|
||||
of the VRAM. Note that this doesn't change the coordinate system; the pixel
|
||||
at (DWIDTH/2, DHEIGHT/2) is always in the middle of the screen regardless of
|
||||
the window setting. However, it might be masked out by the window.
|
||||
|
||||
Returns the old dwindow settings (if it needs to be restored later). */
|
||||
struct dwindow dwindow_set(struct dwindow new_mode);
|
||||
|
||||
//---
|
||||
// Area rendering functions
|
||||
//---
|
||||
|
@ -129,6 +152,19 @@ void drect(int x1, int y1, int x2, int y2, int color);
|
|||
void drect_border(int x1, int y1, int x2, int y2,
|
||||
int fill_color, int border_width, int border_color);
|
||||
|
||||
|
||||
|
||||
void dellipse(int xm, int ym, int a, int b, int c);
|
||||
|
||||
void dellipserect(int x0, int y0, int x1, int y1, int c);
|
||||
|
||||
void dcircle(int xm, int ym, int r, int c);
|
||||
|
||||
void dellipse_fill(int xm, int ym, int a, int b, int cback, int cborder);
|
||||
|
||||
void dcircle_fill(int xm, int ym, int r, int cback, int cborder);
|
||||
|
||||
|
||||
//---
|
||||
// Point drawing functions
|
||||
//---
|
||||
|
@ -151,6 +187,11 @@ void drect_border(int x1, int y1, int x2, int y2,
|
|||
*: When the gray engine is on, see dgray(). */
|
||||
void dpixel(int x, int y, int color);
|
||||
|
||||
/* dgetpixel(): Get a pixel's color
|
||||
Returns the current color of any pixel in the VRAM. This function ignores
|
||||
the rendering window. Returns -1 if (x,y) is out of bounds. */
|
||||
int dgetpixel(int x, int y);
|
||||
|
||||
/* dline(): Render a straight line
|
||||
|
||||
This function draws a line using a Bresenham-style algorithm. Please note
|
||||
|
|
|
@ -0,0 +1,293 @@
|
|||
//---
|
||||
// gint:usb:asyncio - Asynchronous I/O common definitions
|
||||
//---
|
||||
|
||||
#ifndef GINT_USB_ASYNCIO
|
||||
#define GINT_USB_ASYNCIO
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
|
||||
/* Data tracking the progress of a multi-part multi-round async I/O operation.
|
||||
|
||||
* Writes are multi-part because they are constructed over several calls to
|
||||
write(2) followed by a "commit" with fynsc(2). They are additionally
|
||||
multi-round because each call to write(2) requires mutiple rounds of
|
||||
hardware communication when the hardware buffer is smaller than the data.
|
||||
|
||||
* Reads are multi-part because each transaction from the host requires
|
||||
several calls to read(2) ("user segments") if the user's buffer is shorter
|
||||
than the full transaction. In addition, reads are multi-round because a
|
||||
single read(2) to a user buffer takes multiple rounds of hardware
|
||||
communication ("hardware segments") whenever the user buffer is larger
|
||||
than the hardware buffer.
|
||||
|
||||
The process of performing such an I/O operation, as tracked by this
|
||||
structure and use throughout gint, is as follows.
|
||||
|
||||
## Writes
|
||||
|
||||
WRITING ---------------------.
|
||||
^ | | HW buffer
|
||||
Start writing | | Not full | full: start
|
||||
| | | transmission
|
||||
write(2) | v v
|
||||
--> IDLE ------------------> PENDING <------------- FLYING-WRITE
|
||||
^ ^ | DONE interrupt
|
||||
| DONE write(2) | |
|
||||
| interrupt | |
|
||||
| | | Data exhausted
|
||||
| fsync(2): start | v
|
||||
FLYING-SYNC <------------ IN-PROGRESS
|
||||
transmission
|
||||
|
||||
Initially the operation is in the IDLE state. When a write(2) is issued, it
|
||||
interacts with hardware then transitions to the IN-PROGRESS state, where it
|
||||
remains for any subsequent write(2). A fsync(2) will properly commit data to
|
||||
the hardware, finish the operation and return to the IDLE state.
|
||||
|
||||
The FLYING-WRITE and FLYING-SYNC states refer to waiting periods, after
|
||||
issuing hardware commands, during which hardware communicates. Usually an
|
||||
interrupt signals when hardware is ready to resume work.
|
||||
|
||||
Note that in a series of write(2), hardware is only instructed to send data
|
||||
once the hardware buffer is full. Therefore, a write(2) might transition
|
||||
directly from IDLE or IN-PROGRESS, to PENDING, to IN-PROGRESS, without
|
||||
actually communicating with the outside world.
|
||||
|
||||
An asynchronous write(2) might return to the caller as soon as writing is
|
||||
finished even if the operation is left in the FLYING-WRITE state, and it may
|
||||
even return while the operation is in the WRITING state if the DMA is used.
|
||||
|
||||
The invariants and meaning for each state are as follow:
|
||||
|
||||
State(s) Characterization Description
|
||||
============================================================================
|
||||
IDLE type == ASYNCIO_NONE No I/O operation
|
||||
----------------------------------------------------------------------------
|
||||
PENDING data_w && round_size == 0 Ready to write pending data
|
||||
----------------------------------------------------------------------------
|
||||
WRITING, round_size > 0 CPU/DMA write to HW in progress
|
||||
FLYING-WRITE HW transmission in progress
|
||||
----------------------------------------------------------------------------
|
||||
IN-PROGRESS !data_w && type == WRITE Waiting for write(2) or fsync(2)
|
||||
----------------------------------------------------------------------------
|
||||
FLYING-SYNC type == ASYNCIO_SYNC HW commit in progress
|
||||
============================================================================
|
||||
|
||||
The series of asyncio_op function calls for a write is as follows:
|
||||
|
||||
transaction ::= write* fsync
|
||||
write ::= asyncio_op_start_write round+ asyncio_op_finish_write
|
||||
round ::= asyncio_op_start_write_round asyncio_op_finish_write_round
|
||||
fsync ::= asyncio_op_start_sync asyncio_op_finish_sync
|
||||
|
||||
Each write(2) (with a single user-provided buffer) is split into multiple
|
||||
rounds that each fill the (small) hardware buffer. More writes can follow
|
||||
until an fynsc(2) commits the pipe.
|
||||
|
||||
## Reads
|
||||
|
||||
IN interrupt
|
||||
--> IDLE-EMPTY --------------> IDLE-READY
|
||||
| \ read(2) | ^
|
||||
read(2) | \ Transaction | | User buffer
|
||||
| \ exhausted* | | filled
|
||||
| '----<----------. | |
|
||||
| \ | |
|
||||
| IN interrupt \ | |
|
||||
v .--------->--------. \ v | .---. Read from
|
||||
WAITING READING v hardware
|
||||
'---------<--------' '---'
|
||||
HW buffer exhausted with
|
||||
user buffer not full
|
||||
|
||||
On this diagram, the right side indicates the presence of data to read from
|
||||
hardware while the bottom side indicates a read(2) request by the user.
|
||||
Notice the diagonal arrow back to IDLE-EMPTY insteaf of WAITING, which
|
||||
highlights that read(2) will always return at the end of a transaction even
|
||||
if the user-provided buffer is not full (to avoid waiting).
|
||||
|
||||
A read(2) request (a "user segment") might consume several full hardware
|
||||
buffers ("hardware segments") if the user buffer is large, thus looping
|
||||
repeatedly between WAITING and READING. Conversely, each hardware segment
|
||||
might fulfill many read(2) requests if the user buffer is small, thus
|
||||
looping between IDLE-READY and READING.
|
||||
|
||||
* Note that if the transaction finishes right as the user buffer fills up,
|
||||
we return to IDLE-READY and the next call to read(2) will successfully read
|
||||
0 bytes and transition back to IDLE-EMPTY. This allows the user to read the
|
||||
entire transaction by reading data until they get fewer bytes than requested
|
||||
without running the risk of blocking on the next transaction.
|
||||
|
||||
The invariants and meaning for each state are as follow. The right side
|
||||
(presence of a hardware segment) is indicated by `buffer_used >= 0`, while
|
||||
the bottom side (presence of a read(2) request) is indicated by `size > 0`.
|
||||
As an exception, IDLE-EMPTY uses `buffer_used == 0` so that the default
|
||||
zero-initialization of transfer data is sufficient.
|
||||
|
||||
State Invariant characterization Description
|
||||
============================================================================
|
||||
IDLE-EMPTY type == ASYNCIO_NONE No I/O operation, the pipe is
|
||||
&& buffer_used == 0 idle with no request and no
|
||||
&& size == 0 hardware segment (but we might
|
||||
&& round_size == 0 still be mid-transaction)
|
||||
----------------------------------------------------------------------------
|
||||
IDLE-READY type == ASYNCIO_READ There is a hardware segment not
|
||||
&& buffer_used >= 0 marked as complete, but no
|
||||
&& size == 0 read(2) request to consume it
|
||||
&& round_size == 0
|
||||
----------------------------------------------------------------------------
|
||||
WAITING type == ASYNCIO_READ There is a read(2) request but
|
||||
&& buffer_used < 0 no hardware segment, and either
|
||||
&& size > 0 the request is new or the
|
||||
&& round_size == 0 transaction isn't exhausted
|
||||
----------------------------------------------------------------------------
|
||||
READING type == ASYNCIO_READ A read round in progress and
|
||||
&& buffer_used >= 0 either the read(2) request or
|
||||
&& size > 0 hardware segment will be
|
||||
&& round_size > 0 exhausted when it ends
|
||||
============================================================================
|
||||
|
||||
The series of asyncio_op function calls for a read is a bit more complicated
|
||||
because transactions are divided into two non-comparable sequences of
|
||||
segments: one for packets received by the hardware buffer (on BRDY), one for
|
||||
the data being copied to user buffers (on read(2)).
|
||||
|
||||
|<------ Transaction from the host (no size limit) ------>|
|
||||
|
||||
v BRDY v BRDY v BRDY v BRDY v BRDY
|
||||
+-----------+-----------+-----------+-----------+---------+
|
||||
| HW buffer | HW buffer | HW buffer | HW buffer | (short) | HW segments
|
||||
+-----------+------+----+-----------+-----------+---------+
|
||||
| R1 | R2 | R3 | R4 | R5 | R6 | Read rounds
|
||||
+-----------+------+----+-----------+-----------+---------+
|
||||
| User buffer #1 | User buffer #2 | (short) | User segments
|
||||
+------------------+----------------------------+---------+
|
||||
^ read(2) ^ read(2) ^ read(2)
|
||||
|
||||
Reads rounds are exactly the intersections between hardware segments and
|
||||
read(2) user segments.
|
||||
|
||||
States can be checked and transitioned with the API functions below. */
|
||||
|
||||
enum { ASYNCIO_NONE, ASYNCIO_READ, ASYNCIO_WRITE, ASYNCIO_SYNC };
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* Type of I/O operation (NONE/WRITE/SYNC/READ) */
|
||||
uint8_t type;
|
||||
/* Whether the DMA should be used for hardware access */
|
||||
bool dma :1;
|
||||
/* For reading pipes, whether the transaction is expected to continue with
|
||||
another hardware segment after the current one */
|
||||
bool cont_r :1;
|
||||
/* For reading pipes, interrupt flag signaling an incoming hardware segment
|
||||
not yet added to the operation */
|
||||
bool interrupt_r :1;
|
||||
/* For reading pipes, whether the current read call should close the
|
||||
current hardware segment if all the data is read even if the read call
|
||||
is not partial */
|
||||
bool autoclose_r :1;
|
||||
/* Hardware resource being used for access (meaning depends on hardware).
|
||||
Usually, this is assigned for the duration of hardware transaction.
|
||||
This value is user-managed and not modified by asyncio_op functions. */
|
||||
uint8_t controller;
|
||||
|
||||
/* Number of bytes in short buffer (0..3) */
|
||||
uint8_t shbuf_size;
|
||||
/* Short buffer */
|
||||
uint32_t shbuf;
|
||||
|
||||
/* Size of data currently in the hardware buffer */
|
||||
int16_t buffer_used;
|
||||
/* Size of data being read/written in the current round (which may itself
|
||||
be asynchronous if it's using the DMA) */
|
||||
uint16_t round_size;
|
||||
|
||||
union {
|
||||
/* Address of data to transfer, incremented gradually [write] */
|
||||
void const *data_w;
|
||||
/* Address of buffer to store data to, incremented gradually [read] */
|
||||
void *data_r;
|
||||
};
|
||||
/* Size of data left to transfer to satisfy the complete request */
|
||||
int size;
|
||||
/* For reading operations, pointer to total amount of transferred data */
|
||||
int *realized_size_r;
|
||||
/* Callback at the end of the current write, final commit, or read */
|
||||
gint_call_t callback;
|
||||
|
||||
} asyncio_op_t;
|
||||
|
||||
//---
|
||||
// Initialization and query functions
|
||||
//---
|
||||
|
||||
/* asyncio_op_clear(): Initialize/clear the storage for an I/O operation */
|
||||
void asyncio_op_clear(asyncio_op_t *op);
|
||||
|
||||
/* asyncio_op_busy(): Check whether the transfer is busy for syscalls
|
||||
|
||||
This function checks whether the transfer is in a state where the CPU is
|
||||
busy wrt. starting a new syscall, ie. read(2), write(2) or fsync(2). Returns
|
||||
true if the CPU is busy and the call has to wait, false if the call can
|
||||
proceed immediately. */
|
||||
bool asyncio_op_busy(asyncio_op_t const *op);
|
||||
|
||||
//---
|
||||
// I/O functions
|
||||
//---
|
||||
|
||||
/* Start/finish a write(2) call. */
|
||||
void asyncio_op_start_write(asyncio_op_t *op,
|
||||
void const *data, size_t size, bool use_dma, gint_call_t const *callback);
|
||||
void asyncio_op_finish_write(asyncio_op_t *op);
|
||||
|
||||
/* Start/finish a single-block write to hardware. */
|
||||
void asyncio_op_start_write_round(asyncio_op_t *op, size_t size);
|
||||
void asyncio_op_finish_write_round(asyncio_op_t *op);
|
||||
|
||||
/* Start an fsync(2) operation (after one or more writes) and finish it. */
|
||||
void asyncio_op_start_sync(asyncio_op_t *op, gint_call_t const *callback);
|
||||
void asyncio_op_finish_sync(asyncio_op_t *op);
|
||||
|
||||
/* Start a read(2) call. The call will finish automatically when the final
|
||||
round finishes. If `autoclose` is set, the current hardware segment will
|
||||
be marked as completed if the round reads it entirely, even if the request
|
||||
is fulfilled. */
|
||||
void asyncio_op_start_read(asyncio_op_t *op, void *data, size_t size,
|
||||
bool use_dma, int *realized_size, bool autoclose,
|
||||
gint_call_t const *callback);
|
||||
|
||||
/* Start a hardware segment. `cont` should be true if there will be another
|
||||
segment in the same transaction. The segment will finish automatically when
|
||||
it is completely consumed by a read round. */
|
||||
void asyncio_op_start_read_hwseg(asyncio_op_t *op, size_t size, bool cont);
|
||||
|
||||
bool asyncio_op_has_read_call(asyncio_op_t const *op);
|
||||
|
||||
bool asyncio_op_has_read_hwseg(asyncio_op_t const *op);
|
||||
|
||||
/* Start a single-block read from hardware. The requested size is automatically
|
||||
t->size, however the round may of course be smaller depending on how much
|
||||
data is available. Returns the round size. */
|
||||
int asyncio_op_start_read_round(asyncio_op_t *op);
|
||||
|
||||
enum {
|
||||
ASYNCIO_HWSEG_EXHAUSTED = 0x01,
|
||||
ASYNCIO_REQUEST_FINISHED = 0x02,
|
||||
ASYNCIO_TRANSACTION_EXHAUSTED = 0x04,
|
||||
};
|
||||
|
||||
/* Finish a single-block read from hardware. This function also finishes the
|
||||
current hardware segment and read call if appropriate, *except* that it
|
||||
doesn't invoke the read(2) callback. You should make a copy of it before
|
||||
calling and invoke it manually after. Returns a combination of the above
|
||||
flags indicating what finished along with the round. */
|
||||
int asyncio_op_finish_read_round(asyncio_op_t *op);
|
||||
|
||||
/* Cancel a read call. This keeps the hardware segment part intact. */
|
||||
void asyncio_op_cancel_read(asyncio_op_t *op);
|
||||
|
||||
#endif /* GINT_USB_ASYNCIO */
|
|
@ -10,6 +10,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <gint/keyboard.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Size of the buffer event queue */
|
||||
#define KEYBOARD_QUEUE_SIZE 32
|
||||
|
@ -124,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. */
|
||||
keydev_repeat_profile_t repeater;
|
||||
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
|
||||
|
@ -135,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.
|
||||
|
||||
|
@ -159,6 +163,13 @@ typedef struct {
|
|||
/* 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/
|
||||
|
@ -173,7 +184,7 @@ typedef struct {
|
|||
|
||||
/* Candidate key for repeats (or 0 if no key is candidate yet) */
|
||||
int rep_key;
|
||||
/* Number of repeats alreay sent */
|
||||
/* Number of repeats already sent */
|
||||
int rep_count;
|
||||
/* Time since key was first pressed (us) */
|
||||
int rep_time;
|
||||
|
@ -181,7 +192,7 @@ typedef struct {
|
|||
int rep_delay;
|
||||
|
||||
/* Latest state of keys we are aware of. At every processing step, the
|
||||
different between this and the fresh information is queued and this
|
||||
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. */
|
||||
|
@ -193,8 +204,8 @@ typedef struct {
|
|||
/* Event queue (circular buffer) */
|
||||
key_event_t queue[KEYBOARD_QUEUE_SIZE];
|
||||
|
||||
/* Parameters for the standard repeat function */
|
||||
int rep_standard_first, rep_standard_next;
|
||||
/* Parameters for the standard repeat function */
|
||||
int rep_standard_first, rep_standard_next;
|
||||
|
||||
} keydev_t;
|
||||
|
||||
|
@ -250,6 +261,12 @@ key_event_t keydev_unqueue_event(keydev_t *d);
|
|||
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
|
||||
//---
|
||||
|
@ -284,8 +301,11 @@ void keydev_set_transform(keydev_t *d, keydev_transform_t tr);
|
|||
@next_us Delay between subsequent repeats (microseconds) */
|
||||
void keydev_set_standard_repeats(keydev_t *d, int first_us, int next_us);
|
||||
|
||||
/* keydev_read(): Retrieve the next transformed event */
|
||||
key_event_t keydev_read(keydev_t *d);
|
||||
/* 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
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/config.h>
|
||||
#include <gint/mpu/dma.h>
|
||||
#include <gint/clock.h>
|
||||
|
||||
|
@ -88,13 +89,23 @@ typedef struct {
|
|||
typedef struct {
|
||||
/* Control and power-up. We don't save power-related registers from other
|
||||
modules nor UPONCR, because they must be changed to use the module */
|
||||
uint16_t SYSCFG, DVSTCTR, TESTMODE, REG_C2;
|
||||
/* FIFO configuration */
|
||||
uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL;
|
||||
uint16_t SYSCFG, BUSWAIT, DVSTCTR, SOFCFG, TESTMODE, REG_C2;
|
||||
/* Interrupt configuration */
|
||||
uint16_t INTENB0, BRDYENB, NRDYENB, BEMPENB, SOFCFG;
|
||||
/* Default Control Pipe (maybe not needed) */
|
||||
uint16_t DCPCFG, DCPMAXP, DCPCTR;
|
||||
uint16_t INTENB0, INTENB1, BRDYENB, NRDYENB, BEMPENB;
|
||||
/* Default Control Pipe */
|
||||
uint16_t DCPMAXP;
|
||||
|
||||
#ifdef GINT_USB_DEBUG
|
||||
/* Registers tracked read-only for state analysis and debugging */
|
||||
uint16_t SYSSTS, FRMNUM, UFRMNUM;
|
||||
uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL, CFIFOCTR, D0FIFOCTR, D1FIFOCTR;
|
||||
uint16_t INTSTS0, INTSTS1, BRDYSTS, NRDYSTS, BEMPSTS;
|
||||
uint16_t DCPCFG, DCPCTR;
|
||||
uint16_t USBADDR, USBREQ, USBVAL, USBINDX, USBLENG;
|
||||
uint16_t PIPESEL, PIPECFG[9], PIPEnCTR[9], PIPEBUF[9];
|
||||
/* Ignored: UPONCR, PIPEnMAXP, PIPEnPERI, PIPEnTRN, PIPEnTRE, DEVADDn */
|
||||
#endif
|
||||
|
||||
} usb_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -13,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
|
||||
|
||||
|
|
|
@ -59,14 +59,23 @@ void gint_world_sync(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);
|
||||
|
@ -100,6 +109,26 @@ static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
|
|||
Returns the return value of the callback. */
|
||||
extern int (*gint_inth_callback)(gint_call_t const *call);
|
||||
|
||||
/* gint_set_quit_handler(): Setup a call to be invoked when leaving the add-in
|
||||
|
||||
This function sets up the provided GINT_CALL() to be invoked when the
|
||||
add-in is unloaded, which is either when we exit from main() or when
|
||||
starting another application from the main menu. Crucially, this is only
|
||||
*after* selecting an application, not before opening the main menu. The
|
||||
quit handler is not invoked if the user re-enters the add-in.
|
||||
|
||||
This is based on the SetQuitHandler() syscall, and therefore the callback
|
||||
runs in the OS world by default. If [run_in_os_world] is set to false, a
|
||||
world switch will be performed to run the callback in the gint world.
|
||||
|
||||
TODO: Currently the quit handler is not called when exiting from main().
|
||||
TODO: Detail how this interacts with destructor functions!
|
||||
TODO: [run_in_os_world == false] is not honored yet (because unstable)
|
||||
|
||||
@call Callback to be performed when leaving add-in
|
||||
@run_in_os_world true to stay in OS world, false to use gint world */
|
||||
void gint_set_quit_handler(gint_call_t gcall, bool run_in_os_world);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -45,6 +45,12 @@ void hw_detect(void);
|
|||
#define isSH4() (!isSH3())
|
||||
#endif
|
||||
|
||||
#if defined(FX9860G)
|
||||
#define isSlim() (gint[HWCALC] == HWCALC_FX9860G_SLIM)
|
||||
#else
|
||||
#define isSlim() 0
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
#define isSH3() 0
|
||||
#define isSH4() 1
|
||||
|
@ -105,6 +111,8 @@ void hw_detect(void);
|
|||
#define HWCALC_FXCG50 5
|
||||
/* fx-CG 50 emulator, hardcoded in kernel/inth.S */
|
||||
#define HWCALC_FXCG_MANAGER 6
|
||||
/* fx-9860G Slim, SH-3-based fx-9860G with hardware differences */
|
||||
#define HWCALC_FX9860G_SLIM 7
|
||||
|
||||
/*
|
||||
** Keyboard
|
||||
|
|
|
@ -41,6 +41,8 @@ extern "C" {
|
|||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
struct dwindow;
|
||||
|
||||
//---
|
||||
// Image structures
|
||||
//---
|
||||
|
@ -653,13 +655,14 @@ struct gint_image_box
|
|||
};
|
||||
|
||||
/* Clip the provided box against the input. If, after clipping, the box no
|
||||
longer intersects the output (whose size is specified as out_w/out_h),
|
||||
returns false. Otherwise, returns true. */
|
||||
longer intersects the output window, returns false. Otherwise, returns
|
||||
true. */
|
||||
bool gint_image_clip_input(image_t const *img, struct gint_image_box *box,
|
||||
int out_w, int out_h);
|
||||
struct dwindow const *window);
|
||||
|
||||
/* Clip the provided box against the output. */
|
||||
void gint_image_clip_output(struct gint_image_box *b, int out_w, int out_h);
|
||||
void gint_image_clip_output(struct gint_image_box *b,
|
||||
struct dwindow const *window);
|
||||
|
||||
//---
|
||||
// Internal image rendering routines
|
||||
|
@ -745,15 +748,14 @@ struct gint_image_cmd
|
|||
@left_edge Whether to force 2-alignment on the input (box->left)
|
||||
@right_edge Whether to force 2-alignment on the width
|
||||
@cmd Command to be filled
|
||||
@out_width Output width (usually DWIDTH)
|
||||
@out_height Output height (usually DHEIGHT)
|
||||
@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, int out_width, int out_height);
|
||||
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
|
||||
|
|
|
@ -207,7 +207,7 @@ enum {
|
|||
/* Enable modifiers keys */
|
||||
GETKEY_MOD_SHIFT = 0x01,
|
||||
GETKEY_MOD_ALPHA = 0x02,
|
||||
/* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
|
||||
/* SHIFT + OPTN (requires GETKEY_MOD_SHIFT) or LIGHT toggles backlight */
|
||||
GETKEY_BACKLIGHT = 0x04,
|
||||
/* MENU triggers a task switch and displays the main menu */
|
||||
GETKEY_MENU = 0x08,
|
||||
|
|
|
@ -75,6 +75,10 @@ enum {
|
|||
of the matrix one could use a ghosting effect to boot the calc. */
|
||||
KEY_ACON = 0x07,
|
||||
|
||||
/* Virtual key codes */
|
||||
KEY_HELP = 0x20, /* fx-9860G Slim: 0x75 */
|
||||
KEY_LIGHT = 0x10, /* fx-9860G Slim: 0x76 */
|
||||
|
||||
/* Key aliases (handle with care =D) */
|
||||
KEY_X2 = KEY_SQUARE,
|
||||
KEY_CARET = KEY_POWER,
|
||||
|
|
|
@ -39,6 +39,20 @@ void *krealloc(void *ptr, size_t size);
|
|||
/* kfree(): Free memory allocated with kalloc() */
|
||||
void kfree(void *ptr);
|
||||
|
||||
/* kmalloc_max(): Allocate the largest block available in an arena
|
||||
|
||||
This function is like kmalloc(), but it find the largest block in the arena
|
||||
and returns it whole after setting its size in *size. This is useful if you
|
||||
need memory to manage with another allocator, or you don't yet know the size
|
||||
of the data to be generated. The block can later be shrunk with realloc().
|
||||
|
||||
Currently only gint-managed arenas support this operation.
|
||||
|
||||
@size Will be set to size of returned block (if there is one)
|
||||
@arean_name Name of arena to allocate in (*cannot* be NULL)
|
||||
Returns the address of the largest block available, NULL on error. */
|
||||
void *kmalloc_max(size_t *size, char const *arena_name);
|
||||
|
||||
//---
|
||||
// Extension API for new areas and statistics
|
||||
//---
|
||||
|
@ -53,6 +67,8 @@ typedef struct {
|
|||
void * (*realloc)(void *ptr, size_t newsize, void *data);
|
||||
/* kfree() handles ptr == NULL*/
|
||||
void (*free)(void *ptr, void *data);
|
||||
/* kmalloc_max() backend */
|
||||
void * (*malloc_max)(size_t *size, void *data);
|
||||
|
||||
/* Name, should be unique; gint reserves names starting with "_" */
|
||||
char const *name;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -12,6 +12,80 @@ extern "C" {
|
|||
#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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -298,8 +298,8 @@ typedef struct
|
|||
//---
|
||||
|
||||
/* Provided by intc/intc.c */
|
||||
extern sh7705_intc_t SH7705_INTC;
|
||||
extern sh7305_intc_t SH7305_INTC;
|
||||
extern sh7705_intc_t const SH7705_INTC;
|
||||
extern sh7305_intc_t const SH7305_INTC;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -49,12 +49,10 @@ typedef struct
|
|||
} GPACKED(4) tlb_data_t;
|
||||
|
||||
//---
|
||||
// SH7305 TLB. Refer to:
|
||||
// "Renesas SH7724 User's Manual: Hardware"
|
||||
// Section 7: "Memory Management Unit (MMU)"
|
||||
// SH7305 TLB. Refer to SH4AL-DSP manual, section 7 (MMU)
|
||||
//---
|
||||
|
||||
/* utlb_addr_t - address part of a UTLB entry */
|
||||
/* utlb_addr_t: Address part of a UTLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint VPN :22;
|
||||
|
@ -64,7 +62,7 @@ typedef struct
|
|||
|
||||
} GPACKED(4) utlb_addr_t;
|
||||
|
||||
/* utlb_data_t - data part of a UTLB entry */
|
||||
/* utlb_data_t: Data part of a UTLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint :3;
|
||||
|
@ -81,6 +79,34 @@ typedef struct
|
|||
|
||||
} GPACKED(4) utlb_data_t;
|
||||
|
||||
/* itlb_addr_t: Address part of an ITLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint VPN :22;
|
||||
uint :1;
|
||||
uint V :1;
|
||||
uint ASID :8;
|
||||
|
||||
} GPACKED(4) itlb_addr_t;
|
||||
|
||||
/* itlb_data_t: Data part of an ITLB entry */
|
||||
typedef struct
|
||||
{
|
||||
uint :3;
|
||||
uint PPN :19;
|
||||
uint :1;
|
||||
uint V :1;
|
||||
uint SZ1 :1;
|
||||
uint PR :1;
|
||||
uint :1;
|
||||
uint SZ0 :1;
|
||||
uint C :1;
|
||||
uint :1;
|
||||
uint SH :1;
|
||||
uint :1;
|
||||
|
||||
} GPACKED(4) itlb_data_t;
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
lword_union(PTEH,
|
||||
|
|
|
@ -76,9 +76,162 @@ typedef volatile struct
|
|||
#define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100))
|
||||
|
||||
//---
|
||||
// TODO: Document the SH7305 Pin Function Controller
|
||||
// SH7305 Pin Function Controller.
|
||||
// This is somewhat reminiscent of the SH7724, but there are many differences.
|
||||
// The official emulator is the basis for this module.
|
||||
//---
|
||||
|
||||
/* All port control registers (PCRx) have 2-bit entries specifying 4 pin modes:
|
||||
00: Special functions
|
||||
01: Output
|
||||
10: Input (pull-up ON)
|
||||
11: Input (pull-up OFF)
|
||||
|
||||
Some pins do not accept all settings. The SH7724 has these exceptions:
|
||||
- PGCR (all pins): Input now allowed (00 and 01 only)
|
||||
- PJCR.P5MD/P6MD/P7MD: Input not allowed (00 and 01 only)
|
||||
|
||||
Also, some entries are not configurable or are not mapped to actual pins.
|
||||
Again the SH7724 has these exceptions:
|
||||
- PGCR.P6MD/P7MD: No setting
|
||||
- PJCR.P4MD: No setting
|
||||
- PSCR.P7MD: No setting */
|
||||
typedef volatile word_union(sh7305_pfc_control_t,
|
||||
uint16_t P7MD :2;
|
||||
uint16_t P6MD :2;
|
||||
uint16_t P5MD :2;
|
||||
uint16_t P4MD :2;
|
||||
uint16_t P3MD :2;
|
||||
uint16_t P2MD :2;
|
||||
uint16_t P1MD :2;
|
||||
uint16_t P0MD :2;
|
||||
);
|
||||
|
||||
/* Data register; a plain 8-bit value. Each bit can be either read or written
|
||||
depending on the mode of the corresponding pin. */
|
||||
typedef volatile byte_union(sh7305_pfc_data_t,
|
||||
uint8_t P7DT :1;
|
||||
uint8_t P6DT :1;
|
||||
uint8_t P5DT :1;
|
||||
uint8_t P4DT :1;
|
||||
uint8_t P3DT :1;
|
||||
uint8_t P2DT :1;
|
||||
uint8_t P1DT :1;
|
||||
uint8_t P0DT :1;
|
||||
);
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
sh7305_pfc_control_t
|
||||
PACR, PBCR, PCCR, PDCR, PECR, PFCR, PGCR, PHCR,
|
||||
PJCR, PKCR, PLCR, PMCR, PNCR, PQCR, PRCR, PSCR;
|
||||
|
||||
sh7305_pfc_data_t PADR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PBDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PCDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PDDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PEDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PFDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PGDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PHDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PJDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PKDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PLDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PMDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PNDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PQDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PRDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PSDR;
|
||||
pad(1);
|
||||
|
||||
sh7305_pfc_control_t PTCR;
|
||||
sh7305_pfc_control_t PUCR;
|
||||
sh7305_pfc_control_t PVCR;
|
||||
pad(6);
|
||||
sh7305_pfc_control_t PPCR;
|
||||
|
||||
/* PSEM*: Multiplexing pin settings. These are highly MPU-dependent and
|
||||
the assignment is basically unknown.
|
||||
HIZCR*: High-impedance settings for pins' I/O buffers. */
|
||||
uint16_t PSELA;
|
||||
uint16_t PSELB;
|
||||
uint16_t PSELC;
|
||||
uint16_t PSELD;
|
||||
uint16_t PSELE;
|
||||
uint16_t HIZCRA;
|
||||
uint16_t HIZCRB;
|
||||
uint16_t HIZCRC;
|
||||
uint16_t PSELF;
|
||||
|
||||
sh7305_pfc_data_t PTDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PUDR;
|
||||
pad(1);
|
||||
sh7305_pfc_data_t PVDR;
|
||||
pad(5);
|
||||
sh7305_pfc_data_t PPDR;
|
||||
pad(0x15);
|
||||
|
||||
/* Module function selection registers.
|
||||
WARNING: These are the SH7724 bits, not necessarily the SH7305! */
|
||||
word_union(MSELCRA,
|
||||
uint16_t :8;
|
||||
uint16_t UNKNOWN_USB :2;
|
||||
uint16_t :6;
|
||||
);
|
||||
word_union(MSELCRB,
|
||||
uint16_t XTAL_USB :2;
|
||||
uint16_t :6;
|
||||
uint16_t SCIF2_PORT :1;
|
||||
uint16_t :1;
|
||||
uint16_t SCIF3_PORT :1;
|
||||
uint16_t :3;
|
||||
uint16_t LDC_VSYNC_DIR :1;
|
||||
uint16_t :1;
|
||||
);
|
||||
|
||||
// TODO: Doc
|
||||
uint16_t DRVCRD, DRVCRA, DRVCRB, DRVCRC;
|
||||
pad(4);
|
||||
|
||||
/* Pull-up control registers. Not sure what these are since that's
|
||||
normally handled in the modes for P*CR? */
|
||||
uint8_t PULCRA, PULCRB, PULCRC, PULCRD, PULCRE, PULCRF, PULCRG,
|
||||
PULCRH, PULCRJ, PULCRK, PULCRL, PULCRM, PULCRN, PULCRQ,
|
||||
PULCRR, PULCRS;
|
||||
pad(0x20);
|
||||
uint8_t PULCRT;
|
||||
uint8_t PULCRU;
|
||||
uint8_t PULCRV;
|
||||
uint8_t PULCRBSC;
|
||||
pad(1);
|
||||
uint8_t PULCRTRST;
|
||||
uint8_t PULCRP;
|
||||
pad(1);
|
||||
|
||||
uint16_t PSELG;
|
||||
pad(12);
|
||||
uint16_t PSELH;
|
||||
|
||||
} sh7305_pfc_t;
|
||||
|
||||
#define SH7305_PFC (*((sh7305_pfc_t *)0xa4050100))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
//---
|
||||
// gint:mpu:scif - Serial Communication Interface with FIFO (SCIF)
|
||||
//---
|
||||
|
||||
#ifndef GINT_MPU_SCIF
|
||||
#define GINT_MPU_SCIF
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 SCIF (from: SH7705 manual, section 16)
|
||||
//---
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* Serial Mode Register */
|
||||
word_union(SCSMR,
|
||||
uint16_t :5;
|
||||
uint16_t SRC :3; /* Sampling Control */
|
||||
uint16_t CA :1; /* Communication Mode */
|
||||
uint16_t CHR :1; /* Character Length */
|
||||
uint16_t PE :1; /* Parity Enable */
|
||||
uint16_t OE :1; /* Parity Mode */
|
||||
uint16_t STOP :1; /* Stop Bit Length */
|
||||
uint16_t :1;
|
||||
uint16_t CKS :2; /* Clock Select */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Bit Rate Register */
|
||||
uint8_t SCBRR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial Control Register */
|
||||
word_union(SCSCR,
|
||||
uint16_t :4;
|
||||
uint16_t TSIE :1; /* Transmit Data Stop Interrupt Enable */
|
||||
uint16_t ERIE :1; /* Receive Error Interrupt Enable */
|
||||
uint16_t BRIE :1; /* Break Interrupt Enable */
|
||||
uint16_t DRIE :1; /* Receive Data Ready Interrupt Enable */
|
||||
uint16_t TIE :1; /* Transmit Interrupt Enable */
|
||||
uint16_t RIE :1; /* Receive Interrupt Enable */
|
||||
uint16_t TE :1; /* Transmit Enable */
|
||||
uint16_t RE :1; /* Receive Mode */
|
||||
uint16_t :2;
|
||||
uint16_t CKE :2; /* Clock Enable */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Transmit Data Stop Register */
|
||||
uint8_t SCTDSR;
|
||||
pad(0x3);
|
||||
|
||||
/* FIFO Error Count Register */
|
||||
word_union(SCFER,
|
||||
uint16_t :2;
|
||||
uint16_t PER :6; /* Parity Error Count */
|
||||
uint16_t :2;
|
||||
uint16_t FER :6; /* Framing Error Count */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial Status Register */
|
||||
word_union(SCSSR,
|
||||
uint16_t :6;
|
||||
uint16_t ORER :1; /* Overrun Error */
|
||||
uint16_t TSF :1; /* Transmit Data Stop */
|
||||
uint16_t ER :1; /* Receive Error */
|
||||
uint16_t TEND :1; /* Transmit End */
|
||||
uint16_t TDFE :1; /* Transmit FIFO Data Empty */
|
||||
uint16_t BRK :1; /* Break Detect */
|
||||
uint16_t FER :1; /* Framing Error */
|
||||
uint16_t PER :1; /* Parity Error */
|
||||
uint16_t RDF :1; /* Receive FIFO Data Full */
|
||||
uint16_t DR :1; /* Receive Data Ready */
|
||||
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* FIFO Control Register */
|
||||
word_union(SCFCR,
|
||||
uint16_t TSE :1; /* Transmit Data Stop Enable */
|
||||
uint16_t TCRST :1; /* Transmit Count Reset */
|
||||
uint16_t :3;
|
||||
uint16_t RSTRG :3; /* RTS Output Active Trigger */
|
||||
uint16_t RTRG :2; /* Receive FIFO Data Number Trigger */
|
||||
uint16_t TTRG :2; /* Transmit FIFO Data Number Trigger */
|
||||
uint16_t MCE :1; /* Modem Control Enable */
|
||||
uint16_t TFRST :1; /* Transmit FIFO Data Register Reset */
|
||||
uint16_t RFRST :1; /* Receive FIFO Data Register Reset */
|
||||
uint16_t LOOP :1; /* Loopback Test */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* FIFO Data Count Register */
|
||||
word_union(SCFDR,
|
||||
uint16_t :1;
|
||||
uint16_t T :7; /* Pending bytes in transmit FIFO */
|
||||
uint16_t :1;
|
||||
uint16_t R :7; /* Received bytes in receive FIFO */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial FIFO Transmit Data Register (64 bytes rolling register) */
|
||||
uint8_t SCFTDR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial FIFO Receive Data Register (64 bytes rolling register) */
|
||||
uint8_t SCFRDR;
|
||||
|
||||
} GPACKED(4) sh7705_scif_t;
|
||||
|
||||
#define SH7705_SCIF (*((sh7705_scif_t *)0xa4410000))
|
||||
|
||||
//---
|
||||
// SH7305 SCIF (from: SH7305 emulator and SH7730 SCIF)
|
||||
// The module is very close to the SH7724 but it has a couple extra bits in
|
||||
// SCFCR, which shows that it is closer to the SH7730 SCIF.
|
||||
//---
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* Serial Mode Register */
|
||||
word_union(SCSMR,
|
||||
uint16_t :8;
|
||||
uint16_t CA :1; /* Communication Mode */
|
||||
uint16_t CHR :1; /* Character Length */
|
||||
uint16_t PE :1; /* Parity Enable */
|
||||
uint16_t OE :1; /* Parity Mode */
|
||||
uint16_t STOP :1; /* Stop Bit Length */
|
||||
uint16_t :1;
|
||||
uint16_t CKS :2; /* Clock Select */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial Bit Rate Register */
|
||||
uint8_t SCBRR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial Control Register */
|
||||
word_union(SCSCR,
|
||||
uint16_t :8;
|
||||
uint16_t TIE :1; /* Transmit Interrupt Enable */
|
||||
uint16_t RIE :1; /* Receive Interrupt Enable */
|
||||
uint16_t TE :1; /* Transmit Enable */
|
||||
uint16_t RE :1; /* Receive Mode */
|
||||
uint16_t REIE :1; /* Receive Error Interrupt Enable */
|
||||
uint16_t :1;
|
||||
uint16_t CKE :2; /* Clock Enable */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial FIFO Transmit Data Register */
|
||||
uint8_t SCFTDR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial FIFO Status Register */
|
||||
word_union(SCFSR,
|
||||
uint16_t const PERC :4; /* Number of Parity Errors */
|
||||
uint16_t const FERC :4; /* Number of Framing Errors */
|
||||
uint16_t ER :1; /* Receive Error */
|
||||
uint16_t TEND :1; /* Transmit End */
|
||||
uint16_t TDFE :1; /* Transmit FIFO Data Empty */
|
||||
uint16_t BRK :1; /* Break Detection */
|
||||
uint16_t const FER :1; /* Framing Error */
|
||||
uint16_t const PER :1; /* Parity Error */
|
||||
uint16_t RDF :1; /* Receive FIFO Data Full */
|
||||
uint16_t DR :1; /* Data Ready */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial FIFO Receive Data Register */
|
||||
uint8_t SCFRDR;
|
||||
pad(0x3);
|
||||
|
||||
/* Serial FIFO Control Register */
|
||||
word_union(SCFCR,
|
||||
uint16_t :5;
|
||||
uint16_t RSTRG :3; /* RTS Output Active Trigger */
|
||||
uint16_t RTRG :2; /* Receive FIFO Data Trigger */
|
||||
uint16_t TTRG :2; /* Transmit FIFO Data Trigger */
|
||||
uint16_t MCE :1; /* Model Control Enable */
|
||||
uint16_t TFRST :1; /* Transmit FIFO Data Register Reset */
|
||||
uint16_t RFRST :1; /* Receive FIFO Data Register Reset */
|
||||
uint16_t LOOP :1; /* Loopback Test */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
/* Serial FIFO Data Count Register */
|
||||
word_union(SCFDR,
|
||||
uint16_t :3;
|
||||
uint16_t TFDC :5; /* Number of Data Bytes in Transmit FIFO */
|
||||
uint16_t :3;
|
||||
uint16_t RFDC :5; /* Number of Data Bytes in Receive FIFO */
|
||||
);
|
||||
pad(0x6);
|
||||
|
||||
/* Serial Line Status */
|
||||
word_union(SCLSR,
|
||||
uint16_t :15;
|
||||
uint16_t ORER :1; /* Overrun Error */
|
||||
);
|
||||
pad(0x2);
|
||||
|
||||
} GPACKED(4) sh7305_scif_t;
|
||||
|
||||
#define SH7305_SCIF (*((sh7305_scif_t *)0xa4410000))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_SCIF */
|
|
@ -11,6 +11,19 @@ extern "C" {
|
|||
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
word_union(TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
} sh7305_usb_pipetr;
|
||||
|
||||
typedef volatile struct
|
||||
{
|
||||
/* System Configuration Control Register */
|
||||
|
@ -28,12 +41,16 @@ typedef volatile struct
|
|||
|
||||
/* CPU Bus Wait Setting Register */
|
||||
word_union(BUSWAIT,
|
||||
uint16_t :12;
|
||||
uint16_t :8;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t :1;
|
||||
uint16_t _2 :1; /* Unknown role; can be set */
|
||||
uint16_t :1;
|
||||
uint16_t BWAIT :4; /* Bus Wait */
|
||||
);
|
||||
|
||||
/* System Configuration Status Register */
|
||||
word_union(SYSSTS,
|
||||
const word_union(SYSSTS,
|
||||
uint16_t :14;
|
||||
uint16_t LNST :2; /* Line Status */
|
||||
);
|
||||
|
@ -41,7 +58,9 @@ typedef volatile struct
|
|||
|
||||
/* Device State Control Register */
|
||||
word_union(DVSTCTR,
|
||||
uint16_t :7;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t :2;
|
||||
uint16_t _2 :4; /* Unknown role; can be set */
|
||||
uint16_t WKUP :1; /* Wakeup Output */
|
||||
uint16_t RWUPE :1; /* Wakeup Detection Enable */
|
||||
uint16_t USBRT :1; /* USB Reset Output */
|
||||
|
@ -92,8 +111,9 @@ typedef volatile struct
|
|||
uint16_t DREQE :1; /* DMA Transfert Request Enable */
|
||||
uint16_t MBW :2; /* Access Bits Width */
|
||||
uint16_t :1;
|
||||
uint16_t BIGEND :1; /* Endiant Control */
|
||||
uint16_t :4;
|
||||
uint16_t BIGEND :1; /* Endian Control */
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t :3;
|
||||
uint16_t CURPIPE:4; /* Port Access Pipe Specification */
|
||||
);
|
||||
word_union(D0FIFOCTR,
|
||||
|
@ -111,7 +131,8 @@ typedef volatile struct
|
|||
uint16_t MBW :2; /* Access Bits Width */
|
||||
uint16_t :1;
|
||||
uint16_t BIGEND :1; /* Endian Control */
|
||||
uint16_t :4;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t :3;
|
||||
uint16_t CURPIPE:4; /* Port Access Pipe Specification */
|
||||
);
|
||||
word_union(D1FIFOCTR,
|
||||
|
@ -134,50 +155,26 @@ typedef volatile struct
|
|||
uint16_t BRDYE :1; /* Buffer Ready */
|
||||
uint16_t :8;
|
||||
);
|
||||
pad(4);
|
||||
word_union(INTENB1,
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t BCHGE :1; /* Bus Change */
|
||||
uint16_t :1;
|
||||
uint16_t DTCHE :1; /* Disconnection Detection */
|
||||
uint16_t ATTCHE :1; /* Connection Detection */
|
||||
uint16_t :4;
|
||||
uint16_t EOFERRE:1; /* EOF Error Detection */
|
||||
uint16_t SIGNE :1; /* Setup Transaction Error */
|
||||
uint16_t SACKE :1; /* Setup Transaction Normal Response */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
/* BRDY Interrupt Enable Register */
|
||||
word_union(BRDYENB,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9BRDYE :1;
|
||||
uint16_t PIPE8BRDYE :1;
|
||||
uint16_t PIPE6BRDYE :1;
|
||||
uint16_t PIPE7BRDYE :1;
|
||||
uint16_t PIPE5BRDYE :1;
|
||||
uint16_t PIPE3BRDYE :1;
|
||||
uint16_t PIPE4BRDYE :1;
|
||||
uint16_t PIPE2BRDYE :1;
|
||||
uint16_t PIPE1BRDYE :1;
|
||||
uint16_t PIPE0BRDYE :1;
|
||||
);
|
||||
uint16_t BRDYENB;
|
||||
/* NRDY Interrupt Enable Register */
|
||||
word_union(NRDYENB,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9NRDYE :1;
|
||||
uint16_t PIPE8NRDYE :1;
|
||||
uint16_t PIPE6NRDYE :1;
|
||||
uint16_t PIPE7NRDYE :1;
|
||||
uint16_t PIPE5NRDYE :1;
|
||||
uint16_t PIPE3NRDYE :1;
|
||||
uint16_t PIPE4NRDYE :1;
|
||||
uint16_t PIPE2NRDYE :1;
|
||||
uint16_t PIPE1NRDYE :1;
|
||||
uint16_t PIPE0NRDYE :1;
|
||||
);
|
||||
uint16_t NRDYENB;
|
||||
/* BEMP Interrupt Enable Register */
|
||||
word_union(BEMPENB,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9BEMPE :1;
|
||||
uint16_t PIPE8BEMPE :1;
|
||||
uint16_t PIPE6BEMPE :1;
|
||||
uint16_t PIPE7BEMPE :1;
|
||||
uint16_t PIPE5BEMPE :1;
|
||||
uint16_t PIPE3BEMPE :1;
|
||||
uint16_t PIPE4BEMPE :1;
|
||||
uint16_t PIPE2BEMPE :1;
|
||||
uint16_t PIPE1BEMPE :1;
|
||||
uint16_t PIPE0BEMPE :1;
|
||||
);
|
||||
uint16_t BEMPENB;
|
||||
|
||||
/* SOF Control Register */
|
||||
word_union(SOFCFG,
|
||||
|
@ -186,7 +183,10 @@ typedef volatile struct
|
|||
uint16_t :1;
|
||||
uint16_t BRDYM :1; /* BRDY Status Clear Timing */
|
||||
uint16_t enable :1; /* SHOULD BE SET TO 1 MANUALLY */
|
||||
uint16_t :5;
|
||||
uint16_t :1;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t _2 :1; /* Unknown role; can be set */
|
||||
uint16_t :2;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
|
@ -205,56 +205,32 @@ typedef volatile struct
|
|||
uint16_t VALID :1; /* USB Request Reception */
|
||||
uint16_t CTSQ :3; /* Control Transfer Stage */
|
||||
);
|
||||
pad(4);
|
||||
word_union(INTSTS1,
|
||||
uint16_t _1 :1; /* Unknown role */
|
||||
uint16_t BCHG :1; /* Bus Change */
|
||||
uint16_t :1;
|
||||
uint16_t DTCH :1; /* Disconnection Detection */
|
||||
uint16_t ATTCH :1; /* Connection Detection */
|
||||
uint16_t :4;
|
||||
uint16_t EOFERR :1; /* EOF Error Detection */
|
||||
uint16_t SIGN :1; /* Setup Transaction Error */
|
||||
uint16_t SACK :1; /* Setup Transaction Normal Response */
|
||||
uint16_t :4;
|
||||
);
|
||||
pad(2);
|
||||
|
||||
/* BRDY Interrupt Status Register */
|
||||
word_union(BRDYSTS,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9BRDY :1;
|
||||
uint16_t PIPE8BRDY :1;
|
||||
uint16_t PIPE6BRDY :1;
|
||||
uint16_t PIPE7BRDY :1;
|
||||
uint16_t PIPE5BRDY :1;
|
||||
uint16_t PIPE3BRDY :1;
|
||||
uint16_t PIPE4BRDY :1;
|
||||
uint16_t PIPE2BRDY :1;
|
||||
uint16_t PIPE1BRDY :1;
|
||||
uint16_t PIPE0BRDY :1;
|
||||
);
|
||||
uint16_t BRDYSTS;
|
||||
/* NRDY Interrupt Status Register */
|
||||
word_union(NRDYSTS,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9NRDY :1;
|
||||
uint16_t PIPE8NRDY :1;
|
||||
uint16_t PIPE6NRDY :1;
|
||||
uint16_t PIPE7NRDY :1;
|
||||
uint16_t PIPE5NRDY :1;
|
||||
uint16_t PIPE3NRDY :1;
|
||||
uint16_t PIPE4NRDY :1;
|
||||
uint16_t PIPE2NRDY :1;
|
||||
uint16_t PIPE1NRDY :1;
|
||||
uint16_t PIPE0NRDY :1;
|
||||
);
|
||||
uint16_t NRDYSTS;
|
||||
/* BEMP Interrupt Status Register */
|
||||
word_union(BEMPSTS,
|
||||
uint16_t :6;
|
||||
uint16_t PIPE9BEMP :1;
|
||||
uint16_t PIPE8BEMP :1;
|
||||
uint16_t PIPE6BEMP :1;
|
||||
uint16_t PIPE7BEMP :1;
|
||||
uint16_t PIPE5BEMP :1;
|
||||
uint16_t PIPE3BEMP :1;
|
||||
uint16_t PIPE4BEMP :1;
|
||||
uint16_t PIPE2BEMP :1;
|
||||
uint16_t PIPE1BEMP :1;
|
||||
uint16_t PIPE0BEMP :1;
|
||||
);
|
||||
uint16_t BEMPSTS;
|
||||
|
||||
/* Frame Number Registers */
|
||||
word_union(FRMNUM,
|
||||
uint16_t OVRN :1; /* Overrun/Underrun Detection Status */
|
||||
uint16_t CRCE :1; /* Receive Data Error */
|
||||
uint16_t cons :3;
|
||||
uint16_t const :3;
|
||||
uint16_t FRNM :11; /* Frame Number */
|
||||
);
|
||||
word_union(UFRMNUM,
|
||||
|
@ -289,7 +265,10 @@ typedef volatile struct
|
|||
|
||||
/* DCP Configuration Register */
|
||||
word_union(DCPCFG,
|
||||
uint16_t :11;
|
||||
uint16_t :7;
|
||||
uint16_t _1 :1; /* Unknown role; can be set */
|
||||
uint16_t _2 :1; /* Unknown role; can be set */
|
||||
uint16_t :2;
|
||||
uint16_t DIR :1; /* Transfer Direction */
|
||||
uint16_t :4;
|
||||
);
|
||||
|
@ -341,7 +320,7 @@ typedef volatile struct
|
|||
/* Pipe Buffer Setting Register */
|
||||
word_union(PIPEBUF,
|
||||
uint16_t :1;
|
||||
uint16_t BUFSIZE : 5; /* Buffer Size */
|
||||
uint16_t BUFSIZE:5; /* Buffer Size */
|
||||
uint16_t :2;
|
||||
uint16_t BUFNMB :8; /* Buffer Number */
|
||||
);
|
||||
|
@ -379,56 +358,20 @@ typedef volatile struct
|
|||
);
|
||||
pad(14);
|
||||
|
||||
/* PIPEn Transaction Counter Enable Registers and */
|
||||
/* PIPEn Transaction Counter Registers */
|
||||
word_union(PIPE1TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE1TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
word_union(PIPE2TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE2TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
word_union(PIPE3TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE3TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
word_union(PIPE4TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE4TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
word_union(PIPE5TRE,
|
||||
uint16_t :6;
|
||||
uint16_t TRENB :1; /* Transaction Counter Enable */
|
||||
uint16_t TRCLR :1; /* Transaction Counter Clear */
|
||||
uint16_t :8;
|
||||
);
|
||||
word_union(PIPE5TRN,
|
||||
uint16_t TRCNT :16; /* Transaction Counter */
|
||||
);
|
||||
/* Transaction Counter Registers (PIPE1..PIPE5 only) */
|
||||
sh7305_usb_pipetr PIPETR[5];
|
||||
pad(0x1e);
|
||||
|
||||
uint16_t REG_C2;
|
||||
pad(12);
|
||||
|
||||
word_union(DEVADD[11],
|
||||
uint16_t :1;
|
||||
uint16_t UPPHUB :4; /* Address of target's hub */
|
||||
uint16_t HUBPORT:3; /* Hub port where target connects */
|
||||
uint16_t USBSPD :2; /* Transfer speed / Target present */
|
||||
uint16_t :6;
|
||||
);
|
||||
|
||||
} GPACKED(4) sh7305_usb_t;
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
//---
|
||||
// gint:mpu:wdt - Watchdog Timer
|
||||
//---
|
||||
|
||||
#ifndef GINT_MPU_WDT
|
||||
#define GINT_MPU_WDT
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
//---
|
||||
// SH7705 WatchDog Timer. Refer to:
|
||||
// "Renesas SH7705 Group Hardware Manual"
|
||||
// Section 10: "WatchDog Timer (WDT)"
|
||||
//---
|
||||
|
||||
/* sh7705_wdt_t - Watch Dog Timer */
|
||||
typedef volatile struct
|
||||
{
|
||||
/* WDT registers are unique in access size; reads are performed with
|
||||
8-bit accesses, but writes are performed with 16-bit accesses. */
|
||||
|
||||
union {
|
||||
uint8_t READ;
|
||||
uint16_t WRITE;
|
||||
} WTCNT;
|
||||
|
||||
union {
|
||||
byte_union(READ,
|
||||
uint8_t TME :1; /* Timer Enable */
|
||||
uint8_t WTIT :1; /* Watchdog/Interval Select */
|
||||
uint8_t RSTS :1; /* Watchdog Reset Select */
|
||||
uint8_t WOVF :1; /* Watchdog Overflow Flag */
|
||||
uint8_t IOVF :1; /* Interval Overflow Flag */
|
||||
uint8_t CKS :3; /* Clock Select */
|
||||
);
|
||||
uint16_t WRITE;
|
||||
} WTCSR;
|
||||
|
||||
} sh7705_wdt_t;
|
||||
|
||||
#define SH7705_WDT (*((sh7705_wdt_t *)0xffffff84))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_MPU_WDT */
|
|
@ -0,0 +1,64 @@
|
|||
//---
|
||||
// gint:serial - Serial communication
|
||||
//---
|
||||
|
||||
// TODO: This is a template for a future implementation.
|
||||
|
||||
#ifndef GINT_SERIAL
|
||||
#define GINT_SERIAL
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/call.h>
|
||||
|
||||
typedef enum {
|
||||
SERIAL_PARITY_NONE,
|
||||
SERIAL_PARITY_EVEN,
|
||||
SERIAL_PARITY_ODD,
|
||||
|
||||
} GPACKEDENUM serial_parity_t;
|
||||
|
||||
typedef struct {
|
||||
/* Baud rate (valid options are 300, 600, 1200, 2400, 4800, 9600,
|
||||
19200, 38400, 57600 and 115200) */
|
||||
int baudrate;
|
||||
/* Parity */
|
||||
serial_parity_t parity;
|
||||
/* Data width (valid options are 7 and 8) */
|
||||
uint8_t data_width;
|
||||
/* Stop bits (valid options are 1 and 2) */
|
||||
uint8_t stop_bits;
|
||||
|
||||
} serial_config_t;
|
||||
|
||||
// TODO: Error management...
|
||||
|
||||
int serial_open(serial_config_t const *config);
|
||||
|
||||
bool serial_is_open(void);
|
||||
|
||||
void serial_write_async(void const *data, size_t size, gint_call_t callback);
|
||||
|
||||
void serial_write_sync(void const *data, size_t size);
|
||||
|
||||
void serial_read_async(void const *data, size_t size, gint_call_t callback);
|
||||
|
||||
void serial_read_sync(void const *data, size_t size);
|
||||
|
||||
// Waits for communications to finish
|
||||
void serial_wait(void);
|
||||
|
||||
// Calls serial_wait() automatically
|
||||
void serial_close(void);
|
||||
|
||||
// TODO: Info on how much data is pending in each buffer?
|
||||
// Ultimately we'll bind this to a file descriptor so we can't really know.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* GINT_SERIAL */
|
|
@ -1,5 +1,27 @@
|
|||
//---
|
||||
// gint:usb-ff-bulk - A trivial bulk-based transfer class
|
||||
// gint:usb-ff-bulk - A bulk-based transfer class using the fxlink protocol
|
||||
//
|
||||
// This interface (class code 0xff/0x77) implements a simple bidirectional
|
||||
// communication channel running the fxlink protocol. It basically talks to
|
||||
// fxlink's interactive mode on the host, and can broadly be used for 4 things:
|
||||
//
|
||||
// 1. Sending standard messages to fxlink (screenshots, logs...). Convenient
|
||||
// functions are provided in this header to minimize the overhead. fxlink
|
||||
// running in interactive mode (fxlink -t) will handle these messages in a
|
||||
// suitable way automatically (eg. save screenshots as PNG).
|
||||
//
|
||||
// 2. Sending custom messages to fxlink (bench results, memory dumps, crash
|
||||
// reports...). fxlink running in interactive mode will forward the data to
|
||||
// appropriate user-provided external programs to handle the messages.
|
||||
//
|
||||
// 3. Applications based around on data streams coming from the host (screen
|
||||
// projector, web browser...). This can be achieved by modifying fxlink or
|
||||
// using it as a library.
|
||||
//
|
||||
// 4. Remote control from the fxlink interactive mode (fxlink -t). This is
|
||||
// based on fxlink's 'command' and 'keyboard' protocols. This module will
|
||||
// automatically handle commands and respond to them as long as the message
|
||||
// handling function is called regularly.
|
||||
//---
|
||||
|
||||
#ifndef GINT_USB_FF_BULK
|
||||
|
@ -10,164 +32,37 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include <gint/usb.h>
|
||||
struct usb_fxlink_header;
|
||||
|
||||
/* The bulk transfer interface with class code 0xff provides a very simple
|
||||
communication channel between the calculator and a host. There is
|
||||
(currently) a single IN pipe that sends data from the calculator to the
|
||||
host, at high-speed (USB 2.0).
|
||||
|
||||
The class code of this interface is 0xff, which means that the protocol is
|
||||
custom and requires a custom program on the host to receive the data (unlike
|
||||
for instance LINK on a Graph 90+E which behaves as a USB stick and can be
|
||||
used with the file browser). fxlink can be used to the effect. */
|
||||
|
||||
/* This bulk transfer interface with class code 0xff/0x77 implements a simple
|
||||
bidirectional communication channel between the calculator and a host,
|
||||
running at high-speed USB 2.0 and using the fxlink protocol. You can use it
|
||||
to communicate with fxlink's interactive mode on the host. */
|
||||
extern usb_interface_t const usb_ff_bulk;
|
||||
|
||||
//---
|
||||
// Direct bulk access
|
||||
//
|
||||
// The following functions can be used to access the bulk pipes directly. They
|
||||
// provide the pipe numbers, which can be passed for instance to usb_write
|
||||
// functions.
|
||||
// Sending standard messages
|
||||
//---
|
||||
|
||||
/* usb_ff_bulk_output(): Pipe for calculator -> host communication */
|
||||
int usb_ff_bulk_output(void);
|
||||
|
||||
//---
|
||||
// fxlink protocol
|
||||
//
|
||||
// fxlink is a bulk-based communication tool in the fxSDK that can be used to
|
||||
// conveniently transfer or manipulate information during the execution of an
|
||||
// add-in. For instance, screenshots can be saved, files can be transferred,
|
||||
// and text can be logged.
|
||||
//
|
||||
// Each communication with fxlink is a message that consists of:
|
||||
// * A first write with a message header;
|
||||
// * One or more writes with message data (of a size announced in the header);
|
||||
// * A commit on the USB pipe (to ensure that everything is sent).
|
||||
//
|
||||
// There are two ways to use the fxlink protocol in this header; you can either
|
||||
// use one of the convenience functions like usb_fxlink_screenshot() that do
|
||||
// all the steps for you, or you can perform the writes and commits yourself to
|
||||
// send custom messages.
|
||||
//---
|
||||
|
||||
/* usb_fxlink_header_t: Message header for fxlink
|
||||
|
||||
fxlink supports a minimalist protocol to receive data sent from the
|
||||
calculator and automatically process it (such as save it to file, convert
|
||||
to an image, etc). It is designed as a convenience feature, and it can be
|
||||
extended with custom types rather easily.
|
||||
|
||||
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 +74,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 defined(FX9860G) || defined(FX9860G_AS_CG)
|
||||
/* 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,
|
||||
|
||||
/* 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_write_async()) */
|
||||
USB_WRITE_BUSY,
|
||||
/* Both FIFO controllers are busy, none is available to transfer */
|
||||
USB_WRITE_NOFIFO,
|
||||
|
||||
/* This pipe is busy (returned by usb_commit_async()) */
|
||||
USB_COMMIT_BUSY,
|
||||
USB_WRITE_NOFIFO = -10,
|
||||
/* This pipe has no ongoing transfer to commit */
|
||||
USB_COMMIT_INACTIVE,
|
||||
USB_COMMIT_INACTIVE = -11,
|
||||
/* This pipe is currently not receiving any data */
|
||||
USB_READ_IDLE = -12,
|
||||
/* No FIFO controller is available */
|
||||
USB_READ_NOFIFO = -13,
|
||||
|
||||
/* (Internal codes) */
|
||||
USB_ERROR_ZERO_LENGTH = -100,
|
||||
};
|
||||
|
||||
/* usb_open(): Open the USB link
|
||||
|
@ -129,8 +140,12 @@ struct usb_interface {
|
|||
/* Answer class-specific SETUP requests */
|
||||
/* TODO */
|
||||
|
||||
/* Receive data from an endpoint */
|
||||
/* TODO */
|
||||
/* Notification that an endpoint has data to be read. This function is
|
||||
called frequently when data is being transmitted; the particular
|
||||
timings depend on low-level details. The notification should be
|
||||
passed down to cause the main thread to read later. Do not read in
|
||||
the notification function! */
|
||||
void (*notify_read)(int endpoint);
|
||||
};
|
||||
|
||||
/* usb_interface_endpoint_t: Parameters for an interface endpoint
|
||||
|
@ -169,23 +184,22 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
|
|||
|
||||
/* usb_write_sync(): Synchronously write to a USB pipe
|
||||
|
||||
This functions writes (size) bytes of (data) into the specified pipe, by
|
||||
units of (unit_size) bytes. The unit size must be 1, 2 or 4, and both (data)
|
||||
and (size) must be multiples of the unit size. In general, you should try to
|
||||
use the largest possible unit size, as it will be much faster. In a sequence
|
||||
of writes that concludes with a commit, all the writes must use the same
|
||||
unit size.
|
||||
This functions writes (size) bytes of (data) into the specified pipe. If the
|
||||
data fits into the pipe, this function returns right away, and the data is
|
||||
*not* transmitted. Otherwise, data is written until the pipe is full, at
|
||||
which point it is automatically transmitted. After the transfer, this
|
||||
function resumes writing, returning only once everything is written. Even
|
||||
then the last bytes will still not have been transmitted, to allow for other
|
||||
writes to follow. After the last write in a sequence, use usb_commit_sync()
|
||||
or usb_commit_async() to transmit the last bytes.
|
||||
|
||||
If the data fits into the pipe, this function returns right away, and the
|
||||
data is *not* transmitted. Otherwise, data is written until the pipe is
|
||||
full, at which point it is automatically transmitted. After the transfer,
|
||||
this function resumes writing, returning only once everything is written.
|
||||
Even then the last bytes will still not have been transmitted, to allow for
|
||||
other writes to follow. After the last write in a sequence, use
|
||||
usb_commit_sync() or usb_commit_async() to transmit the last bytes.
|
||||
|
||||
If (use_dma=true), the write is performed with the DMA instead of the CPU,
|
||||
which is generally faster.
|
||||
If (use_dma=true), the write is performed with the DMA instead of the CPU.
|
||||
This requires at least 4-byte alignment on:
|
||||
1. The input data;
|
||||
2. The size of this write;
|
||||
3. The amount of data previously written to the pipe not yet committed.
|
||||
This is because using the DMA does not allow any insertion of CPU logic to
|
||||
handle unaligned stuff.
|
||||
|
||||
This function will use a FIFO controller to access the pipe. The FIFO
|
||||
controller will be reserved for further writes until the contents of the
|
||||
|
@ -198,13 +212,15 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
|
|||
waits for the ressources to become available then proceeds normally.
|
||||
|
||||
@pipe Pipe to write into
|
||||
@data Source data (unit_size-aligned)
|
||||
@size Size of source (multiple of unit_size)
|
||||
@unit_size FIFO access size (must be 1, 2, or 4)
|
||||
@data Source data
|
||||
@size Size of source
|
||||
@dma Whether to use the DMA to perform the write
|
||||
-> Returns an error code (0 on success). */
|
||||
int usb_write_sync(int pipe, void const *data, int size, int unit_size,
|
||||
bool use_dma);
|
||||
int usb_write_sync(int pipe, void const *data, int size, bool use_dma);
|
||||
|
||||
/* usb_write_sync_timeout(): Synchronously write, with a timeout */
|
||||
int usb_write_sync_timeout(int pipe, void const *data, int size,
|
||||
bool use_dma, timeout_t const *timeout);
|
||||
|
||||
/* usb_write_async(): Asynchronously write to a USB pipe
|
||||
|
||||
|
@ -226,14 +242,13 @@ int usb_write_sync(int pipe, void const *data, int size, int unit_size,
|
|||
is idle and USB_WRITE_BUSY otherwise.
|
||||
|
||||
@pipe Pipe to write into
|
||||
@data Source data (unit_size-aligned)
|
||||
@size Size of source (multiple of unit_size)
|
||||
@unit_size FIFO access size (must be 1, 2, or 4)
|
||||
@data Source data
|
||||
@size Size of source
|
||||
@dma Whether to use the DMA to perform the write
|
||||
@callback Optional callback to invoke when the write completes
|
||||
-> Returns an error code (0 on success). */
|
||||
int usb_write_async(int pipe, void const *data, int size, int unit_size,
|
||||
bool use_dma, gint_call_t callback);
|
||||
int usb_write_async(int pipe, void const *data, int size, bool use_dma,
|
||||
gint_call_t callback);
|
||||
|
||||
/* usb_commit_sync(): Synchronously commit a write
|
||||
|
||||
|
@ -241,6 +256,9 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size,
|
|||
transfers whatever data is left, and returns when the transfer completes. */
|
||||
void usb_commit_sync(int pipe);
|
||||
|
||||
/* usb_commit_sync_timeout(): Synchronously commit a write, with timeout */
|
||||
int usb_commit_sync_timeout(int pipe, timeout_t const *timeout);
|
||||
|
||||
/* usb_commit_async(): Asynchronously commit a write
|
||||
|
||||
This function commits the specified pipe, causing the pipe to transfer
|
||||
|
@ -255,23 +273,139 @@ 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
|
||||
#ifdef GINT_USB_DEBUG
|
||||
#define USB_LOG(...) usb_log(__VA_ARGS__)
|
||||
#define USB_TRACE(...) usb_trace(__VA_ARGS__)
|
||||
|
||||
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_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_trace(): Trace the current state of the driver */
|
||||
void usb_trace(char const *message);
|
||||
|
||||
#else
|
||||
#define USB_LOG(...) do {} while(0)
|
||||
#define USB_TRACE(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
//---
|
||||
// Standard descriptors
|
||||
//---
|
||||
|
|
|
@ -27,7 +27,6 @@ const clock_frequency_t *clock_freq(void)
|
|||
//---
|
||||
|
||||
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
|
||||
#define CPG SH7705_CPG
|
||||
|
||||
void sh7705_probe(void)
|
||||
{
|
||||
|
@ -41,35 +40,23 @@ 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 */
|
||||
|
||||
//---
|
||||
|
@ -134,9 +121,7 @@ static void configure(void)
|
|||
{
|
||||
/* Disable spread spectrum in SSGSCR */
|
||||
if(isSH4())
|
||||
{
|
||||
SH7305_CPG.SSCGCR.SSEN = 0;
|
||||
}
|
||||
|
||||
cpg_compute_freq();
|
||||
}
|
||||
|
@ -147,18 +132,16 @@ static void configure(void)
|
|||
|
||||
static void hsave(cpg_state_t *s)
|
||||
{
|
||||
if(isSH4()) {
|
||||
if(isSH4())
|
||||
s->SSCGCR = SH7305_CPG.SSCGCR.lword;
|
||||
cpg_get_overclock_setting(&s->speed);
|
||||
}
|
||||
cpg_get_overclock_setting(&s->speed);
|
||||
}
|
||||
|
||||
static void hrestore(cpg_state_t const *s)
|
||||
{
|
||||
if(isSH4()) {
|
||||
if(isSH4())
|
||||
SH7305_CPG.SSCGCR.lword = s->SSCGCR;
|
||||
cpg_set_overclock_setting(&s->speed);
|
||||
}
|
||||
cpg_set_overclock_setting(&s->speed);
|
||||
}
|
||||
|
||||
gint_driver_t drv_cpg = {
|
||||
|
|
|
@ -14,137 +14,309 @@
|
|||
#include <gint/hardware.h>
|
||||
#include <gint/mpu/cpg.h>
|
||||
#include <gint/mpu/bsc.h>
|
||||
|
||||
#define CPG SH7305_CPG
|
||||
#define BSC SH7305_BSC
|
||||
#include <gint/mpu/wdt.h>
|
||||
|
||||
//---
|
||||
// Low-level clock speed access
|
||||
//---
|
||||
|
||||
#define SDMR3_CL2 ((volatile uint8_t *)0xFEC15040)
|
||||
#define SDMR3_CL3 ((volatile uint8_t *)0xFEC15060)
|
||||
|
||||
void cpg_get_overclock_setting(struct cpg_overclock_setting *s)
|
||||
{
|
||||
if(!isSH4())
|
||||
return;
|
||||
|
||||
s->FLLFRQ = CPG.FLLFRQ.lword;
|
||||
s->FRQCR = CPG.FRQCR.lword;
|
||||
|
||||
s->CS0BCR = BSC.CS0BCR.lword;
|
||||
s->CS0WCR = BSC.CS0WCR.lword;
|
||||
s->CS2BCR = BSC.CS2BCR.lword;
|
||||
s->CS2WCR = BSC.CS2WCR.lword;
|
||||
s->CS3BCR = BSC.CS3BCR.lword;
|
||||
s->CS3WCR = BSC.CS3WCR.lword;
|
||||
s->CS5aBCR = BSC.CS5ABCR.lword;
|
||||
s->CS5aWCR = BSC.CS5AWCR.lword;
|
||||
}
|
||||
|
||||
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s)
|
||||
{
|
||||
if(!isSH4())
|
||||
return;
|
||||
|
||||
BSC.CS0WCR.WR = 11; /* 18 cycles */
|
||||
|
||||
CPG.FLLFRQ.lword = s->FLLFRQ;
|
||||
CPG.FRQCR.lword = s->FRQCR;
|
||||
CPG.FRQCR.KICK = 1;
|
||||
while(CPG.LSTATS != 0) {}
|
||||
|
||||
BSC.CS0BCR.lword = s->CS0BCR;
|
||||
BSC.CS0WCR.lword = s->CS0WCR;
|
||||
BSC.CS2BCR.lword = s->CS2BCR;
|
||||
BSC.CS2WCR.lword = s->CS2WCR;
|
||||
BSC.CS3BCR.lword = s->CS3BCR;
|
||||
BSC.CS3WCR.lword = s->CS3WCR;
|
||||
|
||||
if(BSC.CS3WCR.A3CL == 1)
|
||||
*SDMR3_CL2 = 0;
|
||||
else
|
||||
*SDMR3_CL3 = 0;
|
||||
|
||||
BSC.CS5ABCR.lword = s->CS5aBCR;
|
||||
BSC.CS5AWCR.lword = s->CS5aWCR;
|
||||
}
|
||||
#define SH7305_SDMR3_CL2 ((volatile uint8_t *)0xFEC15040)
|
||||
#define SH7305_SDMR3_CL3 ((volatile uint8_t *)0xFEC15060)
|
||||
|
||||
//---
|
||||
// Predefined clock speeds
|
||||
//---
|
||||
|
||||
#ifdef FXCG50
|
||||
/* 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
|
||||
|
||||
#define PLL_32x 0b011111
|
||||
#define PLL_26x 0b011001
|
||||
#define PLL_16x 0b001111
|
||||
#define DIV_2 0
|
||||
#define DIV_4 1
|
||||
#define DIV_8 2
|
||||
#define DIV_16 3
|
||||
#define DIV_32 4
|
||||
/* 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
|
||||
|
||||
static struct cpg_overclock_setting settings_cg50[5] = {
|
||||
void cpg_get_overclock_setting(struct cpg_overclock_setting *s)
|
||||
{
|
||||
if(isSH3()) {
|
||||
s->FLLFRQ = -1;
|
||||
s->FRQCR = SH7705_CPG.FRQCR.word;
|
||||
|
||||
s->CS0BCR = SH7705_BSC.CS0BCR.lword;
|
||||
s->CS0WCR = SH7705_BSC.CS0WCR.lword;
|
||||
s->CS2BCR = SH7705_BSC.CS2BCR.lword;
|
||||
s->CS2WCR = SH7705_BSC.CS2WCR.lword;
|
||||
s->CS3BCR = SH7705_BSC.CS3BCR.lword;
|
||||
s->CS3WCR = SH7705_BSC.CS3WCR.lword;
|
||||
s->CS5aBCR = SH7705_BSC.CS5ABCR.lword;
|
||||
s->CS5aWCR = SH7705_BSC.CS5AWCR.lword;
|
||||
}
|
||||
else {
|
||||
s->FLLFRQ = SH7305_CPG.FLLFRQ.lword;
|
||||
s->FRQCR = SH7305_CPG.FRQCR.lword;
|
||||
|
||||
s->CS0BCR = SH7305_BSC.CS0BCR.lword;
|
||||
s->CS0WCR = SH7305_BSC.CS0WCR.lword;
|
||||
s->CS2BCR = SH7305_BSC.CS2BCR.lword;
|
||||
s->CS2WCR = SH7305_BSC.CS2WCR.lword;
|
||||
s->CS3BCR = SH7305_BSC.CS3BCR.lword;
|
||||
s->CS3WCR = SH7305_BSC.CS3WCR.lword;
|
||||
s->CS5aBCR = SH7305_BSC.CS5ABCR.lword;
|
||||
s->CS5aWCR = SH7305_BSC.CS5AWCR.lword;
|
||||
}
|
||||
}
|
||||
|
||||
static void cpg_low_level_set_setting(struct cpg_overclock_setting const *s)
|
||||
{
|
||||
if(isSH3()) {
|
||||
SH7705_WDT.WTCNT.WRITE = 0;
|
||||
SH7705_WDT.WTCSR.WRITE = 0x65;
|
||||
SH7705_CPG.FRQCR.word = s->FRQCR | 0x1000;
|
||||
SH7705_BSC.CS0BCR.lword = s->CS0BCR;
|
||||
SH7705_BSC.CS0WCR.lword = s->CS0WCR;
|
||||
SH7705_BSC.CS2BCR.lword = s->CS2BCR;
|
||||
SH7705_BSC.CS2WCR.lword = s->CS2WCR;
|
||||
SH7705_BSC.CS3BCR.lword = s->CS3BCR;
|
||||
SH7705_BSC.CS3WCR.lword = s->CS3WCR;
|
||||
SH7705_BSC.CS5ABCR.lword = s->CS5aBCR;
|
||||
SH7705_BSC.CS5AWCR.lword = s->CS5aWCR;
|
||||
}
|
||||
else {
|
||||
SH7305_BSC.CS0WCR.WR = 11; /* 18 cycles */
|
||||
|
||||
SH7305_CPG.FLLFRQ.lword = s->FLLFRQ;
|
||||
SH7305_CPG.FRQCR.lword = s->FRQCR;
|
||||
SH7305_CPG.FRQCR.KICK = 1;
|
||||
while(SH7305_CPG.LSTATS != 0) {}
|
||||
|
||||
SH7305_BSC.CS0BCR.lword = s->CS0BCR;
|
||||
SH7305_BSC.CS0WCR.lword = s->CS0WCR;
|
||||
SH7305_BSC.CS2BCR.lword = s->CS2BCR;
|
||||
SH7305_BSC.CS2WCR.lword = s->CS2WCR;
|
||||
SH7305_BSC.CS3BCR.lword = s->CS3BCR;
|
||||
SH7305_BSC.CS3WCR.lword = s->CS3WCR;
|
||||
|
||||
if(SH7305_BSC.CS3WCR.A3CL == 1)
|
||||
*SH7305_SDMR3_CL2 = 0;
|
||||
else
|
||||
*SH7305_SDMR3_CL3 = 0;
|
||||
|
||||
SH7305_BSC.CS5ABCR.lword = s->CS5aBCR;
|
||||
SH7305_BSC.CS5AWCR.lword = s->CS5aWCR;
|
||||
}
|
||||
}
|
||||
|
||||
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s)
|
||||
{
|
||||
uint32_t old_Pphi = clock_freq()->Pphi_f;
|
||||
|
||||
/* Wait for asynchronous tasks to complete */
|
||||
gint_world_sync();
|
||||
|
||||
/* Disable interrupts during the change */
|
||||
cpu_atomic_start();
|
||||
|
||||
/* Load the clock settings */
|
||||
cpg_low_level_set_setting(s);
|
||||
|
||||
/* Determine the change in frequency for Pϕ and recompute CPG data */
|
||||
cpg_compute_freq();
|
||||
uint32_t new_Pphi = clock_freq()->Pphi_f;
|
||||
|
||||
/* Update timers' TCNT and TCOR to match the new clock speed */
|
||||
void timer_rescale(uint32_t old_Pphi, uint32_t new_Pphi);
|
||||
timer_rescale(old_Pphi, new_Pphi);
|
||||
|
||||
cpu_atomic_end();
|
||||
}
|
||||
|
||||
#ifdef FX9860G
|
||||
|
||||
static struct cpg_overclock_setting const settings_fx9860g_sh3[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = 0x0F011112,
|
||||
.CS0BCR = 0x36DA0400,
|
||||
.CS2BCR = 0x36DA3400,
|
||||
.CS3BCR = 0x36DB4400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
{ .FRQCR = 0x1001,
|
||||
.CS0BCR = 0x02480400,
|
||||
.CS2BCR = 0x02483400,
|
||||
.CS3BCR = 0x36DB0600,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x00000500,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (PLL_16x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000340,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
{ .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 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (PLL_26x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000240,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
{ .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 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (PLL_32x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_8<<8)+DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x000002C0,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
{ .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 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000440,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
{ .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 settings_cg20[5] = {
|
||||
static struct cpg_overclock_setting const settings_fx9860g_sh4[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = 0x0F202203,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000005C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x24920200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x14900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000302C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting const settings_g35pe2[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = 0x0F112213,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x000001C0,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031340 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x24920200,
|
||||
.CS0WCR = 0x00000140,
|
||||
.CS2WCR = 0x00000140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00000D41 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x04900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031B40 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004384,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x14900400,
|
||||
.CS2BCR = 0x04903400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x224A0200,
|
||||
.CS0WCR = 0x000001C0,
|
||||
.CS2WCR = 0x00020140,
|
||||
.CS3WCR = 0x000024D0,
|
||||
.CS5aWCR = 0x00031B40 },
|
||||
};
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
||||
#ifdef FXCG50
|
||||
|
||||
static struct cpg_overclock_setting const settings_prizm[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = 0x0F102203,
|
||||
|
@ -158,7 +330,7 @@ static struct cpg_overclock_setting settings_cg20[5] = {
|
|||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (PLL_32x<<24)+(DIV_8<<20)+(DIV_16<<12)+(DIV_16<<8)+DIV_32,
|
||||
.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,
|
||||
|
@ -169,7 +341,7 @@ static struct cpg_overclock_setting settings_cg20[5] = {
|
|||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (PLL_32x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_32,
|
||||
.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,
|
||||
|
@ -180,7 +352,7 @@ static struct cpg_overclock_setting settings_cg20[5] = {
|
|||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (PLL_32x<<24)+(DIV_4<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_32,
|
||||
.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,
|
||||
|
@ -191,7 +363,7 @@ static struct cpg_overclock_setting settings_cg20[5] = {
|
|||
.CS5aWCR = 0x00010240 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_16,
|
||||
.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,
|
||||
|
@ -202,35 +374,125 @@ static struct cpg_overclock_setting settings_cg20[5] = {
|
|||
.CS5aWCR = 0x00010240 },
|
||||
};
|
||||
|
||||
static struct cpg_overclock_setting *get_settings(void)
|
||||
static struct cpg_overclock_setting const settings_fxcg50[5] = {
|
||||
/* CLOCK_SPEED_F1 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = 0x0F011112,
|
||||
.CS0BCR = 0x36DA0400,
|
||||
.CS2BCR = 0x36DA3400,
|
||||
.CS3BCR = 0x36DB4400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x000003C0,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F2 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000340,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F3 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000240,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F4 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x000002C0,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
/* CLOCK_SPEED_F5 */
|
||||
{ .FLLFRQ = 0x00004000 + 900,
|
||||
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_8,
|
||||
.CS0BCR = 0x24920400,
|
||||
.CS2BCR = 0x24923400,
|
||||
.CS3BCR = 0x24924400,
|
||||
.CS5aBCR = 0x17DF0400,
|
||||
.CS0WCR = 0x00000440,
|
||||
.CS2WCR = 0x000003C0,
|
||||
.CS3WCR = 0x000024D1,
|
||||
.CS5aWCR = 0x000203C1 },
|
||||
};
|
||||
|
||||
#endif /* FXCG50 */
|
||||
|
||||
static struct cpg_overclock_setting const *get_settings(void)
|
||||
{
|
||||
if(gint[HWCALC] == HWCALC_FXCG50)
|
||||
return settings_cg50;
|
||||
#ifdef FX9860G
|
||||
if(gint[HWCALC] == HWCALC_FX9860G_SH3)
|
||||
return settings_fx9860g_sh3;
|
||||
if(gint[HWCALC] == HWCALC_FX9860G_SH4)
|
||||
return settings_fx9860g_sh4;
|
||||
if(gint[HWCALC] == HWCALC_G35PE2)
|
||||
return settings_g35pe2;
|
||||
#endif /* FX9860G */
|
||||
|
||||
#ifdef FXCG50
|
||||
if(gint[HWCALC] == HWCALC_PRIZM)
|
||||
return settings_cg20;
|
||||
return settings_prizm;
|
||||
if(gint[HWCALC] == HWCALC_FXCG50)
|
||||
return settings_fxcg50;
|
||||
#endif /* FXCG50 */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int clock_get_speed(void)
|
||||
{
|
||||
struct cpg_overclock_setting *settings = get_settings();
|
||||
struct cpg_overclock_setting const *settings = get_settings();
|
||||
if(!settings)
|
||||
return CLOCK_SPEED_UNKNOWN;
|
||||
|
||||
for(int i = 0; i < 5; i++) {
|
||||
struct cpg_overclock_setting *s = &settings[i];
|
||||
if(isSH3()) {
|
||||
for(int i = 0; i < 5; i++) {
|
||||
struct cpg_overclock_setting const *s = &settings[i];
|
||||
|
||||
if(CPG.FLLFRQ.lword == s->FLLFRQ
|
||||
&& CPG.FRQCR.lword == s->FRQCR
|
||||
&& BSC.CS0BCR.lword == s->CS0BCR
|
||||
&& BSC.CS2BCR.lword == s->CS2BCR
|
||||
&& BSC.CS3BCR.lword == s->CS3BCR
|
||||
&& BSC.CS5ABCR.lword == s->CS5aBCR
|
||||
&& BSC.CS0WCR.lword == s->CS0WCR
|
||||
&& BSC.CS2WCR.lword == s->CS2WCR
|
||||
&& BSC.CS3WCR.lword == s->CS3WCR
|
||||
&& BSC.CS5AWCR.lword == s->CS5aWCR)
|
||||
return CLOCK_SPEED_F1 + i;
|
||||
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;
|
||||
|
@ -243,31 +505,10 @@ void clock_set_speed(int level)
|
|||
if(clock_get_speed() == level)
|
||||
return;
|
||||
|
||||
struct cpg_overclock_setting *settings = get_settings();
|
||||
struct cpg_overclock_setting const *settings = get_settings();
|
||||
if(!settings)
|
||||
return;
|
||||
|
||||
struct cpg_overclock_setting *s = &settings[level - CLOCK_SPEED_F1];
|
||||
uint32_t old_Pphi = clock_freq()->Pphi_f;
|
||||
|
||||
/* Wait for asynchronous tasks to complete */
|
||||
gint_world_sync();
|
||||
|
||||
/* Disable interrupts during the change */
|
||||
cpu_atomic_start();
|
||||
|
||||
/* Set the clock settings */
|
||||
struct cpg_overclock_setting const *s = &settings[level - CLOCK_SPEED_F1];
|
||||
cpg_set_overclock_setting(s);
|
||||
|
||||
/* Determine the change in frequency for Pϕ and recompute CPG data */
|
||||
cpg_compute_freq();
|
||||
uint32_t new_Pphi = clock_freq()->Pphi_f;
|
||||
|
||||
/* Update timers' TCNT and TCOR to match the new clock speed */
|
||||
void timer_rescale(uint32_t old_Pphi, uint32_t new_Pphi);
|
||||
timer_rescale(old_Pphi, new_Pphi);
|
||||
|
||||
cpu_atomic_end();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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
|
||||
|
|
19
src/fs/fs.c
19
src/fs/fs.c
|
@ -2,13 +2,14 @@
|
|||
#include <gint/defs/attributes.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* File descriptor table */
|
||||
static fs_descriptor_t fdtable[FS_FD_MAX] = { 0 };
|
||||
static fs_descriptor_t *fdtable;
|
||||
|
||||
fs_descriptor_t const *fs_get_descriptor(int fd)
|
||||
{
|
||||
if((unsigned)fd >= FS_FD_MAX)
|
||||
if(!fdtable || (unsigned)fd >= FS_FD_MAX)
|
||||
return NULL;
|
||||
if(fdtable[fd].type == NULL)
|
||||
return NULL;
|
||||
|
@ -18,7 +19,7 @@ fs_descriptor_t const *fs_get_descriptor(int fd)
|
|||
|
||||
int fs_create_descriptor(fs_descriptor_t const *data)
|
||||
{
|
||||
if(data->type == NULL)
|
||||
if(!fdtable || data->type == NULL)
|
||||
return -1;
|
||||
|
||||
/* Leave 0/1/2 for stdin, stdout and stderr */
|
||||
|
@ -34,7 +35,7 @@ int fs_create_descriptor(fs_descriptor_t const *data)
|
|||
|
||||
void fs_free_descriptor(int fd)
|
||||
{
|
||||
if((unsigned)fd >= FS_FD_MAX)
|
||||
if(!fdtable || (unsigned)fd >= FS_FD_MAX)
|
||||
return;
|
||||
|
||||
fdtable[fd].type = NULL;
|
||||
|
@ -43,6 +44,10 @@ void fs_free_descriptor(int fd)
|
|||
|
||||
int open_generic(fs_descriptor_type_t *type, void *data, int fd)
|
||||
{
|
||||
if(!fdtable) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
if(!type) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
|
@ -81,8 +86,12 @@ static fs_descriptor_type_t devnull = {
|
|||
.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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <stdio.h>
|
||||
#include "fugue/fugue.h"
|
||||
|
||||
int rename(char const *oldpath, char const *newpath)
|
||||
{
|
||||
/* Standard rename() is the Fugue filesystem only */
|
||||
return fugue_rename(oldpath, newpath);
|
||||
}
|
|
@ -41,6 +41,7 @@ static struct rendering_mode const gray_mode = {
|
|||
.dclear = gclear,
|
||||
.drect = grect,
|
||||
.dpixel = gpixel,
|
||||
.dgetpixel = ggetpixel,
|
||||
.gint_dhline = gint_ghline,
|
||||
.gint_dvline = gint_gvline,
|
||||
.dtext_opt = gtext_opt,
|
||||
|
@ -51,6 +52,7 @@ static struct rendering_mode const gray_exit_mode = {
|
|||
.dclear = NULL,
|
||||
.drect = NULL,
|
||||
.dpixel = NULL,
|
||||
.dgetpixel = NULL,
|
||||
.gint_dhline = NULL,
|
||||
.gint_dvline = NULL,
|
||||
.dtext_opt = NULL,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#include <gint/gray.h>
|
||||
#include <gint/defs/types.h>
|
||||
|
||||
int ggetpixel(int x, int y)
|
||||
{
|
||||
uint32_t *light, *dark;
|
||||
dgray_getvram(&light, &dark);
|
||||
|
||||
int offset = (y << 2) + (x >> 5);
|
||||
uint32_t mask = 1 << (~x & 31);
|
||||
|
||||
bool l = (light[offset] & mask) != 0;
|
||||
bool d = (dark [offset] & mask) != 0;
|
||||
return (d << 1) | l;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/display.h>
|
||||
#include <string.h>
|
||||
#undef image_sub
|
||||
|
||||
|
@ -15,8 +16,9 @@ image_t *image_sub(image_t const *src, int left, int top, int w, int h,
|
|||
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, w, h)) {
|
||||
!gint_image_clip_input(src, &box, &in_window)) {
|
||||
memset(dst, 0, sizeof *dst);
|
||||
return dst;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
//---
|
||||
// Interrupt controllers
|
||||
//---
|
||||
|
||||
GDATA3 sh7705_intc_t SH7705_INTC = {
|
||||
GRODATA3 sh7705_intc_t const SH7705_INTC = {
|
||||
.IPR = {
|
||||
(void *)0xfffffee2, (void *)0xfffffee4,
|
||||
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
|
||||
|
@ -21,7 +20,7 @@ GDATA3 sh7705_intc_t SH7705_INTC = {
|
|||
.ICR1 = (void *)0xa4000010,
|
||||
};
|
||||
|
||||
sh7305_intc_t SH7305_INTC = {
|
||||
sh7305_intc_t const SH7305_INTC = {
|
||||
.IPR = (void *)0xa4080000,
|
||||
.MSK = (void *)0xa4080080,
|
||||
.MSKCLR = (void *)0xa40800c0,
|
||||
|
|
|
@ -2,16 +2,43 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/clock.h>
|
||||
#include <gint/mpu/dma.h>
|
||||
#include <gint/drivers/keydev.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/hardware.h>
|
||||
#include <gint/gint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void __Reset(void);
|
||||
|
||||
#define dprint(x, y, ...) dprint(x, y, C_BLACK, __VA_ARGS__)
|
||||
#define dtext(x, y, str) dtext (x, y, C_BLACK, str)
|
||||
|
||||
/* Weak implementation of driver functions, which are used if the keyboard and
|
||||
display drivers are not linked in. This allows add-ins to not link in these
|
||||
drivers (which is useful for low-level debugging). */
|
||||
GWEAK void _WEAK_dupdate(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GWEAK keydev_t *_WEAK_keydev_std(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
GWEAK bool _WEAK_keydev_keydown(GUNUSED keydev_t *d, GUNUSED int key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
GWEAK key_event_t _WEAK_keydev_unqueue_event(GUNUSED keydev_t *d)
|
||||
{
|
||||
return (key_event_t){ .type = KEYEV_NONE };
|
||||
}
|
||||
|
||||
/* gint_panic_default(): Default panic handler */
|
||||
GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
||||
{
|
||||
uint32_t TEA, TRA;
|
||||
keydev_t *kd = _WEAK_keydev_std();
|
||||
|
||||
if(isSH3())
|
||||
{
|
||||
|
@ -31,9 +58,9 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
|||
if(isSH4()) __asm__("stc sgr, %0" : "=r"(SGR));
|
||||
|
||||
dfont(NULL);
|
||||
dclear(C_WHITE);
|
||||
|
||||
#ifdef FX9860G
|
||||
memset(gint_vram, 0, 1024);
|
||||
dtext(1, 0, "Exception! (SysERROR)");
|
||||
for(int i = 0; i < 32; i++) gint_vram[i] = ~gint_vram[i];
|
||||
|
||||
|
@ -55,16 +82,23 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
|||
if(name[0]) dtext(1, 9, name);
|
||||
else dprint(1, 9, "%03x", code);
|
||||
|
||||
dprint(1, 17, " PC :%08x", PC);
|
||||
dprint(1, 25, "TEA :%08x", TEA);
|
||||
dprint(1, 33, "TRA :%08x", TRA);
|
||||
if(isSH4()) dprint(1, 41, "SGR :%08x", SGR);
|
||||
dprint(1, 17, " PC:%08x TRA:%d", PC, TRA);
|
||||
dprint(1, 25, "TEA:%08x", TEA);
|
||||
|
||||
dtext(1, 49, "The add-in crashed.");
|
||||
dtext(1, 57, "Please reset the calc");
|
||||
dtext(1, 33, "The add-in crashed.");
|
||||
if(kd == NULL) {
|
||||
dtext(1, 41, "Please reset the calc");
|
||||
}
|
||||
else {
|
||||
dtext(1, 41, "[EXIT]:Quit add-in");
|
||||
dtext(1, 49, "[MENU]:Main menu");
|
||||
dtext(1, 57, "[F1]+[F6]: RESET calc");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef FXCG50
|
||||
/* Don't require the DMA driver just for a clear */
|
||||
memset(gint_vram, 0xff, DWIDTH*DHEIGHT*2);
|
||||
dtext(6, 3, "An exception occured! (System ERROR)");
|
||||
|
||||
uint32_t *long_vram = (void *)gint_vram;
|
||||
|
@ -99,34 +133,63 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
|||
dprint(38, 75, "= %#x", TRA);
|
||||
dtext(281, 75, "(Trap number)");
|
||||
|
||||
dtext(6, 95, "An unrecoverable error occurred in the add-in.");
|
||||
dtext(6, 108, "Please press the RESET button to restart the");
|
||||
dtext(6, 121, "calculator.");
|
||||
dtext(6, 95, "The add-in crashed!");
|
||||
if(kd == NULL) {
|
||||
dtext(6, 108, "Please press the RESET button to restart the");
|
||||
dtext(6, 121, "calculator.");
|
||||
}
|
||||
else {
|
||||
dtext(6, 121, "[EXIT]: Exit the program with abort()");
|
||||
dtext(6, 134, "[MENU]: Leave to main menu");
|
||||
dtext(6, 147, "[F1]+[F6]: RESET the calculator");
|
||||
}
|
||||
|
||||
/* DMA address error handler */
|
||||
if(code == 0x1020)
|
||||
{
|
||||
#define DMA SH7305_DMA
|
||||
dprint(6, 141, "SAR0: %08x DAR0: %08x TCR0: %08x",
|
||||
dprint(6, 167, "SAR0: %08x DAR0: %08x TCR0: %08x",
|
||||
DMA.DMA0.SAR, DMA.DMA0.DAR, DMA.DMA0.TCR);
|
||||
dprint(6, 154, "CHCR0: %08x", DMA.DMA0.CHCR);
|
||||
dprint(6, 167, "SAR1: %08x DAR1: %08x TCR1: %08x",
|
||||
dprint(6, 180, "CHCR0: %08x", DMA.DMA0.CHCR);
|
||||
dprint(6, 193, "SAR1: %08x DAR1: %08x TCR1: %08x",
|
||||
DMA.DMA1.SAR, DMA.DMA1.DAR, DMA.DMA1.TCR);
|
||||
dprint(6, 180, "CHCR1: %08x", DMA.DMA1.CHCR);
|
||||
dprint(6, 193, "DMAOR: %04x", DMA.OR);
|
||||
dprint(6, 206, "CHCR1: %08x DMAOR:%04x", DMA.DMA1.CHCR, DMA.OR);
|
||||
#undef DMA
|
||||
}
|
||||
/* Illegal instruction handler */
|
||||
if(code == 0x180)
|
||||
{
|
||||
uint16_t *opcodes = (void *)PC;
|
||||
dprint(6, 141, "Opcodes: %04x %04x [%04x] %04x",
|
||||
dprint(6, 160, "Opcodes: %04x %04x [%04x] %04x",
|
||||
opcodes[-2], opcodes[-1], opcodes[0], opcodes[1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
dupdate();
|
||||
while(1) sleep();
|
||||
_WEAK_dupdate();
|
||||
|
||||
/* Make sure relevant keys are released before taking in events; we don't
|
||||
want the user to RESET through this screen by holding a key */
|
||||
bool has_released = false;
|
||||
|
||||
while(1) {
|
||||
while(_WEAK_keydev_unqueue_event(kd).type != KEYEV_NONE) {}
|
||||
|
||||
bool exit = _WEAK_keydev_keydown(kd, KEY_EXIT);
|
||||
bool menu = _WEAK_keydev_keydown(kd, KEY_MENU);
|
||||
bool f1 = _WEAK_keydev_keydown(kd, KEY_F1);
|
||||
bool f6 = _WEAK_keydev_keydown(kd, KEY_F6);
|
||||
|
||||
if(has_released && exit)
|
||||
abort();
|
||||
if(has_released && menu)
|
||||
gint_osmenu();
|
||||
if(has_released && f1 && f6)
|
||||
__Reset();
|
||||
if(!exit && !menu && !f1 && !f6)
|
||||
has_released = true;
|
||||
|
||||
sleep();
|
||||
}
|
||||
}
|
||||
|
||||
/* Panic handler */
|
||||
|
|
|
@ -69,7 +69,8 @@ void hw_detect(void)
|
|||
gint[HWCALC] = HWCALC_FX9860G_SH4;
|
||||
if(gint[HWMPU] == HWMPU_SH7337 || gint[HWMPU] == HWMPU_SH7355)
|
||||
{
|
||||
gint[HWCALC] = HWCALC_FX9860G_SH3;
|
||||
gint[HWCALC] = (SH7705_PFC.PEDR & 0x08) ? HWCALC_FX9860G_SH3 :
|
||||
HWCALC_FX9860G_SLIM;
|
||||
}
|
||||
|
||||
/* Tell Graph 35+E II from OS version (this is accurate unless someone
|
||||
|
|
|
@ -102,7 +102,7 @@ _gint_inth_7305:
|
|||
|
||||
#ifdef FX9860G
|
||||
|
||||
/* SH7705-TYPE INTERRUT HANDLER ENTRY - 56 BYTES */
|
||||
/* SH7705-TYPE INTERRUPT HANDLER ENTRY - 56 BYTES */
|
||||
|
||||
_gint_inth_7705:
|
||||
/* Save caller-saved registers as before */
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <gint/gint.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/hardware.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
@ -12,18 +13,22 @@ int __GetKeyWait(int *col,int *row,int type,int time,int menu,uint16_t *key);
|
|||
void __ClearKeyBuffer(void); /* ? */
|
||||
void *__GetVRAMAddress(void);
|
||||
void __ConfigureStatusArea(int mode);
|
||||
void __SetQuitHandler(void (*callback)(void));
|
||||
|
||||
static int __osmenu_id;
|
||||
|
||||
static void __osmenu_handler(void)
|
||||
{
|
||||
__PutKeyCode(0x04, 0x09, 0);
|
||||
if(isSlim())
|
||||
__PutKeyCode(0x07, 0x0A, 0);
|
||||
else
|
||||
__PutKeyCode(0x04, 0x09, 0);
|
||||
|
||||
__Timer_Stop(__osmenu_id);
|
||||
__Timer_Deinstall(__osmenu_id);
|
||||
}
|
||||
|
||||
static void __osmenu(void)
|
||||
void gint_osmenu_native(void)
|
||||
{
|
||||
__ClearKeyBuffer();
|
||||
|
||||
|
@ -70,5 +75,30 @@ static void __osmenu(void)
|
|||
/* gint_osmenu() - switch out of gint and call the calculator's main menu */
|
||||
void gint_osmenu(void)
|
||||
{
|
||||
gint_world_switch(GINT_CALL(__osmenu));
|
||||
gint_world_switch(GINT_CALL(gint_osmenu_native));
|
||||
}
|
||||
|
||||
static gint_call_t __gcall;
|
||||
static bool __do_world_switch;
|
||||
|
||||
static void __handler()
|
||||
{
|
||||
if(__do_world_switch){
|
||||
gint_call(__gcall);
|
||||
}else{
|
||||
/* TODO: quit the world switch */
|
||||
gint_call(__gcall);
|
||||
}
|
||||
}
|
||||
|
||||
static void __sethandler()
|
||||
{
|
||||
__SetQuitHandler((void *)__handler);
|
||||
}
|
||||
|
||||
void gint_set_quit_handler(gint_call_t gcall, bool do_world_switch)
|
||||
{
|
||||
__gcall = gcall;
|
||||
__do_world_switch = do_world_switch;
|
||||
gint_world_switch(GINT_CALL(__sethandler));
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ extern uint32_t
|
|||
lilram, silram, rilram, /* IL memory section */
|
||||
lxyram, sxyram, rxyram, /* X and Y memory section */
|
||||
sbss, rbss; /* User's BSS section */
|
||||
#ifdef FX9860G
|
||||
#if defined(FX9860G) && !defined(FX9860G_AS_CG)
|
||||
extern uint32_t
|
||||
lgmapped, sgmapped, /* Permanently mapped functions */
|
||||
lreloc, sreloc; /* Relocatable references */
|
||||
|
@ -94,11 +94,7 @@ static void callarray(void (**f)(void), void (**l)(void))
|
|||
while(f < l) (*(*f++))();
|
||||
}
|
||||
|
||||
/* start(): Where it all starts
|
||||
Returns a status code. Invoking main menu is better than returning, see
|
||||
gint_setrestart() for that. */
|
||||
GSECTION(".text.entry")
|
||||
int start(int isappli, int optnum)
|
||||
static int start2(int isappli, int optnum)
|
||||
{
|
||||
/* We are currently in a dynamic userspace mapping of an add-in run
|
||||
from the storage memory. We are running in privileged mode with one
|
||||
|
@ -124,7 +120,7 @@ int start(int isappli, int optnum)
|
|||
1.00 and the fx-9860G emulator) is not clear yet, so gint can't load
|
||||
pages dynamically. Load everything preventively (works only if the
|
||||
add-in is small enough) */
|
||||
#ifdef FX9860G
|
||||
#if defined(FX9860G) && !defined(FX9860G_AS_CG)
|
||||
if(isSH3())
|
||||
{
|
||||
/* Try to map every ROM address up to _srom */
|
||||
|
@ -153,7 +149,7 @@ int start(int isappli, int optnum)
|
|||
regcpy(&lxyram, &sxyram, &rxyram);
|
||||
}
|
||||
|
||||
#ifdef FX9860G
|
||||
#if defined(FX9860G) && !defined(FX9860G_AS_CG)
|
||||
/* Copy permanently-mapped code to start of user RAM (on fx-CG 50 it
|
||||
is loaded along ILRAM contents) */
|
||||
void *rgmapped = mmu_uram();
|
||||
|
@ -179,20 +175,14 @@ int start(int isappli, int optnum)
|
|||
hosted user application, which has its own constructors and
|
||||
destructors to work with. */
|
||||
|
||||
while(1)
|
||||
{
|
||||
/* Here, we use exit() to allow the standard library to do
|
||||
what it wants in exit() after main() finishes executing */
|
||||
if(!setjmp(gint_exitbuf)) {
|
||||
callarray(&bctors, &ectors);
|
||||
exit(main(isappli, optnum));
|
||||
}
|
||||
else {
|
||||
callarray(&bdtors, &edtors);
|
||||
}
|
||||
|
||||
if(!gint_restart) break;
|
||||
gint_osmenu();
|
||||
/* Here, we use exit() to allow the standard library to do
|
||||
what it wants in exit() after main() finishes executing */
|
||||
if(!setjmp(gint_exitbuf)) {
|
||||
callarray(&bctors, &ectors);
|
||||
exit(main(isappli, optnum));
|
||||
}
|
||||
else {
|
||||
callarray(&bdtors, &edtors);
|
||||
}
|
||||
|
||||
/* Before leaving the application, we need to clean everything we
|
||||
|
@ -206,6 +196,18 @@ int start(int isappli, int optnum)
|
|||
return gint_exitcode;
|
||||
}
|
||||
|
||||
GSECTION(".text.entry")
|
||||
int start(int isappli, int optnum)
|
||||
{
|
||||
int rc;
|
||||
while(1) {
|
||||
rc = start2(isappli, optnum);
|
||||
if(!gint_restart) break;
|
||||
gint_osmenu_native();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Standard _Exit, used by the fxlibc exit() to leave control */
|
||||
void _Exit(int rc)
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
/* Bfile driver */
|
||||
.global _BFile_Remove
|
||||
.global _BFile_Rename
|
||||
.global _BFile_Create
|
||||
.global _BFile_Open
|
||||
.global _BFile_Close
|
||||
|
@ -41,6 +42,10 @@
|
|||
.global ___ClearKeyBuffer
|
||||
.global ___GetVRAMAddress
|
||||
.global ___ConfigureStatusArea
|
||||
.global ___SetQuitHandler
|
||||
|
||||
/* Reset */
|
||||
.global ___Reset
|
||||
|
||||
#define syscall_(id, syscall_table) \
|
||||
mov.l syscall_table, r2 ;\
|
||||
|
@ -110,6 +115,13 @@ ___ClearKeyBuffer:
|
|||
syscall(0x241)
|
||||
___GetVRAMAddress:
|
||||
syscall(0x135)
|
||||
___SetQuitHandler:
|
||||
syscall(0x494)
|
||||
|
||||
/* Reset */
|
||||
|
||||
___Reset:
|
||||
syscall(0x236)
|
||||
|
||||
syscall_table:
|
||||
.long 0x80010070
|
||||
|
@ -131,6 +143,8 @@ ___realloc:
|
|||
|
||||
_BFile_Remove:
|
||||
syscall(0x1db4)
|
||||
_BFile_Rename:
|
||||
syscall(0x1db3)
|
||||
_BFile_Create:
|
||||
syscall(0x1dae)
|
||||
_BFile_Open:
|
||||
|
@ -175,11 +189,18 @@ ___GetVRAMAddress:
|
|||
syscall(0x1e6)
|
||||
___ConfigureStatusArea:
|
||||
syscall(0x2b7)
|
||||
___SetQuitHandler:
|
||||
syscall(0x1e6e)
|
||||
|
||||
.global ___SpecialMatrixCodeProcessing
|
||||
___SpecialMatrixCodeProcessing:
|
||||
syscall(0x1e60)
|
||||
|
||||
/* Reset */
|
||||
|
||||
___Reset:
|
||||
syscall(0x1187)
|
||||
|
||||
syscall_table:
|
||||
.long 0x80020070
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ int gint_world_switch(gint_call_t call)
|
|||
if(canary)
|
||||
*canary = 0xb7c0ffee;
|
||||
|
||||
int rc = call.function ? gint_call(call) : 0;
|
||||
int rc = gint_call(call);
|
||||
|
||||
/* The canary check needs to occur before switching in the gint world;
|
||||
otherwise we just crash due to the overflow. gint_panic() isn't
|
||||
|
|
|
@ -35,7 +35,7 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
|
|||
|
||||
while(1)
|
||||
{
|
||||
e = keydev_read(d);
|
||||
e = keydev_read(d, true, timeout);
|
||||
if(e.type == KEYEV_NONE && timeout && *timeout) break;
|
||||
|
||||
/* Skip repeat events that are not enabled by options */
|
||||
|
@ -51,7 +51,8 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
|
|||
#ifdef FX9860G
|
||||
/* Backlight toggle */
|
||||
if((opt & GETKEY_BACKLIGHT) && e.type == KEYEV_DOWN &&
|
||||
e.key == KEY_OPTN && e.shift && !e.alpha)
|
||||
((e.key == KEY_LIGHT) ||
|
||||
(e.key == KEY_OPTN && e.shift && !e.alpha)))
|
||||
{
|
||||
t6k11_backlight(-1);
|
||||
continue;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/mpu/pfc.h>
|
||||
#include <gint/hardware.h>
|
||||
|
||||
/* This file is SH7705-only. */
|
||||
#ifdef FX9860G
|
||||
|
@ -54,7 +55,6 @@ static void iokbd_delay(void)
|
|||
uint8_t iokbd_row(int row)
|
||||
{
|
||||
if((unsigned)row > 9) return 0x00;
|
||||
row ^= 1;
|
||||
|
||||
int orig_PBCR = PFC.PBCR;
|
||||
int orig_PMCR = PFC.PMCR;
|
||||
|
@ -111,12 +111,55 @@ uint8_t iokbd_row(int row)
|
|||
return input;
|
||||
}
|
||||
|
||||
static const uint16_t SLIM_SC[] = {
|
||||
0x0940, 0x0920, 0x0910, 0x0908,
|
||||
0x0840, 0x0820, 0x0810, 0x0808, 0x0804, 0x0802,
|
||||
0x0740, 0x0720, 0x0710, 0x0708, 0x0704, 0x0702,
|
||||
0x0640, 0x0620, 0x0610, 0x0608, 0x0604, 0x0602,
|
||||
0x0540, 0x0520, 0x0510, 0x0508, 0x0504, 0x0502,
|
||||
0x0440, 0x0420, 0x0410, 0x0408, 0x0404, 0x0402,
|
||||
0x0340, 0x0320, 0x0310, 0x0308, 0x0304, 0x0302,
|
||||
0x0240, 0x0220, 0x0210, 0x0208, 0x0204, 0x0202,
|
||||
0x0120, 0x0110, 0x0108, 0x0104, 0x0102,
|
||||
0x0001
|
||||
};
|
||||
|
||||
#define SCANCODE_COUNT (sizeof(SLIM_SC) / sizeof(uint16_t))
|
||||
|
||||
static const uint16_t SLIM_TR[] = {
|
||||
0x0808, 0x0640, 0x0840, 0x0740,
|
||||
0x0940, 0x0620, 0x0720, 0x0710, 0x0804, 0x0802,
|
||||
0x0920, 0x0610, 0x0504, 0x0820, 0x0704, 0x0702,
|
||||
0x0910, 0x0608, 0x0502, 0x0810, 0x0280, 0x0180,
|
||||
0x0908, 0x0604, 0x0440, 0x0340, 0x0240, 0x0140,
|
||||
0x0904, 0x0602, 0x0420, 0x0320, 0x0220, 0x0120,
|
||||
0x0902, 0x0540, 0x0410, 0x0310, 0x0210, 0x0110,
|
||||
0x0708, 0x0520, 0x0408, 0x0308, 0x0208, 0x0108,
|
||||
0x0510, 0x0508, 0x0304, 0x0104, 0x0204,
|
||||
0x0001
|
||||
};
|
||||
|
||||
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data */
|
||||
void iokbd_scan(uint8_t *scan)
|
||||
{
|
||||
/* Scan each row independently; the gain from scanning them altogether
|
||||
/* Scan each row independently; the gain from scanning them all together
|
||||
is probably not worth it */
|
||||
for(int i = 0; i < 12; i++) scan[i] = iokbd_row(i);
|
||||
|
||||
/* Translate fx-9860G Slim scancodes to standard scancodes */
|
||||
if(isSlim())
|
||||
{
|
||||
uint8_t slim_scan[12];
|
||||
for(uint i = 0; i < 10; i++)
|
||||
{
|
||||
slim_scan[i] = scan[i];
|
||||
scan[i] = 0x00;
|
||||
}
|
||||
|
||||
for(uint i = 0; i < SCANCODE_COUNT; i++)
|
||||
if(slim_scan[SLIM_SC[i] >> 8] & (SLIM_SC[i] & 0xFF))
|
||||
scan[SLIM_TR[i] >> 8] |= (SLIM_TR[i] & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* FX9860G */
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//---
|
||||
|
||||
#include <gint/keyboard.h>
|
||||
#include <gint/cpu.h>
|
||||
#include <gint/drivers/keydev.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/util.h>
|
||||
|
@ -30,6 +31,9 @@ static int standard_repeater(GUNUSED int key, GUNUSED int duration, int count)
|
|||
Returns false if the event cannot be pushed. */
|
||||
bool keydev_queue_push(keydev_t *d, key_event_t ev)
|
||||
{
|
||||
if(d->async_filter && !d->async_filter(ev))
|
||||
return true;
|
||||
|
||||
int next = (d->queue_end + 1) % KEYBOARD_QUEUE_SIZE;
|
||||
if(next == d->queue_next)
|
||||
{
|
||||
|
@ -146,6 +150,16 @@ void keydev_tick(keydev_t *d, uint us)
|
|||
}
|
||||
}
|
||||
|
||||
keydev_async_filter_t keydev_async_filter(keydev_t const *d)
|
||||
{
|
||||
return d->async_filter;
|
||||
}
|
||||
|
||||
void keydev_set_async_filter(keydev_t *d, keydev_async_filter_t filter)
|
||||
{
|
||||
d->async_filter = filter;
|
||||
}
|
||||
|
||||
//---
|
||||
// Keyboard event generation
|
||||
//---
|
||||
|
@ -191,6 +205,8 @@ key_event_t keydev_unqueue_event(keydev_t *d)
|
|||
|
||||
return ev;
|
||||
}
|
||||
__attribute__((alias("keydev_unqueue_event")))
|
||||
key_event_t _WEAK_keydev_unqueue_event(keydev_t *d);
|
||||
|
||||
/* keydev_keydown(): Check if a key is down according to generated events */
|
||||
bool keydev_keydown(keydev_t *d, int key)
|
||||
|
@ -200,6 +216,8 @@ bool keydev_keydown(keydev_t *d, int key)
|
|||
|
||||
return (d->state_queue[row] & col) != 0;
|
||||
}
|
||||
__attribute__((alias("keydev_keydown")))
|
||||
bool _WEAK_keydev_keydown(keydev_t *d, int key);
|
||||
|
||||
//---
|
||||
// Event transforms
|
||||
|
@ -239,7 +257,7 @@ void keydev_set_standard_repeats(keydev_t *d, int first, int next)
|
|||
}
|
||||
|
||||
/* keydev_read(): Retrieve the next transformed event */
|
||||
key_event_t keydev_read(keydev_t *d)
|
||||
key_event_t keydev_read(keydev_t *d, bool wait, volatile int *timeout)
|
||||
{
|
||||
#define opt(NAME) (d->tr.enabled & KEYDEV_TR_ ## NAME)
|
||||
key_event_t e;
|
||||
|
@ -248,7 +266,12 @@ key_event_t keydev_read(keydev_t *d)
|
|||
{
|
||||
e = keydev_unqueue_event(d);
|
||||
if(e.type == KEYEV_NONE)
|
||||
return e;
|
||||
{
|
||||
if(!wait || (timeout && *timeout))
|
||||
return e;
|
||||
sleep();
|
||||
continue;
|
||||
}
|
||||
|
||||
int k = e.key;
|
||||
e.mod = (opt(ALL_MODS) != 0);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <gint/drivers/iokbd.h>
|
||||
#include <gint/hardware.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Keyboard scan frequency in Hertz. Start with 128 Hz, this frequency *must
|
||||
be high* for the keyboard to work! Reading at low frequencies produces a lot
|
||||
|
@ -33,24 +33,32 @@ keydev_t *keydev_std(void)
|
|||
{
|
||||
return &keysc_dev;
|
||||
}
|
||||
__attribute__((alias("keydev_std")))
|
||||
keydev_t *_WEAK_keydev_std(void);
|
||||
|
||||
/* keysc_scan(): Scand the keyboard */
|
||||
static void keysc_scan(uint8_t *scan)
|
||||
{
|
||||
if(isSH3())
|
||||
{
|
||||
iokbd_scan(scan);
|
||||
return;
|
||||
}
|
||||
|
||||
volatile uint16_t *KEYSC = (void *)0xa44b0000;
|
||||
|
||||
for(int i = 0; i < 6; i++) {
|
||||
int data = KEYSC[i];
|
||||
scan[2*i] = data & 0xff;
|
||||
scan[2*i+1] = data >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* keysc_tick(): Update the keyboard to the next state */
|
||||
static int keysc_tick(void)
|
||||
{
|
||||
GALIGNED(2) uint8_t scan[12] = { 0 };
|
||||
|
||||
/* Scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */
|
||||
if(isSH3()) iokbd_scan(scan);
|
||||
else
|
||||
{
|
||||
volatile uint16_t *KEYSC = (void *)0xa44b0000;
|
||||
uint16_t *array = (void *)&scan;
|
||||
|
||||
for(int i = 0; i < 6; i++) {
|
||||
array[i] = KEYSC[i];
|
||||
array[i] = (array[i] << 8) | (array[i] >> 8);
|
||||
}
|
||||
}
|
||||
uint8_t scan[12] = { 0 };
|
||||
keysc_scan(scan);
|
||||
|
||||
keydev_process_state(&keysc_dev, scan);
|
||||
keydev_tick(&keysc_dev, keysc_scan_us);
|
||||
|
@ -99,6 +107,11 @@ static void configure(void)
|
|||
{
|
||||
keydev_init(&keysc_dev);
|
||||
|
||||
/* Do a first scan to load the initial state (so that keys that are
|
||||
pressed at startup are not registered as new presses) */
|
||||
keysc_scan(keysc_dev.state_now);
|
||||
memcpy(keysc_dev.state_queue, keysc_dev.state_now, 12);
|
||||
|
||||
/* Set the default repeat to 400/40 ms */
|
||||
keydev_t *d = keydev_std();
|
||||
keydev_set_transform(d, (keydev_transform_t){ KEYDEV_TR_REPEATS, NULL });
|
||||
|
|
|
@ -178,6 +178,25 @@ static block_t *best_fit(block_t *list, size_t size)
|
|||
return best_match;
|
||||
}
|
||||
|
||||
/* Find a worst fit in the list */
|
||||
static block_t *worst_fit(block_t *list)
|
||||
{
|
||||
block_t *best_match = NULL;
|
||||
size_t best_size = 0;
|
||||
|
||||
while(list)
|
||||
{
|
||||
if(list->size >= best_size)
|
||||
{
|
||||
best_match = list;
|
||||
best_size = list->size;
|
||||
}
|
||||
list = next_link(list);
|
||||
}
|
||||
|
||||
return best_match;
|
||||
}
|
||||
|
||||
//---
|
||||
// Index-level operations
|
||||
//---
|
||||
|
@ -240,7 +259,7 @@ static void *gint_malloc(size_t size, void *data)
|
|||
int c = size_class(size);
|
||||
|
||||
/* Try to find a class that has a free block available */
|
||||
block_t *alloc;
|
||||
block_t *alloc = NULL;
|
||||
for(; c <= 15; c++)
|
||||
{
|
||||
block_t *list = index->classes[c];
|
||||
|
@ -366,6 +385,35 @@ static void *gint_realloc(void *ptr, size_t size, void *data)
|
|||
return new_ptr;
|
||||
}
|
||||
|
||||
static void *gint_malloc_max(size_t *size, void *data)
|
||||
{
|
||||
index_t *index = data;
|
||||
stats_t *s = index->stats;
|
||||
|
||||
/* Find the largest available block in the largest possible class */
|
||||
block_t *alloc;
|
||||
for(int c = 15; c >= 0; c--)
|
||||
{
|
||||
block_t *list = index->classes[c];
|
||||
alloc = (c < 14) ? list : worst_fit(list);
|
||||
if(alloc) break;
|
||||
}
|
||||
if(!alloc) return NULL;
|
||||
|
||||
remove_link(alloc, index);
|
||||
|
||||
/* Mark the block as allocated and return it */
|
||||
block_t *next = next_block(alloc);
|
||||
alloc->used = true;
|
||||
if(next) next->previous_used = true;
|
||||
|
||||
if(s) s->used_memory += alloc->size;
|
||||
if(s) s->peak_used_memory = max(s->peak_used_memory, s->used_memory);
|
||||
|
||||
*size = alloc->size;
|
||||
return (void *)alloc + sizeof(block_t);
|
||||
}
|
||||
|
||||
/* kmalloc_init_arena(): Initialize an arena with gint's allocator */
|
||||
void kmalloc_init_arena(kmalloc_arena_t *a, bool enable_statistics)
|
||||
{
|
||||
|
@ -375,6 +423,7 @@ void kmalloc_init_arena(kmalloc_arena_t *a, bool enable_statistics)
|
|||
a->malloc = gint_malloc;
|
||||
a->free = gint_free;
|
||||
a->realloc = gint_realloc;
|
||||
a->malloc_max = gint_malloc_max;
|
||||
|
||||
/* The index is located at the very start of the arena */
|
||||
index_t *index = a->start;
|
||||
|
|
|
@ -112,6 +112,16 @@ void *krealloc(void *ptr, size_t size)
|
|||
else
|
||||
{
|
||||
a->stats.total_failures++;
|
||||
|
||||
/* If reallocation within the original arena fails, try another
|
||||
one. The memory copy behavior is sub-optimal (we copy the
|
||||
new size which might be more than the original size) but
|
||||
it's all we can do with this arena interface. */
|
||||
rc = kmalloc(size, NULL);
|
||||
if(rc)
|
||||
{
|
||||
memcpy(rc, ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
@ -131,6 +141,36 @@ void kfree(void *ptr)
|
|||
a->stats.live_blocks--;
|
||||
}
|
||||
|
||||
/* kmalloc_max(): Allocate the largest block available in an arena */
|
||||
void *kmalloc_max(size_t *size, char const *name)
|
||||
{
|
||||
for(int i = 0; i < KMALLOC_ARENA_MAX; i++) if(arenas[i])
|
||||
{
|
||||
kmalloc_arena_t *a = arenas[i];
|
||||
if(strcmp(a->name, name)) continue;
|
||||
|
||||
void *rc = a->malloc_max ? a->malloc_max(size, a->data) : NULL;
|
||||
|
||||
/* Maintain statistics */
|
||||
struct kmalloc_stats *s = &a->stats;
|
||||
if(rc)
|
||||
{
|
||||
s->live_blocks++;
|
||||
s->peak_live_blocks = max(s->live_blocks,
|
||||
s->peak_live_blocks);
|
||||
s->total_volume += *size;
|
||||
s->total_blocks++;
|
||||
return rc;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->total_failures++;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* kmalloc_add_arena(): Add a new arena to the heap source */
|
||||
bool kmalloc_add_arena(kmalloc_arena_t *arena)
|
||||
{
|
||||
|
|
|
@ -129,21 +129,18 @@ uint32_t tlb_translate(uint32_t page, uint32_t *size)
|
|||
// SH7305 Unified TLB
|
||||
//---
|
||||
|
||||
/* utlb_addr() - get the P4 address of a UTLB address entry */
|
||||
GINLINE const utlb_addr_t *utlb_addr(uint E)
|
||||
{
|
||||
uint32_t addr = 0xf6000000 | ((E & 0x3f) << 8);
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
/* utlb_data() - get the P4 address of a UTLB data entry */
|
||||
GINLINE const utlb_data_t *utlb_data(uint E)
|
||||
{
|
||||
uint32_t addr = 0xf7000000 | ((E & 0x3f) << 8);
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
/* utlb_mapped_memory() - count amount of mapped memory */
|
||||
void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
|
||||
{
|
||||
uint32_t rom = 0, ram = 0;
|
||||
|
@ -170,7 +167,6 @@ void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
|
|||
gint[HWURAM] = ram;
|
||||
}
|
||||
|
||||
/* utlb_translate(): Get the physical address for a virtual page */
|
||||
uint32_t utlb_translate(uint32_t page, uint32_t *size)
|
||||
{
|
||||
for(int E = 0; E < 64; E++)
|
||||
|
@ -191,6 +187,18 @@ uint32_t utlb_translate(uint32_t page, uint32_t *size)
|
|||
return -1;
|
||||
}
|
||||
|
||||
itlb_addr_t const *itlb_addr(uint E)
|
||||
{
|
||||
uint32_t addr = 0xf2000000 | ((E & 3) << 8);
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
itlb_data_t const *itlb_data(uint E)
|
||||
{
|
||||
uint32_t addr = 0xf3000000 | ((E & 3) << 8);
|
||||
return (void *)addr;
|
||||
}
|
||||
|
||||
static void configure(void)
|
||||
{
|
||||
/* Make writes to the control register area synchronous; this is needed
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/dma.h>
|
||||
|
||||
/* dclear() - fill the screen with a single color */
|
||||
void dclear(uint16_t color)
|
||||
{
|
||||
dma_memset(gint_vram, (color << 16) | color, 396 * 224 * 2);
|
||||
return;
|
||||
bool full_width = (dwindow.left == 0 && dwindow.right == DWIDTH);
|
||||
bool dma_aligned = !(dwindow.top & 3) && !(dwindow.bottom & 3);
|
||||
|
||||
if(full_width && dma_aligned) {
|
||||
uint16_t *vram = gint_vram + DWIDTH * dwindow.top;
|
||||
int size_bytes = DWIDTH * (dwindow.bottom - dwindow.top) * 2;
|
||||
dma_memset(vram, (color << 16) | color, size_bytes);
|
||||
}
|
||||
else {
|
||||
drect(dwindow.left, dwindow.top, dwindow.right - 1,
|
||||
dwindow.bottom - 1, color);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#include <gint/display.h>
|
||||
|
||||
int dgetpixel(int x, int y)
|
||||
{
|
||||
if((uint)x >= DWIDTH || (uint)y >= DHEIGHT) return -1;
|
||||
return gint_vram[DWIDTH * y + x];
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
#include <gint/display.h>
|
||||
|
||||
/* dpixel() - change a pixel's color */
|
||||
void dpixel(int x, int y, int color)
|
||||
{
|
||||
/* Coordinate checks */
|
||||
if((uint)x >= 396 || (uint)y >= 224 || color == C_NONE) return;
|
||||
int index = 396 * y + x;
|
||||
if(x < dwindow.left || x >= dwindow.right) return;
|
||||
if(y < dwindow.top || y >= dwindow.bottom) return;
|
||||
if(color == C_NONE) return;
|
||||
|
||||
int index = DWIDTH * y + x;
|
||||
|
||||
if(color == C_INVERT) gint_vram[index] ^= 0xffff;
|
||||
else gint_vram[index] = color;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include <gint/defs/util.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
/* drect() - fill a rectangle of the screen */
|
||||
void drect(int x1, int y1, int x2, int y2, int color)
|
||||
{
|
||||
if(color == C_NONE) return;
|
||||
|
@ -9,17 +8,19 @@ void drect(int x1, int y1, int x2, int y2, int color)
|
|||
if(x1 > x2) swap(x1, x2);
|
||||
if(y1 > y2) swap(y1, y2);
|
||||
|
||||
/* Order and bounds */
|
||||
if(x1 >= 396 || x2 < 0 || y1 >= 224 || y2 < 0) return;
|
||||
if(x1 < 0) x1 = 0;
|
||||
if(x2 >= 396) x2 = 395;
|
||||
if(y1 < 0) y1 = 0;
|
||||
if(y2 >= 224) y2 = 223;
|
||||
/* Rectangle is completely outside the rendering window */
|
||||
if(x1 >= dwindow.right || x2 < dwindow.left) return;
|
||||
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
|
||||
/* Clipping */
|
||||
x1 = max(x1, dwindow.left);
|
||||
x2 = min(x2, dwindow.right - 1);
|
||||
y1 = max(y1, dwindow.top);
|
||||
y2 = min(y2, dwindow.bottom - 1);
|
||||
|
||||
/* The method is exactly like dhline(). I first handle odd endpoints,
|
||||
then write longwords for the longest section */
|
||||
|
||||
uint16_t *base = gint_vram + 396 * y1;
|
||||
uint16_t *base = gint_vram + DWIDTH * y1;
|
||||
int height = y2 - y1 + 1;
|
||||
|
||||
/* Now copy everything that's left as longwords */
|
||||
|
@ -33,18 +34,19 @@ void drect(int x1, int y1, int x2, int y2, int color)
|
|||
|
||||
if(color == C_INVERT) for(int h = height; h; h--)
|
||||
{
|
||||
base[x1] ^= 0xffff;
|
||||
base[x2] ^= 0xffff;
|
||||
/* We can't double-draw on base[x1] and base[x2] here */
|
||||
if(x1 & 1) base[x1] ^= 0xffff;
|
||||
if(!(x2 & 1)) base[x2] ^= 0xffff;
|
||||
for(int w = 0; w < width; w++) v[w] ^= 0xffffffff;
|
||||
v += 198;
|
||||
base += 396;
|
||||
v += DWIDTH / 2;
|
||||
base += DWIDTH;
|
||||
}
|
||||
else for(int h = height; h; h--)
|
||||
{
|
||||
base[x1] = color;
|
||||
base[x2] = color;
|
||||
for(int w = 0; w < width; w++) v[w] = op;
|
||||
v += 198;
|
||||
base += 396;
|
||||
v += DWIDTH / 2;
|
||||
base += DWIDTH;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,10 @@ void dupdate(void)
|
|||
|
||||
r61524_display(gint_vram, 0, 224, method);
|
||||
|
||||
gint_call_t hook = dupdate_get_hook();
|
||||
if(hook.function) gint_call(hook);
|
||||
gint_call(dupdate_get_hook());
|
||||
|
||||
/* Switch buffers if triple buffering is enabled */
|
||||
dvram_switch();
|
||||
}
|
||||
__attribute__((alias("dupdate")))
|
||||
void _WEAK_dupdate(void);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <gint/display.h>
|
||||
#include <stdlib.h>
|
||||
#include <gint/kmalloc.h>
|
||||
#include <gint/config.h>
|
||||
|
||||
/* Up to two VRAM pointers can be set, for triple buffering. */
|
||||
static uint16_t *vram_1 = NULL, *vram_2 = NULL;
|
||||
|
@ -11,9 +12,15 @@ bool dvram_init(void)
|
|||
int const MARGIN = 32;
|
||||
|
||||
/* Leave MARGIN bytes on each side of the region; this enables some
|
||||
important optimisations in the image renderer. We also add another
|
||||
important optimizations in the image renderer. We also add another
|
||||
32 bytes so we can manually 32-align the region */
|
||||
uint32_t region = (uint32_t)malloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32);
|
||||
uint32_t region = (uint32_t)kmalloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32,
|
||||
#if !defined(GINT_NO_OS_STACK)
|
||||
"_ostk"
|
||||
#else
|
||||
NULL
|
||||
#endif
|
||||
);
|
||||
if(region == 0)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -4,14 +4,13 @@
|
|||
/* gint_dhline(): Optimized horizontal line */
|
||||
void gint_dhline(int x1, int x2, int y, uint16_t color)
|
||||
{
|
||||
/* Order and bounds */
|
||||
if((uint)y >= 224) return;
|
||||
if(y < dwindow.top || y >= dwindow.bottom) return;
|
||||
if(x1 > x2) swap(x1, x2);
|
||||
if(x1 >= 396 || x2 < 0) return;
|
||||
if(x1 < 0) x1 = 0;
|
||||
if(x2 >= 396) x2 = 395;
|
||||
if(x1 >= dwindow.right || x2 < dwindow.left) return;
|
||||
x1 = max(x1, dwindow.left);
|
||||
x2 = min(x2, dwindow.right - 1);
|
||||
|
||||
int offset = 396 * y;
|
||||
int offset = DWIDTH * y;
|
||||
|
||||
/* Use longwords to do the copy, but first paint the endpoints to heed
|
||||
for odd x1 and x2. Checking the parity may be a waste of time. */
|
||||
|
@ -33,15 +32,14 @@ void gint_dhline(int x1, int x2, int y, uint16_t color)
|
|||
/* gint_dvline(): Optimized vertical line */
|
||||
void gint_dvline(int y1, int y2, int x, uint16_t color)
|
||||
{
|
||||
/* Order and bounds */
|
||||
if((uint)x >= 396) return;
|
||||
if(x < dwindow.left || x >= dwindow.right) return;
|
||||
if(y1 > y2) swap(y1, y2);
|
||||
if(y1 < 0) y1 = 0;
|
||||
if(y2 >= 224) y2 = 223;
|
||||
y1 = max(y1, dwindow.top);
|
||||
y2 = min(y2, dwindow.bottom - 1);
|
||||
|
||||
uint16_t *v = gint_vram + 396 * y1 + x;
|
||||
uint16_t *v = gint_vram + DWIDTH * y1 + x;
|
||||
int height = y2 - y1 + 1;
|
||||
|
||||
while(height-- > 0) *v = color, v += 396;
|
||||
while(height-- > 0) *v = color, v += DWIDTH;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include <gint/image.h>
|
||||
#include <gint/display.h>
|
||||
#include <gint/defs/util.h>
|
||||
|
||||
bool gint_image_clip_input(image_t const *img, struct gint_image_box *b,
|
||||
int out_w, int out_h)
|
||||
struct dwindow const *window)
|
||||
{
|
||||
/* Adjust the bounding box of the input image */
|
||||
if(b->left < 0) b->w += b->left, b->x -= b->left, b->left = 0;
|
||||
|
@ -13,37 +14,50 @@ bool gint_image_clip_input(image_t const *img, struct gint_image_box *b,
|
|||
/* Check whether the box intersects the screen */
|
||||
if(b->w <= 0 || b->h <= 0)
|
||||
return false;
|
||||
if(b->x + b->w <= 0 || b->x >= out_w)
|
||||
if(b->x + b->w <= window->left || b->x >= window->right)
|
||||
return false;
|
||||
if(b->y + b->h <= 0 || b->y >= out_h)
|
||||
if(b->y + b->h <= window->top || b->y >= window->bottom)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void gint_image_clip_output(struct gint_image_box *b, int out_w, int out_h)
|
||||
void gint_image_clip_output(struct gint_image_box *b,
|
||||
struct dwindow const *window)
|
||||
{
|
||||
/* Intersect with the bounding box on-screen */
|
||||
if(b->y < 0) b->top -= b->y, b->h += b->y, b->y = 0;
|
||||
if(b->y + b->h > out_h) b->h = (out_h - b->y);
|
||||
if(b->x < 0) b->left -= b->x, b->w += b->x, b->x = 0;
|
||||
if(b->x + b->w > out_w) b->w = (out_w - b->x);
|
||||
|
||||
if(b->y < window->top) {
|
||||
int d = window->top - b->y; /* > 0 */
|
||||
b->top += d;
|
||||
b->h -= d;
|
||||
b->y += d;
|
||||
}
|
||||
b->h = min(b->h, window->bottom - b->y);
|
||||
|
||||
if(b->x < window->left) {
|
||||
int d = window->left - b->x; /* > 0 */
|
||||
b->left += d;
|
||||
b->w -= d;
|
||||
b->x += d;
|
||||
}
|
||||
b->w = min(b->w, window->right - b->x);
|
||||
}
|
||||
|
||||
bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img,
|
||||
int effects, bool left_edge, bool right_edge,
|
||||
struct gint_image_cmd *cmd, int out_width, int out_height)
|
||||
struct gint_image_cmd *cmd, struct dwindow const *window)
|
||||
{
|
||||
/* Convert the old DIMAGE_NOCLIP flag */
|
||||
if(effects & DIMAGE_NOCLIP)
|
||||
effects |= IMAGE_NOCLIP;
|
||||
|
||||
if(!(effects & IMAGE_NOCLIP_INPUT)) {
|
||||
if(!gint_image_clip_input(img, box, out_width, out_height))
|
||||
if(!gint_image_clip_input(img, box, window))
|
||||
return false;
|
||||
}
|
||||
if(!(effects & IMAGE_NOCLIP_OUTPUT))
|
||||
gint_image_clip_output(box, out_width, out_height);
|
||||
gint_image_clip_output(box, window);
|
||||
|
||||
cmd->effect = (effects & (IMAGE_VFLIP | IMAGE_HFLIP)) >> 8;
|
||||
cmd->columns = box->w;
|
||||
|
|
|
@ -16,8 +16,8 @@ void dsubimage_p4(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.loop = gint_image_p4_normal;
|
||||
gint_image_p4_loop(DWIDTH, &cmd);
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ void dsubimage_p4_clearbg(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = bg_color;
|
||||
cmd.loop = gint_image_p4_clearbg;
|
||||
gint_image_p4_loop(DWIDTH, &cmd);
|
||||
|
|
|
@ -13,8 +13,8 @@ void dsubimage_p4_clearbg_alt(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = bg_color;
|
||||
cmd.loop = gint_image_p4_clearbg_alt;
|
||||
gint_image_p4_loop(DWIDTH, &cmd);
|
||||
|
|
|
@ -13,8 +13,8 @@ void dsubimage_p4_dye(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = image_alpha(img->format);
|
||||
cmd.color_2 = dye_color;
|
||||
cmd.loop = gint_image_p4_dye;
|
||||
|
|
|
@ -14,8 +14,8 @@ void dsubimage_p4_swapcolor(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = old_index;
|
||||
cmd.color_2 = new_color;
|
||||
cmd.loop = gint_image_p4_swapcolor;
|
||||
|
@ -35,8 +35,8 @@ void dsubimage_p4_addbg(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = image_alpha(img->format);
|
||||
cmd.color_2 = bg_color;
|
||||
cmd.loop = gint_image_p4_swapcolor;
|
||||
|
|
|
@ -16,8 +16,8 @@ void dsubimage_p8(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.loop = gint_image_p8_normal;
|
||||
gint_image_p8_loop(DWIDTH, &cmd);
|
||||
}
|
||||
|
@ -33,8 +33,8 @@ void dsubimage_p8_clearbg(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = bg_color;
|
||||
cmd.loop = gint_image_p8_clearbg;
|
||||
gint_image_p8_loop(DWIDTH, &cmd);
|
||||
|
|
|
@ -13,8 +13,8 @@ void dsubimage_p8_dye(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = image_alpha(img->format);
|
||||
cmd.color_2 = dye_color;
|
||||
cmd.loop = gint_image_p8_dye;
|
||||
|
|
|
@ -14,8 +14,8 @@ void dsubimage_p8_swapcolor(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = old_index;
|
||||
cmd.color_2 = new_color;
|
||||
cmd.loop = gint_image_p8_swapcolor;
|
||||
|
@ -35,8 +35,8 @@ void dsubimage_p8_addbg(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = image_alpha(img->format);
|
||||
cmd.color_2 = bg_color;
|
||||
cmd.loop = gint_image_p8_swapcolor;
|
||||
|
|
|
@ -16,8 +16,8 @@ void dsubimage_rgb16(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.loop = gint_image_rgb16_normal;
|
||||
gint_image_rgb16_loop(DWIDTH, &cmd);
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ void dsubimage_rgb16_clearbg(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = bg_color;
|
||||
cmd.loop = gint_image_rgb16_clearbg;
|
||||
gint_image_rgb16_loop(DWIDTH, &cmd);
|
||||
|
|
|
@ -13,8 +13,8 @@ void dsubimage_rgb16_dye(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = image_alpha(img->format);
|
||||
cmd.color_2 = dye_color;
|
||||
cmd.loop = gint_image_rgb16_dye;
|
||||
|
|
|
@ -14,8 +14,8 @@ void dsubimage_rgb16_swapcolor(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = old_color;
|
||||
cmd.color_2 = new_color;
|
||||
cmd.loop = gint_image_rgb16_swapcolor;
|
||||
|
@ -35,8 +35,8 @@ void dsubimage_rgb16_addbg(int x, int y, image_t const *img,
|
|||
struct gint_image_box box = { x, y, w, h, left, top };
|
||||
struct gint_image_cmd cmd;
|
||||
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
|
||||
DHEIGHT)) return;
|
||||
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
|
||||
return;
|
||||
cmd.color_1 = image_alpha(img->format);
|
||||
cmd.color_2 = bg_color;
|
||||
cmd.loop = gint_image_rgb16_swapcolor;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
#include <string.h>
|
||||
|
@ -8,9 +9,16 @@
|
|||
#include "topti-asm.h"
|
||||
|
||||
/* Default font */
|
||||
extern font_t gint_font8x9;
|
||||
font_t const * gint_default_font = &gint_font8x9;
|
||||
font_t const * topti_font = &gint_font8x9;
|
||||
#if(FX9860G_AS_CG)
|
||||
extern font_t gint_font5x7;
|
||||
font_t const * gint_default_font = &gint_font5x7;
|
||||
font_t const * topti_font = &gint_font5x7;
|
||||
#else
|
||||
extern font_t gint_font8x9;
|
||||
font_t const * gint_default_font = &gint_font8x9;
|
||||
font_t const * topti_font = &gint_font8x9;
|
||||
#endif
|
||||
|
||||
|
||||
/* topti_glyph(): Render a glyph on the VRAM
|
||||
Prints a glyph naively using word accesses, because for most fonts with a
|
||||
|
@ -56,12 +64,20 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
int height = f->data_height, top = 0;
|
||||
|
||||
/* Vertical clipping */
|
||||
if(x > 395 || y > 223 || y + height <= 0) return;
|
||||
if(y + height > 224) height = 224 - y;
|
||||
if(y < 0) top = -y, height += y, y = 0;
|
||||
if(x >= dwindow.right || y >= dwindow.bottom) return;
|
||||
if(y + height <= dwindow.top) return;
|
||||
|
||||
int top_overflow = y - dwindow.top;
|
||||
if(top_overflow < 0) {
|
||||
top = -top_overflow;
|
||||
height += top_overflow;
|
||||
y -= top_overflow;
|
||||
}
|
||||
height = min(height, dwindow.bottom - y);
|
||||
if(height <= 0) return;
|
||||
|
||||
/* Move to top row */
|
||||
uint16_t *target = gint_vram + 396 * y;
|
||||
uint16_t *target = gint_vram + DWIDTH * y;
|
||||
|
||||
/* Character spacing waiting to be drawn, in pixels */
|
||||
int space = 0;
|
||||
|
@ -80,7 +96,7 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
/* Draw character spacing if background is opaque */
|
||||
if(space && bg >= 0) drect(x, y, x+space-1, y+height-1, bg);
|
||||
x += space;
|
||||
if(x >= 396) break;
|
||||
if(x >= dwindow.right) break;
|
||||
|
||||
int index = topti_offset(f, glyph);
|
||||
|
||||
|
@ -88,14 +104,17 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
|
||||
int width = dataw, left = 0;
|
||||
|
||||
if(x + dataw <= 0)
|
||||
if(x + dataw <= dwindow.left)
|
||||
{
|
||||
x += dataw;
|
||||
space = f->char_spacing;
|
||||
continue;
|
||||
}
|
||||
if(x < 0) left = -x, width += x;
|
||||
if(x + width > 396) width = 396 - x;
|
||||
if(x < dwindow.left) {
|
||||
left = dwindow.left - x;
|
||||
width -= left;
|
||||
}
|
||||
width = min(width, dwindow.right - x);
|
||||
|
||||
/* Render glyph */
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/display.h>
|
||||
#include "render-fx.h"
|
||||
#include "bopti-asm.h"
|
||||
|
@ -271,18 +272,29 @@ int bopti_clip(bopti_image_t const *img, struct rbox *r)
|
|||
int x = r->visual_x, y = r->y;
|
||||
int left = r->left, top = r->top;
|
||||
int width = r->width, height = r->height;
|
||||
int diff;
|
||||
|
||||
/* Adjust the bounding box of the input image */
|
||||
if(left < 0) width += left, x -= left, left = 0;
|
||||
if(top < 0) height += top, y -= top, top = 0;
|
||||
if(left + width > img->width) width = img->width - left;
|
||||
if(top + height > img->height) height = img->height - top;
|
||||
width = min(width, img->width - left);
|
||||
height = min(height, img->height - top);
|
||||
|
||||
/* Intersect with the bounding box on-screen */
|
||||
if(x < 0) width += x, left -= x, x = 0;
|
||||
if(y < 0) height += y, top -= y, y = 0;
|
||||
if(x + width > DWIDTH) width = DWIDTH - x;
|
||||
if(y + height > DHEIGHT) height = DHEIGHT - y;
|
||||
if((diff = dwindow.left - x) > 0)
|
||||
{
|
||||
width -= diff;
|
||||
left += diff;
|
||||
x += diff;
|
||||
}
|
||||
if((diff = dwindow.top - y) > 0)
|
||||
{
|
||||
height -= diff;
|
||||
top += diff;
|
||||
y += diff;
|
||||
}
|
||||
width = min(width, dwindow.right - x);
|
||||
height = min(height, dwindow.bottom - y);
|
||||
|
||||
r->visual_x = x;
|
||||
r->y = y;
|
||||
|
|
|
@ -4,6 +4,12 @@
|
|||
/* dclear() - fill the screen with a single color */
|
||||
void dclear(color_t color)
|
||||
{
|
||||
if(dwindow.left != 0 || dwindow.right != DWIDTH) {
|
||||
drect(dwindow.left, dwindow.top, dwindow.right - 1,
|
||||
dwindow.bottom - 1, color);
|
||||
return;
|
||||
}
|
||||
|
||||
DMODE_OVERRIDE(dclear, color);
|
||||
|
||||
/* SuperH only supports a single write-move addressing mode, which is
|
||||
|
@ -13,9 +19,10 @@ void dclear(color_t color)
|
|||
if(color != C_WHITE && color != C_BLACK) return;
|
||||
uint32_t fill = -(color >> 1);
|
||||
|
||||
uint32_t *index = gint_vram + 256;
|
||||
uint32_t *start = gint_vram + 4 * dwindow.top;
|
||||
uint32_t *index = gint_vram + 4 * dwindow.bottom;
|
||||
|
||||
while(index > gint_vram)
|
||||
while(index > start)
|
||||
{
|
||||
/* Do it by batches to avoid losing cycles on loop tests */
|
||||
*--index = fill;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/defs/types.h>
|
||||
#include "render-fx.h"
|
||||
|
||||
int dgetpixel(int x, int y)
|
||||
{
|
||||
if(x < dwindow.left || x >= dwindow.right) return -1;
|
||||
if(y < dwindow.top || y >= dwindow.bottom) return -1;
|
||||
|
||||
DMODE_OVERRIDE(dgetpixel, x, y);
|
||||
|
||||
int offset = (y << 2) + (x >> 5);
|
||||
uint32_t mask = 1 << (~x & 31);
|
||||
|
||||
return (gint_vram[offset] & mask) ? C_BLACK : C_WHITE;
|
||||
}
|
|
@ -5,8 +5,8 @@
|
|||
/* dpixel() - change a pixel's color */
|
||||
void dpixel(int x, int y, int color)
|
||||
{
|
||||
/* Sanity checks */
|
||||
if((uint)x >= 128 || (uint)y >= 64) return;
|
||||
if(x < dwindow.left || x >= dwindow.right) return;
|
||||
if(y < dwindow.top || y >= dwindow.bottom) return;
|
||||
|
||||
DMODE_OVERRIDE(dpixel, x, y, color);
|
||||
|
||||
|
|
|
@ -2,18 +2,19 @@
|
|||
#include <gint/display.h>
|
||||
#include "render-fx.h"
|
||||
|
||||
/* drect() - fill a rectangle of the screen */
|
||||
void drect(int x1, int y1, int x2, int y2, int color)
|
||||
{
|
||||
if(x1 > x2) swap(x1, x2);
|
||||
if(y1 > y2) swap(y1, y2);
|
||||
|
||||
/* Argument checking */
|
||||
if(x1 >= 128 || x2 < 0 || y1 >= 64 || y2 < 0) return;
|
||||
if(x1 < 0) x1 = 0;
|
||||
if(x2 >= 128) x2 = 127;
|
||||
if(y1 < 0) y1 = 0;
|
||||
if(y2 >= 64) y2 = 63;
|
||||
/* Rectangle is completely outside the rendering window */
|
||||
if(x1 >= dwindow.right || x2 < dwindow.left) return;
|
||||
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
|
||||
/* Clipping */
|
||||
x1 = max(x1, dwindow.left);
|
||||
x2 = min(x2, dwindow.right - 1);
|
||||
y1 = max(y1, dwindow.top);
|
||||
y2 = min(y2, dwindow.bottom - 1);
|
||||
|
||||
DMODE_OVERRIDE(drect, x1, y1, x2, y2, color);
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ void dupdate(void)
|
|||
t6k11_display(gint_vram, 0, 64, 16);
|
||||
}
|
||||
|
||||
gint_call_t hook = dupdate_get_hook();
|
||||
if(hook.function) gint_call(hook);
|
||||
gint_call(dupdate_get_hook());
|
||||
}
|
||||
__attribute__((alias("dupdate")))
|
||||
void _WEAK_dupdate(void);
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
/* gint_dhline(): Optimized horizontal line using a rectangle mask */
|
||||
void gint_dhline(int x1, int x2, int y, int color)
|
||||
{
|
||||
if((uint)y >= 64) return;
|
||||
if(y < dwindow.top || y >= dwindow.bottom) return;
|
||||
if(x1 > x2) swap(x1, x2);
|
||||
if(x1 >= 128 || x2 < 0) return;
|
||||
if(x1 >= dwindow.right || x2 < dwindow.left) return;
|
||||
|
||||
/* Get the masks for the [x1, x2] range */
|
||||
uint32_t m[4];
|
||||
|
@ -41,11 +41,11 @@ void gint_dhline(int x1, int x2, int y, int color)
|
|||
/* gint_dvline(): Optimized vertical line */
|
||||
void gint_dvline(int y1, int y2, int x, int color)
|
||||
{
|
||||
if((uint)x >= 128) return;
|
||||
if(x < dwindow.left || x >= dwindow.right) return;
|
||||
if(y1 > y2) swap(y1, y2);
|
||||
if(y1 >= 64 || y2 < 0) return;
|
||||
if(y1 < 0) y1 = 0;
|
||||
if(y2 >= 64) y2 = 63;
|
||||
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
|
||||
y1 = max(y1, dwindow.top);
|
||||
y2 = min(y2, dwindow.bottom - 1);
|
||||
|
||||
uint32_t *base = gint_vram + (y1 << 2) + (x >> 5);
|
||||
uint32_t *lword = base + ((y2 - y1 + 1) << 2);
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
#include <gint/defs/util.h>
|
||||
#include "render-fx.h"
|
||||
|
||||
/* masks() - compute the vram masks for a given rectangle */
|
||||
void masks(int x1, int x2, uint32_t *masks)
|
||||
{
|
||||
if(x1 < 0) x1 = 0;
|
||||
if(x2 >= 128) x2 = 127;
|
||||
x1 = max(x1, dwindow.left);
|
||||
x2 = min(x2, dwindow.right - 1);
|
||||
|
||||
/* Indexes of the first and last non-empty longs */
|
||||
size_t l1 = x1 >> 5;
|
||||
size_t l2 = x2 >> 5;
|
||||
size_t i = 0;
|
||||
|
||||
/* Base masks (0's are final, 0xffffffff will be adjusted later) */
|
||||
while(i < l1) masks[i++] = 0x00000000;
|
||||
while(i <= l2) masks[i++] = 0xffffffff;
|
||||
while(i < 4) masks[i++] = 0x00000000;
|
||||
|
||||
/* Remove the index information in x1 and x2 (it's now in l1 and l2)
|
||||
and keep only the offsets */
|
||||
/* Remove the index information in x1 and x2; keep only the offsets */
|
||||
x1 &= 31;
|
||||
/* For x2 we also want the complement to 31 to invert the shift */
|
||||
x2 = ~x2 & 31;
|
||||
|
||||
/* Now roll! */
|
||||
masks[l1] &= (0xffffffffu >> x1);
|
||||
masks[l2] &= (0xffffffffu << x2);
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ struct rendering_mode
|
|||
void (*drect)(int x1, int y1, int x2, int y2, color_t color);
|
||||
/* Point rendering */
|
||||
void (*dpixel)(int x, int y, color_t color);
|
||||
int (*dgetpixel)(int x, int y);
|
||||
void (*gint_dhline)(int x1, int x2, int y, int color);
|
||||
void (*gint_dvline)(int y1, int y2, int x, int color);
|
||||
/* Text and image rendering */
|
||||
|
@ -87,6 +88,7 @@ int gupdate(void);
|
|||
void gclear(color_t color);
|
||||
void grect(int x1, int y1, int x2, int y2, color_t color);
|
||||
void gpixel(int x, int y, color_t color);
|
||||
int ggetpixel(int x, int y);
|
||||
void gint_ghline(int x1, int x2, int y, int color);
|
||||
void gint_gvline(int y1, int y2, int x, int color);
|
||||
void gtext_opt(int x, int y, int fg, int bg, int halign, int valign,
|
||||
|
@ -94,10 +96,8 @@ void gtext_opt(int x, int y, int fg, int bg, int halign, int valign,
|
|||
void gsubimage(bopti_image_t const *image, struct rbox *r, int flags);
|
||||
|
||||
/* Short macro to call the alternate rendering function when available */
|
||||
#define DMODE_OVERRIDE(func, ...) \
|
||||
if(dmode && dmode->func) { \
|
||||
dmode->func(__VA_ARGS__); \
|
||||
return; \
|
||||
#define DMODE_OVERRIDE(func, ...) \
|
||||
if(dmode && dmode->func) { \
|
||||
return dmode->func(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#endif /* RENDER_FX */
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <gint/defs/types.h>
|
||||
#include <gint/defs/attributes.h>
|
||||
#include <gint/defs/util.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
#include "../render/render.h"
|
||||
|
@ -97,12 +98,21 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
uint32_t const *data = f->data;
|
||||
|
||||
/* Basic clipping */
|
||||
if(x > 127 || y > 63 || y + height <= 0) return;
|
||||
if(y + height > 64) height = 64 - y;
|
||||
if(x >= dwindow.right || y >= dwindow.bottom) return;
|
||||
if(y + height <= dwindow.top) return;
|
||||
height = min(height, dwindow.bottom - y);
|
||||
|
||||
/* How much we need to skip vertically if we render text at y < 0 */
|
||||
/* How much we skip vertically if we render at y < dwindow.top */
|
||||
int vdisp = 0;
|
||||
if(y < 0) vdisp = -y, y = 0;
|
||||
if(y < dwindow.top)
|
||||
{
|
||||
vdisp = dwindow.top - y;
|
||||
y = dwindow.top;
|
||||
}
|
||||
if(vdisp >= height) return;
|
||||
|
||||
uint32_t bg_mask[4];
|
||||
masks(dwindow.left, dwindow.right, bg_mask);
|
||||
|
||||
/* Operator data and background */
|
||||
uint32_t operators[height];
|
||||
|
@ -146,6 +156,12 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
|
||||
if(x >= 0)
|
||||
{
|
||||
for(int i = 0; i < height; i++)
|
||||
{
|
||||
operators[i] &= bg_mask[x];
|
||||
bg[i] &= bg_mask[x];
|
||||
}
|
||||
|
||||
asm_bg(v1, v2, bg + vdisp, height - vdisp);
|
||||
asm_fg(v1, v2, operators + vdisp, height - vdisp);
|
||||
}
|
||||
|
@ -171,7 +187,11 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
|
|||
/* Put the final longwords */
|
||||
if(x >= 0 && x < 4 && free < 32)
|
||||
{
|
||||
for(int i = 0; i < height; i++) bg[i] &= ~((1 << free) - 1);
|
||||
for(int i = 0; i < height; i++)
|
||||
{
|
||||
operators[i] &= bg_mask[x];
|
||||
bg[i] &= bg_mask[x] & ~((1 << free) - 1);
|
||||
}
|
||||
asm_bg(v1, v2, bg + vdisp, height - vdisp);
|
||||
asm_fg(v1, v2, operators + vdisp, height - vdisp);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
#include <gint/display.h>
|
||||
#include <gint/kmalloc.h>
|
||||
#include <gint/config.h>
|
||||
#include <gint/drivers/r61524.h>
|
||||
#include "../render-fx/render-fx.h"
|
||||
|
||||
/* The destination VRAM for upscaling + centering + conversion B&W --> 16bits RGB565 */
|
||||
uint16_t *cg_vram; // set to 0 at initialisation
|
||||
|
||||
/* Standard video RAM for fx9860g is 1 bit per pixel */
|
||||
GALIGNED(32) static uint32_t fx_vram[256];
|
||||
|
||||
/* Here is the definition of the VRAM pointer, exposed in <gint/display.h> */
|
||||
uint32_t *gint_vram = fx_vram;
|
||||
|
||||
/* The current rendering mode */
|
||||
struct rendering_mode const *dmode = NULL;
|
||||
|
||||
|
||||
bool dvram_init( void )
|
||||
{
|
||||
int const MARGIN = 32;
|
||||
|
||||
/* Leave MARGIN bytes on each side of the region; this enables some
|
||||
important optimizations in the image renderer. We also add another
|
||||
32 bytes so we can manually 32-align the region */
|
||||
uint32_t region = (uint32_t)kmalloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32,
|
||||
#if !defined(GINT_NO_OS_STACK)
|
||||
"_ostk"
|
||||
#else
|
||||
NULL
|
||||
#endif
|
||||
);
|
||||
if(region == 0)
|
||||
return false;
|
||||
|
||||
/* 32-align the region */
|
||||
region = (region + 31) & -32;
|
||||
/* Skip a MARGIN */
|
||||
region += MARGIN;
|
||||
/* Use an uncached address */
|
||||
region = (region & 0x1fffffff) | 0xa0000000;
|
||||
|
||||
/* Don't enable triple buffering by default */
|
||||
cg_vram = (void *)region;
|
||||
return true;
|
||||
}
|
||||
|
||||
void dgetvram(uint16_t **ptr_vram_1, uint16_t **ptr_vram_2)
|
||||
{
|
||||
if(ptr_vram_1) *ptr_vram_1 = &cg_vram;
|
||||
if(ptr_vram_2) *ptr_vram_2 = &cg_vram;
|
||||
}
|
||||
|
||||
inline drawupscale( int x, int y, int color )
|
||||
{
|
||||
int u=y*396*3;
|
||||
int v=x*3;
|
||||
|
||||
uint16_t colorcg;
|
||||
|
||||
if (color==C_WHITE) colorcg=0xFFFF;
|
||||
else colorcg=0x0000;
|
||||
|
||||
int baseindex = (396*16+6+v+u); // 16 lines on top/bottom remain black and 6 columns on left/right remain black
|
||||
cg_vram[baseindex] = colorcg;
|
||||
cg_vram[baseindex+1] = colorcg;
|
||||
cg_vram[baseindex+2] = colorcg;
|
||||
|
||||
baseindex+=396;
|
||||
cg_vram[baseindex] = colorcg;
|
||||
cg_vram[baseindex+1] = colorcg;
|
||||
cg_vram[baseindex+2] = colorcg;
|
||||
|
||||
baseindex+=396;
|
||||
cg_vram[baseindex] = colorcg;
|
||||
cg_vram[baseindex+1] = colorcg;
|
||||
cg_vram[baseindex+2] = colorcg;
|
||||
}
|
||||
|
||||
/* dupdate(): Push the video RAM to the display driver */
|
||||
void dupdate(void)
|
||||
{
|
||||
bool run_default = true;
|
||||
|
||||
if(dmode && dmode->dupdate)
|
||||
{
|
||||
/* Call the overridden dupdate(), but continue if it returns
|
||||
non-zero (this is used when stopping the gray engine) */
|
||||
int rc = dmode->dupdate();
|
||||
run_default = (rc != 0);
|
||||
}
|
||||
|
||||
if(run_default)
|
||||
{
|
||||
for( int j=0; j<DHEIGHT; j++ ) // 64 lines
|
||||
{
|
||||
for( int i=0; i<DWIDTH; i++ ) // 128 column
|
||||
{
|
||||
drawupscale( i, j, dgetpixel(i,j) ); // really not optimised; just to check if OK
|
||||
}
|
||||
}
|
||||
|
||||
//draw a black layer around the screen
|
||||
int base1 = 0;
|
||||
int base2 = (64*3+16)*396;
|
||||
for(int u = 0; u<16; u++)
|
||||
{
|
||||
for(int v = 0; v<396; v++)
|
||||
{
|
||||
cg_vram[base1+v] = 0;
|
||||
cg_vram[base2+v] = 0;
|
||||
}
|
||||
base1+=396;
|
||||
base2+=396;
|
||||
}
|
||||
|
||||
base1 = 16*396;
|
||||
base2 = 16*396+6+128*3;
|
||||
for(int u = 0; u<64*3; u++)
|
||||
{
|
||||
for(int v = 0; v<6; v++)
|
||||
{
|
||||
cg_vram[base1+v] = 0;
|
||||
cg_vram[base2+v] = 0;
|
||||
}
|
||||
base1+=396;
|
||||
base2+=396;
|
||||
}
|
||||
|
||||
r61524_display(cg_vram, 0, 224, R61524_DMA_WAIT );
|
||||
}
|
||||
|
||||
gint_call(dupdate_get_hook());
|
||||
}
|
||||
__attribute__((alias("dupdate")))
|
||||
void _WEAK_dupdate(void);
|
|
@ -0,0 +1,340 @@
|
|||
//---
|
||||
// gint:gray:engine - Core gray engine
|
||||
//---
|
||||
|
||||
#include <gint/defs/types.h>
|
||||
#include <gint/drivers/r61524.h>
|
||||
#include <gint/gray.h>
|
||||
#include <gint/display.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../render-fx/render-fx.h"
|
||||
|
||||
/* Three additional video RAMS, allocated statically if --static-gray was set
|
||||
at configure time, or with malloc() otherwise. */
|
||||
#ifdef GINT_STATIC_GRAY
|
||||
GBSS static uint32_t gvrams[1][256];
|
||||
#endif
|
||||
|
||||
uint16_t *cg_vram_gray;
|
||||
|
||||
/* two VRAMs: two to draw and two to display */
|
||||
static uint32_t *vrams[2] = { NULL, NULL };
|
||||
|
||||
/* Whether the engine is scheduled to run at the next frame */
|
||||
static int runs = 0;
|
||||
|
||||
extern struct rendering_mode const *dmode;
|
||||
|
||||
|
||||
/* 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,
|
||||
.dsubimage = gsubimage,
|
||||
};
|
||||
static struct rendering_mode const gray_exit_mode = {
|
||||
.dupdate = gupdate,
|
||||
.dclear = NULL,
|
||||
.drect = NULL,
|
||||
.dpixel = NULL,
|
||||
.dgetpixel = NULL,
|
||||
.gint_dhline = NULL,
|
||||
.gint_dvline = NULL,
|
||||
.dtext_opt = NULL,
|
||||
.dsubimage = NULL,
|
||||
};
|
||||
|
||||
//---
|
||||
// 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] >= 0);
|
||||
}
|
||||
|
||||
|
||||
bool dvram_init_gray( void )
|
||||
{
|
||||
int const MARGIN = 32;
|
||||
|
||||
/* Leave MARGIN bytes on each side of the region; this enables some
|
||||
important optimizations in the image renderer. We also add another
|
||||
32 bytes so we can manually 32-align the region */
|
||||
uint32_t region = (uint32_t)kmalloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32,
|
||||
#if !defined(GINT_NO_OS_STACK)
|
||||
"_ostk"
|
||||
#else
|
||||
NULL
|
||||
#endif
|
||||
);
|
||||
if(region == 0)
|
||||
return false;
|
||||
|
||||
/* 32-align the region */
|
||||
region = (region + 31) & -32;
|
||||
/* Skip a MARGIN */
|
||||
region += MARGIN;
|
||||
/* Use an uncached address */
|
||||
region = (region & 0x1fffffff) | 0xa0000000;
|
||||
|
||||
/* Don't enable triple buffering by default */
|
||||
cg_vram_gray = (void *)region;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* gray_init(): Initialize the engine
|
||||
This is done at startup so that memory can be reserved very early from the
|
||||
heap (because not having enough memory is unrecoverable for the engine). */
|
||||
GCONSTRUCTOR static void gray_init(void)
|
||||
{
|
||||
/* We need four VRAMs. First use the standard monochrome one */
|
||||
vrams[0] = gint_vram;
|
||||
|
||||
#ifdef GINT_STATIC_GRAY
|
||||
vrams[1] = gvrams[0];
|
||||
#else
|
||||
vrams[1] = malloc(1024);
|
||||
#endif /* GINT_STATIC_GRAY */
|
||||
|
||||
dvram_init_gray();
|
||||
|
||||
/* On failure, release the resources that we obtained */
|
||||
if(!gray_isinit()) gray_quit();
|
||||
}
|
||||
|
||||
/* gray_quit(): Free engine resources */
|
||||
GDESTRUCTOR static void gray_quit(void)
|
||||
{
|
||||
#ifndef GINT_STATIC_GRAY
|
||||
if(vrams[1]) free(vrams[1]);
|
||||
vrams[1] = NULL;
|
||||
#endif /* GINT_STATIC_GRAY */
|
||||
}
|
||||
|
||||
/* gray_start(): Start the gray engine */
|
||||
static void gray_start(void)
|
||||
{
|
||||
runs = 1;
|
||||
}
|
||||
|
||||
/* gray_stop(): Stop the gray engine */
|
||||
static void gray_stop(void)
|
||||
{
|
||||
runs = 0;
|
||||
}
|
||||
|
||||
//---
|
||||
// Dynamic udpate and rendering mode
|
||||
//---
|
||||
|
||||
/* dgray(): Start or stop the gray engine at the next dupdate() */
|
||||
int dgray(int mode)
|
||||
{
|
||||
/* Stack of states for the push modes */
|
||||
static uint8_t states[32] = { 0 };
|
||||
static uint8_t current = 0;
|
||||
|
||||
if(mode == DGRAY_ON)
|
||||
{
|
||||
if(!gray_isinit()) return 1;
|
||||
|
||||
/* Set the display module's alternate rendering mode to
|
||||
override rendering functions to use their g*() variant */
|
||||
if(!dgray_enabled()) dmode = &gray_mode;
|
||||
}
|
||||
else if(mode == DGRAY_OFF)
|
||||
{
|
||||
/* Set the mode to a temporary one that only overrides
|
||||
dupdate() so that we can stop the engine next frame */
|
||||
if(dgray_enabled()) dmode = &gray_exit_mode;
|
||||
}
|
||||
else if(mode == DGRAY_PUSH_ON)
|
||||
{
|
||||
if(current >= 32) return 1;
|
||||
states[current++] = dgray_enabled() ? DGRAY_ON : DGRAY_OFF;
|
||||
|
||||
return dgray(DGRAY_ON);
|
||||
}
|
||||
else if(mode == DGRAY_PUSH_OFF)
|
||||
{
|
||||
if(current >= 32) return 1;
|
||||
states[current++] = dgray_enabled() ? DGRAY_ON : DGRAY_OFF;
|
||||
|
||||
return dgray(DGRAY_OFF);
|
||||
}
|
||||
else if(mode == DGRAY_POP)
|
||||
{
|
||||
/* Stay at 0 if the user's push/pop logic is broken */
|
||||
if(current > 0) current--;
|
||||
|
||||
/* Switch to previous state */
|
||||
return dgray(states[current]);
|
||||
}
|
||||
else return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* convert the gray scale into RGB565 and draw in the virutal VRAMthe 3x3 pixels upscaled and centered */
|
||||
inline gdrawupscale( int x, int y, int color_fx )
|
||||
{
|
||||
int u=y*396*3;
|
||||
int v=x*3;
|
||||
|
||||
uint16_t color_cg;
|
||||
|
||||
if (color_fx==C_WHITE) color_cg=0xFFFF;
|
||||
else if (color_fx==C_LIGHT) color_cg=0xAD55;
|
||||
else if (color_fx==C_DARK) color_cg=0x528A;
|
||||
else color_cg=0x0000;
|
||||
|
||||
int baseindex = (396*16+6+v+u); // 16 lines on top/bottom remain black and 6 columns on left/right remain black
|
||||
cg_vram_gray[baseindex] = color_cg; // draw 3 pixels side by side
|
||||
cg_vram_gray[baseindex+1] = color_cg;
|
||||
cg_vram_gray[baseindex+2] = color_cg;
|
||||
|
||||
baseindex+=396; // same for the line below (line #2)
|
||||
cg_vram_gray[baseindex] = color_cg;
|
||||
cg_vram_gray[baseindex+1] = color_cg;
|
||||
cg_vram_gray[baseindex+2] = color_cg;
|
||||
|
||||
baseindex+=396; // same for the line below (line #3)
|
||||
cg_vram_gray[baseindex] = color_cg;
|
||||
cg_vram_gray[baseindex+1] = color_cg;
|
||||
cg_vram_gray[baseindex+2] = color_cg;
|
||||
}
|
||||
|
||||
|
||||
/* gray_int(): Interrupt handler */
|
||||
//int gray_int(void)
|
||||
//{
|
||||
// //t6k11_display(vrams[st ^ 2], 0, 64, 16);
|
||||
// timer_reload(GRAY_TIMER, delays[(st ^ 3) & 1]);
|
||||
//
|
||||
// st ^= 1;
|
||||
//
|
||||
// return TIMER_CONTINUE;
|
||||
//}
|
||||
|
||||
/* gupdate(): Push the current VRAMs to the screen */
|
||||
int gupdate(void)
|
||||
{
|
||||
/* At the first gupdate(), start the engine */
|
||||
if(dmode == &gray_mode && !runs)
|
||||
{
|
||||
gray_start();
|
||||
}
|
||||
/* At the last gupdate(), stop the engine */
|
||||
else if(dmode == &gray_exit_mode)
|
||||
{
|
||||
gray_stop();
|
||||
dmode = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
uint32_t *light, *dark;
|
||||
dgray_getscreen(&light, &dark);
|
||||
|
||||
for( int j=0; j<DHEIGHT; j++ ) // 64 lines
|
||||
{
|
||||
for( int i=0; i<DWIDTH; i++ ) // 128 column
|
||||
{
|
||||
int offset = (j << 2) + (i >> 5);
|
||||
uint32_t mask = 1 << (~i & 31);
|
||||
int l = (light[offset] & mask) !=0 ? 1 : 0;
|
||||
int d = (dark [offset] & mask) !=0 ? 1 : 0;
|
||||
|
||||
int color_fx = (d << 1) | l;
|
||||
|
||||
gdrawupscale( i, j, color_fx ); // really not optimised; just to check if OK
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//draw a black layer around the screen
|
||||
int base1 = 0;
|
||||
int base2 = (64*3+16)*396;
|
||||
for(int u = 0; u<16; u++)
|
||||
{
|
||||
for(int v = 0; v<396; v++)
|
||||
{
|
||||
cg_vram_gray[base1+v] = 0;
|
||||
cg_vram_gray[base2+v] = 0;
|
||||
}
|
||||
base1+=396;
|
||||
base2+=396;
|
||||
}
|
||||
|
||||
base1 = 16*396;
|
||||
base2 = 16*396+6+128*3;
|
||||
for(int u = 0; u<64*3; u++)
|
||||
{
|
||||
for(int v = 0; v<6; v++)
|
||||
{
|
||||
cg_vram_gray[base1+v] = 0;
|
||||
cg_vram_gray[base2+v] = 0;
|
||||
}
|
||||
base1+=396;
|
||||
base2+=396;
|
||||
}
|
||||
|
||||
r61524_display(cg_vram_gray, 0, 224, R61524_DMA_WAIT );
|
||||
|
||||
/* When the engine is running, swap frames */
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---
|
||||
// Query and configuration functions
|
||||
//---
|
||||
|
||||
/* dgray_enabled(): Check whether gray mode is enabled */
|
||||
int dgray_enabled(void)
|
||||
{
|
||||
return (dmode == &gray_mode);
|
||||
}
|
||||
|
||||
/* dgray_setdelays(): Set the gray engine delays */
|
||||
void dgray_setdelays(uint32_t light, uint32_t dark)
|
||||
{
|
||||
// delays[0] = light;
|
||||
// delays[1] = dark;
|
||||
}
|
||||
|
||||
/* dgray_getdelays(): Get the gray engine delays */
|
||||
void dgray_getdelays(uint32_t *light, uint32_t *dark)
|
||||
{
|
||||
// if(light) *light = delays[0];
|
||||
// if(dark) *dark = delays[1];
|
||||
}
|
||||
|
||||
/* dgray_getvram(): Get the current VRAM pointers */
|
||||
void dgray_getvram(uint32_t **light, uint32_t **dark)
|
||||
{
|
||||
if(light) *light = vrams[0];
|
||||
if(dark) *dark = vrams[1];
|
||||
}
|
||||
|
||||
/* dgray_getscreen(): Get the current screen pointers */
|
||||
void dgray_getscreen(uint32_t **light, uint32_t **dark)
|
||||
{
|
||||
if(light) *light = vrams[0];
|
||||
if(dark) *dark = vrams[1];
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
#include <gint/display.h>
|
||||
|
||||
#define abs(x) ((x)>=0 ? (x) : (-x))
|
||||
|
||||
void dellipse(int xm, int ym, int a, int b, int c) {
|
||||
long x = -a, y = 0; /* II. quadrant from bottom left to top right */
|
||||
long e2 = b, dx = (1 + 2 * x) * e2 * e2; /* error increment */
|
||||
long dy = x * x, err = dx + dy; /* error of 1.step */
|
||||
|
||||
long a2 = (long)a * a;
|
||||
long b2 = (long)b * b;
|
||||
|
||||
do {
|
||||
dpixel(xm - x, ym + y, c); /* I. Quadrant */
|
||||
dpixel(xm + x, ym + y, c); /* II. Quadrant */
|
||||
dpixel(xm + x, ym - y, c); /* III. Quadrant */
|
||||
dpixel(xm - x, ym - y, c); /* IV. Quadrant */
|
||||
e2 = 2 * err;
|
||||
if (e2 >= dx) {
|
||||
x++;
|
||||
err += dx += 2 * b2;
|
||||
} /* x step */
|
||||
if (e2 <= dy) {
|
||||
y++;
|
||||
err += dy += 2 * a2;
|
||||
} /* y step */
|
||||
} while (x <= 0);
|
||||
while (y++ < b) { /* to early stop for flat ellipses with a=1, */
|
||||
dpixel(xm, ym + y, c); /* -> finish tip of ellipse */
|
||||
dpixel(xm, ym - y, c);
|
||||
}
|
||||
}
|
||||
|
||||
void dellipserect(int x0, int y0, int x1, int y1,
|
||||
int c) { /* rectangular parameter enclosing the ellipse */
|
||||
long a = abs(x1 - x0), b = abs(y1 - y0), b1 = b & 1; /* diameter */
|
||||
double dx = 4 * (1.0 - a) * b * b,
|
||||
dy = 4 * (b1 + 1) * a * a; /* error increment */
|
||||
double err = dx + dy + b1 * a * a, e2; /* error of 1.step */
|
||||
if (x0 > x1) {
|
||||
x0 = x1;
|
||||
x1 += a;
|
||||
} /* if called with swapped points */
|
||||
if (y0 > y1)
|
||||
y0 = y1; /* .. exchange them */
|
||||
y0 += (b + 1) / 2;
|
||||
y1 = y0-b1; /* starting pixel */
|
||||
a = 8 * a * a;
|
||||
b1 = 8 * b * b;
|
||||
do {
|
||||
dpixel(x1, y0, c); /* I. Quadrant */
|
||||
dpixel(x0, y0, c); /* II. Quadrant */
|
||||
dpixel(x0, y1, c); /* III. Quadrant */
|
||||
dpixel(x1, y1, c); /* IV. Quadrant */
|
||||
e2 = 2 * err;
|
||||
if (e2 <= dy) {
|
||||
y0++;
|
||||
y1--;
|
||||
err += dy += a;
|
||||
} /* y step */
|
||||
if (e2 >= dx || 2 * err > dy) {
|
||||
x0++;
|
||||
x1--;
|
||||
err += dx += b1;
|
||||
} /* x */
|
||||
} while (x0 <= x1);
|
||||
while (y0 - y1 <= b) { /* to early stop of flat ellipses a=1 */
|
||||
dpixel(x0 - 1, y0, c); /* -> finish tip of ellipse */
|
||||
dpixel(x1 + 1, y0++, c);
|
||||
dpixel(x0 - 1, y1, c);
|
||||
dpixel(x1 + 1, y1--, c);
|
||||
}
|
||||
}
|
||||
|
||||
void dcircle(int xm, int ym, int r, int c)
|
||||
{
|
||||
dellipse( xm, ym, r, r, c);
|
||||
}
|
||||
|
||||
|
||||
void dellipse_fill(int xm, int ym, int a, int b, int cback, int cborder) {
|
||||
long x = -a, y = 0; /* II. quadrant from bottom left to top right */
|
||||
long e2 = b, dx = (1 + 2 * x) * e2 * e2; /* error increment */
|
||||
long dy = x * x, err = dx + dy; /* error of 1.step */
|
||||
|
||||
long a2 = (long)a * a;
|
||||
long b2 = (long)b * b;
|
||||
|
||||
do {
|
||||
dline(xm - x, ym + y, xm + x, ym + y, cback );
|
||||
dpixel(xm - x, ym + y, cborder); /* I. Quadrant */
|
||||
dpixel(xm + x, ym + y, cborder); /* II. Quadrant */
|
||||
|
||||
dline(xm - x, ym - y, xm + x, ym - y, cback );
|
||||
dpixel(xm + x, ym - y, cborder); /* III. Quadrant */
|
||||
dpixel(xm - x, ym - y, cborder); /* IV. Quadrant */
|
||||
e2 = 2 * err;
|
||||
if (e2 >= dx) {
|
||||
x++;
|
||||
err += dx += 2 * b2;
|
||||
} /* x step */
|
||||
if (e2 <= dy) {
|
||||
y++;
|
||||
err += dy += 2 * a2;
|
||||
} /* y step */
|
||||
} while (x <= 0);
|
||||
while (y++ < b) { /* to early stop for flat ellipses with a=1, */
|
||||
dpixel(xm, ym + y, cborder); /* -> finish tip of ellipse */
|
||||
dpixel(xm, ym - y, cborder);
|
||||
}
|
||||
}
|
||||
|
||||
void dcircle_fill(int xm, int ym, int r, int cback, int cborder)
|
||||
{
|
||||
dellipse_fill( xm, ym, r, r, cback, cborder );
|
||||
}
|
|
@ -3,5 +3,5 @@
|
|||
/* dhline(): Full-width horizontal line */
|
||||
void dhline(int y, int color)
|
||||
{
|
||||
dline(0, y, DWIDTH - 1, y, color);
|
||||
dline(dwindow.left, y, dwindow.right - 1, y, color);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include <gint/defs/util.h>
|
||||
|
||||
#include "../render/render.h"
|
||||
#ifdef FX9860G
|
||||
#if defined(FX9860G) || defined(FX9860G_AS_CG)
|
||||
#include "../render-fx/render-fx.h"
|
||||
#endif
|
||||
|
||||
|
@ -18,7 +18,7 @@ void dline(int x1, int y1, int x2, int y2, int color)
|
|||
/* Possible optimizations */
|
||||
if(y1 == y2)
|
||||
{
|
||||
#ifdef FX9860G
|
||||
#if defined(FX9860G) || defined(FX9860G_AS_CG)
|
||||
DMODE_OVERRIDE(gint_dhline, x1, x2, y1, color);
|
||||
#endif
|
||||
|
||||
|
@ -27,7 +27,7 @@ void dline(int x1, int y1, int x2, int y2, int color)
|
|||
}
|
||||
if(x1 == x2)
|
||||
{
|
||||
#ifdef FX9860G
|
||||
#if defined(FX9860G) || defined(FX9860G_AS_CG)
|
||||
DMODE_OVERRIDE(gint_dvline, y1, y2, x1, color);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ void drect_border(int x1, int y1, int x2, int y2, int fill, int width,
|
|||
if(x1 > x2) swap(x1, x2);
|
||||
if(y1 > y2) swap(y1, y2);
|
||||
|
||||
if(x1 >= DWIDTH || x2 < 0 || y1 >= DHEIGHT || y2 < 0) return;
|
||||
if(x1 >= dwindow.right || x2 < dwindow.left) return;
|
||||
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
|
||||
|
||||
drect(x1, y1, x2, y1 + (width-1), border);
|
||||
drect(x1, y2 - (width-1), x2, y2, border);
|
||||
|
|
|
@ -3,5 +3,5 @@
|
|||
/* dvline(): Full-height vertical line */
|
||||
void dvline(int x, int color)
|
||||
{
|
||||
dline(x, 0, x, DHEIGHT - 1, color);
|
||||
dline(x, dwindow.top, x, dwindow.bottom - 1, color);
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue