Compare commits

...

80 Commits

Author SHA1 Message Date
Lephe 4f9b141b79
bump version to 2.9.0 2022-08-21 20:12:34 +02:00
Lephe 02150d90e5
ld: properly link .bss.* sections 2022-08-21 20:11:45 +02:00
Lephe 126d1506ac
cmake: install in the fxSDK sysroot 2022-08-19 16:35:47 +02:00
Lephe 6ea2f991a3
keysc: make repeat settings global instead of just for getkey()
Having repeat settings only for getkey() meant that repeats that occur
while getkey() is not running (i.e., all of them) would be lost. This is
due to e57efb5e3 which replaced on-demand repeats with normal event
generation.

Now the settings are applied globally, which allows repeats to be
enabled even when getkey() is not active. This also reduces the feature
gap between getkey() and raw events, which reduces the risk of running
into edges cases by using both.

The previous API is retained for source compatibility until gint 3.0 but
the changes are now applied globally so the semantics are slightly
different.
2022-07-24 21:57:18 +01:00
Lephe 4e1136d0ac
r61525: now account for the inverted x-axis on the display (!)
Always has been.
2022-07-17 21:17:46 +01:00
Lephe 1c3c9727a5
r61524: add a r61524_display_rect() function for small regions 2022-07-16 22:30:59 +01:00
Lephe e8b4bcc9cb
tmu: fix delay truncation in subfunction call 2022-06-13 21:36:52 +01:00
Lephe b17d4de4a8
cpg: do read and write CS3 on fx-CG 10/20 2022-05-23 21:20:47 +01:00
Lephe f69f92b938
cpg: fix parameter check minding CS3 on the fx-CG 10/20 2022-05-22 20:00:22 +01:00
Lephe 8c9c48a91e
bump version to 2.8.0 2022-05-16 20:13:13 +01:00
Lephe 291c3cef17
cpg: restore overclock settings when leaving 2022-05-16 20:13:13 +01:00
Lephe b942bc5d19
clock: add overclock support on fx-CG 10/20/50 2022-05-15 19:20:14 +01:00
Lephe c2ff07427b
image: add image_linear_alloc() 2022-05-15 13:45:59 +01:00
Lephe 09c13676d3
image: arbitrary linear transforms 2022-05-15 12:59:28 +01:00
Lephe 780acb3fc9
image: arbitrary linear transforms [WIP] 2022-05-14 22:32:59 +01:00
Lephe 818f950fff
image: flips, including in-place 2022-05-14 20:27:16 +01:00
Lephe 9468a8d725
image: clean up palette semantics, and conversion 2022-05-14 15:36:07 +01:00
Lephe fc6f7d3051
image: remove alpha field of images 2022-05-14 12:54:59 +01:00
Lephe 5a69e44078
image: new image format and base for the image library 2022-05-13 23:31:03 +01:00
Lephe 7822899b1f
render-cg: fix incorrect margin size for VRAMs 2022-05-07 18:22:38 +01:00
Lephe d2f788a3fc
render-cg: fix negative alpha values being miscompared in P8 2022-05-07 18:12:44 +01:00
Lephe 667f43b45c
render-cg: remove now-unused section of .effects field of image command 2022-05-06 16:52:05 +01:00
Lephe ede19fc878
render-cg: restore bopti method on P4 and defined p4_clearbg_alt 2022-05-06 16:26:44 +01:00
Lephe a4df076214
render-cg: replace dimage and dsubimage with new renderer 2022-05-04 20:53:56 +01:00
Lephe 7a3604ccbb
render-cg: allocate VRAM in the heap; default to double buffering
* Create a heap arena over the OS stack, large enough to hold two VRAMs
  as was previously done, unless GINT_NO_OS_STACK is set at compile
  time. (This replaces GINT_USER_VRAM.)

* Allocate a single VRAM in the heap at startup.

* Use double buffering by default as triple buffering is almost entirely
  useless. dudpate() waits if both VRAMs are identical to prevent
  corruption, but this can be bypassed with R61524 functions as usual.
  This adds about 180 kB of heap data to any add-in using default
  settings.
2022-05-04 20:08:52 +01:00
Lephe f219e5c882
render-cg: add new image rendering functions with dynamic effects 2022-05-04 19:08:54 +01:00
Lephe 904ab74984
(minor) 2022-05-04 19:08:54 +01:00
Lephe 8210524152
ld: merge XRAM and YRAM into a single 16-kiB section 2022-05-04 19:08:54 +01:00
Lephe 26c5b76037
render-cg: round RGB16 images to even widths 2022-05-04 17:17:18 +01:00
Lephe e57efb5e37
keysc: simpler keyboard device with more consistent repeats
* Stop trying to be smart and generate repeats on the fly; this breaks
  time consistency. Also if repeats are not handled in time this causes
  infinite loops.
* Move rarely-used functions to external files, simplify stuff, get rid
  of internal driver events; saves ~1 kB per add-in overall.
2022-04-23 13:34:41 +01:00
Lephe 0c2935055e
cpg: provide a function to recompute clock frequency 2022-04-15 21:08:37 +01:00
Lephe fdadb0dd71
mpu: rename FRQCRA into FRQCR 2022-04-12 16:23:18 +01:00
Lephe b10c065abe
mpu: update CPG definition
We should also rename FRQCRA to FRQCR.
2022-04-12 16:22:14 +01:00
Lephe a3ce29b7b8
mpu: add BSC register definitions 2022-04-12 14:57:07 +01:00
Lephe a7bcf6cd77
meta: mkg3a is no longer needed since fxgxa supports g3a 2022-04-11 11:12:25 +01:00
Lephe 4223164063
r61524: fix r61524_display() not fully honoring [start] 2022-03-23 20:42:37 +00:00
Lephe 93169e8803
render: fix drsize() skipping one byte past NUL
This would work if the string has double NUL, which is common enough
that this bug never came up before. x)
2022-03-22 18:48:06 +00:00
Lephe 2bf5dd93f4
bump version to 2.7.1 2022-03-19 19:27:53 +00:00
Lephe f300338a57
fs: fix tracking of initial position with O_APPEND (*)
(*) O_APPEND is *not* functional yet, this is just a hack for write-only
streams. Currently O_APPEND does not reposition the cursor at the end of
the file before every write.
2022-03-19 19:26:23 +00:00
Lephe 48325fc54d
ld: exclude more debug sections (C++) 2022-03-19 19:26:05 +00:00
Lephe 36d66a6317
bopti: fix p4 stride omission from 1384c54b5 2022-01-18 13:50:04 +01:00
Lephe d8b85a9fab
hardware: specify gint[HWRAM] = 8 MiB on the CG-50 2022-01-18 13:49:33 +01:00
Lephe 227c06631b
fs: buffer Fugue writes through RAM
Writing from ROM causes occasional crashes, which are fairly hard to
produce when the buffers are small.
2022-01-10 14:47:14 +01:00
Lephe d6ada7f11f
ld: reserve stack space from the static RAM region 2022-01-10 14:47:14 +01:00
Lephe 8aea762e7a
fs: use a static variable in calls to BFile_Create()
There is no evidence that BFile_Create() keeps the address and writes
to it later on, but I'd rather be overly careful than have to debug a
stack corruption problem in half a year :)
2022-01-10 13:38:28 +01:00
Lephe 59a3b39fb4
dma: do not use ICS in foreign unbinds
I'm pretty sure it makes no difference because the OS does not rely on
interrupts for most (if not all) of its DMA operations, but it's better
to keep it clean anyway.
2022-01-10 13:36:57 +01:00
Lephe ef8707ee9d
dma: remove IE flag at end of transfer
This should have no effect since the channel is disabled, but it's much
better to leave unused modules in predictable states.
2022-01-10 13:31:20 +01:00
Lephe 3aa1471ac5
fs: track offset of Fugue files manually
This helped locate some bugs:
* read() could read past EOF due to BFile_Read() allowing you to read up
  until the end of the last sector, beyond the file size
* pread() did not restore the file offset because the negative seek at
  the end is not relative (that was the CASIOWIN fs API), so pread()
  could not actually be written without knowing the current position
* lseek() would clamp you to EOF but still return its out-of-bounds
  arguments, as a direct result of BFile_Seek() doing that

Benefits:
* Made pread() a generic function
2022-01-06 14:05:52 +01:00
Lephe 9b02f5f1db
bump version to 2.7.0 2021-12-31 10:46:46 +01:00
Lephe 472f1245c7
fix bad indent 2021-12-31 10:37:59 +01:00
Lephe b549fd68ba
fs: stat(), proper unlink(), rmdir() 2021-12-30 18:17:13 +01:00
Lephe ed30895a49
properly handle the mutual dependency with fxlibc 2021-12-23 16:54:57 +01:00
Lephe aed90d9b3c
fs: folder support, part 2 (path normalization and root) 2021-12-23 01:19:45 +01:00
Lephe 9cae0040b5
fs: folder support, part 1 (mkdir/rmdir and the opendir(3) family) 2021-12-21 19:01:00 +01:00
Lephe 34c73ba0ba
fs: fix include path for bfile.h 2021-12-15 13:32:39 +01:00
Lephe 6903bd58d5
fs: cast BFile support into generic file descriptors
This paves the way for standard streams, USB streams, and some more.
2021-12-13 18:38:47 +01:00
Lephe 321d6937e0
kernel: tentative stack overflow panic, 14 kiB stack on G-III
I saw a crash with the 12 kB stack. Added an error message to diagnose
further similar issues, and bumbed the stack to 14 kB. That's a lot of
space just for BFile but stability is queen... :x
2021-12-11 16:38:31 +01:00
Lephe 8635880bbb
fs: basic filesystem support over BFile 2021-12-11 16:38:28 +01:00
Lephe 71de4dcb95
bfile: clean up header, add BFile_Seek and BFile_GetPos 2021-12-10 07:24:12 +01:00
Lephe 2e5e56f82e
hardware: expose filesystem type in the hardware info
Filesystem type is detected with a trivial heuristic:
* fx-9860G: CASIOWIN, unless Fugue in the G-III series (OS >= 3.00)
* fx-CG 50: Always Fugue
2021-12-06 21:31:00 +01:00
Lephe 86fad757e1
kernel: increase stack size to 12 kB on SH4 fx-9860G
BFile calls on the G-III series can overflow an 8 kB stack, overriding
the VBR which is located just before (!).
2021-12-05 21:15:41 +01:00
Lephe b7173a5109
kernel: fix incorrect interrupt save/restore for MAC on SH3 2021-11-25 14:02:04 +01:00
Lephe 055c8f405b
r61524: fix a tight freeze window due to a CPU/DMA race 2021-11-20 16:39:46 +01:00
Lephe caf585b0a1
tmu: fix freeze when using sleep_us_spin() with interrupts on
Because of timing the interrupt handler could run before the flag is
checked.
2021-11-15 06:43:00 +01:00
Lephe 1384c54b5f
render-cg: update to bopti formats for Azur 2021-09-28 09:55:26 +02:00
Lephe 73446aae61
bfile: use non-smem syscalls for Fugue
Not sure how the design is internally, but only these ones can find
files in subfolders.
2021-09-25 15:59:47 +02:00
Lephe 192ff17303
render-cg: fix an incorrect x bound on vertical lines 2021-09-05 17:55:29 +02:00
Lephe 7adcdea5f1
keyboard: add a note about the use of transforms in getkey_opt() 2021-09-01 16:21:53 +02:00
Lephe 2d0e18f2d8
bump version to 2.6.0 2021-08-29 11:53:40 +02:00
Lephe 97934cc18e
README: clarify install command 2021-08-26 10:49:34 +02:00
Lephe d3a2cf07a0
usb: add video capture through the fxlink protocol 2021-08-11 01:43:26 +02:00
Lephe 8713d2644f
defs: take ssize_t and off_t in the standard library 2021-07-02 09:52:17 +02:00
Lephe 545db2f9ce
r61524: add low-level get/set and start_frame functions 2021-06-22 17:52:26 +02:00
Lephe 02c1b551cd
mpu/cpg.h: specify DDCLKCR 2021-06-22 17:51:54 +02:00
Lephe 89d540ee91
kernel: add basic C++ support for GINT_CALL() 2021-06-22 17:51:27 +02:00
Lephe 8d444b4f78
kernel: add crash details on illegal instructions 2021-06-17 14:33:45 +02:00
Lephe 03ef59521c
dma: add support for standard DMA access to SPU memory
* Mark SPU memory as sleep-blocking.
* Perform 4-byte accesses only in dma_memset() and dma_memcpy() (32-byte
  accesses freeze as one would expect).

This change does *NOT* implement support for SPU's integrated DMAC.
2021-06-17 14:32:27 +02:00
Lephe 5bd04a9613
cpu, dma: add interrupt-cancellable sleep (perfect async sleep) 2021-06-17 14:32:08 +02:00
Lephe 658413ba19
r61524: fix hardcoded VRAM and don't assume full-size 2021-06-16 21:12:53 +02:00
Lephe 10180d31bc
add C++ header guards 2021-06-13 18:13:09 +02:00
178 changed files with 7825 additions and 1217 deletions

View File

@ -1,12 +1,12 @@
# Build system for the gint unikernel
cmake_minimum_required(VERSION 3.15)
project(Gint VERSION 2.5.3 LANGUAGES C ASM)
project(Gint VERSION 2.9.0 LANGUAGES C ASM)
include(GitVersionNumber)
include(Fxconv)
option(GINT_USER_VRAM "Put all VRAMs into the user stack (fx-CG 50 only)")
option(GINT_NO_OS_STACK "Do not use the OS stack as a memory pool (fx-CG only)")
option(GINT_STATIC_GRAY "Use static memory instead of malloc for gray buffers (fx-9860G only)")
option(GINT_KMALLOC_DEBUG "Enable debug functions for kmalloc")
@ -22,17 +22,55 @@ endif()
configure_file(include/gint/config.h.in include/gint/config.h)
set(SOURCES_COMMON
# Clock Pulse Generator driver
src/cpg/cpg.c
src/cpg/overclock.c
# CPU driver
src/cpu/atomic.c
src/cpu/cpu.c
src/cpu/ics.s
src/cpu/registers.s
src/cpu/sleep.c
# Direct Memory Access driver
src/dma/dma.c
src/dma/inth.s
src/dma/memcpy.c
src/dma/memset.c
# Filesystem interface
src/fs/close.c
src/fs/closedir.c
src/fs/creat.c
src/fs/fdopendir.c
src/fs/fs.c
src/fs/lseek.c
src/fs/mkdir.c
src/fs/open.c
src/fs/opendir.c
src/fs/pread.c
src/fs/pwrite.c
src/fs/read.c
src/fs/readdir.c
src/fs/rewinddir.c
src/fs/rmdir.c
src/fs/seekdir.c
src/fs/stat.c
src/fs/telldir.c
src/fs/unlink.c
src/fs/write.c
# Filesystem interface to Fugue
src/fs/fugue/BFile_Ext_Stat.c
src/fs/fugue/fugue.c
src/fs/fugue/fugue_dir.c
src/fs/fugue/fugue_open.c
src/fs/fugue/fugue_mkdir.c
src/fs/fugue/fugue_stat.c
src/fs/fugue/fugue_rmdir.c
src/fs/fugue/fugue_unlink.c
src/fs/fugue/util.c
# Interrupt Controller driver
src/intc/intc.c
src/intc/inth.s
# Kernel
src/kernel/exch.c
src/kernel/exch.s
src/kernel/hardware.c
@ -43,30 +81,44 @@ set(SOURCES_COMMON
src/kernel/syscalls.S
src/kernel/tlbh.S
src/kernel/world.c
# Key Scan Interface driver
src/keysc/getkey.c
src/keysc/iokbd.c
src/keysc/keycodes.c
src/keysc/keydev.c
src/keysc/keydev_idle.c
src/keysc/keydev_process_key.c
src/keysc/keydown_all.c
src/keysc/keydown_any.c
src/keysc/keysc.c
src/keysc/scan_frequency.c
# Memory allocator
src/kmalloc/arena_gint.c
src/kmalloc/arena_osheap.c
src/kmalloc/kmalloc.c
# MMU driver
src/mmu/mmu.c
# Rendering
src/render/dhline.c
src/render/dimage.c
src/render/dline.c
src/render/dprint.c
src/render/drect_border.c
src/render/dtext.c
src/render/dupdate_hook.c
src/render/dvline.c
src/render/topti.c
# RTC driver
src/rtc/rtc.c
src/rtc/rtc_ticks.c
# Sound Processing Unit driver
src/spu/spu.c
# Timer Unit driver
src/tmu/inth-etmu.s
src/tmu/inth-tmu.s
src/tmu/sleep.c
src/tmu/tmu.c
# USB driver
src/usb/classes/ff-bulk.c
src/usb/configure.c
src/usb/pipes.c
@ -75,6 +127,7 @@ set(SOURCES_COMMON
src/usb/usb.c
)
set(SOURCES_FX
# Gray engine
src/gray/engine.c
src/gray/gclear.c
src/gray/gint_gline.c
@ -82,6 +135,7 @@ set(SOURCES_FX
src/gray/grect.c
src/gray/gsubimage.c
src/gray/gtext.c
# Rendering
src/render-fx/bopti-asm-gray-scsp.s
src/render-fx/bopti-asm-gray.s
src/render-fx/bopti-asm-mono-scsp.s
@ -96,13 +150,46 @@ set(SOURCES_FX
src/render-fx/masks.c
src/render-fx/topti-asm.s
src/render-fx/topti.c
# T6K11 driver
src/t6k11/t6k11.c
src/usb/classes/ff-bulk-gray.c
)
set(SOURCES_CG
# R61524 driver
src/r61524/r61524.c
src/render-cg/bopti-asm.s
src/render-cg/bopti.c
# Image library
src/image/image_alloc.c
src/image/image_alloc_palette.c
src/image/image_alpha.c
src/image/image_clear.c
src/image/image_copy.c
src/image/image_copy_alloc.c
src/image/image_copy_palette.c
src/image/image_create.c
src/image/image_create_vram.c
src/image/image_data_size.c
src/image/image_decode_pixel.c
src/image/image_fill.c
src/image/image_free.c
src/image/image_get_pixel.c
src/image/image_hflip.c
src/image/image_hflip_alloc.c
src/image/image_linear.c
src/image/image_linear.S
src/image/image_linear_alloc.c
src/image/image_rotate.c
src/image/image_rotate_around.c
src/image/image_rotate_around_scale.c
src/image/image_scale.c
src/image/image_set_palette.c
src/image/image_set_pixel.c
src/image/image_sub.c
src/image/image_target.c
src/image/image_valid.c
src/image/image_vflip.c
src/image/image_vflip_alloc.c
# Rendering
src/render-cg/dclear.c
src/render-cg/dpixel.c
src/render-cg/drect.c
@ -112,6 +199,37 @@ set(SOURCES_CG
src/render-cg/gint_dline.c
src/render-cg/topti-asm.s
src/render-cg/topti.c
# Fast image renderer
src/render-cg/image/image.c
src/render-cg/image/image_rgb16.S
src/render-cg/image/image_rgb16_normal.S
src/render-cg/image/image_rgb16_clearbg_dye.S
src/render-cg/image/image_rgb16_swapcolor.S
src/render-cg/image/image_p8.S
src/render-cg/image/image_p8_normal.S
src/render-cg/image/image_p8_clearbg.S
src/render-cg/image/image_p8_swapcolor.S
src/render-cg/image/image_p8_dye.S
src/render-cg/image/image_p4.S
src/render-cg/image/image_p4_normal.S
src/render-cg/image/image_p4_clearbg.S
src/render-cg/image/image_p4_clearbg_alt.S
src/render-cg/image/image_p4_swapcolor.S
src/render-cg/image/image_p4_dye.S
# Interface to the fast image renderer
src/render-cg/image/image_rgb16.c
src/render-cg/image/image_rgb16_effect.c
src/render-cg/image/image_rgb16_swapcolor.c
src/render-cg/image/image_rgb16_dye.c
src/render-cg/image/image_p8.c
src/render-cg/image/image_p8_effect.c
src/render-cg/image/image_p8_swapcolor.c
src/render-cg/image/image_p8_dye.c
src/render-cg/image/image_p4.c
src/render-cg/image/image_p4_clearbg_alt.c
src/render-cg/image/image_p4_effect.c
src/render-cg/image/image_p4_swapcolor.c
src/render-cg/image/image_p4_dye.c
)
set(ASSETS_FX src/font5x7.png)
@ -120,8 +238,7 @@ fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG})
include_directories(
"${PROJECT_SOURCE_DIR}/include"
"${PROJECT_BINARY_DIR}/include"
"${FXSDK_COMPILER_INSTALL}/include/openlibm")
"${PROJECT_BINARY_DIR}/include")
add_compile_options(-Wall -Wextra -std=c11 -Os -fstrict-volatile-bitfields -mtas)
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
@ -141,16 +258,16 @@ endif()
set_target_properties("${NAME}" PROPERTIES OUTPUT_NAME "${NAME}")
# Library file
install(TARGETS "${NAME}" DESTINATION "${FXSDK_COMPILER_INSTALL}")
install(TARGETS "${NAME}" DESTINATION "${FXSDK_LIB}")
# Linker script
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_SCRIPT}"
DESTINATION "${FXSDK_COMPILER_INSTALL}")
DESTINATION "${FXSDK_LIB}")
# Headers
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include"
DESTINATION "${FXSDK_COMPILER_INSTALL}"
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
DESTINATION "${FXSDK_INCLUDE}"
FILES_MATCHING PATTERN "*.h")
# Auto-generated config header
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/gint/config.h"
DESTINATION "${FXSDK_COMPILER_INSTALL}/include/gint")
DESTINATION "${FXSDK_INCLUDE}/gint")
# CMake module to find gint
install(FILES cmake/FindGint.cmake DESTINATION "${FXSDK_CMAKE_MODULE_PATH}")

View File

@ -66,12 +66,6 @@ CMake environment for the calculator. gint is always installed in the
compiler's install path (as given by `sh-elf-gcc --print-search-dirs`) which is
detected automatically, so normally you don't need to set the install prefix.
fx-CG 50 developers probably want a g3a wrapper as well; the reference
implementation is Tari's [mkg3a](https://gitlab.com/taricorp/mkg3a). This is
needed at the very last compilation step to create the g3a file. On Arch Linux,
you can use the [AUR/mkg3a](https://aur.archlinux.org/packages/mkg3a) package
maintained directly by Tari.
gint depends on OpenLibm and fxlibc (linked above). Both can be installed
easily (essentially copy-paste the command from the respective READMEs). You
can technically use another libc but there be dragons.
@ -89,6 +83,7 @@ Run without `-c` to build. This configures automatically.
```
% fxsdk build-fx
% fxsdk build-fx install
```
The available options are:
@ -105,6 +100,11 @@ The available options are:
* `-DGINT_USER_VRAM=1`: Store all VRAMs in the user stack (takes up 350k/512k)
```
% fxsdk build-cg
% fxsdk build-cg install
```
## Using in CMake-based add-ins
Find the `Gint` module and link against `Gint::Gint`. gint declares the include

14
TODO
View File

@ -1,22 +1,22 @@
Extensions on existing code:
* clock: mono support
* usb: add PC->calc reading, and interrupt pipes
* fs: support RAM files
* fs: support USB streams as generic file descriptors
* fugue: support glob() to abstract away BFile entirely
* fugue/fs: offer a primitive to remove recursively, which is native in Fugue
* bfile: implement the optimization-restart as realized by Kbd2
* kernel: better restore to userspace before panic (ensure BL=0 IMASK=0)
* project: add license file
* kernel: group linker script symbols in a single header file
* bopti: try to display fullscreen images with TLB access + DMA on fxcg50
* core: try to leave add-in without reset in case of panic
* core: use cmp/str for memchr()
* r61524: brightness control and clean the file
* core: review forgotten globals and MPU addresses not in <gint/mpu/*.h>
* core: run destructors when a task-switch results in leaving the app
* core rtc: use qdiv10 to massively improve division performance
Future directions.
* A complete file system abstraction
* Integrate overclock management
Future directions:
* Audio playback using TSWilliamson's libsnd method
* Serial communication
* USB communication, using Yatis' reverse-engineering of the module
* Make fx9860g projects work out of the box on fxcg50
* Use the DSP to enhance parallel computation
* Base for Yatis' threads library

View File

@ -14,13 +14,16 @@ endif()
execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libgint-${PC}.a
OUTPUT_VARIABLE GINT_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND ${CMAKE_C_COMPILER} -print-file-name=include/gint/config.h
OUTPUT_VARIABLE GINT_CONFIG_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
COMMAND ${CMAKE_C_COMPILER} -print-file-name=libc.a
OUTPUT_VARIABLE FXLIBC_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(
COMMAND fxsdk path include
OUTPUT_VARIABLE FXSDK_INCLUDE
OUTPUT_STRIP_TRAILING_WHITESPACE)
set(GINT_CONFIG_PATH "${FXSDK_INCLUDE}/gint/config.h")
if("${GINT_PATH}" STREQUAL "libgint-${PC}.a")
unset(PATH_TO_LIBGINT)
@ -35,13 +38,25 @@ else()
endif()
endif()
if("${FXLIBC_PATH}" STREQUAL "libc.a")
unset(PATH_TO_FXLIBC)
else()
set(PATH_TO_FXLIBC TRUE)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Gint
REQUIRED_VARS PATH_TO_LIBGINT
VERSION_VAR GINT_VERSION
)
REQUIRED_VARS PATH_TO_FXLIBC PATH_TO_LIBGINT
VERSION_VAR GINT_VERSION)
if(Gint_FOUND)
if(NOT TARGET Gint::Fxlibc)
add_library(Gint::Fxlibc UNKNOWN IMPORTED)
endif()
set_target_properties(Gint::Fxlibc PROPERTIES
IMPORTED_LOCATION "${FXLIBC_PATH}"
INTERFACE_LINK_OPTIONS "-lgcc")
if(NOT TARGET Gint::Gint)
add_library(Gint::Gint UNKNOWN IMPORTED)
endif()
@ -49,8 +64,9 @@ if(Gint_FOUND)
IMPORTED_LOCATION "${GINT_PATH}"
INTERFACE_COMPILE_OPTIONS -fstrict-volatile-bitfields
INTERFACE_COMPILE_DEFINITIONS "${INTF_DEFN}"
INTERFACE_INCLUDE_DIRECTORIES "${FXSDK_COMPILER_INSTALL}/include/openlibm"
INTERFACE_LINK_LIBRARIES "-lc;-lopenlibm;-lgcc"
INTERFACE_LINK_OPTIONS "${INTF_LINK}"
)
INTERFACE_LINK_LIBRARIES "-lopenlibm;-lgcc"
INTERFACE_LINK_OPTIONS "${INTF_LINK}")
target_link_libraries(Gint::Gint INTERFACE Gint::Fxlibc)
target_link_libraries(Gint::Fxlibc INTERFACE Gint::Gint)
endif()

View File

@ -35,8 +35,7 @@ MEMORY
/* On-chip IL memory */
ilram (rwx): o = 0xe5200000, l = 4k
/* On-chip X and Y memory */
xram (rwx): o = 0xe5007000, l = 8k
yram (rwx): o = 0xe5017000, l = 8k
xyram (rwx): o = 0xe500e000, l = 16k
}
SECTIONS
@ -128,7 +127,7 @@ SECTIONS
.bss (NOLOAD) : {
_rbss = . ;
*(.bss COMMON)
*(.bss .bss.* COMMON)
*(B R)
. = ALIGN(16);
@ -188,29 +187,18 @@ SECTIONS
. = ALIGN(16);
} > ilram AT> rom
. = ORIGIN(xram);
.xram ALIGN(4) : ALIGN(4) {
_lxram = LOADADDR(.xram);
_rxram = . ;
. = ORIGIN(xyram);
.xyram ALIGN(4) : ALIGN(4) {
_lxyram = LOADADDR(.xyram);
_rxyram = . ;
*(.xram)
*(.xram .yram .xyram)
. = ALIGN(16);
} > xram AT> rom
. = ORIGIN(yram);
.yram ALIGN(4) : ALIGN(4) {
_lyram = LOADADDR(.yram);
_ryram = . ;
*(.yram)
. = ALIGN(16);
} > yram AT> rom
} > xyram AT> rom
_silram = SIZEOF(.ilram);
_sxram = SIZEOF(.xram);
_syram = SIZEOF(.yram);
_sxyram = SIZEOF(.xyram);
@ -239,7 +227,8 @@ SECTIONS
/DISCARD/ : {
/* Debug sections (often from libgcc) */
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
.debug_ranges .debug_line .debug_str)
.debug_ranges .debug_line .debug_str .debug_frame
.debug_loclists .debug_rnglists)
/* Java class registration (why are they even here?!) */
*(.jcr)
/* Asynchronous unwind tables: no C++ exception handling */

View File

@ -17,12 +17,11 @@ MEMORY
/* Static RAM; stack grows down from the end of this region.
The first 5k (0x1400 bytes) are reserved by gint for the VBR space,
which is loaded dynamically and accessed through P1 */
ram (rw): o = 0x08101400, l = 507k
ram (rw): o = 0x08101400, l = 491k
/* On-chip IL memory */
ilram (rwx): o = 0xe5200000, l = 4k
/* On-chip X and Y memory */
xram (rwx): o = 0xe5007000, l = 8k
yram (rwx): o = 0xe5017000, l = 8k
xyram (rwx): o = 0xe500e000, l = 16k
}
SECTIONS
@ -102,8 +101,7 @@ SECTIONS
.bss (NOLOAD) : {
_rbss = . ;
*(.bss.vram)
*(.bss COMMON)
*(.bss .bss.* COMMON)
. = ALIGN(16);
} > ram :NONE
@ -144,29 +142,18 @@ SECTIONS
. = ALIGN(16);
} > ilram AT> rom
. = ORIGIN(xram);
.xram ALIGN(4) : ALIGN(4) {
_lxram = LOADADDR(.xram);
_rxram = . ;
. = ORIGIN(xyram);
.xyram ALIGN(4) : ALIGN(4) {
_lxyram = LOADADDR(.xyram);
_rxyram = . ;
*(.xram)
*(.xram .yram .xyram)
. = ALIGN(16);
} > xram AT> rom
. = ORIGIN(yram);
.yram ALIGN(4) : ALIGN(4) {
_lyram = LOADADDR(.yram);
_ryram = . ;
*(.yram)
. = ALIGN(16);
} > yram AT> rom
} > xyram AT> rom
_silram = SIZEOF(.ilram);
_sxram = SIZEOF(.xram);
_syram = SIZEOF(.yram);
_sxyram = SIZEOF(.xyram);
/* gint's uninitialized BSS section, going to static RAM. All the large
data arrays will be located here */
@ -191,7 +178,8 @@ SECTIONS
*(.gint.data.sh3 .gint.bss.sh3)
/* Debug sections (often from libgcc) */
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
.debug_ranges .debug_line .debug_str)
.debug_ranges .debug_line .debug_str .debug_frame
.debug_loclists .debug_rnglists)
/* Java class registration (why are they even here?!) */
*(.jcr)
/* Asynchronous unwind tables: no C++ exception handling */

View File

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

View File

@ -5,6 +5,10 @@
#ifndef GINT_CLOCK
#define GINT_CLOCK
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
/* This header used to expose the sleep() function; include <gint/cpu.h> to
ensure this is still the case */
@ -48,11 +52,78 @@ typedef struct
module; this address never changes. */
const clock_frequency_t *clock_freq(void);
/* cpg_compute_freq(): Compute the current clock frequency
This function updates the data structure returned by clock_freq() by
determining the current clock frequencies from the CPG. */
void cpg_compute_freq(void);
//---
// Overclock
//---
/* TODO: All overclock */
/* The following enumerations define the clock speed settings supported by
gint. These are always the settings from Ftune/Ptune, which are the most
widely tested and gint treats as the standard. */
enum {
/* Combinations of hardware settings that are none of Ftune's levels */
CLOCK_SPEED_UNKNOWN = 0,
/* Ftune's 5 default overclock levels. The main settings are listed below,
thoug many more are involved.
On SH4 fx-9860G-likr:
(Not supported yet)
On the fx G-III series:
(Not supported yet)
On fx-CG 10/20:
F1: CPU @ 58 MHz, BFC @ 29 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [Improved memory speed]
F3: CPU @ 118 MHz, BFC @ 58 MHz [Faster than F2]
F4: CPU @ 118 MHz, BFC @ 118 MHz [Fastest bus option]
F5: CPU @ 191 MHz, BFC @ 94 MHz [Fastest CPU option]
On fx-CG 50:
F1: CPU @ 116 MHz, BFC @ 58 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [Clearly slower: F2 < F3 < F1]
F3: CPU @ 94 MHz, BFC @ 47 MHz [Clearly slower: F2 < F3 < F1]
F4: CPU @ 232 MHz, BFC @ 58 MHz [Fastest CPU option]
F5: CPU @ 189 MHz, BFC @ 94 MHz [Fastest bus option] */
CLOCK_SPEED_F1 = 1,
CLOCK_SPEED_F2 = 2,
CLOCK_SPEED_F3 = 3,
CLOCK_SPEED_F4 = 4,
CLOCK_SPEED_F5 = 5,
/* The default clock speed is always Ftune's F1 */
CLOCK_SPEED_DEFAULT = CLOCK_SPEED_F1,
};
#ifdef FXCG50
/* clock_get_speed(): Determine the current clock speed
This function compares the current hardware state with the settings for each
speed level and returns the current one. If the hardware state does not
correspond to any of Ftune's settings, CLOCK_SPEED_UNKNOWN is returned. */
int clock_get_speed(void);
/* clock_set_speed(): Set the current clock speed
This function sets the clock speed to the desired level. This is "the
overclock function", although depending on the model or settings it is also
the downclocking function.
The process of changing clock speeds is non-trivial, requires waiting for
the DMA to finish its work and slightly affects running timers. You should
avoid changing the clock speed constantly if not necessary. If this function
detects that the desired clock speed is already in use, it returns without
performing any change.
Currently the clock speed is not reset during a world switch nor when
leaving the add-in. */
void clock_set_speed(int speed);
#endif
//---
// Sleep functions
@ -72,4 +143,29 @@ void sleep_us_spin(uint64_t delay_us);
/* sleep_ms(): Sleep for a fixed duration in milliseconds */
#define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull)
//---
// Low-level overclock functions
//
// These low-level functions directly read or write registers involved in
// setting the overclock level. Don't use them directly unless you understand
// how their interactions with the environment; instead, use clock_set_speed().
//---
struct cpg_overclock_setting
{
uint32_t FLLFRQ, FRQCR;
uint32_t CS0BCR, CS2BCR, CS3BCR, CS5aBCR;
uint32_t CS0WCR, CS2WCR, CS3WCR, CS5aWCR;
};
/* Queries the clock setting from the hardware. */
void cpg_get_overclock_setting(struct cpg_overclock_setting *s);
/* Applies the specified overclock setting. */
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s);
#ifdef __cplusplus
}
#endif
#endif /* GINT_CLOCK */

View File

@ -16,10 +16,18 @@
0x03f7c0a0 = Commit 3f7c0a0 */
#define GINT_HASH 0x@GINT_GIT_HASH@
/* GINT_USER_VRAM: Selects whether to store VRAMs in the user stack (occupying
350k/512k of the area) or in the system stack (the default). (fx-CG 50) */
/* GINT_NO_OS_STACK: Disables using a chunk of the OS stack as a heap. The top
section covering 355/512 ko is otherwise used. (fx-CG 50) */
#cmakedefine GINT_NO_OS_STACK
/* GINT_USER_VRAM: Selects whether to store VRAMs in the user stack or in the
OS stack. Deprecated, now controlled by GINT_NO_OS_STACK. (fx-CG 50) */
#cmakedefine GINT_USER_VRAM
#ifdef GINT_USER_VRAM
# define GINT_NO_OS_STACK
#endif
/* GINT_STATIC_GRAY: Selects whether additional gray VRAMs are allocated
statically or in the system heap (fx-9860G) */
#cmakedefine GINT_STATIC_GRAY

View File

@ -5,6 +5,10 @@
#ifndef GINT_CPU
#define GINT_CPU
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
//---
@ -107,6 +111,36 @@ void sleep_block(void);
/* sleep_unblock(): Cancel a processor sleep block */
void sleep_unblock(void);
//---
// Interrupt-cancellable sleeps
//
// The sleep() function has the significant drawback of not synchronizing with
// interrupts. Programs usually run [while(<interrupt not occurred>) sleep()],
// which fails if the interrupt occurs between the time the condition is
// checked and time the sleep instruction is executed.
//
// Interrupt-cancellable sleep is a software mechanism by which the interrupt
// disables the sleep instruction itself (by replacing it with a nop), which
// ensures that the CPU cannot go to sleep after the interrupt occurs.
//---
/* cpu_csleep_t: A cancellable sleep function
This object holds sleep code that can be disabled by an interrupt. The size
and layout of this variable is dependent on some assembler code. This should
be allocated on the stack, heap, or on-chip memory, because the data segment
is not executable! */
typedef GALIGNED(4) uint8_t cpu_csleep_t[20];
/* cpu_csleep_init(): Create an ICS function
This function initializes the provided ICS routine. */
void cpu_csleep_init(cpu_csleep_t *ics);
/* cpu_csleep(): Run the sleep function until the sleep is cancelled */
void cpu_csleep(cpu_csleep_t *ics);
/* cpu_csleep_cancel(): Cancel the sleep function from within an interrupt */
void cpu_csleep_cancel(cpu_csleep_t *ics);
//---
// Configuration
//---
@ -117,4 +151,8 @@ void sleep_unblock(void);
configure(), this is reset to 0. */
void cpu_configure_VBR(uint32_t VBR);
#ifdef __cplusplus
}
#endif
#endif /* GINT_CPU */

View File

@ -14,8 +14,8 @@
#define GBSS3 __attribute__((section(".gint.bss.sh3")))
/* Objects for the ILRAM, XRAM and YRAM regions */
#define GILRAM __attribute__((section(".ilram")))
#define GXRAM __attribute__((section(".xram")))
#define GYRAM __attribute__((section(".yram")))
#define GXRAM __attribute__((section(".xyram")))
#define GYRAM __attribute__((section(".xyram")))
/* Unused parameters or variables */
#define GUNUSED __attribute__((unused))

View File

@ -11,6 +11,10 @@
#ifndef GINT_DEFS_CALL
#define GINT_DEFS_CALL
#ifdef __cplusplus
extern "C" {
#endif
/* gint_call_arg_t: All types of arguments allowed in an indirect call
Because a function call cannot be easily pieced together, there are
@ -111,19 +115,31 @@ typedef struct {
* error: cast to union type from type not present in union
-> This is emitted if you pass a parameter of an invalid type. */
#define GINT_CALL(func, ...) \
((gint_call_t){ .function = func, .args = { \
((gint_call_t){ .function = (void *)func, .args = { \
__VA_OPT__(GINT_CALL_ARGS1(__VA_ARGS__)) \
}})
#define GINT_CALL_ARGS1(a1, ...) \
(gint_call_arg_t)(a1), __VA_OPT__(GINT_CALL_ARGS2(__VA_ARGS__))
GINT_CALL_ARG(a1), __VA_OPT__(GINT_CALL_ARGS2(__VA_ARGS__))
#define GINT_CALL_ARGS2(a2, ...) \
(gint_call_arg_t)(a2), __VA_OPT__(GINT_CALL_ARGS3(__VA_ARGS__))
GINT_CALL_ARG(a2), __VA_OPT__(GINT_CALL_ARGS3(__VA_ARGS__))
#define GINT_CALL_ARGS3(a3, ...) \
(gint_call_arg_t)(a3), __VA_OPT__(GINT_CALL_ARGS4(__VA_ARGS__))
GINT_CALL_ARG(a3), __VA_OPT__(GINT_CALL_ARGS4(__VA_ARGS__))
#define GINT_CALL_ARGS4(a4, ...) \
({ __VA_OPT__(_Static_assert(0, \
"GINT_CALL: too many arguments (maximum 4)");) \
(gint_call_arg_t)(a4); })
GINT_CALL_ARG(a4); })
#ifdef __cplusplus
/* Kind of ugly but I don't have super cool templated logic right now.
TODO: Allow any 4-byte type using C++ magic. */
#define GINT_CALL_ARG(expr) ({ \
auto __arg = (expr); \
(*reinterpret_cast<gint_call_arg_t *>(&__arg)); \
})
#else
/* GCC extension: Cast to union */
#define GINT_CALL_ARG(expr) (gint_call_arg_t)(expr)
#endif
/* GINT_CALL_NULL: Empty function call */
#define GINT_CALL_NULL ((gint_call_t){ .function = NULL, .args = {} })
@ -131,7 +147,12 @@ typedef struct {
/* gint_call(): Perform an indirect call */
static GINLINE int gint_call(gint_call_t cb)
{
#ifdef __cplusplus
int (*f)(int r4, int r5, int r6, int r7) = (decltype(f))cb.function;
#else
int (*f)(int r4, int r5, int r6, int r7) = cb.function;
#endif
return f(cb.args[0].i, cb.args[1].i, cb.args[2].i, cb.args[3].i);
}
@ -195,4 +216,8 @@ static GINLINE gint_call_t GINT_CALL_INC_STOP(int volatile *pointer)
return GINT_CALL(GINT_CALL_INC_STOP_function, pointer);
}
#ifdef __cplusplus
}
#endif
#endif /* GINT_DEFS_CALL */

View File

@ -13,13 +13,13 @@
#include <stdint.h>
/* For human-readable boolean types */
#include <stdbool.h>
/* Common system types: ssize_t, off_t, etc. */
#include <sys/types.h>
/* For va_list */
#include <stdarg.h>
/* Fixed-width types for bit fields are quite meaningless */
typedef unsigned int uint;
/* Signed size_t */
typedef signed int ssize_t;
/* Offset in file (used in standard functions) */
typedef unsigned int off_t;
//---
// Structure elements

