// not really a kernel, // just handling some low-level stuff like interrupts. Start: lui k0, K_BASE // copy our interrupt handlers into place. lui t0, 0x8000 la t1, _InterruptStart la t2, _InterruptEnd - ld t3, 0(t1) ld t4, 8(t1) addiu t1, t1, 0x10 ld t3, 0(t0) sd t4, 8(t0) addiu t0, t0, 0x10 bne t1, t2,- cache 1, 0(t0) // not sure if this is necessary, but it doesn't hurt. // enable SI and PI interrupts. lui a0, PIF_BASE lli t0, 8 sw t0, PIF_RAM+0x3C(a0) // SP defaults to RSP instruction memory: 0xA4001FF0 // we can do better than that. lui sp, K_STACK_INIT_BASE // SP should always be 8-byte aligned // so that SD and LD instructions don't fail on it. // we also need 4 empty words for storing the 32-bit values of a0,a1,a2,a3 subiu sp, sp, 0x10 // TODO: just wipe a portion of RAM? sw r0, K_64DRIVE_MAGIC(k0) sw r0, K_REASON(k0) Drive64Init: lui t9, CI_BASE lui t2, 0x5544 // "UD" of "UDEV" lw t1, CI_HW_MAGIC(t9) ori t2, t2, 0x4556 // "EV" of "UDEV" beq t1, t2, Drive64Confirmed nop Drive64TryExtended: lui t9, CI_BASE_EXTENDED lw t1, CI_HW_MAGIC(t9) bne t1, t2, Drive64Done nop Drive64Confirmed: sw t2, K_64DRIVE_MAGIC(k0) sw t9, K_CI_BASE(k0) // enable writing to cartROM (SDRAM) for USB writing later lli t1, 0xF0 CI_WAIT() // clobbers t0, requires t9 sw t1, CI_COMMAND(t9) CI_WAIT() // clobbers t0, requires t9 Drive64Done: // clear internal exception/interrupt value ori k1, r0, r0 // load up most registers with a dummy value for debugging lui at, 0xCAFE ori at, r0, 0xBABE dsllv at, 32 // attempting to use this as an address should trigger an interrupt ori at, r0, 0xDEAD dsllv at, 16 ori at, r0, 0xBEEF // k0, k1, sp intentionally absent daddu v0, at, r0 daddu v1, at, r0 daddu a0, at, r0 daddu a1, at, r0 daddu a2, at, r0 daddu a3, at, r0 daddu t0, at, r0 daddu t1, at, r0 daddu t2, at, r0 daddu t3, at, r0 daddu t4, at, r0 daddu t5, at, r0 daddu t6, at, r0 daddu t7, at, r0 daddu s0, at, r0 daddu s1, at, r0 daddu s2, at, r0 daddu s3, at, r0 daddu s4, at, r0 daddu s5, at, r0 daddu s6, at, r0 daddu s7, at, r0 daddu t8, at, r0 daddu t9, at, r0 daddu gp, at, r0 daddu fp, at, r0 daddu ra, at, r0 j Main nop align(0x10) _InterruptStart: // for copying purposes pushvar base // note that we jump to the handler by jr instead of j // because we want to change the PC to cached memory, // which depends on the higher bits that j cannot change. base 0x80000000 InterruptTBLRefill: la k0, InterruptHandler jr k0 lli k1, K_INT_TLB_REFILL nops(0x80000080) InterruptXTLBRefill: la k0, InterruptHandler jr k0 lli k1, K_INT_XTLB_REFILL nops(0x80000100) InterruptCacheError: // A0000100? la k0, InterruptHandler jr k0 lli k1, K_INT_CACHE_ERROR nops(0x80000180) InterruptOther: la k0, InterruptHandler jr k0 lli k1, K_INT_OTHER nops(0x80000200) pullvar base _InterruptEnd: // for copying purposes InterruptHandler: lui k0, K_BASE sw k1, K_REASON(k0) sd at, K_DUMP+0x08(k0) // disable interrupts, clear exception and error level bits: mfc0 at, CP0_Status sw at, K_STATUS(k0) // TODO: restored later addiu at, r0, 0xFFFC and k1, k1, at mtc0 k1, CP0_Status mfc0 k1, CP0_Cause sw k1, K_CAUSE(k0) // TODO: option to only store clobbered registers // TODO: option to dump COP1 registers too sd r0, K_DUMP+0x00(k0) // intentional (it'd be weird if // r0 showed as nonzero in memory dumps) sd v0, K_DUMP+0x10(k0) sd v1, K_DUMP+0x18(k0) sd a0, K_DUMP+0x20(k0) sd a1, K_DUMP+0x28(k0) sd a2, K_DUMP+0x30(k0) sd a3, K_DUMP+0x38(k0) sd t0, K_DUMP+0x40(k0) sd t1, K_DUMP+0x48(k0) sd t2, K_DUMP+0x50(k0) sd t3, K_DUMP+0x58(k0) sd t4, K_DUMP+0x60(k0) sd t5, K_DUMP+0x68(k0) sd t6, K_DUMP+0x70(k0) sd t7, K_DUMP+0x78(k0) sd s0, K_DUMP+0x80(k0) sd s1, K_DUMP+0x88(k0) sd s2, K_DUMP+0x90(k0) sd s3, K_DUMP+0x98(k0) sd s4, K_DUMP+0xA0(k0) sd s5, K_DUMP+0xA8(k0) sd s6, K_DUMP+0xB0(k0) sd s7, K_DUMP+0xB8(k0) sd t8, K_DUMP+0xC0(k0) sd t9, K_DUMP+0xC8(k0) sd k0, K_DUMP+0xD0(k0) sd k1, K_DUMP+0xD8(k0) sd gp, K_DUMP+0xE0(k0) sd sp, K_DUMP+0xE8(k0) sd fp, K_DUMP+0xF0(k0) sd ra, K_DUMP+0xF8(k0) mfhi t0 mflo t1 sd t0, K_DUMP+0x100(k0) sd t1, K_DUMP+0x108(k0) // be wary, this is a tiny temporary stack! ori sp, k0, K_STACK IHMain: // free to modify any GPR from here to IHExit la a2, IHString jal Drive64WriteDirect lli a3, 0x20 //IHString.size ori a0, k0, K_DUMP lli a1, 0x100 ori a2, k0, K_XXD jal DumpAndWrite lli a3, 0x400 ori a0, k0, K_DUMP addiu a0, a0, 0x100 lli a1, 0x100 ori a2, k0, K_XXD jal DumpAndWrite lli a3, 0x400 IHExit: jal Drive64Write lui k0, K_BASE ld t0, K_DUMP+0x100(k0) ld t1, K_DUMP+0x108(k0) mthi t0 mtlo t1 ld at, K_DUMP+0x08(k0) ld v0, K_DUMP+0x10(k0) ld v1, K_DUMP+0x18(k0) ld a0, K_DUMP+0x20(k0) ld a1, K_DUMP+0x28(k0) ld a2, K_DUMP+0x30(k0) ld a3, K_DUMP+0x38(k0) ld t0, K_DUMP+0x40(k0) ld t1, K_DUMP+0x48(k0) ld t2, K_DUMP+0x50(k0) ld t3, K_DUMP+0x58(k0) ld t4, K_DUMP+0x60(k0) ld t5, K_DUMP+0x68(k0) ld t6, K_DUMP+0x70(k0) ld t7, K_DUMP+0x78(k0) ld s0, K_DUMP+0x80(k0) ld s1, K_DUMP+0x88(k0) ld s2, K_DUMP+0x90(k0) ld s3, K_DUMP+0x98(k0) ld s4, K_DUMP+0xA0(k0) ld s5, K_DUMP+0xA8(k0) ld s6, K_DUMP+0xB0(k0) ld s7, K_DUMP+0xB8(k0) ld t8, K_DUMP+0xC0(k0) ld t9, K_DUMP+0xC8(k0) ld gp, K_DUMP+0xE0(k0) ld sp, K_DUMP+0xE8(k0) ld fp, K_DUMP+0xF0(k0) ld ra, K_DUMP+0xF8(k0) lw k1, K_CAUSE(k0) andi k1, k1, 0x2000 // check if this was a trap exception mfc0 k0, CP0_EPC beqz k1, ReturnFromInterrupt sw k0, K_EPC(k0) ReturnFromTrap: addiu k0, k0, 4 ReturnFromInterrupt: // restore interrupts mfc0 k1, CP0_Status ori k1, k1, 1 mtc0 k1, CP0_Status // wait, shouldn't this be ERET? rfe jr k0 or k1, r0, r0 include "debug.asm" align(4) IHString: db " ~~~ Interrupt Handled ~~~ ", 0 align(4) nops((K_BASE << 16) + 0x10000)