From 5bd83516ba7030788e4b3a1e9286483bee1dc816 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Thu, 16 Jul 2015 14:32:19 +0300 Subject: [PATCH] SPU Interrupt Enable Status implemented SPU Interrupts are still NOT implemented --- rpcs3/Emu/Cell/RawSPUThread.cpp | 10 +++- rpcs3/Emu/Cell/SPUInterpreter.cpp | 54 ++++++++++----------- rpcs3/Emu/Cell/SPUInterpreter.h | 80 ++++++++++++++++++------------- rpcs3/Emu/Cell/SPUInterpreter2.h | 2 + rpcs3/Emu/Cell/SPUThread.cpp | 41 +++++++++++++--- rpcs3/Emu/Cell/SPUThread.h | 7 ++- rpcs3/Emu/Memory/vm.cpp | 6 +-- 7 files changed, 121 insertions(+), 79 deletions(-) diff --git a/rpcs3/Emu/Cell/RawSPUThread.cpp b/rpcs3/Emu/Cell/RawSPUThread.cpp index 279530619f..b0be2d46b9 100644 --- a/rpcs3/Emu/Cell/RawSPUThread.cpp +++ b/rpcs3/Emu/Cell/RawSPUThread.cpp @@ -224,9 +224,15 @@ bool RawSPUThread::WriteReg(const u32 addr, const u32 value) void RawSPUThread::Task() { - PC = npc.exchange(0) & ~3; + // get next PC and SPU Interrupt status + PC = npc.exchange(0); + + set_interrupt_status((PC & 1) != 0); + + PC &= 0x3FFFC; SPUThread::Task(); - npc.store(PC | 1); + // save next PC and current SPU Interrupt status + npc.store(PC | ((ch_event_stat.load() & SPU_EVENT_INTR_ENABLED) != 0)); } diff --git a/rpcs3/Emu/Cell/SPUInterpreter.cpp b/rpcs3/Emu/Cell/SPUInterpreter.cpp index b498573b1b..31ff7f37c1 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/SPUInterpreter.cpp @@ -25,6 +25,24 @@ void spu_interpreter::DEFAULT(SPUThread& CPU, spu_opcode_t op) } +void spu_interpreter::set_interrupt_status(SPUThread& CPU, spu_opcode_t op) +{ + if (op.e) + { + CPU.set_interrupt_status(true); + + if (op.d) + { + throw EXCEPTION("Undefined behaviour"); + } + } + else if (op.d) + { + CPU.set_interrupt_status(false); + } +} + + void spu_interpreter::STOP(SPUThread& CPU, spu_opcode_t op) { CPU.stop_and_signal(op.opcode & 0x3fff); @@ -272,53 +290,37 @@ void spu_interpreter::WRCH(SPUThread& CPU, spu_opcode_t op) void spu_interpreter::BIZ(SPUThread& CPU, spu_opcode_t op) { - if (op.d || op.e) - { - throw EXCEPTION("Unimplemented interrupt flags (d=%d, e=%d)", op.d, op.e); - } - if (CPU.GPR[op.rt]._u32[3] == 0) { CPU.PC = SPUOpcodes::branchTarget(CPU.GPR[op.ra]._u32[3], 0) - 4; + set_interrupt_status(CPU, op); } } void spu_interpreter::BINZ(SPUThread& CPU, spu_opcode_t op) { - if (op.d || op.e) - { - throw EXCEPTION("Unimplemented interrupt flags (d=%d, e=%d)", op.d, op.e); - } - if (CPU.GPR[op.rt]._u32[3] != 0) { CPU.PC = SPUOpcodes::branchTarget(CPU.GPR[op.ra]._u32[3], 0) - 4; + set_interrupt_status(CPU, op); } } void spu_interpreter::BIHZ(SPUThread& CPU, spu_opcode_t op) { - if (op.d || op.e) - { - throw EXCEPTION("Unimplemented interrupt flags (d=%d, e=%d)", op.d, op.e); - } - if (CPU.GPR[op.rt]._u16[6] == 0) { CPU.PC = SPUOpcodes::branchTarget(CPU.GPR[op.ra]._u32[3], 0) - 4; + set_interrupt_status(CPU, op); } } void spu_interpreter::BIHNZ(SPUThread& CPU, spu_opcode_t op) { - if (op.d || op.e) - { - throw EXCEPTION("Unimplemented interrupt flags (d=%d, e=%d)", op.d, op.e); - } - if (CPU.GPR[op.rt]._u16[6] != 0) { CPU.PC = SPUOpcodes::branchTarget(CPU.GPR[op.ra]._u32[3], 0) - 4; + set_interrupt_status(CPU, op); } } @@ -334,24 +336,16 @@ void spu_interpreter::STQX(SPUThread& CPU, spu_opcode_t op) void spu_interpreter::BI(SPUThread& CPU, spu_opcode_t op) { - if (op.d || op.e) - { - throw EXCEPTION("Unimplemented interrupt flags (d=%d, e=%d)", op.d, op.e); - } - CPU.PC = SPUOpcodes::branchTarget(CPU.GPR[op.ra]._u32[3], 0) - 4; + set_interrupt_status(CPU, op); } void spu_interpreter::BISL(SPUThread& CPU, spu_opcode_t op) { - if (op.d || op.e) - { - throw EXCEPTION("Unimplemented interrupt flags (d=%d, e=%d)", op.d, op.e); - } - const u32 target = SPUOpcodes::branchTarget(CPU.GPR[op.ra]._u32[3], 0); CPU.GPR[op.rt] = u128::from32r(CPU.PC + 4); CPU.PC = target - 4; + set_interrupt_status(CPU, op); } void spu_interpreter::IRET(SPUThread& CPU, spu_opcode_t op) diff --git a/rpcs3/Emu/Cell/SPUInterpreter.h b/rpcs3/Emu/Cell/SPUInterpreter.h index 0c0a61cbc2..ab5c430f58 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.h +++ b/rpcs3/Emu/Cell/SPUInterpreter.h @@ -315,17 +315,19 @@ private: } void BIZ(u32 intr, u32 rt, u32 ra) { - switch (intr & 0x30) - { - case 0: break; - default: UNIMPLEMENTED(); return; - } - u32 target = branchTarget(CPU.GPR[ra]._u32[3], 0); if (CPU.GPR[rt]._u32[3] == 0) { LOG5_OPCODE("taken (0x%x)", target); CPU.PC = target - 4; + + switch (intr & 0x30) + { + case 0: break; + case 0x10: CPU.set_interrupt_status(true); + case 0x20: CPU.set_interrupt_status(false); + default: UNIMPLEMENTED(); return; + } } else { @@ -334,17 +336,19 @@ private: } void BINZ(u32 intr, u32 rt, u32 ra) { - switch (intr & 0x30) - { - case 0: break; - default: UNIMPLEMENTED(); return; - } - u32 target = branchTarget(CPU.GPR[ra]._u32[3], 0); if (CPU.GPR[rt]._u32[3] != 0) { LOG5_OPCODE("taken (0x%x)", target); CPU.PC = target - 4; + + switch (intr & 0x30) + { + case 0: break; + case 0x10: CPU.set_interrupt_status(true); + case 0x20: CPU.set_interrupt_status(false); + default: UNIMPLEMENTED(); return; + } } else { @@ -353,17 +357,19 @@ private: } void BIHZ(u32 intr, u32 rt, u32 ra) { - switch (intr & 0x30) - { - case 0: break; - default: UNIMPLEMENTED(); return; - } - u32 target = branchTarget(CPU.GPR[ra]._u32[3], 0); if (CPU.GPR[rt]._u16[6] == 0) { LOG5_OPCODE("taken (0x%x)", target); CPU.PC = target - 4; + + switch (intr & 0x30) + { + case 0: break; + case 0x10: CPU.set_interrupt_status(true); + case 0x20: CPU.set_interrupt_status(false); + default: UNIMPLEMENTED(); return; + } } else { @@ -372,17 +378,19 @@ private: } void BIHNZ(u32 intr, u32 rt, u32 ra) { - switch (intr & 0x30) - { - case 0: break; - default: UNIMPLEMENTED(); return; - } - u32 target = branchTarget(CPU.GPR[ra]._u32[3], 0); if (CPU.GPR[rt]._u16[6] != 0) { LOG5_OPCODE("taken (0x%x)", target); CPU.PC = target - 4; + + switch (intr & 0x30) + { + case 0: break; + case 0x10: CPU.set_interrupt_status(true); + case 0x20: CPU.set_interrupt_status(false); + default: UNIMPLEMENTED(); return; + } } else { @@ -401,28 +409,32 @@ private: } void BI(u32 intr, u32 ra) { - switch (intr & 0x30) - { - case 0: break; - default: UNIMPLEMENTED(); return; - } - u32 target = branchTarget(CPU.GPR[ra]._u32[3], 0); LOG5_OPCODE("branch (0x%x)", target); CPU.PC = target - 4; - } - void BISL(u32 intr, u32 rt, u32 ra) - { + switch (intr & 0x30) { case 0: break; + case 0x10: CPU.set_interrupt_status(true); + case 0x20: CPU.set_interrupt_status(false); default: UNIMPLEMENTED(); return; } - + } + void BISL(u32 intr, u32 rt, u32 ra) + { u32 target = branchTarget(CPU.GPR[ra]._u32[3], 0); CPU.GPR[rt] = u128::from32r(CPU.PC + 4); LOG5_OPCODE("branch (0x%x)", target); CPU.PC = target - 4; + + switch (intr & 0x30) + { + case 0: break; + case 0x10: CPU.set_interrupt_status(true); + case 0x20: CPU.set_interrupt_status(false); + default: UNIMPLEMENTED(); return; + } } void IRET(u32 ra) { diff --git a/rpcs3/Emu/Cell/SPUInterpreter2.h b/rpcs3/Emu/Cell/SPUInterpreter2.h index 8d1afd0c6e..6abe8516ed 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter2.h +++ b/rpcs3/Emu/Cell/SPUInterpreter2.h @@ -88,6 +88,8 @@ namespace spu_interpreter { void DEFAULT(SPUThread& CPU, spu_opcode_t op); + void set_interrupt_status(SPUThread& CPU, spu_opcode_t op); + void STOP(SPUThread& CPU, spu_opcode_t op); void LNOP(SPUThread& CPU, spu_opcode_t op); void SYNC(SPUThread& CPU, spu_opcode_t op); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index d339ec6227..c6c25d6b28 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -208,7 +208,7 @@ void SPUThread::InitRegs() ch_snr1 = {}; ch_snr2 = {}; - ch_event_mask = 0; + ch_event_mask = {}; ch_event_stat = {}; last_raddr = 0; @@ -548,7 +548,7 @@ u32 SPUThread::get_events(bool waiting) // polling with atomically set/removed SPU_EVENT_WAITING flag return ch_event_stat.atomic_op([this](u32& stat) -> u32 { - if (u32 res = stat & ch_event_mask) + if (u32 res = stat & ch_event_mask.load()) { stat &= ~SPU_EVENT_WAITING; return res; @@ -562,7 +562,7 @@ u32 SPUThread::get_events(bool waiting) } // simple polling - return ch_event_stat.load() & ch_event_mask; + return ch_event_stat.load() & ch_event_mask.load(); } void SPUThread::set_events(u32 mask) @@ -587,6 +587,24 @@ void SPUThread::set_events(u32 mask) } } +void SPUThread::set_interrupt_status(bool enable) +{ + if (enable) + { + // detect enabling interrupts with events masked + if (u32 mask = ch_event_mask.load()) + { + throw EXCEPTION("SPU Interrupts not implemented (mask=0x%x)", mask); + } + + ch_event_stat |= SPU_EVENT_INTR_ENABLED; + } + else + { + ch_event_stat &= ~SPU_EVENT_INTR_ENABLED; + } +} + u32 SPUThread::get_ch_count(u32 ch) { if (Ini.HLELogging.GetValue()) @@ -716,7 +734,7 @@ u32 SPUThread::get_ch_value(u32 ch) case SPU_RdEventMask: { - return ch_event_mask; + return ch_event_mask.load(); } case SPU_RdEventStat: @@ -729,7 +747,7 @@ u32 SPUThread::get_ch_value(u32 ch) return res; } - if (ch_event_mask & SPU_EVENT_LR) + if (ch_event_mask.load() & SPU_EVENT_LR) { // register waiter if polling reservation status is required vm::wait_op(*this, last_raddr, 128, WRAP_EXPR(get_events(true) || IsStopped())); @@ -756,7 +774,9 @@ u32 SPUThread::get_ch_value(u32 ch) case SPU_RdMachStat: { - return 0; // hack (not isolated, interrupts disabled) + // HACK: "Not isolated" status + // Return SPU Interrupt status in LSB + return (ch_event_stat.load() & SPU_EVENT_INTR_ENABLED) != 0; } } @@ -1107,12 +1127,19 @@ void SPUThread::set_ch_value(u32 ch, u32 value) case SPU_WrEventMask: { + // detect masking events with enabled interrupt status + if (value && ch_event_stat.load() & SPU_EVENT_INTR_ENABLED) + { + throw EXCEPTION("SPU Interrupts not implemented (mask=0x%x)", value); + } + + // detect masking unimplemented events if (value & ~SPU_EVENT_IMPLEMENTED) { break; } - ch_event_mask = value; + ch_event_mask.store(value); return; } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 9ccafb51df..856872a364 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -65,7 +65,9 @@ enum : u32 SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR, // Mask of implemented events - SPU_EVENT_WAITING = 0x80000000, // This bit is originally unused, set when SPU thread starts waiting on ch_event_stat + SPU_EVENT_WAITING = 0x80000000, // Originally unused, set when SPU thread starts waiting on ch_event_stat + //SPU_EVENT_AVAILABLE = 0x40000000, // Originally unused, channel count of the SPU_RdEventStat channel + SPU_EVENT_INTR_ENABLED = 0x20000000, // Originally unused, represents "SPU Interrupts Enabled" status }; // SPU Class 0 Interrupts @@ -502,7 +504,7 @@ public: spu_channel_t ch_snr1; // SPU Signal Notification Register 1 spu_channel_t ch_snr2; // SPU Signal Notification Register 2 - u32 ch_event_mask; + atomic_t ch_event_mask; atomic_t ch_event_stat; u32 last_raddr; // Last Reservation Address (0 if not set) @@ -558,6 +560,7 @@ public: u32 get_events(bool waiting = false); void set_events(u32 mask); + void set_interrupt_status(bool enable); u32 get_ch_count(u32 ch); u32 get_ch_value(u32 ch); void set_ch_value(u32 ch, u32 value); diff --git a/rpcs3/Emu/Memory/vm.cpp b/rpcs3/Emu/Memory/vm.cpp index 8486a162e7..1701c951f3 100644 --- a/rpcs3/Emu/Memory/vm.cpp +++ b/rpcs3/Emu/Memory/vm.cpp @@ -222,13 +222,12 @@ namespace vm } catch (...) { - // catch exception thrown by predicate + // capture any exception possibly thrown by predicate auto exception = std::current_exception(); // set new predicate that will throw this exception from the original thread pred = [exception]() -> bool { - // rethrow exception std::rethrow_exception(exception); // dummy return value @@ -241,10 +240,9 @@ namespace vm return true; } - // clear predicate if succeeded + // clear predicate and signal pred = nullptr; - // signal if succeeded or an exception thrown if (!thread->Signal()) { throw EXCEPTION("Thread already signaled");