core, tmu: add gint_switch(), return to menu, and improve timer code

* 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 commit is contained in:
Lephe 2020-05-10 14:03:41 +02:00
parent 2cdf925f94
commit 4485e7f865
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
22 changed files with 425 additions and 327 deletions

View file

@ -21,7 +21,7 @@ all-targets := $(foreach b,$(builds),all-$b)
all: $(all-targets) all: $(all-targets)
all-build%: build% all-build%: build%
@ echo -e "\n$B::$W Making into $<$N" @ echo -e "$B::$W Making into $<$N"
@ $(MAKE) --no-print-directory -C $< @ $(MAKE) --no-print-directory -C $<
# #
@ -33,7 +33,7 @@ install-targets := $(foreach b,$(builds),install-$b)
install: $(install-targets) install: $(install-targets)
install-build%: build% install-build%: build%
@ echo -e "\n$B::$W Installing from $<$N" @ echo -e "$B::$W Installing from $<$N"
@ $(MAKE) --no-print-directory -C $< install @ $(MAKE) --no-print-directory -C $< install
# #
@ -45,7 +45,7 @@ uninstall-targets := $(foreach b,$(builds),uninstall-$b)
uninstall: $(uninstall-targets) uninstall: $(uninstall-targets)
uninstall-build%: build% uninstall-build%: build%
@ echo -e "\n$B::$W Uninstalling from $<$N" @ echo -e "$B::$W Uninstalling from $<$N"
@ $(MAKE) --no-print-directory -C $< uninstall @ $(MAKE) --no-print-directory -C $< uninstall
# #

4
TODO
View file

@ -2,6 +2,7 @@ Crucial, missing things.
! core: the four basic memory functions ! core: the four basic memory functions
! core: gint_switch() with driver contexts on stack and arbitrary code ! core: gint_switch() with driver contexts on stack and arbitrary code
! core: use gint_switch() to handle TLB misses ! core: use gint_switch() to handle TLB misses
! core: return to menu on fxcg50
! bopti: fxcg50 version ! bopti: fxcg50 version
! syscalls: fxcg50 BFile calls ! syscalls: fxcg50 BFile calls
@ -12,10 +13,11 @@ Issues.
* #3 make drawing functions parameterized * #3 make drawing functions parameterized
* #5 add decent random number generation (TinyMT) * #5 add decent random number generation (TinyMT)
* #8 support fx-CG Manager * #8 support fx-CG Manager
* #9 add libimg
* #10 support fx-CG 20 * #10 support fx-CG 20
Complementary elements on existing code. Complementary elements on existing code.
* gray: double-buffer gray settings and unify d* with g*
* display: deprecate image_t and rename it bopti_image_t
* make fx9860g projects work out of the box on fxcg50 * make fx9860g projects work out of the box on fxcg50
* topti: support unicode fonts * topti: support unicode fonts
* gray: find good values for more models than the Graph 35+E II * gray: find good values for more models than the Graph 35+E II

View file

@ -5,6 +5,9 @@
#ifndef GINT_DEFS_UTIL #ifndef GINT_DEFS_UTIL
#define GINT_DEFS_UTIL #define GINT_DEFS_UTIL
/* synco instruction (in a form compatible with sh3eb-elf) */
#define synco() __asm__ volatile (".word 0x00ab":::"memory")
/* min(), max() (without double evaluation) */ /* min(), max() (without double evaluation) */
#define min(a, b) ({ \ #define min(a, b) ({ \
__auto_type _a = (a); \ __auto_type _a = (a); \

View file

@ -57,18 +57,13 @@ typedef struct
If there is no unload function, the field may be set to NULL */ If there is no unload function, the field may be set to NULL */
void (*unload)(void); void (*unload)(void);
/* Size of a context object for the driver */ /* System's context and gint's context. These should point to enough
uint ctx_size; memory to store a full driver state each. These are used when
switching from the system to gint and back to the main menu. If they
/* System context. The driver has to allocate a buffer of size at least don't need to be initialized, put them in gint's uninitialized BSS
ctx_size, where gint stores the system's configuration. It is section using the GBSS macro of <gint/defs/attributes.h>. */
advised to place this buffer in the .gint_bss section using the GBSS
macro of <defs/attributes.h> if it doesn't need to be initialized */
void *sys_ctx; void *sys_ctx;
void *gint_ctx;
/* Stack context. This is a gint *internal* pointer used when switching
in and out of the driver. Ignore it. */
void *_stack_ctx;
/* ctx_save() - save the driver's hardware support /* ctx_save() - save the driver's hardware support
This function is provided by the driver to save the state of its This function is provided by the driver to save the state of its

View file

@ -29,11 +29,26 @@ void gint_install(void);
/* gint_unload() - unload gint and give back control to the system /* gint_unload() - unload gint and give back control to the system
This function restores the runtime environment saved by gint_install(). It This function restores the runtime environment saved by gint_install(). It
is only called when the add-in terminates. To temporarily leave gint during is only called when the add-in terminates. To temporarily leave gint during
execution, use gint_pause(). When possible, use syscalls without leaving execution, use gint_switch(). When possible, use syscalls without leaving
gint for better performance. */ gint for better performance. */
void gint_unload(void); void gint_unload(void);
/* gint_pause() - return to main menu, with possibility of coming back /* gint_switch() - temporarily switch out of gint
This function can be used to leave gint, restore the system's driver
context, and execute code there before returning to gint. By doing this one
can effectively interleave gint with the standard OS execution. The
limitations are quite extreme though, so unless you know precisely why
you're calling this function, you're likely doing it wrong.
This switch is used to get back to the main menu and to answer TLB misses.
To go back to the menu, use getkey(), or getkey_opt() with the GETKEY_MENU
flag set, or call gint_osmenu() for an immediate return.
@function Function to call in OS mode */
void gint_switch(void (*function)(void));
/* gint_osmenu() - switch out of gint and call the calculator's main menu
This function safely invokes the calculator's main menu by unloading gint. This function safely invokes the calculator's main menu by unloading gint.
If the user selects the gint application again in the menu, this function If the user selects the gint application again in the menu, this function
@ -42,7 +57,7 @@ void gint_unload(void);
This function is typically called when the [MENU] key is pressed during a This function is typically called when the [MENU] key is pressed during a
getkey() call. */ getkey() call. */
void gint_pause(void); void gint_osmenu(void);
//--- //---
// Public functions // Public functions

View file

@ -13,8 +13,14 @@
//--- //---
/* gray_start(): Start the gray engine /* gray_start(): Start the gray engine
The control of the screen is transferred to the engine; you should not use The control of the screen is transferred to the engine; you should not use
dupdate() after this function, only gupdate(). */ dupdate() after this function, only gupdate().
The engine uses timer number GRAY_TIMER (equal to 0 by default) to run. If
the timer is unavailable, this function will fail. Otherwise, the gray
engine is started and any attempt to use timer 0 while it is running will be
denied by the timer module. */
void gray_start(void); void gray_start(void);
/* gray_stop(): Stop the gray engine /* gray_stop(): Stop the gray engine

View file

@ -46,8 +46,9 @@ typedef volatile struct
uint8_t UNF :1; /* Underflow flag */ uint8_t UNF :1; /* Underflow flag */
uint8_t UNIE :1; /* Underflow interrupt enable */ uint8_t UNIE :1; /* Underflow interrupt enable */
); );
pad(19);
} GPACKED(4) etmu_t; } GPACKED(0x10) etmu_t;
//--- //---
// SH7705 Timer Unit. Refer to: // SH7705 Timer Unit. Refer to:
@ -67,7 +68,14 @@ typedef volatile struct
} GPACKED(4) sh7705_tmu_t; } GPACKED(4) sh7705_tmu_t;
#define SH7705_TMU (*((sh7705_tmu_t *)0xfffffe90)) #define SH7705_TMU (*(sh7705_tmu_t *)0xfffffe90)
//---
// SH7705 Extra Timer Unit. No official documentation exists.
//---
typedef volatile etmu_t sh7705_etmu_t[1];
#define SH7705_ETMU (*(sh7705_etmu_t *)0xa44c0030)
//--- //---
// SH7305 Timer Unit. Refer to: // SH7305 Timer Unit. Refer to:
@ -86,4 +94,11 @@ typedef volatile struct
#define SH7305_TMU (*((sh7305_tmu_t *)0xa4490004)) #define SH7305_TMU (*((sh7305_tmu_t *)0xa4490004))
//---
// SH7305 Extra Timer Unit. No official documentation exists.
//---
typedef volatile etmu_t sh7305_etmu_t[6];
#define SH7305_ETMU (*(sh7305_etmu_t *)0xa44d0030)
#endif /* GINT_MPU_TMU */ #endif /* GINT_MPU_TMU */

View file

@ -1,11 +0,0 @@
//---
// gint:syscalls - calls to CASIOWIN
//---
#ifndef GINT_SYSCALLS
#define GINT_SYSCALLS
/* __os_version(): Get OS version on the form MM.mm.iiii (10 bytes) */
void __os_version(char *version);
#endif /* GINT_SYSCALLS */

View file

@ -19,9 +19,8 @@
SH4-based: 9 timers, ids 0..8 [SH7305] SH4-based: 9 timers, ids 0..8 [SH7305]
You should be aware that some of these timers are used by default by gint: You should be aware that some of these timers are used by default by gint:
- Timer 1 is used by the gray engine on fx9860g. - Timer 0 is used by the gray engine on fx9860g.
- Timer 3 is used by the keyboard, unless GINT_RTC_KEYBOARD is defined. This - Timer 3/8 is used by the keyboard on SH3/SH4.
macro is controlled by the -rtc-keyboard switch when gint is compiled.
timer_setup() will fail if you try to use a timer that's already running. timer_setup() will fail if you try to use a timer that's already running.
Always check the return value of timer_setup()! Using a timer id that has Always check the return value of timer_setup()! Using a timer id that has
@ -152,16 +151,4 @@ void timer_stop(int timer);
int * and you must declare the variable as volatile int. */ int * and you must declare the variable as volatile int. */
int timer_timeout(volatile void *arg); int timer_timeout(volatile void *arg);
//---
// Low-level functions
//---
/* timer_address() - get the address of a timer structure
Returns a tmu_t if the id is 0, 1 or 2 and an etmu_t otherwise. The address
will be NULL if the requested timer does not exist.
@timer Requested timer
@TSTR If the requested timer is a TMU, set to the TSTR address */
void *timer_address(int timer, volatile uint8_t **TSTR);
#endif /* GINT_TIMER */ #endif /* GINT_TIMER */

View file

@ -12,7 +12,6 @@
#include <gint/gint.h> #include <gint/gint.h>
#include <gint/display.h> #include <gint/display.h>
#include <gint/syscalls.h>
#include <gint/clock.h> #include <gint/clock.h>
#ifdef GINT_BOOT_LOG #ifdef GINT_BOOT_LOG
@ -87,9 +86,12 @@ void bootlog_loaded(void)
print(8, 2, "---"); print(8, 2, "---");
#endif #endif
char os[11]; #ifdef FX9860G
__os_version(os); char const *osversion = (void *)0x80010020;
char os[11] = { 0 };
for(int i = 0; i < 10; i++) os[i] = osversion[i];
print(12, 2, "OS%2s%2s%4s", os, os+3, os+6); print(12, 2, "OS%2s%2s%4s", os, os+3, os+6);
#endif
print(1, 3, "ROM%4dk RAM%3d+%1dk ??", print(1, 3, "ROM%4dk RAM%3d+%1dk ??",
(rom_size + 0x3ff) >> 10, (rom_size + 0x3ff) >> 10,

View file

@ -8,6 +8,8 @@
#include <core/setup.h> #include <core/setup.h>
#include <gint/hardware.h> #include <gint/hardware.h>
#include <gint/mpu/intc.h> #include <gint/mpu/intc.h>
#include <gint/defs/util.h>
#include <gint/display.h>
/* VBR address, from the linker script */ /* VBR address, from the linker script */
extern char gint_vbr[]; extern char gint_vbr[];
@ -25,6 +27,7 @@ extern gint_driver_t bdrv, edrv;
typedef struct typedef struct
{ {
uint16_t iprs[12]; uint16_t iprs[12];
uint8_t masks[13];
} GPACKED(2) gint_core_ctx; } GPACKED(2) gint_core_ctx;
@ -32,20 +35,45 @@ typedef struct
@arg ctx gint core context object */ @arg ctx gint core context object */
static void gint_ctx_save(gint_core_ctx *ctx) static void gint_ctx_save(gint_core_ctx *ctx)
{ {
if(isSH3()) for(int i = 0; i < 8; i++) if(isSH3())
{
for(int i = 0; i < 8; i++)
ctx->iprs[i] = *(SH7705_INTC.IPRS[i]); ctx->iprs[i] = *(SH7705_INTC.IPRS[i]);
else for(int i = 0; i < 12; i++) }
else
{
for(int i = 0; i < 12; i++)
ctx->iprs[i] = SH7305_INTC.IPRS[2 * i]; ctx->iprs[i] = SH7305_INTC.IPRS[2 * i];
uint8_t *IMR = (void *)SH7305_INTC.MSK;
for(int i = 0; i < 13; i++, IMR += 4)
ctx->masks[i] = *IMR;
}
} }
/* gint_ctx_restore() - restore interrupt controller configuration /* gint_ctx_restore() - restore interrupt controller configuration
@arg ctx gint core context object */ @arg ctx gint core context object */
static void gint_ctx_restore(gint_core_ctx *ctx) static void gint_ctx_restore(gint_core_ctx *ctx)
{ {
if(isSH3()) for(int i = 0; i < 8; i++) if(isSH3())
{
for(int i = 0; i < 8; i++)
*(SH7705_INTC.IPRS[i]) = ctx->iprs[i]; *(SH7705_INTC.IPRS[i]) = ctx->iprs[i];
else for(int i = 0; i < 12; i++) }
else
{
for(int i = 0; i < 12; i++)
SH7305_INTC.IPRS[2 * i] = ctx->iprs[i]; SH7305_INTC.IPRS[2 * i] = ctx->iprs[i];
/* Setting masks it a bit more involved than reading them */
uint8_t *IMCR = (void *)SH7305_INTC.MSKCLR;
uint8_t *IMR = (void *)SH7305_INTC.MSK;
for(int i = 0; i < 13; i++, IMR += 4, IMCR += 4)
{
*IMCR = 0xff;
*IMR = ctx->masks[i];
}
}
} }
//--- //---
@ -94,8 +122,6 @@ void gint_install(void)
/* unlock() - unlock interrupts, restoring system settings */ /* unlock() - unlock interrupts, restoring system settings */
static void unlock(void) static void unlock(void)
{ {
gint_ctx_restore(&sys_ctx);
/* Restore all driver settings, but do it in reverse order of loading /* Restore all driver settings, but do it in reverse order of loading
to honor the dependency system */ to honor the dependency system */
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;) for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
@ -103,6 +129,8 @@ static void unlock(void)
if(drv->unload) drv->unload(); if(drv->unload) drv->unload();
if(drv->ctx_restore) drv->ctx_restore(drv->sys_ctx); if(drv->ctx_restore) drv->ctx_restore(drv->sys_ctx);
} }
gint_ctx_restore(&sys_ctx);
} }
/* gint_unload() - unload gint and give back control to the system */ /* gint_unload() - unload gint and give back control to the system */
@ -110,3 +138,100 @@ void gint_unload(void)
{ {
gint_setvbr(system_vbr, unlock); gint_setvbr(system_vbr, unlock);
} }
//---
// Hot switching to operating system
//---
static gint_core_ctx gint_ctx;
extern gint_driver_t bdrv, edrv;
static void gint_switch_out(void)
{
/* Save all drivers in reverse order */
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
{
if(!drv->ctx_save || !drv->ctx_restore) continue;
drv->ctx_save(drv->gint_ctx);
}
gint_ctx_save(&gint_ctx);
/* Restore the system context */
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
{
if(!drv->ctx_save || !drv->ctx_restore) continue;
drv->ctx_restore(drv->sys_ctx);
}
gint_ctx_restore(&sys_ctx);
}
static void gint_switch_in(void)
{
/* Save system state again */
for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++)
{
if(!drv->ctx_save || !drv->ctx_restore) continue;
drv->ctx_save(drv->sys_ctx);
}
gint_ctx_save(&sys_ctx);
/* Restore all drivers to their gint state */
for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++)
{
if(!drv->ctx_save || !drv->ctx_restore) continue;
drv->ctx_restore(drv->gint_ctx);
}
gint_ctx_restore(&gint_ctx);
}
/* gint_switch() - temporarily switch out of gint */
void gint_switch(void (*function)(void))
{
gint_setvbr(system_vbr, gint_switch_out);
if(function) function();
gint_setvbr((uint32_t)&gint_vbr, gint_switch_in);
}
int __Timer_Install(int id, void (*handler)(void), int delay);
int __Timer_Start(int id);
int __Timer_Stop(int id);
int __Timer_Deinstall(int id);
int __PutKeyCode(short *matrixcode);
int __GetKeyWait(int *col,int *row,int type,int time,int menu,uint16_t *key);
void __ClearKeyBuffer(void); /* ? */
void *__GetVRAMAddress(void);
static int __osmenu_id;
static void __osmenu_handler(void)
{
short matrixcode = 0x0308;
__PutKeyCode(&matrixcode);
__Timer_Stop(__osmenu_id);
__Timer_Deinstall(__osmenu_id);
}
static void __osmenu(void)
{
__ClearKeyBuffer();
memcpy(__GetVRAMAddress(), gint_vram, 1024);
__osmenu_id = __Timer_Install(0, __osmenu_handler, 0 /* ms */);
if(__osmenu_id <= 0) return;
__Timer_Start(__osmenu_id);
int column, row;
unsigned short keycode;
__GetKeyWait(&column, &row, 0, 0 /* s */, 0, &keycode);
}
/* gint_osmenu() - switch out of gint and call the calculator's main menu */
void gint_osmenu(void)
{
/* TODO: return to menu on fxcg50 */
#ifdef FX9860G
gint_switch(__osmenu);
#endif
}

View file

@ -184,6 +184,11 @@ int start(int isappli, int optnum)
/* Unload gint and give back control to the system. Driver settings /* Unload gint and give back control to the system. Driver settings
will be restored while interrupts are disabled */ will be restored while interrupts are disabled */
#ifdef FXCG50
/* TODO: Put that in the driver model, stupid! */
/* TODO: Also do it in gint_switch()! */
dma_transfer_wait(-1);
#endif
gint_unload(); gint_unload();
/* TODO: Invoke main menu instead of returning? */ /* TODO: Invoke main menu instead of returning? */

View file

@ -9,9 +9,6 @@
** * File system, because it's a mess and we might ruin the ROM. ** * File system, because it's a mess and we might ruin the ROM.
*/ */
/* OS version */
.global ___os_version
/* Dynamic allocation */ /* Dynamic allocation */
.global _malloc .global _malloc
.global _free .global _free
@ -30,6 +27,16 @@
.global _BFile_FindNext .global _BFile_FindNext
.global _BFile_FindClose .global _BFile_FindClose
/* Return to menu */
.global ___Timer_Install
.global ___Timer_Start
.global ___Timer_Stop
.global ___Timer_Deinstall
.global ___PutKeyCode
.global ___GetKeyWait
.global ___ClearKeyBuffer
.global ___GetVRAMAddress
.section ".pretext" .section ".pretext"
#define syscall(id) \ #define syscall(id) \
@ -41,11 +48,6 @@
#ifdef FX9860G #ifdef FX9860G
/* OS version */
___os_version:
syscall(0x02ee)
/* Dynamic allocation */ /* Dynamic allocation */
_malloc: _malloc:
@ -101,6 +103,25 @@ _BFile_FindNext:
_BFile_FindClose: _BFile_FindClose:
syscall(0x043d) syscall(0x043d)
/* Return to menu */
___Timer_Install:
syscall(0x118)
___Timer_Start:
syscall(0x11a)
___Timer_Stop:
syscall(0x11b)
___Timer_Deinstall:
syscall(0x119)
___PutKeyCode:
syscall(0x24f)
___GetKeyWait:
syscall(0x247)
___ClearKeyBuffer:
syscall(0x241)
___GetVRAMAddress:
syscall(0x135)
syscall_table: syscall_table:
.long 0x80010070 .long 0x80010070
@ -108,11 +129,6 @@ syscall_table:
#ifdef FXCG50 #ifdef FXCG50
/* OS version */
___os_version:
syscall(0x1406)
/* Dynamic allocation */ /* Dynamic allocation */
_malloc: _malloc:

View file

@ -154,10 +154,6 @@ gint_driver_t drv_cpg = {
.name = "CPG", .name = "CPG",
.init = init, .init = init,
.status = GINT_DRIVER_STATUS(cpg_status), .status = GINT_DRIVER_STATUS(cpg_status),
.ctx_size = 0,
.sys_ctx = NULL,
.ctx_save = NULL,
.ctx_restore = NULL,
}; };
GINT_DECLARE_DRIVER(1, drv_cpg); GINT_DECLARE_DRIVER(1, drv_cpg);

View file

