diff --git a/TODO b/TODO index e507750..4287009 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,7 @@ Crucial, missing things. ! core: the four basic memory functions -! core: build an exception handler and a TLB miss handler -! core: gint_switch() (driver contexts on stack; arbitrary code?) +! core: gint_switch() with driver contexts on stack and arbitrary code +! core: use gint_switch() to handle TLB misses ! bopti: fxcg50 version ! syscalls: fxcg50 BFile calls @@ -10,10 +10,11 @@ Tests to run. * topti: all charsets Complementary elements on existing code. +* 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 -* core: use topti in the error message for missing mappings * core: find the #ifdef FX9860G|FXCG50 that have a cross-platform def +* core: try to leave add-in without reset in case of fatal exception * topti: support Unicode fonts * hardware: fill in the HWMEM_FITTLB flag * keyboard: think of extended functions diff --git a/fx9860g.ld b/fx9860g.ld index 6b5a3ba..476f318 100644 --- a/fx9860g.ld +++ b/fx9860g.ld @@ -55,6 +55,10 @@ SECTIONS } > rom .text : { + _gint_exch_tlbh_start = . ; + *(.gint.exch_tlbh); + _gint_exch_tlbh_size = ABSOLUTE(. - _gint_exch_tlbh_start); + *(.text .text.*) *(C P) } > rom diff --git a/fxcg50.ld b/fxcg50.ld index 78d7410..cb5ab49 100644 --- a/fxcg50.ld +++ b/fxcg50.ld @@ -49,6 +49,10 @@ SECTIONS *(.dtors .dtors.*) _etors = . ; + _gint_exch_tlbh_start = . ; + *(.gint.exch_tlbh); + _gint_exch_tlbh_size = ABSOLUTE(. - _gint_exch_tlbh_start); + *(.text .text.*) } > rom diff --git a/include/core/bootlog.h b/include/core/bootlog.h index 1318ac3..d84124c 100644 --- a/include/core/bootlog.h +++ b/include/core/bootlog.h @@ -11,12 +11,6 @@ Called when RAM sections have been wiped and copied */ void bootlog_loaded(void); -/* bootlog_unmapped() - ROM mapping stage failed - Called if not enough ROM pages have been mapped. - @rom Amount of mapped ROM, in bytes - @size Minimum mapping required */ -void bootlog_unmapped(int rom, int size); - /* bootlog_mapped() - ROM mapping stage Called after all ROM pages have been traversed. All of them may not have been mapped. @@ -37,7 +31,6 @@ void bootlog_driver_summary(void); /* All these functions are enabled only if GINT_BOOT_LOG is defined */ #ifndef GINT_BOOT_LOG #define bootlog_loaded(...) - #define bootlog_unmapped(...) #define bootlog_mapped(...) #define bootlog_kernel(...) #define bootlog_driver(...) diff --git a/include/core/setup.h b/include/core/setup.h index c692b02..4118968 100644 --- a/include/core/setup.h +++ b/include/core/setup.h @@ -20,4 +20,12 @@ Returns the previous VBR address. */ uint32_t gint_setvbr(uint32_t vbr, void (*configure)(void)); +/* gint_exch_tlbh(): Exception and TLB miss handler */ +void gint_exch_tlbh(void); + +/* gint_inth_7705(): SH7705 exception handler */ +void gint_inth_7705(void); +/* gint_inth_7305(): SH7305 exception handler */ +void gint_inth_7305(void); + #endif /* GINT_CORE_SETUP */ diff --git a/include/gint/display.h b/include/gint/display.h index 177c879..d5dd9fd 100644 --- a/include/gint/display.h +++ b/include/gint/display.h @@ -199,6 +199,11 @@ void dsize(const char *str, font_t const * font, int *w, int *h); fxcg50: Any R5G6B5 color, or C_NONE */ void dtext(int x, int y, const char *str, int fg, int bg); +/* dprint(): Display a formatted string + Much like dtext(), but accepts printf-like formats with arguments. See + for a detailed view of what this format supports. */ +void dprint(int x, int y, int fg, int bg, char const *format, ...); + //--- // Image rendering (bopti) //--- diff --git a/include/gint/dma.h b/include/gint/dma.h index 2425050..6e4f978 100644 --- a/include/gint/dma.h +++ b/include/gint/dma.h @@ -79,4 +79,19 @@ void dma_transfer_noint(int channel, dma_size_t size, uint blocks, void const *src, dma_address_t src_mode, void *dst, dma_address_t dst_mode); +//--- +// DMA-based memory manipulation functions +//--- + +/* dma_memset(): Fast 32-aligned memset + This function is your typical memset, except that the destination and size + must be 32-aligned, and that the pattern is 4 bytes instead of one. It is + replicated to 32 bytes then used to fill the destination area. This 4-byte + fixed size may be lifted in future versions. + + @dst Destination address (32-aligned) + @pattern 4-byte pattern to fill @dst + @size Sie of destination area (32-aligned) */ +void *dma_memset(void *dst, uint32_t pattern, size_t size); + #endif /* GINT_DMA */ diff --git a/include/gint/exc.h b/include/gint/exc.h new file mode 100644 index 0000000..372755a --- /dev/null +++ b/include/gint/exc.h @@ -0,0 +1,21 @@ +//--- +// gint:exc - Exception handling +// +// This small module is used to display exceptions and configure when the +// exception handler displays these messages. This is for advanced users +// only! +//--- + +#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); + +/* TODO: Add functions to disable fatal errors for some exceptions */ + +#endif /* GINT_EXC */ diff --git a/src/core/bootlog.c b/src/core/bootlog.c index 301dc98..678645b 100644 --- a/src/core/bootlog.c +++ b/src/core/bootlog.c @@ -117,14 +117,6 @@ void bootlog_mapped(int rom, GUNUSED int ram) dupdate(); } -/* bootlog_unmapped() - ROM mapping stage failure */ -void bootlog_unmapped(int rom, int size) -{ - print(1, 6, "MMU: add-in too large"); - print(1, 7, "%8x < %8x", rom, size); - dupdate(); -} - /* bootlog_kernel() - gint loading stage */ void bootlog_kernel(void) { diff --git a/src/core/exch.S b/src/core/exch.S deleted file mode 100644 index bf3a118..0000000 --- a/src/core/exch.S +++ /dev/null @@ -1,26 +0,0 @@ -/* -** gint:core:exch - Exception handlers -*/ - -.global _exch_entry_7305 - -#ifdef FX9860G -.global _exch_entry_7705 -#endif - -.section .gint.blocks, "ax" -.align 4 - -/* SH7305-TYPE DEBUG EXCEPTION HANDLER */ - -_exch_entry_7705: -_exch_entry_7305: - mov.l 1f, r0 - mov.l @r0, r4 - - rte - nop - - .zero 20 - -1: .long 0xff000024 diff --git a/src/core/exch.c b/src/core/exch.c new file mode 100644 index 0000000..355f943 --- /dev/null +++ b/src/core/exch.c @@ -0,0 +1,98 @@ +#define GINT_NEED_VRAM +#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) +{ + uint32_t TEA = *(volatile uint32_t *)0xff00000c; + uint32_t TRA = *(volatile uint32_t *)0xff000020 >> 2; + + uint32_t PC; + __asm__("stc spc, %0" : "=r"(PC)); + + dfont(NULL); + dclear(C_WHITE); + + #ifdef FX9860G + dtext(1, 0, "Exception! (SysERROR)"); + for(int i = 0; i < 32; i++) vram[i] = ~vram[i]; + + char const *name = ""; + if(code == 0x040) name = "TLB miss read"; + if(code == 0x060) name = "TLB miss write"; + if(code == 0x0e0) name = "Read address error"; + if(code == 0x100) name = "Write address error"; + if(code == 0x160) name = "Unconditional trap"; + if(code == 0x180) name = "Illegal instruction"; + if(code == 0x1a0) name = "Illegal delay slot"; + + /* Custom gint codes for convenience */ + if(code == 0x1020) name = "DMA address error"; + if(code == 0x1040) name = "Add-in too large"; + + if(name[0]) dtext(1, 9, name); + else dprint(1, 9, "%03x", code); + + dprint(1, 17, " PC :%08x", PC); + dprint(1, 25, "TEA :%08x", TEA); + dprint(1, 33, "TRA :%08x", TRA); + + dtext(1, 49, "The add-in crashed."); + dtext(1, 57, "Please reset the calc"); + #endif + + #ifdef FXCG50 + dtext(6, 3, "An exception occured! (System ERROR)"); + + uint32_t *long_vram = (void *)vram; + for(int i = 0; i < 198 * 16; i++) long_vram[i] = ~long_vram[i]; + + char const *name = ""; + if(code == 0x040) name = "TLB miss (nonexisting address) on read"; + if(code == 0x060) name = "TLB miss (nonexisting address) on write"; + if(code == 0x0e0) name = "Read address error (probably alignment)"; + if(code == 0x100) name = "Write address error (probably alignment)"; + if(code == 0x160) name = "Unconditional trap"; + if(code == 0x180) name = "Illegal instruction"; + if(code == 0x1a0) name = "Illegal delay slot instruction"; + + /* Custom gint codes for convenience */ + if(code == 0x1020) name = "DMA address error"; + if(code == 0x1040) name = "Add-in not fully mapped (too large)"; + + dprint(6, 25, "%03x %s", code, name); + + dtext(6, 45, "PC"); + dprint(38, 45, "= %08x", PC); + dtext(261, 45, "(Error location)"); + + dtext(6, 60, "TEA"); + dprint(38, 60, "= %08x", TEA); + dtext(234, 60, "(Offending address)"); + + dtext(6, 75, "TRA"); + dprint(38, 75, "= %#x", TRA); + dtext(281, 75, "(Trap number)"); + + 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."); + #endif + + dupdate_noint(); +} + +/* gint_exch_tlbh(): Exception and TLB miss handler */ +__attribute__((interrupt_handler)) GSECTION(".gint.exch_tlbh") GALIGNED(4) +void gint_exch_tlbh(void) +{ + uint32_t EXPEVT = *(volatile uint32_t *)0xff000024; + + gint_exc(EXPEVT); + while(1); +} diff --git a/src/core/inth.S b/src/core/inth.S index acffb66..01672eb 100644 --- a/src/core/inth.S +++ b/src/core/inth.S @@ -5,10 +5,10 @@ ** blocks depending on its configuration. */ -.global _inth_entry_7305 +.global _gint_inth_7305 #ifdef FX9860G -.global _inth_entry_7705 +.global _gint_inth_7705 #endif .section .gint.blocks, "ax" @@ -36,7 +36,7 @@ /* SH7305-TYPE DEBUG INTERRUPT HANDLER - 26 BYTES */ #if 0 -_inth_entry_7305: +_gint_inth_7305: mov.l 1f, r0 mov.l @r0, r4 @@ -56,7 +56,7 @@ _inth_entry_7305: /* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */ -_inth_entry_7305: +_gint_inth_7305: /* Get the event code from the INTEVT register */ mov.l 1f, r0 mov.l @r0, r0 @@ -78,7 +78,7 @@ _inth_entry_7305: /* SH7705-TYPE INTERRUT HANDLER ENTRY - 32 BYTES */ -_inth_entry_7705: +_gint_inth_7705: /* Get the event code from the INTEVT2 register */ mov.l 1f, r0 mov.l @r0, r0 /* r0 = old_code */ diff --git a/src/core/setup.c b/src/core/setup.c index b1fe5a5..a2b6efb 100644 --- a/src/core/setup.c +++ b/src/core/setup.c @@ -13,6 +13,8 @@ extern char gint_vbr; /* System's VBR address */ GBSS static uint32_t system_vbr; +/* Size of exception and TLB handler */ +extern char gint_exch_tlbh_size; /* Driver table */ extern gint_driver_t bdrv, edrv; @@ -67,11 +69,13 @@ static void lock(void) void gint_install(void) { /* VBR address, provided by the linker script */ - uint32_t vbr = (uint32_t)&gint_vbr; + void *vbr = (void *)&gint_vbr; /* Event handler entry points */ - void *exch_entry; - void *inth_entry; + void *inth_entry = isSH3() ? gint_inth_7705 : gint_inth_7305; + + /* Size of the exception and TLB handlers */ + uint32_t exch_tlbh_size = (uint32_t)&gint_exch_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 @@ -79,28 +83,12 @@ void gint_install(void) gint_ctx_save(&sys_ctx); /* Load the event handler entry points into memory */ - /* TODO: Load an exception handler and a TLB miss handler */ - - if(isSH3()) - { - extern void exch_entry_7705(void); - extern void inth_entry_7705(void); - exch_entry = exch_entry_7705; - inth_entry = inth_entry_7705; - } - else - { - extern void exch_entry_7305(void); - extern void inth_entry_7305(void); - exch_entry = exch_entry_7305; - inth_entry = inth_entry_7305; - } - - memcpy((void *)(vbr + 0x100), exch_entry, 32); - memcpy((void *)(vbr + 0x600), inth_entry, 32); + memcpy(vbr + 0x100, gint_exch_tlbh, exch_tlbh_size); + memcpy(vbr + 0x400, gint_exch_tlbh, exch_tlbh_size); + memcpy(vbr + 0x600, inth_entry, 32); /* Time to switch VBR and roll! */ - system_vbr = gint_setvbr(vbr, lock); + system_vbr = gint_setvbr((uint32_t)vbr, lock); } /* unlock() - unlock interrupts, restoring system settings */ diff --git a/src/core/start.c b/src/core/start.c index d7117da..1120180 100644 --- a/src/core/start.c +++ b/src/core/start.c @@ -9,6 +9,7 @@ #include #include #include +#include /* Symbols provided by the linker script. For sections: - l* represents the load address (source address in ROM) @@ -135,7 +136,7 @@ int start(int isappli, int optnum) /* Cancel add-in execution if not all pages are mapped */ if(rom < (uint32_t)&srom) { - bootlog_unmapped(rom, (uint32_t)&srom); + gint_exc(0x1040); while(1); return 1; } diff --git a/src/dma/dma.c b/src/dma/dma.c index 8b86cf4..17c918e 100644 --- a/src/dma/dma.c +++ b/src/dma/dma.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #define DMA SH7305_DMA #define INTC SH7305_INTC @@ -150,6 +152,34 @@ 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 19f5aeb..846a28b 100644 --- a/src/dma/inth.s +++ b/src/dma/inth.s @@ -46,4 +46,4 @@ _inth_dma_ae: .zero 14 -1: .long _dma_address_error +1: .long _gint_dma_ae diff --git a/src/dma/memset.c b/src/dma/memset.c index 40ce5c9..2d5aff2 100644 --- a/src/dma/memset.c +++ b/src/dma/memset.c @@ -1,11 +1,12 @@ #include -/* dma_memset(): Fast memset for highly-aligned addresses */ -void dma_memset(void *dst, uint32_t l, size_t size) +/* dma_memset(): Fast 32-aligned memset */ +void *dma_memset(void *dst, uint32_t l, size_t size) { /* TODO: Use a proper IL memory allocation scheme */ uint32_t *IL = (void *)0xe5200000; for(int i = 0; i < 8; i++) IL[i] = l; dma_transfer_noint(1, DMA_32B, size >> 5, IL, DMA_FIXED, dst, DMA_INC); + return dst; } diff --git a/src/render/dprint.c b/src/render/dprint.c new file mode 100644 index 0000000..1afb6c5 --- /dev/null +++ b/src/render/dprint.c @@ -0,0 +1,15 @@ +#include +#include + +/* dprint(): Display a formatted string */ +void dprint(int x, int y, int fg, int bg, char const *format, ...) +{ + char str[256]; + va_list args; + + va_start(args, format); + vsnprintf(str, 256, format, args); + va_end(args); + + dtext(x, y, str, fg, bg); +}