tp/libs/dolphin/os/OSInterrupt.c

449 lines
13 KiB
C

#include "dolphin/os/OSInterrupt.h"
#include "dolphin/dsp.h"
#include "dolphin/exi/EXIBios.h"
#include "dolphin/os.h"
vu32 __PIRegs[12] : 0xCC003000;
vu16 __MEMRegs[64] : 0xCC004000;
/* 8033D6F4-8033D700 338034 000C+00 2/2 200/200 5/5 .text OSDisableInterrupts */
asm BOOL OSDisableInterrupts(void) {
// clang-format off
nofralloc
entry __RAS_OSDisableInterrupts_begin
mfmsr r3
// Clear external interrupts bit
rlwinm r4, r3, 0, 17, 15
mtmsr r4
entry __RAS_OSDisableInterrupts_end
// Return old interrupt status
rlwinm r3, r3, 17, 31, 31
blr
// clang-format on
}
/* 8033D708-8033D71C 338048 0014+00 0/0 10/10 1/1 .text OSEnableInterrupts */
asm BOOL OSEnableInterrupts(void) {
// clang-format off
nofralloc
mfmsr r3
// Set external interrupts bit
ori r4, r3, (1 << 15)
mtmsr r4
// Return old interrupt status
rlwinm r3, r3, 17, 31, 31
blr
// clang-format on
}
/* 8033D71C-8033D740 33805C 0024+00 2/2 197/197 6/6 .text OSRestoreInterrupts */
asm BOOL OSRestoreInterrupts(register BOOL status) {
// clang-format off
nofralloc
cmpwi status, 0
mfmsr r4
beq disable
// Set external interrupts bit
ori r5, r4, (1 << 15)
b set_msr
disable:
// Clear external interrupts bit
rlwinm r5, r4, 0, 17, 15
set_msr:
mtmsr r5
// Return old interrupt status
rlwinm r3, r4, 17, 31, 31
blr
// clang-format on
}
/* ############################################################################################## */
/* 80451670-80451674 000B70 0004+00 4/4 0/0 0/0 .sbss InterruptHandlerTable */
static __OSInterruptHandler* InterruptHandlerTable;
/* 8033D740-8033D75C 338080 001C+00 0/0 12/12 0/0 .text __OSSetInterruptHandler */
__OSInterruptHandler __OSSetInterruptHandler(__OSInterrupt type, __OSInterruptHandler handler) {
__OSInterruptHandler old = InterruptHandlerTable[type];
InterruptHandlerTable[type] = handler;
return old;
}
/* 8033D75C-8033D770 33809C 0014+00 0/0 1/1 0/0 .text __OSGetInterruptHandler */
__OSInterruptHandler __OSGetInterruptHandler(__OSInterrupt type) {
return InterruptHandlerTable[type];
}
/* 8033D770-8033D7E4 3380B0 0074+00 0/0 1/1 0/0 .text __OSInterruptInit */
void __OSInterruptInit(void) {
InterruptHandlerTable = OSPhysicalToCached(0x3040);
memset(InterruptHandlerTable, 0, 32 * sizeof(__OSInterruptHandler));
*(OSInterruptMask*)OSPhysicalToCached(0x00C4) = 0;
*(OSInterruptMask*)OSPhysicalToCached(0x00C8) = 0;
__PIRegs[1] = 0xf0;
__OSMaskInterrupts(OS_INTERRUPTMASK_MEM | OS_INTERRUPTMASK_DSP | OS_INTERRUPTMASK_AI |
OS_INTERRUPTMASK_EXI | OS_INTERRUPTMASK_PI);
__OSSetExceptionHandler(4, (OSExceptionHandler)ExternalInterruptHandler);
}
/* 8033D7E4-8033DABC 338124 02D8+00 2/2 0/0 0/0 .text SetInterruptMask */
u32 SetInterruptMask(OSInterruptMask mask, OSInterruptMask current) {
u32 reg;
switch (__cntlzw(mask)) {
case OS_INTR_MEM_0:
case OS_INTR_MEM_1:
case OS_INTR_MEM_2:
case OS_INTR_MEM_3:
case OS_INTR_MEM_ADDRESS:
reg = 0;
if (!(current & OS_INTERRUPTMASK_MEM_0))
reg |= 0x1;
if (!(current & OS_INTERRUPTMASK_MEM_1))
reg |= 0x2;
if (!(current & OS_INTERRUPTMASK_MEM_2))
reg |= 0x4;
if (!(current & OS_INTERRUPTMASK_MEM_3))
reg |= 0x8;
if (!(current & OS_INTERRUPTMASK_MEM_ADDRESS))
reg |= 0x10;
__MEMRegs[0x0000000e] = (u16)reg;
mask &= ~OS_INTERRUPTMASK_MEM;
break;
case OS_INTR_DSP_AI:
case OS_INTR_DSP_ARAM:
case OS_INTR_DSP_DSP:
reg = __DSPRegs[0x00000005];
reg &= ~0x1F8;
if (!(current & OS_INTERRUPTMASK_DSP_AI))
reg |= 0x10;
if (!(current & OS_INTERRUPTMASK_DSP_ARAM))
reg |= 0x40;
if (!(current & OS_INTERRUPTMASK_DSP_DSP))
reg |= 0x100;
__DSPRegs[0x00000005] = (u16)reg;
mask &= ~OS_INTERRUPTMASK_DSP;
break;
case OS_INTR_AI_AI:
reg = __AIRegs[0];
reg &= ~0x2C;
if (!(current & OS_INTERRUPTMASK_AI_AI))
reg |= 0x4;
__AIRegs[0] = reg;
mask &= ~OS_INTERRUPTMASK_AI;
break;
case OS_INTR_EXI_0_EXI:
case OS_INTR_EXI_0_TC:
case OS_INTR_EXI_0_EXT:
reg = __EXIRegs[0];
reg &= ~0x2C0F;
if (!(current & OS_INTERRUPTMASK_EXI_0_EXI))
reg |= 0x1;
if (!(current & OS_INTERRUPTMASK_EXI_0_TC))
reg |= 0x4;
if (!(current & OS_INTERRUPTMASK_EXI_0_EXT))
reg |= 0x400;
__EXIRegs[0] = reg;
mask &= ~OS_INTERRUPTMASK_EXI_0;
break;
case OS_INTR_EXI_1_EXI:
case OS_INTR_EXI_1_TC:
case OS_INTR_EXI_1_EXT:
reg = __EXIRegs[5];
reg &= ~0xC0F;
if (!(current & OS_INTERRUPTMASK_EXI_1_EXI))
reg |= 0x1;
if (!(current & OS_INTERRUPTMASK_EXI_1_TC))
reg |= 0x4;
if (!(current & OS_INTERRUPTMASK_EXI_1_EXT))
reg |= 0x400;
__EXIRegs[5] = reg;
mask &= ~OS_INTERRUPTMASK_EXI_1;
break;
case OS_INTR_EXI_2_EXI:
case OS_INTR_EXI_2_TC:
reg = __EXIRegs[10];
reg &= ~0xF;
if (!(current & OS_INTERRUPTMASK_EXI_2_EXI))
reg |= 0x1;
if (!(current & OS_INTERRUPTMASK_EXI_2_TC))
reg |= 0x4;
__EXIRegs[10] = reg;
mask &= ~OS_INTERRUPTMASK_EXI_2;
break;
case OS_INTR_PI_CP:
case OS_INTR_PI_SI:
case OS_INTR_PI_DI:
case OS_INTR_PI_RSW:
case OS_INTR_PI_ERROR:
case OS_INTR_PI_VI:
case OS_INTR_PI_DEBUG:
case OS_INTR_PI_PE_TOKEN:
case OS_INTR_PI_PE_FINISH:
case OS_INTR_PI_HSP:
reg = 0xF0;
if (!(current & OS_INTERRUPTMASK_PI_CP)) {
reg |= 0x800;
}
if (!(current & OS_INTERRUPTMASK_PI_SI)) {
reg |= 0x8;
}
if (!(current & OS_INTERRUPTMASK_PI_DI)) {
reg |= 0x4;
}
if (!(current & OS_INTERRUPTMASK_PI_RSW)) {
reg |= 0x2;
}
if (!(current & OS_INTERRUPTMASK_PI_ERROR)) {
reg |= 0x1;
}
if (!(current & OS_INTERRUPTMASK_PI_VI)) {
reg |= 0x100;
}
if (!(current & OS_INTERRUPTMASK_PI_DEBUG)) {
reg |= 0x1000;
}
if (!(current & OS_INTERRUPTMASK_PI_PE_TOKEN)) {
reg |= 0x200;
}
if (!(current & OS_INTERRUPTMASK_PI_PE_FINISH)) {
reg |= 0x400;
}
if (!(current & OS_INTERRUPTMASK_PI_HSP)) {
reg |= 0x2000;
}
__PIRegs[1] = reg;
mask &= ~OS_INTERRUPTMASK_PI;
break;
default:
break;
}
return mask;
}
/* 8033DABC-8033DB44 3383FC 0088+00 1/1 15/15 0/0 .text __OSMaskInterrupts */
OSInterruptMask __OSMaskInterrupts(OSInterruptMask global) {
BOOL enabled;
OSInterruptMask prev;
OSInterruptMask local;
OSInterruptMask mask;
enabled = OSDisableInterrupts();
prev = *(OSInterruptMask*)OSPhysicalToCached(0x00C4);
local = *(OSInterruptMask*)OSPhysicalToCached(0x00C8);
mask = ~(prev | local) & global;
global |= prev;
*(OSInterruptMask*)OSPhysicalToCached(0x00C4) = global;
while (mask) {
mask = SetInterruptMask(mask, global | local);
}
OSRestoreInterrupts(enabled);
return prev;
}
/* 8033DB44-8033DBCC 338484 0088+00 0/0 18/18 0/0 .text __OSUnmaskInterrupts */
OSInterruptMask __OSUnmaskInterrupts(OSInterruptMask global) {
BOOL enabled;
OSInterruptMask prev;
OSInterruptMask local;
OSInterruptMask mask;
enabled = OSDisableInterrupts();
prev = *(OSInterruptMask*)OSPhysicalToCached(0x00C4);
local = *(OSInterruptMask*)OSPhysicalToCached(0x00C8);
mask = (prev | local) & global;
global = prev & ~global;
*(OSInterruptMask*)OSPhysicalToCached(0x00C4) = global;
while (mask) {
mask = SetInterruptMask(mask, global | local);
}
OSRestoreInterrupts(enabled);
return prev;
}
/* ############################################################################################## */
/* 803D0758-803D0788 02D878 002C+04 1/1 0/0 0/0 .data InterruptPrioTable */
static u32 InterruptPrioTable[] = {
0x00000100, 0x00000040, 0xF8000000, 0x00000200, 0x00000080, 0x00003000,
0x00000020, 0x03FF8C00, 0x04000000, 0x00004000, 0xFFFFFFFF,
};
/* 80451680-80451684 000B80 0004+00 1/1 1/1 0/0 .sbss __OSLastInterruptTime */
extern OSTime __OSLastInterruptTime;
OSTime __OSLastInterruptTime;
/* 80451678-80451680 000B78 0002+06 1/1 1/1 0/0 .sbss __OSLastInterrupt */
extern s16 __OSLastInterrupt;
s16 __OSLastInterrupt;
/* 80451674-80451678 000B74 0004+00 1/1 1/1 0/0 .sbss __OSLastInterruptSrr0 */
extern u32 __OSLastInterruptSrr0;
u32 __OSLastInterruptSrr0;
/* 8033DBCC-8033DF10 33850C 0344+00 1/1 0/0 0/0 .text __OSDispatchInterrupt */
void __OSDispatchInterrupt(__OSException exception, OSContext* context) {
u32 intsr;
u32 reg;
OSInterruptMask cause;
OSInterruptMask unmasked;
OSInterruptMask* prio;
__OSInterrupt interrupt;
__OSInterruptHandler handler;
intsr = __PIRegs[0];
intsr &= ~0x00010000;
if (intsr == 0 || (intsr & __PIRegs[1]) == 0) {
OSLoadContext(context);
}
cause = 0;
if (intsr & 0x00000080) {
reg = __MEMRegs[15];
if (reg & 0x1)
cause |= OS_INTERRUPTMASK_MEM_0;
if (reg & 0x2)
cause |= OS_INTERRUPTMASK_MEM_1;
if (reg & 0x4)
cause |= OS_INTERRUPTMASK_MEM_2;
if (reg & 0x8)
cause |= OS_INTERRUPTMASK_MEM_3;
if (reg & 0x10)
cause |= OS_INTERRUPTMASK_MEM_ADDRESS;
}
if (intsr & 0x00000040) {
reg = __DSPRegs[5];
if (reg & 0x8)
cause |= OS_INTERRUPTMASK_DSP_AI;
if (reg & 0x20)
cause |= OS_INTERRUPTMASK_DSP_ARAM;
if (reg & 0x80)
cause |= OS_INTERRUPTMASK_DSP_DSP;
}
if (intsr & 0x00000020) {
reg = __AIRegs[0];
if (reg & 0x8)
cause |= OS_INTERRUPTMASK_AI_AI;
}
if (intsr & 0x00000010) {
reg = __EXIRegs[0];
if (reg & 0x2)
cause |= OS_INTERRUPTMASK_EXI_0_EXI;
if (reg & 0x8)
cause |= OS_INTERRUPTMASK_EXI_0_TC;
if (reg & 0x800)
cause |= OS_INTERRUPTMASK_EXI_0_EXT;
reg = __EXIRegs[5];
if (reg & 0x2)
cause |= OS_INTERRUPTMASK_EXI_1_EXI;
if (reg & 0x8)
cause |= OS_INTERRUPTMASK_EXI_1_TC;
if (reg & 0x800)
cause |= OS_INTERRUPTMASK_EXI_1_EXT;
reg = __EXIRegs[10];
if (reg & 0x2)
cause |= OS_INTERRUPTMASK_EXI_2_EXI;
if (reg & 0x8)
cause |= OS_INTERRUPTMASK_EXI_2_TC;
}
if (intsr & 0x00002000)
cause |= OS_INTERRUPTMASK_PI_HSP;
if (intsr & 0x00001000)
cause |= OS_INTERRUPTMASK_PI_DEBUG;
if (intsr & 0x00000400)
cause |= OS_INTERRUPTMASK_PI_PE_FINISH;
if (intsr & 0x00000200)
cause |= OS_INTERRUPTMASK_PI_PE_TOKEN;
if (intsr & 0x00000100)
cause |= OS_INTERRUPTMASK_PI_VI;
if (intsr & 0x00000008)
cause |= OS_INTERRUPTMASK_PI_SI;
if (intsr & 0x00000004)
cause |= OS_INTERRUPTMASK_PI_DI;
if (intsr & 0x00000002)
cause |= OS_INTERRUPTMASK_PI_RSW;
if (intsr & 0x00000800)
cause |= OS_INTERRUPTMASK_PI_CP;
if (intsr & 0x00000001)
cause |= OS_INTERRUPTMASK_PI_ERROR;
unmasked = cause & ~(*(OSInterruptMask*)OSPhysicalToCached(0x00C4) |
*(OSInterruptMask*)OSPhysicalToCached(0x00C8));
if (unmasked) {
for (prio = InterruptPrioTable;; ++prio) {
if (unmasked & *prio) {
interrupt = (__OSInterrupt)__cntlzw(unmasked & *prio);
break;
}
}
handler = __OSGetInterruptHandler(interrupt);
if (handler) {
if (OS_INTR_MEM_ADDRESS < interrupt) {
__OSLastInterrupt = interrupt;
__OSLastInterruptTime = OSGetTime();
__OSLastInterruptSrr0 = context->srr0;
}
OSDisableScheduler();
handler(interrupt, context);
OSEnableScheduler();
__OSReschedule();
OSLoadContext(context);
}
}
OSLoadContext(context);
}
/* 8033DF10-8033DF60 338850 0050+00 1/1 0/0 0/0 .text ExternalInterruptHandler */
static asm void ExternalInterruptHandler(register __OSInterrupt type, register OSContext* context) {
// clang-format off
nofralloc
stw r0, context->gpr[0]
stw r1, context->gpr[1]
stw r2, context->gpr[2]
stmw r6, context->gpr[6]
mfspr r0, GQR1
stw r0, context->gqr[1]
mfspr r0, GQR2
stw r0, context->gqr[2]
mfspr r0, GQR3
stw r0, context->gqr[3]
mfspr r0, GQR4
stw r0, context->gqr[4]
mfspr r0, GQR5
stw r0, context->gqr[5]
mfspr r0, GQR6
stw r0, context->gqr[6]
mfspr r0, GQR7
stw r0, context->gqr[7]
stwu r1, -8(r1)
b __OSDispatchInterrupt
// clang-format on
}