@ -120,6 +120,13 @@ void dma_transfer(int channel, dma_size_t size, uint blocks,
/* dma_transfer_wait(): Wait for a transfer to finish */ /* dma_transfer_wait(): Wait for a transfer to finish */
void dma_transfer_wait(int channel) void dma_transfer_wait(int channel)
{ {
if(channel < 0)
{
for(int channel = 0; channel < 6; channel++)
dma_transfer_wait(channel);
return;
}
channel_t *ch = dma_channel(channel); channel_t *ch = dma_channel(channel);
if(!ch) return; if(!ch) return;
@ -211,7 +218,7 @@ static void init(void)
static void unload(void) static void unload(void)
{ {
/* Make sure any DMA transfer is finished before leaving the app */ /* Make sure any DMA transfer is finished before leaving the app */
for(int i = 0; i < 6; i++) dma_transfer_wait(i); dma_transfer_wait(-1);
} }
//--- //---
@ -227,10 +234,12 @@ typedef struct
} GPACKED(4) ctx_t; } GPACKED(4) ctx_t;
/* One buffer for the system state will go in gint's .bss section */ /* One buffer for the system state will go in gint's .bss section */
GBSS static ctx_t sys_ctx; GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(void *buf) static void ctx_save(void *buf)
{ {
dma_transfer_wait(-1);
ctx_t *ctx = buf; ctx_t *ctx = buf;
for(int i = 0; i < 6; i++) for(int i = 0; i < 6; i++)
@ -250,8 +259,16 @@ static void ctx_save(void *buf)
static void ctx_restore(void *buf) static void ctx_restore(void *buf)
{ {
dma_transfer_wait(-1);
ctx_t *ctx = buf; ctx_t *ctx = buf;
/* Restore the supply status of the DMA0 clock first. If the DMA was
originally on standby, the context is basically trash so we don't
want to write that back. If it was originally running, we need to
power it up again for the writes to registers to have any effect */
POWER.MSTPCR0.DMAC0 = ctx->clock;
for(int i = 0; i < 6; i++) for(int i = 0; i < 6; i++)
{ {
channel_t *ch = dma_channel(i); channel_t *ch = dma_channel(i);
@ -262,9 +279,6 @@ static void ctx_restore(void *buf)
} }
DMA.OR.word = ctx->OR; DMA.OR.word = ctx->OR;
/* Restore the supply status of the DMA0 clock */
POWER.MSTPCR0.DMAC0 = ctx->clock;
} }
//--- //---
@ -275,8 +289,8 @@ gint_driver_t drv_dma0 = {
.name = "DMA0", .name = "DMA0",
.init = init, .init = init,
.unload = unload, .unload = unload,
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx, .sys_ctx = &sys_ctx,
.gint_ctx = &gint_ctx,
.ctx_save = ctx_save, .ctx_save = ctx_save,
.ctx_restore = ctx_restore, .ctx_restore = ctx_restore,
}; };

View file

@ -33,8 +33,8 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
/* Keyboard time when the key was pressed */ /* Keyboard time when the key was pressed */
static int rep_time = 0; static int rep_time = 0;
/* Reset the state if the repeated key went up due to different /* Reset the state if the repeated key went up while getkey() was not
graphical primitives being used */ aware (this happens when different keyboard primitives are used) */
if(rep_key && !keydown(rep_key)) if(rep_key && !keydown(rep_key))
{ {
rep_key = 0; rep_key = 0;
@ -59,9 +59,10 @@ key_event_t getkey_opt(int opt, volatile int *timeout)
} }
#endif #endif
/* Return to menu. TODO: use gint_switch() in getkey_opt() */ /* Return to menu */
if(opt & GETKEY_MENU && key == KEY_MENU && !(alpha || shift)) if(opt & GETKEY_MENU && key == KEY_MENU && !(alpha || shift))
{ {
gint_osmenu();
continue; continue;
} }

View file

@ -24,7 +24,7 @@
Events can be seen as a delta-encoding of the keyboard state over time. Events can be seen as a delta-encoding of the keyboard state over time.
To ensure that adding pending events to the last-read state always gives the To ensure that adding pending events to the last-read state always gives the
internal driger state, this array is not updated if the generation of an internal driver state, this array is not updated if the generation of an
event fails. (Most likely the even will be regenerated at the next scan.) */ event fails. (Most likely the even will be regenerated at the next scan.) */
GDATA static volatile uint8_t state[12] = { 0 }; GDATA static volatile uint8_t state[12] = { 0 };
@ -34,13 +34,13 @@ GDATA static volatile uint8_t state[12] = { 0 };
user's view of the keyboard. */ user's view of the keyboard. */
GDATA static uint8_t current[12] = { 0 }; GDATA static uint8_t current[12] = { 0 };
/* A driver event, which is a change in a full row instead of a single key. */ /* A driver event, which is a common change in a full row */
typedef struct typedef struct
{ {
uint16_t time; /* Locally unique time identifier */ uint16_t time; /* Locally unique time identifier */
uint8_t row; /* Row number */ uint8_t row; /* Row number */
uint8_t changed; /* Keys that changed state */ uint8_t changed; /* Keys that changed state */
uint8_t state; /* Key state for the new row */ uint8_t kind; /* Type of change, either KEYEV_DOWN or KEYEV_UP */
} driver_event_t; } driver_event_t;
@ -93,22 +93,26 @@ static void keysc_frame(void)
for(int i = 0; i < 6; i++) array[i] = KEYSC[i]; for(int i = 0; i < 6; i++) array[i] = KEYSC[i];
} }
/* Compare new data with the internal state. Push releases before
presses so that a key change occuring within a single analysis frame
can be performed. This happens all the time when going back to the
main MENU via gint_osmenu() on a keybind. */
for(int row = 0; row < 12; row++) for(int row = 0; row < 12; row++)
{ {
/* Compare new data with the internal state. */ int diff = ~scan[row] & state[row];
int old = state[row]; if(!diff) continue;
int new = scan[row];
if(old == new) continue;
driver_event_t ev = {
.time = time,
.row = row,
.changed = old ^ new,
.state = new,
};
/* Update internal status if the event could be pushed */ /* Update internal status if the event could be pushed */
if(!buffer_push(ev)) state[row] = new; driver_event_t ev = { time, row, diff, KEYEV_UP };
if(!buffer_push(ev)) state[row] &= scan[row];
}
for(int row = 0; row < 12; row++)
{
int diff = scan[row] & ~state[row];
if(!diff) continue;
driver_event_t ev = { time, row, diff, KEYEV_DOWN };
if(!buffer_push(ev)) state[row] |= scan[row];
} }
} }
@ -149,22 +153,18 @@ key_event_t pollevent(void)
/* Generate new key events and return the first of them*/ /* Generate new key events and return the first of them*/
int changed = ev.changed; int changed = ev.changed;
int state = ev.state;
for(int code = ((ev.row ^ 1) << 4) | 0x7; code & 0x7; code--) for(int code = ((ev.row ^ 1) << 4) | 0x7; code & 0x7; code--)
{ {
if(changed & 1) if(changed & 1)
{ {
key_event_t keyev = { key_event_t keyev = {
.time = ev.time, .time = ev.time,
.type = 2 - (state & 1), .type = ev.kind,
.key = code, .key = code,
}; };
events[events_pending++] = keyev; events[events_pending++] = keyev;
} }
changed >>= 1; changed >>= 1;
state >>= 1;
} }
/* Call recursively to avoid duplicating the event emission code */ /* Call recursively to avoid duplicating the event emission code */
@ -263,7 +263,6 @@ static void init(void)
/* Configure the timer to do 128 keyboard scans per second. This /* Configure the timer to do 128 keyboard scans per second. This
frequency *must* be high for the KEYSC interface to work! */ frequency *must* be high for the KEYSC interface to work! */
/* Note: the supporting timer always runs at 32768 Hz. */ /* Note: the supporting timer always runs at 32768 Hz. */
/* TODO: The SH3 does not need to run fast, adjust user settings? */
int delay = 32768 / KEYBOARD_SCAN_FREQUENCY; int delay = 32768 / KEYBOARD_SCAN_FREQUENCY;
if(!delay) delay = 1; if(!delay) delay = 1;
@ -305,10 +304,6 @@ gint_driver_t drv_keysc = {
.init = init, .init = init,
.status = GINT_DRIVER_STATUS(keysc_status), .status = GINT_DRIVER_STATUS(keysc_status),
.unload = unload, .unload = unload,
.ctx_size = 0,
.sys_ctx = NULL,
.ctx_save = NULL,
.ctx_restore = NULL,
}; };
GINT_DECLARE_DRIVER(4, drv_keysc); GINT_DECLARE_DRIVER(4, drv_keysc);

