Compare commits

...

89 Commits

Author SHA1 Message Date
Sylvain PILLOT 4720a69fdc added primitives 2023-06-09 20:44:10 +02:00
Sylvain PILLOT cd5db9a26c add graphical primitives to gint 2023-03-28 19:43:53 +02:00
Lephe 3a2a194693
usb: unaligned reading (release candidate on features)
The only thing left on the bucket list is performance.
2023-03-26 11:36:40 +02:00
Sylvain PILLOT e960ec3463 update of README.md 2023-03-23 21:07:15 +01:00
Sylvain PILLOT 0e3bd1e2e5 addin upscaling now supports usb bulk transfert for screenshot and video capture (of course also text output) 2023-03-22 22:29:29 +01:00
Sylvain PILLOT 5e086917eb bug resolved 2023-03-22 21:04:10 +01:00
Sylvain PILLOT c8903fdc58 removed vram switch and timer for fx-as-cg build target 2023-03-21 21:16:31 +01:00
Lephe 4983849510
usb: consolidate reading logic again, now beta-worthy
This looks like it could work in the long term. The only issue that
really hasn't been addressed is how to use packet counters to cut
transactions when there's no ZLP, but we can leave that for later.
2023-03-18 19:51:57 +01:00
Sylvain PILLOT fa7885a287 corrected dupdate redirection as per dmode (normal / gray engine) - gray engine now woking on FX9860G_AS_CG target 2023-03-17 22:41:44 +01:00
Sylvain PILLOT e4653c0ace minors fixes 2023-03-17 21:07:12 +01:00
Sylvain PILLOT a41c44988b [WIP] - conversion of the gray scale engine to the CG for fx_as_cg build target - compiling - linking _ but ony B&W yet 2023-03-16 22:28:28 +01:00
Sylvain PILLOT 469be10609 corrected issue with screen aspect ratio on CG50 during upscaling 2023-03-13 22:22:36 +01:00
Sylvain PILLOT 69b5478e40 [WIP] build-fx-as-cg : upscaling of FX VRAM to CG VRAM + adhoc dupdate 2023-03-12 21:41:57 +01:00
Lephe fc1f510288
usb: consolidate reading mode (basic multi-segment reads now working) 2023-03-12 17:53:44 +01:00
Sylvain PILLOT 68ef7b2507 libgint-fxascg - compilation options set to TARGET_FXCG50 2023-03-12 16:18:54 +01:00
Sylvain PILLOT 9633ec52f0 Start working on libgint-fxascg - compilation options 2023-03-11 20:36:46 +01:00
Lephe c59b2f90fb
usb: prototype reading mode
Currently only tested with short messages using the fxlink interface.
There is much to be expanded upon, but this is a worthy start.
2023-03-04 18:06:38 +01:00
Lephe eb1f9a35a1
usb: use negative values for error codes
This will allow usb_read_sync() to return a combined error code / size
read integer.
2023-03-04 18:06:38 +01:00
Lephe f4e13afa84
usb: fix a missing PIPESEL access
This resulted in a random PIPECFG access.
2023-03-04 10:59:06 +01:00
Lephe 911691461f
usb: clarify functions used to access endpoint configuration
The previous scheme was really unclear. Now, an endpoint_t carries all
of the endpoint info (instead of the global address being implicitly
its array index, which couldn't be returned).

An _endpoint address_ is the 8-bit value consisting of an endpoint
number (bits 0x0f) and a direction (bit 0x80).

The _global address_ of an endpoint is the address used to communicate
with the host, and it is unique. The _local address_ of an endpoint is
the interface-specific numbering that gint provides to allow interfaces
to compose without risks of address collisions.

The complete data for an endpoint can be queried with the new functions
usb_get_endpoint_by_*() which accept global addresses, local addresses,
and pipe numbers.
2023-03-04 10:58:07 +01:00
Lephe aee67a76f3
usb: clear pipes/FIFOs during world switches, delay configuration
* Clear pipes and FIFOs during world switches to avoid interference
  with the OS. LINK uses pipes 3 and 4, and attempts to add a second
  pipe to the fxlink interface (thus using pipe 4) would interfere with
  LINK and somehow prevent the pipe from being used (Wireshark captures
  showed no responses on that pipe). Forcing a blank state is a valid
  move because that state occurs naturally after a RESET, thus LINK and
  other add-ins must support it as well.

* Delay the application of configuration to the USB configuration stage
  (specifically, the DVST configured interrupt, even though technically
  we should do that in SET_CONFIGURATION 0). This is because we
  previously relied on world switches preserving pipe settings (by not
  changing them) to reconnect the gint driver after a world switch.
  This is no longer possible as the world switch now clears the pipes.
  The new timing makes the driver automatically re-configure as the
  connection restarts.
2023-02-18 17:15:28 +01:00
Lephe c0671649df
usb: prevent double opening
This could happen in gintctl's USB tracer and obviously cause all sorts
of problems.
2023-02-18 16:48:28 +01:00
Lephe 53c67af52d
usb: fix wrong interface numbering (+ comments)
This worked previously because the only interface (number 0) had its
interface descriptor in first position (position 0).
2023-02-18 16:47:49 +01:00
Lephe 6910714c2c
asyncio: remove unused field asyncio_op_t.flying_w 2023-02-16 16:09:53 +01:00
Lephe af5c16a3d3
usb: massively improve writing logic
* Move logic around tracking transfers to asyncio.c.

* Add a "short buffer" holding 0-3 bytes between writes, so that the
  driver performs only 4-byte writes in the FIFO and a short write in
  the commit, if needed.
  - This is partially due to me thinking at some point that degrading
    writing size was impossible, but it might actually be possible by
    writing to FIFO/FIFO+2 or FIFO/FIFO+1/FIFO+2/FIFO+3.
  - In any case I think this new approach wins on performance.

* Get rid of unit_size since we now always use 4 bytes.

* Add a waiting function which is used in usb_close() (and once tested
  should be used in world switches too).

* Eliminate some of the special cases for the DCP, though not all (in
  particular I can't get the commit to rely on the BEMP interrupt yet,
  nor can I properly clear PID to NAK when unbinding).
2023-02-09 23:00:44 +01:00
Lephe 1a61e97ef0
mmu: provide read-only access to ITLB 2023-02-02 14:23:03 +01:00
Lephe a091efc894
usb: replace struct transfer with a more generic async. I/O op structure (WIP)
This lays the ground for both generalization to reading and sharing
that logic with the serial driver.
2023-01-31 16:04:35 +01:00
Lephe 6f758cd36c
defs: allow NULL callbacks in gint_call() 2023-01-31 16:04:35 +01:00
Lephe 18e0db3886
usb: improve driver with updated knowledge and prepare reading
* Finish updating the register list
* Use RTC-based timeouts to not involve more interrupts
* Be a lot more conservative about PID=BUF
* Start setting up parameters and checking invariants for future
  bidirectional communications
2023-01-31 16:04:35 +01:00
Lephe cf2b86deaa
usb: update module details with register analysis experiments
Unknown writable bits and host-only registers were found.
2023-01-28 13:10:03 +01:00
Lephe db50c9b192
kernel: more options in System ERROR screen
* Add options to RESET, go to menu, or abort()
* Define weak symbols for driver functions so that low-level debugging
  add-ins can be linked with minimal drivers (CPU/INTC/MMU)
2023-01-25 22:38:39 +01:00
Lephe 42853103aa
usb: remove incorrectly-ordered bits in USB registers 2023-01-25 16:28:20 +01:00
Lephe c28d06002f
ld: add support for FastLoad config for circuit10's Add-In Push 2023-01-17 09:53:58 +01:00
Lephe ac6b1c7d70
ld: generate linker scripts; add fxcg50_fastload.ld 2023-01-15 01:36:28 +01:00
Lephe 45881995e9
fs: initialize dynamically-allocated fd table
Obvious oversight from 736b58f205.
2023-01-14 22:28:27 +01:00
Lephe 70dccc29da
style: initialize non-trivially initialized pointer
We always have c <= 15 but it's not structural enough that we can
outright skip initializing the pointer.
2023-01-14 22:28:18 +01:00
Lephe 7e859169fe
cpg: add overclock save/restore functions 2023-01-05 20:25:44 +01:00
Lephe b3416dcc25
cpg: add overclock for SH3/SH4 fx-9860G and G-III (#23)
Co-authored-by: Slyvtt <pillot.sylvain@gmail.com>
2023-01-05 20:02:41 +01:00
Lephe 736b58f205
reduce static user RAM footprint for mono targets
* Make INTC data const (it should've always been)
* Introduce GRODATA3/.gint.rodata.sh3 for that purpose
* Dynamically allocate file descriptor table
2023-01-01 19:22:41 +01:00
Lephe 478fdaea76
keysc: don't emit KEYEV_DOWN for keys pressed at startup 2023-01-01 18:49:50 +01:00
Lephe d3b29c50e6
defs: use C++ min/max when compiling in C++ mode 2023-01-01 18:49:13 +01:00
Lephe 1272a6a71a
scif: fix incorrect base module address 2022-12-20 22:27:35 +01:00
Lephe 723bae134b
serial: add template header for future implementation
Co-authored-by: Slyvtt <pillot.sylvain@gmail.com>
2022-12-10 15:27:38 +01:00
Lephe 3bc3892524
scif: add the SH7705 and SH7305 SCIF descriptions
Co-authored-by: Slyvtt <pillot.sylvain@gmail.com>
2022-12-10 14:41:30 +01:00
Lephe 211d236496
pfc: add the SH7305 PFC description
Co-authored-by: Slyvtt <pillot.sylvain@gmail.com>
2022-12-10 11:46:44 +01:00
Lephe a4cf3516e7
usb: fix some messages being lost after a post-world-switch reconnect 2022-12-03 11:05:35 +01:00
Lephe 177879d432
usb: add a USB_TRACE() debugging mechanism 2022-12-03 11:04:43 +01:00
Lephe feb74a38ec
usb: hide USB_LOG() behind a compile-time debug option 2022-11-29 20:03:03 +01:00
Lephe 3192078c4c
kernel: more detailed quit handler documentation 2022-11-27 22:57:50 +01:00
Lephenixnoir a5fb6d3401 Merge pull request 'Added SetQuitHandler syscall into gint' (#21) from mibi88/gint:dev into dev
Reviewed-on: https://gitea.planet-casio.com/Lephenixnoir/gint/pulls/21
2022-11-27 22:50:10 +01:00
Lephe e2c507855f
render-cg: allocate VRAM in _ostk to keep _uram for large allocs 2022-11-26 18:38:09 +01:00
mibi88 813af222fd Merge branch 'dev' of https://gitea.planet-casio.com/mibi88/gint into dev 2022-11-20 11:48:37 +01:00
mibi88 984f162cb2 Added gint_set_quit_handler() 2022-11-20 11:48:26 +01:00
mibi88 6426406043 Fix typo 2022-11-20 11:48:26 +01:00
mibi88 2fe67412cc Added SetQuitHandler. 2022-11-20 11:48:26 +01:00
mibi88 f807b528bb Merge branch 'dev' of https://gitea.planet-casio.com/mibi88/gint into dev 2022-11-20 11:38:23 +01:00
mibi88 ad0da59de6 Added gint_set_quit_handler() 2022-11-20 11:38:17 +01:00
mibi88 56e5c2452a Added gint_set_quit_handler() 2022-11-20 11:36:57 +01:00
Lephe 8442e0b9cf
render: fix missed clipping test in dtext() 2022-11-19 23:41:47 +01:00
Lephe 5461d54083
ld: do not remove debug sections from ELF files
They are already removed by objcopy when converting to a flat binary
because they don't have the LOAD flag.
2022-11-19 23:05:57 +01:00
mibi88 386303136c Merge branch 'dev' of https://gitea.planet-casio.com/mibi88/gint into dev 2022-11-19 20:17:58 +01:00
mibi88 8a6f3bfdce Fix typo 2022-11-19 20:14:17 +01:00
mibi88 d6e8b096b5 Added SetQuitHandler. 2022-11-19 20:14:17 +01:00
Lephe 4df3d69d8c
render: add a window setting to restrict rendering
Mono text rendering is a bad hack and probably not that fast, but meh.
We can optimize it the day it becomes a bottleneck, if ever...
2022-11-19 17:19:28 +01:00
Lephe 74438f5da5
render: add a dgetpixel() function 2022-11-16 19:12:48 +01:00
Lephe 45fa6c87c2
render-cg: fix a bug with unaligned C_INVERT drect()
The alignment optimization would write the edges twice, which is fine
for opaque color fills, but cancels out for C_INVERT.
2022-11-16 18:10:22 +01:00
Lephe 5e35871c98
usb: add timeout variants for functions
We're not using them yet (specifically in fxlink) because timeouts
leave the pipes in undesirable states that currently end up crashing.
Some reset mechanism is needed, plus support from the protocol for
canceling messages, etc.
2022-11-13 08:56:59 +01:00
Lephe 5f9553f3b8
usb: fix transmissions resuming early after world switch
When the driver goes through a world switch a reconnection with the host
is needed before operations can resume. This requires usb_open_status to
be reset.

Also: bind a FIFO before a commit that involves data transfer, mirroring
what happens in writes. This ensures that PID is set to BUF, mainly.
2022-11-12 14:51:00 +01:00
mibi88 93e055cfba Fix typo 2022-11-11 18:04:38 +01:00
Lephe d110ab608e
usb: fix freeze on sync commit for inactive pipes
Performing an asynchronous commit on an inactive pipe would yield
USB_COMMIT_INACTVE and *not* invoke the callback (as intended),
which usb_commit_sync() ignored, causing a freeze.

This issue appeared after a world switch, which (for reasons not yet
known) appear to fail the first writes until a commit, and that commit
would then hit an inactive pipe.
2022-11-11 17:47:29 +01:00
mibi88 45e90b55ba Added SetQuitHandler. 2022-11-11 17:06:36 +01:00
Lephe bbda77769d
kmalloc: fix krealloc() not trying across arenas 2022-11-10 19:54:45 +01:00
Lephe 7b662987f8
kmalloc: add kmalloc_max() function 2022-11-09 21:35:32 +01:00
Lephe 252bd4eb41
keydev: add low-level filter for async keyboard handlers 2022-11-08 22:22:41 +01:00
Lephenixnoir 48db9bf09d Merge pull request 'Update Slim scancodes for the row unswap' (#20) from calamari/gint-slim:dev into dev
Reviewed-on: https://gitea.planet-casio.com/Lephenixnoir/gint/pulls/20
2022-10-10 00:38:37 +02:00
calamari 1d5675c99f Update Slim scancodes for the row unswap 2022-10-09 11:00:53 -07:00
Lephe 16890ab71b
iokbd: don't swap rows; keydev doesn't need it 2022-10-09 18:19:25 +02:00
Lephe c9df6326e4
reduce static RAM usage to maintain SH3 support 2022-10-09 18:15:12 +02:00
Lephenixnoir 97701e8eed Merge pull request 'Add fx-9860G Slim hardware detection and support to gint' (#18) from calamari/gint-slim:dev into dev
Reviewed-on: https://gitea.planet-casio.com/Lephenixnoir/gint/pulls/18
2022-09-27 23:09:30 +02:00
calamari a735e074d8 Clean up style mismatch 2022-09-25 11:23:07 -07:00
calamari ddf340cccd Support gint_osmenu() on fx-9860G Slim 2022-09-25 10:37:41 -07:00
calamari 5c331e5fa0 Add isSlim() macro 2022-09-25 10:37:18 -07:00
calamari 98fea4f2ec Translate fx-9860G Slim keyboard scancodes to standard scancodes
The Slim has two keys that the standard 9860G doesn't: HELP and LIGHT.
Those are mapped to virtual scancodes, resulting in virtual keycodes.
2022-09-25 01:34:40 -07:00
calamari a4adc5e1bd Support fx-9860G Slim backlight 2022-09-24 19:49:29 -07:00
calamari b23ed9629e Add HWCALC entry for fx-9860G Slim 2022-09-24 19:48:07 -07:00
Lephe 2b4075c8f9
keysc: have getkey() sleep
This was embarassingly no longer the case since the move to the keydev
API.
2022-09-07 23:25:57 +02:00
Lephe e95a91d5ab
minor API comment update 2022-09-07 23:25:57 +02:00
Lephe 3e5c45c5ad
kernel: make gint_setrestart() reset everything 2022-09-02 22:29:19 +02:00
Lephe 9924dc4684
fs: add rename() function on fx-CG 2022-08-22 15:25:14 +02:00
113 changed files with 5157 additions and 1317 deletions

View File

@ -9,6 +9,7 @@ include(Fxconv)
option(GINT_NO_OS_STACK "Do not use the OS stack as a memory pool (fx-CG only)")
option(GINT_STATIC_GRAY "Use static memory instead of malloc for gray buffers (fx-9860G only)")
option(GINT_KMALLOC_DEBUG "Enable debug functions for kmalloc")
option(GINT_USB_DEBUG "Enable debug functions for the USB driver")
set(CMAKE_INSTALL_MESSAGE LAZY)
@ -50,6 +51,7 @@ set(SOURCES_COMMON
src/fs/pwrite.c
src/fs/read.c
src/fs/readdir.c
src/fs/rename.c
src/fs/rewinddir.c
src/fs/rmdir.c
src/fs/seekdir.c
@ -64,6 +66,7 @@ set(SOURCES_COMMON
src/fs/fugue/fugue_open.c
src/fs/fugue/fugue_mkdir.c
src/fs/fugue/fugue_stat.c
src/fs/fugue/fugue_rename.c
src/fs/fugue/fugue_rmdir.c
src/fs/fugue/fugue_unlink.c
src/fs/fugue/util.c
@ -107,7 +110,9 @@ set(SOURCES_COMMON
src/render/dtext.c
src/render/dupdate_hook.c
src/render/dvline.c
src/render/dwindow.c
src/render/topti.c
src/render/dellipse.c
# RTC driver
src/rtc/rtc.c
src/rtc/rtc_ticks.c
@ -119,17 +124,21 @@ set(SOURCES_COMMON
src/tmu/sleep.c
src/tmu/tmu.c
# USB driver
src/usb/asyncio.c
src/usb/classes/ff-bulk.c
src/usb/configure.c
src/usb/pipes.c
src/usb/read4.S
src/usb/setup.c
src/usb/string.c
src/usb/usb.c
src/usb/write4.S
)
set(SOURCES_FX
# Gray engine
src/gray/engine.c
src/gray/gclear.c
src/gray/ggetpixel.c
src/gray/gint_gline.c
src/gray/gpixel.c
src/gray/grect.c
@ -142,6 +151,7 @@ set(SOURCES_FX
src/render-fx/bopti-asm.s
src/render-fx/bopti.c
src/render-fx/dclear.c
src/render-fx/dgetpixel.c
src/render-fx/dpixel.c
src/render-fx/drect.c
src/render-fx/dsubimage.c
@ -191,6 +201,7 @@ set(SOURCES_CG
src/image/image_vflip_alloc.c
# Rendering
src/render-cg/dclear.c
src/render-cg/dgetpixel.c
src/render-cg/dpixel.c
src/render-cg/drect.c
src/render-cg/dsubimage.c
@ -232,8 +243,47 @@ set(SOURCES_CG
src/render-cg/image/image_p4_dye.c
)
set(ASSETS_FX src/font5x7.png)
set(ASSETS_CG src/font8x9.png)
set(SOURCES_FXASCG
# Gray engine
src/gray/gclear.c
src/gray/ggetpixel.c
src/gray/gint_gline.c
src/gray/gpixel.c
src/gray/grect.c
src/gray/gsubimage.c
src/gray/gtext.c
# Rendering
src/render-fx/bopti-asm-gray-scsp.s
src/render-fx/bopti-asm-gray.s
src/render-fx/bopti-asm-mono-scsp.s
src/render-fx/bopti-asm.s
src/render-fx/bopti.c
src/render-fx/dclear.c
src/render-fx/dgetpixel.c
src/render-fx/dpixel.c
src/render-fx/drect.c
src/render-fx/dsubimage.c
src/render-fx/gint_dline.c
src/render-fx/masks.c
src/render-fx/topti-asm.s
src/render-fx/topti.c
#special dupdate functions to treat a FX VRAM and convert to a CG VRAM
src/render-fxascg/dupdate.c
src/render-fxascg/engine_fxascg.c
# R61524 driver as we are working with a CG50
src/r61524/r61524.c
src/usb/classes/ff-bulk-gray.c
)
set(ASSETS_FX
src/font5x7.png
)
set(ASSETS_CG
src/font8x9.png
)
fxconv_declare_assets(${ASSETS_FX} ${ASSETS_CG})
include_directories(
@ -243,25 +293,52 @@ add_compile_options(-Wall -Wextra -std=c11 -Os -fstrict-volatile-bitfields -mtas
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
add_compile_definitions(FX9860G)
add_library(gint-fx STATIC ${SOURCES_COMMON} ${SOURCES_FX} ${ASSETS_FX})
set(NAME "gint-fx")
set(LINKER_SCRIPT "fx9860g.ld")
set(LINKER_SCRIPTS
"${CMAKE_CURRENT_BINARY_DIR}/fx9860g.ld")
add_library(gint-fx STATIC ${SOURCES_COMMON} ${SOURCES_FX} ${ASSETS_FX}
${LINKER_SCRIPTS})
endif()
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
add_compile_definitions(FXCG50)
add_library(gint-cg STATIC ${SOURCES_COMMON} ${SOURCES_CG} ${ASSETS_CG})
set(NAME "gint-cg")
set(LINKER_SCRIPT "fxcg50.ld")
set(LINKER_SCRIPTS
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50.ld"
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50_fastload.ld")
add_library(gint-cg STATIC ${SOURCES_COMMON} ${SOURCES_CG} ${ASSETS_CG}
${LINKER_SCRIPTS})
endif()
if("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_AS_CG)
add_compile_options(-DFXCG50)
add_compile_definitions(FX9860G_AS_CG)
set(NAME "gint-fxascg")
set(LINKER_SCRIPTS
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50.ld"
"${CMAKE_CURRENT_BINARY_DIR}/fxcg50_fastload.ld")
add_library(gint-fxascg STATIC ${SOURCES_COMMON} ${SOURCES_FXASCG} ${ASSETS_FX}
${LINKER_SCRIPTS})
endif()
set_target_properties("${NAME}" PROPERTIES OUTPUT_NAME "${NAME}")
# Generate linker scripts
macro(generate_linker_script OUTPUT INPUT OPTIONS)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT}"
COMMAND ${FXSDK_TOOLCHAIN}cpp "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}"
-P -C -traditional-cpp ${OPTIONS} -o "${OUTPUT}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${INPUT}")
endmacro()
generate_linker_script("fx9860g.ld" "fx9860g.ld.c" "")
generate_linker_script("fxcg50.ld" "fxcg50.ld.c" "")
generate_linker_script("fxcg50_fastload.ld" "fxcg50.ld.c" "-DFXCG50_FASTLOAD")
# Library file
install(TARGETS "${NAME}" DESTINATION "${FXSDK_LIB}")
# Linker script
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_SCRIPT}"
DESTINATION "${FXSDK_LIB}")
# Linker scripts
install(FILES ${LINKER_SCRIPTS} DESTINATION "${FXSDK_LIB}")
# Headers
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
DESTINATION "${FXSDK_INCLUDE}"

View File

@ -32,7 +32,7 @@ The library also offers powerful higher-level features:
* An enhanced version of the system's GetKey() and GetKeyWait()
* A gray engine that works by rapidly swapping monochrome images on fx-9860G II
* Blazingly fast rendering functions (image rendering is 10 times faster tha
* Blazingly fast rendering functions (image rendering is 10 times faster than
MonochromeLib)
* Integrated font management
@ -105,6 +105,22 @@ The available options are:
% fxsdk build-cg install
```
**"Cross-Building" a fx-9860G II project for fx-CG 50**
fx-9860G II sources can be automatically converted to run on fx-CG 50, provided no
low level function/specific hardware manipulation/or syscall is used by the program.
The command is very similar to fx-9860G II one, except it is `fxsdk build-fx-as-cg`
instead of `fxsdk build-fx`.
The available options are:
```
% fxsdk build-fx-as-cg
% fxsdk build-fx-as-cg install
```
## Using in CMake-based add-ins
Find the `Gint` module and link against `Gint::Gint`. gint declares the include
@ -125,6 +141,10 @@ using the fxSDK, you will need to:
* Link with `-T fx9860g.ld -lgint-fx -lopenlibm -lc` on fx-9860G;
* Link with `-T fxcg50.ld -lgint-cg -lopenlibm -lc` on fx-CG 50.
Manually building a fx-9860G project to run on a fx-CG50 can be done :
* Build with `-m4-nofpu -mb -DFXCG50 -DFX9860G_AS_CG`;
* Link with `-T fxcg50.ld -lgint-fxascg -lopenlibm -lc`.
If you don't have a standard library such as
[Memallox's port of newlib](/PlaneteCasio/libc), you also need `-nostdlib`. I
typically use `-m3 -mb` or `-m4-nofpu -mb` to specify the platform, but that

8
TODO
View File

@ -1,3 +1,6 @@
Bugs to fix:
* render: figure out why fx-CG dclear() now takes 4.1 ms instead of 2.6 ms
Extensions on existing code:
* clock: mono support
* usb: add PC->calc reading, and interrupt pipes
@ -9,11 +12,12 @@ Extensions on existing code:
* kernel: better restore to userspace before panic (ensure BL=0 IMASK=0)
* project: add license file
* kernel: group linker script symbols in a single header file
* core: try to leave add-in without reset in case of panic
* core: use cmp/str for memchr()
* r61524: brightness control and clean the file
* core: review forgotten globals and MPU addresses not in <gint/mpu/*.h>
* core: run destructors when a task-switch results in leaving the app
* fs: support read-only files backed with GetBlockAddress() on fx-CG
* kernel: SH4- or G-III-specific linker scripts?
* keysc: global shortcut SHIFT+0+EXIT for abort() as an infinite loop break
Future directions:
* Audio playback using TSWilliamson's libsnd method

View File

@ -2,11 +2,15 @@
if("${FXSDK_PLATFORM_LONG}" STREQUAL fxCG50)
set(PC cg)
set(INTF_DEFN FXCG50)
set(INTF_LINK "-T;fxcg50.ld")
set(INTF_LINK "-T;$<IF:$<CONFIG:FastLoad>,fxcg50_fastload.ld,fxcg50.ld>")
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G)
set(PC fx)
set(INTF_DEFN FX9860G)
set(INTF_LINK "-T;fx9860g.ld")
elseif("${FXSDK_PLATFORM_LONG}" STREQUAL fx9860G_AS_CG)
set(PC fxascg)
set(INTF_DEFN FX9860G_AS_CG)
set(INTF_LINK "-T;fxcg50.ld")
else()
message(FATAL_ERROR "gint: unknown fxSDK platform '${FXSDK_PLATFORM}'")
endif()

View File

@ -109,6 +109,7 @@ SECTIONS
*(.rodata.4)
*(.rodata .rodata.*)
*(.gint.rodata.sh3)
} > rom
@ -225,10 +226,6 @@ SECTIONS
*/
/DISCARD/ : {
/* Debug sections (often from libgcc) */
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
.debug_ranges .debug_line .debug_str .debug_frame
.debug_loclists .debug_rnglists)
/* Java class registration (why are they even here?!) */
*(.jcr)
/* Asynchronous unwind tables: no C++ exception handling */

View File

@ -10,10 +10,16 @@ OUTPUT_FORMAT(elf32-sh)
/* Located in core/start.c */
ENTRY(_start)
#ifdef FXCG50_FASTLOAD
# define _ROM_REGION 0x8c200000
#else
# define _ROM_REGION 0x00300000
#endif
MEMORY
{
/* Userspace mapping of the add-in (without G3A header) */
rom (rx): o = 0x00300000, l = 2M
rom (rx): o = _ROM_REGION, l = 2M
/* Static RAM; stack grows down from the end of this region.
The first 5k (0x1400 bytes) are reserved by gint for the VBR space,
which is loaded dynamically and accessed through P1 */
@ -31,7 +37,7 @@ SECTIONS
*/
/* First address to be mapped to ROM */
_brom = 0x00300000;
_brom = ORIGIN(rom);
/* Size of ROM mappings */
_srom = SIZEOF(.text) + SIZEOF(.rodata)
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
@ -175,11 +181,7 @@ SECTIONS
/DISCARD/ : {
/* SH3-only data sections */
*(.gint.data.sh3 .gint.bss.sh3)
/* Debug sections (often from libgcc) */
*(.debug_info .debug_abbrev .debug_loc .debug_aranges
.debug_ranges .debug_line .debug_str .debug_frame
.debug_loclists .debug_rnglists)
*(.gint.rodata.sh3 .gint.data.sh3 .gint.bss.sh3)
/* Java class registration (why are they even here?!) */
*(.jcr)
/* Asynchronous unwind tables: no C++ exception handling */

View File

@ -5,14 +5,17 @@
configure:
@ fxsdk build-fx -c $(GINT_CMAKE_OPTIONS)
@ fxsdk build-cg -c $(GINT_CMAKE_OPTIONS)
@ fxsdk build-fx-as-cg -c $(GINT_CMAKE_OPTIONS)
build:
@ fxsdk build-fx
@ fxsdk build-cg
@ fxsdk build-fx-as-cg
install:
@ fxsdk build-fx install
@ fxsdk build-cg install
@ fxsdk build-fx-as-cg install
uninstall:
@ if [ -e build-fx/install_manifest.txt ]; then \
@ -21,5 +24,8 @@ uninstall:
@ if [ -e build-cg/install_manifest.txt ]; then \
xargs rm -f < build-cg/install_manifest.txt; \
fi
@ if [ -e build-fx-as-cg/install_manifest.txt ]; then \
xargs rm -f < build-fxascg/install_manifest.txt; \
fi
.PHONY: configure build install uninstall

View File

@ -73,6 +73,9 @@ extern "C" {
/* Remove a file or folder (also works if the entry does not exist). */
int BFile_Remove(uint16_t const *path);
/* Rename a file (can move folders; Fugue only). */
int BFile_Rename(uint16_t const *oldpath, uint16_t const *newpath);
#define BFile_File 1
#define BFile_Folder 5

View File

@ -68,28 +68,40 @@ enum {
/* Combinations of hardware settings that are none of Ftune's levels */
CLOCK_SPEED_UNKNOWN = 0,
/* Ftune's 5 default overclock levels. The main settings are listed below,
thoug many more are involved.
On SH4 fx-9860G-likr:
(Not supported yet)
On the fx G-III series:
(Not supported yet)
/* Ftune's 5 default overclock levels. The main settings are listed
below, though many more are involved.
On SH3 fx-9860G-like:
F1: CPU @ 29 MHz [Default speed]
F2: CPU @ 58 MHz [Similar to G-III default]
F3: CPU @ 88 MHz
F4: CPU @ 118 MHz [Fastest CPU option]
F5: CPU @ 118 MHz [Reduced memory wait times]
On SH4 fx-9860G-like:
F1: CPU @ 29 MHz, BFC @ 29 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [Similar to G-III default]
F3: CPU @ 29 MHz, BFC @ 29 MHz [SH3 default]
F4: CPU @ 118 MHz, BFC @ 59 MHz
F5: CPU @ 236 MHz, BFC @ 118 MHz [Fastest option]
On G-III / Graph 35+E II:
F1: CPU @ 58 MHz, BFC @ 29 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [fx-CG 10/20 default]
F3: CPU @ 29 MHz, BFC @ 29 MHz [SH3 default]
F4: CPU @ 118 MHz, BFC @ 58 MHz
F5: CPU @ 235 MHz, BFC @ 58 MHz [Fastest option]
On fx-CG 10/20:
F1: CPU @ 58 MHz, BFC @ 29 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [Improved memory speed]
F3: CPU @ 118 MHz, BFC @ 58 MHz [Faster than F2]
F4: CPU @ 118 MHz, BFC @ 118 MHz [Fastest bus option]
F5: CPU @ 191 MHz, BFC @ 94 MHz [Fastest CPU option]
F1: CPU @ 58 MHz, BFC @ 29 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [Improved memory speed]
F3: CPU @ 118 MHz, BFC @ 58 MHz [Faster than F2]
F4: CPU @ 118 MHz, BFC @ 118 MHz [Fastest bus option]
F5: CPU @ 191 MHz, BFC @ 94 MHz [Fastest CPU option]
On fx-CG 50:
F1: CPU @ 116 MHz, BFC @ 58 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [fx-CG 10/20 default]
F3: CPU @ 94 MHz, BFC @ 47 MHz [Clearly slow: F2 < F3 < F1]
F4: CPU @ 232 MHz, BFC @ 58 MHz [Fastest CPU option]
F5: CPU @ 189 MHz, BFC @ 94 MHz [Fastest bus option] */
On fx-CG 50:
F1: CPU @ 116 MHz, BFC @ 58 MHz [Default speed]
F2: CPU @ 58 MHz, BFC @ 29 MHz [Clearly slower: F2 < F3 < F1]
F3: CPU @ 94 MHz, BFC @ 47 MHz [Clearly slower: F2 < F3 < F1]
F4: CPU @ 232 MHz, BFC @ 58 MHz [Fastest CPU option]
F5: CPU @ 189 MHz, BFC @ 94 MHz [Fastest bus option] */
CLOCK_SPEED_F1 = 1,
CLOCK_SPEED_F2 = 2,
CLOCK_SPEED_F3 = 3,
@ -100,7 +112,6 @@ enum {
CLOCK_SPEED_DEFAULT = CLOCK_SPEED_F1,
};
#ifdef FXCG50
/* clock_get_speed(): Determine the current clock speed
This function compares the current hardware state with the settings for each
@ -123,7 +134,26 @@ int clock_get_speed(void);
Currently the clock speed is not reset during a world switch nor when
leaving the add-in. */
void clock_set_speed(int speed);
#endif
/* If you want to faithfully save and restore the clock state while properly
handling clock speeds that are not Ftune/PTune's defaults, you can get a
full copy of the settings.
WARNING: Applying random settings with cpg_set_overclock_setting() might
damage your calculator! */
struct cpg_overclock_setting
{
uint32_t FLLFRQ, FRQCR;
uint32_t CS0BCR, CS2BCR, CS3BCR, CS5aBCR;
uint32_t CS0WCR, CS2WCR, CS3WCR, CS5aWCR;
};
/* Queries the clock setting from the hardware. */
void cpg_get_overclock_setting(struct cpg_overclock_setting *s);
/* Applies the specified overclock setting. */
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s);
//---
// Sleep functions
@ -143,27 +173,6 @@ void sleep_us_spin(uint64_t delay_us);
/* sleep_ms(): Sleep for a fixed duration in milliseconds */
#define sleep_ms(delay_ms) sleep_us((delay_ms) * 1000ull)
//---
// Low-level overclock functions
//
// These low-level functions directly read or write registers involved in
// setting the overclock level. Don't use them directly unless you understand
// how their interactions with the environment; instead, use clock_set_speed().
//---
struct cpg_overclock_setting
{
uint32_t FLLFRQ, FRQCR;
uint32_t CS0BCR, CS2BCR, CS3BCR, CS5aBCR;
uint32_t CS0WCR, CS2WCR, CS3WCR, CS5aWCR;
};
/* Queries the clock setting from the hardware. */
void cpg_get_overclock_setting(struct cpg_overclock_setting *s);
/* Applies the specified overclock setting. */
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s);
#ifdef __cplusplus
}
#endif

View File

@ -38,4 +38,7 @@
enabled or disabled at runtime. */
#cmakedefine GINT_KMALLOC_DEBUG
/* GINT_USB_DEBUG: Selects whether USB debug functions are enabled */
#cmakedefine GINT_USB_DEBUG
#endif /* GINT_CONFIG */

View File

@ -10,6 +10,7 @@
/* Objects from the gint's uninitialized BSS section */
#define GBSS __attribute__((section(".gint.bss")))
/* Additional sections that are only needed on SH3 */
#define GRODATA3 __attribute__((section(".gint.rodata.sh3")))
#define GDATA3 __attribute__((section(".gint.data.sh3")))
#define GBSS3 __attribute__((section(".gint.bss.sh3")))
/* Objects for the ILRAM, XRAM and YRAM regions */

View File

@ -153,7 +153,8 @@ static GINLINE int gint_call(gint_call_t cb)
int (*f)(int r4, int r5, int r6, int r7) = cb.function;
#endif
return f(cb.args[0].i, cb.args[1].i, cb.args[2].i, cb.args[3].i);
gint_call_arg_t *args = cb.args;
return f ? f(args[0].i, args[1].i, args[2].i, args[3].i) : -1;
}
//---

View File

@ -0,0 +1,36 @@
//---
// gint:defs:timeout - RTC-based timeouts
//
// This header provides an interface for simplistic timers used for timeout
// waiting. Currently they are based on the RTC with a resolution of 1/128 s.
//---
#ifndef GINT_DEFS_TIMEOUT
#define GINT_DEFS_TIMEOUT
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
#include <stdbool.h>
/* Object holding information about a timeout (specifically, when it expires).
TODO: timeout: consider using a struct timespec with clock_gettime()? */
typedef clock_t timeout_t;
static inline timeout_t timeout_make_ms(int ms)
{
return clock() + (int64_t)ms * CLOCKS_PER_SEC / 1000;
}
static inline bool timeout_elapsed(timeout_t const *t)
{
return t && clock() >= *t;
}
#ifdef __cplusplus
}
#endif
#endif /* GINT_DEFS_TIMEOUT */

View File

@ -14,6 +14,11 @@
#define GAUTOTYPE __auto_type
#endif
#ifdef __cplusplus
#include <algorithm>
using std::min;
using std::max;
#else
/* min(), max() (without double evaluation) */
#define min(a, b) ({ \
GAUTOTYPE _a = (a); \
@ -25,6 +30,7 @@
GAUTOTYPE _b = (b); \
_a > _b ? _a : _b; \
})
#endif
/* sgn() (without double evaluation) */
#define sgn(s) ({ \

View File

@ -15,7 +15,7 @@
#ifndef GINT_DISPLAY_CG
#define GINT_DISPLAY_CG
#ifdef FXCG50
#if defined(FXCG50) && !defined(FX9860G_AS_CG)
#ifdef __cplusplus
extern "C" {
@ -70,31 +70,27 @@ typedef image_t bopti_image_t;
// Video RAM management
//---
/* dsetvram() - Control video RAM address and triple buffering
/* dsetvram(): Control video RAM address and triple buffering
Normal rendering under gint uses double-buffering: there is one image
displayed on the screen and one in memory, in a region called the video RAM
(VRAM). The application draws frames in the VRAM then sends them to the
screen only when they are finished, using dupdate().
On fxcg50, the performance bottleneck is almost always the graphical
rendering (especially in games) because the high amount of data, 173 kB per
frame in full-resolution, makes graphics manipulation computationally
expensive. The transfer also takes about 10 ms in itself.
On fx-CG, sending frames with dupdate() is a common bottleneck because it
takes about 11 ms. Fortunately, while the DMA is sending the frame to the
display, the CPU is available to do work in parallel. This function sets up
triple buffering (ie. a second VRAM) so that the CPU can start working on
the next frame while the DMA is sending the current one.
Since gint transfers data to the screen using the DMA, it is possible to run
the application while the finished frame is being transferred. However,
writing to the VRAM during this period will cause display artifacts since
the VRAM it is still being read by the DMA.
However, experience shows minimal performance improvements, because writing
to main RAM does not parallelize with DMA transfers. Since gint 2.8, this
is no longer the default, and the memory for the extra VRAM is instead
available via malloc().
The solution to this is to use triple-buffering with the display and two
VRAMs that are alternately being written to while the other is being
transferred. The VRAM switching is handled by dupdate() and is activated
whenever two VRAMs are configured.
By default gint uses triple buffering with two VRAMs in the system stack.
VRAMs must be contiguous, 32-aligned, (2*396*224)-byte buffers.
VRAMs must be contiguous, 32-aligned, (2*396*224)-byte buffers with 32 bytes
of extra data on each side (ie. 32 bytes into a 32-aligned buffer of size
177472).
@main Main VRAM area, used alone if [secondary] is NULL
@secondary Additional VRAM area, enables triple buffering if non-NULL */

View File

@ -9,7 +9,7 @@
#ifndef GINT_DISPLAY_FX
#define GINT_DISPLAY_FX
#ifdef FX9860G
#if defined(FX9860G) || defined(FX9860G_AS_CG)
#ifdef __cplusplus
extern "C" {

View File

@ -19,16 +19,14 @@ extern "C" {
/* Platform-specific functions include VRAM management and the definition of
the color_t type. */
#ifdef FX9860G
#if defined(FX9860G) || defined(FX9860G_AS_CG)
#include <gint/display-fx.h>
#endif
#ifdef FXCG50
#if defined(FXCG50) && ! defined(FX9860G_AS_CG)
#include <gint/display-cg.h>
#endif
/* TODO: dinfo() or similar */
//---
// Video RAM management
//---
@ -82,6 +80,31 @@ void dupdate_set_hook(gint_call_t function);
/* dupdate_get_hook(): Get a copy of the dupdate() hook */
gint_call_t dupdate_get_hook(void);
//---
// Rendering mode control
//---
/* dmode: Rendering mode settings
This structure indicates the current window settings. Rendering is limited
to the rectangle spanning from (left,top) included, to (right,bottom)
excluded. The default is from (0,0) to (DWIDTH,DHEIGHT). */
struct dwindow {
int left, top;
int right, bottom;
};
extern struct dwindow dwindow;
/* dwindow_set(): Set the rendering window
This function changes [dwindow] settings to limit rendering to a sub-surface
of the VRAM. Note that this doesn't change the coordinate system; the pixel
at (DWIDTH/2, DHEIGHT/2) is always in the middle of the screen regardless of
the window setting. However, it might be masked out by the window.
Returns the old dwindow settings (if it needs to be restored later). */
struct dwindow dwindow_set(struct dwindow new_mode);
//---
// Area rendering functions
//---
@ -129,6 +152,19 @@ void drect(int x1, int y1, int x2, int y2, int color);
void drect_border(int x1, int y1, int x2, int y2,
int fill_color, int border_width, int border_color);
void dellipse(int xm, int ym, int a, int b, int c);
void dellipserect(int x0, int y0, int x1, int y1, int c);
void dcircle(int xm, int ym, int r, int c);
void dellipse_fill(int xm, int ym, int a, int b, int cback, int cborder);
void dcircle_fill(int xm, int ym, int r, int cback, int cborder);
//---
// Point drawing functions
//---
@ -151,6 +187,11 @@ void drect_border(int x1, int y1, int x2, int y2,
*: When the gray engine is on, see dgray(). */
void dpixel(int x, int y, int color);
/* dgetpixel(): Get a pixel's color
Returns the current color of any pixel in the VRAM. This function ignores
the rendering window. Returns -1 if (x,y) is out of bounds. */
int dgetpixel(int x, int y);
/* dline(): Render a straight line
This function draws a line using a Bresenham-style algorithm. Please note

View File

@ -0,0 +1,293 @@
//---
// gint:usb:asyncio - Asynchronous I/O common definitions
//---
#ifndef GINT_USB_ASYNCIO
#define GINT_USB_ASYNCIO
#include <gint/defs/types.h>
#include <gint/defs/call.h>
/* Data tracking the progress of a multi-part multi-round async I/O operation.
* Writes are multi-part because they are constructed over several calls to
write(2) followed by a "commit" with fynsc(2). They are additionally
multi-round because each call to write(2) requires mutiple rounds of
hardware communication when the hardware buffer is smaller than the data.
* Reads are multi-part because each transaction from the host requires
several calls to read(2) ("user segments") if the user's buffer is shorter
than the full transaction. In addition, reads are multi-round because a
single read(2) to a user buffer takes multiple rounds of hardware
communication ("hardware segments") whenever the user buffer is larger
than the hardware buffer.
The process of performing such an I/O operation, as tracked by this
structure and use throughout gint, is as follows.
## Writes
WRITING ---------------------.
^ | | HW buffer
Start writing | | Not full | full: start
| | | transmission
write(2) | v v
--> IDLE ------------------> PENDING <------------- FLYING-WRITE
^ ^ | DONE interrupt
| DONE write(2) | |
| interrupt | |
| | | Data exhausted
| fsync(2): start | v
FLYING-SYNC <------------ IN-PROGRESS
transmission
Initially the operation is in the IDLE state. When a write(2) is issued, it
interacts with hardware then transitions to the IN-PROGRESS state, where it
remains for any subsequent write(2). A fsync(2) will properly commit data to
the hardware, finish the operation and return to the IDLE state.
The FLYING-WRITE and FLYING-SYNC states refer to waiting periods, after
issuing hardware commands, during which hardware communicates. Usually an
interrupt signals when hardware is ready to resume work.
Note that in a series of write(2), hardware is only instructed to send data
once the hardware buffer is full. Therefore, a write(2) might transition
directly from IDLE or IN-PROGRESS, to PENDING, to IN-PROGRESS, without
actually communicating with the outside world.
An asynchronous write(2) might return to the caller as soon as writing is
finished even if the operation is left in the FLYING-WRITE state, and it may
even return while the operation is in the WRITING state if the DMA is used.
The invariants and meaning for each state are as follow:
State(s) Characterization Description
============================================================================
IDLE type == ASYNCIO_NONE No I/O operation
----------------------------------------------------------------------------
PENDING data_w && round_size == 0 Ready to write pending data
----------------------------------------------------------------------------
WRITING, round_size > 0 CPU/DMA write to HW in progress
FLYING-WRITE HW transmission in progress
----------------------------------------------------------------------------
IN-PROGRESS !data_w && type == WRITE Waiting for write(2) or fsync(2)
----------------------------------------------------------------------------
FLYING-SYNC type == ASYNCIO_SYNC HW commit in progress
============================================================================
The series of asyncio_op function calls for a write is as follows:
transaction ::= write* fsync
write ::= asyncio_op_start_write round+ asyncio_op_finish_write
round ::= asyncio_op_start_write_round asyncio_op_finish_write_round
fsync ::= asyncio_op_start_sync asyncio_op_finish_sync
Each write(2) (with a single user-provided buffer) is split into multiple
rounds that each fill the (small) hardware buffer. More writes can follow
until an fynsc(2) commits the pipe.
## Reads
IN interrupt
--> IDLE-EMPTY --------------> IDLE-READY
| \ read(2) | ^
read(2) | \ Transaction | | User buffer
| \ exhausted* | | filled
| '----<----------. | |
| \ | |
| IN interrupt \ | |
v .--------->--------. \ v | .---. Read from
WAITING READING v hardware
'---------<--------' '---'
HW buffer exhausted with
user buffer not full
On this diagram, the right side indicates the presence of data to read from
hardware while the bottom side indicates a read(2) request by the user.
Notice the diagonal arrow back to IDLE-EMPTY insteaf of WAITING, which
highlights that read(2) will always return at the end of a transaction even
if the user-provided buffer is not full (to avoid waiting).
A read(2) request (a "user segment") might consume several full hardware
buffers ("hardware segments") if the user buffer is large, thus looping
repeatedly between WAITING and READING. Conversely, each hardware segment
might fulfill many read(2) requests if the user buffer is small, thus
looping between IDLE-READY and READING.
* Note that if the transaction finishes right as the user buffer fills up,
we return to IDLE-READY and the next call to read(2) will successfully read
0 bytes and transition back to IDLE-EMPTY. This allows the user to read the
entire transaction by reading data until they get fewer bytes than requested
without running the risk of blocking on the next transaction.
The invariants and meaning for each state are as follow. The right side
(presence of a hardware segment) is indicated by `buffer_used >= 0`, while
the bottom side (presence of a read(2) request) is indicated by `size > 0`.
As an exception, IDLE-EMPTY uses `buffer_used == 0` so that the default
zero-initialization of transfer data is sufficient.
State Invariant characterization Description
============================================================================
IDLE-EMPTY type == ASYNCIO_NONE No I/O operation, the pipe is
&& buffer_used == 0 idle with no request and no
&& size == 0 hardware segment (but we might
&& round_size == 0 still be mid-transaction)
----------------------------------------------------------------------------
IDLE-READY type == ASYNCIO_READ There is a hardware segment not
&& buffer_used >= 0 marked as complete, but no
&& size == 0 read(2) request to consume it
&& round_size == 0
----------------------------------------------------------------------------
WAITING type == ASYNCIO_READ There is a read(2) request but
&& buffer_used < 0 no hardware segment, and either
&& size > 0 the request is new or the
&& round_size == 0 transaction isn't exhausted
----------------------------------------------------------------------------
READING type == ASYNCIO_READ A read round in progress and
&& buffer_used >= 0 either the read(2) request or
&& size > 0 hardware segment will be
&& round_size > 0 exhausted when it ends
============================================================================
The series of asyncio_op function calls for a read is a bit more complicated
because transactions are divided into two non-comparable sequences of
segments: one for packets received by the hardware buffer (on BRDY), one for
the data being copied to user buffers (on read(2)).
|<------ Transaction from the host (no size limit) ------>|
v BRDY v BRDY v BRDY v BRDY v BRDY
+-----------+-----------+-----------+-----------+---------+
| HW buffer | HW buffer | HW buffer | HW buffer | (short) | HW segments
+-----------+------+----+-----------+-----------+---------+
| R1 | R2 | R3 | R4 | R5 | R6 | Read rounds
+-----------+------+----+-----------+-----------+---------+
| User buffer #1 | User buffer #2 | (short) | User segments
+------------------+----------------------------+---------+
^ read(2) ^ read(2) ^ read(2)
Reads rounds are exactly the intersections between hardware segments and
read(2) user segments.
States can be checked and transitioned with the API functions below. */
enum { ASYNCIO_NONE, ASYNCIO_READ, ASYNCIO_WRITE, ASYNCIO_SYNC };
typedef volatile struct
{
/* Type of I/O operation (NONE/WRITE/SYNC/READ) */
uint8_t type;
/* Whether the DMA should be used for hardware access */
bool dma :1;
/* For reading pipes, whether the transaction is expected to continue with
another hardware segment after the current one */
bool cont_r :1;
/* For reading pipes, interrupt flag signaling an incoming hardware segment
not yet added to the operation */
bool interrupt_r :1;
/* For reading pipes, whether the current read call should close the
current hardware segment if all the data is read even if the read call
is not partial */
bool autoclose_r :1;
/* Hardware resource being used for access (meaning depends on hardware).
Usually, this is assigned for the duration of hardware transaction.
This value is user-managed and not modified by asyncio_op functions. */
uint8_t controller;
/* Number of bytes in short buffer (0..3) */
uint8_t shbuf_size;
/* Short buffer */
uint32_t shbuf;
/* Size of data currently in the hardware buffer */
int16_t buffer_used;
/* Size of data being read/written in the current round (which may itself
be asynchronous if it's using the DMA) */
uint16_t round_size;
union {
/* Address of data to transfer, incremented gradually [write] */
void const *data_w;
/* Address of buffer to store data to, incremented gradually [read] */
void *data_r;
};
/* Size of data left to transfer to satisfy the complete request */
int size;
/* For reading operations, pointer to total amount of transferred data */
int *realized_size_r;
/* Callback at the end of the current write, final commit, or read */
gint_call_t callback;
} asyncio_op_t;
//---
// Initialization and query functions
//---
/* asyncio_op_clear(): Initialize/clear the storage for an I/O operation */
void asyncio_op_clear(asyncio_op_t *op);
/* asyncio_op_busy(): Check whether the transfer is busy for syscalls
This function checks whether the transfer is in a state where the CPU is
busy wrt. starting a new syscall, ie. read(2), write(2) or fsync(2). Returns
true if the CPU is busy and the call has to wait, false if the call can
proceed immediately. */
bool asyncio_op_busy(asyncio_op_t const *op);
//---
// I/O functions
//---
/* Start/finish a write(2) call. */
void asyncio_op_start_write(asyncio_op_t *op,
void const *data, size_t size, bool use_dma, gint_call_t const *callback);
void asyncio_op_finish_write(asyncio_op_t *op);
/* Start/finish a single-block write to hardware. */
void asyncio_op_start_write_round(asyncio_op_t *op, size_t size);
void asyncio_op_finish_write_round(asyncio_op_t *op);
/* Start an fsync(2) operation (after one or more writes) and finish it. */
void asyncio_op_start_sync(asyncio_op_t *op, gint_call_t const *callback);
void asyncio_op_finish_sync(asyncio_op_t *op);
/* Start a read(2) call. The call will finish automatically when the final
round finishes. If `autoclose` is set, the current hardware segment will
be marked as completed if the round reads it entirely, even if the request
is fulfilled. */
void asyncio_op_start_read(asyncio_op_t *op, void *data, size_t size,
bool use_dma, int *realized_size, bool autoclose,
gint_call_t const *callback);
/* Start a hardware segment. `cont` should be true if there will be another
segment in the same transaction. The segment will finish automatically when
it is completely consumed by a read round. */
void asyncio_op_start_read_hwseg(asyncio_op_t *op, size_t size, bool cont);
bool asyncio_op_has_read_call(asyncio_op_t const *op);
bool asyncio_op_has_read_hwseg(asyncio_op_t const *op);
/* Start a single-block read from hardware. The requested size is automatically
t->size, however the round may of course be smaller depending on how much
data is available. Returns the round size. */
int asyncio_op_start_read_round(asyncio_op_t *op);
enum {
ASYNCIO_HWSEG_EXHAUSTED = 0x01,
ASYNCIO_REQUEST_FINISHED = 0x02,
ASYNCIO_TRANSACTION_EXHAUSTED = 0x04,
};
/* Finish a single-block read from hardware. This function also finishes the
current hardware segment and read call if appropriate, *except* that it
doesn't invoke the read(2) callback. You should make a copy of it before
calling and invoke it manually after. Returns a combination of the above
flags indicating what finished along with the round. */
int asyncio_op_finish_read_round(asyncio_op_t *op);
/* Cancel a read call. This keeps the hardware segment part intact. */
void asyncio_op_cancel_read(asyncio_op_t *op);
#endif /* GINT_USB_ASYNCIO */

View File

@ -10,6 +10,7 @@ extern "C" {
#endif
#include <gint/keyboard.h>
#include <stdbool.h>
/* Size of the buffer event queue */
#define KEYBOARD_QUEUE_SIZE 32
@ -124,10 +125,13 @@ typedef struct {
that the precision of the delay is limited by the speed at which the
keyboard is scanned, which is nowhere near microsecond-level. By
default (128 Hz) the precision is about 7.8 ms. */
keydev_repeat_profile_t repeater;
keydev_repeat_profile_t repeater;
} GPACKEDENUM keydev_transform_t;
/* keydev_async_filter_t: Low-level asynchronous event filter. */
typedef bool (*keydev_async_filter_t)(key_event_t event);
/* keydev_t: Keyboard device
This structure represents the state and settings of a keyboard device that
@ -135,9 +139,9 @@ typedef struct {
is useful for demo/replays that input events without the physical keyboard,
and a couple of corner uses like control over USB.
The keyboard device has built-in event transformations, which modifty the
The keyboard device has built-in event transformations, which modify the
stream of events by adding information, combining modifiers, and removing
undesired events. Because the event transformation reky on the current state
undesired events. Because the event transformation rely on the current state
of the keyboard, they must be run by the driver whenever events are read, so
they are tied to the device.
@ -159,6 +163,13 @@ typedef struct {
/* Event transforms */
keydev_transform_t tr;
/* Asynchronous event filter. This is a low-level filter which is
called when events are generated to process and filter them. It
provides the unique ability to run keyboard-triggered code even if
the program's main thread is busy, for instance running some sort of
infinite loop. */
keydev_async_filter_t async_filter;
// <Delayed Modifiers>
/* delayed_* is set when the delayed modifier is active (after a press/
@ -173,7 +184,7 @@ typedef struct {
/* Candidate key for repeats (or 0 if no key is candidate yet) */
int rep_key;
/* Number of repeats alreay sent */
/* Number of repeats already sent */
int rep_count;
/* Time since key was first pressed (us) */
int rep_time;
@ -181,7 +192,7 @@ typedef struct {
int rep_delay;
/* Latest state of keys we are aware of. At every processing step, the
different between this and the fresh information is queued and this
difference between this and the fresh information is queued and this
is updated. state_now is identical to the real state obtained from
the device unless earlier events failed to be queued, in which case
a difference is maintained so they will be reconsidered later. */
@ -193,8 +204,8 @@ typedef struct {
/* Event queue (circular buffer) */
key_event_t queue[KEYBOARD_QUEUE_SIZE];
/* Parameters for the standard repeat function */
int rep_standard_first, rep_standard_next;
/* Parameters for the standard repeat function */
int rep_standard_first, rep_standard_next;
} keydev_t;
@ -250,6 +261,12 @@ key_event_t keydev_unqueue_event(keydev_t *d);
must be terminated by a 0 keycode. */
bool keydev_idle(keydev_t *d, ...);
/* keydev_async_filter(): Obtain current async filter */
keydev_async_filter_t keydev_async_filter(keydev_t const *d);
/* keydev_set_async_filter(): Set low-level async filter */
void keydev_set_async_filter(keydev_t *d, keydev_async_filter_t filter);
//---
// High-level API to read from the device
//---
@ -284,8 +301,11 @@ void keydev_set_transform(keydev_t *d, keydev_transform_t tr);
@next_us Delay between subsequent repeats (microseconds) */
void keydev_set_standard_repeats(keydev_t *d, int first_us, int next_us);
/* keydev_read(): Retrieve the next transformed event */
key_event_t keydev_read(keydev_t *d);
/* keydev_read(): Retrieve the next transformed event
If there is no event, returns an event with type KEYEV_NONE, unless
[wait=true], in which case waits for an event to occur or *timeout to
become non-zero (if timeout is not NULL), whichever comes first. */
key_event_t keydev_read(keydev_t *d, bool wait, volatile int *timeout);
#ifdef __cplusplus
}

View File

@ -13,6 +13,7 @@
extern "C" {
#endif
#include <gint/config.h>
#include <gint/mpu/dma.h>
#include <gint/clock.h>
@ -88,13 +89,23 @@ typedef struct {
typedef struct {
/* Control and power-up. We don't save power-related registers from other
modules nor UPONCR, because they must be changed to use the module */
uint16_t SYSCFG, DVSTCTR, TESTMODE, REG_C2;
/* FIFO configuration */
uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL;
uint16_t SYSCFG, BUSWAIT, DVSTCTR, SOFCFG, TESTMODE, REG_C2;
/* Interrupt configuration */
uint16_t INTENB0, BRDYENB, NRDYENB, BEMPENB, SOFCFG;
/* Default Control Pipe (maybe not needed) */
uint16_t DCPCFG, DCPMAXP, DCPCTR;
uint16_t INTENB0, INTENB1, BRDYENB, NRDYENB, BEMPENB;
/* Default Control Pipe */
uint16_t DCPMAXP;
#ifdef GINT_USB_DEBUG
/* Registers tracked read-only for state analysis and debugging */
uint16_t SYSSTS, FRMNUM, UFRMNUM;
uint16_t CFIFOSEL, D0FIFOSEL, D1FIFOSEL, CFIFOCTR, D0FIFOCTR, D1FIFOCTR;
uint16_t INTSTS0, INTSTS1, BRDYSTS, NRDYSTS, BEMPSTS;
uint16_t DCPCFG, DCPCTR;
uint16_t USBADDR, USBREQ, USBVAL, USBINDX, USBLENG;
uint16_t PIPESEL, PIPECFG[9], PIPEnCTR[9], PIPEBUF[9];
/* Ignored: UPONCR, PIPEnMAXP, PIPEnPERI, PIPEnTRN, PIPEnTRE, DEVADDn */
#endif
} usb_state_t;
#ifdef __cplusplus

View File

@ -13,7 +13,7 @@ extern "C" {
#include <stddef.h>
/* Maximum number of file descriptors */
#define FS_FD_MAX 32
#define FS_FD_MAX 16
/* fs_descriptor_type_t: Overloaded file descriptor functions

View File

@ -59,14 +59,23 @@ void gint_world_sync(void);
call to getkey(), but can also be called manually. */
void gint_osmenu(void);
/* gint_osmenu_native(): Like gint_osmenu() without the world switch
This is a replacement for gint_osmenu() which can be used when the current
kernel is already the native OS kernel. */
void gint_osmenu_native(void);
/* gint_setrestart(): Set whether to restart the add-in after exiting
An add-in that reaches the end of its code exits. On the calculator, except
using OS-dependent settings, it cannot be started again unless another
application is launched first.
An add-in that returns from its main() function automatically exits to the
OS' main menu. However, when this happens the OS does not allow the add-in
to be restarted unless another add-in is launched first. (This is because
the OS tries to *resume* the current add-in, which then proceeds to exit
again immediately.)
This setting allows the add-in to restart by calling gint_osmenu() instead
of exiting. This can give a proper illusion of restarting if used correctly.
This function enables a gint trick where after main() returns the add-in
will invoke the main menu with gint_osmenu() rather than exiting. If the
add-in is selected again, gint will jump back to the entry point, creating
the illusion that the add-in exited and was then restarted.
@restart 0 to exit, 1 to restart by using gint_osmenu() */
void gint_setrestart(int restart);
@ -100,6 +109,26 @@ static GINLINE void *gint_inthandler(int code, void const *h, size_t size) {
Returns the return value of the callback. */
extern int (*gint_inth_callback)(gint_call_t const *call);
/* gint_set_quit_handler(): Setup a call to be invoked when leaving the add-in
This function sets up the provided GINT_CALL() to be invoked when the
add-in is unloaded, which is either when we exit from main() or when
starting another application from the main menu. Crucially, this is only
*after* selecting an application, not before opening the main menu. The
quit handler is not invoked if the user re-enters the add-in.
This is based on the SetQuitHandler() syscall, and therefore the callback
runs in the OS world by default. If [run_in_os_world] is set to false, a
world switch will be performed to run the callback in the gint world.
TODO: Currently the quit handler is not called when exiting from main().
TODO: Detail how this interacts with destructor functions!
TODO: [run_in_os_world == false] is not honored yet (because unstable)
@call Callback to be performed when leaving add-in
@run_in_os_world true to stay in OS world, false to use gint world */
void gint_set_quit_handler(gint_call_t gcall, bool run_in_os_world);
#ifdef __cplusplus
}
#endif

View File

@ -45,6 +45,12 @@ void hw_detect(void);
#define isSH4() (!isSH3())
#endif
#if defined(FX9860G)
#define isSlim() (gint[HWCALC] == HWCALC_FX9860G_SLIM)
#else
#define isSlim() 0
#endif
#ifdef FXCG50
#define isSH3() 0
#define isSH4() 1
@ -105,6 +111,8 @@ void hw_detect(void);
#define HWCALC_FXCG50 5
/* fx-CG 50 emulator, hardcoded in kernel/inth.S */
#define HWCALC_FXCG_MANAGER 6
/* fx-9860G Slim, SH-3-based fx-9860G with hardware differences */
#define HWCALC_FX9860G_SLIM 7
/*
** Keyboard

View File

@ -41,6 +41,8 @@ extern "C" {
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
struct dwindow;
//---
// Image structures
//---
@ -653,13 +655,14 @@ struct gint_image_box
};
/* Clip the provided box against the input. If, after clipping, the box no
longer intersects the output (whose size is specified as out_w/out_h),
returns false. Otherwise, returns true. */
longer intersects the output window, returns false. Otherwise, returns
true. */
bool gint_image_clip_input(image_t const *img, struct gint_image_box *box,
int out_w, int out_h);
struct dwindow const *window);
/* Clip the provided box against the output. */
void gint_image_clip_output(struct gint_image_box *b, int out_w, int out_h);
void gint_image_clip_output(struct gint_image_box *b,
struct dwindow const *window);
//---
// Internal image rendering routines
@ -745,15 +748,14 @@ struct gint_image_cmd
@left_edge Whether to force 2-alignment on the input (box->left)
@right_edge Whether to force 2-alignment on the width
@cmd Command to be filled
@out_width Output width (usually DWIDTH)
@out_height Output height (usually DHEIGHT)
@window Rendering window (usually {0, 0, DWIDTH, DHEIGHT})
Returns false if there is nothing to render because of clipping (in which
case [cmd] is unchanged), true otherwise. [*box] is also updated to reflect
the final box after clipping but not accounting for edges. */
bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img,
int effects, bool left_edge, bool right_edge,
struct gint_image_cmd *cmd, int out_width, int out_height);
struct gint_image_cmd *cmd, struct dwindow const *window);
/* Entry point of the renderers. These functions can be called normally as long
as you can build the commands (eg. by using gint_image_mkcmd() then filling

View File

@ -207,7 +207,7 @@ enum {
/* Enable modifiers keys */
GETKEY_MOD_SHIFT = 0x01,
GETKEY_MOD_ALPHA = 0x02,
/* SHIFT + OPTN toggles backlight (requires GETKEY_MOD_SHIFT) */
/* SHIFT + OPTN (requires GETKEY_MOD_SHIFT) or LIGHT toggles backlight */
GETKEY_BACKLIGHT = 0x04,
/* MENU triggers a task switch and displays the main menu */
GETKEY_MENU = 0x08,

View File

@ -75,6 +75,10 @@ enum {
of the matrix one could use a ghosting effect to boot the calc. */
KEY_ACON = 0x07,
/* Virtual key codes */
KEY_HELP = 0x20, /* fx-9860G Slim: 0x75 */
KEY_LIGHT = 0x10, /* fx-9860G Slim: 0x76 */
/* Key aliases (handle with care =D) */
KEY_X2 = KEY_SQUARE,
KEY_CARET = KEY_POWER,

View File

@ -39,6 +39,20 @@ void *krealloc(void *ptr, size_t size);
/* kfree(): Free memory allocated with kalloc() */
void kfree(void *ptr);
/* kmalloc_max(): Allocate the largest block available in an arena
This function is like kmalloc(), but it find the largest block in the arena
and returns it whole after setting its size in *size. This is useful if you
need memory to manage with another allocator, or you don't yet know the size
of the data to be generated. The block can later be shrunk with realloc().
Currently only gint-managed arenas support this operation.
@size Will be set to size of returned block (if there is one)
@arean_name Name of arena to allocate in (*cannot* be NULL)
Returns the address of the largest block available, NULL on error. */
void *kmalloc_max(size_t *size, char const *arena_name);
//---
// Extension API for new areas and statistics
//---
@ -53,6 +67,8 @@ typedef struct {
void * (*realloc)(void *ptr, size_t newsize, void *data);
/* kfree() handles ptr == NULL*/
void (*free)(void *ptr, void *data);
/* kmalloc_max() backend */
void * (*malloc_max)(size_t *size, void *data);
/* Name, should be unique; gint reserves names starting with "_" */
char const *name;

View File

@ -89,7 +89,7 @@ uint32_t tlb_translate(uint32_t page, uint32_t *size);
/* utlb_addr() - get the P4 address of a UTLB address entry
@E Entry number (should be in range 0..63)
Returns a pointer to the entry. */
const utlb_addr_t *utlb_addr(uint E);
utlb_addr_t const *utlb_addr(uint E);
/* utlb_data() - get the P4 address of a UTLB data entry
@E Entry number (should be in range 0..63)
@ -110,6 +110,16 @@ void utlb_mapped_memory(uint32_t *rom, uint32_t *ram);
/* utlb_translate(): Get the physical address for a virtual page */
uint32_t utlb_translate(uint32_t page, uint32_t *size);
/* itlb_addr(): Get the P4 address of an ITLB address entry
@E Entry number (0..3)
Returns a pointer to the entry. */
itlb_addr_t const *itlb_addr(uint E);
/* itlb_data(): Get the P4 address of an ITLB data entry
@E Entry number (0..3)
Returns a pointer to the entry. */
itlb_data_t const *itlb_data(uint E);
#ifdef __cplusplus
}
#endif

View File

@ -12,6 +12,80 @@ extern "C" {
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 But State Controller. Refer to:
// Renesas SH7705 Group Hardware Manual
// Section 7: Bus State Controller (BSC)
//---
typedef volatile lword_union(sh7705_bsc_CSnBCR_t,
uint32_t :2;
uint32_t IWW :2; /* Wait cycles for Write-Read and Write-Write */
uint32_t :1;
uint32_t IWRWD :2; /* Wait cycles for other-space Read-Write */
uint32_t :1;
uint32_t IWRWS :2; /* Wait cycles for same-space Read-Write */
uint32_t :1;
uint32_t IWRRD :2; /* Wait cycles for other-space Read-Read */
uint32_t :1;
uint32_t IWRRS :2; /* Wait cycles for same-space Read-Read */
uint32_t :1;
uint32_t TYPE :3; /* Memory type */
uint32_t :1;
uint32_t BSZ :2; /* Data bus size */
uint32_t :9;
);
/* Warning: the layout of this register changes with n *and* with the memory
type. This version is not exhaustive. Check the manual! */
typedef volatile lword_union(sh7705_bsc_CSnWCR_t,
uint32_t :13;
uint32_t WW :3; /* Write access wait cycles */
uint32_t :3;
uint32_t SW :2; /* Wait from CSn/address to RD/WEn assertion */
uint32_t WR :4; /* Access wait cycles */
uint32_t WM :1; /* Whether to use external wait */
uint32_t :4;
uint32_t HW :2; /* Wait from RD/WEn to CSn/address negation */
);
typedef volatile struct
{
lword_union(CMNCR,
uint32_t :24;
uint32_t DMAIW :2; /* DMA single-address wait states */
uint32_t DMAIWA :1; /* DMAIW wait states insertion method */
uint32_t :1;
uint32_t const ENDIAN :1; /* Global CPU endianness flag */
uint32_t :1;
uint32_t HIZMEM :1; /* High-Z memory Control*/
uint32_t HIZCNT :1; /* High-Z Control*/
);
sh7705_bsc_CSnBCR_t CS0BCR;
sh7705_bsc_CSnBCR_t CS2BCR;
sh7705_bsc_CSnBCR_t CS3BCR;
sh7705_bsc_CSnBCR_t CS4BCR;
sh7705_bsc_CSnBCR_t CS5ABCR;
sh7705_bsc_CSnBCR_t CS5BBCR;
sh7705_bsc_CSnBCR_t CS6ABCR;
sh7705_bsc_CSnBCR_t CS6BBCR;
sh7705_bsc_CSnWCR_t CS0WCR;
sh7705_bsc_CSnWCR_t CS2WCR;
sh7705_bsc_CSnWCR_t CS3WCR;
sh7705_bsc_CSnWCR_t CS4WCR;
sh7705_bsc_CSnWCR_t CS5AWCR;
sh7705_bsc_CSnWCR_t CS5BWCR;
sh7705_bsc_CSnWCR_t CS6AWCR;
sh7705_bsc_CSnWCR_t CS6BWCR;
/* TODO: There are more registers (not involved in overclocking). */
} GPACKED(4) sh7705_bsc_t;
#define SH7705_BSC (*(sh7705_bsc_t *)0xa4fd0000)
//---
// SH7305 But State Controller. Refer to:
// Renesas SH7730 Group Hardware Manual

View File

@ -32,6 +32,12 @@ typedef volatile struct
uint16_t PFC :2; /* Peripheral clock divider */
);
byte_union(UCLKCR,
uint8_t USSCS :2; /* Source Clock Selection Bit */
uint8_t USBEN :1; /* USB On-Chip Oscillator Enable */
uint8_t :5;
);
} GPACKED(4) sh7705_cpg_t;
#define SH7705_CPG (*((sh7705_cpg_t *)0xffffff80))

View File

@ -298,8 +298,8 @@ typedef struct
//---
/* Provided by intc/intc.c */
extern sh7705_intc_t SH7705_INTC;
extern sh7305_intc_t SH7305_INTC;
extern sh7705_intc_t const SH7705_INTC;
extern sh7305_intc_t const SH7305_INTC;
#ifdef __cplusplus
}

View File

@ -49,12 +49,10 @@ typedef struct
} GPACKED(4) tlb_data_t;
//---
// SH7305 TLB. Refer to:
// "Renesas SH7724 User's Manual: Hardware"
// Section 7: "Memory Management Unit (MMU)"
// SH7305 TLB. Refer to SH4AL-DSP manual, section 7 (MMU)
//---
/* utlb_addr_t - address part of a UTLB entry */
/* utlb_addr_t: Address part of a UTLB entry */
typedef struct
{
uint VPN :22;
@ -64,7 +62,7 @@ typedef struct
} GPACKED(4) utlb_addr_t;
/* utlb_data_t - data part of a UTLB entry */
/* utlb_data_t: Data part of a UTLB entry */
typedef struct
{
uint :3;
@ -81,6 +79,34 @@ typedef struct
} GPACKED(4) utlb_data_t;
/* itlb_addr_t: Address part of an ITLB entry */
typedef struct
{
uint VPN :22;
uint :1;
uint V :1;
uint ASID :8;
} GPACKED(4) itlb_addr_t;
/* itlb_data_t: Data part of an ITLB entry */
typedef struct
{
uint :3;
uint PPN :19;
uint :1;
uint V :1;
uint SZ1 :1;
uint PR :1;
uint :1;
uint SZ0 :1;
uint C :1;
uint :1;
uint SH :1;
uint :1;
} GPACKED(4) itlb_data_t;
typedef volatile struct
{
lword_union(PTEH,

View File

@ -76,9 +76,162 @@ typedef volatile struct
#define SH7705_PFC (*((sh7705_pfc_t *)0xa4000100))
//---
// TODO: Document the SH7305 Pin Function Controller
// SH7305 Pin Function Controller.
// This is somewhat reminiscent of the SH7724, but there are many differences.
// The official emulator is the basis for this module.
//---
/* All port control registers (PCRx) have 2-bit entries specifying 4 pin modes:
00: Special functions
01: Output
10: Input (pull-up ON)
11: Input (pull-up OFF)
Some pins do not accept all settings. The SH7724 has these exceptions:
- PGCR (all pins): Input now allowed (00 and 01 only)
- PJCR.P5MD/P6MD/P7MD: Input not allowed (00 and 01 only)
Also, some entries are not configurable or are not mapped to actual pins.
Again the SH7724 has these exceptions:
- PGCR.P6MD/P7MD: No setting
- PJCR.P4MD: No setting
- PSCR.P7MD: No setting */
typedef volatile word_union(sh7305_pfc_control_t,
uint16_t P7MD :2;
uint16_t P6MD :2;
uint16_t P5MD :2;
uint16_t P4MD :2;
uint16_t P3MD :2;
uint16_t P2MD :2;
uint16_t P1MD :2;
uint16_t P0MD :2;
);
/* Data register; a plain 8-bit value. Each bit can be either read or written
depending on the mode of the corresponding pin. */
typedef volatile byte_union(sh7305_pfc_data_t,
uint8_t P7DT :1;
uint8_t P6DT :1;
uint8_t P5DT :1;
uint8_t P4DT :1;
uint8_t P3DT :1;
uint8_t P2DT :1;
uint8_t P1DT :1;
uint8_t P0DT :1;
);
typedef volatile struct
{
sh7305_pfc_control_t
PACR, PBCR, PCCR, PDCR, PECR, PFCR, PGCR, PHCR,
PJCR, PKCR, PLCR, PMCR, PNCR, PQCR, PRCR, PSCR;
sh7305_pfc_data_t PADR;
pad(1);
sh7305_pfc_data_t PBDR;
pad(1);
sh7305_pfc_data_t PCDR;
pad(1);
sh7305_pfc_data_t PDDR;
pad(1);
sh7305_pfc_data_t PEDR;
pad(1);
sh7305_pfc_data_t PFDR;
pad(1);
sh7305_pfc_data_t PGDR;
pad(1);
sh7305_pfc_data_t PHDR;
pad(1);
sh7305_pfc_data_t PJDR;
pad(1);
sh7305_pfc_data_t PKDR;
pad(1);
sh7305_pfc_data_t PLDR;
pad(1);
sh7305_pfc_data_t PMDR;
pad(1);
sh7305_pfc_data_t PNDR;
pad(1);
sh7305_pfc_data_t PQDR;
pad(1);
sh7305_pfc_data_t PRDR;
pad(1);
sh7305_pfc_data_t PSDR;
pad(1);
sh7305_pfc_control_t PTCR;
sh7305_pfc_control_t PUCR;
sh7305_pfc_control_t PVCR;
pad(6);
sh7305_pfc_control_t PPCR;
/* PSEM*: Multiplexing pin settings. These are highly MPU-dependent and
the assignment is basically unknown.
HIZCR*: High-impedance settings for pins' I/O buffers. */
uint16_t PSELA;
uint16_t PSELB;
uint16_t PSELC;
uint16_t PSELD;
uint16_t PSELE;
uint16_t HIZCRA;
uint16_t HIZCRB;
uint16_t HIZCRC;
uint16_t PSELF;
sh7305_pfc_data_t PTDR;
pad(1);
sh7305_pfc_data_t PUDR;
pad(1);
sh7305_pfc_data_t PVDR;
pad(5);
sh7305_pfc_data_t PPDR;
pad(0x15);
/* Module function selection registers.
WARNING: These are the SH7724 bits, not necessarily the SH7305! */
word_union(MSELCRA,
uint16_t :8;
uint16_t UNKNOWN_USB :2;
uint16_t :6;
);
word_union(MSELCRB,
uint16_t XTAL_USB :2;
uint16_t :6;
uint16_t SCIF2_PORT :1;
uint16_t :1;
uint16_t SCIF3_PORT :1;
uint16_t :3;
uint16_t LDC_VSYNC_DIR :1;
uint16_t :1;
);
// TODO: Doc
uint16_t DRVCRD, DRVCRA, DRVCRB, DRVCRC;
pad(4);
/* Pull-up control registers. Not sure what these are since that's
normally handled in the modes for P*CR? */
uint8_t PULCRA, PULCRB, PULCRC, PULCRD, PULCRE, PULCRF, PULCRG,
PULCRH, PULCRJ, PULCRK, PULCRL, PULCRM, PULCRN, PULCRQ,
PULCRR, PULCRS;
pad(0x20);
uint8_t PULCRT;
uint8_t PULCRU;
uint8_t PULCRV;
uint8_t PULCRBSC;
pad(1);
uint8_t PULCRTRST;
uint8_t PULCRP;
pad(1);
uint16_t PSELG;
pad(12);
uint16_t PSELH;
} sh7305_pfc_t;
#define SH7305_PFC (*((sh7305_pfc_t *)0xa4050100))
#ifdef __cplusplus
}
#endif

218
include/gint/mpu/scif.h Normal file
View File

@ -0,0 +1,218 @@
//---
// gint:mpu:scif - Serial Communication Interface with FIFO (SCIF)
//---
#ifndef GINT_MPU_SCIF
#define GINT_MPU_SCIF
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 SCIF (from: SH7705 manual, section 16)
//---
typedef volatile struct
{
/* Serial Mode Register */
word_union(SCSMR,
uint16_t :5;
uint16_t SRC :3; /* Sampling Control */
uint16_t CA :1; /* Communication Mode */
uint16_t CHR :1; /* Character Length */
uint16_t PE :1; /* Parity Enable */
uint16_t OE :1; /* Parity Mode */
uint16_t STOP :1; /* Stop Bit Length */
uint16_t :1;
uint16_t CKS :2; /* Clock Select */
);
pad(0x2);
/* Bit Rate Register */
uint8_t SCBRR;
pad(0x3);
/* Serial Control Register */
word_union(SCSCR,
uint16_t :4;
uint16_t TSIE :1; /* Transmit Data Stop Interrupt Enable */
uint16_t ERIE :1; /* Receive Error Interrupt Enable */
uint16_t BRIE :1; /* Break Interrupt Enable */
uint16_t DRIE :1; /* Receive Data Ready Interrupt Enable */
uint16_t TIE :1; /* Transmit Interrupt Enable */
uint16_t RIE :1; /* Receive Interrupt Enable */
uint16_t TE :1; /* Transmit Enable */
uint16_t RE :1; /* Receive Mode */
uint16_t :2;
uint16_t CKE :2; /* Clock Enable */
);
pad(0x2);
/* Transmit Data Stop Register */
uint8_t SCTDSR;
pad(0x3);
/* FIFO Error Count Register */
word_union(SCFER,
uint16_t :2;
uint16_t PER :6; /* Parity Error Count */
uint16_t :2;
uint16_t FER :6; /* Framing Error Count */
);
pad(0x2);
/* Serial Status Register */
word_union(SCSSR,
uint16_t :6;
uint16_t ORER :1; /* Overrun Error */
uint16_t TSF :1; /* Transmit Data Stop */
uint16_t ER :1; /* Receive Error */
uint16_t TEND :1; /* Transmit End */
uint16_t TDFE :1; /* Transmit FIFO Data Empty */
uint16_t BRK :1; /* Break Detect */
uint16_t FER :1; /* Framing Error */
uint16_t PER :1; /* Parity Error */
uint16_t RDF :1; /* Receive FIFO Data Full */
uint16_t DR :1; /* Receive Data Ready */
);
pad(0x2);
/* FIFO Control Register */
word_union(SCFCR,
uint16_t TSE :1; /* Transmit Data Stop Enable */
uint16_t TCRST :1; /* Transmit Count Reset */
uint16_t :3;
uint16_t RSTRG :3; /* RTS Output Active Trigger */
uint16_t RTRG :2; /* Receive FIFO Data Number Trigger */
uint16_t TTRG :2; /* Transmit FIFO Data Number Trigger */
uint16_t MCE :1; /* Modem Control Enable */
uint16_t TFRST :1; /* Transmit FIFO Data Register Reset */
uint16_t RFRST :1; /* Receive FIFO Data Register Reset */
uint16_t LOOP :1; /* Loopback Test */
);
pad(0x2);
/* FIFO Data Count Register */
word_union(SCFDR,
uint16_t :1;
uint16_t T :7; /* Pending bytes in transmit FIFO */
uint16_t :1;
uint16_t R :7; /* Received bytes in receive FIFO */
);
pad(0x2);
/* Serial FIFO Transmit Data Register (64 bytes rolling register) */
uint8_t SCFTDR;
pad(0x3);
/* Serial FIFO Receive Data Register (64 bytes rolling register) */
uint8_t SCFRDR;
} GPACKED(4) sh7705_scif_t;
#define SH7705_SCIF (*((sh7705_scif_t *)0xa4410000))
//---
// SH7305 SCIF (from: SH7305 emulator and SH7730 SCIF)
// The module is very close to the SH7724 but it has a couple extra bits in
// SCFCR, which shows that it is closer to the SH7730 SCIF.
//---
typedef volatile struct
{
/* Serial Mode Register */
word_union(SCSMR,
uint16_t :8;
uint16_t CA :1; /* Communication Mode */
uint16_t CHR :1; /* Character Length */
uint16_t PE :1; /* Parity Enable */
uint16_t OE :1; /* Parity Mode */
uint16_t STOP :1; /* Stop Bit Length */
uint16_t :1;
uint16_t CKS :2; /* Clock Select */
);
pad(0x2);
/* Serial Bit Rate Register */
uint8_t SCBRR;
pad(0x3);
/* Serial Control Register */
word_union(SCSCR,
uint16_t :8;
uint16_t TIE :1; /* Transmit Interrupt Enable */
uint16_t RIE :1; /* Receive Interrupt Enable */
uint16_t TE :1; /* Transmit Enable */
uint16_t RE :1; /* Receive Mode */
uint16_t REIE :1; /* Receive Error Interrupt Enable */
uint16_t :1;
uint16_t CKE :2; /* Clock Enable */
);
pad(0x2);
/* Serial FIFO Transmit Data Register */
uint8_t SCFTDR;
pad(0x3);
/* Serial FIFO Status Register */
word_union(SCFSR,
uint16_t const PERC :4; /* Number of Parity Errors */
uint16_t const FERC :4; /* Number of Framing Errors */
uint16_t ER :1; /* Receive Error */
uint16_t TEND :1; /* Transmit End */
uint16_t TDFE :1; /* Transmit FIFO Data Empty */
uint16_t BRK :1; /* Break Detection */
uint16_t const FER :1; /* Framing Error */
uint16_t const PER :1; /* Parity Error */
uint16_t RDF :1; /* Receive FIFO Data Full */
uint16_t DR :1; /* Data Ready */
);
pad(0x2);
/* Serial FIFO Receive Data Register */
uint8_t SCFRDR;
pad(0x3);
/* Serial FIFO Control Register */
word_union(SCFCR,
uint16_t :5;
uint16_t RSTRG :3; /* RTS Output Active Trigger */
uint16_t RTRG :2; /* Receive FIFO Data Trigger */
uint16_t TTRG :2; /* Transmit FIFO Data Trigger */
uint16_t MCE :1; /* Model Control Enable */
uint16_t TFRST :1; /* Transmit FIFO Data Register Reset */
uint16_t RFRST :1; /* Receive FIFO Data Register Reset */
uint16_t LOOP :1; /* Loopback Test */
);
pad(0x2);
/* Serial FIFO Data Count Register */
word_union(SCFDR,
uint16_t :3;
uint16_t TFDC :5; /* Number of Data Bytes in Transmit FIFO */
uint16_t :3;
uint16_t RFDC :5; /* Number of Data Bytes in Receive FIFO */
);
pad(0x6);
/* Serial Line Status */
word_union(SCLSR,
uint16_t :15;
uint16_t ORER :1; /* Overrun Error */
);
pad(0x2);
} GPACKED(4) sh7305_scif_t;
#define SH7305_SCIF (*((sh7305_scif_t *)0xa4410000))
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_SCIF */

View File

@ -11,6 +11,19 @@ extern "C" {
#include <gint/defs/types.h>
typedef struct
{
word_union(TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
} sh7305_usb_pipetr;
typedef volatile struct
{
/* System Configuration Control Register */
@ -28,12 +41,16 @@ typedef volatile struct
/* CPU Bus Wait Setting Register */
word_union(BUSWAIT,
uint16_t :12;
uint16_t :8;
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t :1;
uint16_t _2 :1; /* Unknown role; can be set */
uint16_t :1;
uint16_t BWAIT :4; /* Bus Wait */
);
/* System Configuration Status Register */
word_union(SYSSTS,
const word_union(SYSSTS,
uint16_t :14;
uint16_t LNST :2; /* Line Status */
);
@ -41,7 +58,9 @@ typedef volatile struct
/* Device State Control Register */
word_union(DVSTCTR,
uint16_t :7;
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t :2;
uint16_t _2 :4; /* Unknown role; can be set */
uint16_t WKUP :1; /* Wakeup Output */
uint16_t RWUPE :1; /* Wakeup Detection Enable */
uint16_t USBRT :1; /* USB Reset Output */
@ -92,8 +111,9 @@ typedef volatile struct
uint16_t DREQE :1; /* DMA Transfert Request Enable */
uint16_t MBW :2; /* Access Bits Width */
uint16_t :1;
uint16_t BIGEND :1; /* Endiant Control */
uint16_t :4;
uint16_t BIGEND :1; /* Endian Control */
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t :3;
uint16_t CURPIPE:4; /* Port Access Pipe Specification */
);
word_union(D0FIFOCTR,
@ -111,7 +131,8 @@ typedef volatile struct
uint16_t MBW :2; /* Access Bits Width */
uint16_t :1;
uint16_t BIGEND :1; /* Endian Control */
uint16_t :4;
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t :3;
uint16_t CURPIPE:4; /* Port Access Pipe Specification */
);
word_union(D1FIFOCTR,
@ -134,50 +155,26 @@ typedef volatile struct
uint16_t BRDYE :1; /* Buffer Ready */
uint16_t :8;
);
pad(4);
word_union(INTENB1,
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t BCHGE :1; /* Bus Change */
uint16_t :1;
uint16_t DTCHE :1; /* Disconnection Detection */
uint16_t ATTCHE :1; /* Connection Detection */
uint16_t :4;
uint16_t EOFERRE:1; /* EOF Error Detection */
uint16_t SIGNE :1; /* Setup Transaction Error */
uint16_t SACKE :1; /* Setup Transaction Normal Response */
uint16_t :4;
);
pad(2);
/* BRDY Interrupt Enable Register */
word_union(BRDYENB,
uint16_t :6;
uint16_t PIPE9BRDYE :1;
uint16_t PIPE8BRDYE :1;
uint16_t PIPE6BRDYE :1;
uint16_t PIPE7BRDYE :1;
uint16_t PIPE5BRDYE :1;
uint16_t PIPE3BRDYE :1;
uint16_t PIPE4BRDYE :1;
uint16_t PIPE2BRDYE :1;
uint16_t PIPE1BRDYE :1;
uint16_t PIPE0BRDYE :1;
);
uint16_t BRDYENB;
/* NRDY Interrupt Enable Register */
word_union(NRDYENB,
uint16_t :6;
uint16_t PIPE9NRDYE :1;
uint16_t PIPE8NRDYE :1;
uint16_t PIPE6NRDYE :1;
uint16_t PIPE7NRDYE :1;
uint16_t PIPE5NRDYE :1;
uint16_t PIPE3NRDYE :1;
uint16_t PIPE4NRDYE :1;
uint16_t PIPE2NRDYE :1;
uint16_t PIPE1NRDYE :1;
uint16_t PIPE0NRDYE :1;
);
uint16_t NRDYENB;
/* BEMP Interrupt Enable Register */
word_union(BEMPENB,
uint16_t :6;
uint16_t PIPE9BEMPE :1;
uint16_t PIPE8BEMPE :1;
uint16_t PIPE6BEMPE :1;
uint16_t PIPE7BEMPE :1;
uint16_t PIPE5BEMPE :1;
uint16_t PIPE3BEMPE :1;
uint16_t PIPE4BEMPE :1;
uint16_t PIPE2BEMPE :1;
uint16_t PIPE1BEMPE :1;
uint16_t PIPE0BEMPE :1;
);
uint16_t BEMPENB;
/* SOF Control Register */
word_union(SOFCFG,
@ -186,7 +183,10 @@ typedef volatile struct
uint16_t :1;
uint16_t BRDYM :1; /* BRDY Status Clear Timing */
uint16_t enable :1; /* SHOULD BE SET TO 1 MANUALLY */
uint16_t :5;
uint16_t :1;
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t _2 :1; /* Unknown role; can be set */
uint16_t :2;
);
pad(2);
@ -205,56 +205,32 @@ typedef volatile struct
uint16_t VALID :1; /* USB Request Reception */
uint16_t CTSQ :3; /* Control Transfer Stage */
);
pad(4);
word_union(INTSTS1,
uint16_t _1 :1; /* Unknown role */
uint16_t BCHG :1; /* Bus Change */
uint16_t :1;
uint16_t DTCH :1; /* Disconnection Detection */
uint16_t ATTCH :1; /* Connection Detection */
uint16_t :4;
uint16_t EOFERR :1; /* EOF Error Detection */
uint16_t SIGN :1; /* Setup Transaction Error */
uint16_t SACK :1; /* Setup Transaction Normal Response */
uint16_t :4;
);
pad(2);
/* BRDY Interrupt Status Register */
word_union(BRDYSTS,
uint16_t :6;
uint16_t PIPE9BRDY :1;
uint16_t PIPE8BRDY :1;
uint16_t PIPE6BRDY :1;
uint16_t PIPE7BRDY :1;
uint16_t PIPE5BRDY :1;
uint16_t PIPE3BRDY :1;
uint16_t PIPE4BRDY :1;
uint16_t PIPE2BRDY :1;
uint16_t PIPE1BRDY :1;
uint16_t PIPE0BRDY :1;
);
uint16_t BRDYSTS;
/* NRDY Interrupt Status Register */
word_union(NRDYSTS,
uint16_t :6;
uint16_t PIPE9NRDY :1;
uint16_t PIPE8NRDY :1;
uint16_t PIPE6NRDY :1;
uint16_t PIPE7NRDY :1;
uint16_t PIPE5NRDY :1;
uint16_t PIPE3NRDY :1;
uint16_t PIPE4NRDY :1;
uint16_t PIPE2NRDY :1;
uint16_t PIPE1NRDY :1;
uint16_t PIPE0NRDY :1;
);
uint16_t NRDYSTS;
/* BEMP Interrupt Status Register */
word_union(BEMPSTS,
uint16_t :6;
uint16_t PIPE9BEMP :1;
uint16_t PIPE8BEMP :1;
uint16_t PIPE6BEMP :1;
uint16_t PIPE7BEMP :1;
uint16_t PIPE5BEMP :1;
uint16_t PIPE3BEMP :1;
uint16_t PIPE4BEMP :1;
uint16_t PIPE2BEMP :1;
uint16_t PIPE1BEMP :1;
uint16_t PIPE0BEMP :1;
);
uint16_t BEMPSTS;
/* Frame Number Registers */
word_union(FRMNUM,
uint16_t OVRN :1; /* Overrun/Underrun Detection Status */
uint16_t CRCE :1; /* Receive Data Error */
uint16_t cons :3;
uint16_t const :3;
uint16_t FRNM :11; /* Frame Number */
);
word_union(UFRMNUM,
@ -289,7 +265,10 @@ typedef volatile struct
/* DCP Configuration Register */
word_union(DCPCFG,
uint16_t :11;
uint16_t :7;
uint16_t _1 :1; /* Unknown role; can be set */
uint16_t _2 :1; /* Unknown role; can be set */
uint16_t :2;
uint16_t DIR :1; /* Transfer Direction */
uint16_t :4;
);
@ -341,7 +320,7 @@ typedef volatile struct
/* Pipe Buffer Setting Register */
word_union(PIPEBUF,
uint16_t :1;
uint16_t BUFSIZE : 5; /* Buffer Size */
uint16_t BUFSIZE:5; /* Buffer Size */
uint16_t :2;
uint16_t BUFNMB :8; /* Buffer Number */
);
@ -379,56 +358,20 @@ typedef volatile struct
);
pad(14);
/* PIPEn Transaction Counter Enable Registers and */
/* PIPEn Transaction Counter Registers */
word_union(PIPE1TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE1TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
word_union(PIPE2TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE2TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
word_union(PIPE3TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE3TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
word_union(PIPE4TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE4TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
word_union(PIPE5TRE,
uint16_t :6;
uint16_t TRENB :1; /* Transaction Counter Enable */
uint16_t TRCLR :1; /* Transaction Counter Clear */
uint16_t :8;
);
word_union(PIPE5TRN,
uint16_t TRCNT :16; /* Transaction Counter */
);
/* Transaction Counter Registers (PIPE1..PIPE5 only) */
sh7305_usb_pipetr PIPETR[5];
pad(0x1e);
uint16_t REG_C2;
pad(12);
word_union(DEVADD[11],
uint16_t :1;
uint16_t UPPHUB :4; /* Address of target's hub */
uint16_t HUBPORT:3; /* Hub port where target connects */
uint16_t USBSPD :2; /* Transfer speed / Target present */
uint16_t :6;
);
} GPACKED(4) sh7305_usb_t;

52
include/gint/mpu/wdt.h Normal file
View File

@ -0,0 +1,52 @@
//---
// gint:mpu:wdt - Watchdog Timer
//---
#ifndef GINT_MPU_WDT
#define GINT_MPU_WDT
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/attributes.h>
#include <gint/defs/types.h>
//---
// SH7705 WatchDog Timer. Refer to:
// "Renesas SH7705 Group Hardware Manual"
// Section 10: "WatchDog Timer (WDT)"
//---
/* sh7705_wdt_t - Watch Dog Timer */
typedef volatile struct
{
/* WDT registers are unique in access size; reads are performed with
8-bit accesses, but writes are performed with 16-bit accesses. */
union {
uint8_t READ;
uint16_t WRITE;
} WTCNT;
union {
byte_union(READ,
uint8_t TME :1; /* Timer Enable */
uint8_t WTIT :1; /* Watchdog/Interval Select */
uint8_t RSTS :1; /* Watchdog Reset Select */
uint8_t WOVF :1; /* Watchdog Overflow Flag */
uint8_t IOVF :1; /* Interval Overflow Flag */
uint8_t CKS :3; /* Clock Select */
);
uint16_t WRITE;
} WTCSR;
} sh7705_wdt_t;
#define SH7705_WDT (*((sh7705_wdt_t *)0xffffff84))
#ifdef __cplusplus
}
#endif
#endif /* GINT_MPU_WDT */

64
include/gint/serial.h Normal file
View File

@ -0,0 +1,64 @@
//---
// gint:serial - Serial communication
//---
// TODO: This is a template for a future implementation.
#ifndef GINT_SERIAL
#define GINT_SERIAL
#ifdef __cplusplus
extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/defs/call.h>
typedef enum {
SERIAL_PARITY_NONE,
SERIAL_PARITY_EVEN,
SERIAL_PARITY_ODD,
} GPACKEDENUM serial_parity_t;
typedef struct {
/* Baud rate (valid options are 300, 600, 1200, 2400, 4800, 9600,
19200, 38400, 57600 and 115200) */
int baudrate;
/* Parity */
serial_parity_t parity;
/* Data width (valid options are 7 and 8) */
uint8_t data_width;
/* Stop bits (valid options are 1 and 2) */
uint8_t stop_bits;
} serial_config_t;
// TODO: Error management...
int serial_open(serial_config_t const *config);
bool serial_is_open(void);
void serial_write_async(void const *data, size_t size, gint_call_t callback);
void serial_write_sync(void const *data, size_t size);
void serial_read_async(void const *data, size_t size, gint_call_t callback);
void serial_read_sync(void const *data, size_t size);
// Waits for communications to finish
void serial_wait(void);
// Calls serial_wait() automatically
void serial_close(void);
// TODO: Info on how much data is pending in each buffer?
// Ultimately we'll bind this to a file descriptor so we can't really know.
#ifdef __cplusplus
}
#endif
#endif /* GINT_SERIAL */

View File

@ -1,5 +1,27 @@
//---
// gint:usb-ff-bulk - A trivial bulk-based transfer class
// gint:usb-ff-bulk - A bulk-based transfer class using the fxlink protocol
//
// This interface (class code 0xff/0x77) implements a simple bidirectional
// communication channel running the fxlink protocol. It basically talks to
// fxlink's interactive mode on the host, and can broadly be used for 4 things:
//
// 1. Sending standard messages to fxlink (screenshots, logs...). Convenient
// functions are provided in this header to minimize the overhead. fxlink
// running in interactive mode (fxlink -t) will handle these messages in a
// suitable way automatically (eg. save screenshots as PNG).
//
// 2. Sending custom messages to fxlink (bench results, memory dumps, crash
// reports...). fxlink running in interactive mode will forward the data to
// appropriate user-provided external programs to handle the messages.
//
// 3. Applications based around on data streams coming from the host (screen
// projector, web browser...). This can be achieved by modifying fxlink or
// using it as a library.
//
// 4. Remote control from the fxlink interactive mode (fxlink -t). This is
// based on fxlink's 'command' and 'keyboard' protocols. This module will
// automatically handle commands and respond to them as long as the message
// handling function is called regularly.
//---
#ifndef GINT_USB_FF_BULK
@ -10,164 +32,37 @@ extern "C" {
#endif
#include <gint/usb.h>
struct usb_fxlink_header;
/* The bulk transfer interface with class code 0xff provides a very simple
communication channel between the calculator and a host. There is
(currently) a single IN pipe that sends data from the calculator to the
host, at high-speed (USB 2.0).
The class code of this interface is 0xff, which means that the protocol is
custom and requires a custom program on the host to receive the data (unlike
for instance LINK on a Graph 90+E which behaves as a USB stick and can be
used with the file browser). fxlink can be used to the effect. */
/* This bulk transfer interface with class code 0xff/0x77 implements a simple
bidirectional communication channel between the calculator and a host,
running at high-speed USB 2.0 and using the fxlink protocol. You can use it
to communicate with fxlink's interactive mode on the host. */
extern usb_interface_t const usb_ff_bulk;
//---
// Direct bulk access
//
// The following functions can be used to access the bulk pipes directly. They
// provide the pipe numbers, which can be passed for instance to usb_write
// functions.
// Sending standard messages
//---
/* usb_ff_bulk_output(): Pipe for calculator -> host communication */
int usb_ff_bulk_output(void);
//---
// fxlink protocol
//
// fxlink is a bulk-based communication tool in the fxSDK that can be used to
// conveniently transfer or manipulate information during the execution of an
// add-in. For instance, screenshots can be saved, files can be transferred,
// and text can be logged.
//
// Each communication with fxlink is a message that consists of:
// * A first write with a message header;
// * One or more writes with message data (of a size announced in the header);
// * A commit on the USB pipe (to ensure that everything is sent).
//
// There are two ways to use the fxlink protocol in this header; you can either
// use one of the convenience functions like usb_fxlink_screenshot() that do
// all the steps for you, or you can perform the writes and commits yourself to
// send custom messages.
//---
/* usb_fxlink_header_t: Message header for fxlink
fxlink supports a minimalist protocol to receive data sent from the
calculator and automatically process it (such as save it to file, convert
to an image, etc). It is designed as a convenience feature, and it can be
extended with custom types rather easily.
A message is categorized with an (application, type) pair; both are UTF-8
strings of up to 16 characters. The application name "fxlink" is reserved
for built-in types supported by fxlink; any other custom application name
can be set (in which case fxlink will call a user-provided program to handle
the message).
The size of the data to be transferred must be specified in order of the
transfer to proceed correctly, as it cannot reliably be guessed on the other
side (and guessing wrong means all further messages will be in trouble).
As with the rest of the USB protocol, all the multi-byte integer fields in
this header are encoded as *little-endian*. */
typedef struct
{
/* Protocol version = 0x00000100 */
uint32_t version;
/* Size of the data to transfer (excluding this header) */
uint32_t size;
/* Size of individual transfers (related to the size of the FIFO) */
uint32_t transfer_size;
/* Application name, UTF-8 (might not be zero-terminated) */
char application[16];
/* Message type */
char type[16];
} usb_fxlink_header_t;
/* usb_fxlink_fill_header(): Fill an fxlink message header
This function will fill the specified fxlink header. You need to specify the
exact amount of data that the fxlink message will contain; if you cannot
determine it, consider splitting the message into several messages each with
their own header, and use the application-specific script on the host to
recombine them.
Returns false if the parameters are invalid or don't fit, in this case the
contents of the header are unchanged. */
bool usb_fxlink_fill_header(usb_fxlink_header_t *header,
char const *application, char const *type, uint32_t data_size);
//---
// Short functions for fxlink built-in types
//---
/* Subheader for the fxlink built-in "image" type */
typedef struct
{
uint32_t width;
uint32_t height;
/* Pixel format, see below */
int pixel_format;
} usb_fxlink_image_t;
/* Pixel formats */
typedef enum
{
/* Image is an array of *big-endian* uint16_t with RGB565 format */
USB_FXLINK_IMAGE_RGB565 = 0,
/* Image is an array of bits in mono format */
USB_FXLINK_IMAGE_MONO,
/* Image is two consecutive mono arrays, one for light, one for dark */
USB_FXLINK_IMAGE_GRAY,
} usb_fxlink_image_format_t;
/* usb_fxlink_text(): Send raw text
Send a string; fxlink will display it in the terminal. This can be used to
back stdout/stderr. Sending lots of small messages can be slow; if that's a
problem, fill in message manually. If size is 0, uses strlen(text). */
void usb_fxlink_text(char const *text, int size);
/* usb_fxlink_screenshot(): Take a screenshot
This function takes a screenshot. If (onscreen = false), it sends the
contents of the VRAM. This mode is best used just before dupdate(), since
the VRAM contents are exactly as they will appear on screen a moment later.
However, this is somewhat unintuitive for interactive GUI as the user has to
press a button after the option is present on-screen, therefore the contents
of the *next* frame are captured.
This function sends a copy of the VRAM to fxlink. This is best used just
before dupdate() since this ensures the image sent by USB is identical to
the one displayed on screen.
If (onscreen = true), this function tries to send the pixels that are
currently visible on-screen, with the following heuristic:
* If there is only one VRAM, the VRAM contents are used in the hope they
haven't changed since the last frame was presented with dupdate().
* If there are two VRAMs (on fx-CG 50 or using the gray engine) the contents
of the VRAM not currently being draw to are sent.
This function does not read pixels directly from the display, as this is
usually slow and currently not even implemented. */
If `onscreen` is set and there are two VRAMs (on fx-CG or when using the
gray engine on fx-9860G), sends a copy of the other VRAM. This is a bit more
intuitive when taking a screenshot of the last shown image as a result of a
key press. Note that this function never reads pixels directly from the
display (it's usually slow and currently not even implemented). */
void usb_fxlink_screenshot(bool onscreen);
#ifdef FX9860G
/* usb_fxlink_screenshot_gray(): Take a gray screenshot on fx-9860G
This function is similar to usb_fxlink_screenshot(), but it takes a gray
screenshot. It depends on the gray engine so if you use your add-in will
automatically have the gray engine, that's why it's separate. */
void usb_fxlink_screenshot_gray(bool onscreen);
#endif
/* usb_fxlink_text(): Send raw text
This function sends a string with the "text" type, which fxlink prints on
the terminal. It will send a full fxlink message (with a commit), which is
inefficient if there is a lot of text to send. For better speed, send the
message manually by filling the header and doing the writes and commit
manually.
This function sends the text by blocks of 4 bytes or 2 bytes when alignment
and size allow, and 1 byte otherwise. If size is 0, strlen(text) is used. */
void usb_fxlink_text(char const *text, int size);
/* usb_fxlink_videocapture(): Send a frame for a video recording
This function is essentially the same as usb_fxlink_screenshot(). It sends a
@ -179,12 +74,131 @@ void usb_fxlink_text(char const *text, int size);
automatically send new frames to fxlink. */
void usb_fxlink_videocapture(bool onscreen);
#ifdef FX9860G
/* usb_fxlink_videocapture_gray(): Send a gray frame for a video recording
Like usb_fxlink_videocapture(), but uses VRAM data from the gray engine. */
#if defined(FX9860G) || defined(FX9860G_AS_CG)
/* Similar to usb_fxlink_screenshot(), but takes a gray screenshot if the gray
engine is currently running. */
void usb_fxlink_screenshot_gray(bool onscreen);
/* Like usb_fxlink_videocapture(), but uses VRAM data from the gray engine. */
void usb_fxlink_videocapture_gray(bool onscreen);
#endif
//---
// Receiving messages
//---
/* usb_fxlink_handle_messages(): Process and return incoming messages
This function processes incoming messages. It handles some types of messages
internally (eg. fxlink commands) and returns the others to the caller by
loading the provided header structure and returning true.
When this function returns true, the caller should read the message contents
on the interface's input pipe. Usually every read will return just a portion
of the message (eg. 2048 bytes) so the contents should be read with a loop.
As long as this function returns true it should be called againt to process
any other messages. When there is no more messages to process, this function
returns false. It is important to call this function regularly from the main
thread when quick responses are expected. */
bool usb_fxlink_handle_messages(struct usb_fxlink_header *header);
/* usb_fxlink_drop_transaction(): Drop incoming data until end of transaction
When a message arrives on the USB port incoming data *must* be processed in
order for the pipe to accept any further data in the future. This function
can be used in error situations to drop data until the end of the
transaction (which is usually the end of the message, at least with the
fxSDK's implementation of fxlink). */
void usb_fxlink_drop_transaction(void);
/* usb_fxlink_set_notifier(): Set up a notification for incoming messages
Registers the provided function to be called when data arrives on the fxlink
input pipe. This signals that usb_fxlink_handle_messages() should be called
later from the main thread. Pass NULL to disable the notification. */
void usb_fxlink_set_notifier(void (*notifier_function)(void));
//---
// Direct bulk access
//
// The following functions can be used to access the bulk pipes directly. They
// provide the pipe numbers, which can be passed for instance to usb_write
// functions.
//---
/* usb_ff_bulk_output(): Pipe number for calculator -> host communication */
int usb_ff_bulk_output(void);
/* usb_ff_bulk_input(): Pipe number for host -> calculator communication */
int usb_ff_bulk_input(void);
//---
// Construction and analysis of fxlink protocol messages
//
// fxlink messages consist of a simple header followed by message contents
// (sometimes with a subheader). In addition to the functions above, it is
// possible to craft custom fxlink messages and send them manually.
//
// To send a message manually, simple write an fxlink header to the output
// pipe, followed by the contents. The message can be built from any number of
// writes to the pipe. After the last write, commit the pipe.
//---
/* usb_fxlink_header_t: Message header for fxlink
Messages are categorized with an (application, type) pair; both are UTF-8
strings of up to 16 bytes (NUL not required). The application name "fxlink"
is reserved for built-in types of messages. Other names can be used freely,
and fxlink's interactive mode on the host side will forward messages to
external programs based on the application name.
The size of the data to be transferred must be specified upfront, but this
restriction might be lifted in the future. As with the rest of the USB
protocol, all the integer are encoded as *little-endian*. */
typedef struct usb_fxlink_header
{
/* Protocol version = 0x00000100 */
uint32_t version;
/* Size of the data to transfer (excluding this header) */
uint32_t size;
/* Size of individual transfers (related to the size of the FIFO) */
uint32_t transfer_size;
/* Application name and message type, UTF-8 (need not be zero-terminated) */
char application[16];
char type[16];
} usb_fxlink_header_t;
/* usb_fxlink_fill_header(): Fill an fxlink message header
Fills an fxlink header's fields with the provided. You need to specify the
exact amount of data that the fxlink message will contain. Returns false if
parameters are invalid or don't fit the fields. */
bool usb_fxlink_fill_header(usb_fxlink_header_t *header,
char const *application, char const *type, uint32_t data_size);
/* Subheader for the fxlink built-in "image" type */
typedef struct
{
/* Image size; storage is row-major */
uint32_t width;
uint32_t height;
/* Pixel format, see below */
int pixel_format;
} usb_fxlink_image_t;
/* Pixel formats */
enum {
/* Image is an array of *big-endian* uint16_t with RGB565 format */
USB_FXLINK_IMAGE_RGB565 = 0,
/* Image is an array of bits in mono format */
USB_FXLINK_IMAGE_MONO,
/* Image is two consecutive mono arrays, one for light, one for dark */
USB_FXLINK_IMAGE_GRAY,
};
#ifdef __cplusplus
}
#endif

View File

@ -10,9 +10,11 @@ extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/std/endian.h>
#include <gint/defs/timeout.h>
#include <gint/gint.h>
#include <gint/config.h>
#include <stdarg.h>
#include <endian.h>
/* See "Interfaces to communicate with USB transfers" below for details */
typedef struct usb_interface usb_interface_t;
@ -27,26 +29,35 @@ enum {
/* There are no interfaces */
USB_OPEN_NO_INTERFACE = 1,
/* There are more interfaces than supported (16) */
USB_OPEN_TOO_MANY_INTERFACES,
USB_OPEN_TOO_MANY_INTERFACES = -2,
/* There are not enough endpoint numbers for every interface, or there
are not enough pipes to set them up */
USB_OPEN_TOO_MANY_ENDPOINTS,
USB_OPEN_TOO_MANY_ENDPOINTS = -3,
/* There is not enough FIFO memory to use the requested buffer sizes */
USB_OPEN_NOT_ENOUGH_MEMORY,
USB_OPEN_NOT_ENOUGH_MEMORY = -4,
/* Information is missing, such as buffer size for some endpoints */
USB_OPEN_MISSING_DATA,
USB_OPEN_MISSING_DATA = -5,
/* Invalid parameters: bad endpoint numbers, bad buffer sizes... */
USB_OPEN_INVALID_PARAMS,
USB_OPEN_INVALID_PARAMS = -6,
/* USB interfaces are already opened */
USB_OPEN_ALREADY_OPEN = -7,
/* General timeout for a sync_timeout call */
USB_TIMEOUT = -8,
/* This pipe is busy with another call */
USB_BUSY = -9,
/* This pipe is busy (returned by usb_write_async()) */
USB_WRITE_BUSY,
/* Both FIFO controllers are busy, none is available to transfer */
USB_WRITE_NOFIFO,
/* This pipe is busy (returned by usb_commit_async()) */
USB_COMMIT_BUSY,
USB_WRITE_NOFIFO = -10,
/* This pipe has no ongoing transfer to commit */
USB_COMMIT_INACTIVE,
USB_COMMIT_INACTIVE = -11,
/* This pipe is currently not receiving any data */
USB_READ_IDLE = -12,
/* No FIFO controller is available */
USB_READ_NOFIFO = -13,
/* (Internal codes) */
USB_ERROR_ZERO_LENGTH = -100,
};
/* usb_open(): Open the USB link
@ -129,8 +140,12 @@ struct usb_interface {
/* Answer class-specific SETUP requests */
/* TODO */
/* Receive data from an endpoint */
/* TODO */
/* Notification that an endpoint has data to be read. This function is
called frequently when data is being transmitted; the particular
timings depend on low-level details. The notification should be
passed down to cause the main thread to read later. Do not read in
the notification function! */
void (*notify_read)(int endpoint);
};
/* usb_interface_endpoint_t: Parameters for an interface endpoint
@ -169,23 +184,22 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
/* usb_write_sync(): Synchronously write to a USB pipe
This functions writes (size) bytes of (data) into the specified pipe, by
units of (unit_size) bytes. The unit size must be 1, 2 or 4, and both (data)
and (size) must be multiples of the unit size. In general, you should try to
use the largest possible unit size, as it will be much faster. In a sequence
of writes that concludes with a commit, all the writes must use the same
unit size.
This functions writes (size) bytes of (data) into the specified pipe. If the
data fits into the pipe, this function returns right away, and the data is
*not* transmitted. Otherwise, data is written until the pipe is full, at
which point it is automatically transmitted. After the transfer, this
function resumes writing, returning only once everything is written. Even
then the last bytes will still not have been transmitted, to allow for other
writes to follow. After the last write in a sequence, use usb_commit_sync()
or usb_commit_async() to transmit the last bytes.
If the data fits into the pipe, this function returns right away, and the
data is *not* transmitted. Otherwise, data is written until the pipe is
full, at which point it is automatically transmitted. After the transfer,
this function resumes writing, returning only once everything is written.
Even then the last bytes will still not have been transmitted, to allow for
other writes to follow. After the last write in a sequence, use
usb_commit_sync() or usb_commit_async() to transmit the last bytes.
If (use_dma=true), the write is performed with the DMA instead of the CPU,
which is generally faster.
If (use_dma=true), the write is performed with the DMA instead of the CPU.
This requires at least 4-byte alignment on:
1. The input data;
2. The size of this write;
3. The amount of data previously written to the pipe not yet committed.
This is because using the DMA does not allow any insertion of CPU logic to
handle unaligned stuff.
This function will use a FIFO controller to access the pipe. The FIFO
controller will be reserved for further writes until the contents of the
@ -198,13 +212,15 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint);
waits for the ressources to become available then proceeds normally.
@pipe Pipe to write into
@data Source data (unit_size-aligned)
@size Size of source (multiple of unit_size)
@unit_size FIFO access size (must be 1, 2, or 4)
@data Source data
@size Size of source
@dma Whether to use the DMA to perform the write
-> Returns an error code (0 on success). */
int usb_write_sync(int pipe, void const *data, int size, int unit_size,
bool use_dma);
int usb_write_sync(int pipe, void const *data, int size, bool use_dma);
/* usb_write_sync_timeout(): Synchronously write, with a timeout */
int usb_write_sync_timeout(int pipe, void const *data, int size,
bool use_dma, timeout_t const *timeout);
/* usb_write_async(): Asynchronously write to a USB pipe
@ -226,14 +242,13 @@ int usb_write_sync(int pipe, void const *data, int size, int unit_size,
is idle and USB_WRITE_BUSY otherwise.
@pipe Pipe to write into
@data Source data (unit_size-aligned)
@size Size of source (multiple of unit_size)
@unit_size FIFO access size (must be 1, 2, or 4)
@data Source data
@size Size of source
@dma Whether to use the DMA to perform the write
@callback Optional callback to invoke when the write completes
-> Returns an error code (0 on success). */
int usb_write_async(int pipe, void const *data, int size, int unit_size,
bool use_dma, gint_call_t callback);
int usb_write_async(int pipe, void const *data, int size, bool use_dma,
gint_call_t callback);
/* usb_commit_sync(): Synchronously commit a write
@ -241,6 +256,9 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size,
transfers whatever data is left, and returns when the transfer completes. */
void usb_commit_sync(int pipe);
/* usb_commit_sync_timeout(): Synchronously commit a write, with timeout */
int usb_commit_sync_timeout(int pipe, timeout_t const *timeout);
/* usb_commit_async(): Asynchronously commit a write
This function commits the specified pipe, causing the pipe to transfer
@ -255,23 +273,139 @@ void usb_commit_sync(int pipe);
of the remaining data completes. */
int usb_commit_async(int pipe, gint_call_t callback);
/* usb_read_sync(): Synchronously read from a USB pipe
This function waits for data to become available on the specified `pipe`,
and then reads up to `size` bytes into `data`. It is "synchronized" with USB
transactions on the pipe in the following ways:
1. If there is no active transaction, it waits for one to arrive.
2. If the active transaction has no data left to read, it is skipped.
3. If the transaction's data is completely read, the transaction is closed
even if `data` is full. (See usb_read_async() for the alternative.)
Basically usb_read_sync() returns the next `size` bytes of data that the
calculator receives on the pipe, with a single exception: it doesn't read
across transactions, so if the active transaction has 100 bytes left and you
request 200, you will only get 100. usb_read_sync() only ever returns 0
bytes if there is an empty transaction, which is rare.
Because this is a blocking function, a call to usb_read_sync() will *FREEZE*
if there is no data to read on the pipe and the host doesn't send any. In
addition, it's not possible to detect how much data is left to read with
usb_read_sync()'s interface. If you want to avoid or debug a freeze, use
use usb_read_async() or set a timeout with usb_read_sync_timeout(). If you
want to read a transaction until the end without knowing its size in
advance, use usb_read_async().
If `use_dma=true`, uses the DMA for transfers. This requires 4-byte
alignment on the buffer, size, and number of bytes previously read in the
current transaction.
Returns the number of bytes read or a negative error code. */
int usb_read_sync(int pipe, void *data, int size, bool use_dma);
/* usb_read_sync_timeout(): Synchronous read with a timeout */
int usb_read_sync_timeout(int pipe, void *data, int size, bool use_dma,
timeout_t const *timeout);
/* Flags for usb_read_async(), see below. */
enum {
/* Use the DMA for data transfer (requires full 4-byte alignment). */
USB_READ_USE_DMA = 0x01,
/* Ignore reads of 0 bytes from exhausted transactions. */
USB_READ_IGNORE_ZEROS = 0x02,
/* Close transactions when exhausted even by non-partial reads. */
USB_READ_AUTOCLOSE = 0x04,
/* Wait for the read to finish before returning. */
USB_READ_WAIT = 0x08,
/* Block for a new transaction if none is currenty active. */
USB_READ_BLOCK = 0x10,
};
/* usb_read_async(): Asynchronously read from a USB pipe
This function is similar to usb_read_sync() but it is asynchronous, ie. it
returns after starting the read and then runs in the background. Without
options, usb_read_async() is guaranteed to return quickly without waiting
for communications. The read itself also never blocks unless there is a
disagreement with the host on the size of transactions.
usb_read_async() is also slightly lower-level than usb_read_sync(). In
particular, it only closes transactions when the data is fully read *and*
the user buffer is not full. If the user requests exactly the number of
bytes left in the transaction, the entire contents will be read but the
transaction will be left in an "active with 0 bytes left" state. Only on the
next read will the transaction be closed.
This behavior is designed so that a read closes a transaction if and only if
it returns fewer bytes than requested, which is useful for detecting the end
of transfers when their size isn't known in advance.
This function returns a preliminary error code. If it is zero, then the
callback will be invoked later with *rc set to the final return value of the
read, which is itself either an error (< 0) or the number of bytes read.
Due to its low-level nature, raw usb_read_async() is a bit impractical to
use and almost always requires repeated calls and callback waits. The
following flags are provided to make it more convenient by incorporating
features of usb_read_sync():
* USB_READ_IGNORE_ZEROS causes usb_read_async() to ignore reads of 0 bytes
from transactions that were exhausted but not closed.
* USB_READ_AUTOCLOSE causes usb_read_async() to always close transactions
when exhausted even if the read is not partial. This is useful when the
size of the transaction is in fact known in advance.
* USB_READ_WAIT causes usb_read_async() to wait for the end of the transfer
before returning. Unless there is a driver bug or USB_READ_BLOCK is also
set, this cannot cause the function to freeze since the read will always
complete in finite time (returning USB_READ_IDLE if no transaction is
active). When USB_READ_WAIT it set, `callback` and `rc` are ignored; the
callback is not invoked, and the status of the entire read is returned.
* USB_READ_BLOCK causes usb_read_async() to wait for a transaction if none
is active. This can stall the transfer indefinitely if the host doesn't
transmit any data, and freeze the call entirely if USB_READ_WAIT is set.
This option really makes usb_read_async() a synchronous function.
* A timeout is supported (pass NULL to ignore).
With all options enabled, usb_read_async() becomes functionally identical to
usb_read_sync_timeout(). */
int usb_read_async(int pipe, void *data, int size, int flags, int *rc,
timeout_t const *timeout, gint_call_t callback);
/* usb_read_cancel(): Cancel an asynchronous read
Once started, an async read will run in the background and keep writing to
the provided buffer. This function cancels the operation so that no further
writes to the buffer are made and the associated memory can be safely
deallocated. */
void usb_read_cancel(int pipe);
//---
// USB debugging log
// USB debugging functions
//---
/* usb_set_log(): Set the logging function for the USB driver
#ifdef GINT_USB_DEBUG
#define USB_LOG(...) usb_log(__VA_ARGS__)
#define USB_TRACE(...) usb_trace(__VA_ARGS__)
The USB driver can produce logs, which are mostly useful to troubleshoot
problems and add new protocols. The logging is disabled by default but can
be enabled by specifying this function.
It is up to you whether to store that in a buffer, rotate logs, send them to
storage memory or a console on the PC. */
/* usb_set_log(): Set the logging function for the USB driver */
void usb_set_log(void (*logger)(char const *format, va_list args));
/* usb_log(): Send a message to the USB log */
void usb_log(char const *format, ...);
/* usb_set_trace(): Set the tracing function for the USB driver
The function is called atomically, thus cannot be interrupted, therefore it
is safe to call usb_trace() in interrupt handlers. */
void usb_set_trace(void (*tracer)(char const *message));
/* usb_trace(): Trace the current state of the driver */
void usb_trace(char const *message);
#else
#define USB_LOG(...) do {} while(0)
#define USB_TRACE(...) do {} while(0)
#endif
//---
// Standard descriptors
//---

View File

@ -27,7 +27,6 @@ const clock_frequency_t *clock_freq(void)
//---
#if defined(FX9860G) || (!defined(FX9860G) && !defined(FXCG50))
#define CPG SH7705_CPG
void sh7705_probe(void)
{
@ -41,35 +40,23 @@ void sh7705_probe(void)
int ckio = xtal * pll2;
/* This signal is multiplied by the PLL1 circuit */
int pll1 = CPG.FRQCR.STC + 1;
int pll1 = SH7705_CPG.FRQCR.STC + 1;
/* Iphi and Pphi have dividers (Bphi is always equal to CKIO) */
int idiv = CPG.FRQCR.IFC;
int pdiv = CPG.FRQCR.PFC;
/* Fill in the setting structure */
/* Fill in the setting structure. Iϕ and Pϕ have dividers, while Bϕ is
always equal to CKIO. */
freq.PLL1 = pll1;
freq.PLL2 = pll2;
freq.Bphi_div = 1;
freq.Iphi_div = idiv + 1;
freq.Pphi_div = pdiv + 1;
/* Deduce the frequency of the main clocks. This value is ckio/3 */
int ckio_3 = 9830400;
/* Exchange the setting values 2 and 3 (corresponding to /3 and /4)
This means that /1, /2, /4 are now 0, 1, 2, which is perfect for a
quick bit shift */
idiv = idiv ^ (idiv >> 1);
pdiv = pdiv ^ (pdiv >> 1);
freq.Iphi_div = SH7705_CPG.FRQCR.IFC + 1;
freq.Pphi_div = SH7705_CPG.FRQCR.PFC + 1;
/* Deduce the frequency of the main clocks */
freq.CKIO_f = ckio;
freq.Bphi_f = ckio;
freq.Iphi_f = (idiv == 3) ? ckio_3 : ckio >> idiv;
freq.Pphi_f = (pdiv == 3) ? ckio_3 : ckio >> pdiv;
freq.Iphi_f = (ckio * pll1) / freq.Iphi_div;
freq.Pphi_f = (ckio * pll1) / freq.Pphi_div;
}
#undef CPG
#endif /* FX9860G and platform-agnostic */
//---
@ -134,9 +121,7 @@ static void configure(void)
{
/* Disable spread spectrum in SSGSCR */
if(isSH4())
{
SH7305_CPG.SSCGCR.SSEN = 0;
}
cpg_compute_freq();
}
@ -147,18 +132,16 @@ static void configure(void)
static void hsave(cpg_state_t *s)
{
if(isSH4()) {
if(isSH4())
s->SSCGCR = SH7305_CPG.SSCGCR.lword;
cpg_get_overclock_setting(&s->speed);
}
cpg_get_overclock_setting(&s->speed);
}
static void hrestore(cpg_state_t const *s)
{
if(isSH4()) {
if(isSH4())
SH7305_CPG.SSCGCR.lword = s->SSCGCR;
cpg_set_overclock_setting(&s->speed);
}
cpg_set_overclock_setting(&s->speed);
}
gint_driver_t drv_cpg = {

View File

@ -14,137 +14,309 @@
#include <gint/hardware.h>
#include <gint/mpu/cpg.h>
#include <gint/mpu/bsc.h>
#define CPG SH7305_CPG
#define BSC SH7305_BSC
#include <gint/mpu/wdt.h>
//---
// Low-level clock speed access
//---
#define SDMR3_CL2 ((volatile uint8_t *)0xFEC15040)
#define SDMR3_CL3 ((volatile uint8_t *)0xFEC15060)
void cpg_get_overclock_setting(struct cpg_overclock_setting *s)
{
if(!isSH4())
return;
s->FLLFRQ = CPG.FLLFRQ.lword;
s->FRQCR = CPG.FRQCR.lword;
s->CS0BCR = BSC.CS0BCR.lword;
s->CS0WCR = BSC.CS0WCR.lword;
s->CS2BCR = BSC.CS2BCR.lword;
s->CS2WCR = BSC.CS2WCR.lword;
s->CS3BCR = BSC.CS3BCR.lword;
s->CS3WCR = BSC.CS3WCR.lword;
s->CS5aBCR = BSC.CS5ABCR.lword;
s->CS5aWCR = BSC.CS5AWCR.lword;
}
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s)
{
if(!isSH4())
return;
BSC.CS0WCR.WR = 11; /* 18 cycles */
CPG.FLLFRQ.lword = s->FLLFRQ;
CPG.FRQCR.lword = s->FRQCR;
CPG.FRQCR.KICK = 1;
while(CPG.LSTATS != 0) {}
BSC.CS0BCR.lword = s->CS0BCR;
BSC.CS0WCR.lword = s->CS0WCR;
BSC.CS2BCR.lword = s->CS2BCR;
BSC.CS2WCR.lword = s->CS2WCR;
BSC.CS3BCR.lword = s->CS3BCR;
BSC.CS3WCR.lword = s->CS3WCR;
if(BSC.CS3WCR.A3CL == 1)
*SDMR3_CL2 = 0;
else
*SDMR3_CL3 = 0;
BSC.CS5ABCR.lword = s->CS5aBCR;
BSC.CS5AWCR.lword = s->CS5aWCR;
}
#define SH7305_SDMR3_CL2 ((volatile uint8_t *)0xFEC15040)
#define SH7305_SDMR3_CL3 ((volatile uint8_t *)0xFEC15060)
//---
// Predefined clock speeds
//---
#ifdef FXCG50
/* SH7305 CPG */
#define SH4_PLL_32x 0b011111
#define SH4_PLL_26x 0b011001
#define SH4_PLL_16x 0b001111
#define SH4_DIV_2 0
#define SH4_DIV_4 1
#define SH4_DIV_8 2
#define SH4_DIV_16 3
#define SH4_DIV_32 4
#define PLL_32x 0b011111
#define PLL_26x 0b011001
#define PLL_16x 0b001111
#define DIV_2 0
#define DIV_4 1
#define DIV_8 2
#define DIV_16 3
#define DIV_32 4
/* SH7705-like CPG */
#define SH3_PLL_1x 0
#define SH3_PLL_2x 1
#define SH3_PLL_3x 2
#define SH3_PLL_4x 3
#define SH3_DIV_1 0
#define SH3_DIV_2 1
#define SH3_DIV_3 2
#define SH3_DIV_4 3
static struct cpg_overclock_setting settings_cg50[5] = {
void cpg_get_overclock_setting(struct cpg_overclock_setting *s)
{
if(isSH3()) {
s->FLLFRQ = -1;
s->FRQCR = SH7705_CPG.FRQCR.word;
s->CS0BCR = SH7705_BSC.CS0BCR.lword;
s->CS0WCR = SH7705_BSC.CS0WCR.lword;
s->CS2BCR = SH7705_BSC.CS2BCR.lword;
s->CS2WCR = SH7705_BSC.CS2WCR.lword;
s->CS3BCR = SH7705_BSC.CS3BCR.lword;
s->CS3WCR = SH7705_BSC.CS3WCR.lword;
s->CS5aBCR = SH7705_BSC.CS5ABCR.lword;
s->CS5aWCR = SH7705_BSC.CS5AWCR.lword;
}
else {
s->FLLFRQ = SH7305_CPG.FLLFRQ.lword;
s->FRQCR = SH7305_CPG.FRQCR.lword;
s->CS0BCR = SH7305_BSC.CS0BCR.lword;
s->CS0WCR = SH7305_BSC.CS0WCR.lword;
s->CS2BCR = SH7305_BSC.CS2BCR.lword;
s->CS2WCR = SH7305_BSC.CS2WCR.lword;
s->CS3BCR = SH7305_BSC.CS3BCR.lword;
s->CS3WCR = SH7305_BSC.CS3WCR.lword;
s->CS5aBCR = SH7305_BSC.CS5ABCR.lword;
s->CS5aWCR = SH7305_BSC.CS5AWCR.lword;
}
}
static void cpg_low_level_set_setting(struct cpg_overclock_setting const *s)
{
if(isSH3()) {
SH7705_WDT.WTCNT.WRITE = 0;
SH7705_WDT.WTCSR.WRITE = 0x65;
SH7705_CPG.FRQCR.word = s->FRQCR | 0x1000;
SH7705_BSC.CS0BCR.lword = s->CS0BCR;
SH7705_BSC.CS0WCR.lword = s->CS0WCR;
SH7705_BSC.CS2BCR.lword = s->CS2BCR;
SH7705_BSC.CS2WCR.lword = s->CS2WCR;
SH7705_BSC.CS3BCR.lword = s->CS3BCR;
SH7705_BSC.CS3WCR.lword = s->CS3WCR;
SH7705_BSC.CS5ABCR.lword = s->CS5aBCR;
SH7705_BSC.CS5AWCR.lword = s->CS5aWCR;
}
else {
SH7305_BSC.CS0WCR.WR = 11; /* 18 cycles */
SH7305_CPG.FLLFRQ.lword = s->FLLFRQ;
SH7305_CPG.FRQCR.lword = s->FRQCR;
SH7305_CPG.FRQCR.KICK = 1;
while(SH7305_CPG.LSTATS != 0) {}
SH7305_BSC.CS0BCR.lword = s->CS0BCR;
SH7305_BSC.CS0WCR.lword = s->CS0WCR;
SH7305_BSC.CS2BCR.lword = s->CS2BCR;
SH7305_BSC.CS2WCR.lword = s->CS2WCR;
SH7305_BSC.CS3BCR.lword = s->CS3BCR;
SH7305_BSC.CS3WCR.lword = s->CS3WCR;
if(SH7305_BSC.CS3WCR.A3CL == 1)
*SH7305_SDMR3_CL2 = 0;
else
*SH7305_SDMR3_CL3 = 0;
SH7305_BSC.CS5ABCR.lword = s->CS5aBCR;
SH7305_BSC.CS5AWCR.lword = s->CS5aWCR;
}
}
void cpg_set_overclock_setting(struct cpg_overclock_setting const *s)
{
uint32_t old_Pphi = clock_freq()->Pphi_f;
/* Wait for asynchronous tasks to complete */
gint_world_sync();
/* Disable interrupts during the change */
cpu_atomic_start();
/* Load the clock settings */
cpg_low_level_set_setting(s);
/* Determine the change in frequency for Pϕ and recompute CPG data */
cpg_compute_freq();
uint32_t new_Pphi = clock_freq()->Pphi_f;
/* Update timers' TCNT and TCOR to match the new clock speed */
void timer_rescale(uint32_t old_Pphi, uint32_t new_Pphi);
timer_rescale(old_Pphi, new_Pphi);
cpu_atomic_end();
}
#ifdef FX9860G
static struct cpg_overclock_setting const settings_fx9860g_sh3[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = 0x0F011112,
.CS0BCR = 0x36DA0400,
.CS2BCR = 0x36DA3400,
.CS3BCR = 0x36DB4400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x000003C0,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
{ .FRQCR = 0x1001,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_16x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000340,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
{ .FRQCR = (SH3_PLL_2x<<8)+(SH3_DIV_1<<4)+SH3_DIV_2,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_26x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000240,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
{ .FRQCR = (SH3_PLL_3x<<8)+(SH3_DIV_1<<4)+SH3_DIV_3,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_32x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_8<<8)+DIV_16,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x000002C0,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
{ .FRQCR = (SH3_PLL_4x<<8)+(SH3_DIV_1<<4)+SH3_DIV_4,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000440,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
{ .FRQCR = (SH3_PLL_4x<<8)+(SH3_DIV_1<<4)+SH3_DIV_4,
.CS0BCR = 0x02480400,
.CS2BCR = 0x02483400,
.CS3BCR = 0x36DB0600,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000000C0,
.CS2WCR = 0x000100C0,
.CS3WCR = 0x00000500,
.CS5aWCR = 0x00000D41 },
};
static struct cpg_overclock_setting settings_cg20[5] = {
static struct cpg_overclock_setting const settings_fx9860g_sh4[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004384,
.FRQCR = 0x0F202203,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000005C0,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x24920200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00020140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
.CS0BCR = 0x14900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000003C0,
.CS2WCR = 0x000302C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
};
static struct cpg_overclock_setting const settings_g35pe2[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004384,
.FRQCR = 0x0F112213,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x000001C0,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00031340 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x24920200,
.CS0WCR = 0x00000140,
.CS2WCR = 0x00000140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00000D41 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00020140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00031B40 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004384,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x14900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x224A0200,
.CS0WCR = 0x000001C0,
.CS2WCR = 0x00020140,
.CS3WCR = 0x000024D0,
.CS5aWCR = 0x00031B40 },
};
#endif /* FX9860G */
#ifdef FXCG50
static struct cpg_overclock_setting const settings_prizm[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = 0x0F102203,
@ -158,7 +330,7 @@ static struct cpg_overclock_setting settings_cg20[5] = {
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_32x<<24)+(DIV_8<<20)+(DIV_16<<12)+(DIV_16<<8)+DIV_32,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_8<<20)+(SH4_DIV_16<<12)+(SH4_DIV_16<<8)+SH4_DIV_32,
.CS0BCR = 0x04900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
@ -169,7 +341,7 @@ static struct cpg_overclock_setting settings_cg20[5] = {
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_32x<<24)+(DIV_4<<20)+(DIV_8<<12)+(DIV_8<<8)+DIV_32,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_32,
.CS0BCR = 0x24900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
@ -180,7 +352,7 @@ static struct cpg_overclock_setting settings_cg20[5] = {
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_32x<<24)+(DIV_4<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_32,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_32,
.CS0BCR = 0x44900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
@ -191,7 +363,7 @@ static struct cpg_overclock_setting settings_cg20[5] = {
.CS5aWCR = 0x00010240 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (PLL_26x<<24)+(DIV_2<<20)+(DIV_4<<12)+(DIV_4<<8)+DIV_16,
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_16,
.CS0BCR = 0x34900400,
.CS2BCR = 0x04903400,
.CS3BCR = 0x24924400,
@ -202,35 +374,125 @@ static struct cpg_overclock_setting settings_cg20[5] = {
.CS5aWCR = 0x00010240 },
};
static struct cpg_overclock_setting *get_settings(void)
static struct cpg_overclock_setting const settings_fxcg50[5] = {
/* CLOCK_SPEED_F1 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = 0x0F011112,
.CS0BCR = 0x36DA0400,
.CS2BCR = 0x36DA3400,
.CS3BCR = 0x36DB4400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x000003C0,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F2 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_16x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000340,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F3 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_4<<20)+(SH4_DIV_8<<12)+(SH4_DIV_8<<8)+SH4_DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000240,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F4 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_32x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_8<<8)+SH4_DIV_16,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x000002C0,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
/* CLOCK_SPEED_F5 */
{ .FLLFRQ = 0x00004000 + 900,
.FRQCR = (SH4_PLL_26x<<24)+(SH4_DIV_2<<20)+(SH4_DIV_4<<12)+(SH4_DIV_4<<8)+SH4_DIV_8,
.CS0BCR = 0x24920400,
.CS2BCR = 0x24923400,
.CS3BCR = 0x24924400,
.CS5aBCR = 0x17DF0400,
.CS0WCR = 0x00000440,
.CS2WCR = 0x000003C0,
.CS3WCR = 0x000024D1,
.CS5aWCR = 0x000203C1 },
};
#endif /* FXCG50 */
static struct cpg_overclock_setting const *get_settings(void)
{
if(gint[HWCALC] == HWCALC_FXCG50)
return settings_cg50;
#ifdef FX9860G
if(gint[HWCALC] == HWCALC_FX9860G_SH3)
return settings_fx9860g_sh3;
if(gint[HWCALC] == HWCALC_FX9860G_SH4)
return settings_fx9860g_sh4;
if(gint[HWCALC] == HWCALC_G35PE2)
return settings_g35pe2;
#endif /* FX9860G */
#ifdef FXCG50
if(gint[HWCALC] == HWCALC_PRIZM)
return settings_cg20;
return settings_prizm;
if(gint[HWCALC] == HWCALC_FXCG50)
return settings_fxcg50;
#endif /* FXCG50 */
return NULL;
}
int clock_get_speed(void)
{
struct cpg_overclock_setting *settings = get_settings();
struct cpg_overclock_setting const *settings = get_settings();
if(!settings)
return CLOCK_SPEED_UNKNOWN;
for(int i = 0; i < 5; i++) {
struct cpg_overclock_setting *s = &settings[i];
if(isSH3()) {
for(int i = 0; i < 5; i++) {
struct cpg_overclock_setting const *s = &settings[i];
if(CPG.FLLFRQ.lword == s->FLLFRQ
&& CPG.FRQCR.lword == s->FRQCR
&& BSC.CS0BCR.lword == s->CS0BCR
&& BSC.CS2BCR.lword == s->CS2BCR
&& BSC.CS3BCR.lword == s->CS3BCR
&& BSC.CS5ABCR.lword == s->CS5aBCR
&& BSC.CS0WCR.lword == s->CS0WCR
&& BSC.CS2WCR.lword == s->CS2WCR
&& BSC.CS3WCR.lword == s->CS3WCR
&& BSC.CS5AWCR.lword == s->CS5aWCR)
return CLOCK_SPEED_F1 + i;
if(SH7705_CPG.FRQCR.word == (s->FRQCR | 0x1000)
&& SH7705_BSC.CS0BCR.lword == s->CS0BCR
&& SH7705_BSC.CS2BCR.lword == s->CS2BCR
&& SH7705_BSC.CS3BCR.lword == s->CS3BCR
&& SH7705_BSC.CS5ABCR.lword == s->CS5aBCR
&& SH7705_BSC.CS0WCR.lword == s->CS0WCR
&& SH7705_BSC.CS2WCR.lword == s->CS2WCR
&& SH7705_BSC.CS3WCR.lword == s->CS3WCR
&& SH7705_BSC.CS5AWCR.lword == s->CS5aWCR)
return CLOCK_SPEED_F1 + i;
}
}
else {
for(int i = 0; i < 5; i++) {
struct cpg_overclock_setting const *s = &settings[i];
if(SH7305_CPG.FLLFRQ.lword == s->FLLFRQ
&& SH7305_CPG.FRQCR.lword == s->FRQCR
&& SH7305_BSC.CS0BCR.lword == s->CS0BCR
&& SH7305_BSC.CS2BCR.lword == s->CS2BCR
&& SH7305_BSC.CS3BCR.lword == s->CS3BCR
&& SH7305_BSC.CS5ABCR.lword == s->CS5aBCR
&& SH7305_BSC.CS0WCR.lword == s->CS0WCR
&& SH7305_BSC.CS2WCR.lword == s->CS2WCR
&& SH7305_BSC.CS3WCR.lword == s->CS3WCR
&& SH7305_BSC.CS5AWCR.lword == s->CS5aWCR)
return CLOCK_SPEED_F1 + i;
}
}
return CLOCK_SPEED_UNKNOWN;
@ -243,31 +505,10 @@ void clock_set_speed(int level)
if(clock_get_speed() == level)
return;
struct cpg_overclock_setting *settings = get_settings();
struct cpg_overclock_setting const *settings = get_settings();
if(!settings)
return;
struct cpg_overclock_setting *s = &settings[level - CLOCK_SPEED_F1];
uint32_t old_Pphi = clock_freq()->Pphi_f;
/* Wait for asynchronous tasks to complete */
gint_world_sync();
/* Disable interrupts during the change */
cpu_atomic_start();
/* Set the clock settings */
struct cpg_overclock_setting const *s = &settings[level - CLOCK_SPEED_F1];
cpg_set_overclock_setting(s);
/* Determine the change in frequency for Pϕ and recompute CPG data */
cpg_compute_freq();
uint32_t new_Pphi = clock_freq()->Pphi_f;
/* Update timers' TCNT and TCOR to match the new clock speed */
void timer_rescale(uint32_t old_Pphi, uint32_t new_Pphi);
timer_rescale(old_Pphi, new_Pphi);
cpu_atomic_end();
}
#endif

View File

@ -160,11 +160,8 @@ static void dma_interrupt_transfer_ended(int channel)
if(dma_wait_ics[channel])
cpu_csleep_cancel(dma_wait_ics[channel]);
if(dma_callbacks[channel].function)
{
gint_call(dma_callbacks[channel]);
dma_callbacks[channel] = GINT_CALL_NULL;
}
gint_call(dma_callbacks[channel]);
dma_callbacks[channel] = GINT_CALL_NULL;
}
/* dma_channel_wait(): Wait for a particular channel's transfer to finish
@ -178,7 +175,7 @@ static void dma_channel_wait(int channel, bool foreign)
if(!ch) return;
/* If interrupts are disabled or we don't own the device, spin-wait by
checking either for TE to be set (Transfere Ended) or DE to be gone
checking either for TE to be set (Transfer Ended) or DE to be gone
(channel disabled).
There are definitely race conditions if the DMA is restarted between

View File

@ -2,13 +2,14 @@
#include <gint/defs/attributes.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
/* File descriptor table */
static fs_descriptor_t fdtable[FS_FD_MAX] = { 0 };
static fs_descriptor_t *fdtable;
fs_descriptor_t const *fs_get_descriptor(int fd)
{
if((unsigned)fd >= FS_FD_MAX)
if(!fdtable || (unsigned)fd >= FS_FD_MAX)
return NULL;
if(fdtable[fd].type == NULL)
return NULL;
@ -18,7 +19,7 @@ fs_descriptor_t const *fs_get_descriptor(int fd)
int fs_create_descriptor(fs_descriptor_t const *data)
{
if(data->type == NULL)
if(!fdtable || data->type == NULL)
return -1;
/* Leave 0/1/2 for stdin, stdout and stderr */
@ -34,7 +35,7 @@ int fs_create_descriptor(fs_descriptor_t const *data)
void fs_free_descriptor(int fd)
{
if((unsigned)fd >= FS_FD_MAX)
if(!fdtable || (unsigned)fd >= FS_FD_MAX)
return;
fdtable[fd].type = NULL;
@ -43,6 +44,10 @@ void fs_free_descriptor(int fd)
int open_generic(fs_descriptor_type_t *type, void *data, int fd)
{
if(!fdtable) {
errno = ENOMEM;
return -1;
}
if(!type) {
errno = EINVAL;
return -1;
@ -81,8 +86,12 @@ static fs_descriptor_type_t devnull = {
.close = NULL,
};
GCONSTRUCTOR static void init_standard_streams(void)
GCONSTRUCTOR static void init_fs(void)
{
fdtable = calloc(FS_FD_MAX, sizeof *fdtable);
if(!fdtable)
return;
fdtable[STDIN_FILENO].type = &devnull;
fdtable[STDIN_FILENO].data = NULL;

View File

@ -25,6 +25,8 @@ int fugue_open(char const *path, int flags, mode_t mode);
int fugue_unlink(char const *path);
int fugue_rename(char const *oldpath, char const *newpath);
int fugue_mkdir(char const *path, mode_t mode);
int fugue_rmdir(char const *path);

View File

@ -0,0 +1,38 @@
#include <gint/hardware.h>
#include <gint/bfile.h>
#include <gint/fs.h>
#include <errno.h>
#include <sys/stat.h>
#include "util.h"
int fugue_rename(char const *oldpath, char const *newpath)
{
ENOTSUP_IF_NOT_FUGUE(-1);
int rc = -1;
uint16_t *fcpath_1 = NULL;
uint16_t *fcpath_2 = NULL;
fcpath_1 = fs_path_normalize_fc(oldpath);
if(!fcpath_1) {
errno = ENOMEM;
goto end;
}
fcpath_2 = fs_path_normalize_fc(newpath);
if(!fcpath_2) {
errno = ENOMEM;
goto end;
}
rc = BFile_Rename(fcpath_1, fcpath_2);
if(rc < 0) {
errno = bfile_error_to_errno(rc);
rc = -1;
}
else rc = 0;
end:
free(fcpath_1);
free(fcpath_2);
return rc;
}

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

@ -0,0 +1,8 @@
#include <stdio.h>
#include "fugue/fugue.h"
int rename(char const *oldpath, char const *newpath)
{
/* Standard rename() is the Fugue filesystem only */
return fugue_rename(oldpath, newpath);
}

View File

@ -41,6 +41,7 @@ static struct rendering_mode const gray_mode = {
.dclear = gclear,
.drect = grect,
.dpixel = gpixel,
.dgetpixel = ggetpixel,
.gint_dhline = gint_ghline,
.gint_dvline = gint_gvline,
.dtext_opt = gtext_opt,
@ -51,6 +52,7 @@ static struct rendering_mode const gray_exit_mode = {
.dclear = NULL,
.drect = NULL,
.dpixel = NULL,
.dgetpixel = NULL,
.gint_dhline = NULL,
.gint_dvline = NULL,
.dtext_opt = NULL,

15
src/gray/ggetpixel.c Normal file
View File

@ -0,0 +1,15 @@
#include <gint/gray.h>
#include <gint/defs/types.h>
int ggetpixel(int x, int y)
{
uint32_t *light, *dark;
dgray_getvram(&light, &dark);
int offset = (y << 2) + (x >> 5);
uint32_t mask = 1 << (~x & 31);
bool l = (light[offset] & mask) != 0;
bool d = (dark [offset] & mask) != 0;
return (d << 1) | l;
}

View File

@ -1,4 +1,5 @@
#include <gint/image.h>
#include <gint/display.h>
#include <string.h>
#undef image_sub
@ -15,8 +16,9 @@ image_t *image_sub(image_t const *src, int left, int top, int w, int h,
h = src->height - top;
struct gint_image_box box = { 0, 0, w, h, left, top };
struct dwindow in_window = { 0, 0, w, h };
if(!image_valid(src) || IMAGE_IS_P4(src->format) ||
!gint_image_clip_input(src, &box, w, h)) {
!gint_image_clip_input(src, &box, &in_window)) {
memset(dst, 0, sizeof *dst);
return dst;
}

View File

@ -11,8 +11,7 @@
//---
// Interrupt controllers
//---
GDATA3 sh7705_intc_t SH7705_INTC = {
GRODATA3 sh7705_intc_t const SH7705_INTC = {
.IPR = {
(void *)0xfffffee2, (void *)0xfffffee4,
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
@ -21,7 +20,7 @@ GDATA3 sh7705_intc_t SH7705_INTC = {
.ICR1 = (void *)0xa4000010,
};
sh7305_intc_t SH7305_INTC = {
sh7305_intc_t const SH7305_INTC = {
.IPR = (void *)0xa4080000,
.MSK = (void *)0xa4080080,
.MSKCLR = (void *)0xa40800c0,

View File

@ -2,16 +2,43 @@
#include <gint/display.h>
#include <gint/clock.h>
#include <gint/mpu/dma.h>
#include <gint/drivers/keydev.h>
#include <gint/defs/attributes.h>
#include <gint/hardware.h>
#include <gint/gint.h>
#include <stdlib.h>
#include <string.h>
void __Reset(void);
#define dprint(x, y, ...) dprint(x, y, C_BLACK, __VA_ARGS__)
#define dtext(x, y, str) dtext (x, y, C_BLACK, str)
/* Weak implementation of driver functions, which are used if the keyboard and
display drivers are not linked in. This allows add-ins to not link in these
drivers (which is useful for low-level debugging). */
GWEAK void _WEAK_dupdate(void)
{
return;
}
GWEAK keydev_t *_WEAK_keydev_std(void)
{
return NULL;
}
GWEAK bool _WEAK_keydev_keydown(GUNUSED keydev_t *d, GUNUSED int key)
{
return false;
}
GWEAK key_event_t _WEAK_keydev_unqueue_event(GUNUSED keydev_t *d)
{
return (key_event_t){ .type = KEYEV_NONE };
}
/* gint_panic_default(): Default panic handler */
GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
{
uint32_t TEA, TRA;
keydev_t *kd = _WEAK_keydev_std();
if(isSH3())
{
@ -31,9 +58,9 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
if(isSH4()) __asm__("stc sgr, %0" : "=r"(SGR));
dfont(NULL);
dclear(C_WHITE);
#ifdef FX9860G
memset(gint_vram, 0, 1024);
dtext(1, 0, "Exception! (SysERROR)");
for(int i = 0; i < 32; i++) gint_vram[i] = ~gint_vram[i];
@ -55,16 +82,23 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
if(name[0]) dtext(1, 9, name);
else dprint(1, 9, "%03x", code);
dprint(1, 17, " PC :%08x", PC);
dprint(1, 25, "TEA :%08x", TEA);
dprint(1, 33, "TRA :%08x", TRA);
if(isSH4()) dprint(1, 41, "SGR :%08x", SGR);
dprint(1, 17, " PC:%08x TRA:%d", PC, TRA);
dprint(1, 25, "TEA:%08x", TEA);
dtext(1, 49, "The add-in crashed.");
dtext(1, 57, "Please reset the calc");
dtext(1, 33, "The add-in crashed.");
if(kd == NULL) {
dtext(1, 41, "Please reset the calc");
}
else {
dtext(1, 41, "[EXIT]:Quit add-in");
dtext(1, 49, "[MENU]:Main menu");
dtext(1, 57, "[F1]+[F6]: RESET calc");
}
#endif
#ifdef FXCG50
/* Don't require the DMA driver just for a clear */
memset(gint_vram, 0xff, DWIDTH*DHEIGHT*2);
dtext(6, 3, "An exception occured! (System ERROR)");
uint32_t *long_vram = (void *)gint_vram;
@ -99,34 +133,63 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
dprint(38, 75, "= %#x", TRA);
dtext(281, 75, "(Trap number)");
dtext(6, 95, "An unrecoverable error occurred in the add-in.");
dtext(6, 108, "Please press the RESET button to restart the");
dtext(6, 121, "calculator.");
dtext(6, 95, "The add-in crashed!");
if(kd == NULL) {
dtext(6, 108, "Please press the RESET button to restart the");
dtext(6, 121, "calculator.");
}
else {
dtext(6, 121, "[EXIT]: Exit the program with abort()");
dtext(6, 134, "[MENU]: Leave to main menu");
dtext(6, 147, "[F1]+[F6]: RESET the calculator");
}
/* DMA address error handler */
if(code == 0x1020)
{
#define DMA SH7305_DMA
dprint(6, 141, "SAR0: %08x DAR0: %08x TCR0: %08x",
dprint(6, 167, "SAR0: %08x DAR0: %08x TCR0: %08x",
DMA.DMA0.SAR, DMA.DMA0.DAR, DMA.DMA0.TCR);
dprint(6, 154, "CHCR0: %08x", DMA.DMA0.CHCR);
dprint(6, 167, "SAR1: %08x DAR1: %08x TCR1: %08x",
dprint(6, 180, "CHCR0: %08x", DMA.DMA0.CHCR);
dprint(6, 193, "SAR1: %08x DAR1: %08x TCR1: %08x",
DMA.DMA1.SAR, DMA.DMA1.DAR, DMA.DMA1.TCR);
dprint(6, 180, "CHCR1: %08x", DMA.DMA1.CHCR);
dprint(6, 193, "DMAOR: %04x", DMA.OR);
dprint(6, 206, "CHCR1: %08x DMAOR:%04x", DMA.DMA1.CHCR, DMA.OR);
#undef DMA
}
/* Illegal instruction handler */
if(code == 0x180)
{
uint16_t *opcodes = (void *)PC;
dprint(6, 141, "Opcodes: %04x %04x [%04x] %04x",
dprint(6, 160, "Opcodes: %04x %04x [%04x] %04x",
opcodes[-2], opcodes[-1], opcodes[0], opcodes[1]);
}
#endif
dupdate();
while(1) sleep();
_WEAK_dupdate();
/* Make sure relevant keys are released before taking in events; we don't
want the user to RESET through this screen by holding a key */
bool has_released = false;
while(1) {
while(_WEAK_keydev_unqueue_event(kd).type != KEYEV_NONE) {}
bool exit = _WEAK_keydev_keydown(kd, KEY_EXIT);
bool menu = _WEAK_keydev_keydown(kd, KEY_MENU);
bool f1 = _WEAK_keydev_keydown(kd, KEY_F1);
bool f6 = _WEAK_keydev_keydown(kd, KEY_F6);
if(has_released && exit)
abort();
if(has_released && menu)
gint_osmenu();
if(has_released && f1 && f6)
__Reset();
if(!exit && !menu && !f1 && !f6)
has_released = true;
sleep();
}
}
/* Panic handler */

View File

@ -69,7 +69,8 @@ void hw_detect(void)
gint[HWCALC] = HWCALC_FX9860G_SH4;
if(gint[HWMPU] == HWMPU_SH7337 || gint[HWMPU] == HWMPU_SH7355)
{
gint[HWCALC] = HWCALC_FX9860G_SH3;
gint[HWCALC] = (SH7705_PFC.PEDR & 0x08) ? HWCALC_FX9860G_SH3 :
HWCALC_FX9860G_SLIM;
}
/* Tell Graph 35+E II from OS version (this is accurate unless someone

View File

@ -102,7 +102,7 @@ _gint_inth_7305:
#ifdef FX9860G
/* SH7705-TYPE INTERRUT HANDLER ENTRY - 56 BYTES */
/* SH7705-TYPE INTERRUPT HANDLER ENTRY - 56 BYTES */
_gint_inth_7705:
/* Save caller-saved registers as before */

View File

@ -1,5 +1,6 @@
#include <gint/gint.h>
#include <gint/display.h>
#include <gint/hardware.h>
#include <string.h>
@ -12,18 +13,22 @@ int __GetKeyWait(int *col,int *row,int type,int time,int menu,uint16_t *key);
void __ClearKeyBuffer(void); /* ? */
void *__GetVRAMAddress(void);
void __ConfigureStatusArea(int mode);
void __SetQuitHandler(void (*callback)(void));
static int __osmenu_id;
static void __osmenu_handler(void)
{
__PutKeyCode(0x04, 0x09, 0);
if(isSlim())
__PutKeyCode(0x07, 0x0A, 0);
else
__PutKeyCode(0x04, 0x09, 0);
__Timer_Stop(__osmenu_id);
__Timer_Deinstall(__osmenu_id);
}
static void __osmenu(void)
void gint_osmenu_native(void)
{
__ClearKeyBuffer();
@ -70,5 +75,30 @@ static void __osmenu(void)
/* gint_osmenu() - switch out of gint and call the calculator's main menu */
void gint_osmenu(void)
{
gint_world_switch(GINT_CALL(__osmenu));
gint_world_switch(GINT_CALL(gint_osmenu_native));
}
static gint_call_t __gcall;
static bool __do_world_switch;
static void __handler()
{
if(__do_world_switch){
gint_call(__gcall);
}else{
/* TODO: quit the world switch */
gint_call(__gcall);
}
}
static void __sethandler()
{
__SetQuitHandler((void *)__handler);
}
void gint_set_quit_handler(gint_call_t gcall, bool do_world_switch)
{
__gcall = gcall;
__do_world_switch = do_world_switch;
gint_world_switch(GINT_CALL(__sethandler));
}

View File

@ -25,7 +25,7 @@ extern uint32_t
lilram, silram, rilram, /* IL memory section */
lxyram, sxyram, rxyram, /* X and Y memory section */
sbss, rbss; /* User's BSS section */
#ifdef FX9860G
#if defined(FX9860G) && !defined(FX9860G_AS_CG)
extern uint32_t
lgmapped, sgmapped, /* Permanently mapped functions */
lreloc, sreloc; /* Relocatable references */
@ -94,11 +94,7 @@ static void callarray(void (**f)(void), void (**l)(void))
while(f < l) (*(*f++))();
}
/* start(): Where it all starts
Returns a status code. Invoking main menu is better than returning, see
gint_setrestart() for that. */
GSECTION(".text.entry")
int start(int isappli, int optnum)
static int start2(int isappli, int optnum)
{
/* We are currently in a dynamic userspace mapping of an add-in run
from the storage memory. We are running in privileged mode with one
@ -124,7 +120,7 @@ int start(int isappli, int optnum)
1.00 and the fx-9860G emulator) is not clear yet, so gint can't load
pages dynamically. Load everything preventively (works only if the
add-in is small enough) */
#ifdef FX9860G
#if defined(FX9860G) && !defined(FX9860G_AS_CG)
if(isSH3())
{
/* Try to map every ROM address up to _srom */
@ -153,7 +149,7 @@ int start(int isappli, int optnum)
regcpy(&lxyram, &sxyram, &rxyram);
}
#ifdef FX9860G
#if defined(FX9860G) && !defined(FX9860G_AS_CG)
/* Copy permanently-mapped code to start of user RAM (on fx-CG 50 it
is loaded along ILRAM contents) */
void *rgmapped = mmu_uram();
@ -179,20 +175,14 @@ int start(int isappli, int optnum)
hosted user application, which has its own constructors and
destructors to work with. */
while(1)
{
/* Here, we use exit() to allow the standard library to do
what it wants in exit() after main() finishes executing */
if(!setjmp(gint_exitbuf)) {
callarray(&bctors, &ectors);
exit(main(isappli, optnum));
}
else {
callarray(&bdtors, &edtors);
}
if(!gint_restart) break;
gint_osmenu();
/* Here, we use exit() to allow the standard library to do
what it wants in exit() after main() finishes executing */
if(!setjmp(gint_exitbuf)) {
callarray(&bctors, &ectors);
exit(main(isappli, optnum));
}
else {
callarray(&bdtors, &edtors);
}
/* Before leaving the application, we need to clean everything we
@ -206,6 +196,18 @@ int start(int isappli, int optnum)
return gint_exitcode;
}
GSECTION(".text.entry")
int start(int isappli, int optnum)
{
int rc;
while(1) {
rc = start2(isappli, optnum);
if(!gint_restart) break;
gint_osmenu_native();
}
return rc;
}
/* Standard _Exit, used by the fxlibc exit() to leave control */
void _Exit(int rc)
{

View File

@ -19,6 +19,7 @@
/* Bfile driver */
.global _BFile_Remove
.global _BFile_Rename
.global _BFile_Create
.global _BFile_Open
.global _BFile_Close
@ -41,6 +42,10 @@
.global ___ClearKeyBuffer
.global ___GetVRAMAddress
.global ___ConfigureStatusArea
.global ___SetQuitHandler
/* Reset */
.global ___Reset
#define syscall_(id, syscall_table) \
mov.l syscall_table, r2 ;\
@ -110,6 +115,13 @@ ___ClearKeyBuffer:
syscall(0x241)
___GetVRAMAddress:
syscall(0x135)
___SetQuitHandler:
syscall(0x494)
/* Reset */
___Reset:
syscall(0x236)
syscall_table:
.long 0x80010070
@ -131,6 +143,8 @@ ___realloc:
_BFile_Remove:
syscall(0x1db4)
_BFile_Rename:
syscall(0x1db3)
_BFile_Create:
syscall(0x1dae)
_BFile_Open:
@ -175,11 +189,18 @@ ___GetVRAMAddress:
syscall(0x1e6)
___ConfigureStatusArea:
syscall(0x2b7)
___SetQuitHandler:
syscall(0x1e6e)
.global ___SpecialMatrixCodeProcessing
___SpecialMatrixCodeProcessing:
syscall(0x1e60)
/* Reset */
___Reset:
syscall(0x1187)
syscall_table:
.long 0x80020070

View File

@ -149,7 +149,7 @@ int gint_world_switch(gint_call_t call)
if(canary)
*canary = 0xb7c0ffee;
int rc = call.function ? gint_call(call) : 0;
int rc = gint_call(call);
/* The canary check needs to occur before switching in the gint world;
otherwise we just crash due to the overflow. gint_panic() isn't

View File

@ -35,7 +35,7 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
while(1)
{
e = keydev_read(d);
e = keydev_read(d, true, timeout);
if(e.type == KEYEV_NONE && timeout && *timeout) break;
/* Skip repeat events that are not enabled by options */
@ -51,7 +51,8 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
#ifdef FX9860G
/* Backlight toggle */
if((opt & GETKEY_BACKLIGHT) && e.type == KEYEV_DOWN &&
e.key == KEY_OPTN && e.shift && !e.alpha)
((e.key == KEY_LIGHT) ||
(e.key == KEY_OPTN && e.shift && !e.alpha)))
{
t6k11_backlight(-1);
continue;

View File

@ -4,6 +4,7 @@
#include <gint/defs/types.h>
#include <gint/mpu/pfc.h>
#include <gint/hardware.h>
/* This file is SH7705-only. */
#ifdef FX9860G
@ -54,7 +55,6 @@ static void iokbd_delay(void)
uint8_t iokbd_row(int row)
{
if((unsigned)row > 9) return 0x00;
row ^= 1;
int orig_PBCR = PFC.PBCR;
int orig_PMCR = PFC.PMCR;
@ -111,12 +111,55 @@ uint8_t iokbd_row(int row)
return input;
}
static const uint16_t SLIM_SC[] = {
0x0940, 0x0920, 0x0910, 0x0908,
0x0840, 0x0820, 0x0810, 0x0808, 0x0804, 0x0802,
0x0740, 0x0720, 0x0710, 0x0708, 0x0704, 0x0702,
0x0640, 0x0620, 0x0610, 0x0608, 0x0604, 0x0602,
0x0540, 0x0520, 0x0510, 0x0508, 0x0504, 0x0502,
0x0440, 0x0420, 0x0410, 0x0408, 0x0404, 0x0402,
0x0340, 0x0320, 0x0310, 0x0308, 0x0304, 0x0302,
0x0240, 0x0220, 0x0210, 0x0208, 0x0204, 0x0202,
0x0120, 0x0110, 0x0108, 0x0104, 0x0102,
0x0001
};
#define SCANCODE_COUNT (sizeof(SLIM_SC) / sizeof(uint16_t))
static const uint16_t SLIM_TR[] = {
0x0808, 0x0640, 0x0840, 0x0740,
0x0940, 0x0620, 0x0720, 0x0710, 0x0804, 0x0802,
0x0920, 0x0610, 0x0504, 0x0820, 0x0704, 0x0702,
0x0910, 0x0608, 0x0502, 0x0810, 0x0280, 0x0180,
0x0908, 0x0604, 0x0440, 0x0340, 0x0240, 0x0140,
0x0904, 0x0602, 0x0420, 0x0320, 0x0220, 0x0120,
0x0902, 0x0540, 0x0410, 0x0310, 0x0210, 0x0110,
0x0708, 0x0520, 0x0408, 0x0308, 0x0208, 0x0108,
0x0510, 0x0508, 0x0304, 0x0104, 0x0204,
0x0001
};
/* iokbd_scan() - scan ports A/B/M to generate 12 rows of key data */
void iokbd_scan(uint8_t *scan)
{
/* Scan each row independently; the gain from scanning them altogether
/* Scan each row independently; the gain from scanning them all together
is probably not worth it */
for(int i = 0; i < 12; i++) scan[i] = iokbd_row(i);
/* Translate fx-9860G Slim scancodes to standard scancodes */
if(isSlim())
{
uint8_t slim_scan[12];
for(uint i = 0; i < 10; i++)
{
slim_scan[i] = scan[i];
scan[i] = 0x00;
}
for(uint i = 0; i < SCANCODE_COUNT; i++)
if(slim_scan[SLIM_SC[i] >> 8] & (SLIM_SC[i] & 0xFF))
scan[SLIM_TR[i] >> 8] |= (SLIM_TR[i] & 0xFF);
}
}
#endif /* FX9860G */

View File

@ -3,6 +3,7 @@
//---
#include <gint/keyboard.h>
#include <gint/cpu.h>
#include <gint/drivers/keydev.h>
#include <gint/defs/types.h>
#include <gint/defs/util.h>
@ -30,6 +31,9 @@ static int standard_repeater(GUNUSED int key, GUNUSED int duration, int count)
Returns false if the event cannot be pushed. */
bool keydev_queue_push(keydev_t *d, key_event_t ev)
{
if(d->async_filter && !d->async_filter(ev))
return true;
int next = (d->queue_end + 1) % KEYBOARD_QUEUE_SIZE;
if(next == d->queue_next)
{
@ -146,6 +150,16 @@ void keydev_tick(keydev_t *d, uint us)
}
}
keydev_async_filter_t keydev_async_filter(keydev_t const *d)
{
return d->async_filter;
}
void keydev_set_async_filter(keydev_t *d, keydev_async_filter_t filter)
{
d->async_filter = filter;
}
//---
// Keyboard event generation
//---
@ -191,6 +205,8 @@ key_event_t keydev_unqueue_event(keydev_t *d)
return ev;
}
__attribute__((alias("keydev_unqueue_event")))
key_event_t _WEAK_keydev_unqueue_event(keydev_t *d);
/* keydev_keydown(): Check if a key is down according to generated events */
bool keydev_keydown(keydev_t *d, int key)
@ -200,6 +216,8 @@ bool keydev_keydown(keydev_t *d, int key)
return (d->state_queue[row] & col) != 0;
}
__attribute__((alias("keydev_keydown")))
bool _WEAK_keydev_keydown(keydev_t *d, int key);
//---
// Event transforms
@ -239,7 +257,7 @@ void keydev_set_standard_repeats(keydev_t *d, int first, int next)
}
/* keydev_read(): Retrieve the next transformed event */
key_event_t keydev_read(keydev_t *d)
key_event_t keydev_read(keydev_t *d, bool wait, volatile int *timeout)
{
#define opt(NAME) (d->tr.enabled & KEYDEV_TR_ ## NAME)
key_event_t e;
@ -248,7 +266,12 @@ key_event_t keydev_read(keydev_t *d)
{
e = keydev_unqueue_event(d);
if(e.type == KEYEV_NONE)
return e;
{
if(!wait || (timeout && *timeout))
return e;
sleep();
continue;
}
int k = e.key;
e.mod = (opt(ALL_MODS) != 0);

View File

@ -14,7 +14,7 @@
#include <gint/drivers/iokbd.h>
#include <gint/hardware.h>
#include <stdarg.h>
#include <string.h>
/* Keyboard scan frequency in Hertz. Start with 128 Hz, this frequency *must
be high* for the keyboard to work! Reading at low frequencies produces a lot
@ -33,24 +33,32 @@ keydev_t *keydev_std(void)
{
return &keysc_dev;
}
__attribute__((alias("keydev_std")))
keydev_t *_WEAK_keydev_std(void);
/* keysc_scan(): Scand the keyboard */
static void keysc_scan(uint8_t *scan)
{
if(isSH3())
{
iokbd_scan(scan);
return;
}
volatile uint16_t *KEYSC = (void *)0xa44b0000;
for(int i = 0; i < 6; i++) {
int data = KEYSC[i];
scan[2*i] = data & 0xff;
scan[2*i+1] = data >> 8;
}
}
/* keysc_tick(): Update the keyboard to the next state */
static int keysc_tick(void)
{
GALIGNED(2) uint8_t scan[12] = { 0 };
/* Scan the key matrix: from I/O ports on SH3, KEYSC on SH4 */
if(isSH3()) iokbd_scan(scan);
else
{
volatile uint16_t *KEYSC = (void *)0xa44b0000;
uint16_t *array = (void *)&scan;
for(int i = 0; i < 6; i++) {
array[i] = KEYSC[i];
array[i] = (array[i] << 8) | (array[i] >> 8);
}
}
uint8_t scan[12] = { 0 };
keysc_scan(scan);
keydev_process_state(&keysc_dev, scan);
keydev_tick(&keysc_dev, keysc_scan_us);
@ -99,6 +107,11 @@ static void configure(void)
{
keydev_init(&keysc_dev);
/* Do a first scan to load the initial state (so that keys that are
pressed at startup are not registered as new presses) */
keysc_scan(keysc_dev.state_now);
memcpy(keysc_dev.state_queue, keysc_dev.state_now, 12);
/* Set the default repeat to 400/40 ms */
keydev_t *d = keydev_std();
keydev_set_transform(d, (keydev_transform_t){ KEYDEV_TR_REPEATS, NULL });

View File

@ -178,6 +178,25 @@ static block_t *best_fit(block_t *list, size_t size)
return best_match;
}
/* Find a worst fit in the list */
static block_t *worst_fit(block_t *list)
{
block_t *best_match = NULL;
size_t best_size = 0;
while(list)
{
if(list->size >= best_size)
{
best_match = list;
best_size = list->size;
}
list = next_link(list);
}
return best_match;
}
//---
// Index-level operations
//---
@ -240,7 +259,7 @@ static void *gint_malloc(size_t size, void *data)
int c = size_class(size);
/* Try to find a class that has a free block available */
block_t *alloc;
block_t *alloc = NULL;
for(; c <= 15; c++)
{
block_t *list = index->classes[c];
@ -366,6 +385,35 @@ static void *gint_realloc(void *ptr, size_t size, void *data)
return new_ptr;
}
static void *gint_malloc_max(size_t *size, void *data)
{
index_t *index = data;
stats_t *s = index->stats;
/* Find the largest available block in the largest possible class */
block_t *alloc;
for(int c = 15; c >= 0; c--)
{
block_t *list = index->classes[c];
alloc = (c < 14) ? list : worst_fit(list);
if(alloc) break;
}
if(!alloc) return NULL;
remove_link(alloc, index);
/* Mark the block as allocated and return it */
block_t *next = next_block(alloc);
alloc->used = true;
if(next) next->previous_used = true;
if(s) s->used_memory += alloc->size;
if(s) s->peak_used_memory = max(s->peak_used_memory, s->used_memory);
*size = alloc->size;
return (void *)alloc + sizeof(block_t);
}
/* kmalloc_init_arena(): Initialize an arena with gint's allocator */
void kmalloc_init_arena(kmalloc_arena_t *a, bool enable_statistics)
{
@ -375,6 +423,7 @@ void kmalloc_init_arena(kmalloc_arena_t *a, bool enable_statistics)
a->malloc = gint_malloc;
a->free = gint_free;
a->realloc = gint_realloc;
a->malloc_max = gint_malloc_max;
/* The index is located at the very start of the arena */
index_t *index = a->start;

View File

@ -112,6 +112,16 @@ void *krealloc(void *ptr, size_t size)
else
{
a->stats.total_failures++;
/* If reallocation within the original arena fails, try another
one. The memory copy behavior is sub-optimal (we copy the
new size which might be more than the original size) but
it's all we can do with this arena interface. */
rc = kmalloc(size, NULL);
if(rc)
{
memcpy(rc, ptr, size);
}
}
return rc;
@ -131,6 +141,36 @@ void kfree(void *ptr)
a->stats.live_blocks--;
}
/* kmalloc_max(): Allocate the largest block available in an arena */
void *kmalloc_max(size_t *size, char const *name)
{
for(int i = 0; i < KMALLOC_ARENA_MAX; i++) if(arenas[i])
{
kmalloc_arena_t *a = arenas[i];
if(strcmp(a->name, name)) continue;
void *rc = a->malloc_max ? a->malloc_max(size, a->data) : NULL;
/* Maintain statistics */
struct kmalloc_stats *s = &a->stats;
if(rc)
{
s->live_blocks++;
s->peak_live_blocks = max(s->live_blocks,
s->peak_live_blocks);
s->total_volume += *size;
s->total_blocks++;
return rc;
}
else
{
s->total_failures++;
}
}
return NULL;
}
/* kmalloc_add_arena(): Add a new arena to the heap source */
bool kmalloc_add_arena(kmalloc_arena_t *arena)
{

View File

@ -129,21 +129,18 @@ uint32_t tlb_translate(uint32_t page, uint32_t *size)
// SH7305 Unified TLB
//---
/* utlb_addr() - get the P4 address of a UTLB address entry */
GINLINE const utlb_addr_t *utlb_addr(uint E)
{
uint32_t addr = 0xf6000000 | ((E & 0x3f) << 8);
return (void *)addr;
}
/* utlb_data() - get the P4 address of a UTLB data entry */
GINLINE const utlb_data_t *utlb_data(uint E)
{
uint32_t addr = 0xf7000000 | ((E & 0x3f) << 8);
return (void *)addr;
}
/* utlb_mapped_memory() - count amount of mapped memory */
void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
{
uint32_t rom = 0, ram = 0;
@ -170,7 +167,6 @@ void utlb_mapped_memory(uint32_t *p_rom, uint32_t *p_ram)
gint[HWURAM] = ram;
}
/* utlb_translate(): Get the physical address for a virtual page */
uint32_t utlb_translate(uint32_t page, uint32_t *size)
{
for(int E = 0; E < 64; E++)
@ -191,6 +187,18 @@ uint32_t utlb_translate(uint32_t page, uint32_t *size)
return -1;
}
itlb_addr_t const *itlb_addr(uint E)
{
uint32_t addr = 0xf2000000 | ((E & 3) << 8);
return (void *)addr;
}
itlb_data_t const *itlb_data(uint E)
{
uint32_t addr = 0xf3000000 | ((E & 3) << 8);
return (void *)addr;
}
static void configure(void)
{
/* Make writes to the control register area synchronous; this is needed

View File

@ -1,9 +1,18 @@
#include <gint/display.h>
#include <gint/dma.h>
/* dclear() - fill the screen with a single color */
void dclear(uint16_t color)
{
dma_memset(gint_vram, (color << 16) | color, 396 * 224 * 2);
return;
bool full_width = (dwindow.left == 0 && dwindow.right == DWIDTH);
bool dma_aligned = !(dwindow.top & 3) && !(dwindow.bottom & 3);
if(full_width && dma_aligned) {
uint16_t *vram = gint_vram + DWIDTH * dwindow.top;
int size_bytes = DWIDTH * (dwindow.bottom - dwindow.top) * 2;
dma_memset(vram, (color << 16) | color, size_bytes);
}
else {
drect(dwindow.left, dwindow.top, dwindow.right - 1,
dwindow.bottom - 1, color);
}
}

View File

@ -0,0 +1,7 @@
#include <gint/display.h>
int dgetpixel(int x, int y)
{
if((uint)x >= DWIDTH || (uint)y >= DHEIGHT) return -1;
return gint_vram[DWIDTH * y + x];
}

View File

@ -1,11 +1,12 @@
#include <gint/display.h>
/* dpixel() - change a pixel's color */
void dpixel(int x, int y, int color)
{
/* Coordinate checks */
if((uint)x >= 396 || (uint)y >= 224 || color == C_NONE) return;
int index = 396 * y + x;
if(x < dwindow.left || x >= dwindow.right) return;
if(y < dwindow.top || y >= dwindow.bottom) return;
if(color == C_NONE) return;
int index = DWIDTH * y + x;
if(color == C_INVERT) gint_vram[index] ^= 0xffff;
else gint_vram[index] = color;

View File

@ -1,7 +1,6 @@
#include <gint/defs/util.h>
#include <gint/display.h>
/* drect() - fill a rectangle of the screen */
void drect(int x1, int y1, int x2, int y2, int color)
{
if(color == C_NONE) return;
@ -9,17 +8,19 @@ void drect(int x1, int y1, int x2, int y2, int color)
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
/* Order and bounds */
if(x1 >= 396 || x2 < 0 || y1 >= 224 || y2 < 0) return;
if(x1 < 0) x1 = 0;
if(x2 >= 396) x2 = 395;
if(y1 < 0) y1 = 0;
if(y2 >= 224) y2 = 223;
/* Rectangle is completely outside the rendering window */
if(x1 >= dwindow.right || x2 < dwindow.left) return;
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
/* Clipping */
x1 = max(x1, dwindow.left);
x2 = min(x2, dwindow.right - 1);
y1 = max(y1, dwindow.top);
y2 = min(y2, dwindow.bottom - 1);
/* The method is exactly like dhline(). I first handle odd endpoints,
then write longwords for the longest section */
uint16_t *base = gint_vram + 396 * y1;
uint16_t *base = gint_vram + DWIDTH * y1;
int height = y2 - y1 + 1;
/* Now copy everything that's left as longwords */
@ -33,18 +34,19 @@ void drect(int x1, int y1, int x2, int y2, int color)
if(color == C_INVERT) for(int h = height; h; h--)
{
base[x1] ^= 0xffff;
base[x2] ^= 0xffff;
/* We can't double-draw on base[x1] and base[x2] here */
if(x1 & 1) base[x1] ^= 0xffff;
if(!(x2 & 1)) base[x2] ^= 0xffff;
for(int w = 0; w < width; w++) v[w] ^= 0xffffffff;
v += 198;
base += 396;
v += DWIDTH / 2;
base += DWIDTH;
}
else for(int h = height; h; h--)
{
base[x1] = color;
base[x2] = color;
for(int w = 0; w < width; w++) v[w] = op;
v += 198;
base += 396;
v += DWIDTH / 2;
base += DWIDTH;
}
}

View File

@ -12,9 +12,10 @@ void dupdate(void)
r61524_display(gint_vram, 0, 224, method);
gint_call_t hook = dupdate_get_hook();
if(hook.function) gint_call(hook);
gint_call(dupdate_get_hook());
/* Switch buffers if triple buffering is enabled */
dvram_switch();
}
__attribute__((alias("dupdate")))
void _WEAK_dupdate(void);

View File

@ -1,5 +1,6 @@
#include <gint/display.h>
#include <stdlib.h>
#include <gint/kmalloc.h>
#include <gint/config.h>
/* Up to two VRAM pointers can be set, for triple buffering. */
static uint16_t *vram_1 = NULL, *vram_2 = NULL;
@ -11,9 +12,15 @@ bool dvram_init(void)
int const MARGIN = 32;
/* Leave MARGIN bytes on each side of the region; this enables some
important optimisations in the image renderer. We also add another
important optimizations in the image renderer. We also add another
32 bytes so we can manually 32-align the region */
uint32_t region = (uint32_t)malloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32);
uint32_t region = (uint32_t)kmalloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32,
#if !defined(GINT_NO_OS_STACK)
"_ostk"
#else
NULL
#endif
);
if(region == 0)
return false;

View File

@ -4,14 +4,13 @@
/* gint_dhline(): Optimized horizontal line */
void gint_dhline(int x1, int x2, int y, uint16_t color)
{
/* Order and bounds */
if((uint)y >= 224) return;
if(y < dwindow.top || y >= dwindow.bottom) return;
if(x1 > x2) swap(x1, x2);
if(x1 >= 396 || x2 < 0) return;
if(x1 < 0) x1 = 0;
if(x2 >= 396) x2 = 395;
if(x1 >= dwindow.right || x2 < dwindow.left) return;
x1 = max(x1, dwindow.left);
x2 = min(x2, dwindow.right - 1);
int offset = 396 * y;
int offset = DWIDTH * y;
/* Use longwords to do the copy, but first paint the endpoints to heed
for odd x1 and x2. Checking the parity may be a waste of time. */
@ -33,15 +32,14 @@ void gint_dhline(int x1, int x2, int y, uint16_t color)
/* gint_dvline(): Optimized vertical line */
void gint_dvline(int y1, int y2, int x, uint16_t color)
{
/* Order and bounds */
if((uint)x >= 396) return;
if(x < dwindow.left || x >= dwindow.right) return;
if(y1 > y2) swap(y1, y2);
if(y1 < 0) y1 = 0;
if(y2 >= 224) y2 = 223;
y1 = max(y1, dwindow.top);
y2 = min(y2, dwindow.bottom - 1);
uint16_t *v = gint_vram + 396 * y1 + x;
uint16_t *v = gint_vram + DWIDTH * y1 + x;
int height = y2 - y1 + 1;
while(height-- > 0) *v = color, v += 396;
while(height-- > 0) *v = color, v += DWIDTH;
}

View File

@ -1,8 +1,9 @@
#include <gint/image.h>
#include <gint/display.h>
#include <gint/defs/util.h>
bool gint_image_clip_input(image_t const *img, struct gint_image_box *b,
int out_w, int out_h)
struct dwindow const *window)
{
/* Adjust the bounding box of the input image */
if(b->left < 0) b->w += b->left, b->x -= b->left, b->left = 0;
@ -13,37 +14,50 @@ bool gint_image_clip_input(image_t const *img, struct gint_image_box *b,
/* Check whether the box intersects the screen */
if(b->w <= 0 || b->h <= 0)
return false;
if(b->x + b->w <= 0 || b->x >= out_w)
if(b->x + b->w <= window->left || b->x >= window->right)
return false;
if(b->y + b->h <= 0 || b->y >= out_h)
if(b->y + b->h <= window->top || b->y >= window->bottom)
return false;
return true;
}
void gint_image_clip_output(struct gint_image_box *b, int out_w, int out_h)
void gint_image_clip_output(struct gint_image_box *b,
struct dwindow const *window)
{
/* Intersect with the bounding box on-screen */
if(b->y < 0) b->top -= b->y, b->h += b->y, b->y = 0;
if(b->y + b->h > out_h) b->h = (out_h - b->y);
if(b->x < 0) b->left -= b->x, b->w += b->x, b->x = 0;
if(b->x + b->w > out_w) b->w = (out_w - b->x);
if(b->y < window->top) {
int d = window->top - b->y; /* > 0 */
b->top += d;
b->h -= d;
b->y += d;
}
b->h = min(b->h, window->bottom - b->y);
if(b->x < window->left) {
int d = window->left - b->x; /* > 0 */
b->left += d;
b->w -= d;
b->x += d;
}
b->w = min(b->w, window->right - b->x);
}
bool gint_image_mkcmd(struct gint_image_box *box, image_t const *img,
int effects, bool left_edge, bool right_edge,
struct gint_image_cmd *cmd, int out_width, int out_height)
struct gint_image_cmd *cmd, struct dwindow const *window)
{
/* Convert the old DIMAGE_NOCLIP flag */
if(effects & DIMAGE_NOCLIP)
effects |= IMAGE_NOCLIP;
if(!(effects & IMAGE_NOCLIP_INPUT)) {
if(!gint_image_clip_input(img, box, out_width, out_height))
if(!gint_image_clip_input(img, box, window))
return false;
}
if(!(effects & IMAGE_NOCLIP_OUTPUT))
gint_image_clip_output(box, out_width, out_height);
gint_image_clip_output(box, window);
cmd->effect = (effects & (IMAGE_VFLIP | IMAGE_HFLIP)) >> 8;
cmd->columns = box->w;

View File

@ -16,8 +16,8 @@ void dsubimage_p4(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.loop = gint_image_p4_normal;
gint_image_p4_loop(DWIDTH, &cmd);
}
@ -33,8 +33,8 @@ void dsubimage_p4_clearbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = bg_color;
cmd.loop = gint_image_p4_clearbg;
gint_image_p4_loop(DWIDTH, &cmd);

View File

@ -13,8 +13,8 @@ void dsubimage_p4_clearbg_alt(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
return;
cmd.color_1 = bg_color;
cmd.loop = gint_image_p4_clearbg_alt;
gint_image_p4_loop(DWIDTH, &cmd);

View File

@ -13,8 +13,8 @@ void dsubimage_p4_dye(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = dye_color;
cmd.loop = gint_image_p4_dye;

View File

@ -14,8 +14,8 @@ void dsubimage_p4_swapcolor(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
return;
cmd.color_1 = old_index;
cmd.color_2 = new_color;
cmd.loop = gint_image_p4_swapcolor;
@ -35,8 +35,8 @@ void dsubimage_p4_addbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, true, true, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = bg_color;
cmd.loop = gint_image_p4_swapcolor;

View File

@ -16,8 +16,8 @@ void dsubimage_p8(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.loop = gint_image_p8_normal;
gint_image_p8_loop(DWIDTH, &cmd);
}
@ -33,8 +33,8 @@ void dsubimage_p8_clearbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, &dwindow))
return;
cmd.color_1 = bg_color;
cmd.loop = gint_image_p8_clearbg;
gint_image_p8_loop(DWIDTH, &cmd);

View File

@ -13,8 +13,8 @@ void dsubimage_p8_dye(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, true, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = dye_color;
cmd.loop = gint_image_p8_dye;

View File

@ -14,8 +14,8 @@ void dsubimage_p8_swapcolor(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = old_index;
cmd.color_2 = new_color;
cmd.loop = gint_image_p8_swapcolor;
@ -35,8 +35,8 @@ void dsubimage_p8_addbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = bg_color;
cmd.loop = gint_image_p8_swapcolor;

View File

@ -16,8 +16,8 @@ void dsubimage_rgb16(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.loop = gint_image_rgb16_normal;
gint_image_rgb16_loop(DWIDTH, &cmd);
}
@ -34,8 +34,8 @@ void dsubimage_rgb16_clearbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = bg_color;
cmd.loop = gint_image_rgb16_clearbg;
gint_image_rgb16_loop(DWIDTH, &cmd);

View File

@ -13,8 +13,8 @@ void dsubimage_rgb16_dye(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = dye_color;
cmd.loop = gint_image_rgb16_dye;

View File

@ -14,8 +14,8 @@ void dsubimage_rgb16_swapcolor(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = old_color;
cmd.color_2 = new_color;
cmd.loop = gint_image_rgb16_swapcolor;
@ -35,8 +35,8 @@ void dsubimage_rgb16_addbg(int x, int y, image_t const *img,
struct gint_image_box box = { x, y, w, h, left, top };
struct gint_image_cmd cmd;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, DWIDTH,
DHEIGHT)) return;
if(!gint_image_mkcmd(&box, img, eff, false, false, &cmd, &dwindow))
return;
cmd.color_1 = image_alpha(img->format);
cmd.color_2 = bg_color;
cmd.loop = gint_image_rgb16_swapcolor;

View File

@ -1,5 +1,6 @@
#include <gint/defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/util.h>
#include <gint/display.h>
#include <string.h>
@ -8,9 +9,16 @@
#include "topti-asm.h"
/* Default font */
extern font_t gint_font8x9;
font_t const * gint_default_font = &gint_font8x9;
font_t const * topti_font = &gint_font8x9;
#if(FX9860G_AS_CG)
extern font_t gint_font5x7;
font_t const * gint_default_font = &gint_font5x7;
font_t const * topti_font = &gint_font5x7;
#else
extern font_t gint_font8x9;
font_t const * gint_default_font = &gint_font8x9;
font_t const * topti_font = &gint_font8x9;
#endif
/* topti_glyph(): Render a glyph on the VRAM
Prints a glyph naively using word accesses, because for most fonts with a
@ -56,12 +64,20 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
int height = f->data_height, top = 0;
/* Vertical clipping */
if(x > 395 || y > 223 || y + height <= 0) return;
if(y + height > 224) height = 224 - y;
if(y < 0) top = -y, height += y, y = 0;
if(x >= dwindow.right || y >= dwindow.bottom) return;
if(y + height <= dwindow.top) return;
int top_overflow = y - dwindow.top;
if(top_overflow < 0) {
top = -top_overflow;
height += top_overflow;
y -= top_overflow;
}
height = min(height, dwindow.bottom - y);
if(height <= 0) return;
/* Move to top row */
uint16_t *target = gint_vram + 396 * y;
uint16_t *target = gint_vram + DWIDTH * y;
/* Character spacing waiting to be drawn, in pixels */
int space = 0;
@ -80,7 +96,7 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
/* Draw character spacing if background is opaque */
if(space && bg >= 0) drect(x, y, x+space-1, y+height-1, bg);
x += space;
if(x >= 396) break;
if(x >= dwindow.right) break;
int index = topti_offset(f, glyph);
@ -88,14 +104,17 @@ static void topti_render(int x, int y, char const *str_char, font_t const *f,
int width = dataw, left = 0;
if(x + dataw <= 0)
if(x + dataw <= dwindow.left)
{
x += dataw;
space = f->char_spacing;
continue;
}
if(x < 0) left = -x, width += x;
if(x + width > 396) width = 396 - x;
if(x < dwindow.left) {
left = dwindow.left - x;
width -= left;
}
width = min(width, dwindow.right - x);
/* Render glyph */

View File

@ -1,4 +1,5 @@
#include <gint/defs/types.h>
#include <gint/defs/util.h>
#include <gint/display.h>
#include "render-fx.h"
#include "bopti-asm.h"
@ -271,18 +272,29 @@ int bopti_clip(bopti_image_t const *img, struct rbox *r)
int x = r->visual_x, y = r->y;
int left = r->left, top = r->top;
int width = r->width, height = r->height;
int diff;
/* Adjust the bounding box of the input image */
if(left < 0) width += left, x -= left, left = 0;
if(top < 0) height += top, y -= top, top = 0;
if(left + width > img->width) width = img->width - left;
if(top + height > img->height) height = img->height - top;
width = min(width, img->width - left);
height = min(height, img->height - top);
/* Intersect with the bounding box on-screen */
if(x < 0) width += x, left -= x, x = 0;
if(y < 0) height += y, top -= y, y = 0;
if(x + width > DWIDTH) width = DWIDTH - x;
if(y + height > DHEIGHT) height = DHEIGHT - y;
if((diff = dwindow.left - x) > 0)
{
width -= diff;
left += diff;
x += diff;
}
if((diff = dwindow.top - y) > 0)
{
height -= diff;
top += diff;
y += diff;
}
width = min(width, dwindow.right - x);
height = min(height, dwindow.bottom - y);
r->visual_x = x;
r->y = y;

View File

@ -4,6 +4,12 @@
/* dclear() - fill the screen with a single color */
void dclear(color_t color)
{
if(dwindow.left != 0 || dwindow.right != DWIDTH) {
drect(dwindow.left, dwindow.top, dwindow.right - 1,
dwindow.bottom - 1, color);
return;
}
DMODE_OVERRIDE(dclear, color);
/* SuperH only supports a single write-move addressing mode, which is
@ -13,9 +19,10 @@ void dclear(color_t color)
if(color != C_WHITE && color != C_BLACK) return;
uint32_t fill = -(color >> 1);
uint32_t *index = gint_vram + 256;
uint32_t *start = gint_vram + 4 * dwindow.top;
uint32_t *index = gint_vram + 4 * dwindow.bottom;
while(index > gint_vram)
while(index > start)
{
/* Do it by batches to avoid losing cycles on loop tests */
*--index = fill;

16
src/render-fx/dgetpixel.c Normal file
View File

@ -0,0 +1,16 @@
#include <gint/display.h>
#include <gint/defs/types.h>
#include "render-fx.h"
int dgetpixel(int x, int y)
{
if(x < dwindow.left || x >= dwindow.right) return -1;
if(y < dwindow.top || y >= dwindow.bottom) return -1;
DMODE_OVERRIDE(dgetpixel, x, y);
int offset = (y << 2) + (x >> 5);
uint32_t mask = 1 << (~x & 31);
return (gint_vram[offset] & mask) ? C_BLACK : C_WHITE;
}

View File

@ -5,8 +5,8 @@
/* dpixel() - change a pixel's color */
void dpixel(int x, int y, int color)
{
/* Sanity checks */
if((uint)x >= 128 || (uint)y >= 64) return;
if(x < dwindow.left || x >= dwindow.right) return;
if(y < dwindow.top || y >= dwindow.bottom) return;
DMODE_OVERRIDE(dpixel, x, y, color);

View File

@ -2,18 +2,19 @@
#include <gint/display.h>
#include "render-fx.h"
/* drect() - fill a rectangle of the screen */
void drect(int x1, int y1, int x2, int y2, int color)
{
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
/* Argument checking */
if(x1 >= 128 || x2 < 0 || y1 >= 64 || y2 < 0) return;
if(x1 < 0) x1 = 0;
if(x2 >= 128) x2 = 127;
if(y1 < 0) y1 = 0;
if(y2 >= 64) y2 = 63;
/* Rectangle is completely outside the rendering window */
if(x1 >= dwindow.right || x2 < dwindow.left) return;
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
/* Clipping */
x1 = max(x1, dwindow.left);
x2 = min(x2, dwindow.right - 1);
y1 = max(y1, dwindow.top);
y2 = min(y2, dwindow.bottom - 1);
DMODE_OVERRIDE(drect, x1, y1, x2, y2, color);

View File

@ -28,6 +28,7 @@ void dupdate(void)
t6k11_display(gint_vram, 0, 64, 16);
}
gint_call_t hook = dupdate_get_hook();
if(hook.function) gint_call(hook);
gint_call(dupdate_get_hook());
}
__attribute__((alias("dupdate")))
void _WEAK_dupdate(void);

View File

@ -5,9 +5,9 @@
/* gint_dhline(): Optimized horizontal line using a rectangle mask */
void gint_dhline(int x1, int x2, int y, int color)
{
if((uint)y >= 64) return;
if(y < dwindow.top || y >= dwindow.bottom) return;
if(x1 > x2) swap(x1, x2);
if(x1 >= 128 || x2 < 0) return;
if(x1 >= dwindow.right || x2 < dwindow.left) return;
/* Get the masks for the [x1, x2] range */
uint32_t m[4];
@ -41,11 +41,11 @@ void gint_dhline(int x1, int x2, int y, int color)
/* gint_dvline(): Optimized vertical line */
void gint_dvline(int y1, int y2, int x, int color)
{
if((uint)x >= 128) return;
if(x < dwindow.left || x >= dwindow.right) return;
if(y1 > y2) swap(y1, y2);
if(y1 >= 64 || y2 < 0) return;
if(y1 < 0) y1 = 0;
if(y2 >= 64) y2 = 63;
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
y1 = max(y1, dwindow.top);
y2 = min(y2, dwindow.bottom - 1);
uint32_t *base = gint_vram + (y1 << 2) + (x >> 5);
uint32_t *lword = base + ((y2 - y1 + 1) << 2);

View File

@ -1,28 +1,24 @@
#include <gint/defs/util.h>
#include "render-fx.h"
/* masks() - compute the vram masks for a given rectangle */
void masks(int x1, int x2, uint32_t *masks)
{
if(x1 < 0) x1 = 0;
if(x2 >= 128) x2 = 127;
x1 = max(x1, dwindow.left);
x2 = min(x2, dwindow.right - 1);
/* Indexes of the first and last non-empty longs */
size_t l1 = x1 >> 5;
size_t l2 = x2 >> 5;
size_t i = 0;
/* Base masks (0's are final, 0xffffffff will be adjusted later) */
while(i < l1) masks[i++] = 0x00000000;
while(i <= l2) masks[i++] = 0xffffffff;
while(i < 4) masks[i++] = 0x00000000;
/* Remove the index information in x1 and x2 (it's now in l1 and l2)
and keep only the offsets */
/* Remove the index information in x1 and x2; keep only the offsets */
x1 &= 31;
/* For x2 we also want the complement to 31 to invert the shift */
x2 = ~x2 & 31;
/* Now roll! */
masks[l1] &= (0xffffffffu >> x1);
masks[l2] &= (0xffffffffu << x2);
}

View File

@ -69,6 +69,7 @@ struct rendering_mode
void (*drect)(int x1, int y1, int x2, int y2, color_t color);
/* Point rendering */
void (*dpixel)(int x, int y, color_t color);
int (*dgetpixel)(int x, int y);
void (*gint_dhline)(int x1, int x2, int y, int color);
void (*gint_dvline)(int y1, int y2, int x, int color);
/* Text and image rendering */
@ -87,6 +88,7 @@ int gupdate(void);
void gclear(color_t color);
void grect(int x1, int y1, int x2, int y2, color_t color);
void gpixel(int x, int y, color_t color);
int ggetpixel(int x, int y);
void gint_ghline(int x1, int x2, int y, int color);
void gint_gvline(int y1, int y2, int x, int color);
void gtext_opt(int x, int y, int fg, int bg, int halign, int valign,
@ -94,10 +96,8 @@ void gtext_opt(int x, int y, int fg, int bg, int halign, int valign,
void gsubimage(bopti_image_t const *image, struct rbox *r, int flags);
/* Short macro to call the alternate rendering function when available */
#define DMODE_OVERRIDE(func, ...) \
if(dmode && dmode->func) { \
dmode->func(__VA_ARGS__); \
return; \
#define DMODE_OVERRIDE(func, ...) \
if(dmode && dmode->func) { \
return dmode->func(__VA_ARGS__); \
}
#endif /* RENDER_FX */

View File

@ -1,5 +1,6 @@
#include <gint/defs/types.h>
#include <gint/defs/attributes.h>
#include <gint/defs/util.h>
#include <gint/display.h>
#include "../render/render.h"
@ -97,12 +98,21 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
uint32_t const *data = f->data;
/* Basic clipping */
if(x > 127 || y > 63 || y + height <= 0) return;
if(y + height > 64) height = 64 - y;
if(x >= dwindow.right || y >= dwindow.bottom) return;
if(y + height <= dwindow.top) return;
height = min(height, dwindow.bottom - y);
/* How much we need to skip vertically if we render text at y < 0 */
/* How much we skip vertically if we render at y < dwindow.top */
int vdisp = 0;
if(y < 0) vdisp = -y, y = 0;
if(y < dwindow.top)
{
vdisp = dwindow.top - y;
y = dwindow.top;
}
if(vdisp >= height) return;
uint32_t bg_mask[4];
masks(dwindow.left, dwindow.right, bg_mask);
/* Operator data and background */
uint32_t operators[height];
@ -146,6 +156,12 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
if(x >= 0)
{
for(int i = 0; i < height; i++)
{
operators[i] &= bg_mask[x];
bg[i] &= bg_mask[x];
}
asm_bg(v1, v2, bg + vdisp, height - vdisp);
asm_fg(v1, v2, operators + vdisp, height - vdisp);
}
@ -171,7 +187,11 @@ void topti_render(int x, int y, char const *str_char, font_t const *f,
/* Put the final longwords */
if(x >= 0 && x < 4 && free < 32)
{
for(int i = 0; i < height; i++) bg[i] &= ~((1 << free) - 1);
for(int i = 0; i < height; i++)
{
operators[i] &= bg_mask[x];
bg[i] &= bg_mask[x] & ~((1 << free) - 1);
}
asm_bg(v1, v2, bg + vdisp, height - vdisp);
asm_fg(v1, v2, operators + vdisp, height - vdisp);
}

137
src/render-fxascg/dupdate.c Normal file
View File

@ -0,0 +1,137 @@
#include <gint/display.h>
#include <gint/kmalloc.h>
#include <gint/config.h>
#include <gint/drivers/r61524.h>
#include "../render-fx/render-fx.h"
/* The destination VRAM for upscaling + centering + conversion B&W --> 16bits RGB565 */
uint16_t *cg_vram; // set to 0 at initialisation
/* Standard video RAM for fx9860g is 1 bit per pixel */
GALIGNED(32) static uint32_t fx_vram[256];
/* Here is the definition of the VRAM pointer, exposed in <gint/display.h> */
uint32_t *gint_vram = fx_vram;
/* The current rendering mode */
struct rendering_mode const *dmode = NULL;
bool dvram_init( void )
{
int const MARGIN = 32;
/* Leave MARGIN bytes on each side of the region; this enables some
important optimizations in the image renderer. We also add another
32 bytes so we can manually 32-align the region */
uint32_t region = (uint32_t)kmalloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32,
#if !defined(GINT_NO_OS_STACK)
"_ostk"
#else
NULL
#endif
);
if(region == 0)
return false;
/* 32-align the region */
region = (region + 31) & -32;
/* Skip a MARGIN */
region += MARGIN;
/* Use an uncached address */
region = (region & 0x1fffffff) | 0xa0000000;
/* Don't enable triple buffering by default */
cg_vram = (void *)region;
return true;
}
void dgetvram(uint16_t **ptr_vram_1, uint16_t **ptr_vram_2)
{
if(ptr_vram_1) *ptr_vram_1 = &cg_vram;
if(ptr_vram_2) *ptr_vram_2 = &cg_vram;
}
inline drawupscale( int x, int y, int color )
{
int u=y*396*3;
int v=x*3;
uint16_t colorcg;
if (color==C_WHITE) colorcg=0xFFFF;
else colorcg=0x0000;
int baseindex = (396*16+6+v+u); // 16 lines on top/bottom remain black and 6 columns on left/right remain black
cg_vram[baseindex] = colorcg;
cg_vram[baseindex+1] = colorcg;
cg_vram[baseindex+2] = colorcg;
baseindex+=396;
cg_vram[baseindex] = colorcg;
cg_vram[baseindex+1] = colorcg;
cg_vram[baseindex+2] = colorcg;
baseindex+=396;
cg_vram[baseindex] = colorcg;
cg_vram[baseindex+1] = colorcg;
cg_vram[baseindex+2] = colorcg;
}
/* dupdate(): Push the video RAM to the display driver */
void dupdate(void)
{
bool run_default = true;
if(dmode && dmode->dupdate)
{
/* Call the overridden dupdate(), but continue if it returns
non-zero (this is used when stopping the gray engine) */
int rc = dmode->dupdate();
run_default = (rc != 0);
}
if(run_default)
{
for( int j=0; j<DHEIGHT; j++ ) // 64 lines
{
for( int i=0; i<DWIDTH; i++ ) // 128 column
{
drawupscale( i, j, dgetpixel(i,j) ); // really not optimised; just to check if OK
}
}
//draw a black layer around the screen
int base1 = 0;
int base2 = (64*3+16)*396;
for(int u = 0; u<16; u++)
{
for(int v = 0; v<396; v++)
{
cg_vram[base1+v] = 0;
cg_vram[base2+v] = 0;
}
base1+=396;
base2+=396;
}
base1 = 16*396;
base2 = 16*396+6+128*3;
for(int u = 0; u<64*3; u++)
{
for(int v = 0; v<6; v++)
{
cg_vram[base1+v] = 0;
cg_vram[base2+v] = 0;
}
base1+=396;
base2+=396;
}
r61524_display(cg_vram, 0, 224, R61524_DMA_WAIT );
}
gint_call(dupdate_get_hook());
}
__attribute__((alias("dupdate")))
void _WEAK_dupdate(void);

View File

@ -0,0 +1,340 @@
//---
// gint:gray:engine - Core gray engine
//---
#include <gint/defs/types.h>
#include <gint/drivers/r61524.h>
#include <gint/gray.h>
#include <gint/display.h>
#include <stdlib.h>
#include "../render-fx/render-fx.h"
/* Three additional video RAMS, allocated statically if --static-gray was set
at configure time, or with malloc() otherwise. */
#ifdef GINT_STATIC_GRAY
GBSS static uint32_t gvrams[1][256];
#endif
uint16_t *cg_vram_gray;
/* two VRAMs: two to draw and two to display */
static uint32_t *vrams[2] = { NULL, NULL };
/* Whether the engine is scheduled to run at the next frame */
static int runs = 0;
extern struct rendering_mode const *dmode;
/* The alternate rendering mode structure used to override d*() */
static struct rendering_mode const gray_mode = {
.dupdate = gupdate,
.dclear = gclear,
.drect = grect,
.dpixel = gpixel,
.dgetpixel = ggetpixel,
.gint_dhline = gint_ghline,
.gint_dvline = gint_gvline,
.dtext_opt = gtext_opt,
.dsubimage = gsubimage,
};
static struct rendering_mode const gray_exit_mode = {
.dupdate = gupdate,
.dclear = NULL,
.drect = NULL,
.dpixel = NULL,
.dgetpixel = NULL,
.gint_dhline = NULL,
.gint_dvline = NULL,
.dtext_opt = NULL,
.dsubimage = NULL,
};
//---
// Engine control (init/quit and start/stop)
//---
//static int gray_int(void);
static void gray_quit(void);
/* gray_isinit(): Check whether the engine is initialized and ready to run */
static int gray_isinit(void)
{
//return (vrams[0] && vrams[1] && vrams[2] && vrams[3] && timer >= 0);
return (vrams[0] && vrams[1] >= 0);
}
bool dvram_init_gray( void )
{
int const MARGIN = 32;
/* Leave MARGIN bytes on each side of the region; this enables some
important optimizations in the image renderer. We also add another
32 bytes so we can manually 32-align the region */
uint32_t region = (uint32_t)kmalloc(DWIDTH*DHEIGHT*2 + MARGIN*2 + 32,
#if !defined(GINT_NO_OS_STACK)
"_ostk"
#else
NULL
#endif
);
if(region == 0)
return false;
/* 32-align the region */
region = (region + 31) & -32;
/* Skip a MARGIN */
region += MARGIN;
/* Use an uncached address */
region = (region & 0x1fffffff) | 0xa0000000;
/* Don't enable triple buffering by default */
cg_vram_gray = (void *)region;
return true;
}
/* gray_init(): Initialize the engine
This is done at startup so that memory can be reserved very early from the
heap (because not having enough memory is unrecoverable for the engine). */
GCONSTRUCTOR static void gray_init(void)
{
/* We need four VRAMs. First use the standard monochrome one */
vrams[0] = gint_vram;
#ifdef GINT_STATIC_GRAY
vrams[1] = gvrams[0];
#else
vrams[1] = malloc(1024);
#endif /* GINT_STATIC_GRAY */
dvram_init_gray();
/* On failure, release the resources that we obtained */
if(!gray_isinit()) gray_quit();
}
/* gray_quit(): Free engine resources */
GDESTRUCTOR static void gray_quit(void)
{
#ifndef GINT_STATIC_GRAY
if(vrams[1]) free(vrams[1]);
vrams[1] = NULL;
#endif /* GINT_STATIC_GRAY */
}
/* gray_start(): Start the gray engine */
static void gray_start(void)
{
runs = 1;
}
/* gray_stop(): Stop the gray engine */
static void gray_stop(void)
{
runs = 0;
}
//---
// Dynamic udpate and rendering mode
//---
/* dgray(): Start or stop the gray engine at the next dupdate() */
int dgray(int mode)
{
/* Stack of states for the push modes */
static uint8_t states[32] = { 0 };
static uint8_t current = 0;
if(mode == DGRAY_ON)
{
if(!gray_isinit()) return 1;
/* Set the display module's alternate rendering mode to
override rendering functions to use their g*() variant */
if(!dgray_enabled()) dmode = &gray_mode;
}
else if(mode == DGRAY_OFF)
{
/* Set the mode to a temporary one that only overrides
dupdate() so that we can stop the engine next frame */
if(dgray_enabled()) dmode = &gray_exit_mode;
}
else if(mode == DGRAY_PUSH_ON)
{
if(current >= 32) return 1;
states[current++] = dgray_enabled() ? DGRAY_ON : DGRAY_OFF;
return dgray(DGRAY_ON);
}
else if(mode == DGRAY_PUSH_OFF)
{
if(current >= 32) return 1;
states[current++] = dgray_enabled() ? DGRAY_ON : DGRAY_OFF;
return dgray(DGRAY_OFF);
}
else if(mode == DGRAY_POP)
{
/* Stay at 0 if the user's push/pop logic is broken */
if(current > 0) current--;
/* Switch to previous state */
return dgray(states[current]);
}
else return 1;
return 0;
}
/* convert the gray scale into RGB565 and draw in the virutal VRAMthe 3x3 pixels upscaled and centered */
inline gdrawupscale( int x, int y, int color_fx )
{
int u=y*396*3;
int v=x*3;
uint16_t color_cg;
if (color_fx==C_WHITE) color_cg=0xFFFF;
else if (color_fx==C_LIGHT) color_cg=0xAD55;
else if (color_fx==C_DARK) color_cg=0x528A;
else color_cg=0x0000;
int baseindex = (396*16+6+v+u); // 16 lines on top/bottom remain black and 6 columns on left/right remain black
cg_vram_gray[baseindex] = color_cg; // draw 3 pixels side by side
cg_vram_gray[baseindex+1] = color_cg;
cg_vram_gray[baseindex+2] = color_cg;
baseindex+=396; // same for the line below (line #2)
cg_vram_gray[baseindex] = color_cg;
cg_vram_gray[baseindex+1] = color_cg;
cg_vram_gray[baseindex+2] = color_cg;
baseindex+=396; // same for the line below (line #3)
cg_vram_gray[baseindex] = color_cg;
cg_vram_gray[baseindex+1] = color_cg;
cg_vram_gray[baseindex+2] = color_cg;
}
/* gray_int(): Interrupt handler */
//int gray_int(void)
//{
// //t6k11_display(vrams[st ^ 2], 0, 64, 16);
// timer_reload(GRAY_TIMER, delays[(st ^ 3) & 1]);
//
// st ^= 1;
//
// return TIMER_CONTINUE;
//}
/* gupdate(): Push the current VRAMs to the screen */
int gupdate(void)
{
/* At the first gupdate(), start the engine */
if(dmode == &gray_mode && !runs)
{
gray_start();
}
/* At the last gupdate(), stop the engine */
else if(dmode == &gray_exit_mode)
{
gray_stop();
dmode = NULL;
return 1;
}
uint32_t *light, *dark;
dgray_getscreen(&light, &dark);
for( int j=0; j<DHEIGHT; j++ ) // 64 lines
{
for( int i=0; i<DWIDTH; i++ ) // 128 column
{
int offset = (j << 2) + (i >> 5);
uint32_t mask = 1 << (~i & 31);
int l = (light[offset] & mask) !=0 ? 1 : 0;
int d = (dark [offset] & mask) !=0 ? 1 : 0;
int color_fx = (d << 1) | l;
gdrawupscale( i, j, color_fx ); // really not optimised; just to check if OK
}
}
//draw a black layer around the screen
int base1 = 0;
int base2 = (64*3+16)*396;
for(int u = 0; u<16; u++)
{
for(int v = 0; v<396; v++)
{
cg_vram_gray[base1+v] = 0;
cg_vram_gray[base2+v] = 0;
}
base1+=396;
base2+=396;
}
base1 = 16*396;
base2 = 16*396+6+128*3;
for(int u = 0; u<64*3; u++)
{
for(int v = 0; v<6; v++)
{
cg_vram_gray[base1+v] = 0;
cg_vram_gray[base2+v] = 0;
}
base1+=396;
base2+=396;
}
r61524_display(cg_vram_gray, 0, 224, R61524_DMA_WAIT );
/* When the engine is running, swap frames */
return 0;
}
//---
// Query and configuration functions
//---
/* dgray_enabled(): Check whether gray mode is enabled */
int dgray_enabled(void)
{
return (dmode == &gray_mode);
}
/* dgray_setdelays(): Set the gray engine delays */
void dgray_setdelays(uint32_t light, uint32_t dark)
{
// delays[0] = light;
// delays[1] = dark;
}
/* dgray_getdelays(): Get the gray engine delays */
void dgray_getdelays(uint32_t *light, uint32_t *dark)
{
// if(light) *light = delays[0];
// if(dark) *dark = delays[1];
}
/* dgray_getvram(): Get the current VRAM pointers */
void dgray_getvram(uint32_t **light, uint32_t **dark)
{
if(light) *light = vrams[0];
if(dark) *dark = vrams[1];
}
/* dgray_getscreen(): Get the current screen pointers */
void dgray_getscreen(uint32_t **light, uint32_t **dark)
{
if(light) *light = vrams[0];
if(dark) *dark = vrams[1];
}

116
src/render/dellipse.c Normal file
View File

@ -0,0 +1,116 @@
#include <gint/display.h>
#define abs(x) ((x)>=0 ? (x) : (-x))
void dellipse(int xm, int ym, int a, int b, int c) {
long x = -a, y = 0; /* II. quadrant from bottom left to top right */
long e2 = b, dx = (1 + 2 * x) * e2 * e2; /* error increment */
long dy = x * x, err = dx + dy; /* error of 1.step */
long a2 = (long)a * a;
long b2 = (long)b * b;
do {
dpixel(xm - x, ym + y, c); /* I. Quadrant */
dpixel(xm + x, ym + y, c); /* II. Quadrant */
dpixel(xm + x, ym - y, c); /* III. Quadrant */
dpixel(xm - x, ym - y, c); /* IV. Quadrant */
e2 = 2 * err;
if (e2 >= dx) {
x++;
err += dx += 2 * b2;
} /* x step */
if (e2 <= dy) {
y++;
err += dy += 2 * a2;
} /* y step */
} while (x <= 0);
while (y++ < b) { /* to early stop for flat ellipses with a=1, */
dpixel(xm, ym + y, c); /* -> finish tip of ellipse */
dpixel(xm, ym - y, c);
}
}
void dellipserect(int x0, int y0, int x1, int y1,
int c) { /* rectangular parameter enclosing the ellipse */
long a = abs(x1 - x0), b = abs(y1 - y0), b1 = b & 1; /* diameter */
double dx = 4 * (1.0 - a) * b * b,
dy = 4 * (b1 + 1) * a * a; /* error increment */
double err = dx + dy + b1 * a * a, e2; /* error of 1.step */
if (x0 > x1) {
x0 = x1;
x1 += a;
} /* if called with swapped points */
if (y0 > y1)
y0 = y1; /* .. exchange them */
y0 += (b + 1) / 2;
y1 = y0-b1; /* starting pixel */
a = 8 * a * a;
b1 = 8 * b * b;
do {
dpixel(x1, y0, c); /* I. Quadrant */
dpixel(x0, y0, c); /* II. Quadrant */
dpixel(x0, y1, c); /* III. Quadrant */
dpixel(x1, y1, c); /* IV. Quadrant */
e2 = 2 * err;
if (e2 <= dy) {
y0++;
y1--;
err += dy += a;
} /* y step */
if (e2 >= dx || 2 * err > dy) {
x0++;
x1--;
err += dx += b1;
} /* x */
} while (x0 <= x1);
while (y0 - y1 <= b) { /* to early stop of flat ellipses a=1 */
dpixel(x0 - 1, y0, c); /* -> finish tip of ellipse */
dpixel(x1 + 1, y0++, c);
dpixel(x0 - 1, y1, c);
dpixel(x1 + 1, y1--, c);
}
}
void dcircle(int xm, int ym, int r, int c)
{
dellipse( xm, ym, r, r, c);
}
void dellipse_fill(int xm, int ym, int a, int b, int cback, int cborder) {
long x = -a, y = 0; /* II. quadrant from bottom left to top right */
long e2 = b, dx = (1 + 2 * x) * e2 * e2; /* error increment */
long dy = x * x, err = dx + dy; /* error of 1.step */
long a2 = (long)a * a;
long b2 = (long)b * b;
do {
dline(xm - x, ym + y, xm + x, ym + y, cback );
dpixel(xm - x, ym + y, cborder); /* I. Quadrant */
dpixel(xm + x, ym + y, cborder); /* II. Quadrant */
dline(xm - x, ym - y, xm + x, ym - y, cback );
dpixel(xm + x, ym - y, cborder); /* III. Quadrant */
dpixel(xm - x, ym - y, cborder); /* IV. Quadrant */
e2 = 2 * err;
if (e2 >= dx) {
x++;
err += dx += 2 * b2;
} /* x step */
if (e2 <= dy) {
y++;
err += dy += 2 * a2;
} /* y step */
} while (x <= 0);
while (y++ < b) { /* to early stop for flat ellipses with a=1, */
dpixel(xm, ym + y, cborder); /* -> finish tip of ellipse */
dpixel(xm, ym - y, cborder);
}
}
void dcircle_fill(int xm, int ym, int r, int cback, int cborder)
{
dellipse_fill( xm, ym, r, r, cback, cborder );
}

View File

@ -3,5 +3,5 @@
/* dhline(): Full-width horizontal line */
void dhline(int y, int color)
{
dline(0, y, DWIDTH - 1, y, color);
dline(dwindow.left, y, dwindow.right - 1, y, color);
}

View File

@ -2,7 +2,7 @@
#include <gint/defs/util.h>
#include "../render/render.h"
#ifdef FX9860G
#if defined(FX9860G) || defined(FX9860G_AS_CG)
#include "../render-fx/render-fx.h"
#endif
@ -18,7 +18,7 @@ void dline(int x1, int y1, int x2, int y2, int color)
/* Possible optimizations */
if(y1 == y2)
{
#ifdef FX9860G
#if defined(FX9860G) || defined(FX9860G_AS_CG)
DMODE_OVERRIDE(gint_dhline, x1, x2, y1, color);
#endif
@ -27,7 +27,7 @@ void dline(int x1, int y1, int x2, int y2, int color)
}
if(x1 == x2)
{
#ifdef FX9860G
#if defined(FX9860G) || defined(FX9860G_AS_CG)
DMODE_OVERRIDE(gint_dvline, y1, y2, x1, color);
#endif

View File

@ -8,7 +8,8 @@ void drect_border(int x1, int y1, int x2, int y2, int fill, int width,
if(x1 > x2) swap(x1, x2);
if(y1 > y2) swap(y1, y2);
if(x1 >= DWIDTH || x2 < 0 || y1 >= DHEIGHT || y2 < 0) return;
if(x1 >= dwindow.right || x2 < dwindow.left) return;
if(y1 >= dwindow.bottom || y2 < dwindow.top) return;
drect(x1, y1, x2, y1 + (width-1), border);
drect(x1, y2 - (width-1), x2, y2, border);

View File

@ -3,5 +3,5 @@
/* dvline(): Full-height vertical line */
void dvline(int x, int color)
{
dline(x, 0, x, DHEIGHT - 1, color);
dline(x, dwindow.top, x, dwindow.bottom - 1, color);
}

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