exc: add exception handlers, use them in the DMA

This change introduces exception handlers that default to a fatal error
with an on-screen description of the exception and some debugging
information.

It also adds the dprint() function as a definitely-needed helper and
removes bootlog_unmapped() by using the exception handler for the fatal
error display. (Also printf() is now required in all gint add-ins; this
is sad, but space is not as much of a constraint as debugging.)

Finally, the exception handler is used to handle an interrupt which is
an exception in practice, the DMA address error. On fx-CG 50, additional
DMA-related information is displayed on the screen. This is left out on
fx-9860G as there is not enough space.
This commit is contained in:
lephe 2019-09-03 22:15:00 +02:00
parent 6d54a5fe0a
commit e1aca8d89b
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
18 changed files with 226 additions and 76 deletions

7
TODO
View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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(...)

View file

@ -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 */

View file

@ -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
<gint/std/stdio.h> 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)
//---

View file

@ -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 */

21
include/gint/exc.h Normal file
View file

@ -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 */

View file

@ -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)
{

View file

@ -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

98
src/core/exch.c Normal file
View file

@ -0,0 +1,98 @@
#define GINT_NEED_VRAM
#include <gint/display.h>
#include <gint/exc.h>
#include <gint/defs/attributes.h>
#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);
}

View file

@ -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 */

View file

@ -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 */

View file

@ -9,6 +9,7 @@
#include <gint/drivers.h>
#include <gint/gint.h>
#include <gint/hardware.h>
#include <gint/exc.h>
/* 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;
}

View file

@ -6,6 +6,8 @@
#include <gint/dma.h>
#include <gint/drivers.h>
#include <gint/clock.h>
#include <gint/display.h>
#include <gint/exc.h>
#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
//---

View file

@ -46,4 +46,4 @@ _inth_dma_ae:
.zero 14
1: .long _dma_address_error
1: .long _gint_dma_ae

View file

@ -1,11 +1,12 @@
#include <gint/dma.h>
/* 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;
}

15
src/render/dprint.c Normal file
View file

@ -0,0 +1,15 @@
#include <gint/display.h>
#include <gint/std/stdio.h>
/* 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);
}