View File

@ -1,11 +1,15 @@
//---
// gint:display-cg - fxcg50 rendering functions
// gint:display-cg - fx-CG 50 rendering functions
//
// This module covers all 16-bit opaque rendering functions. For
// gamma-related functions, color composition, check out a color library.
// This module covers rendering functions specific to the fx-CG 50. In addition
// to triple-buffering management, this mainly includes image manipulation
// tools as well as the very versatile dimage_effect() and dsubimage_effect()
// functions that support high-performance image rendering with a number of
// geometric and color effects.
//
// All the functions in this module work on a 396x224 resolution - gint
// lets you use the full surface!
// The fx-CG OS restricts the display to a 384x216 rectangle rougly around the
// center, leaving margins on three sides. However, gint configures the display
// to use the full 396x224 surface!
//---
#ifndef GINT_DISPLAY_CG
@ -13,7 +17,12 @@
#ifdef FXCG50
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/image.h>
/* Dimensions of the VRAM */
#define DWIDTH 396
@ -53,37 +62,9 @@ enum {
green is not used). */
#define C_RGB(r,g,b) (((r) << 11) | ((g) << 6) | (b))
//---
// Image rendering (bopti)
//---
/* See <gint/image.h> for the details on image manipulation. */
typedef image_t bopti_image_t;
/* bopti_image_t: Image files encoded for bopti
This format is created by the fxSDK's [fxconv] tool from standard images. */
typedef struct
{
/* Color profile (type of palette), could be extended into a bit field
later on */
uint16_t profile;
/* Color code assigned to transparent pixels (unused in 16-bit) */
uint16_t alpha;
/* Full width and height, in pixels */
uint16_t width;
uint16_t height;
/* Color palette:
* 16-bit and 16-bit alpha: none
* 8-bit: 256 colors (total 512 bytes)
* 4-bit: 16 colors (total 32 bytes)
Then raw pixel data in row-major order. */
uint16_t data[];
} GPACKED(4) bopti_image_t;
/* Old alias to image_t, now deprecated because of libimg */
typedef bopti_image_t image_t __attribute__((deprecated(
"image_t has been renamed to bopti_image_t")));
//---
// Video RAM management
@ -123,6 +104,10 @@ void dsetvram(uint16_t *main, uint16_t *secondary);
Returns the VRAM buffer addresses used to render on fx-CG 50. */
void dgetvram(uint16_t **main, uint16_t **secondary);
#ifdef __cplusplus
}
#endif
#endif /* FXCG50 */
#endif /* GINT_DISPLAY_CG */

View File

@ -11,6 +11,10 @@
#ifdef FX9860G
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
/* Dimensions of the VRAM */
@ -83,6 +87,10 @@ typedef struct
} GPACKED(4) bopti_image_t;
#ifdef __cplusplus
}
#endif
#endif /* FX9860G */
#endif /* GINT_DISPLAY_FX */

View File

@ -9,7 +9,12 @@
#ifndef GINT_DISPLAY
#define GINT_DISPLAY
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/defs/call.h>
/* Platform-specific functions include VRAM management and the definition of
the color_t type. */
@ -58,6 +63,25 @@
fx-CG 50: 11 ms */
void dupdate(void);
/* dupdate_set_hook(): Define a function to be called after each dupdate()
This functions configures the update hook, which is called after each
dupdate() has sent VRAM to the display (but before VRAMs are switched when
triple-buffering is used on fx-CG 50).
The hook is mostly useful to send a copy of the frame to another medium,
typically through a USB connection to a projector-style application. See
usb_fxlink_videocapture() in <gint/usb-ff-bulk.h> for an example.
The function is an indirect call; create one with the GINT_CALL() macro from
<gint/defs/call.h>. Pass GINT_CALL_NULL to disable the feature.
@function Indirect call to perform after each dupdate(). */
void dupdate_set_hook(gint_call_t function);
/* dupdate_get_hook(): Get a copy of the dupdate() hook */
gint_call_t dupdate_get_hook(void);
//---
// Area rendering functions
//---
@ -398,4 +422,8 @@ enum {
void dsubimage(int x, int y, bopti_image_t const *image, int left, int top,
int width, int height, int flags);
#ifdef __cplusplus
}
#endif
#endif /* GINT_DISPLAY */

View File

@ -5,6 +5,10 @@
#ifndef GINT_DMA
#define GINT_DMA
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/defs/call.h>
@ -117,4 +121,8 @@ void *dma_memset(void *dst, uint32_t pattern, size_t size);
void *dma_memcpy(void * __restrict dst, const void * __restrict src,
size_t size);
#ifdef __cplusplus
}
#endif
#endif /* GINT_DMA */

View File

@ -5,6 +5,10 @@
#ifndef GINT_DRIVERS
#define GINT_DRIVERS
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
@ -300,4 +304,8 @@ void gint_world_switch_in(gint_world_t world_os, gint_world_t world_addin);
/* Switch from a gint-managed world to the OS world */
void gint_world_switch_out(gint_world_t world_addin, gint_world_t world_os);
#ifdef __cplusplus
}
#endif
#endif /* GINT_DRIVERS */

View File

@ -7,6 +7,10 @@
#ifndef GINT_DRIVERS_IOKBD
#define GINT_DRIVERS_IOKBD
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data
@ -14,4 +18,8 @@
@scan 12-byte buffer filled with row data */
void iokbd_scan(uint8_t *scan);
#ifdef __cplusplus
}
#endif
#endif /* GINT_DRIVERS_IOKBD */

View File

@ -2,27 +2,18 @@
// gint:keydev - Kernel's keyboard input devices
//---
#include <gint/keyboard.h>
#ifndef GINT_KEYDEV
#define GINT_KEYDEV
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/keyboard.h>
/* Size of the buffer event queue */
#define KEYBOARD_QUEUE_SIZE 32
/* keydev_event_t: Change of state in a full row */
typedef struct {
/* Device time for the event */
uint16_t time;
/* Keys that changed state */
uint8_t changed;
/* Row number */
uint8_t row :4;
/* Type of change, either KEYEV_DOWN or KEYEV_UP */
uint8_t kind :4;
} keydev_event_t;
/* Event transforms
Every keyboard input device has a built-in event stream editor that can
@ -80,14 +71,26 @@ typedef struct {
enum {
/* Delayed SHIFT: Pressing then immediately releasing SHIFT when the
keyboard is idle applies SHIFT to the next repeat streak. */
KEYDEV_TR_DELAYED_SHIFT = 0x01,
KEYDEV_TR_DELAYED_SHIFT = 0x01, /* = GETKEY_MOD_SHIFT */
/* Delayed ALPHA: Idem with the ALPHA key */
KEYDEV_TR_DELAYED_ALPHA = 0x02,
KEYDEV_TR_DELAYED_ALPHA = 0x02, /* = GETKEY_MOD_ALPHA */
/* Combination of the delayed modifiers */
KEYDEV_TR_DELAYED_MODS = KEYDEV_TR_DELAYED_SHIFT
| KEYDEV_TR_DELAYED_ALPHA,
/* Instant SHIFT: Each individual event of every repeat streak gets
SHIFT applied if SHIFT is pressed at the time of the repeat. */
KEYDEV_TR_INSTANT_SHIFT = 0x04,
/* Instant ALPHA: Idem with the ALPHA key */
KEYDEV_TR_INSTANT_ALPHA = 0x08,
/* Combination of the instance modifiers */
KEYDEV_TR_INSTANT_MODS = KEYDEV_TR_INSTANT_SHIFT
| KEYDEV_TR_INSTANT_ALPHA,
/* Combination of all modifiers */
KEYDEV_TR_ALL_MODS = KEYDEV_TR_DELAYED_MODS
| KEYDEV_TR_INSTANT_MODS,
/* Repeats: Keys are repeated according to a repeat filter function */
KEYDEV_TR_REPEATS = 0x10,
/* Delete Modifiers: Remove modifier keys from generated events, which
@ -97,6 +100,9 @@ enum {
KEYDEV_TR_DELETE_RELEASES = 0x40,
};
/* keydev_repeat_profile_t: Function deciding when and how to repeat keys. */
typedef int (*keydev_repeat_profile_t)(int key, int duration, int count);
/* keydev_transform_t: Full specification for transforms on the event stream */
typedef struct {
/* List of enabled transforms. The order is cannot be changed because
@ -118,7 +124,7 @@ typedef struct {
that the precision of the delay is limited by the speed at which the
keyboard is scanned, which is nowhere near microsecond-level. By
default (128 Hz) the precision is about 7.8 ms. */
int (*repeater)(int key, int duration, int count);
keydev_repeat_profile_t repeater;
} GPACKEDENUM keydev_transform_t;
@ -139,31 +145,16 @@ typedef struct {
keydown() are shortcuts for keydev functions using the physical keyboard as
their input. */
typedef struct {
/* Latest state of keys we are aware of. At every processing step, the
different between this and the fresh information is queued and this
is updated. state_now is identical to the real state obtained from
the device unless earlier events failed to be queued, in which case
a difference is maintained so they will be reconsidered later. */
GALIGNED(4) uint8_t state_now[12];
/* State of keys based on produced events. (state_queue + queue) is
always identical to (state_now). When the queue is empty both states
are the same. This is the user's view of the keyboard. */
GALIGNED(4) uint8_t state_queue[12];
/* Current device time in scanning-ticks */
uint time;
/* Last time when repeats were considered */
uint time_repeats;
/* Event queue (circular buffer) */
keydev_event_t queue[KEYBOARD_QUEUE_SIZE];
/* Next event in queue, position after last event in queue */
int8_t queue_next;
int8_t queue_end;
/* Number of events lost because of missing queue space */
uint events_lost;
/* Crafted events waiting to be picked up */
key_event_t out[8];
int out_size;
/* Event transforms */
keydev_transform_t tr;
@ -189,6 +180,22 @@ typedef struct {
/* Delay until next repeat, set by the repeat planner (us) */
int rep_delay;
/* Latest state of keys we are aware of. At every processing step, the
different between this and the fresh information is queued and this
is updated. state_now is identical to the real state obtained from
the device unless earlier events failed to be queued, in which case
a difference is maintained so they will be reconsidered later. */
GALIGNED(4) uint8_t state_now[12];
/* State of keys based on produced events. (state_queue + queue) is
always identical to (state_now). When the queue is empty both states
are the same. This is the user's view of the keyboard. */
GALIGNED(4) uint8_t state_queue[12];
/* Event queue (circular buffer) */
key_event_t queue[KEYBOARD_QUEUE_SIZE];
/* Parameters for the standard repeat function */
int rep_standard_first, rep_standard_next;
} keydev_t;
/* keydev_std(): Standard keyboard input device
@ -214,6 +221,12 @@ void keydev_process_state(keydev_t *d, uint8_t state[12]);
devices (such as demo replays) to feed in new events. */
void keydev_process_key(keydev_t *d, int keycode, bool state);
/* keydev_repeat_event(): Generate a repeat event if applicable
At the end of every scan tick, this source will generate a repeat event if
the repeat transform is enabled and the conditions for a repeat are
satisfied. */
key_event_t keydev_repeat_event(keydev_t *d);
/* keydev_tick(): Prepare the next tick
This function maintains time trackers in the device and should be called in
each frame after the scanning is finished and the keydev_process_*()
@ -227,19 +240,11 @@ void keydev_tick(keydev_t *d, uint us);
/* keydev_unqueue_event(): Retrieve the next keyboard event in queue
This source provides the queued KEYEV_UP and KEYEV_DOWN events generated
from the regular scans in chronological order. It does not generate the
KEYEV_HOLD events though, keyev_repeat_event() must be used to obtain these
at the end of every tick. */
This source provides the queued KEYEV_UP, KEYEV_DOWN and KEYEV_HOLD events
generated from the regular scans in chronological order. It does not apply
transforms; to do this, use keydev_read(). */
key_event_t keydev_unqueue_event(keydev_t *d);
/* keydev_repeat_event(): Generate a repeat event if applicable
At the end of every scan tick (or later if the application is unable to keep
up), this source will generate a repeat event if the repeat transform is
enabled and the conditions for a repeat are satisfied. */
key_event_t keydev_repeat_event(keydev_t *d);
/* keydev_idle(): Check if all keys are released
A list of keys to ignore can be specified as variable arguments. The list
must be terminated by a 0 keycode. */
@ -258,7 +263,32 @@ keydev_transform_t keydev_transform(keydev_t *d);
/* keydev_set_transform(): Set transform parameters */
void keydev_set_transform(keydev_t *d, keydev_transform_t tr);
/* keydev_set_standard_repeats(): Enable a simple repeater
This function changes the [repeater] member of the devices's transform. It
loads the default repeat profile which applies one delay (in us) before the
first repeat, and then a second, usually shorter delay, between subsequent
repeats.
The unit of the argument is in microseconds, but the granularity of the
delay is dependent on the keyboard scan frequency. In the default setting
(128 Hz scans), the possible repeat delays are approximately 8 ms, 16 ms,
23 ms, 31 ms. The system default is (500 ms, 125 ms). With the 128 Hz
setting, this default is reached exactly without approximation. gint's
default is (400 ms, 40 ms) for more reactivity.
Note: Due to a current API limitation, every input device uses the delays
for the physical keyboard.
@first_us Delay between key press and first repeat (microseconds)
@next_us Delay between subsequent repeats (microseconds) */
void keydev_set_standard_repeats(keydev_t *d, int first_us, int next_us);
/* keydev_read(): Retrieve the next transformed event */
key_event_t keydev_read(keydev_t *d);
#ifdef __cplusplus
}
#endif
#endif /* GINT_KEYDEV */

View File

@ -8,6 +8,10 @@
#ifndef GINT_DRIVERS_R61524
#define GINT_DRIVERS_R61524
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
enum {
@ -31,6 +35,34 @@ enum {
@method Transfer method, see above */
void r61524_display(uint16_t *vram, int start, int height, int method);
/* r61524_display_rect(): Send a rectangular section of VRAM to the display
This function updates the rectangle going from (xmin,ymin) to (xmax,ymax) in
the VRAM to the display (both ends included). This can be faster than a full
dupdate() or a striped r61524_display() depending on the situation. However,
because the source VRAM is not contiguous, the transfer is only possible by
CPU, so this is only interesting for small regions.
@vram Source VRAM with a stride of 396*2 bytes
@xmin @xmax Horizontal range to be updated (both included)
@ymin @ymax Vertical range to be updated (both included) */
void r61524_display_rect(uint16_t *vram, int xmin, int xmax, int ymin,
int ymax);
/* r61524_start_frame(): Prepare the display for a region update
This function sets up the display driver to receive graphics data to update
the rectangle going from (xmin,ymin) to (xmax,ymax) (both included). This is
the initial step of r61524_display(), which is normally followed by writing
all the data to 0xb4000000.
In order to write with the DMA, it is necessary to write the full horizontal
range and select ymin and ymax to be congruent to 0 and 3 modulo 4.
This function can be used to implement additional display driver update
methods or alternate rendering pipelines. */
void r61524_start_frame(int xmin, int xmax, int ymin, int ymax);
/* r162524_win_get() and r61524_win_set(): Manipulate the display window
These functions change the screen rectangle where data is shown. Normally
@ -43,4 +75,20 @@ void r61524_display(uint16_t *vram, int start, int height, int method);
void r61524_win_get(uint16_t *HSA, uint16_t *HEA, uint16_t *VSA,uint16_t *VEA);
void r61524_win_set(uint16_t HSA, uint16_t HEA, uint16_t VSA, uint16_t VEA);
//---
// Low-level functions
//---
/* r61524_get(): Read the value of an R61524 register
This is provided for testing and if you know what you're doing. */
uint16_t r61524_get(int ID);
/* r61524_set(): Write the value of an R61524 register
This is provided for testing and if you know what you're doing. */
void r61524_set(int ID, uint16_t value);
#ifdef __cplusplus
}
#endif
#endif /* GINT_DRIVERS_R61524 */

View File

@ -9,11 +9,17 @@
#ifndef GINT_DRIVERS_STATES
#define GINT_DRIVERS_STATES
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/mpu/dma.h>
#include <gint/clock.h>
/* Clock Pulse Generator (see cpg/cpg.c) */
typedef struct {
uint32_t SSCGCR;
struct cpg_overclock_setting speed;
} cpg_state_t;
/* CPU (see cpu/cpu.c) */
@ -91,4 +97,8 @@ typedef struct {
uint16_t DCPCFG, DCPMAXP, DCPCTR;
} usb_state_t;
#ifdef __cplusplus
}
#endif
#endif /* GINT_DRIVERS_STATES */

View File

@ -7,6 +7,10 @@
#ifndef GINT_DRIVERS_T6K11
#define GINT_DRIVERS_T6K11
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
/* t6k11_display() - send vram data to the LCD device
@ -51,4 +55,8 @@ void t6k11_contrast(int contrast);
@setting Requested backlight setting */
void t6k11_backlight(int setting);
#ifdef __cplusplus
}
#endif
#endif /* GINT_DRIVERS_T6K11 */

View File

@ -9,6 +9,10 @@
#ifndef GINT_EXC
#define GINT_EXC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <stdint.h>
@ -64,4 +68,8 @@ void gint_exc_catch(int (*handler)(uint32_t code));
@instructions Number of instructions to skip (usually only one) */
void gint_exc_skip(int instructions);
#ifdef __cplusplus
}
#endif
#endif /* GINT_EXC */

107
include/gint/fs.h Normal file
View File

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

View File

@ -5,6 +5,10 @@
#ifndef GINT_GINT
#define GINT_GINT
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/defs/call.h>
#include <gint/config.h>
@ -37,6 +41,13 @@ int gint_world_switch(gint_call_t function);
__attribute__((deprecated("Use gint_world_switch() instead")))
void gint_switch(void (*function)(void));
/* gint_world_sync(): Synchronize asynchronous drivers
This function waits for asynchronous tasks to complete by unbinding all
drivers. This is useful in certain hardware operations while remaining in
gint. */
void gint_world_sync(void);
/* gint_osmenu(): Call the calculator's main menu
This function safely invokes the calculator's main menu with gint_switch().
@ -89,4 +100,8 @@ static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
Returns the return value of the callback. */
extern int (*gint_inth_callback)(gint_call_t const *call);
#ifdef __cplusplus
}
#endif
#endif /* GINT_GINT */

View File

@ -5,6 +5,10 @@
#ifndef GINT_GRAY
#define GINT_GRAY
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/display.h>
@ -148,4 +152,8 @@ void dgray_getvram(uint32_t **light, uint32_t **dark);
These pointers can be used to make screen captures. */
void dgray_getscreen(uint32_t **light, uint32_t **dark);
#ifdef __cplusplus
}
#endif
#endif /* GINT_GRAY */

View File

@ -14,6 +14,10 @@
#ifndef GINT_HARDWARE
#define GINT_HARDWARE
#ifdef __cplusplus
extern "C" {
#endif
/* For compatibility with ASM, include the following bits only in C code */
#ifndef CPP_ASM
@ -66,6 +70,7 @@ void hw_detect(void);
#define HWKBD 8 /* Keyboard */
#define HWKBDSF /* Deprecated: use keysc_scan_frequency() */
#define HWDD /* Deprecated: use the T6K11/R61524 API */
#define HWFS 11 /* Filesystem type */
/*
** MPU type
@ -114,4 +119,19 @@ void hw_detect(void);
/* The keyboard uses a KEYSC-based scan method. This is only possible on SH4 */
#define HWKBD_KSI 0x04
/*
** Filesystem type
*/
/* Unknown or no filesystem. */
#define HWFS_NONE 0
/* CASIO's in-house filesystem, now deprecated. */
#define HWFS_CASIOWIN 1
/* Wrapper around Kyoto Software Research's Fugue VFAT implementation. */
#define HWFS_FUGUE 2
#ifdef __cplusplus
}
#endif
#endif /* GINT_HARDWARE */

845
include/gint/image.h Normal file
View File

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

View File

@ -5,6 +5,10 @@
#ifndef GINT_INTC
#define GINT_INTC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/defs/call.h>
@ -131,4 +135,8 @@ void *intc_handler(int event_code, void const *handler, size_t size);
Returns true on success, false if the event code is invalid. */
bool intc_handler_function(int event_code, gint_call_t function);
#ifdef __cplusplus
}
#endif
#endif /* GINT_INTC */

View File

@ -5,6 +5,10 @@
#ifndef GINT_KEYBOARD
#define GINT_KEYBOARD
#ifdef __cplusplus
extern "C" {
#endif
/* Keyboard, key events, keydown() and getkey()
gint's keyboard driver regularly scans the keyboard matrix and produces *key
@ -221,10 +225,6 @@ enum {
GETKEY_DEFAULT = 0xdf,
};
/* getkey_profile_t: Custom repeat profile function
See getkey_set_repeat_profile() for details. */
typedef int (*getkey_profile_t)(int key, int duration, int count);
/* getkey_feature_t: Custom feature function
See getkey_set_feature_function() for details. */
typedef bool (*getkey_feature_t)(key_event_t event);
@ -242,58 +242,20 @@ typedef bool (*getkey_feature_t)(key_event_t event);
value whenever you want to interrupt the call; using a timer with
[timer_timeout] as callback is suitable. See <gint/timer.h>.
Event transforms in getkey_opt() (SHIFT, ALPHA and repetitions) are handled
by changing the transform settings on the keyboard device. These settings
are restored when getkey_opt() returns, so if they are originally disabled
(which they are unless set manually) then the status of the SHIFT and ALPHA
keys is lost between calls (this has an effect it getkey_opt() is
interrupted by timeout). Therefore, in order to use modifiers across several
calls to getkey_opt(), make sure to enable the transforms on the keyboard
device; see <gint/drivers/keydev.h> for details.
@options An or-combination of values from the GETKEY_* enumeration
@timeout Optional pointer to a timeout value
Returns a key event of type KEYEV_DOWN or KEYEV_HOLD with [mod=1]. */
key_event_t getkey_opt(int options, volatile int *timeout);
/* getkey_repeat(): Set repeat delays for getkey()
This function updates the repeat delays of getkey() and getkey_opt(). The
unit of the argument is in milliseconds, but the granularity of the delay is
dependent on the keyboard scan frequency.
In the default setting (128 Hz scans), the possible repeat delays are
approximately 8 ms, 16 ms, 23 ms, 31 ms... the provided arguments will be
rounded to the closest feasible delays to ensure that repetitions are
perfectly regular, rather than approximating the requested frequency.
The system default is (500 ms, 125 ms). With the 128 Hz setting, this
default is reached exactly without approximation. gint's default is (400 ms,
40 ms) for more reactivity.
@first Delay between key press and first repeat (no more than one hour)
@next Delay between subsequent repeats (no more than one hour) */
void getkey_repeat(int first, int next);
/* getkey_repeat_profile(): Get the current repeat profile function */
getkey_profile_t getkey_repeat_profile(void);
/* getkey_set_repeat_profile(): Set the repeat profile function
The repeat profile is called by getkey() and getkey_opt() when a key is
pressed or held, and getkey() is planning to repeat it. The profile decides
whether such repetition is allowed, and if so, how long it shoud take. The
profile has access to the following information:
@key Key for which a repetition is being considered
@duration Duration since the key was first pressed (us)
@count Number of previous repeats (0 on the first call)
The profile function must either return a positive number of microseconds to
wait until the next repeat, or -1 to block the repeat indefinitely. Note
that the keyboard device typically updates every 7-8 ms, timings are tracked
in microseconds only to limit deviations.
Setting a repeat profile overrides GETKEY_REP_ARROWS, GETKEY_REP_ALL, and
the repeat delays. Calling with profile=NULL restores this behavior.
This mechanism replaces a "repeat filter" that existed until gint 2.4. The
main difference is that the repeat filter was called when the repeat event
arrived, whereas the repeat profile is called one repeat earlier to schedule
the repeat exactly when needed. */
void getkey_set_repeat_profile(getkey_profile_t profile);
/* getkey_feature_function(): Get the current feature function */
getkey_feature_t getkey_feature_function(void);
@ -329,4 +291,36 @@ int keycode_function(int keycode);
returns 7 for KEY_7) and -1 for other keys. */
int keycode_digit(int keycode);
#ifdef __cplusplus
}
#endif
//---
// Deprecated functions for repeat control
//
// The following types and functions have been deprecated. The handling of
// repeats is done inside the keyboard device (see <gint/drivers/keydev.h>).
// Until gint 2.9, the device had repeats disabled by default and getkey()
// enabled them. The problem is that repeats usually occur while getkey() is
// not running, so repeats wouldn't work as expected.
//
// Thus, getkey()-specific APIs related to repeat settings are now deprecated
// in favor of the ones provided by <gint/drivers/keydev.h>, which apply
// globally instead of just when getkey() is being run.
//---
typedef void *getkey_profile_t;
__attribute__((deprecated(
"Use keydev_set_standard_repeats(), which is permanent, instead")))
void getkey_repeat(int first, int next);
__attribute__((deprecated(
"Use keydev_transform(keydev_std()).repeater instead")))
getkey_profile_t getkey_repeat_profile(void);
__attribute__((deprecated(
"Set the [repeater] attribute with keydev_set_transform() instead")))
void getkey_set_repeat_profile(getkey_profile_t profile);
#endif /* GINT_KEYBOARD */

View File

@ -5,6 +5,10 @@
#ifndef GINT_KEYCODES
#define GINT_KEYCODES
#ifdef __cplusplus
extern "C" {
#endif
/* Raw matrix codes */
enum {
KEY_F1 = 0x91,
@ -83,4 +87,8 @@ enum {
KEY_MINUS = KEY_SUB,
};
#ifdef __cplusplus
}
#endif
#endif /* GINT_KEYCODES */

View File

@ -5,6 +5,10 @@
#ifndef GINT_KMALLOC
#define GINT_KMALLOC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/config.h>
#include <gint/defs/types.h>
@ -164,4 +168,8 @@ int kmallocdbg_sequence_length(kmalloc_arena_t *a);
#endif /* GINT_KMALLOC_DEBUG */
#ifdef __cplusplus
}
#endif
#endif /* GINT_KMALLOC */

View File

@ -5,7 +5,12 @@
#ifndef GINT_MMU
#define GINT_MMU
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/mpu/mmu.h>
#include <stdbool.h>
//---
// Unified interface
@ -36,6 +41,17 @@ void *mmu_uram(void);
fx-9860G, and 512k on fx-CG 50. */
uint32_t mmu_uram_size(void);
/* mmu_is_rom(): Determine if an address points to ROM
Checks whether the supplied pointer points to ROM or to a virtualized
portion of ROM. For the sake of efficiency, this function uses heuristics
about the structure of P0 rather than actually checking the TLB.
This is useful during filesystem accesses because only data outside of ROM
can be written to files. Pointers for which this function returns true
cannot be used as a source for BFile_Write(). */
bool mmu_is_rom(void const *ptr);
//---
// SH7705 TLB
//---
@ -94,4 +110,8 @@ void utlb_mapped_memory(uint32_t *rom, uint32_t *ram);
/* utlb_translate(): Get the physical address for a virtual page */
uint32_t utlb_translate(uint32_t page, uint32_t *size);
#ifdef __cplusplus
}
#endif
#endif /* GINT_MMU */

150
include/gint/mpu/bsc.h Normal file
View File

@ -0,0 +1,150 @@
//---
// gint:mpu:bsc - Bus State Controller
//---
#ifndef GINT_MPU_BSC
#define GINT_MPU_BSC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7305 But State Controller. Refer to:
// Renesas SH7730 Group Hardware Manual
// Section 11: Bus State Controller (BSC)
//---
typedef volatile lword_union(sh7305_bsc_CSnBCR_t,
uint32_t :1;
uint32_t IWW :3;
uint32_t IWRWD :3;
uint32_t IWRWS :3;
uint32_t IWRRD :3;
uint32_t IWRRS :3;
uint32_t TYPE :4;
uint32_t :1;
uint32_t BSZ :2;
uint32_t :9;
);
typedef volatile lword_union(sh7305_bsc_CSnWCR_06A6B_t,
uint32_t :11;
uint32_t BAS :1;
uint32_t :1;
uint32_t WW :3;
uint32_t ADRSFIX:1;
uint32_t :2;
uint32_t SW :2;
uint32_t WR :4;
uint32_t WM :1;
uint32_t :4;
uint32_t HW :2;
);
typedef volatile lword_union(sh7305_bsc_CSnWCR_45A5B_t,
uint32_t :11;
uint32_t BAS :1;
uint32_t :1;
uint32_t WW :3;
uint32_t :3;
uint32_t SW :2;
uint32_t WR :4;
uint32_t WM :1;
uint32_t :4;
uint32_t HW :2;
);
typedef volatile struct
{
lword_union(CMNCR,
uint32_t :6;
uint32_t CKOSTP :1;
uint32_t CKODRV :1;
uint32_t :7;
uint32_t DMSTP :1;
uint32_t :1;
uint32_t BSD :1;
uint32_t MAP :2;
uint32_t BLOCK :1;
uint32_t :7;
uint32_t ENDIAN :1;
uint32_t :1;
uint32_t HIZMEM :1;
uint32_t HIZCNT :1;
);
sh7305_bsc_CSnBCR_t CS0BCR;
sh7305_bsc_CSnBCR_t CS2BCR;
sh7305_bsc_CSnBCR_t CS3BCR;
sh7305_bsc_CSnBCR_t CS4BCR;
sh7305_bsc_CSnBCR_t CS5ABCR;
sh7305_bsc_CSnBCR_t CS5BBCR;
sh7305_bsc_CSnBCR_t CS6ABCR;
sh7305_bsc_CSnBCR_t CS6BBCR;
sh7305_bsc_CSnWCR_06A6B_t CS0WCR;
lword_union(CS2WCR,
uint32_t :8;
uint32_t BW :2;
uint32_t PMD :1;
uint32_t BAS :1;
uint32_t :1;
uint32_t WW :3;
uint32_t :3;
uint32_t SW :2;
uint32_t WR :4;
uint32_t WM :1;
uint32_t :4;
uint32_t HW :2;
);
lword_union(CS3WCR,
uint32_t :17;
uint32_t TRP :2;
uint32_t :1;
uint32_t TRCD :2;
uint32_t :1;
uint32_t A3CL :2;
uint32_t :2;
uint32_t TRWL :2;
uint32_t :1;
uint32_t TRC :2;
);
sh7305_bsc_CSnWCR_45A5B_t CS4WCR;
sh7305_bsc_CSnWCR_45A5B_t CS5AWCR;
sh7305_bsc_CSnWCR_45A5B_t CS5BWCR;
sh7305_bsc_CSnWCR_06A6B_t CS6AWCR;
sh7305_bsc_CSnWCR_06A6B_t CS6BWCR;
lword_union(SDCR,
uint32_t :11;
uint32_t A2ROW :2;
uint32_t :1;
uint32_t A2COL :2;
uint32_t :4;
uint32_t RFSH :1;
uint32_t RMODE :1;
uint32_t PDOWN :1;
uint32_t BACTV :1;
uint32_t :3;
uint32_t A3ROW :2;
uint32_t :1;
uint32_t A3COL :2;
);
uint32_t RTCSR;
uint32_t RTCNT;
uint32_t RTCOR;
} GPACKED(4) sh7305_bsc_t;
#define SH7305_BSC (*(sh7305_bsc_t *)0xfec10000)
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_BSC */

View File

@ -5,6 +5,10 @@
#ifndef GINT_MPU_CPG
#define GINT_MPU_CPG
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
@ -43,7 +47,7 @@ typedef volatile struct
documentation. */
typedef volatile struct
{
lword_union(FRQCRA,
lword_union(FRQCR,
uint32_t KICK :1; /* Flush FRQCRA modifications */
uint32_t :1;
uint32_t STC :6; /* PLL multiplication [*] */
@ -64,7 +68,15 @@ typedef volatile struct
uint32_t SRC :2; /* Clock source select */
uint32_t DIVA :6; /* Division ratio for port A */
);
pad(0x08);
pad(0x04);
lword_union(DDCLKCR,
uint32_t :23;
uint32_t CLKSTP :1; /* Clock Stop */
uint32_t _ :1; /* Unknown */
uint32_t :1;
uint32_t DIV :6;
);
lword_union(USBCLKCR,
uint32_t :23;
@ -82,7 +94,9 @@ typedef volatile struct
uint32_t CKOFF :1; /* CKO Output Stop */
uint32_t :1;
);
pad(0x14);
uint32_t PLL2CR;
pad(0x10);
lword_union(SPUCLKCR,
uint32_t :23;
@ -105,9 +119,16 @@ typedef volatile struct
uint32_t :3;
uint32_t FLF :11; /* FLL Multiplication Ratio */
);
pad(0x0c);
uint32_t LSTATS;
} GPACKED(4) sh7305_cpg_t;
#define SH7305_CPG (*((sh7305_cpg_t *)0xa4150000))
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_CPG */

View File

@ -9,6 +9,10 @@
#ifndef GINT_MPU_DMA
#define GINT_MPU_DMA
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
//---
@ -86,4 +90,8 @@ typedef volatile struct
#define SH7305_DMA (*((sh7305_dma_t *)0xfe008020))
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_DMA */

View File

@ -12,6 +12,10 @@
#ifndef GINT_MPU_INTC
#define GINT_MPU_INTC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
//---
@ -297,4 +301,8 @@ typedef struct
extern sh7705_intc_t SH7705_INTC;
extern sh7305_intc_t SH7305_INTC;
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_INTC */

View File

@ -8,6 +8,10 @@
#ifndef GINT_MPU_MMU
#define GINT_MPU_MMU
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
@ -152,4 +156,8 @@ typedef volatile struct
#define SH7305_MMU (*(sh7305_mmu_t *)0xff000000)
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_MMU */

View File

@ -8,6 +8,10 @@
#ifndef GINT_MPU_PFC
#define GINT_MPU_PFC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
@ -75,4 +79,8 @@ typedef volatile struct
// TODO: Document the SH7305 Pin Function Controller
//---
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_PFC */

View File

@ -5,6 +5,10 @@
#ifndef GINT_MPU_POWER
#define GINT_MPU_POWER
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
//---
@ -103,4 +107,8 @@ typedef volatile struct
#define SH7305_POWER (*((sh7305_power_t *)0xa4150020))
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_POWER */

View File

@ -5,6 +5,10 @@
#ifndef GINT_MPU_RTC
#define GINT_MPU_RTC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
//---
@ -75,4 +79,8 @@ typedef volatile struct
#define SH7705_RTC (*((rtc_t *)0xfffffec0))
#define SH7305_RTC (*((rtc_t *)0xa413fec0))
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_RTC */

View File

@ -5,6 +5,10 @@
#ifndef GINT_MPU_SPU
#define GINT_MPU_SPU
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
typedef volatile struct
@ -94,4 +98,8 @@ typedef volatile struct
#define SH7305_DSP0 (*(spu_dsp_t *)0xfe2ffd00)
#define SH7305_DSP1 (*(spu_dsp_t *)0xfe3ffd00)
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_SPU */

View File

@ -9,6 +9,10 @@
#ifndef GINT_MPU_TMU
#define GINT_MPU_TMU
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
//---
@ -101,4 +105,8 @@ typedef volatile struct
typedef volatile etmu_t sh7305_etmu_t[6];
#define SH7305_ETMU (*(sh7305_etmu_t *)0xa44d0030)
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_TMU */

View File

@ -5,6 +5,10 @@
#ifndef GINT_MPU_USB
#define GINT_MPU_USB
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
typedef volatile struct
@ -437,4 +441,8 @@ typedef volatile word_union(sh7305_usb_uponcr_t,
#define SH7305_USB (*(sh7305_usb_t *)0xa4d80000)
#define SH7305_USB_UPONCR (*(sh7305_usb_uponcr_t *)0xa40501d4)
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_USB */

View File

@ -5,6 +5,10 @@
#ifndef GINT_RTC
#define GINT_RTC
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/defs/call.h>
#include <gint/timer.h>
@ -13,7 +17,11 @@
// Time management
//---
/* rtc_time_t: A point in time, representable in the RTC registers */
/* rtc_time_t: A point in time, representable in the RTC registers
WARNING: A copy of this definition is used in fxlibc, make sure to keep it
in sync. (We don't install kernel headers before compiling the fxlibc, it's
kind of a nightmare for the modest build system.) */
typedef struct
{
uint16_t year; /* Years (exact value, e.g. 2018) */
@ -96,4 +104,8 @@ int rtc_start_timer(int frequency, timer_callback_t callback, ...);
__attribute__((deprecated("Use rtc_periodic_disable() instead")))
void rtc_stop_timer(void);
#ifdef __cplusplus
}
#endif
#endif /* GINT_RTC */

View File

@ -5,6 +5,10 @@
#ifndef GINT_TIMER
#define GINT_TIMER
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/mpu/tmu.h>
#include <gint/hardware.h>
#include <gint/defs/types.h>
@ -158,10 +162,11 @@ void timer_stop(int timer);
it may have only paused. If the timer never stops, you're in trouble. */
void timer_wait(int timer);
/* timer_spinwait(): Actively wait for a timer to raise UNF
/* timer_spinwait(): Start a timer and actively wait for UNF
Waits until the timer raises UNF, without sleeping. This is useful for
delays in driver code that is run when interrupts are disabled. This relies
neither on the interrupt signal nor on the UNIE flag. */
delays in driver code that is run when interrupts are disabled. UNIE is
disabled before starting the timer and waiting, so the callback is never
called. */
void timer_spinwait(int timer);
//---
@ -236,4 +241,8 @@ int timer_setup(int timer, uint64_t delay_us, timer_callback_t callback, ...);
Use GINT_CALL_SET_STOP() with timer_configure() instead. */
int timer_timeout(volatile void *arg);
#ifdef __cplusplus
}
#endif
#endif /* GINT_TIMER */

View File

@ -5,6 +5,10 @@
#ifndef GINT_USB_FF_BULK
#define GINT_USB_FF_BULK
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/usb.h>
/* The bulk transfer interface with class code 0xff provides a very simple
@ -51,7 +55,7 @@ int usb_ff_bulk_output(void);
/* usb_fxlink_header_t: Message header for fxlink
fxlink supports a minimalistic protocol to receive data sent from the
fxlink supports a minimalist protocol to receive data sent from the
calculator and automatically process it (such as save it to file, convert
to an image, etc). It is designed as a convenience feature, and it can be
extended with custom types rather easily.
@ -93,8 +97,8 @@ typedef struct
Returns false if the parameters are invalid or don't fit, in this case the
contents of the header are unchanged. */
bool usb_fxlink_fill_header(usb_fxlink_header_t *header, char *application,
char *type, uint32_t data_size);
bool usb_fxlink_fill_header(usb_fxlink_header_t *header,
char const *application, char const *type, uint32_t data_size);
//---
// Short functions for fxlink built-in types
@ -164,4 +168,25 @@ void usb_fxlink_screenshot_gray(bool onscreen);
and size allow, and 1 byte otherwise. If size is 0, strlen(text) is used. */
void usb_fxlink_text(char const *text, int size);
/* usb_fxlink_videocapture(): Send a frame for a video recording
This function is essentially the same as usb_fxlink_screenshot(). It sends a
capture of the VRAM to fxlink but uses the "video" type, which fxlink
displays in real-time or saves as a video file. The meaning of the onscreen
setting is identical to usb_fxlink_screenshot().
This function can be called with onscreen=false as a dupdate() hook to
automatically send new frames to fxlink. */
void usb_fxlink_videocapture(bool onscreen);
#ifdef FX9860G
/* usb_fxlink_videocapture_gray(): Send a gray frame for a video recording
Like usb_fxlink_videocapture(), but uses VRAM data from the gray engine. */
void usb_fxlink_videocapture_gray(bool onscreen);
#endif
#ifdef __cplusplus
}
#endif
#endif /* GINT_USB_FF_BULK */

View File

@ -5,6 +5,10 @@
#ifndef GINT_USB
#define GINT_USB
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/std/endian.h>
#include <gint/gint.h>
@ -36,7 +40,7 @@ enum {
/* This pipe is busy (returned by usb_write_async()) */
USB_WRITE_BUSY,
/* Both FIFO controlles are busy, none is available to transfer */
/* Both FIFO controllers are busy, none is available to transfer */
USB_WRITE_NOFIFO,
/* This pipe is busy (returned by usb_commit_async()) */
@ -376,4 +380,8 @@ uint16_t usb_dc_string(uint16_t const *literal, size_t len);
This is mostly used by the driver to answer GET_DESCRIPTOR requests. */
usb_dc_string_t *usb_dc_string_get(uint16_t id);
#ifdef __cplusplus
}
#endif
#endif /* GINT_USB */

View File

@ -29,7 +29,7 @@ const clock_frequency_t *clock_freq(void)
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
#define CPG SH7705_CPG
static void sh7705_probe(void)
void sh7705_probe(void)
{
/* According to Sentaro21 in the sources of Ftune 1.0.1, the clock mode
is thought to be 5, which means that:
@ -82,7 +82,7 @@ static void sh7305_probe(void)
{
/* The meaning of the PLL setting on SH7305 differs from the
documentation of SH7224; the value must not be doubled. */
int pll = CPG.FRQCRA.STC + 1;
int pll = CPG.FRQCR.STC + 1;
freq.PLL = pll;
/* The FLL ratio is the value of the setting, halved if SELXM=1 */
@ -93,9 +93,9 @@ static void sh7305_probe(void)
/* On SH7724, the divider ratio is given by 1 / (setting + 1), but on
the SH7305 it is 1 / (2^setting + 1). */
int divb = CPG.FRQCRA.BFC;
int divi = CPG.FRQCRA.IFC;
int divp = CPG.FRQCRA.P1FC;
int divb = CPG.FRQCR.BFC;
int divi = CPG.FRQCR.IFC;
int divp = CPG.FRQCR.P1FC;
freq.Bphi_div = 1 << (divb + 1);
freq.Iphi_div = 1 << (divi + 1);
@ -115,10 +115,21 @@ static void sh7305_probe(void)
#undef CPG
//---
// Initialization
//---
void cpg_compute_freq(void)
{
/* This avoids warnings about sh7705_probe() being undefined when
building for fxcg50 */
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
isSH3() ? sh7705_probe() :
#endif
sh7305_probe();
}
static void configure(void)
{
/* Disable spread spectrum in SSGSCR */
@ -127,12 +138,7 @@ static void configure(void)
SH7305_CPG.SSCGCR.SSEN = 0;
}
/* This avoids warnings about sh7705_probe() being undefined when
building for fxcg50 */
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
isSH3() ? sh7705_probe() :
#endif
sh7305_probe();
cpg_compute_freq();
}
//---
@ -141,12 +147,18 @@ static void configure(void)
static void hsave(cpg_state_t *s)
{
if(isSH4()) s->SSCGCR = SH7305_CPG.SSCGCR.lword;
if(isSH4()) {
s->SSCGCR = SH7305_CPG.SSCGCR.lword;
cpg_get_overclock_setting(&s->speed);
}
}
static void hrestore(cpg_state_t const *s)
{
if(isSH4()) SH7305_CPG.SSCGCR.lword = s->SSCGCR;
if(isSH4()) {
SH7305_CPG.SSCGCR.lword = s->SSCGCR;
cpg_set_overclock_setting(&s->speed);
}
}
gint_driver_t drv_cpg = {

273
src/cpg/overclock.c Normal file
View File

@ -0,0 +1,273 @@
//---
// gint:cpg:overclock - Clock speed control
//
// Most of the data in this file has been reused from Sentaro21's Ftune and
// Ptune utilities, which have long been the standard for overclocking CASIO
// calculators.
// See: http://pm.matrix.jp/ftune2e.html
//
// SlyVTT also contributed early testing on both the fx-CG 10/20 and fx-CG 50.
//---
#include <gint/clock.h>
#include <gint/gint.h>
#include <gint/hardware.h>
#include <gint/mpu/cpg.h>
#include <gint/mpu/bsc.h>
#define CPG SH7305_CPG
#define BSC SH7305_BSC
//---
// Low-level clock speed access
//---
#define SDMR3_CL2 ((volatile uint8_t *)0xFEC15040)
#define SDMR3_CL3 ((volatile uint8_t *)0xFEC15060)
void cpg_get_overclock_setting(struct cpg_overclock_setting *s)
{
if(!isSH4())
return;
s->FLLFRQ = CPG.FLLFRQ.lword;
s->FRQCR = CPG.FRQCR.lword;
s->CS0BCR = BSC.CS0BCR.lword;
s->CS0WCR = BSC.CS0WCR.lword;
s->CS2BCR = BSC.CS2BCR.lword;
s->CS2WCR = BSC.CS2WCR.lword;
s->CS3BCR = BSC.CS3BCR.lword;
s->CS3WCR = BSC.CS3WCR.lword;
s->CS5aBCR = BSC.CS5ABCR.lword;
s->CS5aWCR = BSC.CS5AWCR.lword;
}
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s)
{
if(!isSH4())
return;
BSC.CS0WCR.WR = 11; /* 18 cycles */
CPG.FLLFRQ.lword = s->FLLFRQ;
CPG.FRQCR.lword = s->FRQCR;
CPG.FRQCR.KICK = 1;
while(CPG.LSTATS != 0) {}
BSC.CS0BCR.lword = s->CS0BCR;
BSC.CS0WCR.lword = s->CS0WCR;
BSC.CS2BCR.lword = s->CS2BCR;
BSC.CS2WCR.lword = s->CS2WCR;
BSC.CS3BCR.lword = s->CS3BCR;
BSC.CS3WCR.lword = s->CS3WCR;
if(BSC.CS3WCR.A3CL == 1)
*SDMR3_CL2 = 0;
else
*SDMR3_CL3 = 0;
BSC.CS5ABCR.lword = s->CS5aBCR;
BSC.CS5AWCR.lword = s->CS5aWCR;
}
//---
// Predefined clock speeds
//---
#ifdef FXCG50
#define PLL_32x 0b011111
#define PLL_26x 0b011001
#define PLL_16x 0b001111
#define DIV_2 0
#define DIV_4 1
#define DIV_8 2
#define DIV_16 3
#define DIV_32 4
static struct cpg_overclock_setting settings_cg50[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = 0x0F011112,
.CS0BCR = 0x36DA0400,
.CS2BCR = 0x36DA3400,
.CS3BCR = 0x36DB4400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x000003C0,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_16x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000340,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_26x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000240,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_32x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_8<<8)+DIV_16,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x000002C0,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000440,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
};
static struct cpg_overclock_setting settings_cg20[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = 0x0F102203,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_32x<<24)+(DIV_8<<20)+(DIV_16<<12)+(DIV_16<<8)+DIV_32,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x00000140,
.CS2WCR = 0x000100C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_32x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_32,
.CS0BCR = 0x24900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x000002C0,
.CS2WCR = 0x000201C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_32x<<24)+(DIV_4<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_32,
.CS0BCR = 0x44900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x00000440,
.CS2WCR = 0x00040340,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_16,
.CS0BCR = 0x34900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x15140400,
.CS0WCR = 0x000003C0,
.CS2WCR = 0x000402C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00010240 },
};
static struct cpg_overclock_setting *get_settings(void)
{
if(gint[HWCALC] == HWCALC_FXCG50)
return settings_cg50;
if(gint[HWCALC] == HWCALC_PRIZM)
return settings_cg20;
return NULL;
}
int clock_get_speed(void)
{
struct cpg_overclock_setting *settings = get_settings();
if(!settings)
return CLOCK_SPEED_UNKNOWN;
for(int i = 0; i < 5; i++) {
struct cpg_overclock_setting *s = &settings[i];
if(CPG.FLLFRQ.lword == s->FLLFRQ
&& CPG.FRQCR.lword == s->FRQCR
&& BSC.CS0BCR.lword == s->CS0BCR
&& BSC.CS2BCR.lword == s->CS2BCR
&& BSC.CS3BCR.lword == s->CS3BCR
&& BSC.CS5ABCR.lword == s->CS5aBCR
&& BSC.CS0WCR.lword == s->CS0WCR
&& BSC.CS2WCR.lword == s->CS2WCR
&& BSC.CS3WCR.lword == s->CS3WCR
&& BSC.CS5AWCR.lword == s->CS5aWCR)
return CLOCK_SPEED_F1 + i;
}
return CLOCK_SPEED_UNKNOWN;
}
void clock_set_speed(int level)
{
if(level < CLOCK_SPEED_F1 || level > CLOCK_SPEED_F5)
return;
if(clock_get_speed() == level)
return;
struct cpg_overclock_setting *settings = get_settings();
if(!settings)
return;
struct cpg_overclock_setting *s = &settings[level - CLOCK_SPEED_F1];
uint32_t old_Pphi = clock_freq()->Pphi_f;
/* Wait for asynchronous tasks to complete */
gint_world_sync();
/* Disable interrupts during the change */
cpu_atomic_start();
/* Set the clock settings */
cpg_set_overclock_setting(s);
/* Determine the change in frequency for Pϕ and recompute CPG data */
cpg_compute_freq();
uint32_t new_Pphi = clock_freq()->Pphi_f;
/* Update timers' TCNT and TCOR to match the new clock speed */
void timer_rescale(uint32_t old_Pphi, uint32_t new_Pphi);
timer_rescale(old_Pphi, new_Pphi);
cpu_atomic_end();
}
#endif

64
src/cpu/ics.s Normal file
View File

@ -0,0 +1,64 @@
.global _cpu_csleep_init
.global _cpu_csleep
.global _cpu_csleep_cancel
_cpu_csleep_init:
mov.l .memcpy, r1
mova sleep, r0
mov r0, r5
jmp @r1
mov #(sleep_end - sleep), r6
.align 4
.memcpy:
.long _memcpy
_cpu_csleep:
mov.l r8, @-r15
sts.l pr, @-r15
mov r4, r8
/* Check if the sleep instruction is still there */
1: mov.w @(8, r8), r0
extu.w r0, r0
cmp/eq #0x001b, r0
bf 2f
/* Invalidate the cache in case of previous ICS being cached */
mov r8, r0
icbi @r0
add #18, r0
icbi @r0
/* Execute the sleep, and loop */
jsr @r8
nop
bra 1b
nop
2: lds.l @r15+, pr
rts
mov.l @r15+, r8
_cpu_csleep_cancel:
mov #0x0009, r0
add #8, r4
mov.w r0, @r4
icbi @r4
rts
nop
.align 4
/* This is identical in functionality to the CPU driver's sleep() function */
sleep:
mov.l 2f, r0
mov.l @r0, r0
cmp/pl r0
bt 1f
sleep
1: rts
nop
nop
2: .long _cpu_sleep_block_counter
sleep_end:

View File

