CPU/NewRec: Handle mtc0 rt, sr
This commit is contained in:
parent
0ba50243ec
commit
312790c9a6
|
@ -200045,7 +200045,6 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"traits": {
|
"traits": {
|
||||||
"ForceInterpreter": true,
|
|
||||||
"IsLibCryptProtected": true
|
"IsLibCryptProtected": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -200084,11 +200083,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"rating": 5,
|
"rating": 5
|
||||||
"comments": "Must use interpreter to avoid crash"
|
|
||||||
},
|
|
||||||
"traits": {
|
|
||||||
"ForceInterpreter": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -2357,6 +2357,13 @@ void CPU::CodeCache::InterpretUncachedBlock()
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else if ((g_state.current_instruction.bits & 0xFFC0FFFFu) == 0x40806000u && HasPendingInterrupt())
|
||||||
|
{
|
||||||
|
// mtc0 rt, sr - Jackie Chan Stuntmaster, MTV Sports games.
|
||||||
|
// Pain in the ass games trigger a software interrupt by writing to SR.Im.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
in_branch_delay_slot = branch;
|
in_branch_delay_slot = branch;
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,14 +367,14 @@ void CPU::NewRec::AArch32Compiler::EndBlock(const std::optional<u32>& newpc, boo
|
||||||
|
|
||||||
// flush regs
|
// flush regs
|
||||||
Flush(FLUSH_END_BLOCK);
|
Flush(FLUSH_END_BLOCK);
|
||||||
EndAndLinkBlock(newpc, do_event_test);
|
EndAndLinkBlock(newpc, do_event_test, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NewRec::AArch32Compiler::EndBlockWithException(Exception excode)
|
void CPU::NewRec::AArch32Compiler::EndBlockWithException(Exception excode)
|
||||||
{
|
{
|
||||||
// flush regs, but not pc, it's going to get overwritten
|
// flush regs, but not pc, it's going to get overwritten
|
||||||
// flush cycles because of the GTE instruction stuff...
|
// flush cycles because of the GTE instruction stuff...
|
||||||
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION);
|
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
||||||
|
|
||||||
// TODO: flush load delay
|
// TODO: flush load delay
|
||||||
// TODO: break for pcdrv
|
// TODO: break for pcdrv
|
||||||
|
@ -385,14 +385,16 @@ void CPU::NewRec::AArch32Compiler::EndBlockWithException(Exception excode)
|
||||||
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
||||||
m_dirty_pc = false;
|
m_dirty_pc = false;
|
||||||
|
|
||||||
EndAndLinkBlock(std::nullopt, true);
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NewRec::AArch32Compiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test)
|
void CPU::NewRec::AArch32Compiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test,
|
||||||
|
bool force_run_events)
|
||||||
{
|
{
|
||||||
// event test
|
// event test
|
||||||
// pc should've been flushed
|
// pc should've been flushed
|
||||||
DebugAssert(!m_dirty_pc);
|
DebugAssert(!m_dirty_pc && !force_run_events);
|
||||||
|
m_block_ended = true;
|
||||||
|
|
||||||
// TODO: try extracting this to a function
|
// TODO: try extracting this to a function
|
||||||
|
|
||||||
|
@ -420,7 +422,11 @@ void CPU::NewRec::AArch32Compiler::EndAndLinkBlock(const std::optional<u32>& new
|
||||||
armEmitCondBranch(armAsm, ge, CodeCache::g_run_events_and_dispatch);
|
armEmitCondBranch(armAsm, ge, CodeCache::g_run_events_and_dispatch);
|
||||||
|
|
||||||
// jump to dispatcher or next block
|
// jump to dispatcher or next block
|
||||||
if (!newpc.has_value())
|
if (force_run_events)
|
||||||
|
{
|
||||||
|
armEmitJmp(armAsm, CodeCache::g_run_events_and_dispatch, false);
|
||||||
|
}
|
||||||
|
else if (!newpc.has_value())
|
||||||
{
|
{
|
||||||
armEmitJmp(armAsm, CodeCache::g_dispatcher, false);
|
armEmitJmp(armAsm, CodeCache::g_dispatcher, false);
|
||||||
}
|
}
|
||||||
|
@ -438,8 +444,6 @@ void CPU::NewRec::AArch32Compiler::EndAndLinkBlock(const std::optional<u32>& new
|
||||||
armEmitJmp(armAsm, target, true);
|
armEmitJmp(armAsm, target, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_block_ended = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* CPU::NewRec::AArch32Compiler::EndCompile(u32* code_size, u32* far_code_size)
|
const void* CPU::NewRec::AArch32Compiler::EndCompile(u32* code_size, u32* far_code_size)
|
||||||
|
@ -1979,9 +1983,31 @@ void CPU::NewRec::AArch32Compiler::TestInterrupts(const vixl::aarch32::Register&
|
||||||
|
|
||||||
SwitchToFarCode(true, ne);
|
SwitchToFarCode(true, ne);
|
||||||
BackupHostState();
|
BackupHostState();
|
||||||
Flush(FLUSH_FLUSH_MIPS_REGISTERS | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
||||||
EmitCall(reinterpret_cast<const void*>(&DispatchInterrupt));
|
|
||||||
EndBlock(std::nullopt, true);
|
// Can't use EndBlockWithException() here, because it'll use the wrong PC.
|
||||||
|
// Can't use RaiseException() on the fast path if we're the last instruction, because the next PC is unknown.
|
||||||
|
if (!iinfo->is_last_instruction)
|
||||||
|
{
|
||||||
|
EmitMov(RARG1, Cop0Registers::CAUSE::MakeValueForException(Exception::INT, iinfo->is_branch_instruction, false,
|
||||||
|
(inst + 1)->cop.cop_n));
|
||||||
|
EmitMov(RARG2, m_compiler_pc);
|
||||||
|
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
||||||
|
m_dirty_pc = false;
|
||||||
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EmitMov(RARG1, 0);
|
||||||
|
if (m_dirty_pc)
|
||||||
|
EmitMov(RARG2, m_compiler_pc);
|
||||||
|
armAsm->str(RARG1, PTR(&g_state.downcount));
|
||||||
|
if (m_dirty_pc)
|
||||||
|
armAsm->str(RARG2, m_compiler_pc);
|
||||||
|
m_dirty_pc = false;
|
||||||
|
EndAndLinkBlock(std::nullopt, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
RestoreHostState();
|
RestoreHostState();
|
||||||
SwitchToNearCode(false);
|
SwitchToNearCode(false);
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ protected:
|
||||||
void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) override;
|
void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) override;
|
||||||
void EndBlock(const std::optional<u32>& newpc, bool do_event_test) override;
|
void EndBlock(const std::optional<u32>& newpc, bool do_event_test) override;
|
||||||
void EndBlockWithException(Exception excode) override;
|
void EndBlockWithException(Exception excode) override;
|
||||||
void EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test);
|
void EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, bool force_run_events);
|
||||||
const void* EndCompile(u32* code_size, u32* far_code_size) override;
|
const void* EndCompile(u32* code_size, u32* far_code_size) override;
|
||||||
|
|
||||||
void Flush(u32 flags) override;
|
void Flush(u32 flags) override;
|
||||||
|
|
|
@ -339,14 +339,14 @@ void CPU::NewRec::AArch64Compiler::EndBlock(const std::optional<u32>& newpc, boo
|
||||||
|
|
||||||
// flush regs
|
// flush regs
|
||||||
Flush(FLUSH_END_BLOCK);
|
Flush(FLUSH_END_BLOCK);
|
||||||
EndAndLinkBlock(newpc, do_event_test);
|
EndAndLinkBlock(newpc, do_event_test, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NewRec::AArch64Compiler::EndBlockWithException(Exception excode)
|
void CPU::NewRec::AArch64Compiler::EndBlockWithException(Exception excode)
|
||||||
{
|
{
|
||||||
// flush regs, but not pc, it's going to get overwritten
|
// flush regs, but not pc, it's going to get overwritten
|
||||||
// flush cycles because of the GTE instruction stuff...
|
// flush cycles because of the GTE instruction stuff...
|
||||||
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION);
|
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
||||||
|
|
||||||
// TODO: flush load delay
|
// TODO: flush load delay
|
||||||
// TODO: break for pcdrv
|
// TODO: break for pcdrv
|
||||||
|
@ -357,14 +357,16 @@ void CPU::NewRec::AArch64Compiler::EndBlockWithException(Exception excode)
|
||||||
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
||||||
m_dirty_pc = false;
|
m_dirty_pc = false;
|
||||||
|
|
||||||
EndAndLinkBlock(std::nullopt, true);
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NewRec::AArch64Compiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test)
|
void CPU::NewRec::AArch64Compiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test,
|
||||||
|
bool force_run_events)
|
||||||
{
|
{
|
||||||
// event test
|
// event test
|
||||||
// pc should've been flushed
|
// pc should've been flushed
|
||||||
DebugAssert(!m_dirty_pc);
|
DebugAssert(!m_dirty_pc && !m_block_ended);
|
||||||
|
m_block_ended = true;
|
||||||
|
|
||||||
// TODO: try extracting this to a function
|
// TODO: try extracting this to a function
|
||||||
|
|
||||||
|
@ -392,7 +394,11 @@ void CPU::NewRec::AArch64Compiler::EndAndLinkBlock(const std::optional<u32>& new
|
||||||
armEmitCondBranch(armAsm, ge, CodeCache::g_run_events_and_dispatch);
|
armEmitCondBranch(armAsm, ge, CodeCache::g_run_events_and_dispatch);
|
||||||
|
|
||||||
// jump to dispatcher or next block
|
// jump to dispatcher or next block
|
||||||
if (!newpc.has_value())
|
if (force_run_events)
|
||||||
|
{
|
||||||
|
armEmitJmp(armAsm, CodeCache::g_run_events_and_dispatch, false);
|
||||||
|
}
|
||||||
|
else if (!newpc.has_value())
|
||||||
{
|
{
|
||||||
armEmitJmp(armAsm, CodeCache::g_dispatcher, false);
|
armEmitJmp(armAsm, CodeCache::g_dispatcher, false);
|
||||||
}
|
}
|
||||||
|
@ -410,8 +416,6 @@ void CPU::NewRec::AArch64Compiler::EndAndLinkBlock(const std::optional<u32>& new
|
||||||
armEmitJmp(armAsm, target, true);
|
armEmitJmp(armAsm, target, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_block_ended = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* CPU::NewRec::AArch64Compiler::EndCompile(u32* code_size, u32* far_code_size)
|
const void* CPU::NewRec::AArch64Compiler::EndCompile(u32* code_size, u32* far_code_size)
|
||||||
|
@ -1953,9 +1957,29 @@ void CPU::NewRec::AArch64Compiler::TestInterrupts(const vixl::aarch64::WRegister
|
||||||
|
|
||||||
SwitchToFarCode(true, ne);
|
SwitchToFarCode(true, ne);
|
||||||
BackupHostState();
|
BackupHostState();
|
||||||
Flush(FLUSH_FLUSH_MIPS_REGISTERS | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
||||||
EmitCall(reinterpret_cast<const void*>(&DispatchInterrupt));
|
|
||||||
EndBlock(std::nullopt, true);
|
// Can't use EndBlockWithException() here, because it'll use the wrong PC.
|
||||||
|
// Can't use RaiseException() on the fast path if we're the last instruction, because the next PC is unknown.
|
||||||
|
if (!iinfo->is_last_instruction)
|
||||||
|
{
|
||||||
|
EmitMov(RWARG1, Cop0Registers::CAUSE::MakeValueForException(Exception::INT, iinfo->is_branch_instruction, false,
|
||||||
|
(inst + 1)->cop.cop_n));
|
||||||
|
EmitMov(RWARG2, m_compiler_pc);
|
||||||
|
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
||||||
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_dirty_pc)
|
||||||
|
EmitMov(RWARG1, m_compiler_pc);
|
||||||
|
armAsm->str(wzr, PTR(&g_state.downcount));
|
||||||
|
if (m_dirty_pc)
|
||||||
|
armAsm->str(RWARG1, PTR(&g_state.pc));
|
||||||
|
m_dirty_pc = false;
|
||||||
|
EndAndLinkBlock(std::nullopt, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
RestoreHostState();
|
RestoreHostState();
|
||||||
SwitchToNearCode(false);
|
SwitchToNearCode(false);
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ protected:
|
||||||
void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) override;
|
void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) override;
|
||||||
void EndBlock(const std::optional<u32>& newpc, bool do_event_test) override;
|
void EndBlock(const std::optional<u32>& newpc, bool do_event_test) override;
|
||||||
void EndBlockWithException(Exception excode) override;
|
void EndBlockWithException(Exception excode) override;
|
||||||
void EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test);
|
void EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, bool force_run_events);
|
||||||
const void* EndCompile(u32* code_size, u32* far_code_size) override;
|
const void* EndCompile(u32* code_size, u32* far_code_size) override;
|
||||||
|
|
||||||
void Flush(u32 flags) override;
|
void Flush(u32 flags) override;
|
||||||
|
|
|
@ -586,14 +586,14 @@ void CPU::NewRec::RISCV64Compiler::EndBlock(const std::optional<u32>& newpc, boo
|
||||||
|
|
||||||
// flush regs
|
// flush regs
|
||||||
Flush(FLUSH_END_BLOCK);
|
Flush(FLUSH_END_BLOCK);
|
||||||
EndAndLinkBlock(newpc, do_event_test);
|
EndAndLinkBlock(newpc, do_event_test, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NewRec::RISCV64Compiler::EndBlockWithException(Exception excode)
|
void CPU::NewRec::RISCV64Compiler::EndBlockWithException(Exception excode)
|
||||||
{
|
{
|
||||||
// flush regs, but not pc, it's going to get overwritten
|
// flush regs, but not pc, it's going to get overwritten
|
||||||
// flush cycles because of the GTE instruction stuff...
|
// flush cycles because of the GTE instruction stuff...
|
||||||
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION);
|
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
||||||
|
|
||||||
// TODO: flush load delay
|
// TODO: flush load delay
|
||||||
// TODO: break for pcdrv
|
// TODO: break for pcdrv
|
||||||
|
@ -604,14 +604,16 @@ void CPU::NewRec::RISCV64Compiler::EndBlockWithException(Exception excode)
|
||||||
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
||||||
m_dirty_pc = false;
|
m_dirty_pc = false;
|
||||||
|
|
||||||
EndAndLinkBlock(std::nullopt, true);
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NewRec::RISCV64Compiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test)
|
void CPU::NewRec::RISCV64Compiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test,
|
||||||
|
bool force_run_events)
|
||||||
{
|
{
|
||||||
// event test
|
// event test
|
||||||
// pc should've been flushed
|
// pc should've been flushed
|
||||||
DebugAssert(!m_dirty_pc);
|
DebugAssert(!m_dirty_pc && !m_block_ended);
|
||||||
|
m_block_ended = true;
|
||||||
|
|
||||||
// TODO: try extracting this to a function
|
// TODO: try extracting this to a function
|
||||||
// TODO: move the cycle flush in here..
|
// TODO: move the cycle flush in here..
|
||||||
|
@ -646,7 +648,11 @@ void CPU::NewRec::RISCV64Compiler::EndAndLinkBlock(const std::optional<u32>& new
|
||||||
}
|
}
|
||||||
|
|
||||||
// jump to dispatcher or next block
|
// jump to dispatcher or next block
|
||||||
if (!newpc.has_value())
|
if (force_run_events)
|
||||||
|
{
|
||||||
|
rvEmitJmp(rvAsm, CodeCache::g_run_events_and_dispatch);
|
||||||
|
}
|
||||||
|
else if (!newpc.has_value())
|
||||||
{
|
{
|
||||||
rvEmitJmp(rvAsm, CodeCache::g_dispatcher);
|
rvEmitJmp(rvAsm, CodeCache::g_dispatcher);
|
||||||
}
|
}
|
||||||
|
@ -664,8 +670,6 @@ void CPU::NewRec::RISCV64Compiler::EndAndLinkBlock(const std::optional<u32>& new
|
||||||
rvEmitJmp(rvAsm, target);
|
rvEmitJmp(rvAsm, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_block_ended = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* CPU::NewRec::RISCV64Compiler::EndCompile(u32* code_size, u32* far_code_size)
|
const void* CPU::NewRec::RISCV64Compiler::EndCompile(u32* code_size, u32* far_code_size)
|
||||||
|
@ -2209,9 +2213,29 @@ void CPU::NewRec::RISCV64Compiler::TestInterrupts(const biscuit::GPR& sr)
|
||||||
rvAsm->ANDI(sr, sr, 0xFF);
|
rvAsm->ANDI(sr, sr, 0xFF);
|
||||||
SwitchToFarCode(true, &Assembler::BEQ, sr, zero);
|
SwitchToFarCode(true, &Assembler::BEQ, sr, zero);
|
||||||
BackupHostState();
|
BackupHostState();
|
||||||
Flush(FLUSH_FLUSH_MIPS_REGISTERS | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
||||||
EmitCall(reinterpret_cast<const void*>(&DispatchInterrupt));
|
|
||||||
EndBlock(std::nullopt, true);
|
// Can't use EndBlockWithException() here, because it'll use the wrong PC.
|
||||||
|
// Can't use RaiseException() on the fast path if we're the last instruction, because the next PC is unknown.
|
||||||
|
if (!iinfo->is_last_instruction)
|
||||||
|
{
|
||||||
|
EmitMov(RARG1, Cop0Registers::CAUSE::MakeValueForException(Exception::INT, iinfo->is_branch_instruction, false,
|
||||||
|
(inst + 1)->cop.cop_n));
|
||||||
|
EmitMov(RARG2, m_compiler_pc);
|
||||||
|
EmitCall(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
||||||
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_dirty_pc)
|
||||||
|
EmitMov(RARG1, m_compiler_pc);
|
||||||
|
rvAsm->SW(biscuit::zero, PTR(&g_state.downcount));
|
||||||
|
if (m_dirty_pc)
|
||||||
|
rvAsm->SW(RARG1, PTR(&g_state.pc));
|
||||||
|
m_dirty_pc = false;
|
||||||
|
EndAndLinkBlock(std::nullopt, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
RestoreHostState();
|
RestoreHostState();
|
||||||
SwitchToNearCode(false);
|
SwitchToNearCode(false);
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ protected:
|
||||||
void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) override;
|
void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) override;
|
||||||
void EndBlock(const std::optional<u32>& newpc, bool do_event_test) override;
|
void EndBlock(const std::optional<u32>& newpc, bool do_event_test) override;
|
||||||
void EndBlockWithException(Exception excode) override;
|
void EndBlockWithException(Exception excode) override;
|
||||||
void EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test);
|
void EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, bool force_run_events);
|
||||||
const void* EndCompile(u32* code_size, u32* far_code_size) override;
|
const void* EndCompile(u32* code_size, u32* far_code_size) override;
|
||||||
|
|
||||||
void Flush(u32 flags) override;
|
void Flush(u32 flags) override;
|
||||||
|
|
|
@ -222,14 +222,14 @@ void CPU::NewRec::X64Compiler::EndBlock(const std::optional<u32>& newpc, bool do
|
||||||
|
|
||||||
// flush regs
|
// flush regs
|
||||||
Flush(FLUSH_END_BLOCK);
|
Flush(FLUSH_END_BLOCK);
|
||||||
EndAndLinkBlock(newpc, do_event_test);
|
EndAndLinkBlock(newpc, do_event_test, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NewRec::X64Compiler::EndBlockWithException(Exception excode)
|
void CPU::NewRec::X64Compiler::EndBlockWithException(Exception excode)
|
||||||
{
|
{
|
||||||
// flush regs, but not pc, it's going to get overwritten
|
// flush regs, but not pc, it's going to get overwritten
|
||||||
// flush cycles because of the GTE instruction stuff...
|
// flush cycles because of the GTE instruction stuff...
|
||||||
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION);
|
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
||||||
|
|
||||||
// TODO: flush load delay
|
// TODO: flush load delay
|
||||||
// TODO: break for pcdrv
|
// TODO: break for pcdrv
|
||||||
|
@ -240,14 +240,16 @@ void CPU::NewRec::X64Compiler::EndBlockWithException(Exception excode)
|
||||||
cg->call(static_cast<void (*)(u32, u32)>(&CPU::RaiseException));
|
cg->call(static_cast<void (*)(u32, u32)>(&CPU::RaiseException));
|
||||||
m_dirty_pc = false;
|
m_dirty_pc = false;
|
||||||
|
|
||||||
EndAndLinkBlock(std::nullopt, true);
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::NewRec::X64Compiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test)
|
void CPU::NewRec::X64Compiler::EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test,
|
||||||
|
bool force_run_events)
|
||||||
{
|
{
|
||||||
// event test
|
// event test
|
||||||
// pc should've been flushed
|
// pc should've been flushed
|
||||||
DebugAssert(!m_dirty_pc);
|
DebugAssert(!m_dirty_pc && !m_block_ended);
|
||||||
|
m_block_ended = true;
|
||||||
|
|
||||||
// TODO: try extracting this to a function
|
// TODO: try extracting this to a function
|
||||||
|
|
||||||
|
@ -261,6 +263,12 @@ void CPU::NewRec::X64Compiler::EndAndLinkBlock(const std::optional<u32>& newpc,
|
||||||
cg->inc(cg->dword[PTR(&g_state.pending_ticks)]);
|
cg->inc(cg->dword[PTR(&g_state.pending_ticks)]);
|
||||||
else if (cycles > 0)
|
else if (cycles > 0)
|
||||||
cg->add(cg->dword[PTR(&g_state.pending_ticks)], cycles);
|
cg->add(cg->dword[PTR(&g_state.pending_ticks)], cycles);
|
||||||
|
|
||||||
|
if (force_run_events)
|
||||||
|
{
|
||||||
|
cg->jmp(CodeCache::g_run_events_and_dispatch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -303,8 +311,6 @@ void CPU::NewRec::X64Compiler::EndAndLinkBlock(const std::optional<u32>& newpc,
|
||||||
cg->jmp(target, CodeGenerator::T_NEAR);
|
cg->jmp(target, CodeGenerator::T_NEAR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_block_ended = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* CPU::NewRec::X64Compiler::EndCompile(u32* code_size, u32* far_code_size)
|
const void* CPU::NewRec::X64Compiler::EndCompile(u32* code_size, u32* far_code_size)
|
||||||
|
@ -1339,7 +1345,7 @@ Xbyak::Reg32 CPU::NewRec::X64Compiler::GenerateLoad(const Xbyak::Reg32& addr_reg
|
||||||
cg->mov(RWARG2, m_current_instruction_pc);
|
cg->mov(RWARG2, m_current_instruction_pc);
|
||||||
cg->call(static_cast<void (*)(u32, u32)>(&CPU::RaiseException));
|
cg->call(static_cast<void (*)(u32, u32)>(&CPU::RaiseException));
|
||||||
m_dirty_pc = false;
|
m_dirty_pc = false;
|
||||||
EndAndLinkBlock(std::nullopt, true);
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
|
|
||||||
SwitchToNearCode(false);
|
SwitchToNearCode(false);
|
||||||
RestoreHostState();
|
RestoreHostState();
|
||||||
|
@ -1459,7 +1465,7 @@ void CPU::NewRec::X64Compiler::GenerateStore(const Xbyak::Reg32& addr_reg, const
|
||||||
cg->mov(RWARG2, m_current_instruction_pc);
|
cg->mov(RWARG2, m_current_instruction_pc);
|
||||||
cg->call(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
cg->call(reinterpret_cast<const void*>(static_cast<void (*)(u32, u32)>(&CPU::RaiseException)));
|
||||||
m_dirty_pc = false;
|
m_dirty_pc = false;
|
||||||
EndAndLinkBlock(std::nullopt, true);
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
|
|
||||||
SwitchToNearCode(false);
|
SwitchToNearCode(false);
|
||||||
RestoreHostState();
|
RestoreHostState();
|
||||||
|
@ -1929,9 +1935,28 @@ void CPU::NewRec::X64Compiler::TestInterrupts(const Xbyak::Reg32& sr)
|
||||||
|
|
||||||
SwitchToFarCode(true, &CodeGenerator::jnz);
|
SwitchToFarCode(true, &CodeGenerator::jnz);
|
||||||
BackupHostState();
|
BackupHostState();
|
||||||
Flush(FLUSH_FLUSH_MIPS_REGISTERS | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
Flush(FLUSH_END_BLOCK | FLUSH_FOR_EXCEPTION | FLUSH_FOR_C_CALL);
|
||||||
cg->call(reinterpret_cast<const void*>(&DispatchInterrupt));
|
|
||||||
EndBlock(std::nullopt, true);
|
// Can't use EndBlockWithException() here, because it'll use the wrong PC.
|
||||||
|
// Can't use RaiseException() on the fast path if we're the last instruction, because the next PC is unknown.
|
||||||
|
if (!iinfo->is_last_instruction)
|
||||||
|
{
|
||||||
|
cg->mov(RWARG1, Cop0Registers::CAUSE::MakeValueForException(Exception::INT, iinfo->is_branch_instruction, false,
|
||||||
|
(inst + 1)->cop.cop_n));
|
||||||
|
cg->mov(RWARG2, m_compiler_pc);
|
||||||
|
cg->call(static_cast<void (*)(u32, u32)>(&CPU::RaiseException));
|
||||||
|
m_dirty_pc = false;
|
||||||
|
EndAndLinkBlock(std::nullopt, true, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_dirty_pc)
|
||||||
|
cg->mov(cg->dword[PTR(&g_state.pc)], m_compiler_pc);
|
||||||
|
m_dirty_pc = false;
|
||||||
|
cg->mov(cg->dword[PTR(&g_state.downcount)], 0);
|
||||||
|
EndAndLinkBlock(std::nullopt, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
RestoreHostState();
|
RestoreHostState();
|
||||||
SwitchToNearCode(false);
|
SwitchToNearCode(false);
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ protected:
|
||||||
void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) override;
|
void GenerateCall(const void* func, s32 arg1reg = -1, s32 arg2reg = -1, s32 arg3reg = -1) override;
|
||||||
void EndBlock(const std::optional<u32>& newpc, bool do_event_test) override;
|
void EndBlock(const std::optional<u32>& newpc, bool do_event_test) override;
|
||||||
void EndBlockWithException(Exception excode) override;
|
void EndBlockWithException(Exception excode) override;
|
||||||
void EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test);
|
void EndAndLinkBlock(const std::optional<u32>& newpc, bool do_event_test, bool force_run_events);
|
||||||
const void* EndCompile(u32* code_size, u32* far_code_size) override;
|
const void* EndCompile(u32* code_size, u32* far_code_size) override;
|
||||||
|
|
||||||
void Flush(u32 flags) override;
|
void Flush(u32 flags) override;
|
||||||
|
|
|
@ -2740,7 +2740,14 @@ bool CodeGenerator::Compile_cop0(Instruction instruction, const CodeCache::Instr
|
||||||
EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value);
|
EmitAnd(sr_value.host_reg, sr_value.host_reg, cause_value);
|
||||||
EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00));
|
EmitTest(sr_value.host_reg, Value::FromConstantU32(0xFF00));
|
||||||
EmitConditionalBranch(Condition::Zero, false, &no_interrupt);
|
EmitConditionalBranch(Condition::Zero, false, &no_interrupt);
|
||||||
|
|
||||||
|
EmitBranch(GetCurrentFarCodePointer());
|
||||||
|
SwitchToFarCode();
|
||||||
|
WriteNewPC(CalculatePC(), false);
|
||||||
EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0));
|
EmitStoreCPUStructField(offsetof(State, downcount), Value::FromConstantU32(0));
|
||||||
|
EmitExceptionExit();
|
||||||
|
SwitchToNearCode();
|
||||||
|
|
||||||
EmitBindLabel(&no_interrupt);
|
EmitBindLabel(&no_interrupt);
|
||||||
m_register_cache.UninhibitAllocation();
|
m_register_cache.UninhibitAllocation();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue