diff --git a/TODO b/TODO index 81d3b91..34bcdd2 100644 --- a/TODO +++ b/TODO @@ -10,8 +10,7 @@ Tests to run. * topti: all charsets Complementary elements on existing code. -* core: make it possiblé to catch exceptions (gintctl's memory browser) -* core: check if -DX still works in GCC 9 and if not, put spaces +* topti: support unicode fonts * gray: find good values for more models than the Graph 35+E II * render: get rid of GINT_NEED_VRAM and #define vram gint_vram if you need * dma: dma_memcpy() and dma_memset(), possibly requiring alignment diff --git a/include/gint/exc.h b/include/gint/exc.h index 372755a..bbe8436 100644 --- a/include/gint/exc.h +++ b/include/gint/exc.h @@ -9,13 +9,58 @@ #ifndef GINT_EXC #define GINT_EXC -/* gint_exc(): Exception handler fatal error message - Displays a full-screen fatal error message from an exception event code. - Some custom event codes are also used for library failure. This function is - typically called by exception handlers and returns after a dupdate_noint(). - You probably want to soft-lock or quit the add-in after calling it. */ -void gint_exc(uint32_t code); +#include +#include -/* TODO: Add functions to disable fatal errors for some exceptions */ +/* gint_panic(): Panic handler function + This function is called when an uncaught CPU exception is generated. By + default, it displays a full-screen error message with the event code and + basic debugging information. Some custom event codes may be used for kernel + failure scenarios. This function never returns. */ +GNORETURN void gint_panic(uint32_t code); + +/* gint_panic_set(): Change the panic handler function + Sets up a different panic function instead of the default. It the argument + is NULL, restores the default. */ +void gint_panic_set(GNORETURN void (*panic)(uint32_t code)); + +/* gint_exc_catch(): Set a function to catch exceptions + + Sets up an exception-catching function. If an exception occurs, before a + panic is raised, the exception-catching function is executed in interrupt + mode and is given a chance to handle the exception. Passing NULL disables + this feature. + + The exception-catching function can do anything that does not use interrupts + or causes an exception, such as logging the exception or any other useful + mechanism. + + What happens next depends on the return value: + * If it returns 0, the exception is considered handled and execution + continues normally at or after the offending instruction. + * If it returns nonzero, a panic is raised. + + Please be aware that many exceptions are of re-execution type. When + execution restarts after such an exception is handled, the offending + instruction if re-executed. This can cause the exception handling mechanism + to loop. Use gint_exc_skip() to skip over the offending instruction when + needed. Whether an exception is of re-execution type depends on the + exception code. */ +void gint_exc_catch(int (*handler)(uint32_t code)); + +/* gint_exc_skip(): Skip pending exception instructions + + Many exceptions re-execute the offending instruction after the exception is + handled. For instance the TLB miss handler is supposed to load the required + page into memory, so that the instruction that accessed unmapped memory can + be successfully re-executed. + + When an exception-catching function records an exception without solving it, + this re-execution will fail again and the exception handling process will + loop. In such a situation, gint_exc_skip() can be used to manually skip the + offending instruction. + + @instructions Number of instructions to skip (usually only one) */ +void gint_exc_skip(int instructions); #endif /* GINT_EXC */ diff --git a/src/core/exch.c b/src/core/exch.c index 355f943..3fd2a68 100644 --- a/src/core/exch.c +++ b/src/core/exch.c @@ -1,13 +1,15 @@ #define GINT_NEED_VRAM -#include #include +#include +#include +#include #include #define dprint(x, y, ...) dprint(x, y, C_BLACK, C_NONE, __VA_ARGS__) #define dtext(x, y, str) dtext (x, y, str, C_BLACK, C_NONE) -/* gint_exc(): Exception handler fatal error message */ -void gint_exc(GUNUSED uint32_t code) +/* gint_panic_default(): Default panic handler */ +GNORETURN static void gint_default_panic(GUNUSED uint32_t code) { uint32_t TEA = *(volatile uint32_t *)0xff00000c; uint32_t TRA = *(volatile uint32_t *)0xff000020 >> 2; @@ -82,17 +84,56 @@ void gint_exc(GUNUSED uint32_t code) dtext(6, 95, "An unrecoverable error ocurred in the add-in."); dtext(6, 108, "Please press the RESET button to restart the"); dtext(6, 121, "calculator."); + + /* DMA address error handler */ + if(code == 0x1020) + { + #define DMA SH7305_DMA + dprint(6, 141, "SAR0: %08x DAR0: %08x TCR0: %08x", + DMA.DMA0.SAR, DMA.DMA0.DAR, DMA.DMA0.TCR); + dprint(6, 154, "CHCR0: %08x", DMA.DMA0.CHCR); + dprint(6, 167, "SAR1: %08x DAR1: %08x TCR1: %08x", + DMA.DMA1.SAR, DMA.DMA1.DAR, DMA.DMA1.TCR); + dprint(6, 180, "CHCR1: %08x", DMA.DMA1.CHCR); + dprint(6, 193, "DMAOR: %04x", DMA.OR); + #undef DMA + } #endif - dupdate_noint(); + dupdate(); + while(1) sleep(); } -/* gint_exch_tlbh(): Exception and TLB miss handler */ -__attribute__((interrupt_handler)) GSECTION(".gint.exch_tlbh") GALIGNED(4) -void gint_exch_tlbh(void) +/* Panic handler */ +GDATA GNORETURN void (*gint_exc_panic)(uint32_t code) = gint_default_panic; +/* Exception catcher */ +GDATA int (*gint_exc_catcher)(uint32_t code) = NULL; + +/* gint_panic(): Panic handler function */ +void gint_panic(uint32_t code) { - uint32_t EXPEVT = *(volatile uint32_t *)0xff000024; - - gint_exc(EXPEVT); - while(1); + gint_exc_panic(code); +} + +/* gint_panic_set(): Change the panic handler function */ +void gint_panic_set(GNORETURN void (*panic)(uint32_t code)) +{ + gint_exc_panic = panic; +} + +/* gint_exc_catch(): Set a function to catch exceptions */ +void gint_exc_catch(int (*handler)(uint32_t code)) +{ + gint_exc_catcher = handler; +} + +/* gint_exc_skip(): Skip pending exception instructions */ +void gint_exc_skip(int instructions) +{ + uint32_t spc; + + /* Increase SPC by 2 bytes per instruction */ + __asm__("stc spc, %0" : "=r"(spc)); + spc += 2 * instructions; + __asm__("ldc %0, spc" :: "r"(spc)); } diff --git a/src/core/exch.s b/src/core/exch.s new file mode 100644 index 0000000..a2f8105 --- /dev/null +++ b/src/core/exch.s @@ -0,0 +1,53 @@ +.global _gint_exch_tlbh +.section .gint.exch_tlbh +.align 4 + +_gint_exch_tlbh: + sts.l pr, @-r15 + mov.l r8, @-r15 + + mov.l .expevt, r8 + + /* Panic if the catcher is NULL */ + mov.l .catcher, r0 + mov.l @r0, r0 + tst r0, r0 + bt panic + + /* Leave if the catcher returns zero */ + jsr @r0 + mov.l @r8, r4 + tst r0, r0 + bt end + +panic: + /* RTE to the panic function, but manually, so that SPC is preserved */ + mov.l @r8, r4 + ldc r4, r4_bank + + mov.l @r15+, r8 + lds.l @r15+, pr + + /* Here we switch banks so r0..r7 change meaning! */ + stc ssr, r0 + ldc r0, sr + + mov.l .panic, r0 + mov.l @r0, r0 + jmp @r0 + nop + +end: + mov.l @r15+, r8 + lds.l @r15+, pr + rte + nop + +.align 4 + +.expevt: + .long 0xff000024 +.catcher: + .long _gint_exc_catcher +.panic: + .long _gint_exc_panic diff --git a/src/core/start.c b/src/core/start.c index 1120180..d509ba1 100644 --- a/src/core/start.c +++ b/src/core/start.c @@ -134,12 +134,7 @@ int start(int isappli, int optnum) bootlog_mapped(rom, ram); /* Cancel add-in execution if not all pages are mapped */ - if(rom < (uint32_t)&srom) - { - gint_exc(0x1040); - while(1); - return 1; - } + if(rom < (uint32_t)&srom) gint_panic(0x1040); /* Install gint and switch VBR */ gint_install(); diff --git a/src/dma/dma.c b/src/dma/dma.c index 17c918e..195dae0 100644 --- a/src/dma/dma.c +++ b/src/dma/dma.c @@ -152,34 +152,6 @@ void dma_transfer_noint(int channel, dma_size_t size, uint blocks, DMA.OR.NMIF = 0; } -//--- -// Address error handler -//--- - -/* gint_dma_ae(): DMA Address Error handler */ -void gint_dma_ae(void) -{ - gint_exc(0x1020); - - #ifdef FXCG50 - dprint(6, 141, C_BLACK, C_NONE, - "SAR0: %08x DAR0: %08x TCR0: %08x", - DMA.DMA0.SAR, DMA.DMA0.DAR, DMA.DMA0.TCR); - dprint(6, 154, C_BLACK, C_NONE, - "CHCR0: %08x", DMA.DMA0.CHCR); - dprint(6, 167, C_BLACK, C_NONE, - "SAR1: %08x DAR1: %08x TCR1: %08x", - DMA.DMA1.SAR, DMA.DMA1.DAR, DMA.DMA1.TCR); - dprint(6, 180, C_BLACK, C_NONE, - "CHCR1: %08x", DMA.DMA1.CHCR); - dprint(6, 193, C_BLACK, C_NONE, - "DMAOR: %04x", DMA.OR); - dupdate_noint(); - #endif - - while(1); -} - //--- // Initialization //--- diff --git a/src/dma/inth.s b/src/dma/inth.s index 846a28b..e540534 100644 --- a/src/dma/inth.s +++ b/src/dma/inth.s @@ -34,16 +34,19 @@ _inth_dma_te: /* DMA ADDRESS ERROR INTERRUPT HANDLER - 18 BYTES */ _inth_dma_ae: - /* Call a fixed function */ - sts.l pr, @-r15 - mov.l 1f, r1 - jsr @r1 - nop - lds.l @r15+, pr + /* Manually RTE into the panic routine, preserving SPC */ + mov.l 2f, r4 + ldc r4, r4_bank - rte + /* This instruction changes register bank! */ + stc ssr, r1 + ldc r1, sr + + mov.l 1f, r0 + jmp @r0 nop - .zero 14 + .zero 10 -1: .long _gint_dma_ae +1: .long _gint_panic +2: .long 0x1020