mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-29 13:03:36 +01:00
interrupts: save caller-saved registers in main handler
This is an obvious requirement for the interrupt routine, which was forgotten and only surfaced when I used a timer callback started with multiplications in an innocent add-in. r0..r7 are saved automatically, which leaves pr, gbr, mach et macl susceptible to corruption by the interrupt handler.
This commit is contained in:
parent
61da7debc8
commit
2cdf925f94
8 changed files with 61 additions and 21 deletions
|
@ -97,21 +97,24 @@ int gint_intlevel(int intid, int level);
|
||||||
list of event codes and their associated interrupts.
|
list of event codes and their associated interrupts.
|
||||||
|
|
||||||
The handler function must be an interrupt handler: it must not raise
|
The handler function must be an interrupt handler: it must not raise
|
||||||
exceptions, must end with 'rte', and it will use the kernel register bank.
|
exceptions, must end with 'rts', and it will use the kernel register bank.
|
||||||
|
You read that right, it must end with rts because gint's main handler saves
|
||||||
|
registers besides the automated r0..r7. Do *not* use 'rte' in the handler.
|
||||||
For convenience I allow any block size to be loaded as an interrupt handler,
|
For convenience I allow any block size to be loaded as an interrupt handler,
|
||||||
but it should really be a multiple of 0x20 bytes and not override other
|
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
|
handlers. If it's not written in assembler, then you're likely doing
|
||||||
something wrong, even with __attribute__((interrupt_handler)).
|
something wrong, especially with __attribute__((interrupt_handler)) which
|
||||||
|
uses rte.
|
||||||
|
|
||||||
It is common for interrupt handlers to have a few bytes of data, such as the
|
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
|
address of a callback function. gint often stores this data in the last
|
||||||
bytes of the block. This function returns the VBR address of the block to
|
bytes of the block. This function returns the VBR address of the block which
|
||||||
allow the caller to edit the parameters.
|
has just been installed, to allow the caller to edit the parameters later.
|
||||||
|
|
||||||
@event_code Identifier of the interrupt block
|
@event_code Identifier of the interrupt block
|
||||||
@handler Address of handler function
|
@handler Address of handler function
|
||||||
@size How many bytes to copy
|
@size How many bytes to copy
|
||||||
Returns the VBR address where the handlers was installed. */
|
Returns the VBR address where the handlers was installed. */
|
||||||
void *gint_inthandler(int event_code, const void *handler, size_t size);
|
void *gint_inthandler(int event_code, void const *handler, size_t size);
|
||||||
|
|
||||||
#endif /* GINT_GINT */
|
#endif /* GINT_GINT */
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <gint/clock.h>
|
#include <gint/clock.h>
|
||||||
#include <gint/mpu/dma.h>
|
#include <gint/mpu/dma.h>
|
||||||
#include <gint/defs/attributes.h>
|
#include <gint/defs/attributes.h>
|
||||||
|
#include <gint/hardware.h>
|
||||||
|
|
||||||
#define dprint(x, y, ...) dprint(x, y, C_BLACK, C_NONE, __VA_ARGS__)
|
#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)
|
#define dtext(x, y, str) dtext (x, y, str, C_BLACK, C_NONE)
|
||||||
|
@ -16,6 +17,9 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
||||||
uint32_t PC;
|
uint32_t PC;
|
||||||
__asm__("stc spc, %0" : "=r"(PC));
|
__asm__("stc spc, %0" : "=r"(PC));
|
||||||
|
|
||||||
|
uint32_t SGR = 0xffffffff;
|
||||||
|
if(isSH4()) __asm__("stc sgr, %0" : "=r"(SGR));
|
||||||
|
|
||||||
dfont(NULL);
|
dfont(NULL);
|
||||||
dclear(C_WHITE);
|
dclear(C_WHITE);
|
||||||
|
|
||||||
|
@ -42,6 +46,7 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
|
||||||
dprint(1, 17, " PC :%08x", PC);
|
dprint(1, 17, " PC :%08x", PC);
|
||||||
dprint(1, 25, "TEA :%08x", TEA);
|
dprint(1, 25, "TEA :%08x", TEA);
|
||||||
dprint(1, 33, "TRA :%08x", TRA);
|
dprint(1, 33, "TRA :%08x", TRA);
|
||||||
|
if(isSH4()) dprint(1, 41, "SGR :%08x", SGR);
|
||||||
|
|
||||||
dtext(1, 49, "The add-in crashed.");
|
dtext(1, 49, "The add-in crashed.");
|
||||||
dtext(1, 57, "Please reset the calc");
|
dtext(1, 57, "Please reset the calc");
|
||||||
|
|
|
@ -58,6 +58,6 @@ void *gint_inthandler(int event_code, const void *handler, size_t size)
|
||||||
/* Prevent overriding the entry gate */
|
/* Prevent overriding the entry gate */
|
||||||
if(event_code < 0) return NULL;
|
if(event_code < 0) return NULL;
|
||||||
|
|
||||||
void *dest = (void *)&gint_vbr + event_code + 0x620;
|
void *dest = (void *)&gint_vbr + event_code + 0x640;
|
||||||
return memcpy(dest, handler, size);
|
return memcpy(dest, handler, size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,9 +54,16 @@ _gint_inth_7305:
|
||||||
1: .long 0xff000028
|
1: .long 0xff000028
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */
|
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 40 BYTES */
|
||||||
|
|
||||||
_gint_inth_7305:
|
_gint_inth_7305:
|
||||||
|
/* Save caller-saved registers which might currently be in use by the
|
||||||
|
interrupted function, as we don't know what the callback will do */
|
||||||
|
sts.l pr, @-r15
|
||||||
|
stc.l gbr, @-r15
|
||||||
|
sts.l mach, @-r15
|
||||||
|
sts.l macl, @-r15
|
||||||
|
|
||||||
/* Get the event code from the INTEVT register */
|
/* Get the event code from the INTEVT register */
|
||||||
mov.l 1f, r0
|
mov.l 1f, r0
|
||||||
mov.l @r0, r0
|
mov.l @r0, r0
|
||||||
|
@ -66,19 +73,34 @@ _gint_inth_7305:
|
||||||
shll8 r1
|
shll8 r1
|
||||||
sub r1, r0
|
sub r1, r0
|
||||||
|
|
||||||
/* Add the distance to the first entry and jump */
|
/* Add the distance to the first entry and jump as a subroutine */
|
||||||
add #16, r0
|
add #40, r0
|
||||||
braf r0
|
bsrf r0
|
||||||
nop
|
nop
|
||||||
|
|
||||||
.zero 12
|
/* Restore caller-saved registers */
|
||||||
|
lds.l @r15+, macl
|
||||||
|
lds.l @r15+, mach
|
||||||
|
ldc.l @r15+, gbr
|
||||||
|
lds.l @r15+, pr
|
||||||
|
|
||||||
|
rte
|
||||||
|
nop
|
||||||
|
|
||||||
|
.zero 24
|
||||||
1: .long 0xff000028
|
1: .long 0xff000028
|
||||||
|
|
||||||
#ifdef FX9860G
|
#ifdef FX9860G
|
||||||
|
|
||||||
/* SH7705-TYPE INTERRUT HANDLER ENTRY - 32 BYTES */
|
/* SH7705-TYPE INTERRUT HANDLER ENTRY - 52 BYTES */
|
||||||
|
|
||||||
_gint_inth_7705:
|
_gint_inth_7705:
|
||||||
|
/* Save caller-saved registers as before */
|
||||||
|
sts.l pr, @-r15
|
||||||
|
stc.l gbr, @-r15
|
||||||
|
sts.l mach, @-r15
|
||||||
|
sts.l macl, @-r15
|
||||||
|
|
||||||
/* Get the event code from the INTEVT2 register */
|
/* Get the event code from the INTEVT2 register */
|
||||||
mov.l 1f, r0
|
mov.l 1f, r0
|
||||||
mov.l @r0, r0 /* r0 = old_code */
|
mov.l @r0, r0 /* r0 = old_code */
|
||||||
|
@ -93,10 +115,20 @@ _gint_inth_7705:
|
||||||
shld r3, r0 /* r0 = new_code - 0x400 */
|
shld r3, r0 /* r0 = new_code - 0x400 */
|
||||||
|
|
||||||
/* Add the distance between nop and the first entry, and jump */
|
/* Add the distance between nop and the first entry, and jump */
|
||||||
add #8, r0
|
add #32, r0
|
||||||
braf r0
|
bsrf r0
|
||||||
nop
|
nop
|
||||||
|
|
||||||
|
/* Restore saved registers */
|
||||||
|
lds.l @r15+, mach
|
||||||
|
lds.l @r15+, macl
|
||||||
|
ldc.l @r15+, gbr
|
||||||
|
lds.l @r15+, pr
|
||||||
|
|
||||||
|
rte
|
||||||
|
nop
|
||||||
|
|
||||||
|
.zero 12
|
||||||
1: .long 0xa4000000 /* INTEVT2 register */
|
1: .long 0xa4000000 /* INTEVT2 register */
|
||||||
2: .long _inth_remap
|
2: .long _inth_remap
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ void gint_install(void)
|
||||||
/* Load the event handler entry points into memory */
|
/* Load the event handler entry points into memory */
|
||||||
memcpy(vbr + 0x100, gint_exch_tlbh, exch_tlbh_size);
|
memcpy(vbr + 0x100, gint_exch_tlbh, exch_tlbh_size);
|
||||||
memcpy(vbr + 0x400, gint_exch_tlbh, exch_tlbh_size);
|
memcpy(vbr + 0x400, gint_exch_tlbh, exch_tlbh_size);
|
||||||
memcpy(vbr + 0x600, inth_entry, 32);
|
memcpy(vbr + 0x600, inth_entry, 64);
|
||||||
|
|
||||||
/* Time to switch VBR and roll! */
|
/* Time to switch VBR and roll! */
|
||||||
system_vbr = gint_setvbr((uint32_t)vbr, lock);
|
system_vbr = gint_setvbr((uint32_t)vbr, lock);
|
||||||
|
|
|
@ -25,7 +25,7 @@ _inth_dma_te:
|
||||||
and r2, r0
|
and r2, r0
|
||||||
mov.w r0, @r1
|
mov.w r0, @r1
|
||||||
|
|
||||||
rte
|
rts
|
||||||
nop
|
nop
|
||||||
|
|
||||||
1: .long 0 /* CHCR, set dynamically */
|
1: .long 0 /* CHCR, set dynamically */
|
||||||
|
|
|
@ -54,7 +54,7 @@ _inth_rtc_pri_helper:
|
||||||
|
|
||||||
.end:
|
.end:
|
||||||
lds.l @r15+, pr
|
lds.l @r15+, pr
|
||||||
rte
|
rts
|
||||||
nop
|
nop
|
||||||
|
|
||||||
.zero 8
|
.zero 8
|
||||||
|
|
|
@ -81,7 +81,7 @@ _inth_tmu_1:
|
||||||
|
|
||||||
.end:
|
.end:
|
||||||
lds.l @r15+, pr
|
lds.l @r15+, pr
|
||||||
rte
|
rts
|
||||||
nop
|
nop
|
||||||
|
|
||||||
.zero 12
|
.zero 12
|
||||||
|
@ -176,7 +176,7 @@ _inth_etmu_help:
|
||||||
nop
|
nop
|
||||||
|
|
||||||
lds.l @r15+, pr
|
lds.l @r15+, pr
|
||||||
rte
|
rts
|
||||||
nop
|
nop
|
||||||
|
|
||||||
.zero 18
|
.zero 18
|
||||||
|
@ -199,10 +199,10 @@ _inth_etmux:
|
||||||
|
|
||||||
/* Offset from VBR where extra timer 2 is located:
|
/* Offset from VBR where extra timer 2 is located:
|
||||||
- 0x600 to reach the interrupt handlers
|
- 0x600 to reach the interrupt handlers
|
||||||
- 0x020 to jump over the entry gate
|
- 0x040 to jump over the entry gate
|
||||||
- 0x840 to reach the handler of ETMU2
|
- 0x840 to reach the handler of ETMU2
|
||||||
- 0x004 to skip its first instructions (the size is hardcoded) */
|
- 0x004 to skip its first instructions (the size is hardcoded) */
|
||||||
1: .long 0xe64
|
1: .long 0xe84
|
||||||
|
|
||||||
.id_etmux:
|
.id_etmux:
|
||||||
.long 0 /* Timer ID */
|
.long 0 /* Timer ID */
|
||||||
|
|
Loading…
Reference in a new issue