kernel: compact VBR scheme on SH3

I have recenty discovered that the so-called "rram" section used by gint
to store its VBR space and a couple memory structures gets overwritten
when returning to the main menu. It is thus necessary to get rid of it
and store that data somewhere else.

My current lead is to have it at the start of the static RAM by querying
its address in the TLB. However, the static RAM is very small on SH3
(8k) so the VBR must be made more compact.

This change elaborates the event code translation scheme used on SH3 to
emulate SH4 event codes. It is now used to translate the event codes to
a gint-specific VBR layout that leaves no gaps and thus reduces the size
of the VBR space. The gint_inthandler() method has to be modified for
every new SH3 interrupt to maintain this scheme.
This commit is contained in:
Lephe 2020-07-09 10:32:53 +02:00
parent a99bffe7f4
commit 2b1f408cb4
Signed by untrusted user: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
7 changed files with 104 additions and 23 deletions

View File

@ -200,7 +200,7 @@ SECTIONS
There's an unused 0x100-byte gap at the start of the VBR space.
The VBR space is already a large block (> 2 kiB), so I'm cutting off
the gap to spare some memory */
_gint_vbr_fx9860g = 0x8800df00;
_gint_vbr_fx9860g = ORIGIN(vbr) - 0x100;
. = ORIGIN(rram);

View File

@ -63,9 +63,12 @@ void gint_osmenu(void);
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. Also check the
translation table in gint's source in src/core/inth.S for SH3 support.
codes. SH3 platforms have a different, compact VBR layout. gint_inthandler()
translates the provided SH7305 codes to the compact layout and the interrupt
handler translates the hardware SH3 codes to the compact layout as well. See
gint's source in <src/core/inth.S> and <src/core/kernel.c>. Please note that
gint_inthandler() uses a table that must be modified for every new SH3
interrupt code to extend the compact scheme.
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
@ -76,8 +79,8 @@ void gint_osmenu(void);
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.
something wrong. Using __attribute__((interrupt_handler)), which uses rte,
is especially wrong.
It is common for interrupt handlers to have a few bytes of data, such as the
address of a callback function. gint often stores this data in the last

View File

@ -137,21 +137,38 @@ _gint_inth_7705:
.section .gint.data
.align 4
/* EVENT CODE TRANSLATION TABLE - 96 BYTES */
/* EVENT CODE TRANSLATION TABLE - 96 BYTES
This table was originally used to translate SH3 event codes to corresponding
SH4 events codes so that the VBR can be laid out in the SH4 model on every
platform. However, due to memory size limitations on SH3, it now also
translates SH4 event codes to adjacent gates numbers to reduce the size of
the VBR section.
The VBR interrupt space on SH3 is laid out as follows:
VBR offset SH3 events Description
----------------------------------------------------------------
0x600 --- --- Entry gate
0x640 400 420 440 --- TMU0, TMU1, TMU2 and a helper
0x6c0 f00 --- --- --- ETMU0, ETMU1, ETMU2 and a helper
0x740 4a0 --- RTC Periodic Interrupt and a helper
----------------------------------------------------------------
This represents 12 gates so the VBR space ends at VBR + 0x780. */
_inth_remap:
.byte 0x00, 0x01, 0x02, 0xfe, 0x34, 0x35, 0x36, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f
.byte 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0x20, 0x21, 0x22, 0x23, 0x28, 0x28, 0xff, 0x28
.byte 0x48, 0x48, 0xff, 0x48, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0x2d, 0x2d, 0xff, 0xff, 0x2d, 0x2d, 0xff, 0xff
.byte 0, 1, 2, 0xff, 0xff, 8, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0x2f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
.byte 4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
#endif

View File

@ -131,15 +131,68 @@ void kinit(void)
sys_ctx.VBR = cpu_setVBR(gint_ctx.VBR, drivers_save_and_init, 0);
}
/* Due to dire space restrictions on SH3, event codes that are translated to
SH4 are further remapped into the VBR space to eliminate gaps and save
space. Each entry in this table represents a 32-byte block after the
interrupt handler's entry gate. It shows the SH4 event code whose gate is
placed on that block (some of gint's SH4 event codes are invented to host
helper blocks).
For instance, the 5th block after the entry gate hosts the interrupt handler
for SH4 event 0x9e0, which is ETMU0 underflow.
The _inth_remap table in src/core/inth.S combines the SH3-SH4 translation
with the compact translation, hence its entry for 0xf00 (the SH3 event code
for ETMU0 underflow) is the offset in this table where 0x9e0 is stored,
which is 4. */
static const uint16_t sh3_vbr_map[] = {
0x400, /* TMU0 underflow */
0x420, /* TMU1 underflow */
0x440, /* TMU2 underflow */
0x460, /* (gint custom: TMU helper) */
0x9e0, /* ETMU0 underflow */
0xc20, /* ETMU1 underflow (used as helper on SH3) */
0xc40, /* ETMU2 underflow (used as helper on SH3) */
0xc60, /* (gint custom: ETMU helper) */
0xaa0, /* RTC Periodic Interrupt */
0xae0, /* (gint custom: RTC helper) */
0
};
/* gint_inthandler(): Install interrupt handlers */
void *gint_inthandler(int event_code, const void *handler, size_t size)
{
int offset;
/* 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;
/* On SH3, make VBR compact. Use this offset specified in the VBR map
above to avoid gaps */
if(isSH3())
{
int index = 0;
while(sh3_vbr_map[index])
{
if((int)sh3_vbr_map[index] == event_code) break;
index++;
}
/* This happens if the event has not beed added to the table,
ie. the compact VBR scheme does not support this code */
if(!sh3_vbr_map[index]) return NULL;
offset = index * 0x20;
}
/* On SH4, just use the code as offset */
else
{
offset = event_code - 0x400;
}
/* 0x40 is the size of the entry gate */
void *dest = (void *)gint_ctx.VBR + 0x600 + 0x40 + offset;
return memcpy(dest, handler, size);
}

View File

@ -28,11 +28,11 @@ 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 {
static struct info {
uint16_t IPR4, IPR4bits, IMR, IMRbits;
/* Only set if different than IPR4 with IPR4bits */
uint16_t IPR3, IPR3bits;
} info[] = {
} const info[] = {
/* Standard TMU */
{ IPRA, 0xf000, IMR4, 0x10, _ },
{ IPRA, 0x0f00, IMR4, 0x20, _ },

View File

@ -215,7 +215,9 @@ _inth_etmux:
* 0x600 to reach the interrupt handlers
* 0x040 to jump over the entry gate
* 0x840 to reach the handler of ETMU2
* Skip over the first instructions */
* Skip over the first instructions
This is different on SH3 due to the compact scheme so it's edited
dynamically at install time. */
1: .long 0xe80 + (.shared4 - _inth_etmu2)
.id_etmux:

View File

@ -374,7 +374,13 @@ static void init(void)
timers[i] = h + 24;
if(i == 5) continue;
uint32_t *data_id = (h + 20);
/* On SH3, the ETMU handler is not at an offset of 0x840 (event
code 0xc40) but at an offset of 0xc0 */
uint32_t *etmu_offset = h + 16;
if(isSH3()) *etmu_offset = *etmu_offset - 0x840 + 0xc0;
uint32_t *data_id = h + 20;
*data_id = i;
}