DSPAnalyzer: Add convenience functions over bit tests

Makes it harder to accidentally misuse and increases readability.
This commit is contained in:
Lioncash 2020-12-28 11:15:55 -05:00
parent 5756ece7ce
commit 2ff4d04785
5 changed files with 52 additions and 19 deletions

View File

@ -130,7 +130,7 @@ void Analyzer::AnalyzeRange(u16 start_addr, u16 end_addr)
opcode->opcode == 0x1900 || opcode->opcode == 0x1980 || opcode->opcode == 0x2000 || opcode->opcode == 0x1900 || opcode->opcode == 0x1980 || opcode->opcode == 0x2000 ||
opcode->extended) opcode->extended)
{ {
m_code_flags[static_cast<u16>(addr + opcode->size)] |= CODE_CHECK_INT; m_code_flags[static_cast<u16>(addr + opcode->size)] |= CODE_CHECK_EXC;
} }
addr += opcode->size; addr += opcode->size;

View File

@ -26,7 +26,7 @@ enum CodeFlags : u8
CODE_LOOP_START = 4, CODE_LOOP_START = 4,
CODE_LOOP_END = 8, CODE_LOOP_END = 8,
CODE_UPDATE_SR = 16, CODE_UPDATE_SR = 16,
CODE_CHECK_INT = 32, CODE_CHECK_EXC = 32,
}; };
class Analyzer class Analyzer
@ -49,8 +49,41 @@ public:
// some pretty expensive analysis if necessary. // some pretty expensive analysis if necessary.
void Analyze(); void Analyze();
// Retrieves the flags set during analysis for code in memory. // Whether or not the given address indicates the start of an instruction.
[[nodiscard]] u8 GetCodeFlags(u16 address) const { return m_code_flags[address]; } [[nodiscard]] bool IsStartOfInstruction(u16 address) const
{
return (GetCodeFlags(address) & CODE_START_OF_INST) != 0;
}
// Whether or not the address indicates an idle skip location.
[[nodiscard]] bool IsIdleSkip(u16 address) const
{
return (GetCodeFlags(address) & CODE_IDLE_SKIP) != 0;
}
// Whether or not the address indicates the start of a loop.
[[nodiscard]] bool IsLoopStart(u16 address) const
{
return (GetCodeFlags(address) & CODE_LOOP_START) != 0;
}
// Whether or not the address indicates the end of a loop.
[[nodiscard]] bool IsLoopEnd(u16 address) const
{
return (GetCodeFlags(address) & CODE_LOOP_END) != 0;
}
// Whether or not the address describes an instruction that requires updating the SR register.
[[nodiscard]] bool IsUpdateSR(u16 address) const
{
return (GetCodeFlags(address) & CODE_UPDATE_SR) != 0;
}
// Whether or not the address describes instructions that potentially raise exceptions.
[[nodiscard]] bool IsCheckExceptions(u16 address) const
{
return (GetCodeFlags(address) & CODE_CHECK_EXC) != 0;
}
private: private:
// Flushes all analyzed state. // Flushes all analyzed state.
@ -60,6 +93,9 @@ private:
// Note: start is inclusive, end is exclusive. // Note: start is inclusive, end is exclusive.
void AnalyzeRange(u16 start_addr, u16 end_addr); void AnalyzeRange(u16 start_addr, u16 end_addr);
// Retrieves the flags set during analysis for code in memory.
[[nodiscard]] u8 GetCodeFlags(u16 address) const { return m_code_flags[address]; }
// Holds data about all instructions in RAM. // Holds data about all instructions in RAM.
std::array<u8, 65536> m_code_flags{}; std::array<u8, 65536> m_code_flags{};

View File

@ -51,7 +51,7 @@ void Interpreter::Step()
ExecuteInstruction(UDSPInstruction{opc}); ExecuteInstruction(UDSPInstruction{opc});
const auto pc = state.pc; const auto pc = state.pc;
if ((state.GetAnalyzer().GetCodeFlags(static_cast<u16>(pc - 1)) & Analyzer::CODE_LOOP_END) != 0) if (state.GetAnalyzer().IsLoopEnd(static_cast<u16>(pc - 1)))
HandleLoop(); HandleLoop();
} }
@ -115,8 +115,7 @@ int Interpreter::RunCyclesDebug(int cycles)
return cycles; return cycles;
} }
// Idle skipping. if (state.GetAnalyzer().IsIdleSkip(state.pc))
if ((state.GetAnalyzer().GetCodeFlags(state.pc) & Analyzer::CODE_IDLE_SKIP) != 0)
return 0; return 0;
Step(); Step();
@ -171,8 +170,7 @@ int Interpreter::RunCycles(int cycles)
if ((state.cr & CR_HALT) != 0) if ((state.cr & CR_HALT) != 0)
return 0; return 0;
// Idle skipping. if (state.GetAnalyzer().IsIdleSkip(state.pc))
if ((state.GetAnalyzer().GetCodeFlags(state.pc) & Analyzer::CODE_IDLE_SKIP) != 0)
return 0; return 0;
Step(); Step();

View File

