general cleanup of the kernel

* 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 is contained in:
Lephe 2020-06-20 17:18:51 +02:00
parent b7de559b78
commit 1c7b1350b4
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
22 changed files with 652 additions and 551 deletions

View file

@ -42,23 +42,22 @@ SECTIONS
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
/* Machine code going to ROM:
- Initialization sections (.pretext.entry and .pretext)
- Entry function (.text.entry)
- Compiler-provided constructors (.ctors) and destructors (.dtors)
- All text from .text and .text.* (including user code)
- Code sections from fxlib, named "C" and "P" */
.pretext : {
*(.pretext.entry)
*(.pretext)
_btors = . ;
*(.ctors .ctors.*)
_mtors = . ;
*(.dtors .dtors.*)
_etors = . ;
} > rom
.text : {
*(.text.entry)
_bctors = . ;
*(.ctors .ctors.*)
_ectors = . ;
_bdtors = . ;
*(.dtors .dtors.*)
_edtors = . ;
_gint_exch_start = . ;
*(.gint.exch)
_gint_exch_size = ABSOLUTE(. - _gint_exch_start);

View file

@ -43,18 +43,19 @@ SECTIONS
+ SIZEOF(.gint.drivers) + SIZEOF(.gint.blocks);
/* Machine code going to ROM:
- Initialization sections (.pretext.entry and .pretext)
- Entry function (.text.entry)
- Compiler-provided constructors (.ctors) and destructors (.dtors)
- All text from .text and .text.* (including user code) */
.text : {
*(.pretext.entry)
*(.pretext)
*(.text.entry)
_btors = . ;
_bctors = . ;
*(.ctors .ctors.*)
_mtors = . ;
_ectors = . ;
_bdtors = . ;
*(.dtors .dtors.*)
_etors = . ;
_edtors = . ;
_gint_exch_start = . ;
*(.gint.exch)

View file

@ -10,85 +10,40 @@
/* GINT_VERSION - the library version number
gint is versioned from its repository commits on the master branch. The
GINT_VERSION integer contains the short commit hash.
GINT_VERSION integer contains the short commit hash with 7 digits.
For instance, 0x03f7c0a0 means commit 3f7c0a0. */
extern char GINT_VERSION;
#define GINT_VERSION ((uint32_t)&GINT_VERSION)
//---
// Library management
//---
/* gint_install() - install and start gint
This function installs event handlers, masks interrupts and switches VBR.
Unless you are doing experimental runtime switching and you know how this
function is implemented, you should not call it. */
void gint_install(void);
/* gint_unload() - unload gint and give back control to the system
This function restores the runtime environment saved by gint_install(). It
is only called when the add-in terminates. To temporarily leave gint during
execution, use gint_switch(). When possible, use syscalls without leaving
gint for better performance. */
void gint_unload(void);
/* gint_switch() - temporarily switch out of gint
/* gint_switch(): Switch out of gint to execute a function
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.
limitations are quite extreme though, so unless you know precisely what
you're doing that requires getting out of gint (eg. BFile), be careful.
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.
This main uses for this switch are going back to the main menu and using
BFile function. You can go back to the main menu easily by calling getkey()
(or getkey_opt() with the GETKEY_MENU flag set) and pressing the MENU key,
or by calling gint_osmenu() below which uses this switch.
@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
/* gint_osmenu(): 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 with gint_switch().
If the user selects the gint application again in the menu, this function
reloads gint and returns. Otherwise, the add-in is fully unloaded by the
system and the application terminates.
This function is typically called when the [MENU] key is pressed during a
getkey() call. */
call to getkey(), but can also be called manually. */
void gint_osmenu(void);
//---
// Public functions
//---
/* gint_intlevel() - configure the level of interrupts
This function changes the interrupt level of the requested interrupt. Make
sure you are aware of interrupt assignments to avoid breaking other code.
This function is mainly used by drivers to enable the interrupts that they
support.
The first parameter 'intid' identifies an interrupt by its position in the
sequence:
IPRA & 0xf000 ; IPRA & 0x0f00 .. IPRA & 0x000f ; IPRB & 0xf000 ..
For instance ID 7 refers to the low nibble of IPRB. These IDs and the range
for which there are valid is heavily platform-dependent and any call to this
function should be wrapped inside an MPU type check. This function will
crash if the provided interrupt ID is invalid.
The interrupt level should be in the range 0 (disabled) .. 15 (highest
priority).
@intid Interrupt ID of the targeted interrupt
@level Requested interrupt level
Returns the interrupt level that was assigned before the call. */
int gint_intlevel(int intid, int level);
/* gint_inthandler() - configure interrupt handlers
/* gint_inthandler(): Install interrupt handlers
This function installs (copies) interrupt handlers in the VBR space of the
application. Each handler is a 32-byte block aligned on a 32-byte boundary.
@ -100,24 +55,27 @@ int gint_intlevel(int intid, int level);
The assembler program will assume that consecutive blocks in the source code
will be consecutive in memory, which is not always true. Avoiding cross-
references is a practical rule to avoid problems. (gint breaks this rule
very often but does it carefully... I guess?)
quite often but does it safely.)
This function allows anyone to replace any interrupt handler so make sure
you're not interfering with usual interrupt assignments.
you're not interfering with interrupt assignments from gint or a library.
The first parameter event_code represents the event code associated with the
interrupt. If it's not a multiple of 0x20 then you're doing something wrong.
The codes are normally platform-dependent, but gint always uses SH7305
codes: SH3 platforms have a translation table. See the documentation for a
list of event codes and their associated interrupts.
list of event codes and their associated interrupts. Also check the
translation table in gint's source in src/core/inth.S for SH3 support.
The handler function must be an interrupt handler: it must not raise
exceptions, must end with 'rts', and it will use the kernel register bank.
You read that right, it must end with rts because gint's main handler saves
registers besides the automated r0..r7. Do *not* use 'rte' in the handler.
For convenience I allow any block size to be loaded as an interrupt handler,
but it should really be a multiple of 0x20 bytes and not override other
handlers. If it's not written in assembler, then you're likely doing
The handler function is run in the kernel register bank with interrupts
disabled and must end with 'rts' (not 'rte') as the main interrupt handler
saves some registers for you. By default, user bank registers are not saved
except for gbr/mach/macl; if you want to call back to arbitrary code safely,
use gint_inth_callback() in your handler.
For convenience gint allows any block size to be loaded as an interrupt
handler, but it should really be a multiple of 0x20 bytes and not override
other handlers. If it's not written in assembler, then you're likely doing
something wrong, especially with __attribute__((interrupt_handler)) which
uses rte.
@ -129,7 +87,23 @@ int gint_intlevel(int intid, int level);
@event_code Identifier of the interrupt block
@handler Address of handler function
@size How many bytes to copy
Returns the VBR address where the handlers was installed. */
Returns the VBR address where the handler was installed. */
void *gint_inthandler(int event_code, void const *handler, size_t size);
/* gint_inth_callback(): Call back arbitrary code from an interrupt handler
Calls the specified function with the given argument after saving the user
context, enabling interrupts and going to user bank. This function is used
to call user code from interrupt handlers, typically from timer or RTC
callbacks.
It is not safe to call from C code in any capacity and is mentioned here
only for documentation purposes; you should really only call this from
an interrupt handler's assembler code.
@callback Callback function, may take no argument in practice
@arg Argument
Returns the return value of the callback. */
int gint_inth_callback(int (*function)(void *arg), void *arg);
#endif /* GINT_GINT */

63
include/gint/intc.h Normal file
View file

@ -0,0 +1,63 @@
//---
// gint:intc - Interrupt Controller
//---
#ifndef GINT_INTC
#define GINT_INTC
//---
// Interrupt names
//---
/* Because of SH3/SH4 differences and the different ways interrupts are mapped
to bits of IPR and IMR registers, gint defines the following names for
interrupt signals, and their corresponding IPR and IMR bits are defined in
the INTC driver. */
enum {
/* Timer Unit [TMU] */
INTC_TMU_TUNI0,
INTC_TMU_TUNI1,
INTC_TMU_TUNI2,
/* Extra Timer Unit [TMU]; SH7305-only except for ETMU_TUNI0 */
INTC_ETMU_TUNI0,
INTC_ETMU_TUNI1,
INTC_ETMU_TUNI2,
INTC_ETMU_TUNI3,
INTC_ETMU_TUNI4,
INTC_ETMU_TUNI5,
/* DMA Controller [DMA0]; a single IPR is used for DEI0..3, and another
IPR is used for DEI4..5 and DADERR together */
INTC_DMA_DEI0,
INTC_DMA_DEI1,
INTC_DMA_DEI2,
INTC_DMA_DEI3,
INTC_DMA_DEI4,
INTC_DMA_DEI5,
INTC_DMA_DADERR,
/* Real-Time Clock [RTC]; a single IPR covers all 3 interrupts */
INTC_RTC_ATI,
INTC_RTC_PRI,
INTC_RTC_CUI,
};
//---
// Interrupt control functions
//---
/* intc_priority(): Configure the level of interrupts
This function changes the interrupt level of the requested interrupt. Make
sure you are aware of interrupt assignments to avoid breaking other code.
This function is mainly used by drivers to enable the interrupts that they
support.
The interrupt level should be in the range 0 (disabled) .. 15 (highest
priority). On SH7305, if the level is not 0, this function also clears the
associated interrupt mask.
@intname Name of the targeted interrupt, from the enumeration above
@level Requested interrupt level
Returns the interrupt level that was assigned before the call. */
int intc_priority(int intname, int level);
#endif /* GINT_INTC */

View file

@ -105,7 +105,7 @@ typedef struct
/* All interrupt priority registers */
union {
sh7705_intc_ipc_t _;
volatile uint16_t *IPRS[8];
volatile uint16_t *IPR[8];
} GPACKED(4);
/* Control registers */
@ -276,7 +276,7 @@ typedef struct
/* Interrupt priority registers */
union {
sh7305_intc_ipc_t *_;
volatile uint16_t *IPRS;
volatile uint16_t *IPR;
};
/* Interrupt mask & mask clear registers */

View file

@ -16,8 +16,9 @@
@vbr New VBR address
@conf_intc Configuration function
@arg Additional argument for conf_intc
Returns the previous VBR address. */
uint32_t cpu_setVBR(uint32_t vbr, void (*conf_intc)(void));
uint32_t cpu_setVBR(uint32_t vbr, void (*conf_intc)(int arg), int arg);
/* cpu_setCPUOPM(): Change the CPU Operation Mode register

View file

@ -10,38 +10,32 @@
/* cpu_setVBR(): Change VBR address */
_cpu_setVBR:
mov.l r9, @-r15
mov.l r8, @-r15
sts.l pr, @-r15
stc.l sr, @-r15
mov #1, r8
mov #28, r0
shld r0, r8
/* Block all interrupts */
/* Block all interrupts by setting IMASK=15 */
mov #0xf, r1
shll2 r1
shll2 r1
stc sr, r0
or r8, r0
or r1, r0
ldc r0, sr
/* Set the new VBR address */
stc vbr, r9
stc vbr, r8
ldc r4, vbr
/* Call the configuration function */
jsr @r5
nop
/* Enable interrupts again */
stc sr, r0
not r8, r8
and r8, r0
ldc r0, sr
mov r6, r4
/* Enable interrupts again by restoring the status register */
ldc.l @r15+, sr
lds.l @r15+, pr
mov.l @r15+, r8
mov r9, r0
mov r8, r0
rts
mov.l @r15+, r9
mov.l @r15+, r8
.text

23
src/core/drivers.h Normal file
View file

@ -0,0 +1,23 @@
//---
// core:drivers - Driver utilities for the kernel
//
// These are internal definitions; for general driver definitions, see
// <gint/drivers.h> instead.
//---
#ifndef GINT_CORE_DRIVERS
#define GINT_CORE_DRIVERS
#include <gint/drivers.h>
/* Linker script symbols for drivers by increasing levels of priority */
extern gint_driver_t bdrv, edrv;
/* Iterate on drivers in increasing level of priority */
#define driver_asc(var) \
(gint_driver_t *var = &bdrv; var < &edrv; var++)
/* Iterate on drivers in decreasing level of priority */
#define driver_dsc(var) \
(gint_driver_t *var = &edrv; (--var) >= &bdrv;)
#endif /* GINT_CORE_DRIVERS */

View file

@ -1,62 +0,0 @@
//---
// gint:core:gint - Library functions
//---
#include <gint/gint.h>
#include <gint/std/string.h>
#include <gint/hardware.h>
#include <gint/mpu/intc.h>
#include "vbr.h"
/* Interrupt controllers */
GDATA3 sh7705_intc_t SH7705_INTC = {
.IPRS = {
(void *)0xfffffee2, (void *)0xfffffee4,
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
(void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004,
},
.ICR1 = (void *)0xa4000010,
};
GDATA sh7305_intc_t SH7305_INTC = {
.IPRS = (void *)0xa4080000,
.MSK = (void *)0xa4080080,
.MSKCLR = (void *)0xa40800c0,
.USERIMASK = (void *)0xa4700000,
};
//---
// Library functions
//---
/* gint_intlevel() - configure the level of interrupts */
int gint_intlevel(int intid, int level)
{
int shift = (~intid & 0x3) << 2;
volatile uint16_t *ipr;
level &= 0xf;
ipr = isSH3()
? SH7705_INTC.IPRS[intid >> 2] /* SH3-based */
: &SH7305_INTC.IPRS[2 * (intid >> 2)]; /* SH4-based */
int oldlevel = (*ipr >> shift) & 0xf;
*ipr = (*ipr & ~(0xf << shift)) | (level << shift);
return oldlevel;
}
/* gint_inthandler() - configure interrupt handlers */
void *gint_inthandler(int event_code, const void *handler, size_t size)
{
/* Normalize the event code */
event_code -= 0x400;
event_code &= ~0x1f;
/* Prevent overriding the entry gate */
if(event_code < 0) return NULL;
void *dest = (void *)gint_vbr + event_code + 0x640;
return memcpy(dest, handler, size);
}

View file

@ -26,7 +26,6 @@ GBSS uint32_t gint[HW_KEYS];
by testing writable bits in the Port L Control Register (PLCR).
Returns the detected MPU type, falling back on mpu_unknown */
GSECTION(".pretext")
static int mpu_detect(void)
{
#define PLCR SH7705_PFC.PLCR
@ -54,7 +53,6 @@ static int mpu_detect(void)
}
/* hw_detect(): Basic hardware detection */
GSECTION(".pretext")
void hw_detect(void)
{
gint[HWMPU] = mpu_detect();
@ -109,7 +107,6 @@ void hw_detect(void)
#ifdef FXCG50
/* hw_detect(): Basic hardware detection */
GSECTION(".pretext")
void hw_detect(void)
{
gint[HWMPU] = HWMPU_SH7305;

167
src/core/kernel.c Normal file
View file

@ -0,0 +1,167 @@
//---
// gint:core:kernel - Installing and unloading the library
//---
#include <gint/gint.h>
#include <gint/drivers.h>
#include <gint/std/string.h>
#include <gint/hardware.h>
#include <gint/mpu/intc.h>
#include "cpu.h"
#include "vbr.h"
#include "drivers.h"
#include "kernel.h"
static void kinit_cpu(void);
//---
// Context for the CPU and registers not directly managed by a driver
//---
typedef struct
{
uint32_t VBR;
uint32_t CPUOPM;
} ctx_t;
/* System context and gint context for the CPU and VBR */
GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(ctx_t *ctx)
{
if(isSH4()) ctx->CPUOPM = cpu_getCPUOPM();
}
static void ctx_restore(ctx_t *ctx)
{
if(isSH4()) cpu_setCPUOPM(ctx->CPUOPM);
}
//---
// Driver control
//---
static void drivers_wait(void)
{
for driver_asc(d)
{
if(d->wait) d->wait();
}
}
static void drivers_save_and_init(GUNUSED int zero)
{
/* Initialize the CPU, which is done here instead of in a driver */
ctx_save(&sys_ctx);
kinit_cpu();
for driver_asc(d)
{
if(d->ctx_save) d->ctx_save(d->sys_ctx);
if(isSH3() && d->driver_sh3) d->driver_sh3();
if(d->init) d->init();
}
}
static void drivers_restore(int who)
{
for driver_dsc(d)
{
if(d->ctx_restore) d->ctx_restore(who?d->gint_ctx:d->sys_ctx);
}
ctx_restore(who ? &gint_ctx : &sys_ctx);
}
static void drivers_switch(int who)
{
/* Save all drivers in reverse order */
for driver_dsc(d)
{
if(!d->ctx_save || !d->ctx_restore) continue;
d->ctx_save(who ? d->gint_ctx : d->sys_ctx);
}
ctx_save(who ? &gint_ctx : &sys_ctx);
/* Restore the other context */
ctx_restore(who ? &sys_ctx : &gint_ctx);
for driver_asc(d)
{
if(!d->ctx_save || !d->ctx_restore) continue;
d->ctx_restore(who ? d->sys_ctx : d->gint_ctx);
}
}
//---
// Initialization and unloading
//---
static void kinit_cpu(void)
{
cpu_setCPUOPM(cpu_getCPUOPM() | 0x00000008);
}
/* kinit(): Install and start gint */
void kinit(void)
{
/* VBR address, provided by the linker script */
#ifdef FX9860G
gint_ctx.VBR = (uint32_t)&gint_vbr_fx9860g;
#endif
#ifdef FXCG50
gint_ctx.VBR = (gint[HWCALC] == HWCALC_FXCG50)
? (uint32_t)&gint_vbr_fxcg50
: (uint32_t)&gint_vbr_fxcg20;
#endif
/* Event handler entry points */
void *inth_entry = isSH3() ? gint_inth_7705 : gint_inth_7305;
uint32_t exch_size = (uint32_t)&gint_exch_size;
uint32_t tlbh_size = (uint32_t)&gint_tlbh_size;
/* Load the event handler entry points into memory */
void *vbr = (void *)gint_ctx.VBR;
memcpy(vbr + 0x100, gint_exch, exch_size);
memcpy(vbr + 0x400, gint_tlbh, tlbh_size);
memcpy(vbr + 0x600, inth_entry, 64);
/* Take control of the VBR and roll! */
drivers_wait();
sys_ctx.VBR = cpu_setVBR(gint_ctx.VBR, drivers_save_and_init, 0);
}
/* gint_inthandler(): Install interrupt handlers */
void *gint_inthandler(int event_code, const void *handler, size_t size)
{
/* Normalize the event code */
if(event_code < 0x400) return NULL;
event_code -= 0x400;
event_code &= ~0x1f;
void *dest = (void *)gint_ctx.VBR + event_code + 0x640;
return memcpy(dest, handler, size);
}
/* gint_switch(): Temporarily switch out of gint */
void gint_switch(void (*function)(void))
{
/* Switch from gint to the OS after a short wait */
drivers_wait();
cpu_setVBR(sys_ctx.VBR, drivers_switch, 1);
if(function) function();
/* Then switch back to gint once the OS finishes working */
drivers_wait();
cpu_setVBR(gint_ctx.VBR, drivers_switch, 0);
}
/* kquit(): Quit gint and give back control to the system */
void kquit(void)
{
/* Wait for hardware tasks then restore all of the drivers' state and
return the VBR space to the OS */
drivers_wait();
cpu_setVBR(sys_ctx.VBR, drivers_restore, 0);
}

14
src/core/kernel.h Normal file
View file

@ -0,0 +1,14 @@
//---
// core:kernel - Kernel functions
//---
#ifndef GINT_CORE_KERNEL
#define GINT_CORE_KERNEL
/* kinit(): Install and start gint */
void kinit(void);
/* kquit(): Quit gint and give back control to the system */
void kquit(void);
#endif /* GINT_CORE_KERNEL */

73
src/core/osmenu.c Normal file
View file

@ -0,0 +1,73 @@
#include <gint/gint.h>
#include <gint/display.h>
#include <gint/std/string.h>
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(int row, int column, int keycode);
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);
static int __osmenu_id;
static void __osmenu_handler(void)
{
__PutKeyCode(0x04, 0x09, 0);
__Timer_Stop(__osmenu_id);
__Timer_Deinstall(__osmenu_id);
}
static void __osmenu(void)
{
__ClearKeyBuffer();
#ifdef FX9860G
memcpy(__GetVRAMAddress(), gint_vram, 1024);
#endif
#ifdef FXCG50
/* Unfortunately ineffective (main menu probably reenables it)
__ConfigureStatusArea(3); */
/* TODO: Improve copied VRAM behavior in gint_osmenu() on fxcg50 */
uint16_t *vram1, *vram2;
dgetvram(&vram1, &vram2);
uint16_t *dst = __GetVRAMAddress();
uint16_t *src = (gint_vram == vram1) ? vram2 + 6 : vram1 + 6;
for(int y = 0; y < 216; y++, dst+=384, src+=396)
for(int x = 0; x < 384; x++)
{
dst[x] = src[x];
}
#endif
/* Mysteriously crashes when coming back; might be useful another time
instead of GetKeyWait()
int C=0x04, R=0x09;
__SpecialMatrixCodeProcessing(&C, &R); */
__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 /* KEYWAIT_HALTON_TIMEROFF */,
1 /* Delay in seconds */,
0 /* Enable return to main menu */,
&keycode);
}
/* gint_osmenu() - switch out of gint and call the calculator's main menu */
void gint_osmenu(void)
{
gint_switch(__osmenu);
}

View file

@ -1,294 +0,0 @@
//---
// gint:core:setup - Installing and unloading the library
//---
#include <gint/gint.h>
#include <gint/drivers.h>
#include <gint/std/string.h>
#include <gint/hardware.h>
#include <gint/mpu/intc.h>
#include <gint/defs/util.h>
#include <gint/display.h>
#include "cpu.h"
#include "vbr.h"
/* VBR addresses for gint and the system */
GBSS uint32_t gint_vbr;
GBSS static uint32_t system_vbr;
/* Driver table */
extern gint_driver_t bdrv, edrv;
//---
// Context system for gint's core
//---
typedef struct
{
uint16_t iprs[12];
uint8_t masks[13];
uint32_t CPUOPM;
} GPACKED(2) gint_core_ctx;
/* gint_ctx_save() - save interrupt controller configuration
@arg ctx gint core context object */
GMAPPED static void gint_ctx_save(gint_core_ctx *ctx)
{
if(isSH3())
{
for(int i = 0; i < 8; i++)
ctx->iprs[i] = *(SH7705_INTC.IPRS[i]);
}
else
{
for(int i = 0; i < 12; 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;
ctx->CPUOPM = cpu_getCPUOPM();
}
}
/* gint_ctx_restore() - restore interrupt controller configuration
@arg ctx gint core context object */
GMAPPED static void gint_ctx_restore(gint_core_ctx *ctx)
{
if(isSH3())
{
for(int i = 0; i < 8; i++)
*(SH7705_INTC.IPRS[i]) = ctx->iprs[i];
}
else
{
for(int i = 0; i < 12; 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];
}
cpu_setCPUOPM(ctx->CPUOPM);
}
}
//---
// Initialization and unloading
//---
/* System context */
GBSS static gint_core_ctx sys_ctx;
/* lock() - lock interrupts to avoid receiving unsupported signals */
GMAPPED static void lock(void)
{
/* Just disable everything, drivers will enable what they support */
if(isSH3()) for(int i = 0; i < 8; i++)
*(SH7705_INTC.IPRS[i]) = 0x0000;
else for(int i = 0; i < 12; i++)
SH7305_INTC.IPRS[2 * i] = 0x0000;
cpu_setCPUOPM(cpu_getCPUOPM() | 0x00000008);
}
/* gint_install() - install and start gint */
void gint_install(void)
{
/* VBR address, provided by the linker script */
#ifdef FX9860G
gint_vbr = (uint32_t)&gint_vbr_fx9860g;
#endif
#ifdef FXCG50
gint_vbr = (gint[HWCALC] == HWCALC_FXCG50)
? (uint32_t)&gint_vbr_fxcg50
: (uint32_t)&gint_vbr_fxcg20;
#endif
/* Event handler entry points */
void *inth_entry = isSH3() ? gint_inth_7705 : gint_inth_7305;
/* Size of the exception and TLB handlers */
uint32_t exch_size = (uint32_t)&gint_exch_size;
uint32_t tlbh_size = (uint32_t)&gint_tlbh_size;
/* First save the hardware configuration. This step is crucial because
we don't want the system to find out about us directly manipulating
the peripheral modules */
gint_ctx_save(&sys_ctx);
/* Load the event handler entry points into memory */
void *vbr = (void *)gint_vbr;
memcpy(vbr + 0x100, gint_exch, exch_size);
memcpy(vbr + 0x400, gint_tlbh, tlbh_size);
memcpy(vbr + 0x600, inth_entry, 64);
/* Time to switch VBR and roll! */
system_vbr = cpu_setVBR(gint_vbr, lock);
}
/* unlock() - unlock interrupts, restoring system settings */
GMAPPED static void unlock(void)
{
/* Restore all driver settings, but do it in reverse order of loading
to honor the dependency system */
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
{
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 */
void gint_unload(void)
{
/* First wait for all the drivers to finish their current jobs */
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
{
if(drv->wait) drv->wait();
}
cpu_setVBR(system_vbr, unlock);
}
//---
// Hot switching to operating system
//---
static gint_core_ctx gint_ctx;
extern gint_driver_t bdrv, edrv;
GMAPPED 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 */
gint_ctx_restore(&sys_ctx);
for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++)
{
if(!drv->ctx_save || !drv->ctx_restore) continue;
drv->ctx_restore(drv->sys_ctx);
}
}
GMAPPED static void gint_switch_in(void)
{
/* Save system state again */
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
{
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 */
gint_ctx_restore(&gint_ctx);
for(gint_driver_t *drv = &bdrv; drv < &edrv; drv++)
{
if(!drv->ctx_save || !drv->ctx_restore) continue;
drv->ctx_restore(drv->gint_ctx);
}
}
/* gint_switch() - temporarily switch out of gint */
void gint_switch(void (*function)(void))
{
/* Wait for all the drivers to finish their current jobs */
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
{
if(drv->wait) drv->wait();
}
cpu_setVBR(system_vbr, gint_switch_out);
if(function) function();
/* Wait for the OS to finish working */
for(gint_driver_t *drv = &edrv; (--drv) >= &bdrv;)
{
if(drv->wait) drv->wait();
}
cpu_setVBR(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(int row, int column, int keycode);
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);
static int __osmenu_id;
static void __osmenu_handler(void)
{
__PutKeyCode(0x04, 0x09, 0);
__Timer_Stop(__osmenu_id);
__Timer_Deinstall(__osmenu_id);
}
static void __osmenu(void)
{
__ClearKeyBuffer();
#ifdef FX9860G
memcpy(__GetVRAMAddress(), gint_vram, 1024);
#endif
#ifdef FXCG50
/* Unfortunately ineffective (main menu probably reenables it)
__ConfigureStatusArea(3); */
/* TODO: Improve copied VRAM behavior in gint_osmenu() on fxcg50 */
uint16_t *vram1, *vram2;
dgetvram(&vram1, &vram2);
uint16_t *dst = __GetVRAMAddress();
uint16_t *src = (gint_vram == vram1) ? vram2 + 6 : vram1 + 6;
for(int y = 0; y < 216; y++, dst+=384, src+=396)
for(int x = 0; x < 384; x++)
{
dst[x] = src[x];
}
#endif
/* Mysteriously crashes when coming back; might be useful another time
instead of GetKeyWait()
int C=0x04, R=0x09;
__SpecialMatrixCodeProcessing(&C, &R); */
__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 /* KEYWAIT_HALTON_TIMEROFF */,
1 /* Delay in seconds */,
0 /* Enable return to main menu */,
&keycode);
}
/* gint_osmenu() - switch out of gint and call the calculator's main menu */
void gint_osmenu(void)
{
gint_switch(__osmenu);
}

View file

@ -10,6 +10,8 @@
#include <gint/hardware.h>
#include <gint/exc.h>
#include "kernel.h"
/* Symbols provided by the linker script. For sections:
- l* represents the load address (source address in ROM)
- s* represents the size of the section
@ -22,19 +24,19 @@ extern uint32_t
lilram, silram, rilram, /* IL memory section */
lxram, sxram, rxram, /* X memory section */
lyram, syram, ryram, /* Y memory section */
sbss, rbss, /* User's BSS section */
btors, mtors, etors; /* Constructor/destructor arrays */
extern gint_driver_t
bdrv, edrv; /* Driver table */
sbss, rbss; /* User's BSS section */
/* Constructor and destructor arrays */
extern void (*bctors)(void), (*ectors)(void);
extern void (*bdtors)(void), (*edtors)(void);
/* User-provided main() function */
int main(int isappli, int optnum);
/* regcpy() - copy a memory region using symbol information
/* regcpy(): Copy a memory region using symbol information
@l Source pointer (load address)
@s Size of area (should be a multiple of 16)
@r Destination pointer (relocation address) */
GSECTION(".pretext")
static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
{
while(s > 0)
@ -48,10 +50,9 @@ static void regcpy(uint32_t * restrict l, int32_t s, uint32_t * restrict r)
}
#define regcpy(l, s, r) regcpy(&l, (int32_t)&s, &r)
/* regclr() - clear a memory region using symbol information
/* regclr(): Clear a memory region using symbol information
@r Source pointer (base address)
@s Size of area (should be a multiple of 16) */
GSECTION(".pretext")
static void regclr(uint32_t *r, int32_t s)
{
while(s > 0)
@ -65,20 +66,17 @@ static void regclr(uint32_t *r, int32_t s)
}
#define regclr(r, s) regclr(&r, (int32_t)&s)
/* acall() - call an array of functions (constructors or destructors)
/* callarray(): Call an array of functions (constructors or destructors)
@f First element of array
@l First element outside of the array */
GSECTION(".pretext")
static void acall(void (**f)(void), void (**l)(void))
static void callarray(void (**f)(void), void (**l)(void))
{
while(f < l) (*(*f++))();
}
#define acall(f, l) acall((void (**)(void))&f, (void (**)(void))&l)
/* start() - this is where it all starts
Returns a status code. Invoking main menu is better than returning! */
GSECTION(".pretext.entry")
GSECTION(".text.entry")
int start(int isappli, int optnum)
{
/* We are currently in a dynamic userspace mapping of an add-in run
@ -88,17 +86,17 @@ int start(int isappli, int optnum)
Do not disturb the operating system.
gint will silently run in an isolated part of the memory (fx9860g)
or at the start of the RAM (fxcg50). It acts as a kernel,
redirecting interrupts and reimplementing drivers, so we can't rely
too much on the system. Ladies and gentlemen, let's have fun! ;D */
or at the start of the RAM (fxcg50). The kernel will redirect
interrupts and load its own drivers, so we can't rely too much on
the system. Ladies and gentlemen, let's have fun! ;D */
/* For now, we use the system's memory mapper for ROM. We'll still do
it from our TLB miss handler once we're installed. RAM is always
it later in our TLB miss handler once we're installed. RAM is always
fully mapped, but we need to initialize it. We also need to do some
hardware detection because old fx9860g models have a different
processor with some incompatible features. */
/* Detect architecture - this will tell SH3 from SH4 on fx9860g */
/* Detect architecture; this will tell SH3 from SH4 on fx9860g */
hw_detect();
/* Load data sections and wipe the bss section. This has to be done
@ -110,46 +108,31 @@ int start(int isappli, int optnum)
regcpy(lyram, syram, ryram);
regclr(rbss, sbss);
/* Install gint and switch VBR */
gint_install();
/* Install gint, switch VBR and initialize drivers */
kinit();
/* We are now running on our own in kernel mode. Since we have taken
control of interrupts, pretty much any interaction with the system
will break it. We'll limit our use of syscalls and do device driving
ourselves. (Hopefully we can add cool features in the process!) */
gint_driver_t *drv;
/* Now that we have initialized the kernel, we are ready to start the
hosted user application, which has its own constructors and
destructors to work with. */
/* Initialize all drivers by saving the system settings */
for(drv = &bdrv; drv < &edrv; drv++)
{
/* Wait for hardware availability */
if(drv->wait) drv->wait();
/* Hook for old SH3 fx9860g machines */
if(isSH3() && drv->driver_sh3) drv->driver_sh3();
if(drv->ctx_save) drv->ctx_save(drv->sys_ctx);
if(drv->init) drv->init();
}
/* With gint fully initialized, we are ready to start the hosted user
application. We have already loaded the RAM sections earlier; all
that's left is calling the constructors and rolling main(). */
acall(btors, mtors);
callarray(&bctors, &ectors);
int ret = main(isappli, optnum);
acall(mtors, etors);
callarray(&bdtors, &edtors);
/* Before leaving the application, we need to clean up our mess. We
have changed many hardware settings while accessing the peripheral
modules. The OS is bound to be confused (and hang, or crash, or any
other kind of giving up) if we don't restore them. */
/* Before leaving the application, we need to clean everything we
changed to hardware settings and peripheral modules. The OS is bound
to be confused (and hang, or crash, or any other kind of giving up)
if we don't restore them. */
/* Unload gint and give back control to the system. Driver settings
will be restored while interrupts are disabled */
gint_unload();
kquit();
/* TODO: Invoke main menu instead of returning? */
/* TODO: Invoke main menu so that add-in can restart? */
return ret;
}

View file

@ -9,7 +9,7 @@
** * File system, because it's a mess and we might ruin the ROM.
*/
.section .pretext
.text
/* Dynamic allocation */
.global _malloc
@ -114,8 +114,6 @@ syscall_table:
/* Dynamic allocation */
.section .pretext
_malloc:
syscall(0x1f44)
_free:

View file

@ -16,9 +16,6 @@ extern char gint_vbr_fxcg50[];
extern char gint_vbr_fxcg20[];
#endif
/* gint's VBR address as determined at runtime; not valid before kinit() */
extern uint32_t gint_vbr;
/* The kernel's interrupt and exception handlers' entry points */
void gint_exch(void);
void gint_tlbh(void);

View file

@ -3,10 +3,10 @@
#include <gint/mpu/power.h>
#include <gint/mpu/intc.h>
#include <gint/gint.h>
#include <gint/intc.h>
#include <gint/dma.h>
#include <gint/drivers.h>
#include <gint/clock.h>
#include <gint/display.h>
#include <gint/exc.h>
#define DMA SH7305_DMA
@ -201,12 +201,13 @@ static void init(void)
/* Set interrupt priority to 3 (IPRE[15..12] for first three channels,
IPRF[11..8] for last two and error gate */
gint_intlevel(16, 3);
gint_intlevel(21, 3);
/* Unmask the channel interrupts and the address error */
INTC.MSKCLR->IMR1 = 0x0f;
INTC.MSKCLR->IMR5 = 0x70;
intc_priority(INTC_DMA_DEI0, 3);
intc_priority(INTC_DMA_DEI1, 3);
intc_priority(INTC_DMA_DEI2, 3);
intc_priority(INTC_DMA_DEI3, 3);
intc_priority(INTC_DMA_DEI4, 3);
intc_priority(INTC_DMA_DEI5, 3);
intc_priority(INTC_DMA_DADERR, 3);
/* Clear blocking flags and enable the master switch */
DMA.OR.AE = 0;

180
src/intc/intc.c Normal file
View file

@ -0,0 +1,180 @@
#include <gint/drivers.h>
#include <gint/hardware.h>
#include <gint/mpu/intc.h>
/* Interrupt controllers */
GDATA3 sh7705_intc_t SH7705_INTC = {
.IPR = {
(void *)0xfffffee2, (void *)0xfffffee4,
(void *)0xa4000016, (void *)0xa4000018, (void *)0xa400001a,
(void *)0xa4080000, (void *)0xa4080002, (void *)0xa4080004,
},
.ICR1 = (void *)0xa4000010,
};
GDATA sh7305_intc_t SH7305_INTC = {
.IPR = (void *)0xa4080000,
.MSK = (void *)0xa4080080,
.MSKCLR = (void *)0xa40800c0,
.USERIMASK = (void *)0xa4700000,
};
/* Interrupt IPR and IMR positions. The order of entries is as in the named
list of interrupt signals in <gint/intc.h>. */
/* Friendly names for IPR and IMR register numbers */
enum{ IPRA, IPRB, IPRC, IPRD, IPRE, IPRF, IPRG, IPRH, IPRI, IPRJ, IPRK, IPRL };
enum{ IMR0, IMR1, IMR2, IMR3, IMR4, IMR5, IMR6, IMR7, IMR8, IMR9, IMR10 };
#define _ 0,0
struct info {
uint16_t IPR4, IPR4bits, IMR, IMRbits;
/* Only set if different than IPR4 with IPR4bits */
uint16_t IPR3, IPR3bits;
} info[] = {
/* Standard TMU */
{ IPRA, 0xf000, IMR4, 0x10, _ },
{ IPRA, 0x0f00, IMR4, 0x20, _ },
{ IPRA, 0x00f0, IMR4, 0x40, _ },
/* ETMU */
{ IPRJ, 0xf000, IMR6, 0x08, IPRF, 0x000f },
{ IPRG, 0x0f00, IMR5, 0x02, _ },
{ IPRG, 0x00f0, IMR5, 0x04, _ },
{ IPRE, 0x00f0, IMR2, 0x01, _ },
{ IPRI, 0xf000, IMR6, 0x10, _ },
{ IPRL, 0xf000, IMR8, 0x02, _ },
/* DMA */
{ IPRE, 0xf000, IMR1, 0x01, _ /* Not supported on SH3! */ },
{ IPRE, 0xf000, IMR1, 0x02, _ },
{ IPRE, 0xf000, IMR1, 0x04, _ },
{ IPRE, 0xf000, IMR1, 0x08, _ },
{ IPRF, 0x0f00, IMR5, 0x10, _ },
{ IPRF, 0x0f00, IMR5, 0x20, _ },
{ IPRF, 0x0f00, IMR5, 0x40, _ },
/* RTC */
{ IPRK, 0xf000, IMR10, 0x04, IPRA, 0x000f },
{ IPRK, 0xf000, IMR10, 0x02, IPRA, 0x000f },
{ IPRK, 0xf000, IMR10, 0x01, IPRA, 0x000f },
};
//---
// Interrupt controller functions
//---
/* intc_priority(): Configure the level of interrupts */
int intc_priority(int intname, int level)
{
struct info *i = &info[intname];
int IPRn = i->IPR4, IPRbits = i->IPR4bits;
if(isSH3() && i->IPR3bits != 0)
{
IPRn = i->IPR3;
IPRbits = i->IPR3bits;
}
/* Bit-shift for the mask */
int shift = 0;
while(IPRbits >>= 4) shift += 4;
uint16_t volatile *IPR;
IPR = isSH3() ? SH7705_INTC.IPR[IPRn] : &SH7305_INTC.IPR[2*IPRn];
int oldlevel = (*IPR >> shift) & 0xf;
*IPR = (*IPR & ~(0xf << shift)) | (level << shift);
if(isSH4() && level > 0 && i->IMRbits)
{
uint8_t volatile *MSKCLR = &SH7305_INTC.MSKCLR->IMR0;
MSKCLR[4*i->IMR] = i->IMRbits;
}
return oldlevel;
}
//---
// Initialization
//---
static void init(void)
{
/* Just disable everything, drivers will enable what they support */
if(isSH3()) for(int i = 0; i < 8; i++)
*(SH7705_INTC.IPR[i]) = 0x0000;
else for(int i = 0; i < 12; i++)
SH7305_INTC.IPR[2 * i] = 0x0000;
}
//---
// Driver context
//---
typedef struct
{
uint16_t IPR[12];
uint8_t MSK[13];
} ctx_t;
GBSS static ctx_t sys_ctx, gint_ctx;
static void ctx_save(void *buf)
{
ctx_t *ctx = buf;
if(isSH3())
{
for(int i = 0; i < 8; i++)
ctx->IPR[i] = *(SH7705_INTC.IPR[i]);
}
else
{
for(int i = 0; i < 12; i++)
ctx->IPR[i] = SH7305_INTC.IPR[2 * i];
uint8_t *IMR = (void *)SH7305_INTC.MSK;
for(int i = 0; i < 13; i++, IMR += 4)
ctx->MSK[i] = *IMR;
}
}
static void ctx_restore(void *buf)
{
ctx_t *ctx = buf;
if(isSH3())
{
for(int i = 0; i < 8; i++)
*(SH7705_INTC.IPR[i]) = ctx->IPR[i];
}
else
{
for(int i = 0; i < 12; i++)
SH7305_INTC.IPR[2 * i] = ctx->IPR[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->MSK[i];
}
}
}
//---
// Driver structure definition
//---
gint_driver_t drv_intc = {
.name = "INTC",
.init = init,
.sys_ctx = &sys_ctx,
.gint_ctx = &gint_ctx,
.ctx_save = ctx_save,
.ctx_restore = ctx_restore,
};
GINT_DECLARE_DRIVER(0, drv_intc);

View file

@ -57,7 +57,7 @@ GDATA static uint time = 0;
/* buffer_push(): Add an event in the keyboard buffer
Returns non-zero if the event cannot be pushed. */
GMAPPED static int buffer_push(driver_event_t ev)
static int buffer_push(driver_event_t ev)
{
int next = (buffer_end + 1) % KEYBOARD_QUEUE_SIZE;
if(next == buffer_start) return 1;
@ -79,7 +79,7 @@ static int buffer_poll(driver_event_t *ev)
}
/* keysc_frame(): Generate driver events from KEYSC state */
GMAPPED static void keysc_frame(void)
static void keysc_frame(void)
{
GALIGNED(2) uint8_t scan[12] = { 0 };
@ -248,7 +248,7 @@ int keydown_any(int key, ...)
// Driver initialization
//---
GMAPPED static int callback(GUNUSED volatile void *arg)
static int callback(GUNUSED volatile void *arg)
{
keysc_frame();
time++;

View file

@ -5,6 +5,7 @@
#include <gint/rtc.h>
#include <gint/drivers.h>
#include <gint/gint.h>
#include <gint/intc.h>
#include <gint/defs/types.h>
#include <gint/hardware.h>
@ -147,7 +148,7 @@ static void init(void)
/* Disable the periodic interrupt for now, but give it priority 5 */
RTC->RCR2.PES = RTC_NONE;
gint_intlevel(isSH3() ? 3 : 40, 5);
intc_priority(INTC_RTC_PRI, 5);
gint[HWRTC] = HW_LOADED | HWRTC_TIMER;
}

View file

@ -6,6 +6,7 @@
#include <gint/drivers.h>
#include <gint/gint.h>
#include <gint/clock.h>
#include <gint/intc.h>
#include <gint/mpu/intc.h>
#include <gint/mpu/tmu.h>
@ -183,7 +184,7 @@ void timer_wait(int id)
//---
/* timer_timeout() - callback that sets a flag and halts the timer */
GMAPPED int timer_timeout(volatile void *arg)
int timer_timeout(volatile void *arg)
{
volatile int *x = arg;
(*x)++;
@ -283,29 +284,19 @@ static void init(void)
gint_inthandler(0xc60, inth_etmu_help, 32);
/* Enable TMU0 at level 13, TMU1 at level 11, TMU2 at level 9 */
gint_intlevel(0, 13);
gint_intlevel(1, 11);
gint_intlevel(2, 9);
intc_priority(INTC_TMU_TUNI0, 13);
intc_priority(INTC_TMU_TUNI1, 11);
intc_priority(INTC_TMU_TUNI2, 9);
/* Enable the extra TMUs at level 7 */
if(isSH3()) gint_intlevel(23, 7);
else
intc_priority(INTC_ETMU_TUNI0, 7);
if(isSH4())
{
/* Unmask the standard timers' interrupts */
SH7305_INTC.MSKCLR->IMR4 = 0x70;
gint_intlevel(36, 7);
gint_intlevel(25, 7);
gint_intlevel(26, 7);
gint_intlevel(18, 7);
gint_intlevel(32, 7);
gint_intlevel(44, 7);
/* Unmask the extra timers' interrupts */
SH7305_INTC.MSKCLR->IMR2 = 0x01;
SH7305_INTC.MSKCLR->IMR5 = 0x06;
SH7305_INTC.MSKCLR->IMR6 = 0x18;
SH7305_INTC.MSKCLR->IMR8 = 0x02;
intc_priority(INTC_ETMU_TUNI1, 7);
intc_priority(INTC_ETMU_TUNI2, 7);
intc_priority(INTC_ETMU_TUNI3, 7);
intc_priority(INTC_ETMU_TUNI4, 7);
intc_priority(INTC_ETMU_TUNI5, 7);
}
/* Record details in gint's hardware information interface */