From cb786efd21936e5c318f0e0b00e92203994ec653 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Sat, 18 Aug 2018 16:39:57 +0200 Subject: [PATCH] lots of work on the interrupt handler --- 64drive.inc | 4 +- debug.asm | 16 +++-- kernel.asm | 165 +++++++++++++++++++++++++++++++++++++++++----------- main.asm | 20 +------ main.inc | 19 ++++-- n64.inc | 22 +++++++ 6 files changed, 183 insertions(+), 63 deletions(-) diff --git a/64drive.inc b/64drive.inc index 53abfc9..fbaa859 100644 --- a/64drive.inc +++ b/64drive.inc @@ -39,7 +39,7 @@ macro CI_WAIT() { lw t0, CI_STATUS(t9) srl t0, t0, 12 // first 12 bits are reserved, so ignore them bnez t0,- - nop // delay slot + nop } macro CI_USB_WRITE_WAIT() { @@ -48,5 +48,5 @@ macro CI_USB_WRITE_WAIT() { lw t0, CI_USB_COMMAND_STATUS(t9) srl t0, t0, 4 // shift out ARM status, leaving WRITE status bnez t0,- - nop // delay slot + nop } diff --git a/debug.asm b/debug.asm index 9af5b22..14ee1fc 100644 --- a/debug.asm +++ b/debug.asm @@ -6,7 +6,6 @@ Drive64Write: // v0: error code (0 is OK) // TODO: a0 should be double-word aligned if used directly with DMA - // assert a0 (RAM address) is word-aligned andi t9, a0, 3 bnezl t9, Drive64WriteExit @@ -27,19 +26,19 @@ Drive64Write: andi t6, a0, 0xF addu t7, a0, a1 // stop flushing around here subu t6, a0, t6 // align a0 to data line - subiu t7, t7, 1 // turn inclusive end-point into exclusive instead - - cache 1, 0(t6) // peter says: "Index Writeback Invalidate" + cache 1, 0(t6) // data cache Index Writeback Invalidate + addiu t6, 0x10 // (delay slot) += data line size sltu at, t6, t7 bnez at,- - addiu t6, 0x10 // (delay slot) += data line size + nop // AND off the DRAM address li t9, 0x007FFFFF // __osPiRawStartDma uses 0x1FFFFFFF? and t1, a0, t9 // cart address - move t2, a2 + or t2, a2, r0 // set length (needs to be decremented due to DMA quirk) subiu t3, a3, 1 @@ -91,16 +90,21 @@ DumpAndWrite: // v0: error code (0 is OK) subiu sp, sp, 0x20 sw ra, 0x10(sp) - // TODO: i think i can just use the a0,a1,a2,a3 slots here? + // TODO: can i just use the a0,a1,a2,a3 slots here? sw s0, 0x14(sp) sw s1, 0x18(sp) or s0, a2, r0 jal xxd or s1, a3, r0 + // v0 passthru bnez v0, DumpAndWriteExit + lui t0, K_BASE // delay slot + lw t1, K_64DRIVE_MAGIC(t0) + beqz t1, DumpAndWriteExit + ori a0, s0, r0 // delay slot jal Drive64Write ori a1, s1, r0 diff --git a/kernel.asm b/kernel.asm index 88904b1..3f34ee0 100644 --- a/kernel.asm +++ b/kernel.asm @@ -2,6 +2,7 @@ // just handling some low-level stuff like interrupts. Start: + mtc0 r0, CP0_Cause // clear cause lui k0, K_BASE // copy our interrupt handlers into place. @@ -12,28 +13,49 @@ Start: ld t3, 0(t1) ld t4, 8(t1) addiu t1, t1, 0x10 - ld t3, 0(t0) + sd t3, 0(t0) sd t4, 8(t0) - addiu t0, t0, 0x10 + cache 0x19, 0(t0) // tell data cache to write itself out + cache 0x10, 0(t0) // tell instruction cache it needs to reload + // an instruction cache line is 2 rows, and a data cache line is 1 row, so + // i'm hoping just poking at the start of each row is enough to flush them. bne t1, t2,- - cache 1, 0(t0) // not sure if this is necessary, but it doesn't hurt. + addiu t0, t0, 0x10 // enable SI and PI interrupts. lui a0, PIF_BASE lli t0, 8 sw t0, PIF_RAM+0x3C(a0) + // enable CPU interrupts. + mfc0 t1, CP0_Status + ori t1, t1, CP0_STATUS_IM_ALL + mtc0 t1, CP0_Status + + // enable even more interrupts. + lui t2, MI_BASE + ori t2, t2, MI_INTR_MASK + lli t0, 0xAAA // LSB to MSB: SP, SI, AI, VI, PI, DP + // by the way, use 0x555 to disable + sw t0, 0(t2) + + // it looks like i should be initializing PI_BSD_DOM1_* from + // the ROM header at this point, but i don't know what even does does. + // 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 + // we also need 4 empty words for storing + // the 32-bit values of the callee's arguments. subiu sp, sp, 0x10 // TODO: just wipe a portion of RAM? + // or just DMA in the IH and our defaults from ROM... sw r0, K_64DRIVE_MAGIC(k0) sw r0, K_REASON(k0) + sw r0, K_IN_MAIN(k0) Drive64Init: lui t9, CI_BASE @@ -62,9 +84,24 @@ Drive64Confirmed: Drive64Done: - // clear internal exception/interrupt value - ori k1, r0, r0 + // delay to empty pipeline? + nop + nop + nop + nop + nop + // try out an interrupt: + //sw r0, 0(r0) + mfc0 t1, CP0_Status + ori t1, 2 + mtc0 t1, CP0_Status + la t0, WipeRegisters + mtc0 t0, CP0_EPC + j InterruptHandler + nop + +WipeRegisters: // load up most registers with a dummy value for debugging lui at, 0xCAFE ori at, r0, 0xBABE @@ -106,8 +143,8 @@ Drive64Done: j Main nop -align(0x10) -_InterruptStart: // for copying purposes +align(0x10) // align to row for cache-poking purposes +_InterruptStart: // label for copying purposes pushvar base // note that we jump to the handler by jr instead of j @@ -140,7 +177,7 @@ InterruptOther: nops(0x80000200) pullvar base -_InterruptEnd: // for copying purposes +_InterruptEnd: // label for copying purposes InterruptHandler: lui k0, K_BASE @@ -149,9 +186,9 @@ InterruptHandler: 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 + mfc0 k1, CP0_Status + addiu at, r0, ~CP0_STATUS_IE + sw k1, K_STATUS(k0) and k1, k1, at mtc0 k1, CP0_Status @@ -159,7 +196,7 @@ InterruptHandler: sw k1, K_CAUSE(k0) // TODO: option to only store clobbered registers - // TODO: option to dump COP1 registers too + // TODO: option to dump COP1 registers too (remember to check Status[FR]) sd r0, K_DUMP+0x00(k0) // intentional (it'd be weird if // r0 showed as nonzero in memory dumps) @@ -199,30 +236,75 @@ InterruptHandler: sd t0, K_DUMP+0x100(k0) sd t1, K_DUMP+0x108(k0) + mfc0 k1, CP0_EPC // TODO: check that this is valid? + sw k1, K_EPC(k0) + + mfc0 k1, CP0_ErrorPC // TODO: check that this is valid? + sw k1, K_ERRORPC(k0) + + mfc0 k1, CP0_BadVAddr + sw k1, K_BADVADDR(k0) + +if K_DEBUG { + + // prevent recursive interrupts if IHMain somehow causes an interrupt + lw t1, K_IN_MAIN(k0) + bnez t1, IHExit + lli t0, 1 + sw t0, K_IN_MAIN(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 +macro KDumpString(str) { + lw t1, K_64DRIVE_MAGIC(k0) + beqz t1,+ + la a2, {str} jal Drive64WriteDirect - lli a3, 0x20 //IHString.size + lli a3, 0x20 // str.size ++ +} - ori a0, k0, K_DUMP - lli a1, 0x100 + KDumpString(KNewline) + KDumpString(KString0) + + ori a0, k0, K_DUMP + 0x80 * 0 + lli a1, 0x80 ori a2, k0, K_XXD jal DumpAndWrite - lli a3, 0x400 + lli a3, 0x80 * 4 - ori a0, k0, K_DUMP - addiu a0, a0, 0x100 - lli a1, 0x100 + KDumpString(KNewline) + + ori a0, k0, K_DUMP + 0x80 * 1 + lli a1, 0x80 ori a2, k0, K_XXD jal DumpAndWrite - lli a3, 0x400 + lli a3, 0x80 * 4 + + KDumpString(KNewline) + + // currently just 0x10 in size: LO and HI registers. + ori a0, k0, K_DUMP + 0x80 * 2 + lli a1, 0x10 + ori a2, k0, K_XXD + jal DumpAndWrite + lli a3, 0x10 * 4 + + KDumpString(KNewline) + KDumpString(KString1) + + ori a0, k0, K_DUMP + 0x80 * 4 + lli a1, 0x80 + ori a2, k0, K_XXD + jal DumpAndWrite + lli a3, 0x80 * 4 IHExit: + sw r0, K_IN_MAIN(k0) - jal Drive64Write +} lui k0, K_BASE ld t0, K_DUMP+0x100(k0) @@ -261,13 +343,13 @@ IHExit: ld ra, K_DUMP+0xF8(k0) lw k1, K_CAUSE(k0) - andi k1, k1, 0x2000 // check if this was a trap exception + xori k1, k1, 13 << 2 // check if this was a trap exception + bnez k1, ReturnFromInterrupt mfc0 k0, CP0_EPC - beqz k1, ReturnFromInterrupt - sw k0, K_EPC(k0) ReturnFromTrap: - addiu k0, k0, 4 + addiu k0, k0, 4 // TODO: this probably fails with branch delays? + mtc0 k0, CP0_EPC ReturnFromInterrupt: // restore interrupts @@ -275,16 +357,33 @@ ReturnFromInterrupt: ori k1, k1, 1 mtc0 k1, CP0_Status - // wait, shouldn't this be ERET? - rfe - jr k0 - or k1, r0, r0 + // eret pseudo-code: + //if status & 4 then + // jump to ErrorPC + // clear status & 4 + //elseif status & 2 then + // jump to EPC + // clear status & 2 + //else + // raise new exception??? + //end + eret + // no branch delay for eret include "debug.asm" align(4) -IHString: - db " ~~~ Interrupt Handled ~~~ ", 0 +KString0: + db " ~~ Interrupt Handled ~~", 10, 0 + +align(4) +KString1: + db " Interrupt States:", 10, 0 + +align(4) +KNewline: + db 10, 0, 0, 0 + dw 0, 0, 0 align(4) nops((K_BASE << 16) + 0x10000) diff --git a/main.asm b/main.asm index 834b42d..16b7b8e 100644 --- a/main.asm +++ b/main.asm @@ -37,30 +37,14 @@ include "lz.asm" mfc0 t0, CP0_Count sw t0, BLAH_COUNTS+8(s0) - lui t0, K_BASE - lw t1, K_64DRIVE_MAGIC(t0) - beqz t1, InitVideo - nop // delay slot - -// jal Drive64TestWrite -// nop // delay slot lui a0, BLAH_BASE lli a1, 0x20 ori a2, a0, BLAH_XXD + jal DumpAndWrite lli a3, 0x20 * 4 - jal xxd - nop // delay slot - - lui a0, BLAH_BASE // write address - ori a0, a0, BLAH_XXD // (RAM gets copied to SDRAM by routine) - lli a1, 0x20 * 4 - jal Drive64Write - nop // delay slot - -InitVideo: // currently 80001190 (this comment is likely out of date) - // A4000FC0 +InitVideo: jal LoadRSPBoot nop diff --git a/main.inc b/main.inc index 57b3382..4620e36 100644 --- a/main.inc +++ b/main.inc @@ -1,21 +1,32 @@ +// settings: +constant K_DEBUG(1) // slows down interrupt handling to enable debug routines + +// address stuff: constant UNCACHED(0xA0000000) constant ADDR_MASK(0x1FFFFFFF) // "kernel" constants: constant K_BASE(0x8000) // k0 is set to this. + constant K_DUMP(0x0400) // we save registers and state here // when handling interrupts + constant K_REASON(0x0600) constant K_CAUSE(0x0604) constant K_STATUS(0x0608) -constant K_EPC(0x060C) +constant K_IN_MAIN(0x060C) +constant K_EPC(0x0610) +constant K_ERRORPC(0x0614) +constant K_BADVADDR(0x0618) + constant K_64DRIVE_MAGIC(0x0700) constant K_CI_BASE(0x0704) -constant K_STACK(0xC00 - 8) + +constant K_STACK(0x0C00 - 0x10) constant K_XXD(0x0C00) // size: 0x400 (any larger and you overwrite kernel code) -constant K_STACK_INIT_BASE(0x803F) // note that this gets subtracted by 8 - // and that the stack grows *backwards.* +// note this gets subtracted by 0x10 and the stack grows *backwards.* +constant K_STACK_INIT_BASE(0x803F) // internal interrupt enum: (0 means no known interrupt/exception) constant K_INT_TLB_REFILL(1) diff --git a/n64.inc b/n64.inc index 905ae7e..40fdce5 100644 --- a/n64.inc +++ b/n64.inc @@ -139,6 +139,28 @@ constant CP0_TagHi(29) // reserved constant CP0_ErrorPC(30) constant CP0_Reserved_7(31) +constant CP0_STATUS_IE($0001) // Interrupt Enable +constant CP0_STATUS_EXL($0002) // Exception Level +constant CP0_STATUS_ERL($0004) // Error Level +constant CP0_STATUS_IM0($0100) // Interrupt Mask 0 (Software) +constant CP0_STATUS_IM1($0200) // Interrupt Mask 1 (Software) +constant CP0_STATUS_IM2($0400) // Interrupt Mask 2 (External) +constant CP0_STATUS_IM3($0800) // Interrupt Mask 3 (External) +constant CP0_STATUS_IM4($1000) // Interrupt Mask 4 (External) +constant CP0_STATUS_IM5($2000) // Interrupt Mask 5 (External) +constant CP0_STATUS_IM6($4000) // Interrupt Mask 6 (External) +constant CP0_STATUS_IM7($8000) // Interrupt Mask 7 (External) +constant CP0_STATUS_IM_ALL($FF01) // enable all interrupts + +constant CP0_CAUSE_IP0($0100) // Interrupt Pending 0 (Software) +constant CP0_CAUSE_IP1($0200) // Interrupt Pending 1 (Software) +constant CP0_CAUSE_IP2($0400) // Interrupt Pending 2 (External) +constant CP0_CAUSE_IP3($0800) // Interrupt Pending 3 (External) +constant CP0_CAUSE_IP4($1000) // Interrupt Pending 4 (External) +constant CP0_CAUSE_IP5($2000) // Interrupt Pending 5 (External) +constant CP0_CAUSE_IP6($4000) // Interrupt Pending 6 (External) +constant CP0_CAUSE_IP7($8000) // Interrupt Pending 7 (External) + // Memory Map constant RDRAM($A000) // $00000000..$003FFFFF RDRAM Memory 4MB ($00000000..$007FFFFF 8MB With Expansion Pak)