@ -128,9 +128,9 @@ void DSPEmitter::checkExceptions(u32 retval)
bool DSPEmitter::FlagsNeeded() const bool DSPEmitter::FlagsNeeded() const
{ {
const u8 flags = m_dsp_core.DSPState().GetAnalyzer().GetCodeFlags(m_compile_pc); const auto& analyzer = m_dsp_core.DSPState().GetAnalyzer();
return !(flags & Analyzer::CODE_START_OF_INST) || (flags & Analyzer::CODE_UPDATE_SR); return !analyzer.IsStartOfInstruction(m_compile_pc) || analyzer.IsUpdateSR(m_compile_pc);
} }
static void FallbackThunk(Interpreter::Interpreter& interpreter, UDSPInstruction inst) static void FallbackThunk(Interpreter::Interpreter& interpreter, UDSPInstruction inst)
@ -245,7 +245,7 @@ void DSPEmitter::Compile(u16 start_addr)
auto& analyzer = m_dsp_core.DSPState().GetAnalyzer(); auto& analyzer = m_dsp_core.DSPState().GetAnalyzer();
while (m_compile_pc < start_addr + MAX_BLOCK_SIZE) while (m_compile_pc < start_addr + MAX_BLOCK_SIZE)
{ {
if (analyzer.GetCodeFlags(m_compile_pc) & Analyzer::CODE_CHECK_INT) if (analyzer.IsCheckExceptions(m_compile_pc))
checkExceptions(m_block_size[start_addr]); checkExceptions(m_block_size[start_addr]);
const UDSPInstruction inst = m_dsp_core.DSPState().ReadIMEM(m_compile_pc); const UDSPInstruction inst = m_dsp_core.DSPState().ReadIMEM(m_compile_pc);
@ -263,7 +263,7 @@ void DSPEmitter::Compile(u16 start_addr)
// Handle loop condition, only if current instruction was flagged as a loop destination // Handle loop condition, only if current instruction was flagged as a loop destination
// by the analyzer. // by the analyzer.
if ((analyzer.GetCodeFlags(static_cast<u16>(m_compile_pc - 1u)) & Analyzer::CODE_LOOP_END) != 0) if (analyzer.IsLoopEnd(static_cast<u16>(m_compile_pc - 1u)))
{ {
MOVZX(32, 16, EAX, M_SDSP_r_st(2)); MOVZX(32, 16, EAX, M_SDSP_r_st(2));
TEST(32, R(EAX), R(EAX)); TEST(32, R(EAX), R(EAX));
@ -284,7 +284,7 @@ void DSPEmitter::Compile(u16 start_addr)
DSPJitRegCache c(m_gpr); DSPJitRegCache c(m_gpr);
HandleLoop(); HandleLoop();
m_gpr.SaveRegs(); m_gpr.SaveRegs();
if (!Host::OnThread() && (analyzer.GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP) != 0) if (!Host::OnThread() && analyzer.IsIdleSkip(start_addr))
{ {
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES)); MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
} }
@ -320,7 +320,7 @@ void DSPEmitter::Compile(u16 start_addr)
DSPJitRegCache c(m_gpr); DSPJitRegCache c(m_gpr);
// don't update g_dsp.pc -- the branch insn already did // don't update g_dsp.pc -- the branch insn already did
m_gpr.SaveRegs(); m_gpr.SaveRegs();
if (!Host::OnThread() && (analyzer.GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP) != 0) if (!Host::OnThread() && analyzer.IsIdleSkip(start_addr))
{ {
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES)); MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
} }
@ -337,7 +337,7 @@ void DSPEmitter::Compile(u16 start_addr)
} }
// End the block if we're before an idle skip address // End the block if we're before an idle skip address
if ((analyzer.GetCodeFlags(m_compile_pc) & Analyzer::CODE_IDLE_SKIP) != 0) if (analyzer.IsIdleSkip(m_compile_pc))
{ {
break; break;
} }
@ -383,7 +383,7 @@ void DSPEmitter::Compile(u16 start_addr)
} }
m_gpr.SaveRegs(); m_gpr.SaveRegs();
if (!Host::OnThread() && (analyzer.GetCodeFlags(start_addr) & Analyzer::CODE_IDLE_SKIP) != 0) if (!Host::OnThread() && analyzer.IsIdleSkip(start_addr))
{ {
MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES)); MOV(16, R(EAX), Imm16(DSP_IDLE_SKIP_CYCLES));
} }

View File

@ -82,8 +82,7 @@ void DSPEmitter::WriteBranchExit()
{ {
DSPJitRegCache c(m_gpr); DSPJitRegCache c(m_gpr);
m_gpr.SaveRegs(); m_gpr.SaveRegs();
if ((m_dsp_core.DSPState().GetAnalyzer().GetCodeFlags(m_start_address) & if (m_dsp_core.DSPState().GetAnalyzer().IsIdleSkip(m_start_address))
Analyzer::CODE_IDLE_SKIP) != 0)
{ {
MOV(16, R(EAX), Imm16(0x1000)); MOV(16, R(EAX), Imm16(0x1000));
} }