From d2ddb9882f8716e538a6b52bb0df2dd869a8dfa1 Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Mon, 13 Feb 2017 16:12:24 +0300 Subject: [PATCH] SPU: IRET, SN event --- rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp | 5 ++- rpcs3/Emu/Cell/SPUAnalyser.cpp | 2 -- rpcs3/Emu/Cell/SPUInterpreter.cpp | 21 ++++++++---- rpcs3/Emu/Cell/SPURecompiler.cpp | 6 ++++ rpcs3/Emu/Cell/SPUThread.cpp | 45 ++++++++++++-------------- rpcs3/Emu/Cell/SPUThread.h | 6 +++- 6 files changed, 49 insertions(+), 36 deletions(-) diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index 131bfd95a9..4a7d668a81 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -917,7 +917,10 @@ void spu_recompiler::BISL(spu_opcode_t op) void spu_recompiler::IRET(spu_opcode_t op) { - fmt::throw_exception("Unimplemented instruction" HERE); + c->mov(*addr, SPU_OFF_32(srr0)); + c->and_(*addr, 0x3fffc); + if (op.d || op.e) c->or_(*addr, op.e << 26 | op.d << 27); // interrupt flags neutralize jump table + c->jmp(*jt); } void spu_recompiler::BISLED(spu_opcode_t op) diff --git a/rpcs3/Emu/Cell/SPUAnalyser.cpp b/rpcs3/Emu/Cell/SPUAnalyser.cpp index 0917df3bb6..7a009d1164 100644 --- a/rpcs3/Emu/Cell/SPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/SPUAnalyser.cpp @@ -168,8 +168,6 @@ std::shared_ptr SPUDatabase::analyse(const be_t* ls, u32 en if (type == BI || type == IRET) // Branch Indirect { - if (type == IRET) LOG_ERROR(SPU, "[0x%05x] Interrupt Return", pos); - blocks.emplace(start); start = pos + 4; } diff --git a/rpcs3/Emu/Cell/SPUInterpreter.cpp b/rpcs3/Emu/Cell/SPUInterpreter.cpp index a8aca695db..112f9083b9 100644 --- a/rpcs3/Emu/Cell/SPUInterpreter.cpp +++ b/rpcs3/Emu/Cell/SPUInterpreter.cpp @@ -49,6 +49,12 @@ void spu_interpreter::set_interrupt_status(SPUThread& spu, spu_opcode_t op) { spu.set_interrupt_status(false); } + + if ((spu.ch_event_stat & SPU_EVENT_INTR_TEST) > SPU_EVENT_INTR_ENABLED) + { + spu.ch_event_stat &= ~SPU_EVENT_INTR_ENABLED; + spu.srr0 = std::exchange(spu.pc, -4) + 4; + } } @@ -313,8 +319,8 @@ void spu_interpreter::BIZ(SPUThread& spu, spu_opcode_t op) { if (spu.gpr[op.rt]._u32[3] == 0) { - set_interrupt_status(spu, op); spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4; + set_interrupt_status(spu, op); } } @@ -322,8 +328,8 @@ void spu_interpreter::BINZ(SPUThread& spu, spu_opcode_t op) { if (spu.gpr[op.rt]._u32[3] != 0) { - set_interrupt_status(spu, op); spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4; + set_interrupt_status(spu, op); } } @@ -331,8 +337,8 @@ void spu_interpreter::BIHZ(SPUThread& spu, spu_opcode_t op) { if (spu.gpr[op.rt]._u16[6] == 0) { - set_interrupt_status(spu, op); spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4; + set_interrupt_status(spu, op); } } @@ -340,8 +346,8 @@ void spu_interpreter::BIHNZ(SPUThread& spu, spu_opcode_t op) { if (spu.gpr[op.rt]._u16[6] != 0) { - set_interrupt_status(spu, op); spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4; + set_interrupt_status(spu, op); } } @@ -357,21 +363,22 @@ void spu_interpreter::STQX(SPUThread& spu, spu_opcode_t op) void spu_interpreter::BI(SPUThread& spu, spu_opcode_t op) { - set_interrupt_status(spu, op); spu.pc = spu_branch_target(spu.gpr[op.ra]._u32[3]) - 4; + set_interrupt_status(spu, op); } void spu_interpreter::BISL(SPUThread& spu, spu_opcode_t op) { - set_interrupt_status(spu, op); const u32 target = spu_branch_target(spu.gpr[op.ra]._u32[3]); spu.gpr[op.rt] = v128::from32r(spu_branch_target(spu.pc + 4)); spu.pc = target - 4; + set_interrupt_status(spu, op); } void spu_interpreter::IRET(SPUThread& spu, spu_opcode_t op) { - fmt::throw_exception("Unimplemented instruction" HERE); + spu.pc = spu_branch_target(spu.srr0) - 4; + set_interrupt_status(spu, op); } void spu_interpreter::BISLED(SPUThread& spu, spu_opcode_t op) diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index f302cd748e..27fdbdb75a 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -76,4 +76,10 @@ void spu_recompiler_base::enter(SPUThread& spu) } spu.pc = res & 0x3fffc; + + if ((spu.ch_event_stat & SPU_EVENT_INTR_TEST) > SPU_EVENT_INTR_ENABLED) + { + spu.ch_event_stat &= ~SPU_EVENT_INTR_ENABLED; + spu.srr0 = std::exchange(spu.pc, 0); + } } diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index ccdf201ce4..0c453fa533 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -146,6 +146,7 @@ void SPUThread::cpu_init() ch_mfc_args = {}; mfc_queue.clear(); + srr0 = 0; ch_tag_mask = 0; ch_tag_stat.data.store({}); ch_stall_stat.data.store({}); @@ -371,6 +372,7 @@ void SPUThread::do_dma_list_cmd(u32 cmd, spu_mfc_arg_t args) if (rec->sb & 0x8000) { ch_stall_stat.set_value((1 << args.tag) | ch_stall_stat.get_value()); + ch_event_stat |= SPU_EVENT_SN; spu_mfc_arg_t stalled; stalled.ea = (args.ea & ~0xffffffff) | (list_addr + (i + 1) * 8); @@ -418,11 +420,6 @@ void SPUThread::process_mfc_cmd(u32 cmd) case MFC_GETLLAR_CMD: // acquire reservation { - if (ch_mfc_args.size != 128) - { - break; - } - const u32 raddr = vm::cast(ch_mfc_args.ea, HERE); vm::reservation_acquire(vm::base(offset + ch_mfc_args.lsa), raddr, 128); @@ -437,11 +434,6 @@ void SPUThread::process_mfc_cmd(u32 cmd) case MFC_PUTLLC_CMD: // store conditionally { - if (ch_mfc_args.size != 128) - { - break; - } - if (vm::reservation_update(vm::cast(ch_mfc_args.ea, HERE), vm::base(offset + ch_mfc_args.lsa), 128)) { if (std::exchange(last_raddr, 0) == 0) @@ -465,11 +457,6 @@ void SPUThread::process_mfc_cmd(u32 cmd) case MFC_PUTLLUC_CMD: // store unconditionally case MFC_PUTQLLUC_CMD: { - if (ch_mfc_args.size != 128) - { - break; - } - vm::reservation_op(vm::cast(ch_mfc_args.ea, HERE), 128, [this]() { std::memcpy(vm::base_priv(vm::cast(ch_mfc_args.ea, HERE)), vm::base(offset + ch_mfc_args.lsa), 128); @@ -512,9 +499,12 @@ u32 SPUThread::get_events(bool waiting) } // SPU Decrementer Event - if ((ch_dec_value - (get_timebased_time() - ch_dec_start_timestamp)) >> 31) + if (!ch_dec_value || (ch_dec_value - (get_timebased_time() - ch_dec_start_timestamp)) >> 31) { - ch_event_stat |= SPU_EVENT_TM; + if ((ch_event_stat & SPU_EVENT_TM) == 0) + { + ch_event_stat |= SPU_EVENT_TM; + } } // initialize waiting @@ -562,7 +552,7 @@ void SPUThread::set_interrupt_status(bool enable) if (enable) { // detect enabling interrupts with events masked - if (u32 mask = ch_event_mask) + if (u32 mask = ch_event_mask & ~SPU_EVENT_INTR_IMPLEMENTED) { fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x)" HERE, mask); } @@ -617,9 +607,11 @@ bool SPUThread::get_ch_value(u32 ch, u32& out) switch (ch) { - //case SPU_RdSRR0: - // value = SRR0; - // break; + case SPU_RdSRR0: + { + out = srr0; + break; + } case SPU_RdInMbox: { while (true) @@ -738,9 +730,12 @@ bool SPUThread::set_ch_value(u32 ch, u32 value) switch (ch) { - //case SPU_WrSRR0: - // SRR0 = value & 0x3FFFC; //LSLR & ~3 - // break; + case SPU_WrSRR0: + { + srr0 = value; + break; + } + case SPU_WrOutIntrMbox: { if (offset >= RAW_SPU_BASE_ADDR) @@ -1013,7 +1008,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value) case SPU_WrEventMask: { // detect masking events with enabled interrupt status - if (value && ch_event_stat & SPU_EVENT_INTR_ENABLED) + if (value & ~SPU_EVENT_INTR_IMPLEMENTED && ch_event_stat & SPU_EVENT_INTR_ENABLED) { fmt::throw_exception("SPU Interrupts not implemented (mask=0x%x)" HERE, value); } diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index 5be1fd6114..31d7d134d5 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -63,11 +63,14 @@ enum : u32 SPU_EVENT_SN = 0x2, // MFC List Command stall-and-notify event SPU_EVENT_TG = 0x1, // MFC Tag Group status update event - SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR | SPU_EVENT_TM, // Mask of implemented events + SPU_EVENT_IMPLEMENTED = SPU_EVENT_LR | SPU_EVENT_TM | SPU_EVENT_SN, // Mask of implemented events + SPU_EVENT_INTR_IMPLEMENTED = SPU_EVENT_SN, 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_EVENT_INTR_TEST = SPU_EVENT_INTR_ENABLED | SPU_EVENT_INTR_IMPLEMENTED }; // SPU Class 0 Interrupts @@ -516,6 +519,7 @@ public: std::vector> mfc_queue; // Only used for stalled list transfers + u32 srr0; u32 ch_tag_mask; spu_channel_t ch_tag_stat; spu_channel_t ch_stall_stat;