gint/src/core/gint.c

181 lines
4.5 KiB
C

#include <internals/gint.h>
#include <internals/interrupt_maps.h>
#include <internals/exceptions.h>
#include <internals/interrupts.h>
#include <gint.h>
#include <mpu.h>
// Referencing the interrupt handlers to force ld to link them in (there should
// be a better way of achieving this, but I can't seem to find it).
__attribute__((used)) static void (*_exc)(void) = &gint_exc;
__attribute__((used)) static void (*_tlb)(void) = &gint_tlb;
__attribute__((used)) static void (*_int)(void) = &gint_int;
//---
// Exception and interrupt handlers
//---
gint_interrupt_handler_t gint_handlers[] = {
//---
// Resets and non-events
//---
{ NULL, NULL, 0, { 0x00, 0x00, 0x00 } },
//---
// General exceptions
//---
// Address error.
{ NULL, exch_address_error, 0,
{ arg_pc, arg_tea, arg_subtype } },
// TLB protection violation.
{ NULL, exch_tlb_protection_violation, 0,
{ arg_pc, arg_tea, arg_subtype } },
// TLB Invalid (SH7705 only).
{ NULL, exch_tlb_invalid, 0,
{ arg_pc, arg_tea, arg_subtype } },
// Illegal instruction.
{ NULL, exch_illegal_instruction, 0,
{ arg_pc, arg_opcode, 0x00 } },
// Illegal opcode.
{ NULL, exch_illegal_slot, 0,
{ arg_pc, arg_opcode, 0x00 } },
// User break.
{ NULL, exch_user_break, 0,
{ 0x00, 0x00, 0x00 } },
// Initial page write.
{ NULL, exch_initial_page_write, 0,
{ 0x00, 0x00, 0x00 } },
// Unconditional trap.
{ NULL, exch_trap, 0,
{ arg_pc, arg_trap, 0x00 } },
// DMA address error.
{ NULL, exch_dma_address, 0,
{ 0x00, 0x00, 0x00 } },
//---
// TLB misses
//---
{ NULL, exch_tlb_miss,
0, { arg_pc, arg_tea, arg_subtype } },
//---
// Interrupt requests
//---
// Non-Maskable Interrupt.
{ NULL, NULL, 0,
{ 0x00, 0x00, 0x00 } },
// Timer underflow.
{ NULL, inth_timer_underflow, 12,
{ arg_subtype, 0x00, 0x00 } },
// Timer channel 2 input capture (SH7705 only)
{ NULL, NULL, 0,
{ arg_timer_capt, 0x00, 0x00 } },
// Real-time clock alarm interrupt.
{ NULL, NULL, 0,
{ 0x00, 0x00, 0x00 } },
// Real-time clock periodic interrupt.
{ NULL, inth_rtc_periodic, 10,
{ 0x00, 0x00, 0x00 } },
// Real-time clock carry interrupt.
{ NULL, NULL, 0,
{ 0x00, 0x00, 0x00 } },
};
/*
gint_invoke()
Invokes an interrupt or exception handler, given its type and subtype.
*/
void gint_invoke(uint8_t type, uint8_t subtype)
{
if(type >= exc_type_max || !gint_handlers[type].function) return;
volatile uint32_t *tea, *tra, *tcpr_2;
uint32_t args[3];
uint16_t *pc;
// Getting some initial information to work with.
__asm__("stc spc, %0" : "=r"(pc));
tea = gint_reg(register_tea);
tra = gint_reg(register_tra);
tcpr_2 = (void *)0xfffffeb8; /* SH7705 only */
// Building up the argument list.
for(int i = 0; i < 3; i++) switch(gint_handlers[type].args[i])
{
case arg_none: args[i] = 0; break;
case arg_subtype: args[i] = subtype; break;
case arg_pc: args[i] = (uint32_t)pc; break;
case arg_opcode: args[i] = *pc; break;
case arg_tea: args[i] = *tea; break;
case arg_trap: args[i] = *tra >> 2; break;
case arg_timer_capt: args[i] = *tcpr_2; break;
}
// Calling the function with the required amount of arguments.
if(gint_handlers[type].args[2])
{
void (*handler)(uint32_t, uint32_t, uint32_t);
handler = gint_handlers[type].function;
(*handler)(args[0], args[1], args[2]);
}
else if(gint_handlers[type].args[1])
{
void (*handler)(uint32_t, uint32_t);
handler = gint_handlers[type].function;
(*handler)(args[0], args[1]);
}
else if(gint_handlers[type].args[0])
{
void (*handler)(uint32_t);
handler = gint_handlers[type].function;
(*handler)(args[0]);
}
else (*(void (*)(void))gint_handlers[type].function)();
}
/*
gint_install()
Installs an exception or interrupt handler for one of gint's recognized
interrupts.
*/
void gint_install(gint_interrupt_type_t type, void *function)
{
if((unsigned)type >= exc_type_max) return;
gint_handlers[type].function = function;
}
/*
gint_uninstall()
Un-installs the exception or interrupt handler that was used for the
given interrupt, falling back to gint's default handler.
*/
void gint_uninstall(gint_interrupt_type_t type)
{
if((unsigned)type >= exc_type_max) return;
gint_handlers[type].function = gint_handlers[type].default_function;
}
//---
// Register access
//---
/*
gint_reg()
Returns the address of a common register. All common registers exist
on both platforms but they may hold different values for the same
information.
*/
volatile void *gint_reg(gint_register_t reg)
{
return isSH3() ? gint_reg_7705(reg) : gint_reg_7305(reg);
}