I'm pretty sure it makes no difference because the OS does not rely on
interrupts for most (if not all) of its DMA operations, but it's better
to keep it clean anyway.
* Mark SPU memory as sleep-blocking.
* Perform 4-byte accesses only in dma_memset() and dma_memcpy() (32-byte
accesses freeze as one would expect).
This change does *NOT* implement support for SPU's integrated DMAC.
Nothing particular to change, simply make sure that the DMA channels
have higher priority than the USB module, otherwise the BEMP interrupt
might be executed before the DMA frees the channel, resulting in the
transfer failing because the channel is still busy.
Also reduce BUSWAIT since it works even on high overclock levels, and
keeping it high won't help increase performance.
This change introduces new sleep_block() and sleep_unblock() functions
that control whether the sleep() function actually sleeps. This type of
behavior was already implemented in the DMA driver, since DMA access to
on-chip memory is paused when sleeping (on-chip memory being paused
itself), which would make waiting for a DMA transfer a freeze.
Because DMA transfers are now asynchronous, and USB transfers that may
involve on-chip memory are coming, this API change allows the DMA and
USB drivers to block the sleep() function so that user code can sleep()
for interrupts without having to worry about asynchronous tasks
requiring on-chip memory to complete.
This change adds asynchronous capabilities to the DMA API. Previously,
transfers would start asynchronously but could only be completed by a
call to dma_transfer_wait(). The API now supports a callback, as well
as the dma_transfer_sync() variant, to be consistent with the upcoming
USB API that has both _sync and _async versions of functions.
The interrupt handler of the DMA was changed to include a return to
userland, which is required to perform the callback.
* dma_transfer() is now an obsolete synonym for dma_transfer_async()
with no callback.
* dma_transfer_noint() is now a synonym for dma_transfer_atomic(), for
consistency with the upcoming USB API.
Changes in the driver and world system:
* Rewrite driver logic to include more advanced concepts. The notion of
binding a driver to a device is introduced to formalize wait(); power
management is now built-in instead of being handled by the drivers
(for instance DMA). The new driver model is described in great detail
in <gint/drivers.h>
* Formalized the concept of "world switch" where the hardware state is
saved and later restored. As a tool, the world switch turns out to be
very stable, and allows a lot of hardware manipulation that would be
edgy at best when running in the OS world.
* Added a GINT_DRV_SHARED flag for drivers to specify that their state
is shared between worlds and not saved/restored. This has a couple of
uses.
* Exposed a lot more of the internal driver/world system as their is no
particular downside to it. This includes stuff in <gint/drivers.h>
and the driver's state structures in <gint/drivers/states.h>. This is
useful for debugging and for cracked concepts, but there is no
API stability guarantee.
* Added a more flexible driver level system that allows any 2-digit
level to be used.
Feature changes:
* Added a CPU driver that provides the VBR change as its state save.
Because the whole context switch relied on interrupts being disabled
anyway, there is no longer an inversion of control when setting the
VBR; this is just part of the CPU driver's configuration. The CPU
driver may also support other features such as XYRAM block transfer
in the future.
* Moved gint_inthandler() to the INTC driver under the name
intc_handler(), pairing up again with intc_priority().
* Added a reentrant atomic lock based on the test-and-set primitive.
Interrupts are disabled with IMASK=15 for the duration of atomic
operations.
* Enabled the DMA driver on SH7305-based fx-9860G. The DMA provides
little benefit on this platform because the RAM is generally faster
and buffers are ultimately small. The DMA is still not available on
SH3-based fx-9860G models.
* Solved an extremely obnoxious bug in timer_spin_wait() where the
timer is not freed, causing the callback to be called when interrupts
are re-enabled. This increments a random value on the stack. As a
consequence of the change, removed the long delays in the USB driver
since they are not actually needed.
Minor changes:
* Deprecated some of the elements in <gint/hardware.h>. There really is
no good way to "enumerate" devices yet.
* Deprecated gint_switch() in favor of a new function
gint_world_switch() which uses the GINT_CALL abstraction.
* Made the fx-9860G VRAM 32-aligned so that it can be used for tests
with the DMA.
Some features of the driver and world systems have not been implemented
yet, but may be in the future:
* Some driver flags should be per-world in order to create multiple
gint worlds. This would be useful in Yatis' hypervisor.
* A GINT_DRV_LAZY flag would be useful for drivers that don't want to
be started up automatically during a world switch. This is relevant
for drivers that have a slow start/stop sequence. However, this is
tricky to do correctly as it requires dynamic start/stop and also
tracking which world the current hardware state belongs to.
* Removed .pretext sections since the TLB is now entirely dynamic; left
only .text.entry for the start symbol.
* Reworked the main files of src/core to move the INTC to its own driver
and let the kernel handle only VBR space and CPU (now: VBR & CPUOPM).
* Moved src/core/gint.c to src/core/kernel.c and centralized all driver
loops that save or restore context for more robustness. This leaves
the callbacks of cpu_setVBR() (formerly gint_setvbr()) pretty short.
* Coalesced gint_switch_out() and gint_switch_in() into a single
function callback for cpu_setVBR().
* Added an abstraction of interrupt signals as an enumerated value so
that drivers no longer hardcode the IPR and IMR numbers and bits,
sometimes even with isSH3() due to differences in the SH7705.
* Changed the interrupt blocking method in cpu_setVBR() from SR.BL=1 to
SR.IMASK=15 so that TLB misses are still handled. This removes the
need for callback functions to be GMAPPED.
* Moved gint_osmenu() and its utilities to a new file src/core/osmenu.c.
This commit improves the stability of gint_switch() in two ways:
1. Wait for hardware availability every time driver contexts are saved
or reloaded; this solves crashes due to DMA use when gint takes
control after a BFile call, since BFile_Create() (and possibly
BFile_Write()) leave the DMA running after returning.
2. Remap the add-in after a switch, as apparently calling BFile
functions causes some pages to be evicted. This is more noticeable on
fxcg50 when the size of add-ins nears 220k.
Additionally, dma_transfer_wait() has been updated to not sleep() unless
it is certain that the conditions for wakeup are fulfilled, as this
would sometimes freeze.
The unload() function is not very relevant for drivers because hardware
state is managed by ctx_save() and ctx_restore() and software state is
managed by underlying drivers when there are dependencies.
For now, it's been replaced with a wait() function that allows drivers
to not be interrupted at any point. It is currently used by the DMA to
wait for ongoing transfers to finish before disabling interrupts (which
would prevent the transfer end from being detected) and switching in and
out of gint.
* Add the gint_switch() function which executes user-provided code from
the system (CASIOWIN) context.
* Added interrupt masks to the core context (should have been there long
ago).
* Added the gint_osmenu() function that switches out of gint to invoke
GetKeyWait() and inject KEY_CTRL_MENU to trigger the main menu. This
uses many CASIOWIN syscalls, but we don't care because gint is unloaded.
Trickery is used to catch the key following the return in the add-in
and/or display a new application frame before GetKeyWait() even finishes
after coming back. This is only available on fx9860g for now.
* Removed any public syscall definition to clear up interfaces.
* Patched the DMA interruption problem in a weird way on fxcg50, a
driver function will be used to do that properly eventually.
* Changed the driver model to save driver contexts in preallocated
spaces instead of on the stack for overall less risk.
* Enabled return-to-menu with the MENU key on fx9860g in getkey().
* Changed the keyboard driver to emit releases before presses, as a
return-to-menu acts as a press+release of different keys in a single
driver frame, which confuses getkey().
* Fixed a really stupid bug in memcpy() that made the function really
not work.
Improvements in the timer driver:
* Expose ETMU modules as SH7705_TMU and SH7305_TMU in <gint/mpu/tmu.h>.
* Remove the timer_t structures, using SH*_ETMU and SH*_TMU instead.
Only interrupt gate entries are left hardcoded.
* Discovered that not only every write to the TCNT or TCR of an ETMU
takes about 1/32k of a second (hinting at registers being powered by
the same clock as the timer), but every write occuring while a previous
write is pending is *lost*. This led to terrible bugs when switching
ETMU contexts too fast in gint_switch().
* Removed an internal timer_address() function.
* Overall simplified the handling of timers and the initialization step.
This is an obvious requirement for the interrupt routine, which was
forgotten and only surfaced when I used a timer callback started with
multiplications in an innocent add-in. r0..r7 are saved automatically,
which leaves pr, gbr, mach et macl susceptible to corruption by the
interrupt handler.
t6k11: use the gint array for variant detection
r61524: use true triple buffering by default
display: define DWIDTH and DHEIGHT
display: add C_RGB(r,g,b) (0 ≤ r,g,b ≤ 31) [fxcg50]
The IL memory is unavailable when the processor goes to sleep, causing
any involved DMA transfer to stall. The dma_transfer_wait() normally
sleeps to save battery power, but this causes the whole system to freeze
and never wake up.
This change lets dma_transfer_wait() decide dynamically whether to sleep
or spinlock. There is no concrete improvement over dma_transfer_noint()
when using IL memory, but it makes dma_transfer() fully generic.
Obviously the same goes for X and Y memory.
This change introduces two new mechanismes for executing user code when
an exception occurs.
* This first is the custom panic message, which usually displays "System
ERROR". The function that performs this task can now be user-defined.
It is also run in user mode because the exception handler rte's into
it, allowing it to execute any kind of interrupt-inducing task. The
behavior is undefined if this function raises an exception.
* The second is an exception-catching function, which (when set) is
called every time an exception occurs, and is granted the chance of
handling the exception to continue execution normally. It can be used
in various ways, the most primitive of which is recording the
exception and going back. It runs in interrupt mode and must not raise
any kind of exception.
This change introduces exception handlers that default to a fatal error
with an on-screen description of the exception and some debugging
information.
It also adds the dprint() function as a definitely-needed helper and
removes bootlog_unmapped() by using the exception handler for the fatal
error display. (Also printf() is now required in all gint add-ins; this
is sad, but space is not as much of a constraint as debugging.)
Finally, the exception handler is used to handle an interrupt which is
an exception in practice, the DMA address error. On fx-CG 50, additional
DMA-related information is displayed on the screen. This is left out on
fx-9860G as there is not enough space.