View file

@ -3,6 +3,7 @@
//--- //---
#include <gint/defs/types.h> #include <gint/defs/types.h>
#include <gint/defs/util.h>
#include <gint/hardware.h> #include <gint/hardware.h>
#include <gint/drivers.h> #include <gint/drivers.h>
#include <gint/dma.h> #include <gint/dma.h>
@ -52,8 +53,6 @@ typedef word_union(entry_mode_t,
// Device communication primitives // Device communication primitives
//--- //---
#define synco() __asm__ volatile (".word 0x00ab":::"memory")
/* Interface with the controller */ /* Interface with the controller */
GDATA static volatile uint16_t *intf = (void *)0xb4000000; GDATA static volatile uint16_t *intf = (void *)0xb4000000;
/* Bit 4 of Port R controls the RS bit of the display driver */ /* Bit 4 of Port R controls the RS bit of the display driver */
@ -246,7 +245,7 @@ typedef struct
} GPACKED(2) ctx_t; } GPACKED(2) ctx_t;
/* Allocate one buffer in gint's storage section */ /* Allocate one buffer in gint's storage section */
GBSS static ctx_t sys_ctx; GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(void *buf) static void ctx_save(void *buf)
{ {
@ -302,8 +301,8 @@ gint_driver_t drv_r61524 = {
.name = "R61524", .name = "R61524",
.init = init, .init = init,
.status = GINT_DRIVER_STATUS(r61524_status), .status = GINT_DRIVER_STATUS(r61524_status),
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx, .sys_ctx = &sys_ctx,
.gint_ctx = &gint_ctx,
.ctx_save = ctx_save, .ctx_save = ctx_save,
.ctx_restore = ctx_restore, .ctx_restore = ctx_restore,
}; };

View file

@ -162,7 +162,7 @@ typedef struct
uint8_t RCR2; uint8_t RCR2;
} ctx_t; } ctx_t;
GBSS static ctx_t sys_ctx; GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(void *buf) static void ctx_save(void *buf)
{ {
@ -174,8 +174,8 @@ static void ctx_save(void *buf)
static void ctx_restore(void *buf) static void ctx_restore(void *buf)
{ {
ctx_t *ctx = buf; ctx_t *ctx = buf;
ctx->RCR1 = RTC->RCR1.byte; RTC->RCR1.byte = ctx->RCR1;
ctx->RCR2 = RTC->RCR2.byte; RTC->RCR2.byte = ctx->RCR2;
} }
//--- //---
@ -186,8 +186,8 @@ gint_driver_t drv_rtc = {
.name = "RTC", .name = "RTC",
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3), .driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
.init = init, .init = init,
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx, .sys_ctx = &sys_ctx,
.gint_ctx = &gint_ctx,
.ctx_save = ctx_save, .ctx_save = ctx_save,
.ctx_restore = ctx_restore, .ctx_restore = ctx_restore,
}; };

View file

