diff --git a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp index 40e5b5946c..3268332e96 100644 --- a/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPUASMJITRecompiler.cpp @@ -1461,7 +1461,7 @@ void spu_recompiler::RDCH(spu_opcode_t op) auto sub2 = [](spu_thread* _spu, v128* _res) { - const u32 out = _spu->ch_dec_value - static_cast(get_timebased_time() - _spu->ch_dec_start_timestamp); + const u32 out = _spu->read_dec().first; *_res = v128::from32r(out); }; @@ -2467,6 +2467,7 @@ void spu_recompiler::WRCH(spu_opcode_t op) { auto sub = [](spu_thread* _spu) { + _spu->get_events(SPU_EVENT_TM); _spu->ch_dec_start_timestamp = get_timebased_time(); }; @@ -2474,6 +2475,7 @@ void spu_recompiler::WRCH(spu_opcode_t op) c->call(+sub); c->mov(qw0->r32(), SPU_OFF_32(gpr, op.rt, &v128::_u32, 3)); c->mov(SPU_OFF_32(ch_dec_value), qw0->r32()); + c->mov(SPU_OFF_8(is_dec_frozen), 0); return; } case SPU_WrEventMask: diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index a7739e2675..9d948b610e 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -5579,7 +5579,7 @@ public: static u32 exec_read_dec(spu_thread* _spu) { - const u32 res = _spu->ch_dec_value - static_cast(get_timebased_time() - _spu->ch_dec_start_timestamp); + const u32 res = _spu->read_dec().first; if (res > 1500 && g_cfg.core.spu_loop_detection) { @@ -6331,6 +6331,7 @@ public: call("spu_get_events", &exec_get_events, m_thread, m_ir->getInt32(SPU_EVENT_TM)); m_ir->CreateStore(call("get_timebased_time", &get_timebased_time), spu_ptr(&spu_thread::ch_dec_start_timestamp)); m_ir->CreateStore(val.value, spu_ptr(&spu_thread::ch_dec_value)); + m_ir->CreateStore(m_ir->getInt8(0), spu_ptr(&spu_thread::is_dec_frozen)); return; } case SPU_Set_Bkmk_Tag: diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 024a51971b..4fd15a190c 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1309,6 +1309,7 @@ void spu_thread::cpu_init() ch_dec_start_timestamp = get_timebased_time(); ch_dec_value = option & SYS_SPU_THREAD_OPTION_DEC_SYNC_TB_ENABLE ? ~static_cast(ch_dec_start_timestamp) : 0; + is_dec_frozen = false; if (get_type() >= spu_type::raw) { @@ -3641,6 +3642,12 @@ bool spu_thread::reservation_check(u32 addr, const decltype(rdata)& data) const return !res; } +std::pair spu_thread::read_dec() const +{ + const u64 res = ch_dec_value - (is_dec_frozen ? 0 : (get_timebased_time() - ch_dec_start_timestamp)); + return {static_cast(res), static_cast(res >> 32)}; +} + spu_thread::ch_events_t spu_thread::get_events(u32 mask_hint, bool waiting, bool reading) { if (auto mask1 = ch_events.load().mask; mask1 & ~SPU_EVENT_IMPLEMENTED) @@ -3664,7 +3671,7 @@ retry: // SPU Decrementer Event on underflow (use the upper 32-bits to determine it) if (mask_hint & SPU_EVENT_TM) { - if (const u64 res = (ch_dec_value - (get_timebased_time() - ch_dec_start_timestamp)) >> 32) + if (const u64 res = read_dec().second) { // Set next event to the next time the decrementer underflows ch_dec_start_timestamp -= res << 32; @@ -3921,7 +3928,7 @@ s64 spu_thread::get_ch_value(u32 ch) case SPU_RdDec: { - u32 out = ch_dec_value - static_cast(get_timebased_time() - ch_dec_start_timestamp); + u32 out = read_dec().first; //Polling: We might as well hint to the scheduler to slot in another thread since this one is counting down if (g_cfg.core.spu_loop_detection && out > spu::scheduler::native_jiffy_duration_us) @@ -4337,6 +4344,7 @@ bool spu_thread::set_ch_value(u32 ch, u32 value) get_events(SPU_EVENT_TM); // Don't discard possibly occured old event ch_dec_start_timestamp = get_timebased_time(); ch_dec_value = value; + is_dec_frozen = false; return true; } @@ -4372,10 +4380,14 @@ bool spu_thread::set_ch_value(u32 ch, u32 value) // "Collect" events before final acknowledgment get_events(value); - if (ch_events.atomic_op([&](ch_events_t& events) + bool freeze_dec = false; + + const bool check_intr = ch_events.atomic_op([&](ch_events_t& events) { events.events &= ~value; + freeze_dec = !!((value & SPU_EVENT_TM) & ~events.mask); + if (events.events & events.mask) { events.count = true; @@ -4383,7 +4395,16 @@ bool spu_thread::set_ch_value(u32 ch, u32 value) } return false; - })) + }); + + if (!is_dec_frozen && freeze_dec) + { + // Save current time, this will be the reported value until the decrementer resumes + ch_dec_value = read_dec().first; + is_dec_frozen = true; + } + + if (check_intr) { // Check interrupts in case count is 1 if (check_mfc_interrupts(pc + 4)) diff --git a/rpcs3/Emu/Cell/SPUThread.h b/rpcs3/Emu/Cell/SPUThread.h index ae2a9f83a0..28869973fe 100644 --- a/rpcs3/Emu/Cell/SPUThread.h +++ b/rpcs3/Emu/Cell/SPUThread.h @@ -733,6 +733,8 @@ public: u64 ch_dec_start_timestamp; // timestamp of writing decrementer value u32 ch_dec_value; // written decrementer value + bool is_dec_frozen = false; + std::pair read_dec() const; // Read decrementer atomic_t run_ctrl; // SPU Run Control register (only provided to get latest data written) shared_mutex run_ctrl_mtx;