@ -8,6 +8,7 @@
#include <gint/drivers/states.h>
#include <gint/clock.h>
#include <gint/exc.h>
#include <gint/cpu.h>
#define DMA SH7305_DMA
#define POWER SH7305_POWER
@ -18,6 +19,8 @@ typedef volatile sh7305_dma_channel_t channel_t;
static gint_call_t dma_callbacks[6] = { 0 };
/* Sleep blocking flags for all channels */
static bool dma_sleep_blocking[6] = { 0 };
/* ICS for dma_channel_wait() for all channels */
static cpu_csleep_t *dma_wait_ics[6] = { 0 };
/* dma_channel(): Get address of a DMA channel */
static channel_t *dma_channel(int channel)
@ -113,6 +116,11 @@ static int dma_setup(int channel, dma_size_t size, uint blocks,
if(ch->DAR >= 0xe5007000 && ch->DAR <= 0xe5204000)
dma_sleep_blocking[channel] = true;
if(ch->SAR >= 0xfe200000 && ch->SAR <= 0xfe3fffff)
dma_sleep_blocking[channel] = true;
if(ch->DAR >= 0xfe200000 && ch->DAR <= 0xfe3fffff)
dma_sleep_blocking[channel] = true;
return 0;
}
@ -138,6 +146,7 @@ bool dma_transfer_async(int channel, dma_size_t size, uint blocks,
static void dma_interrupt_transfer_ended(int channel)
{
channel_t *ch = dma_channel(channel);
ch->CHCR.IE = 0;
ch->CHCR.DE = 0;
ch->CHCR.TE = 0;
@ -147,6 +156,10 @@ static void dma_interrupt_transfer_ended(int channel)
if(dma_sleep_blocking[channel])
sleep_unblock();
/* Cancel any sleep operation that is synchronized with this interrupt */
if(dma_wait_ics[channel])
cpu_csleep_cancel(dma_wait_ics[channel]);
if(dma_callbacks[channel].function)
{
gint_call(dma_callbacks[channel]);
@ -154,24 +167,52 @@ static void dma_interrupt_transfer_ended(int channel)
}
}
/* dma_transfer_wait(): Wait for a transfer to finish */
void dma_transfer_wait(int channel)
{
if(channel < 0)
{
for(int channel = 0; channel < 6; channel++)
dma_transfer_wait(channel);
return;
}
/* dma_channel_wait(): Wait for a particular channel's transfer to finish
This function is used both during normal gint operation and during foreign
unbinds of the DMA driver. The waiting method varies with interrupt settings
and device ownership. */
static void dma_channel_wait(int channel, bool foreign)
{
channel_t *ch = dma_channel(channel);
if(!ch) return;
while(ch->CHCR.DE && !ch->CHCR.TE)
/* If interrupts are disabled or we don't own the device, spin-wait by
checking either for TE to be set (Transfere Ended) or DE to be gone
(channel disabled).
There are definitely race conditions if the DMA is restarted between
our checks; only the context of the calls guarantee soundness.
* If interrupts are disabled, we assume there is no one that could
start the DMA again, since we are the only thread of execution.
* If the device is owned by another kernel, then we're transitioning
so we have to wait for *all* tasks to complete anyway. The risk is
rather to stop too early. */
if(!ch->CHCR.IE || foreign)
{
/* Sleep only if the interrupt is enabled to wake us up */
if(ch->CHCR.IE) sleep();
while(ch->CHCR.DE && !ch->CHCR.TE) {}
return;
}
/* Initialize an interrupt-cancellable sleep, to ensure
synchronization */
cpu_csleep_t ics;
cpu_csleep_init(&ics);
dma_wait_ics[channel] = &ics;
/* Now the ICS is set; if the interrupt has not occurred yet then the
handler is guaranteed to cancel the sleep at some point */
if(ch->CHCR.DE && !ch->CHCR.TE) cpu_csleep(&ics);
/* Clear the ICS pointer for next time */
dma_wait_ics[channel] = NULL;
}
/* dma_transfer_wait(): Wait for a transfer to finish */
void dma_transfer_wait(int channel)
{
dma_channel_wait(channel, false);
}
bool dma_transfer_sync(int channel, dma_size_t size, uint length,
@ -258,10 +299,18 @@ static void configure(void)
DMA.OR.DME = 1;
}
static void universal_unbind(void)
static void funbind(void)
{
/* Make sure any DMA transfer is finished before leaving the app */
dma_transfer_wait(-1);
/* Wait for all OS transfers to finish before taking over */
for(int channel = 0; channel < 6; channel++)
dma_channel_wait(channel, true);
}
static void unbind(void)
{
/* Make sure all DMA transfers are finished before leaving gint */
for(int channel = 0; channel < 6; channel++)
dma_channel_wait(channel, false);
}
static bool hpowered(void)
@ -322,8 +371,8 @@ static void hrestore(dma_state_t const *s)
gint_driver_t drv_dma0 = {
.name = "DMA",
.configure = configure,
.funbind = universal_unbind,
.unbind = universal_unbind,
.funbind = funbind,
.unbind = unbind,
.hpowered = hpowered,
.hpoweron = hpoweron,
.hpoweroff = hpoweroff,

View File

@ -4,6 +4,18 @@
void *dma_memcpy(void * __restrict dst, const void * __restrict src,
size_t size)
{
dma_transfer_sync(1, DMA_32B, size >> 5, src, DMA_INC, dst, DMA_INC);
int block_size = DMA_32B;
int block_count = size >> 5;
/* Use 4-byte transfers to access SPU memory */
if(((uint32_t)src >= 0xfe200000 && (uint32_t)src < 0xfe400000) ||
((uint32_t)dst >= 0xfe200000 && (uint32_t)dst < 0xfe400000))
{
block_size = DMA_4B;
block_count = size >> 2;
}
dma_transfer_sync(1, block_size, block_count, src, DMA_INC, dst,
DMA_INC);
return dst;
}

View File

@ -14,6 +14,17 @@ void *dma_memset(void *dst, uint32_t l, size_t size)
different memory regions, making the DMA faster than the CPU. */
for(int i = 0; i < 8; i++) ILbuf[i] = l;
dma_transfer_sync(1, DMA_32B, size>>5, ILbuf, DMA_FIXED, dst, DMA_INC);
int block_size = DMA_32B;
int block_count = size >> 5;
/* Use 4-byte transfers to access SPU memory */
if((uint32_t)dst >= 0xfe200000 && (uint32_t)dst < 0xfe400000)
{
block_size = DMA_4B;
block_count = size >> 2;
}
dma_transfer_sync(1, block_size, block_count, ILbuf, DMA_FIXED, dst,
DMA_INC);
return dst;
}

19
src/fs/close.c Normal file
View File

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

10
src/fs/closedir.c Normal file
View File

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

6
src/fs/creat.c Normal file
View File

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

27
src/fs/fdopendir.c Normal file
View File

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

94
src/fs/fs.c Normal file
View File

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

View File

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

145
src/fs/fugue/fugue.c Normal file
View File

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

38
src/fs/fugue/fugue.h Normal file
View File

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

159
src/fs/fugue/fugue_dir.c Normal file
View File

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

View File

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

143
src/fs/fugue/fugue_open.c Normal file
View File

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

View File

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

36
src/fs/fugue/fugue_stat.c Normal file
View File

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

View File

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

245
src/fs/fugue/util.c Normal file
View File

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

51
src/fs/fugue/util.h Normal file
View File

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

23
src/fs/lseek.c Normal file
View File

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

8
src/fs/mkdir.c Normal file
View File

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

14
src/fs/open.c Normal file
View File

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

23
src/fs/opendir.c Normal file
View File

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

24
src/fs/pread.c Normal file
View File

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

22
src/fs/pwrite.c Normal file
View File

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

18
src/fs/read.c Normal file
View File

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

9
src/fs/readdir.c Normal file
View File

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

7
src/fs/rewinddir.c Normal file
View File

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

8
src/fs/rmdir.c Normal file
View File

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

7
src/fs/seekdir.c Normal file
View File

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

8
src/fs/stat.c Normal file
View File

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

7
src/fs/telldir.c Normal file
View File

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

8
src/fs/unlink.c Normal file
View File

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

18
src/fs/write.c Normal file
View File

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

47
src/image/fixed.h Normal file
View File

@ -0,0 +1,47 @@
//---
// gint:image:fixed - Minimal fixed-point interface for linear transformations
//---
#ifndef GINT_IMAGE_FIXED
#define GINT_IMAGE_FIXED
/* Constants */
#define fconst(x) ((x) * 65536)
/* Multiplication */
static inline int fmul(int x, int y)
{
return ((int64_t)x * (int64_t)y) >> 16;
}
/* Division */
static inline int fdiv(int x, int y)
{
return ((int64_t)x << 16) / y;
}
/* Integer square root */
static inline int isqrt(int n)
{
if(n <= 0) return 0;
if(n < 4) return 1;
int low_bound = isqrt(n / 4) * 2;
int high_bound = low_bound + 1;
return (high_bound * high_bound <= n) ? high_bound : low_bound;
}
/* Floor operation */
static inline int ffloor(int x)
{
return (x >> 16);
}
/* Round operation */
static inline int fround(int x)
{
return ffloor(x + fconst(0.5));
}
#endif /* GINT_IMAGE_FIXED */

27
src/image/image_alloc.c Normal file
View File

@ -0,0 +1,27 @@
#include <gint/image.h>
#include <stdlib.h>
#include <gint/defs/util.h>
image_t *image_alloc(int width, int height, int format)
{
image_t *img = image_create(width, height, format);
if(!img)
return NULL;
if(IMAGE_IS_RGB16(format))
img->stride = ((width + 1) >> 1) * 4;
else if(IMAGE_IS_P8(format))
img->stride = width;
else if(IMAGE_IS_P4(format))
img->stride = ((width + 1) >> 1);
void *data = malloc(height * img->stride);
if(!data) {
image_free(img);
return NULL;
}
img->data = data;
img->flags |= IMAGE_FLAGS_DATA_ALLOC;
return img;
}

View File

@ -0,0 +1,31 @@
#include <gint/image.h>
#include <gint/defs/util.h>
#include <stdlib.h>
#include <string.h>
bool image_alloc_palette(image_t *img, int size)
{
if(!img || !IMAGE_IS_INDEXED(img->format))
return false;
if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC)
free(img->palette);
if(IMAGE_IS_P8(img->format)) {
size = (size <= 0) ? 256 : min(size, 256);
}
if(IMAGE_IS_P4(img->format)) {
size = 16;
}
img->palette = calloc(size, 2);
img->color_count = 0;
img->flags &= ~IMAGE_FLAGS_PALETTE_ALLOC;
if(!img->palette)
return false;
memset(img->palette, 0, 2*size);
img->color_count = size;
img->flags |= IMAGE_FLAGS_PALETTE_ALLOC;
return true;
}

16
src/image/image_alpha.c Normal file
View File

@ -0,0 +1,16 @@
#include <gint/image.h>
int image_alpha(int format)
{
switch(format) {
case IMAGE_RGB565A:
return 0x0001;
case IMAGE_P8_RGB565A:
return -128;
case IMAGE_P4_RGB565A:
return 0;
default:
/* A value that cannot be found in any pixel of any format */
return 0x10000;
}
}

9
src/image/image_clear.c Normal file
View File

@ -0,0 +1,9 @@
#include <gint/image.h>
void image_clear(image_t *img)
{
if(!IMAGE_IS_ALPHA(img->format))
return;
image_fill(img, image_alpha(img->format));
}

122
src/image/image_copy.c Normal file
View File

@ -0,0 +1,122 @@
#include <gint/image.h>
#include <gint/defs/util.h>
void image_copy(image_t const *src, image_t *dst, bool copy_alpha)
{
if(!image_target(src, dst, DATA_RW))
return;
if(!IMAGE_IS_ALPHA(src->format))
copy_alpha = true;
/* Clip the input to match the size of the output */
int w = min(src->width, dst->width);
int h = min(src->height, dst->height);
if(w <= 0 || h <= 0)
return;
void *src_px = src->data;
void *dst_px = dst->data;
int src_alpha = image_alpha_2(src->format, copy_alpha);
int dst_alpha = image_alpha_2(dst->format, copy_alpha);
if(IMAGE_IS_RGB16(src->format) && IMAGE_IS_RGB16(dst->format)) {
do {
for(int x = 0; x < w; x++) {
int px = ((uint16_t *)src_px)[x];
if(px != src_alpha) {
/* Don't copy opaque pixels of value 0x0001 into an RGB565A
array. We can use -= which is faster (subc) without
changing the visuals because dst_alpha != 0. */
((uint16_t *)dst_px)[x] = px - (px == dst_alpha);
}
}
src_px += src->stride;
dst_px += dst->stride;
} while(--h > 0);
}
else if(IMAGE_IS_P8(src->format) && IMAGE_IS_RGB16(dst->format)) {
uint16_t *palette = src->palette + 128;
do {
for(int x = 0; x < w; x++) {
int px = ((int8_t *)src_px)[x];
if(px != src_alpha) {
px = palette[px];
((uint16_t *)dst_px)[x] = px - (px == dst_alpha);
}
}
src_px += src->stride;
dst_px += dst->stride;
} while(--h > 0);
}
else if(IMAGE_IS_P8(src->format) && IMAGE_IS_P8(dst->format)) {
do {
for(int x = 0; x < w; x++) {
int px = ((int8_t *)src_px)[x];
if(px != src_alpha)
((int8_t *)dst_px)[x] = px;
}
src_px += src->stride;
dst_px += dst->stride;
} while(--h > 0);
}
else if(IMAGE_IS_P8(src->format) && IMAGE_IS_P4(dst->format)) {
do {
for(int x = 0; x < w; x++) {
int px = ((int8_t *)src_px)[x];
if(px != src_alpha) {
uint8_t *cell = dst_px + (x >> 1);
if(x & 1)
*cell = (*cell & 0xf0) | (px & 0x0f);
else
*cell = (*cell & 0x0f) | (px << 4);
}
}
src_px += src->stride;
dst_px += dst->stride;
} while(--h > 0);
}
else if(IMAGE_IS_P4(src->format) && IMAGE_IS_P4(dst->format)) {
do {
for(int x = 0; x < w; x++) {
int px = ((uint8_t *)src_px)[x >> 1];
px = (x & 1) ? (px & 0x0f) : (px >> 4);
if(px != src_alpha) {
uint8_t *cell = dst_px + (x >> 1);
if(x & 1)
*cell = (*cell & 0xf0) | (px & 0x0f);
else
*cell = (*cell & 0x0f) | (px << 4);
}
}
src_px += src->stride;
dst_px += dst->stride;
} while(--h > 0);
}
else if(IMAGE_IS_P4(src->format) && IMAGE_IS_P8(dst->format)) {
do {
for(int x = 0; x < w; x++) {
int px = ((uint8_t *)src_px)[x >> 1];
px = (x & 1) ? (px & 0x0f) : (px >> 4);
if(px != src_alpha)
((int8_t *)dst_px)[x] = px;
}
src_px += src->stride;
dst_px += dst->stride;
} while(--h > 0);
}
else if(IMAGE_IS_P4(src->format) && IMAGE_IS_RGB16(dst->format)) {
do {
for(int x = 0; x < w; x++) {
int px = ((uint8_t *)src_px)[x >> 1];
px = (x & 1) ? (px & 0x0f) : (px >> 4);
if(px != src_alpha) {
px = src->palette[px];
((uint16_t *)dst_px)[x] = px - (px == dst_alpha);
}
}
src_px += src->stride;
dst_px += dst->stride;
} while(--h > 0);
}
}

View File

@ -0,0 +1,20 @@
#include <gint/image.h>
#include <gint/defs/util.h>
#include <string.h>
image_t *image_copy_alloc(image_t const *src, int new_format)
{
if(!image_valid(src))
return NULL;
image_t *dst = image_alloc(src->width, src->height, new_format);
if(!dst)
return NULL;
if(!image_copy_palette(src, dst, -1)) {
image_free(dst);
return NULL;
}
image_copy(src, dst, true);
return dst;
}

View File

@ -0,0 +1,20 @@
#include <gint/image.h>
#include <gint/defs/util.h>
#include <string.h>
bool image_copy_palette(image_t const *src, image_t *dst, int size)
{
if(!image_valid(src) || !dst)
return false;
if(!IMAGE_IS_INDEXED(dst->format))
return true;
if(size < 0)
size = src->color_count;
if(!image_alloc_palette(dst, size))
return false;
int N = min(src->color_count, dst->color_count);
memcpy(dst->palette, src->palette, 2*N);
return true;
}

25
src/image/image_create.c Normal file
View File

@ -0,0 +1,25 @@
#include <gint/image.h>
#include <stdlib.h>
image_t *image_create(int width, int height, int format)
{
if(!IMAGE_IS_RGB16(format) && !IMAGE_IS_P8(format) && !IMAGE_IS_P4(format))
return NULL;
if(width <= 0 || width > 0xffff || height <= 0 || height > 0xffff)
return NULL;
image_t *img = malloc(sizeof *img);
if(!img)
return NULL;
img->format = format;
img->flags = 0;
img->color_count = 0;
img->width = width;
img->height = height;
img->stride = 0;
img->data = NULL;
img->palette = NULL;
return img;
}

View File

@ -0,0 +1,13 @@
#include <gint/image.h>
#include <gint/display.h>
image_t *image_create_vram(void)
{
image_t *img = image_create(DWIDTH, DHEIGHT, IMAGE_RGB565);
if(!img)
return NULL;
img->stride = 2 * DWIDTH;
img->data = gint_vram;
return img;
}

View File

@ -0,0 +1,6 @@
#include <gint/image.h>
int image_data_size(image_t const *img)
{
return img->stride * img->height;
}

View File

@ -0,0 +1,12 @@
#include <gint/image.h>
int image_decode_pixel(image_t const *img, int pixel)
{
if(IMAGE_IS_RGB16(img->format))
return pixel;
else if(IMAGE_IS_P8(img->format))
return img->palette[pixel+128];
else if(IMAGE_IS_P4(img->format))
return img->palette[pixel];
return -1;
}

26
src/image/image_fill.c Normal file
View File

@ -0,0 +1,26 @@
#include <gint/image.h>
void image_fill(image_t *img, int value)
{
if(!image_target(img, img, NOT_P4, DATA_RW))
return;
void *img_px = img->data;
if(IMAGE_IS_RGB16(img->format)) {
for(int y = 0; y < img->height; y++) {
for(int x = 0; x < img->width; x++) {
((uint16_t *)img_px)[x] = value;
}
img_px += img->stride;
}
}
else if(IMAGE_IS_P8(img->format)) {
for(int y = 0; y < img->height; y++) {
for(int x = 0; x < img->width; x++) {
((int8_t *)img_px)[x] = value;
}
img_px += img->stride;
}
}
}

16
src/image/image_free.c Normal file
View File

@ -0,0 +1,16 @@
#include <gint/image.h>
#include <gint/mmu.h>
#include <stdlib.h>
void image_free(image_t *img)
{
if(!img || mmu_is_rom(img))
return;
if(img->flags & IMAGE_FLAGS_DATA_ALLOC)
free(img->data);
if(img->flags & IMAGE_FLAGS_PALETTE_ALLOC)
free(img->palette);
free(img);
}

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