From 2b1f408cb4bec7a81992349cb19783c6738039dc Mon Sep 17 00:00:00 2001 From: Lephe Date: Thu, 9 Jul 2020 10:32:53 +0200 Subject: [PATCH] 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. --- fx9860g.ld | 2 +- include/gint/gint.h | 13 +++++++---- src/core/inth.S | 39 ++++++++++++++++++++++--------- src/core/kernel.c | 57 +++++++++++++++++++++++++++++++++++++++++++-- src/intc/intc.c | 4 ++-- src/tmu/inth.s | 4 +++- src/tmu/tmu.c | 8 ++++++- 7 files changed, 104 insertions(+), 23 deletions(-) diff --git a/fx9860g.ld b/fx9860g.ld index 5160efd..14df6ef 100644 --- a/fx9860g.ld +++ b/fx9860g.ld @@ -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); diff --git a/include/gint/gint.h b/include/gint/gint.h index 3802660..8046b6e 100644 --- a/include/gint/gint.h +++ b/include/gint/gint.h @@ -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 and . 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 diff --git a/src/core/inth.S b/src/core/inth.S index ba20123..9f9bd0d 100644 --- a/src/core/inth.S +++ b/src/core/inth.S @@ -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 diff --git a/src/core/kernel.c b/src/core/kernel.c index 83d5b14..b4014c5 100644 --- a/src/core/kernel.c +++ b/src/core/kernel.c @@ -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); } diff --git a/src/intc/intc.c b/src/intc/intc.c index 02f1549..ae35990 100644 --- a/src/intc/intc.c +++ b/src/intc/intc.c @@ -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, _ }, diff --git a/src/tmu/inth.s b/src/tmu/inth.s index d6a028d..13cddb5 100644 --- a/src/tmu/inth.s +++ b/src/tmu/inth.s @@ -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: diff --git a/src/tmu/tmu.c b/src/tmu/tmu.c index 2d7d731..24c2d41 100644 --- a/src/tmu/tmu.c +++ b/src/tmu/tmu.c @@ -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; }