@ -11,7 +11,7 @@ static void memcpy4(uint32_t * restrict d, const void * restrict src, size_t n)
if(!modulo) if(!modulo)
{ {
const uint32_t *s = src; const uint32_t *s = src;
while(n--) *d++ = *s++; for(; n; n-=4) *d++ = *s++;
} }
#if 0 #if 0
@ -41,7 +41,7 @@ static void memcpy4(uint32_t * restrict d, const void * restrict src, size_t n)
const uint16_t *s = src; const uint16_t *s = src;
uint16_t * restrict dst = (void *)d; uint16_t * restrict dst = (void *)d;
while(n--) for(; n; n-=2)
{ {
*dst++ = *s++; *dst++ = *s++;
*dst++ = *s++; *dst++ = *s++;
@ -74,7 +74,7 @@ void *memcpy(void * restrict dst, const void * restrict src, size_t n)
while((uintptr_t)d & 3) *d++ = *s++, n--; while((uintptr_t)d & 3) *d++ = *s++, n--;
/* Perform the big, efficient copy */ /* Perform the big, efficient copy */
memcpy4((void *)d, s, (n >> 2)); memcpy4((void *)d, s, n & ~3);
size_t m = n & 3; size_t m = n & 3;
d += (n - m); d += (n - m);

View file

@ -9,15 +9,13 @@
#include <gint/defs/types.h> #include <gint/defs/types.h>
#include <gint/hardware.h> #include <gint/hardware.h>
#include <gint/syscalls.h>
//--- //---
// Device specification sheet // Device specification sheet
//--- //---
/* This version number is 1 for the old T6K11 everyone knows, and 2 for the /* This version number is 1 for the old T6K11 everyone knows, and 2 for the
newer one found in the Graph 35+E II. Documentation is available only for newer one found in the Graph 35+E II. Documentation is available only for
version 1. Dumps of Bdisp_PutDisp_DD() are used to driver version 2. */ version 1. Dumps of Bdisp_PutDisp_DD() are used to drive version 2. */
static int t6k11_version = 1; static int t6k11_version = 1;
/* Screen registers on the original T6K11. Registers 8..11 and 13..15 are test /* Screen registers on the original T6K11. Registers 8..11 and 13..15 are test
@ -197,7 +195,7 @@ typedef struct
} GPACKED(1) ctx_t; } GPACKED(1) ctx_t;
/* Pre-allocate a context in gint's uninitialized section */ /* Pre-allocate a context in gint's uninitialized section */
GBSS static ctx_t sys_ctx; GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(void *buf) static void ctx_save(void *buf)
{ {
@ -256,8 +254,8 @@ gint_driver_t drv_t6k11 = {
.name = "T6K11", .name = "T6K11",
.init = init, .init = init,
.status = GINT_DRIVER_STATUS(t6k11_status), .status = GINT_DRIVER_STATUS(t6k11_status),
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx, .sys_ctx = &sys_ctx,
.gint_ctx = &gint_ctx,
.ctx_save = ctx_save, .ctx_save = ctx_save,
.ctx_restore = ctx_restore, .ctx_restore = ctx_restore,
}; };

View file

@ -14,10 +14,9 @@
#include <gint/defs/types.h> #include <gint/defs/types.h>
//--- //---
// Timer structures // Driver storage
//--- //---
/* inth_data_t - data storage inside interrupt handlers */ /* inth_data_t - data storage inside interrupt handlers */
typedef struct typedef struct
{ {
@ -27,34 +26,14 @@ typedef struct
} GPACKED(4) inth_data_t; } GPACKED(4) inth_data_t;
/* timer_t - all data required to run a single timer */ /* This array references the storage areas of all timer handlers */
typedef struct GDATA static inth_data_t *timers[9] = { NULL };
{
void *tmu; /* Address of timer structure */
inth_data_t *data; /* Interrupt handler data */
uint16_t event; /* Interrupt event code */
} timer_t;
//---
// Driver storage
//---
/* This is the description of the structure on SH4. SH3-based fx9860g models,
which are already very rare, will adapt the values in init functions */
GDATA static timer_t timers[9] = {
{ (void *)0xa4490008, NULL, 0x400 },
{ (void *)0xa4490014, NULL, 0x420 },
{ (void *)0xa4490020, NULL, 0x440 },
{ (void *)0xa44d0030, NULL, 0x9e0 },
{ (void *)0xa44d0050, NULL, 0xc20 },
{ (void *)0xa44d0070, NULL, 0xc40 },
{ (void *)0xa44d0090, NULL, 0x900 },
{ (void *)0xa44d00b0, NULL, 0xd00 },
{ (void *)0xa44d00d0, NULL, 0xfa0 },
};
/* Arrays of standard and extra timers */
GDATA static tmu_t *TMU = SH7305_TMU.TMU;
GDATA static etmu_t *ETMU = SH7305_ETMU;
/* TSTR register for standard timers */ /* TSTR register for standard timers */
GDATA static volatile uint8_t *TSTR = (void *)0xa4490004; GDATA static volatile uint8_t *TSTR = &SH7305_TMU.TSTR;
//--- //---
// Timer API // Timer API
@ -64,60 +43,52 @@ GDATA static volatile uint8_t *TSTR = (void *)0xa4490004;
int timer_setup(int id, uint32_t delay, timer_input_t clock, int timer_setup(int id, uint32_t delay, timer_input_t clock,
int (*callback)(volatile void *arg), volatile void *arg) int (*callback)(volatile void *arg), volatile void *arg)
{ {
/* We need to distinguish normal and extra timers */ /* Timer is not installed (TCR address is really required) */
if(!timers[id]) return -1;
if(id < 3) if(id < 3)
{ {
/* Refuse to setup timers that are already in use */ /* Refuse to setup timers that are already in use */
tmu_t *t = timers[id].tmu; tmu_t *T = &TMU[id];
if(t->TCR.UNIE) return -1; if(T->TCR.UNIE || *TSTR & (1 << id)) return -1;
/* Configure the registers of the target timer */ /* Configure the counter, clear interrupt flag*/
t->TCOR = delay; T->TCOR = delay;
t->TCNT = delay; T->TCNT = delay;
t->TCR.TPSC = clock; T->TCR.TPSC = clock;
do T->TCR.UNF = 0;
/* Clear the interrupt flag */ while(T->TCR.UNF);
do t->TCR.UNF = 0;
while(t->TCR.UNF);
/* Enable interrupt and count on rising edge (SH7705) */ /* Enable interrupt and count on rising edge (SH7705) */
t->TCR.UNIE = 1; T->TCR.UNIE = 1;
t->TCR.CKEG = 0; T->TCR.CKEG = 0;
} }
/* Extra timers have a simpler structure */
else else
{ {
etmu_t *t = timers[id].tmu; etmu_t *T = &ETMU[id-3];
if(t->TCR.UNIE) return -1; if(T->TCR.UNIE) return -1;
/* Clear the interrupt flag */ /* No clock input and clock edge here. But TCR and TCNT need a
do t->TCR.UNF = 0; delay to retain the value */
while(t->TCR.UNF); do T->TCR.UNF = 0;
while(T->TCR.UNF);
/* There is no clock input and no clock edge settings */ do T->TCOR = delay;
t->TCOR = delay; while(T->TCOR != delay);
/* TODO: FXCG50: does not always work on first try */ do T->TCNT = delay;
do t->TCNT = delay; while(T->TCNT != delay);
while(t->TCNT != delay);
t->TCR.UNIE = 1; T->TCR.UNIE = 1;
} }
/* Register the callback and its argument */ timers[id]->cb = callback;
if(timers[id].data) timers[id]->arg = arg;
{
timers[id].data->cb = callback;
timers[id].data->arg = arg;
}
/* Return the timer id, since configuration was successful */
return id; return id;
} }
/* timer_delay() - compute a delay constant from a duration in seconds */ /* timer_delay() - compute a delay constant from a duration in seconds */
uint32_t timer_delay(int tid, uint64_t delay_us) uint32_t timer_delay(int id, uint64_t delay_us)
{ {
/* TODO: Proper timer_delay() */ /* TODO: Proper timer_delay() */
const clock_frequency_t *clock = clock_freq(); const clock_frequency_t *clock = clock_freq();
@ -128,7 +99,7 @@ uint32_t timer_delay(int tid, uint64_t delay_us)
// uint64_t freq = 29020000 >> 2; // uint64_t freq = 29020000 >> 2;
/* Extra timers all run at 32768 Hz */ /* Extra timers all run at 32768 Hz */
if(tid >= 3) freq = 32768; if(id >= 3) freq = 32768;
uint64_t product = freq * delay_us; uint64_t product = freq * delay_us;
return product / 1000000; return product / 1000000;
@ -139,10 +110,8 @@ uint32_t timer_delay(int tid, uint64_t delay_us)
@state 0 to start the timer, 1 to stop it (nothing else!) */ @state 0 to start the timer, 1 to stop it (nothing else!) */
static void timer_control(int id, int state) static void timer_control(int id, int state)
{ {
/* For standard timers, use the MPU's TSTR register */
if(id < 3) *TSTR = (*TSTR | (1 << id)) ^ (state << id); if(id < 3) *TSTR = (*TSTR | (1 << id)) ^ (state << id);
/* Extra timers all have their own TSTR register */ else ETMU[id-3].TSTR = state ^ 1;
else ((etmu_t *)timers[id].tmu)->TSTR = state ^ 1;
} }
/* timer_start() - start a configured timer */ /* timer_start() - start a configured timer */
@ -154,8 +123,8 @@ void timer_start(int id)
/* timer_reload() - change a timer's delay constant for next interrupts */ /* timer_reload() - change a timer's delay constant for next interrupts */
void timer_reload(int id, uint32_t delay) void timer_reload(int id, uint32_t delay)
{ {
if(id < 3) ((tmu_t *)timers[id].tmu)->TCOR = delay; if(id < 3) TMU[id].TCOR = delay;
else ((etmu_t *)timers[id].tmu)->TCOR = delay; else ETMU[id-3].TCOR = delay;
} }
/* timer_pause() - stop a running timer */ /* timer_pause() - stop a running timer */
@ -164,7 +133,7 @@ void timer_pause(int id)
timer_control(id, 1); timer_control(id, 1);
} }
/* timer_stp() - stop and free a timer */ /* timer_stop() - stop and free a timer */
void timer_stop(int id) void timer_stop(int id)
{ {
/* Stop the timer and disable UNIE to indicate that it's free */ /* Stop the timer and disable UNIE to indicate that it's free */
@ -172,27 +141,24 @@ void timer_stop(int id)
if(id < 3) if(id < 3)
{ {
tmu_t *t = timers[id].tmu; TMU[id].TCR.UNIE = 0;
t->TCR.UNIE = 0; TMU[id].TCOR = 0xffffffff;
TMU[id].TCNT = 0xffffffff;
/* Clear TCOR and TCNT */
t->TCOR = 0xffffffff;
t->TCNT = 0xffffffff;
} }
else else
{ {
etmu_t *t = timers[id].tmu; etmu_t *T = &ETMU[id-3];
t->TCR.UNIE = 0; T->TCR.UNIE = 0;
/* Also clear TCOR and TCNT to avoid spurious interrupts */ /* Also clear TCOR and TCNT to avoid spurious interrupts */
t->TCOR = 0xffffffff; do T->TCOR = 0xffffffff;
while(T->TCOR + 1);
/* TODO: FXCG50: Again */ do T->TCNT = 0xffffffff;
do t->TCNT = 0xffffffff; while(T->TCNT + 1);
while(t->TCNT + 1);
do t->TCR.UNF = 0; do T->TCR.UNF = 0;
while(t->TCR.UNF); while(T->TCR.UNF);
} }
} }
@ -205,8 +171,6 @@ int timer_timeout(volatile void *arg)
{ {
volatile int *x = arg; volatile int *x = arg;
(*x)++; (*x)++;
/* Always stop after firing once */
return 1; return 1;
} }
@ -214,23 +178,15 @@ int timer_timeout(volatile void *arg)
// Low-level functions // Low-level functions
//--- //---
/* timer_address() - get the address of a timer structure */
void *timer_address(int timer, volatile uint8_t **TSTR_arg)
{
if((uint)timer < 2 && TSTR_arg) *TSTR_arg = TSTR;
return (uint)timer < timer_count() ? timers[timer].tmu : NULL;
}
/* timer_clear() - clear an ETMU flag and possibly stop the timer /* timer_clear() - clear an ETMU flag and possibly stop the timer
@timer Timer ID, must be an ETMU @timer Timer ID, must be an ETMU
@stop Non-zero to stop the timer */ @stop Non-zero to stop the timer */
void timer_clear(int timer, int stop) void timer_clear(int id, int stop)
{ {
etmu_t *t = timers[timer].tmu; do ETMU[id-3].TCR.UNF = 0;
do t->TCR.UNF = 0; while(ETMU[id-3].TCR.UNF);
while(t->TCR.UNF);
if(stop) timer_stop(timer); if(stop) timer_stop(id);
} }
//--- //---
@ -247,79 +203,63 @@ extern void inth_etmux(void);
#ifdef FX9860G #ifdef FX9860G
static void driver_sh3(void) static void driver_sh3(void)
{ {
timers[0].tmu = (void *)0xfffffe94; TMU = SH7705_TMU.TMU;
timers[1].tmu = (void *)0xfffffea0; ETMU = SH7705_ETMU;
timers[2].tmu = (void *)0xfffffeac; TSTR = &SH7705_TMU.TSTR;
/* We must not change the event code of ETMU0 since it's translated to
its SH4 counterpart by the interrupt handler */
timers[3].tmu = (void *)0xa44c0030;
TSTR = (void *)0xfffffe92;
} }
#endif /* FX9860G */ #endif /* FX9860G */
static void init(void) static void init(void)
{ {
/* Install the standard TMU's interrupt handlers */ uint16_t etmu_event[6] = { 0x9e0, 0xc20, 0xc40, 0x900, 0xd00, 0xfa0 };
void *h = gint_inthandler(0x400, inth_tmu, 128);
/* User information in interrupt handlers */
timers[0].data = h + 84;
timers[1].data = h + 104;
timers[2].data = h + 116;
/* Stop all timers */
*TSTR = 0; *TSTR = 0;
/* This driver uses the UNIE (UNderflow Interrupt Enable) bit of the /* Install the standard TMU's interrupt handlers */
TCR register to indicate which timers are being used; reset them */ void *h = gint_inthandler(0x400, inth_tmu, 128);
for(int i = 0; i < 3; i++) timers[0] = h + 84;
timers[1] = h + 104;
timers[2] = h + 116;
/* Clear every timer to avoid surprises */
for(int id = 0; id < 3; id++)
{ {
tmu_t *t = timers[i].tmu; TMU[id].TCOR = 0xffffffff;
TMU[id].TCNT = 0xffffffff;
t->TCOR = 0xffffffff; do TMU[id].TCR.word = 0;
t->TCNT = 0xffffffff; while(TMU[id].TCR.word);
do t->TCR.word = 0;
while(t->TCR.word);
/* Standard timers: TCR is provided to the interrupt handler */ /* Standard timers: TCR is provided to the interrupt handler */
timers[i].data->TCR = &t->TCR; timers[id]->TCR = &TMU[id].TCR;
} }
for(int id = 0; id < timer_count()-3; id++)
/* Clear the extra timers */
for(int i = 3; i < timer_count(); i++)
{ {
etmu_t *t = timers[i].tmu; etmu_t *T = &ETMU[id];
/* Extra timers seem to generate interrupts as long as TCNT=0, /* Extra timers seem to generate interrupts as long as TCNT=0,
regardless of TSTR. I'm not entirely sure about this weird regardless of TSTR. I'm not entirely sure about this weird
behaviour, but for safety I'll set TCOR/TCNT to non-zero. behaviour, but for safety I'll set TCOR/TCNT to non-zero. */
This may be related to difficulties when setting TCNT. */ T->TSTR = 0;
do T->TCOR = 0xffffffff;
while(T->TCOR + 1);
t->TSTR = 0; /* Also TCNT and TCR take some time to record changes */
t->TCOR = 0xffffffff; do T->TCNT = 0xffffffff;
while(T->TCNT + 1);
/* TODO: FXCG50: Safety */ do T->TCR.byte = 0;
do t->TCNT = 0xffffffff; while(T->TCR.byte);
while(t->TCNT + 1);
/* Clear interrupts */
do t->TCR.byte = 0;
while(t->TCR.byte);
} }
/* Install the extra timers. We need three extra timers for the /* Install the extra timers. The interrupt handler takes 3 gates, so we
interrupt handlers to work, so install 3 on SH3, even if only one install 3 even on SH3 where there's only one */
actually exists */
int limit = isSH3() ? 6 : 9; int limit = isSH3() ? 6 : 9;
for(int i = 3; i < limit; i++) for(int i = 3; i < limit; i++)
{ {
void *handler = (i == 5) ? inth_etmu2 : inth_etmux; void *handler = (i == 5) ? inth_etmu2 : inth_etmux;
void *h = gint_inthandler(timers[i].event, handler, 32); void *h = gint_inthandler(etmu_event[i-3], handler, 32);
timers[i].data = h + 24; timers[i] = h + 24;
if(i == 5) continue; if(i == 5) continue;
uint32_t *data_id = (h + 20); uint32_t *data_id = (h + 20);
@ -330,15 +270,12 @@ static void init(void)
gint_inthandler(0xc60, inth_etmu_help, 32); gint_inthandler(0xc60, inth_etmu_help, 32);
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */ /* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
gint_intlevel(0, 13); gint_intlevel(0, 7);
gint_intlevel(1, 11); gint_intlevel(1, 11);
gint_intlevel(2, 9); gint_intlevel(2, 9);
/* Enable the extra TMUs at level 7 */ /* Enable the extra TMUs at level 7 */
if(isSH3()) if(isSH3()) gint_intlevel(23, 7);
{
gint_intlevel(23, 7);
}
else else
{ {
/* Unmask the standard timers' interrupts */ /* Unmask the standard timers' interrupts */
@ -363,15 +300,15 @@ static void init(void)
gint[HWTMU] = HW_LOADED; gint[HWTMU] = HW_LOADED;
gint[HWETMU] = HW_LOADED | (isSH3() ? HWETMU_1 : HWETMU_6); gint[HWETMU] = HW_LOADED | (isSH3() ? HWETMU_1 : HWETMU_6);
for(int i = 3; i < timer_count(); i++) for(int i = 0; i < timer_count() - 3; i++)
{ {
etmu_t *t = timers[i].tmu; etmu_t *T = &ETMU[i];
int v = !(t->TCOR + 1) int v = !(T->TCOR + 1)
&& !(t->TCNT + 1) && !(T->TCNT + 1)
&& !(t->TSTR) && !(T->TSTR)
&& !(t->TCR.UNF) && !(T->TCR.UNF)
&& !(t->TCR.UNIE); && !(T->TCR.UNIE);
gint[HWETMU] |= v << (i - 1); gint[HWETMU] |= v << (i + 2);
} }
} }
@ -380,7 +317,6 @@ static void init(void)
//--- //---
#ifdef GINT_BOOT_LOG #ifdef GINT_BOOT_LOG
/* tmu_status() - status string of extra TMUs for the boot log /* tmu_status() - status string of extra TMUs for the boot log
The status string has a two-character code for each of the extra timers. The status string has a two-character code for each of the extra timers.
@ -401,13 +337,13 @@ static const char *tmu_status(void)
static char status[18] = "ETMU "; static char status[18] = "ETMU ";
int j = 5; int j = 5;
for(int i = 3; i < timer_count(); i++) for(int i = 0; i < timer_count()-3; i++)
{ {
etmu_t *t = timers[i].tmu; etmu_t *T = &ETMU[i];
int v1 = (!(t->TCOR + 1)) int v1 = (!(T->TCOR + 1))
| (!(t->TCNT + 1) << 1) | (!(T->TCNT + 1) << 1)
| (!(t->TSTR) << 2); | (!(T->TSTR) << 2);
int v2 = (t->TCR.UNF << 1) | (t->TCR.UNIE); int v2 = (T->TCR.UNF << 1) | (T->TCR.UNIE);
status[j++] = '0' + v1; status[j++] = '0' + v1;
status[j++] = "DLH!"[v2]; status[j++] = "DLH!"[v2];
@ -416,7 +352,6 @@ static const char *tmu_status(void)
return status; return status;
} }
#endif /* GINT_BOOT_LOG */ #endif /* GINT_BOOT_LOG */
//--- //---
@ -428,63 +363,63 @@ typedef struct
tmu_t std[3]; tmu_t std[3];
etmu_t extra[6]; etmu_t extra[6];
uint8_t TSTR; uint8_t TSTR;
} ctx_t;
} GPACKED(4) ctx_t;
/* Allocate a system buffer in gint's BSS area */ /* Allocate a system buffer in gint's BSS area */
GBSS static ctx_t sys_ctx; GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(void *buf) static void ctx_save(void *buf)
{ {
ctx_t *ctx = buf; ctx_t *ctx = buf;
ctx->TSTR = *TSTR;
for(int i = 0; i < 3; i++) for(int i = 0; i < 3; i++)
{ {
tmu_t *t = timers[i].tmu; tmu_t *c = &ctx->std[i];
ctx->std[i].TCOR = t->TCOR; c->TCOR = TMU[i].TCOR;
ctx->std[i].TCNT = t->TCNT; c->TCNT = TMU[i].TCNT;
ctx->std[i].TCR.word = t->TCR.word; c->TCR.word = TMU[i].TCR.word;
} }
ctx->TSTR = *TSTR;
for(int i = 0; i < timer_count() - 3; i++) for(int i = 0; i < timer_count() - 3; i++)
{ {
etmu_t *t = timers[i + 3].tmu; etmu_t *T = &ETMU[i], *c = &ctx->extra[i];
ctx->extra[i].TCOR = t->TCOR;
ctx->extra[i].TCNT = t->TCNT; /* Don't snapshot an interrupt state, because the timer state
ctx->extra[i].TCR.byte = t->TCR.byte; is sometimes garbage protected by a masked interrupt. */
ctx->extra[i].TSTR = t->TSTR; c->TCOR = T->TCOR ? T->TCOR : 0xffffffff;
c->TCNT = T->TCNT ? T->TCNT : c->TCOR;
c->TCR.byte = T->TCR.byte & 0xd;
c->TSTR = T->TSTR;
} }
} }
static void ctx_restore(void *buf) static void ctx_restore(void *buf)
{ {
ctx_t *ctx = buf; ctx_t *ctx = buf;
*TSTR = ctx->TSTR;
for(int i = 0; i < 3; i++) for(int i = 0; i < 3; i++)
{ {
tmu_t *t = timers[i].tmu; tmu_t *c = &ctx->std[i];
t->TCNT = ctx->std[i].TCNT; TMU[i].TCNT = c->TCNT;
t->TCOR = ctx->std[i].TCOR; TMU[i].TCOR = c->TCOR;
t->TCR.word = ctx->std[i].TCR.word; TMU[i].TCR.word = c->TCR.word;
} }
*TSTR = ctx->TSTR;
for(int i = 0; i < timer_count() - 3; i++) for(int i = 0; i < timer_count() - 3; i++)
{ {
etmu_t *t = timers[i + 3].tmu; etmu_t *T = &ETMU[i], *c = &ctx->extra[i];
/* This thing is being unloaded while interrupts are disabled, do T->TCOR = c->TCOR;
so we don't have to heed for ctx->extra[i].TCNT = 0, which while(T->TCOR != c->TCOR);
can generate interrupts. I tried to do t->TCNT = 0xffffffff
then restore ctx->extra[i], but it causes hangs on SH3 when T->TSTR = c->TSTR;
the overclock level is too high. */
t->TCNT = ctx->extra[i].TCNT; /* Remember that TCNT and TCR take time to write to */
t->TCOR = ctx->extra[i].TCOR; do T->TCNT = c->TCNT;
t->TCR.byte = ctx->extra[i].TCR.byte; while(T->TCNT != c->TCNT);
t->TSTR = ctx->extra[i].TSTR;
do T->TCR.byte = c->TCR.byte;
while(T->TCR.byte != c->TCR.byte);
} }
} }
@ -497,8 +432,8 @@ gint_driver_t drv_tmu = {
.driver_sh3 = GINT_DRIVER_SH3(driver_sh3), .driver_sh3 = GINT_DRIVER_SH3(driver_sh3),
.init = init, .init = init,
.status = GINT_DRIVER_STATUS(tmu_status), .status = GINT_DRIVER_STATUS(tmu_status),
.ctx_size = sizeof(ctx_t),
.sys_ctx = &sys_ctx, .sys_ctx = &sys_ctx,
.gint_ctx = &gint_ctx,
.ctx_save = ctx_save, .ctx_save = ctx_save,
.ctx_restore = ctx_restore, .ctx_restore = ctx_restore,
}; };